Context
Operator design input, 2026-07-18, on the #15401 findings: "blocking all states is too much. my recommendation would be: allowed to stop AFTER creating a PR." Companion to #15401 (the dialogue quadrant); this ticket owns the AUTONOMOUS quadrant, where the hook currently blocks nearly everything (152 blocks in 48h) with only the narrow #15371 clean-terminal edge (8 accepts).
The operator's principle is correct and fixes the real defect: today's stop-permission is DECLARATIVE (lane-state prose + gate declarations — gameable, and it produced fleet-wide idle costume); the proposal re-anchors rest on a MATERIAL, externally-verifiable artifact. Two sharpenings keep the principle from Goodharting.
The Problem
- Declarative stop-permission is the wrong currency. A lane-state block is words; the swarm's actual unit of shipped motion is the lifecycle artifact. An agent that opened a PR has demonstrably driven; an agent that declared eloquently has not.
- But a PR-ONLY key breaks against the hook's own economics.
formatCapacityAdvisory (the hook's directive, live today) weights REVIEW seats above new artifacts once an agent has ≥3 own open PRs — under a PR-only stop key, an agent obeying that advisory could never earn rest, and the perverse incentive is junk PRs minted for stop-permission. It also mechanically reinstates the commit-bias that AGENTS.md §contributions_over_commits exists to override (reviews, author-response cycles, and coordination are first-class contribution).
- A per-session key licenses idling. One PR at hour 1 must not cover hour 7.
The Architectural Reality
- Decision seam:
ai/scripts/lifecycle/stopHookDecision.mjs (decideStopHookAction, evaluateCleanTerminalAcceptance) — pure, unit-tested; consumed by .claude/hooks/laneStateStopHook.mjs.
- The hook already parses transcript evidence (
collectLaneStateToolEvidenceFromJsonl) — the exact mechanism to VERIFY artifacts externally instead of trusting declarations.
- The hook's own audit log already provides the per-session boundary (
countSessionCompliantRefusals precedent) — the same mechanism scopes "since the last accepted stop".
- Dry-run forensic (2026-07-18): all nine seat checkouts carry
NEO_LANE_STATE_ENFORCE=1; the 27 WOULD-BLOCK lines trace to stale long-lived sessions (hook command snapshotted before the flag landed) — a restart hygiene note, not config drift. The audit line does NOT carry the agent identity, which made this forensic slower than it should be.
The Fix
The material-artifact stop key: an autonomous turn may stop when BOTH hold —
A material lifecycle artifact shipped since this session's last accepted stop (audit-log-bounded), verified from transcript tool evidence — never self-declared. The v1 key CLASS (both externally verifiable):
- a PR opened (create-PR tool result carrying a PR URL/number);
- a formal review submitted (
manage_pr_review result).
(The originally-prescribed third class — an own-PR author-response cycle — was FALSIFIED out of v1 at implementation: no single tool-result shape verifies it externally without prose trust. It re-enters only through the successor recognizer spec recorded in the implementation narrowing on this ticket — never by prose recognition.)
A valid lane-state terminal (unchanged validator — the record still matters; the artifact is the license, the terminal is the handoff).
Blocks remain for artifact-less autonomous turns (today's teeth, unchanged). The #15371 clean-terminal edge REMAINS as the artifact-less fallback (rare, ratcheted — e.g. a genuinely-all-handed-off board after review-only coordination that produced no listed artifact); revisit its retirement only with dogfood data. The directive on artifact-less blocks gains one line naming the key: "ship one material artifact (PR / formal review) — that is the stop key."
Composition with #15401 (the four-quadrant model):
| Quadrant |
Behavior |
dialogue + active-lane terminal |
refuse — answer-plus-drive (#15401) |
| dialogue + handed-off terminal |
allow (turn-taking) |
| autonomous + material artifact since last stop + valid terminal |
ALLOW (this ticket) |
| autonomous + no artifact |
block (unchanged teeth; clean-terminal fallback intact) |
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
decideStopHookAction autonomous path |
ai/scripts/lifecycle/stopHookDecision.mjs |
material-artifact key allows; artifact evidence transcript-verified; since-last-allow scoped |
absent/unverifiable evidence → today's block semantics |
fn JSDoc + hook header |
hook read + 48h audit distribution, 2026-07-18 |
| artifact-evidence collector |
validateLaneStateTerminal.mjs sibling (extend collectLaneStateToolEvidenceFromJsonl family) |
recognizes the v1 artifact classes (PR-opened, formal-review) from tool-result records |
unrecognized shapes → no key (fail-closed) |
fn JSDoc |
collector precedent read |
| audit line |
.claude/hooks/laneStateStopHook.mjs |
gains identity= (from NEO_AGENT_IDENTITY) + the new MATERIAL-ALLOW class |
absent identity → identity=? |
header |
the slow forensic this week |
Acceptance Criteria
Out of Scope
- The dialogue quadrant (#15401 owns it; shipped composition is SEQUENCED, not independent — the #15401 seam merged first and this ticket's implementation rebases onto it, composing the four-quadrant table above on the current merged decision surface).
- Retiring the #15371 clean-terminal edge (dogfood-data decision later).
- Non-Claude harness parity (no stop-hook exists there; tracked in the OpenCode-seat context).
- Stale-session restart hygiene (operational note to the operator; no code).
Decision Record impact
none — recalibrates the hook within its own design language; §contributions_over_commits respected by construction (the v1 key class includes formal reviews alongside PRs — never PRs alone; the author-response class is excluded from v1 pending its successor recognizer spec).
Related
- #15401 (companion — dialogue quadrant), #15274 / PR #15371 (the clean-terminal edge + rollout ramp template), #11455 (§L3 substrate).
Live latest-open sweep: latest 10 checked at 2026-07-18T02:56Z (#15401 filing) + hot-context refile 03:00Z; no equivalent. A2A: no competing claim (da405982 is my own #15401 broadcast).
Origin Session ID: abce4d75-7dcb-4145-8afc-b0ff2cdc51e6
Retrieval Hint: query_raw_memories("material artifact stop key PR review RC-clear since last allow autonomous quadrant")
Authority fold, 2026-07-18T09:14Z (ticket author): body folded to the shipped v1 contract per the implementer's disposition record (two artifact classes with the rc-response falsification + successor gate; no live dry-run tier per the #15401 fold; sequenced composition on the merged #15401 seam). The disposition comment on this ticket carries the falsification detail; this fold makes the body match it.
Context
Operator design input, 2026-07-18, on the #15401 findings: "blocking all states is too much. my recommendation would be: allowed to stop AFTER creating a PR." Companion to #15401 (the dialogue quadrant); this ticket owns the AUTONOMOUS quadrant, where the hook currently blocks nearly everything (152 blocks in 48h) with only the narrow #15371 clean-terminal edge (8 accepts).
The operator's principle is correct and fixes the real defect: today's stop-permission is DECLARATIVE (lane-state prose + gate declarations — gameable, and it produced fleet-wide idle costume); the proposal re-anchors rest on a MATERIAL, externally-verifiable artifact. Two sharpenings keep the principle from Goodharting.
The Problem
formatCapacityAdvisory(the hook's directive, live today) weights REVIEW seats above new artifacts once an agent has ≥3 own open PRs — under a PR-only stop key, an agent obeying that advisory could never earn rest, and the perverse incentive is junk PRs minted for stop-permission. It also mechanically reinstates the commit-bias thatAGENTS.md §contributions_over_commitsexists to override (reviews, author-response cycles, and coordination are first-class contribution).The Architectural Reality
ai/scripts/lifecycle/stopHookDecision.mjs(decideStopHookAction,evaluateCleanTerminalAcceptance) — pure, unit-tested; consumed by.claude/hooks/laneStateStopHook.mjs.collectLaneStateToolEvidenceFromJsonl) — the exact mechanism to VERIFY artifacts externally instead of trusting declarations.countSessionCompliantRefusalsprecedent) — the same mechanism scopes "since the last accepted stop".NEO_LANE_STATE_ENFORCE=1; the 27 WOULD-BLOCK lines trace to stale long-lived sessions (hook command snapshotted before the flag landed) — a restart hygiene note, not config drift. The audit line does NOT carry the agent identity, which made this forensic slower than it should be.The Fix
The material-artifact stop key: an autonomous turn may stop when BOTH hold —
A material lifecycle artifact shipped since this session's last accepted stop (audit-log-bounded), verified from transcript tool evidence — never self-declared. The v1 key CLASS (both externally verifiable):
manage_pr_reviewresult).(The originally-prescribed third class — an own-PR author-response cycle — was FALSIFIED out of v1 at implementation: no single tool-result shape verifies it externally without prose trust. It re-enters only through the successor recognizer spec recorded in the implementation narrowing on this ticket — never by prose recognition.)
A valid lane-state terminal (unchanged validator — the record still matters; the artifact is the license, the terminal is the handoff).
Blocks remain for artifact-less autonomous turns (today's teeth, unchanged). The #15371 clean-terminal edge REMAINS as the artifact-less fallback (rare, ratcheted — e.g. a genuinely-all-handed-off board after review-only coordination that produced no listed artifact); revisit its retirement only with dogfood data. The directive on artifact-less blocks gains one line naming the key: "ship one material artifact (PR / formal review) — that is the stop key."
Composition with #15401 (the four-quadrant model):
active-laneterminalContract Ledger Matrix
decideStopHookActionautonomous pathai/scripts/lifecycle/stopHookDecision.mjsvalidateLaneStateTerminal.mjssibling (extendcollectLaneStateToolEvidenceFromJsonlfamily).claude/hooks/laneStateStopHook.mjsidentity=(fromNEO_AGENT_IDENTITY) + the newMATERIAL-ALLOWclassidentity=?Acceptance Criteria
MATERIAL-ALLOW(greppable class).identity=; both decision outputs are unit-pinned (ENFORCE allow/block + the non-enforcing WOULD-BLOCK diagnostic carrying the new class) — NO live dry-run rollout tier exists (the #15401 authority fold's contract:NEO_LANE_STATE_ENFORCE=1is the live wiring; WOULD-BLOCK is a unit-pinned diagnostic output, not a rollout phase).Out of Scope
Decision Record impact
none — recalibrates the hook within its own design language;
§contributions_over_commitsrespected by construction (the v1 key class includes formal reviews alongside PRs — never PRs alone; the author-response class is excluded from v1 pending its successor recognizer spec).Related
Live latest-open sweep: latest 10 checked at 2026-07-18T02:56Z (#15401 filing) + hot-context refile 03:00Z; no equivalent. A2A: no competing claim (da405982 is my own #15401 broadcast).
Origin Session ID: abce4d75-7dcb-4145-8afc-b0ff2cdc51e6
Retrieval Hint:
query_raw_memories("material artifact stop key PR review RC-clear since last allow autonomous quadrant")Authority fold, 2026-07-18T09:14Z (ticket author): body folded to the shipped v1 contract per the implementer's disposition record (two artifact classes with the rc-response falsification + successor gate; no live dry-run tier per the #15401 fold; sequenced composition on the merged #15401 seam). The disposition comment on this ticket carries the falsification detail; this fold makes the body match it.