Problem
For a managed project (in workspace/<name>/), the code-reviewer agent and the block-unreviewed-merge.sh hook disagree about where approval markers live.
Observed during a real merge (ApexYard ops fork → managed project apessolutions/dfn PR #96):
-
Code-reviewer agent (.claude/agents/code-reviewer.md) wrote its approval marker to the ops-repo path:
<ops_root>/.claude/session/reviews/96-rex.approved
This is what the agent's spec / its prior verdict explicitly stated as Files touched.
-
The merge-gate hook (.claude/hooks/block-unreviewed-merge.sh) looked for the marker in the project-repo path (via git rev-parse --show-toplevel from inside workspace/<name>/):
<ops_root>/workspace/<name>/.claude/session/reviews/96-rex.approved
-
Merge was blocked with: BLOCKED: PR #96 has no recorded code-reviewer (Rex) approval. even though Rex had just approved.
-
Worked around manually by copying markers to the project-repo path.
Same skill, contradicting spec
Inside .claude/skills/approve-merge/SKILL.md step 5, the spec uses:
REPO_ROOT=$(git rev-parse --show-toplevel)
mkdir -p "$REPO_ROOT/.claude/session/reviews"
— which puts the CEO marker in the project repo, agreeing with the hook. So /approve-merge and the merge-gate hook are aligned. But the code-reviewer agent writes to a different place.
Suggested fix
Pick one of:
- (a) Both Rex and
/approve-merge write to the ops-repo .claude/session/reviews/ (alongside the ticket markers under .claude/session/tickets/<project>). Update block-unreviewed-merge.sh to walk up to the ops root the same way require-active-ticket.sh already does (looks for onboarding.yaml + apexyard.projects.yaml).
- (b) Both write to the project repo
.claude/session/reviews/. Update the code-reviewer agent's spec + /approve-merge's spec to use git -C workspace/<name> rev-parse --show-toplevel. Also requires every managed project's .gitignore to include .claude/session/ — see apexyard's own .gitignore for the precedent.
(a) seems more coherent with the apexyard#41 layout that already moved ticket markers under the ops root. It also avoids spreading approval state across many gitignore configurations.
Reproduction (against an ApexYard fork)
- Adopt a project via
/handover, clone it into workspace/<name>/
/start-ticket for an issue in that project's repo
- Open a PR, invoke the
code-reviewer agent → it writes to <ops_root>/.claude/session/reviews/
gh pr merge → blocked, hook reports the missing marker at workspace/<name>/.claude/session/reviews/
Workaround in the meantime
Manually copy / write markers under workspace/<name>/.claude/session/reviews/ before retrying the merge. Mind the project's .gitignore (most projects don't gitignore .claude/ — accidentally committing markers is the risk).
Severity
P1 — every audit / framework-flow PR hits this. Friction multiplies linearly with PR count.
Problem
For a managed project (in
workspace/<name>/), thecode-revieweragent and theblock-unreviewed-merge.shhook disagree about where approval markers live.Observed during a real merge (ApexYard ops fork → managed project
apessolutions/dfnPR #96):Code-reviewer agent (
.claude/agents/code-reviewer.md) wrote its approval marker to the ops-repo path:This is what the agent's spec / its prior verdict explicitly stated as
Files touched.The merge-gate hook (
.claude/hooks/block-unreviewed-merge.sh) looked for the marker in the project-repo path (viagit rev-parse --show-toplevelfrom insideworkspace/<name>/):Merge was blocked with:
BLOCKED: PR #96 has no recorded code-reviewer (Rex) approval.even though Rex had just approved.Worked around manually by copying markers to the project-repo path.
Same skill, contradicting spec
Inside
.claude/skills/approve-merge/SKILL.mdstep 5, the spec uses:— which puts the CEO marker in the project repo, agreeing with the hook. So
/approve-mergeand the merge-gate hook are aligned. But thecode-revieweragent writes to a different place.Suggested fix
Pick one of:
/approve-mergewrite to the ops-repo.claude/session/reviews/(alongside the ticket markers under.claude/session/tickets/<project>). Updateblock-unreviewed-merge.shto walk up to the ops root the same wayrequire-active-ticket.shalready does (looks foronboarding.yaml+apexyard.projects.yaml)..claude/session/reviews/. Update thecode-revieweragent's spec +/approve-merge's spec to usegit -C workspace/<name> rev-parse --show-toplevel. Also requires every managed project's.gitignoreto include.claude/session/— see apexyard's own.gitignorefor the precedent.(a) seems more coherent with the apexyard#41 layout that already moved ticket markers under the ops root. It also avoids spreading approval state across many gitignore configurations.
Reproduction (against an ApexYard fork)
/handover, clone it intoworkspace/<name>//start-ticketfor an issue in that project's repocode-revieweragent → it writes to<ops_root>/.claude/session/reviews/gh pr merge→ blocked, hook reports the missing marker atworkspace/<name>/.claude/session/reviews/Workaround in the meantime
Manually copy / write markers under
workspace/<name>/.claude/session/reviews/before retrying the merge. Mind the project's.gitignore(most projects don't gitignore.claude/— accidentally committing markers is the risk).Severity
P1 — every audit / framework-flow PR hits this. Friction multiplies linearly with PR count.