nyx supervisor incident + hardening report · 2026-07-18

Off watch.

You used to run a 25-minute loop in a chat window: check on the queue, see if it stalled, fix it, kick it, repeat. That job now belongs to the machines. This page covers what the supervisor is, the three ways it failed to do that job this week, and the changes that landed today so the watching happens without you.

The 25-minute ritual · retired 2026-07-18
  • Check if the queue is still moving
    now: supervisor tick, every 60 to 120s
  • Notice it's re-doing the same work again
    now: marathon_livelock detector
  • Kill the runaway thing burning tokens
    now: auto-stop via /epic/:id/stop
  • Unpause the item a crash knocked over
    now: queue auto-retry, salvage-first
  • Restart the daemon when it stops answering
    now: deadman + confirmed-hung kickstart

Your remaining role: read one Telegram card when something critical genuinely needs a decision.

01 · WHAT IT IS · tools/supervisor

A watcher outside the thing it watches

The supervisor is a separate process, deliberately outside the moderator daemon, kept alive by launchd. Every 60 to 120 seconds it runs nine detectors against real state (the moderator's own database, process tables, health endpoints, git), writes an audit entry, takes recovery actions where policy allows, and pages Telegram only on new critical findings. A file lease guarantees exactly one supervisor runs at a time.

DetectorWatches forOn finding
moderator-healthdaemon down, crash-looping, hung, degraded-stuckdown/crashloop: restart immediately. Hung: restart after 3 confirmed ticks; live work defers it, but a 10-tick ceiling restarts anyway, capturing a CPU sample first
stalled-incrementa running increment with no live worker and stale commitscancel the dead worker's plan; the controller re-pends it under its retry budget
wedged-epican active epic idle 30+ min with nothing committedescalate (deliberate: with nothing running, a wrong automatic guess is worse than a page)
failed-mergeverified work stranded off the target branchescalate
zombie-workersorphaned worker processestargeted SIGTERM of the proven-terminal pid
broken-toolchainmissing binaries (the node_modules pollution class)escalate
mercury-healthDiscord disconnects, auth failures, stale progressfreeze the one affected capability, never a full restart
self-healthits own audit or recovery hooks failingescalate
livelock · NEWthe same increment dispatched 6+ times in 3hstop the runaway epic + page you
02 · WHY IT WASN'T DOING ITS JOB

Three failures, found and fixed same-day

228
duplicate-instance exits while the lease pointed at a dead pid
22h
livelock it watched without seeing (111 re-dispatches)
321
moderator-hung detections across the week; the hangs kept recurring
206
of 206 supervisor tests green after the fixes
Failure 1: it often wasn't running at allFIXED · 2d92d1f4

When a supervisor process got killed, its lease file still looked fresh. Every 10 seconds launchd spawned a replacement, which saw a "live" lease, logged duplicate_supervisor, and exited. Up to a full 10-minute TTL could pass with nobody on watch, and the cycle repeated for a day. Fix: a new instance now probes the recorded holder pid; a dead same-host holder is stolen from instantly. Gap shrinks from 10 minutes to 10 seconds, regardless of what killed the holder.

Failure 2: it was blind to the failure mode that matteredFIXED · 2d92d1f4

Every detector keyed on absence of activity: dead process, stale commits, idle epic. The 22-hour livelock had live workers, fresh commits, and completing plans, so it scored as healthy on every tick. Fix: the livelock detector reads the marathon's own dispatch ledger, where repetition-without-progress is unmistakable, and its recovery stops the runaway epic through the moderator's sanctioned endpoint. The daemon is healthy in a livelock; the epic is the patient.

Failure 3: the layer below it turned every hiccup into a human gateFIXED · c1b57f11

When a daemon restart killed a just-started plan, the queue's policy was: mark the item failed, pause it, alert you, and stop the whole chain. That is precisely the "it keeps stopping and waits for me" experience. Fix: a failed plan now auto-retries in place, twice, salvage-first, before it is allowed to pause. Partial-honesty and no-progress verdicts still pause, because those deserve eyes.

03 · THE WATCH STACK NOW

Who watches whom

Defense in depth, innermost first. Each layer assumes the ones inside it will sometimes fail.

LayerWatchesActs by
marathon controllerits own verify gates and retriesbounded retry budgets on every failure path, then fail OPEN with a loud audit row, never a loop (99f8f72e)
queue runnereach dispatched plan's verdictauto-retry failed plans twice, salvage-first, before pausing (c1b57f11)
supervisormoderator, marathons, Mercury, workers, toolchain, itself, livelockscapability freezes, confirmed-hung restarts, runaway-epic stops, Telegram pages (2d92d1f4)
deadman + launchdthe daemons and the supervisor as processesrespawn on exit, restart on failed health, KeepAlive
youone Telegram cardonly genuine decisions: money, releases, rejected designs

The result observed live today: the moderator hung twice, the deadman bounced it, the queue's new retry re-dispatched the killed plan, and ME2 continued with no human touch. That exact sequence required three manual interventions yesterday.

04 · STILL ON THE RADAR

Honest remainders

The first version of this page listed three. Two were closed the same evening (commit cda4dfda): stalled increments and zombie workers now heal instead of paging, and hung restarts got a max-defer ceiling (the active-work guard counted pending queue rows as active, so a hung daemon on a box with a standing queue could defer forever: 321 detections, zero recoveries). Every hung-restart now also captures a 5-second CPU sample into data/diagnostics/ first, so the evidence stops dying with the restart. What genuinely remains:

If you ever want to look anyway