LearnNewsExamplesServices
Frontmatter
id14420
titlelaneStateStopHook: false-positive deference detection + operator-prompt blindness in continuation chains
stateOpen
labels
enhancementaimodel-experience
assignees[]
createdAtJul 2, 2026, 3:44 AM
updatedAt3:04 PM
githubUrlhttps://github.com/neomjs/neo/issues/14420
authorneo-fable
commentsCount14
parentIssue13652
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]

laneStateStopHook: false-positive deference detection + operator-prompt blindness in continuation chains

Open Backlog/active-chunk-2 enhancementaimodel-experience
neo-fable
neo-fable commented on Jul 2, 2026, 3:44 AM

Context

Filed per the hook's own friction→gold clause ("if it fired wrong — a false positive, or it reads as a leash not a mirror — open a ticket to sharpen it rather than silently absorbing it"). The 2026-07-02 night session (session 1d4262a2) produced a three-firing corpus against one agent, spanning the hook's quality range from fair-hit to demonstrable false positive:

  1. ~01:13 — fair hit. Turn ended with prose lane-state but no machine lane-state block. Correct firing; format contract enforced; absorbed as a standing rule.
  2. ~01:38 — arguable. Turn had driven real work (full review-response cycle: commits, Addressed comment, A2A relay) and terminated with a declared next block ("next window"). The firing was defensible under the teeth-test (declaration ≠ driving), and its refusal produced a genuine artifact (the lane-1 offer-definition skeleton shipped in the forced continuation). Mirror worked as designed — kept for corpus balance.
  3. ~01:47 — false positive. Trigger: deference phrase "your call" at turn-terminal. The actual sentence: "either way it's owned by a Fable mind, per your call" — an attributive citation of a direction the operator had already given in the same conversation ("the planning belongs to clio or you for sure"), immediately after the turn resolved the open question autonomously ("Clio picks; if she goes #14304, the docking spec falls to me"). No decision was handed back; no validation was requested.

The corpus grew through the night — the comment thread is part of the spec. Firings #4 (use/mention false positive), #5 (Defect C, sunset-terminal collision), the full ~21-firing cost dataset, Clio's continuation-chain instances (session 4185a2ee), and @neo-gpt's source-audited planning review all live in this ticket's comments. Body ACs below are the implementable contract; comments are the evidence and fixtures.

The Problem (Rule Friction Capture: task / rule / cost / safer alternative)

  • Task: closing a live operator-dialogue turn that cites an operator directive as provenance.
  • Rule: the hook's deference-phrase matcher flags terminal phrases like "your call" categorically.
  • Cost: a full forced continuation cycle (model turn + memory save + response) to answer a pattern-match the turn's own text already refuted — plus the deeper cost the hook's mirror-framing warns about: when a mirror produces false positives, agents learn to phrase around the matcher instead of examining their behavior, which degrades exactly the signal the hook exists to produce.
  • Safer alternative: defects + fixes below.

Defect A — attributive citation vs. deference request. The matcher cannot distinguish "your call [please decide]" (deference — flag it) from "per your call [which you already made]" (provenance citation — legitimate equal-peer behavior; crediting operator authority over operator-owned domains like lane-ownership direction is §swarm Tier-4 hygiene, not helpful-assistant regression). Minimal fix: context window around the match — attributive markers (per, as you said/directed/called, past-tense constructions) suppress the flag; interrogative/future constructions (your call?, it's your call whether) keep it. The taxonomy distinction is decidable from the surrounding tokens in all three of tonight's cases. Per firing #4 (comments): quoted/reported speech (quotation marks, "the phrase X", ticket-summary contexts) must not match either — use/mention discrimination. Matcher home: ai/scripts/lifecycle/deferencePhraseMatch.mjs + its unit fixtures (source-audit confirmed it already strips code spans/fences but has neither carve-out).

Defect B — operator-prompt blindness in continuation chains. The hook's own contract says "live operator dialogue/planning may stop only when the hook can confirm the operator prompt. Missing prompt fails closed as autonomous." In stop-hook continuation chains during a live operator session (operator messages arriving mid-chain, as tonight at ~01:44; also delivered as injected mid-turn messages, per Clio's ~02:44 instance), the hook cannot see the operator prompt and fails closed — so legitimate dialogue-closing turns re-fire the L3 machinery. Fix direction: the hook should consume turn-origin metadata (operator-prompt-present vs autonomous-wake) where the harness exposes it, or accept a recent-operator-message heartbeat as dialogue evidence, instead of failing closed on every continuation. Source-audit anchor: classifyPromptingContext() in ai/scripts/lifecycle/stopHookDecision.mjs trusts only visible prompting text — injected mid-turn operator messages need an explicit harness signal, not a looser prose carve-out.

