Measured, 2026-07-31
/ready-to-review-mergeable and stop-prs-green.sh are copied per repo. There is no propagation mechanism, and they have already diverged.
| Repo |
stop-prs-green.sh |
CURRENT_REPO fallback |
dojo-os |
221 lines |
DojoCodingLabs/dojo-os |
freedom-academy-admin |
115 lines |
line absent |
freedom-academy-demolab-gcp |
115 lines |
line absent |
freedom-academy-moodle-plugins |
115 lines |
line absent |
dojo-infra-gitops |
221 + 1 line changed |
DojoCodingLabs/dojo-infra-gitops |
Read via gh api repos/…/contents/.claude/hooks/stop-prs-green.sh, distinguishing "line absent" from "could not read" — all four were read successfully.
The three freedom-academy copies are 106 lines older and predate cross-repo handling entirely. Everything dojo-os learned — including the plugin PR #370 lesson its own header documents — never propagated.
The asymmetry is what makes it durable: dojo-os is the repo that evolves and the repo where the hook gets exercised, so the stale copies are stale and unnoticed.
Three defects, in order of durability
1. Distribution is copy-paste
Porting the command to a sixth repo today required hand-editing the CURRENT_REPO fallback, which is hardcoded to DojoCodingLabs/dojo-os:
CURRENT_REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null)
if [[ -z "$CURRENT_REPO" ]]; then
CURRENT_REPO="DojoCodingLabs/dojo-os"
fi
Copying that verbatim reproduces the exact cross-repo bug the header warns about one screen above: a bare 23 in the state file gets evaluated against dojo-os#23 — an unrelated PR — and the gate passes or blocks on the wrong evidence.
A value that must change on every port is a distribution problem, not a config problem.
2. Session state lives in the working tree
STATE_FILE=".claude/.implement-prs" — written by the agent, read by the hook. It is the only channel between an in-context instruction and an out-of-context enforcer, so the mechanism is necessary. The location is not.
- Not gitignored in any repo that ships the hook. Surfaces as
??; a git add . commits it.
- Shared across sessions in one checkout, and across worktrees.
- Survives abandonment. The hook deletes it when everything is green, but not when a session dies — so the next session starts gated on PRs it never opened.
Measured today: 0 of 50 open PRs across 5 repos touch it, and it 404s on the default branch of all 6 repos. The risk has never materialised. .gitignore shipped as a stopgap in dojo-infra-gitops; the real fix is the session scratch directory, keyed by session, which only makes sense to do once — upstream.
3. The gh-failure path is unobservable
- gh / network failure -> exit 0 with a warning (never block on infra).
Failing open is correct — a network blip must not trap a session. But it means "I could not look" resolves to "proceed", and the only trace is a WARN on a subprocess's stderr, which is the same destination as console.error.
Nobody can currently answer how many green stops were actually unverified. Not asking for fail-closed — asking for the warning to land somewhere countable.
Proposed
Context
Found while porting the command to dojo-infra-gitops during the 2026-07-31 alerts observability audit. The audit's recurring finding was controls that detect correctly and report nowhere; this one is the inverse and worth keeping — it is the only hard gate the audit found. Which is exactly why its distribution should not be manual.
Created by Claude Code on behalf of @andres
Measured, 2026-07-31
/ready-to-review-mergeableandstop-prs-green.share copied per repo. There is no propagation mechanism, and they have already diverged.stop-prs-green.shCURRENT_REPOfallbackdojo-osDojoCodingLabs/dojo-osfreedom-academy-adminfreedom-academy-demolab-gcpfreedom-academy-moodle-pluginsdojo-infra-gitopsDojoCodingLabs/dojo-infra-gitopsRead via
gh api repos/…/contents/.claude/hooks/stop-prs-green.sh, distinguishing "line absent" from "could not read" — all four were read successfully.The three
freedom-academycopies are 106 lines older and predate cross-repo handling entirely. Everythingdojo-oslearned — including the plugin PR #370 lesson its own header documents — never propagated.The asymmetry is what makes it durable:
dojo-osis the repo that evolves and the repo where the hook gets exercised, so the stale copies are stale and unnoticed.Three defects, in order of durability
1. Distribution is copy-paste
Porting the command to a sixth repo today required hand-editing the
CURRENT_REPOfallback, which is hardcoded toDojoCodingLabs/dojo-os:Copying that verbatim reproduces the exact cross-repo bug the header warns about one screen above: a bare
23in the state file gets evaluated againstdojo-os#23— an unrelated PR — and the gate passes or blocks on the wrong evidence.A value that must change on every port is a distribution problem, not a config problem.
2. Session state lives in the working tree
STATE_FILE=".claude/.implement-prs"— written by the agent, read by the hook. It is the only channel between an in-context instruction and an out-of-context enforcer, so the mechanism is necessary. The location is not.??; agit add .commits it.Measured today: 0 of 50 open PRs across 5 repos touch it, and it 404s on the default branch of all 6 repos. The risk has never materialised.
.gitignoreshipped as a stopgap indojo-infra-gitops; the real fix is the session scratch directory, keyed by session, which only makes sense to do once — upstream.3. The
gh-failure path is unobservableFailing open is correct — a network blip must not trap a session. But it means "I could not look" resolves to "proceed", and the only trace is a
WARNon a subprocess's stderr, which is the same destination asconsole.error.Nobody can currently answer how many green stops were actually unverified. Not asking for fail-closed — asking for the warning to land somewhere countable.
Proposed
gh-failure warning somewhere countable, so fail-open stays honestfreedom-academycopies — they will not self-healContext
Found while porting the command to
dojo-infra-gitopsduring the 2026-07-31 alerts observability audit. The audit's recurring finding was controls that detect correctly and report nowhere; this one is the inverse and worth keeping — it is the only hard gate the audit found. Which is exactly why its distribution should not be manual.Created by Claude Code on behalf of @andres