feat(#663 ph2): active mcp_probe elicitation — launch directive + watchdog corroboration - #666
Merged
Merged
Conversation
Phase-2 active mcp_probe elicitation, design-stable foundation only: record_probe_request / get_probe_request + _probe_request_ledger, parallel to the success ledger. Tracks that the daemon ASKED an agent to prove its bind at launch (launch_id+nonce); a current-generation request with no matching success is the positive wedge signal that removes the healthy-idle-vs-wedged ambiguity. Cleared on epoch bump like the success ledger. get_probe_request computes current/fulfilled by matching launch_id under the live epoch. Launch-path directive injection (2a) + watchdog missed-probe trigger (2b) held pending Murzik design review. 8 new tests, 52 pass, ruff clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…boration (2b) Per Murzik design review. Removes the healthy-idle-vs-wedged ambiguity that keeps passive detection single-agent-soak-only, by having the agent POSITIVELY prove its MCP client re-bound — without ever letting a missed probe trigger a recovery on its own. 2a — launch-path directive (api._build_streaming_wake_context): For agents with mcp_recover on, inject a top-of-prompt mcp_probe directive (fresh launch_id+nonce) on every delivered (commit=True) launch/wake/restart. Records the request via record_probe_request BEFORE mutating wake_ctx, so a record failure leaves the normal text intact. Eager preview (commit=False) injects nothing and logs no request (its throwaway nonce could never be answered). Fully fail-safe — any error falls back to normal wake text, never blocks a launch. 2b — watchdog corroboration (session_watchdog._evaluate_mcp_bind): The bind-status fn now surfaces get_probe_request. When the existing passive guard (connected + checkable + bound=false + sustained) ALREADY decides to recover, the reason is enriched with the missed-probe detail iff the launch's request is current + unfulfilled + aged past DEFAULT_MCP_PROBE_DEADLINE (120s). Missed-probe NEVER triggers recovery alone, never manufactures checkable, never shortens the deadline, and a generic bound=true makes the agent healthy regardless of the specific directive. Murzik nits folded in: fulfillment requires launch_id AND nonce match (nonce is proof, not telemetry); ledger comment softened to "corroborating". Tests: shared_mcp request-ledger matrix, 2a injection (commit gating + failure fallback + nonce match), 2b corroboration gating. 623 pass across affected suites, ruff clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Phase-2 of #663 (follow-up to #664 mechanism + #665 activation). Removes the healthy-idle-vs-wedged ambiguity that keeps passive detection only safe for a single watched agent: instead of inferring health from prolonged silence, we have the agent positively prove its MCP client re-bound to the current gateway generation — while keeping the conservative rule that a missed probe never triggers a recovery on its own.
Designed with @murzik (his adversarial review is folded in — see below).
Foundation — probe-request ledger (
shared_mcp)record_probe_request/get_probe_request+_probe_request_ledger, parallel to the success ledger, cleared on epoch bump.fulfilledrequires the success to match the request's launch_id AND nonce under the live epoch (the nonce is part of the proof, not telemetry — Murzik nit).2a — launch-path directive (
api._build_streaming_wake_context)For agents with
mcp_recoveron, inject a top-of-promptmcp_probe(nonce, launch_id)directive on every delivered (commit=True) launch/wake/restart. The request is recorded before mutatingwake_ctx, so a record failure leaves the normal text intact. The eager preview (commit=False) injects nothing and logs no request — its throwaway nonce could never be answered, and the connect-timecommit=Truerebuild is the build whose text actually reaches the model (so its nonce matches the registered request). Fully fail-safe — any error falls back to normal wake text; a probe bug can never block a launch.2b — watchdog corroboration (
session_watchdog._evaluate_mcp_bind)The bind-status fn now surfaces
get_probe_request. When the existing passive guard (connected + checkable +bound=false+ sustained-unbound deadline) has already decided to recover, the reason/audit is enriched with the missed-probe detail iff the launch's request iscurrent + unfulfilled + agedpastDEFAULT_MCP_PROBE_DEADLINE(120s). Missed-probe never triggers recovery alone, never manufacturescheckable, never shortens the deadline, and a genericbound=truemakes the agent healthy regardless of whether the specific directive was answered.Murzik review — decisions folded in
wake_ctx. ✅connected + checkable + bound=false + current + unfulfilled + aged; respects post-recover grace + global rate-limit (it's purely additive to the reason). ✅commit=True-only request registration, with the noted caveat thatcommit=Trueisn't proof the prompt reached the model — acceptable precisely because missed-probe only corroborates. ✅One deliberate deviation to confirm: Murzik suggested "use requested_at to arm/advance the unbound clock" alongside the reason enrichment. I did not change the clock this patch — arming from
requested_atwould make recovery fire earlier than the current observation-based deadline, which contradicts his own "keep the effective recovery no more aggressive than the existing machinery unless we explicitly decide to accelerate after soak." So 2b is reason/audit enrichment only; clock-arming is left as an explicit post-soak acceleration decision. Flagging in case you'd rather I add it now.Tests (623 pass across affected suites, ruff clean)
Flag-gated (per-agent
mcp_recover) + fail-safe throughout. Once merged + deployed, barsik (alreadymcp_recover=on) starts getting probe directives as part of its soak.🤖 Generated with Claude Code