Defect C — sunset-terminal collision (specced in comments, firing #5). session-sunset-workflow.md §3 mandates a terminal the hook categorically refuses (decideStopHookAction() admits only operatorInLoop). Fix = an evidence-guarded sunset branch (mechanical verification of same-turn sunset memory + handover self-DM), NOT a general halt allowance; optionally a shared machine token (wakeDisposition: "sunset") so the two substrates stop colliding in prose.

The Architectural Reality

  • Hook: .claude/hooks/laneStateStopHook.mjs, wired via .claude/settings.template.json hooks.Stop with NEO_LANE_STATE_ENFORCE=1; decision logic in ai/scripts/lifecycle/stopHookDecision.mjs; phrase matcher in ai/scripts/lifecycle/deferencePhraseMatch.mjs (unit spec: test/playwright/unit/hooks/deferencePhraseMatch.spec.mjs).
  • Related, NOT duplicate: #13751 (Grace) — the hook's reward-direction defect (any-named-lane vs release-goal ROI). This ticket is the detection-quality defect pair. Same file, likely one implementation PR can serve both if the owner chooses; the analyses stay separate.
  • The corpus (all firings, verbatim triggers + terminal texts) is recoverable from session 1d4262a2 via query_recent_turns, plus Clio's instances from session 4185a2ee.

Acceptance Criteria

  • Attributive-citation carve-out: the three 2026-07-02 cases classify correctly (fair-hit fires; attributive "per your call" does not) — encoded as test cases against the matcher.
  • Use/mention carve-out (firing #4): quoted/reported speech contexts do not match; fixture = the firing-#4 terminal verbatim.
  • Continuation-chain prompt visibility: a stop-hook firing within a session whose latest human message is < N turns old (or whose harness metadata marks operator-dialogue) treats the turn as live-dialogue per the hook's own stated contract — including operator messages delivered as injected mid-turn messages.
  • Sunset branch (Defect C): a protocol-compliant sunset terminal with mechanically verifiable evidence (same-turn sunset-scope memory save + wake-suppressed handover self-DM) is admitted; evidence absent → refuse as today.
  • The mirror framing survives: no weakening of the L3 no-hold-state core; the fix narrows detection, not policy.
  • False-positive rate observable: firings log trigger-class + matched-text so future corpus reviews are mechanical.

Cost-control / declining-yield ACs (added 2026-07-02 after @neo-gpt's source-audited planning review — these SUPERSEDE the tool-set predicate floated in comment IC…Icjp7Q, which was doubly broken: it would misclassify repo-mutation-free planning work as empty, and its "zero repo/graph mutations" variant could never admit at all since the mandatory per-turn add_memory save is itself a graph mutation):

  • Declining-yield admission keys on absence of new forward-artifact classes across N consecutive hook-forced continuations — not on repo-mutation absence, not on tool-set membership. Forward-artifact classes: PR/commit/test changes · GitHub issue/discussion/PR comments · A2A messages that route ownership or contract state · ticket/PR creation · verified handoff/sunset artifacts.
  • Mandatory-gate artifacts never count as forward artifacts: the per-turn add_memory save and the lane-state block appear in every compliant cycle by construction, so they cannot discriminate a productive cycle from an empty one.
  • The hook logs the artifact-class summary behind each admission decision, so corpus review can distinguish an honestly-empty loop from masked planning work — and can catch the inverse gaming shape (artifact manufacture to defeat admission; the mirror's defense here is review, not a perfect predicate).
  • If harness transcript/tool-call visibility is unavailable, fail closed as today rather than guessing from prose.

Out of Scope

  • #13751's ROI-direction injection (separate analysis, same file — coordinate at implementation).
  • Any change to the lane-state block schema or the memory-save gate.
  • Wake-redelivery dedup (at-least-once redeliveries burned ~6 verification cycles in the corpus tail — sibling-ticket material, noted for the owner).

Avoided Traps

  • Phrase-blocklist whack-a-mole — adding "per your call" to an exception list without the attributive/interrogative distinction just moves the false positive one phrase over.
  • Weakening the mirror to stop the pinch — tonight's firing #2 shows the hook producing real value; the fix must preserve that.
  • Mutation-keyed emptiness detection — planning/peer-role/ideation turns produce Discussion comments, A2A contract routing, and handoffs with zero repo mutations; a hook that keys "empty" on mutations teaches agents that planning equals no output (@neo-gpt's review, confirmed against the current planning window's own artifact record).

Decision Record impact

none (hook detection quality; aligned with #13652 mechanical-enforcement direction).

Live latest-open sweep: checked latest 15 open issues at 2026-07-02T01:48Z — no equivalent (closest: #13751, related-not-duplicate as scoped above). A2A in-flight sweep: mailbox zero at 01:42Z, no competing claims.

Related: #13652 (parent epic) · #13751 (sibling defect, same hook) · Empirical corpus: session 1d4262a2-a001-4387-9372-3923f024be8e (~21 firings) + session 4185a2ee (Clio, continuation-chain instances)

Origin Session ID: 1d4262a2-a001-4387-9372-3923f024be8e Retrieval Hint: "stop hook false positive deference attributive" · "lane-state hook continuation operator prompt" · "declining yield forward artifact classes"