LearnNewsExamplesServices
Frontmatter
id13643
titleAgents must emit the fenced lane-state block the Stop-hook parses
stateClosed
labels
documentationenhancementaimodel-experience
assigneesneo-opus-ada
createdAtJun 20, 2026, 6:52 PM
updatedAtJun 20, 2026, 8:20 PM
githubUrlhttps://github.com/neomjs/neo/issues/13643
authorneo-opus-ada
commentsCount2
parentIssue13623
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 20, 2026, 8:20 PM

Agents must emit the fenced lane-state block the Stop-hook parses

Closed v13.1.0/archive-v13-1-0-chunk-4 documentationenhancementaimodel-experience
neo-opus-ada
neo-opus-ada commented on Jun 20, 2026, 6:52 PM

Context

The laneStateStopHook (#13629) + validateLaneStateTerminal (#13627/#13630) + parseLaneState shipped this session and were live-fired sound. During the operator-directed hook-activation push (2026-06-20) two prerequisites surfaced as un-met, making NEO_LANE_STATE_ENFORCE=1 unsafe to flip:

  1. the hook was never wired (→ dry-run wired locally; durable auto-wire = #13641, @neo-opus-grace);
  2. no substrate tells agents to EMIT the fenced ```lane-state JSON block the parser requires ← this ticket.

V-B-A: grep over *.md substrate returns zero references to ```lane-state or laneContinuation. Agents currently end turns with a *prose* lane-state: line (e.g. lane-state: human-gate / no review request), for which parseLaneState returns nullparseOutcomeToVerdict → "no lane-state block emitted" → block. Enforcing today blocks ~every turn → swarm trap. (Confirmed against my own turns earlier this session.)

The Problem

#13623 operationalized the hook side of §no_hold_state (AC1 reason-content, AC2 placement, AC3 teeth-test, AC4 ratio-observability, AC5 coupling). None of its ACs is the agent-emission side — the contract that agents must emit the structured block the hook validates. The format was decided implicitly when the parser was built (parseLaneState.mjs LANE_STATE_BLOCK regex) but never documented as an agent-facing instruction. That seam-gap is why "test the hooks this session" kept missing: the mechanism is non-functional without its input contract.

The Architectural Reality

  • Parser: ai/scripts/lifecycle/parseLaneState.mjs — requires a fenced ```lane-state block with JSON {wakeDisposition, laneContinuation, namedGates, awaitingOwnPrOnly}; returns null when absent (→ block), throws on malformed JSON (→ block).
  • Validator: ai/scripts/lifecycle/validateLaneStateTerminal.mjs — Rule 3 needs per-gate checkedAt + mergedAt-field evidence that prose structurally cannot carry. This is why the fenced block (not the prose line) is the machine seam — parser JSDoc: "the human-readable lane-state: prose line remains, but the MACHINE seam is the block."
  • Emission home (proposed): the turn-boundary skill post-review-pickup (where lane-state is already declared) carries the full contract + worked example; a terse always-loaded pointer in L3/AGENTS.md ensures every-turn visibility (compress-to-trigger, ADR 0007). Exact placement validated via turn-memory-pre-flight at edit-time (turn-loaded byte-cap + skill-load discipline; create-skill PD routing for the post-review-pickup edit).

The Fix

  1. Document the turn-terminal emission contract: every agent ends its turn with a fenced ```lane-state block carrying {wakeDisposition, laneContinuation∈[active-lane,next-lane,blocker-routed], namedGates:[{ref,checkedAt,mergeClaim?,field?}], awaitingOwnPrOnly}.
  2. Compress-to-trigger placement: terse pointer in always-loaded substrate + full contract + example in post-review-pickup.
  3. (coupled) The hook's injected IDLE_REMINDER should SHOW the exact fenced-block format so a blocked agent knows precisely what to emit (today it says "name a lane" but not the machine shape) — small, semantics-preserving laneStateStopHook.mjs content add.

Decision Record impact

aligned-with #13623 (completes the hook operationalization's input side). No ADR change. Couples to #13623 AC5 (teeth-test as discipline) — this adds the emission discipline, still NOT a validator rule.

Acceptance Criteria

  • Substrate documents the fenced ```lane-state emission contract with the full descriptor schema + a worked example.
  • Placement follows compress-to-trigger (terse always-loaded pointer + full contract in post-review-pickup), validated via turn-memory-pre-flight; stays within the AGENTS.md byte-cap.
  • The hook's injected directive shows the exact fenced-block format (a blocked agent is self-serviceable).
  • Dry-run audit log shows WOULD-ALLOW for compliant turns (compliance measurable) before any enforce flip.
  • (post-merge, gated on #13641 + this) NEO_LANE_STATE_ENFORCE=1 no longer blocks compliant turns — verified against the dry-run log.

Out of Scope

  • Wiring the hook into settings (durable: #13641; immediate: dry-run already wired locally).
  • The ratio-observability surface (AC4 of #13623).
  • Codex parity (#13636 @neo-gpt) — Codex Stop is fail-open; the emission contract is family-agnostic but Codex enforcement is its own lane.
  • Flipping enforcement (operator-gated; post-merge).

Avoided Traps

  • Relax the parser to accept the prose lane-state: line (no new emission burden) — REJECTED: prose can't carry the per-gate checkedAt/mergeClaim/field evidence Rule 3 validates; it would gut the validator's evidence-shape discipline. The fenced block exists precisely to carry structured evidence prose can't.
  • Wire enforcing now to "just turn it on" — REJECTED: blocks every non-emitting turn → swarm trap; the dry-run audit must show compliance first.

Related

Parent: #13623 (sub). Grandparent: #13618. Siblings: #13641 (auto-wire, @neo-opus-grace), #13636 (Codex causality, @neo-gpt). Hook PRs: #13629/#13630/#13631/#13633. Live latest-open sweep: checked latest 25 open issues at 2026-06-20T16:50Z; no equivalent found. A2A in-flight claim sweep (30 msgs, all read-states): no competing emission-substrate claim; peer-ceded (Vega/Grace/Euclid). Release classification: boardless (substrate enablement, not v13-release-blocking). Origin Session ID: 95241bfa-5c15-4a48-846b-fe21c869696b Retrieval Hint: "lane-state emission contract fenced block enforce prerequisite NEO_LANE_STATE_ENFORCE post-review-pickup"