LearnNewsExamplesServices
Frontmatter
id15216
titleStop hook: attachment-delivered mid-turn operator messages stay invisible to the human-filtered walk
stateClosed
labels
bugaiarchitecture
assigneesneo-opus-vega
createdAtJul 16, 2026, 9:37 AM
updatedAtJul 16, 2026, 4:48 PM
githubUrlhttps://github.com/neomjs/neo/issues/15216
authorneo-opus-grace
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[x] 15233 Stop-hook deference lexicon: exempt correct role-attribution in human-only domains
closedAtJul 16, 2026, 4:48 PM

Stop hook: attachment-delivered mid-turn operator messages stay invisible to the human-filtered walk

Closed Backlog/active-chunk-6 bugaiarchitecture
neo-opus-grace
neo-opus-grace commented on Jul 16, 2026, 9:37 AM

Context

PR #15210 (merged 2026-07-16 07:32Z, Resolves #14440) shipped mid-chain operator visibility: extractLatestHumanUserTextFromJsonl walks the transcript for the newest mechanically human-shaped user record, and classifyPromptingContext counts an attested mid-chain dialogue candidate as operatorInLoop. That fix is correct for its fixture corpus — operator messages that START a turn inside a forced-continuation chain land as genuine user-role records and are now rescued.

Operator direction (live session, same day): create the follow-up for the remaining shape.

The Problem

Claude Code delivers mid-TURN operator messages ("The user sent a new message while you were working") as type: 'attachment' records, not user-role records — so the walk cannot see them by construction, and a session whose operator interacts primarily mid-turn keeps looping exactly as before the fix.

Live falsifier (session transcript 90c8bdb2-d5c7-4231-85b3-5271aaf109ec.jsonl, 2026-07-16):

  • The operator's mid-turn scope-rulings message exists ONLY as: two type: 'queue-operation' records (enqueue envelopes, no role) + one type: 'attachment' record carrying the text at record.attachment.prompt (line 407). Zero user-role records carry it.
  • Traced consequence on the same session's six consecutive hook fires (forensics on the closed #14440, comments 4989142471 / 4989282036): at each refused terminal the newest non-meta USER record was a [WAKE] delivery, so midChainOperator stays false and the terminals still BLOCK post-merge — while the operator was actively messaging mid-turn throughout.
  • Also verified live, both clean (no action needed): Stop hook feedback: records are consistently isMeta: true (5/5 — the merged discriminator holds), and <task-notification> deliveries never materialize as user-role records (no false-ALLOW channel).

Same-family review with the full trace: PR #15210 review 4711437374.

The Architectural Reality

  • .claude/hooks/laneStateStopHook.mjsextractLatestHumanUserTextFromJsonl filters on (message.role || record.type) === 'user', skips isMeta: true, text-less, and harness-marker records; FIRST remaining candidate decides (newest-candidate rule — this ordering must survive the extension, or stale attachments could leak past fresher [WAKE] boundaries).
  • ai/scripts/lifecycle/stopHookDecision.mjsisOperatorDialogueText is the single classification authority for BOTH paths; the extension must route attachment text through it unchanged (no second authority).
  • The promptingTextHumanFiltered attestation contract: the Claude adapter attests its extraction mechanically excluded harness-injected records. Attachment records need the same discipline audit: my live instance carries no isMeta and IS genuine operator prose, but other attachment KINDS (file attachments, pasted content?) may exist — the gate should be presence of attachment.prompt (the queued-user-message field), and a small corpus check across real transcripts should confirm no harness-injected record ever carries attachment.prompt.

The Fix

  1. Extend the walk (or add a sibling considered in the same backward pass, preserving strict transcript-position ordering) to accept type === 'attachment' records with a non-empty string attachment.prompt, returning that text as the candidate.
  2. Classification stays in isOperatorDialogueText — a [WAKE]-shaped or synthetic attachment (if any exist) fails exactly like a user-record equivalent.
  3. Fixture corpus: mirror the real record shapes (queue-operation envelope skipped by shape; attachment carrying attachment.prompt; the line-407 chain). The originating session transcript is available on request as the reference corpus.
  4. Specs: mid-turn attachment beneath hook-feedback records → found; attachment ordering vs newer [WAKE] user record → the newer wake wins; attachment.prompt-less attachment records (other kinds) → skipped; end-to-end spawned-hook ALLOW on the attachment chain shape.

Acceptance Criteria

  • A forced-continuation chain whose newest human evidence is an attachment-delivered mid-turn operator message classifies midChainOperator: true → ALLOW (end-to-end spawned-hook spec).
  • Newest-candidate ordering holds across record kinds: a [WAKE] user record NEWER than an operator attachment keeps the turn autonomous (spec-pinned).
  • Attachment records without attachment.prompt never become candidates (spec-pinned).
  • Classification authority remains isOperatorDialogueText — no duplicated gates.
  • Corpus note in the PR: real-transcript check that no harness-injected record carries attachment.prompt (or the discriminator that excludes those that do).

Out of Scope

  • Codex adapter semantics (stays fail-closed without attestation, per the merged design).
  • Any re-litigation of the merged turn-start rescue (shipped, correct).

Decision Record impact

none (hook substrate; #14440 lineage).

Related

Predecessor: #14440 (closed by PR #15210) · same-family review carrying the trace: PR #15210 review 4711437374 · forensic timeline: #14440 comments 4989142471 / 4989282036.

Live latest-open sweep: checked latest 20 open issues at 2026-07-16 ~07:37Z; no equivalent found. A2A in-flight claim sweep: mailbox clean of overlapping claims at file time. Structure-map gate: N/A — extends existing hook + decision module in place.

Origin Session ID: 75ed6708-c66b-4989-862d-2286e87abbf1

Retrieval Hint: "stop hook attachment prompt mid-turn operator visibility human-filtered walk"

tobiu referenced in commit 657d6aa - "fix(hooks): surface attachment-delivered mid-turn operator messages to the stop-hook walk (#15216) (#15222) on Jul 16, 2026, 4:48 PM
tobiu closed this issue on Jul 16, 2026, 4:48 PM