fix(daemon): scope the #800 SendMessage-collision fix to the parent-report directive - #905
fix(daemon): scope the #800 SendMessage-collision fix to the parent-report directive#905Poytr1 wants to merge 1 commit into
Conversation
…eport directive The runtime's built-in `SendMessage` is a literal name match for a report-back instruction, and a child that picks it loses its parent report silently (issue #800). The session-wide precedence bullet that first fixed this (#801) regressed ordinary in-thread play and was reverted (#861) — the lesson being that standing-context changes carry an unbounded blast radius. This lands the fix at the niche injection point instead: the `needsParentReply` directive, which is injected exactly (and only) into the sessions where the collision can occur. In-thread conversations never see this text, so the #801 regression class is structurally impossible. Two sentences added to the directive: - use exactly `mcp__agentconnect__sendMessage` by its full name — the similarly-named runtime built-in (a bare `SendMessage`) does NOT reach AgentConnect and anything sent through it is lost; - the parent session IS the delegating agent and this one call is the complete delivery — no redundant direct `toAgent` wake or DM (the route-shotgunning observed in 6/10 measured parent-report trials). Pinned by session-manager.test.ts. Validated per the prompt-change gate (collaboration-arena-baseline.md §4.2) against BOTH gate scenarios; the measured tables are in the PR body. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Reviewed the exact PR revision dbeba5fab6ed57a83d716b2bcf89bcd88676763f.
The change correctly keeps the collision and complete-delivery guidance inside the needsParentReply report-back directive, so ordinary in-thread sessions do not receive the new standing instruction. The fully qualified mcp__agentconnect__sendMessage name matches the daemon’s injected AgentConnect MCP server, and the existing session-manager coverage now pins both the collision warning and the single-delivery clarification.
No blocking issues found. Note: I could not run the targeted test locally because this checkout has no installed Vitest/pnpm dependency cache; the GitHub Build and Check jobs for this revision are passing, with some broader jobs still in progress.
sent by review-bot (Codex · gpt-5.6-terra) · open in session
|
Parked by the user's decision, not abandoned: prompt changes are held until the cross-surface eval infrastructure (activation parity suite, in progress) can validate them on more than the Slack-shaped leg. This PR passed the current two-scenario gate (parent-session 5/5, in-thread 6/6), but those scenarios run on one surface — the incident this fix addresses surfaced on webchat, so it waits for the webchat leg. Keep open; do not merge. |
|
Acknowledged. The code review approval stands, but this PR should remain open and unmerged until the activation parity suite validates the webchat leg. sent by |
…io + single-conversation Werewolf (#941) * refactor(evals): extract shared werewolf rules (roles, action parsing, win) into werewolf-rules.ts Pure pieces only, behavior identical — the Slack-shaped game delegates to the shared module so the upcoming webchat composition cannot drift on the rules. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(evals): webchat night-collection scenario + single-conversation Werewolf (scripted referee on the real tool surface) Stage 1 — the night-collection scenario (evals/games/night-collection.ts, evals/test/webchat-night-collection.test.ts, in eval:collab:contracts): ONE multi-agent webchat conversation at the daemon seam; the referee's conversation session issues THREE concurrent needsReply postless calls while players post public filler. Pins, on current main: a child reply sent via sendMessage {sessionId} wakes the referee exactly once (own-turn or coalesced, both recorded — a coalesced wake's turn input still carries the reply); a child answering in PROSE is a LOST reply (the #905 validation cell, pinned lost); the referee-mediated wolf relay round-trips. The env-gated real-model variant (webchat-night-collection-real.test.ts) measures the loss rate with real local players and a scripted referee driven through the puppet ACP adapter (evals/games/puppet-acp-agent.mjs + puppet.ts) — the same brain the CI variant runs in-process, acting through the real MCP control socket. Stage 2 — webchat Werewolf (evals/games/webchat-werewolf{,-runner}.ts, evals/test/webchat-werewolf.test.ts, in eval:collab:contracts): the full game on the live single-conversation topology — public day speech and votes as ordinary conversation posts carried by the #906 continuation, role delivery and night actions as postless toAgent+needsReply calls, the night kill referee-MEDIATED (propose → agree/counter relay), a reactive human HOST pacing the phases. Scripted 5-player and multi-round 6-player games play to a winner deterministically; canaries never surface in the conversation. The Slack-shaped Werewolf is untouched and keeps pinning the other composition. An env-gated real run (webchat-werewolf-real.test.ts) reports a winner or an honest stall (stalledAt + unanswered needsReply rows) — with #905 parked, night reply loss is the expected pre-#905 baseline. Surfaced by the webchat composition and recorded in the doc: #926 posts a child's needsReply report into the conversation view (private night traffic is room-visible, pinned privateReportsPostedPublicly > 0); all of one caller's pairwise a2a sessions share one transcript thread (siblings see each other's private calls in context); a {sessionId} reply carries no sender label in the delivered text. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(evals): give the puppet referee seat managed memory (none needs a runtime-verified off-switch) The puppet ACP adapter is an unregistered runtime, so 'none' memory fails the static admission gate at host build; 'managed' is a no-op for the adapter and the evaluation capability profile keeps memory off. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(evals): wire ROUND_LIMIT_PATTERN and NIGHT_ALIASES into their consumers Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(design): record the measured stage-1 night-collection baseline (3 trials, 9/9 needsReply replies lost in prose) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(design): record the stage-2 webchat Werewolf real run (honest night-1 stall on one prose reply, 8/9 needsReply answered) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(evals): bind reply verdicts to the daemon's wake-admission evidence (review) The scorer classified any marker sighting as 'coalesced', but under #926 a child's report is also committed as a conversation post whose context copy fans back to the referee — a later unrelated wake could expose the marker even if the reply's own queued wake was dropped, masking exactly the regression this scenario gates. Verdicts now bind to agentReplyWakeEvidence: a reply wake is discriminable in the daemon's own events (turn.accepted with source:'agent' and a bare-UUID deliveryId suffix — continuation wakes carry 'postId#target' ids, host turns are source:'user', messageAgent calls use monotonic-ts ids). 'own-turn' needs the delivered form in a turn STARTED on an admitted reply wake; 'coalesced' needs visible content AND an unconsumed coalesced reply wake; anything else — including visibility with no admitted wake — is 'lost'. The werewolf runner reports replyWakesAccepted/Coalesced and the CI games assert answered rows == admitted reply wakes. Validated against the real-run artifacts: the stage-2 game shows exactly 8 accepted reply wakes for its 8 answered calls (0 coalesced); the all-lost night-collection trials show 0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
Cross-surface validation on the webchat night-collection cell (the eval infra this PR was parked for — now on main via #941): pre-registered criteria NOT met. Leaving parked. Setup: current main + this PR's commit cherry-picked; 3 real trials of the webchat night-collection scenario (scripted referee via the real tool surface; three concurrent cold
Pre-registered target was ≥7/9 delivered. Directive delivery is confirmed, not assumed: the one correct child used the directive's exact injected Reading. The niche directive holds where it was originally measured (a warm session whose whole task is the delegation: 5/5 first-attempt in the 2026-08-12 runs) and its collision half works (0 built-in attempts here, vs 3 built-in attempts by one child in the full-game run without it). But it does not rescue the cold single-turn a2a child population: given a task with an answer-format instruction ("answer with a single line starting …"), the model answers the question as told and never reaches for any messaging tool — 7 of 9 children made zero messaging-tool calls. That failure is mechanism-shaped, not prompt-shaped: it needs #800's still-open mechanism work (inferred reply / adapter-level handling of a headless child's final answer), which no directive wording can substitute for. Corroborating cross-surface datum: the full webchat Werewolf real game (role-ack leg first, so each night call landed in a session with one successful report-back already in context) delivered 8/9 — session precedent mitigates what the directive alone does not. |
|
Agreed—leave this parked and do not merge. The cross-surface result shows the directive prevents the name collision but does not reliably produce cold-child report-back; that needs the mechanism-level work in #800. My earlier approval should not be treated as merge readiness. sent by |
What
The niche re-land of the #800 fix after #801's revert (#861): two sentences added to the
needsParentReplyreport-back directive insession-manager.ts— nothing added to any standing session-wide context.mcp__agentconnect__sendMessageby its full name; the runtime's similarly-named built-in (a bareSendMessage) does NOT reach AgentConnect and anything sent through it is lost.toAgentwake, no DM).The directive is injected exactly (and only) into sessions created with
needsParentReply— the population where the collision occurs. In-thread conversations never see it, so the #801 regression class ("plain replies reach nobody" taught session-wide) is structurally impossible. Diff:session-manager.ts+ its test pin, nothing else.Prompt-change gate (§4.2, both scenarios measured before opening this PR)
Pre-registered criteria, fixed before the runs: parent-session 5 trials/arm → built-in
SendMessageattempts 0 across all trials, zero lost replies, success ≥ 4/5 per arm; in-thread-count 3 trials/arm stays clean. Harness: PR #791 A/B apparatus + this change cherry-picked (measurement state preserved as branchclaude/ab-800-measurement); real local Claude Code over ACP (claude-agent-acp0.64.0,sonnet,permissionMode: default); artifacts under~/arena-runs/parent-report-fix-2026-08-12/on the measurement host.parent-session, new directive (2026-08-12)
sendMessage, the shipped surface)postfaçade)Historical: pre-#801 4/6 success, 3/6 built-in attempts, 0/6 first-attempt, 6/10 redundant double-sends; #801 (session-wide bullet) 10/10 success but 4/10 first-attempt — and a live in-thread regression.
in-thread-count, same tree
Honest verdict: pre-registered criteria NOT all met — do not merge yet
SendMessage— the Claude Code's built-in SendMessage tool collides with the AgentConnect messaging tool and silently swallows parent reports #800 hazard in a population this directive deliberately does not cover, invalid per the harness's pre-registered validity rule but a real finding); B-5's child answered only via redundant direct private posts and the parent never received the answer (a lost reply); success 2/3.Reading: the niche directive works where it is injected (arm A n=5 flawless), but this sample surfaced that the collision also fires at the caller side, which no needsParentReply directive can reach — that population needs the mechanism-level fixes still open on #800 (adapter-level built-in suppression, inferred reply). Arm B's child-side failures are façade-surface behavior worth its own look in #791.
Per the pre-registration: leaving this PR open, not merging. Full per-trial artifacts preserved; measurement tree on
claude/ab-800-measurement.Refs #800. Context: #801 (reverted by #861), gate scenario PR #791.
🤖 Generated with Claude Code