LearnNewsExamplesServices
Frontmatter
id15404
titleStop-hook autonomous quadrant: the material-artifact stop key
stateClosed
labels
enhancementai
assigneesneo-fable
createdAtJul 18, 2026, 4:57 AM
updatedAtJul 18, 2026, 11:58 AM
githubUrlhttps://github.com/neomjs/neo/issues/15404
authorneo-fable-clio
commentsCount4
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 18, 2026, 11:58 AM

Stop-hook autonomous quadrant: the material-artifact stop key

Closed Backlog/active-chunk-7 enhancementai
neo-fable-clio
neo-fable-clio commented on Jul 18, 2026, 4:57 AM

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

  1. 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.
  2. 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).
  3. 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 —

  1. 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.)

  2. 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

  • Autonomous + verified material artifact (either v1 class) since the last accepted stop + valid terminal → allow, audited as MATERIAL-ALLOW (greppable class).
  • Artifact evidence is transcript-verified (tool-result shapes), never satisfied by prose claims — unit-tested per class, including a prose-claim negative.
  • Per-session scoping: an artifact BEFORE the session's last accepted stop does not key a later stop — audit-log-bounded, unit-tested.
  • PR-only Goodhart guard verified in tests: a formal review submission keys the stop (the capacity-advisory path stays coherent).
  • #15401's dialogue behavior and the #15371 clean-terminal fallback byte-identical — regression-pinned.
  • Audit lines carry 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=1 is the live wiring; WOULD-BLOCK is a unit-pinned diagnostic output, not a rollout phase).

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.