Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion claude/commands/pre-pr.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ Follow the `pre-pr` skill exactly:
- Each reviewer is read-only; do not pass it edit tools.
- On any reviewer returning `REQUEST CHANGES`, treat the overall result
as `FAIL` and stop before writing the sentinel as `PASS`.
- Sentinel location: `$(git rev-parse --show-toplevel)/.git/pre-pr.ok`,
- Sentinel location: `$(git rev-parse --absolute-git-dir)/pre-pr.ok` — the
resolved git directory (matching the hook), so it works in linked worktrees too
(where `.git` is a file),
format per the skill (`head=`, `branch=`, `status=`, `timestamp=`,
`build=`, `reviewers=`, `version=`). Use `git rev-parse HEAD` for the
SHA and `date -u +%Y-%m-%dT%H:%M:%SZ` for the timestamp.
Expand Down
18 changes: 17 additions & 1 deletion scripts/pre-pr-gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,23 @@ if ! printf '%s' "$cmd" \
fi

repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0
sentinel="$repo_root/.git/pre-pr.ok"
# Resolve the real git directory rather than assuming `$repo_root/.git` is one:
# in a linked worktree `.git` is a file (a `gitdir:` pointer), so the sentinel
# lives in the worktree's own git dir, not under `$repo_root/.git`.
#
# Prefer `--absolute-git-dir` (always absolute), but fall back to `--git-dir`
# for Git < 2.13, where `--absolute-git-dir` is unknown — otherwise that single
# failure would trip `|| exit 0` and let the gate fail open. `--git-dir` can be
# relative (`.git` at the work-tree root), so make a relative result absolute
# against `repo_root`; `--absolute-git-dir` output already matches `/*`.
git_dir=$(git -C "$repo_root" rev-parse --absolute-git-dir 2>/dev/null) \
|| git_dir=$(git -C "$repo_root" rev-parse --git-dir 2>/dev/null) \
|| exit 0
case "$git_dir" in
/*) ;;
*) git_dir="$repo_root/$git_dir" ;;
esac
sentinel="$git_dir/pre-pr.ok"
Comment thread
alexander-yevsyukov marked this conversation as resolved.

block() {
cat >&2
Expand Down
6 changes: 4 additions & 2 deletions skills/check-links/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,9 @@ If `K == 0`, report a single line: "All links OK."

Run this even if Lychee failed — leaving a server on port `1414` would
poison the next invocation.
- Write `.git/check-links.ok` at the repo root:
- Write `check-links.ok` to the repository's git directory —
`$(git rev-parse --absolute-git-dir)/check-links.ok` — so it works in worktrees
too (matching the path the `pre-pr` gate reads):

```
head=<full HEAD SHA>
Expand All @@ -295,7 +297,7 @@ If `K == 0`, report a single line: "All links OK."
The sentinel is consumed by the `pre-pr` skill's reviewer step: when it
sees a sentinel whose `head=` matches the current HEAD SHA and
`status=PASS`, it skips re-dispatching `check-links` and records it
as APPROVE with the note "cached from `.git/check-links.ok`". Any
as APPROVE with the note "cached from `check-links.ok`". Any
HEAD advance (commit, amend, rebase) invalidates the cache automatically.

## Notes
Expand Down
26 changes: 15 additions & 11 deletions skills/pre-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ description: >
KDoc/Javadoc links fail locally instead of in CI's Dokka run, and invokes
the relevant reviewers (`kotlin-engineer`, `spine-code-review`,
`review-docs`, `dependency-audit`,
`check-links`) against the branch diff. On success, writes a sentinel file at
`.git/pre-pr.ok` so the `gh pr create` hook can verify the checklist ran
for the current HEAD. Use before opening a PR, or when CI rejected a
branch and a fast local repro is wanted.
`check-links`) against the branch diff. On success, writes the `pre-pr.ok`
sentinel into the repository's git directory so the `gh pr create` hook can
verify the checklist ran for the current HEAD. Use before opening a PR, or
when CI rejected a branch and a fast local repro is wanted.
---

# Pre-PR checklist (repo-specific)
Expand Down Expand Up @@ -50,7 +50,7 @@ Pre-PR progress:
- [ ] 3. Build/check; dokkaGenerate on any `.kt`/`.java` source change (alone if doc-only)
- [ ] 4. Reviewers dispatched for the changed file types
- [ ] 5. Aggregate to PASS / FAIL
- [ ] 6. Write the `.git/pre-pr.ok` sentinel
- [ ] 6. Write the `pre-pr.ok` sentinel
```

### 1. Determine scope and repository capabilities
Expand Down Expand Up @@ -193,9 +193,10 @@ for this repo" rather than failing.
reported by the dispatched reviewers (`spine-code-review`, `review-docs`);
pre-pr itself does not re-check.

**`check-links` sentinel short-circuit.** Read `.git/check-links.ok` (if
present). If `head=` equals the current **full** HEAD SHA and `status=PASS`, skip
the link check and record `APPROVE` with note "cached from `.git/check-links.ok`"
**`check-links` sentinel short-circuit.** Read `check-links.ok` from the git
directory (if present). If `head=` equals the current **full** HEAD SHA and
`status=PASS`, skip the link check and record `APPROVE` with note "cached from
`check-links.ok`"
(caching its ~30 s rebuild+serve cycle; the result is deterministic for a given
HEAD). Otherwise run `check-links` normally.

Expand Down Expand Up @@ -224,9 +225,12 @@ missing version bump.

### 6. Sentinel

Write `.git/pre-pr.ok` at the repo root (never under `.claude/`). The `gh pr
create` hook (`.agents/scripts/pre-pr-gate.sh`) checks `head=` and `status=`;
field names in this block are part of that contract.
Write `pre-pr.ok` to the repository's git directory — `$(git rev-parse
--absolute-git-dir)/pre-pr.ok` (never under `.claude/`). Resolving the git
directory keeps the sentinel correct in linked worktrees, where `.git` is a file,
not a directory; `--absolute-git-dir` matches what the hook reads.
The `gh pr create` hook (`.agents/scripts/pre-pr-gate.sh`) checks `head=` and
`status=`; field names in this block are part of that contract.

```
head=<full HEAD SHA>
Expand Down