feat(preflight): pre-session connector preflight with per-slot-type on_degraded policy (connector-resilience Phase 1) - #187
Open
jordanrburger wants to merge 1 commit into
Open
Conversation
…n_degraded policy (connector-resilience Phase 1) Implements Layers 1-2 of the connector-resilience design (#181, docs/superpowers/specs/2026-07-01-connector-resilience-design.md): - scout.scripts.connector_preflight: probes every connector critical for the upcoming slot type before the session launches — MCP connectors via one `claude mcp list` invocation matched verbatim by a new harness_server_name field; CLI connectors via a preflight_command bash probe (gh auth status). Degraded iff any critical connector is determinably down (no quorum knob by design). - connector_policy block in scout-config.yaml: on_degraded skip|warn|run, global default + per-slot-type overrides. Default `run` — no behavior change until users opt in. - Runner integration in all three run-*.sh.tmpl following the budget-check precedent: exit 3 = orderly skip, any other non-zero = fail open. MODE hoisted above the gates in the dreaming/research runners. - Warn seam: .scout-cache/connector-degradation-pending.md consumed by the session phases (banner + no negative signals for dark connectors). - Error handling per spec: fail open on probe errors; inconclusive-streak counter alerts after 3 consecutive inconclusive probes; healthy runs stamp last_healthy_run per slot type for Phase 2 gap windows. Phase 2 (gaps.jsonl + post-session reconciliation, gated on #121) and Phase 3 (/scout-backfill) are intentionally not included. Co-Authored-By: Claude Fable 5 <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.
Implements Phase 1 of the connector-resilience design (#181 —
docs/superpowers/specs/2026-07-01-connector-resilience-design.md): the pre-session connector preflight (Layers 1–2). Phase 2 (gap records in.scout-state/gaps.jsonl+ post-session reconciliation — gated on #121) and Phase 3 (/scout-backfill) are intentionally not included.What's in
scoutctl connectors preflight --mode <slot-key> | --slot-type <type>(engine/scout/scripts/connector_preflight.py) — probes every connector critical for the slot type before the main session launches, at zero model-token cost:claude mcp listinvocation, matched verbatim by a newharness_server_namefield on the connector roster (registry keys/display names can't be mechanically mapped to harness server names, per the spec).preflight_commandfield (github→gh auth status), harness-independent.connector_policyblock inscout-config.yaml(templates/scout-config.yaml.tmpl):on_degraded: skip | warn | run, global default + per-slot-typeoverrides. Shipped default isrun— existing installs see zero behavior change until they opt in. Malformed config falls back with a stderr warning, never crashes the runner.run-*.sh.tmpl, following the budget-check precedent: exit 3 = policy skip → runner logs it and exits 0 (orderly, like a budget skip); any other non-zero exit fails open — a preflight crash never masquerades as a deliberate skip.MODEis hoisted above the gate block in the dreaming/research runners (it was defined after them)..scout-cache/connector-degradation-pending.mdnaming the dark connectors; the session phases (phases/core/00-run-modes.md,phases/modes/kb-deep-work.md) consume it — prepend a degradation banner, record no "nothing found"-style negative signals for dark connectors, delete after consuming..scout-logs/connector-alerts.log+ best-effort Telegram (action_required), swallowing missing-secret/network failures.claude mcp listfailure/timeout/unparseable output → inconclusive, exit 4, runner fails open. A persisted inconclusive-streak counter (.scout-state/connector-preflight-state.json) fires the alert channel once the streak hits 3 — fail-open + glyph parsing means a routine CLI format change would otherwise silently disable the whole protection.last_healthy_run[slot_type]stamped on healthy preflights (same state file) so Phase 2 gap records get accuratefromwindows from day one. Per spec, a degraded run proceeding underwarn/rundoes not stamp it.warn, notskip, for briefing/consolidationThe spec's recommended posture sets briefing/consolidation to
skip. This PR's commented-out recommended block inscout-config.yaml.tmplsuggestswarnfor them instead, deliberately: theclaude mcp listprobe has a known false-positive mode (anthropics/claude-code#44535), and until the Phase-2 reconciliation/gap-tracking safety net exists, a false-positive underskipsilently drops runs with no recorded gap — exactly the "silent loss" this design exists to prevent.warnstill runs the session, banners the degradation, and suppresses false negative signals. Once Phase 2 lands, flipping the recommendation back toskipis a one-line template change. (Note the shipped default isruneither way; this only affects the suggested opt-in posture.) Happy to flip toskipif you'd rather match the spec text as written.Scope notes / follow-ups
skiptrap from phantom/zombie connectors, [BUG] Connectors continue to appear inclaude mcp listeven after disconnected in the Claude.ai web UI anthropics/claude-code#48275): out of scope here; noted as a follow-up — the state file already gives it a natural home.mcp:fathom,mcp:claude-in-chrome,mcp:whatsapp-mcpship without probe fields — they don't reliably appear inclaude mcp list(browser extension / local bridge), and a wrongharness_server_namewould read as permanently degraded. Users can wire them via theconnectors.local.yamloverlay; the fields are overlay-able like the rest of the roster.connectors.snapshot.jsonis unaffected — the cross-repo projection only carries key/display_name/tier (verified with--check).Testing
engine/tests/unit/test_connector_preflight.py— parser fixtures for all four status markers incl. plugin-scoped names (anonymized per CLAUDE.md), unparseable → inconclusive-not-degraded, policy resolution incl. malformed fallbacks, mode→slot-type resolution, degraded classification (any-critical-down; missing-from-list = down; unknowns fail open; bash probes), run() exit codes, warn pending file, skip alert, healthy stamp, streak alert.engine/tests/unit/test_runner_preflight_templates.py— renders each runner template into a tmp vault with stubbed scoutctl/claude and runs it under bash: exit 3 → orderly skip (session never launches), exit ≠ 0/3 → fail open, MODE defined before the gate (verified these fail against the pre-change templates).ruff format --check,ruff check,mypyclean (same commands as CI, fromengine/with.venv/bin/*).warnrun writes the pending file naming the dark connector.🤖 Generated with Claude Code