fix(execution): remove CC's SCM dependency edges so the monitor watchdog's restart isn't refused (ALP-945) - #350
Conversation
… at the watchdog restart seam (ALP-945) The 2026-06-10 incident: the monitor's event loop froze (ALP-946) and the out-of-process watchdog detected staleness and issued sixteen restarts over 38 minutes — every one refused by the Windows SCM, because AlphaMindCommandCenter declared a DependOnService edge on alphamind-monitor and the SCM refuses a stop control on a service with running dependents. The ~150 s wedge bound ALP-941 built was in practice unbounded. - install_command_center_service.ps1: delete both dependency edges and all supporting prose. The edge's rationale (fail-fast SSE consumers) was never true of the shipped code — the consumers reconnect forever with capped backoff (multiplexer.py) and the control proxy hits upstreams per-request. - process_control.py: _checked_run captures output so CalledProcessError carries the child's stderr; restart() logs that stderr distinctly (the SCM's refusal reason was invisible in the watchdog's own log — it showed only "exit status 1"). Swallow-and-retry behavior unchanged. - install_monitor_service.ps1 + install_safety_core_service.ps1: topology invariant comment — no SCM dependency may target a watchdog-supervised service. - RUNBOOK_production.md §1.4/§1.6/§7/§8.9 + RUNBOOK_command_center.md: no-dependency topology; stop/start orderings reframed as operational convention; §8.9 manual fallback is now a plain stop+start of the monitor with no CC choreography. Also fixed ALP-941 staleness in the CC runbook (seven services, monitor-watchdog in the recovery lists). Deploy note: the live SCM config requires the one-time operator step `sc.exe config AlphaMindCommandCenter depend= /` on the prod box (Scope E). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- _checked_run decodes with errors="replace" so a non-decodable byte in nssm's output degrades to a replacement character instead of raising UnicodeDecodeError in place of the CalledProcessError the watchdog logs (confirmed by repro: strict text=True decode raises before check=True). The real-subprocess test now writes an invalid byte to stderr to guard it. - restart() logs "<no stderr captured>" when CalledProcessError.stderr is None (injected runners that don't capture), instead of a bare "None". - Trimmed the stderr rationale to one canonical site (_checked_run docstring). - De-duplicated the no-dependency prose (RUNBOOK_production §1.4 now points at §7's canonical invariant; CC runbook wiring bullet points at prereq 6). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ALP-941 Monitor wedges on a frozen event loop — in-loop watchdog can't fire, no out-of-process watchdog (ALP-841 recurrence)
SummaryThe This issue fixes the confirmed disease — an unrecoverable loop freeze — and adds freeze introspectability. The trigger of this specific freeze (sustained WAL write-lock contention) is a distinct, not-yet-confirmed defect class spun off to ALP-942; the watchdog here makes a freeze recoverable regardless of cause, so the two ship independently. The exact blocking frame is still open (a 34 MB minidump was captured for offline analysis); the faulthandler change below ensures the next wedge self-captures its frame. Impact (high severity)During the outage the monitor processed no broker fills, so local state diverged from the broker and the ALP-937/938/939 partial-close re-protection never fired. After the 2026-06-09 09:35 ET Detection / symptomA wedged monitor presents as: process alive (NSSM "Running", old StartTime) but Evidence (2026-06-09, prod / HEAD 420d2da)The contention that triggered the freeze (the Root cause (confirmed)
This is exactly the structural gap ADR-0004 closed for the safety core: an out-of-process watchdog reading a file heartbeat across the process boundary makes "the watchdog dies with the loop it guards" unrepresentable. Why it escaped testsThe existing watchdog tests ( Reading
ScopeMirror the safety-core out-of-process watchdog for the monitor, sharing the (generic) primitives rather than duplicating them. The fix converts an unrecoverable silent wedge into a bounded auto-restart regardless of freeze cause, and makes the next freeze self-introspecting. (A) Hoist the generic supervision primitives to a shared package. Move (B) Write an off-hot-path file heartbeat from the monitor supervisor. Inject (C) Add the monitor watchdog entrypoint + subcommand. Add a (D) Add one config knob for the watchdog cadence. Add (E) faulthandler deadman for 3.14 introspectability. Call (F) Install the watchdog as an NSSM service + update the runbook. Refactor Acceptance criteria
Verification
Artifacts (prod box,
|
- RUNBOOK §7: the 42-minute figure is the wedge duration, not the refusal window — reworded so the shipped docs agree. - _checked_run docstring notes the success-path output is discarded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
AlphaMindCommandCenter's SCMDependOnServiceedges (onalphamind-scheduler+alphamind-monitor) from the install script. During the 2026-06-10 incident the SCM refused all sixteen of the monitor watchdog'snssm restartattempts for 38 minutes because CC (a running dependent) blocked the stop — the ~150 s wedge bound ALP-941 built was in practice unbounded. The edge's documented rationale (fail-fast SSE consumers) was never true of the shipped code: the consumers reconnect forever with capped backoff and the control proxy hits upstreams per-request._checked_runcaptures output (errors="replace"so undecodable bytes can't displace theCalledProcessError), andrestart()logsexc.stderrdistinctly — the SCM's refusal reason was previously invisible ("exit status 1" only). Swallow-and-retry semantics unchanged; the seam is shared by both watchdogs.install_monitor_service.ps1andinstall_safety_core_service.ps1now state that no service may declare an SCM dependency on the supervised service.nssm restartworks with everything else running; §8.9's manual fallback drops the stop-CC choreography; §1.4/§1.6 orderings are framed as operational convention. Also fixes ALP-941 staleness in the CC runbook (seven services; monitor-watchdog added to the recovery stop/start lists).Closes https://linear.app/alphamind-jatassi/issue/ALP-945/monitor-watchdog-cannot-restart-the-monitor-in-production-nssm-restart
Deploy notes (operator-executed, prod box, elevated PowerShell)
The live SCM config was written at install time — pulling this PR changes nothing on the installed service. After merge:
sc.exe config AlphaMindCommandCenter depend= /sc.exe qc AlphaMindCommandCentershows an empty DEPENDENCIES list (effective immediately; no service restart required).nssm restart alphamind-monitorexits 0 and the monitor comes back healthy (fresh PID/StartTime, 8766 heartbeat within ~18 s).The fix is not live until step 1 runs.
Test plan
.github/workflows/ci.yml) green on the PR — lint on Linux + full pytest on Windowsruff check,ruff format --check,mypy,lint-imports)uv run pytest tests/execution/process_supervision/ -n auto— 13 passed (incl. the two new stderr criteria)grep -ci depend scripts/install_command_center_service.ps1returns 0🤖 Generated with Claude Code