Skip to content

feat(#663 ph2): active mcp_probe elicitation — launch directive + watchdog corroboration - #666

Merged
olegbrok merged 2 commits into
mainfrom
feat/663-phase2-active-probe
Jun 3, 2026
Merged

feat(#663 ph2): active mcp_probe elicitation — launch directive + watchdog corroboration#666
olegbrok merged 2 commits into
mainfrom
feat/663-phase2-active-probe

Conversation

@olegbrok

@olegbrok olegbrok commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

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. fulfilled requires 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_recover on, inject a top-of-prompt mcp_probe(nonce, launch_id) directive on every delivered (commit=True) launch/wake/restart. The request is recorded before mutating wake_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-time commit=True rebuild 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 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 whether the specific directive was answered.

Murzik review — decisions folded in

  • 2a yes; 2b corroborating-only inside the passive guard, never an independent kill switch. ✅
  • Directive on every launch/wake/restart, behind the flag, at the very top of wake_ctx. ✅
  • Missed-probe gated behind 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 that commit=True isn't proof the prompt reached the model — acceptable precisely because missed-probe only corroborates. ✅
  • No same-launch auto re-request (force-fresh naturally issues a new launch_id/nonce). ✅
  • Fulfillment requires launch_id and nonce; comment softened to "corroborating". ✅

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_at would 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)

  • shared_mcp: request-ledger matrix (current/fulfilled, nonce-match, epoch-clear, blank-agent).
  • 2a: commit=True records exactly one request w/ fresh uuid+nonce matching the directive; commit=False injects nothing + no request; mcp_recover-off → no directive; record failure → normal text + no request.
  • 2b: recovery reason includes missed-probe only when connected/checkable/bound=false + current+unfulfilled+aged; omitted when fulfilled or not-yet-aged; missed-probe alone never recovers when not checkable; bound=true ignores a missed probe.

Flag-gated (per-agent mcp_recover) + fail-safe throughout. Once merged + deployed, barsik (already mcp_recover=on) starts getting probe directives as part of its soak.

🤖 Generated with Claude Code

Oleg and others added 2 commits June 2, 2026 19:29
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>
@olegbrok
olegbrok merged commit c6cf905 into main Jun 3, 2026
11 checks passed
@olegbrok
olegbrok deleted the feat/663-phase2-active-probe branch June 3, 2026 02:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant