Repository navigation
Description
The health verdict reaches 2 of 5 loop-run readers; the three it misses are the supervisor and the owner
f01d5b4 (#580 AC3) added runHealth so a client would stop inferring liveness. It has exactly two production consumers (grep -rn "runHealth\|isStalled(\|waitClause" workers/api/src workers/mcp/src store | grep -v test → 8 hits, 2 call sites): routes/tools.ts:1046 and work-report.ts:203.
The five readers of agent_loop_runs:
| reader | applies verdict? |
|---|---|
routes/tools.ts:1055,1063 (/loop) |
yes |
agent-think.ts:451 → describeLoopRun |
yes |
tool-registry.ts:231,247 (check_work) |
yes |
connectors/supervision.ts:714 (check_delegation) |
no |
lib/instance-work.ts:223 → subordinate_status |
no |
store/console/src/lib/workInFlight.ts:29 + tabs/LoopRunsSection.tsx:96 |
no |
Three answers, one run, same instant
For a run parked 4.35 h on an engine limit:
check_instance_loop→health:"waiting",waitNote:"WAITING, not stalled and not working…"subordinate_status→activity:"working",quietForMinutes: 261- console "Autonomous runs" (
LoopRunsSection.tsx:152) →step 1/40 · started 4h ago+ a Stop button
Mechanism
subordinate-payload.ts:177and:228—activity = runs.some((r) => r.status === "running") ? "working" : "idle", the raw column.subordinate-observation.ts:152—quietForMinutes: minutesSince(r.lastProgressAt ?? r.startedAt, now), the columnagent-loop-store.ts:69-74explicitly calls "NOT a liveness signal, and it was read as one for two releases".instance-work.ts:233-234does notSELECT last_alive_at, waiting_reason, waiting_until— the supervision path cannot produce a verdict.
The description sells the wrong surface hardest (supervision.ts:550): "What every agent you supervise is doing RIGHT NOW… 'which ones are idle' is roster[].activity."
Two comments are now false
run-sweeper.ts:67— "the same rulesummarizeSubordinatesuses, so the sweeper and the supervisor still agree on 'quiet'". The sweeper usesCOALESCE(last_alive_at, last_progress_at, started_at)(:70);summarizeSubordinatesuseslastProgressAt ?? startedAt.workInFlight.ts:28— "A run is live only while the server saysrunning; every terminal state is a different word." Sincefd1c323,runningcovers working, waiting and stalled.
Why this misleads into a wrong action
run-sweeper.ts:9-11 already names the victim: "subordinate_status reports it as work in flight, so a supervisor keeps waiting on an agent that stopped hours ago." A Coder Lead blocks on a dead subordinate; the owner sees a Stop button for a run that stopped itself.
Acceptance criteria
- Every reader of
agent_loop_runsapplies the same verdict, or explicitly declines to make a liveness claim.subordinate-payload.ts:177is the single highest-value line. instance-work.ts's SELECT carries the columns a verdict needs.- A guard asserts every reader of that table derives health from one implementation — denominator stated per ADR 0002 as readers-found, not a hand-listed set. Two surfaces deriving one fact independently is the A run whose engine died at step 1 reported 'running' for 4.35 hours with a fresh lastProgressAt, and wrote nothing to the trace #580 defect; this is the same defect at a different layer.
- The two false comments are corrected or deleted — a comment asserting an agreement that no longer holds is worse than none.
- Red demonstrated: a parked run must not be reported as
workingbysubordinate_status.
Verified vs inferred
- Verified: the grep and its counts; all six reader call sites; the three live answers; every quoted comment.
- Inferred: nothing load-bearing.
Activity
fix(runs): every reader of a run answers with the platform's verdict,…
serge-ivo commentedon Aug 15, 2026
Done in dc68e2a (with a wording correction in b4d6ea3). CI green on both; API + Host workers deployed successfully.
Readers before and after
The issue named 5 readers. The guard's own sweep — needles over
workers/api/src,workers/mcp/src,store/console/src,agents/coder/web/src,packages/sdk/src— finds 25 loop-run readers over 576 source files. That gap is the point: the reader set is FOUND, not listed.runHealth's verdictstatuscolumnsubordinate-payload.ts×2,subordinate-observation.ts, console)status === "running"for OPENNESSAC by AC
subordinate-payload.ts:177→activityFromRuns(runs), overhealth. Same at:228(rosterLines).subordinate-observation.tsnow callsrunHealth/waitClause— it already owned supervision's judgement, so this is a call rather than a second rule.check_delegation(supervision.ts:714) shipshealth/waitNote/healthLegend. The console reads the field the server was already sending.instance-work.ts's SELECT carrieslast_alive_at,waiting_reason,waiting_until. It genuinely could not form a verdict before; a guard arm asserts all six columns a verdict needs stay in that SELECT.workers/api/src/lib/run-health-readers.test.ts. Denominator per ADR 0002 — 8 needles, every one asserted non-empty (a renamed export fails the guard loudly instead of shrinking it silently), reader count floored at 15 with the reason beside the floor, and the offender set compared EXACTLY so removing the last one fails too. The rule: within the reader set, no file outsidework-report.tsmay put astatus === "running"test and a liveness word on the same line. Openness tests stay legal — that distinction is why the console had one sentence for two questions.run-sweeper.ts:67claimed it shared a rule withsummarizeSubordinates; they read different columns deliberately (work-report.ts:136-141), so the claim was arguing for making one of them worse — it now names the agreement that does hold, withrunHealth.workInFlight.ts:28is gone: the function isisOpen, andrunActivity/activityLabelanswer the other question.runs.some(r => r.status === "running") ? "working" : "idle"→workers/api/src/lib/subordinate-payload.ts:230: out.activity = …. Reverting the observation's verdict → 5 failures includingexpected 'working' to be 'waiting'.A vocabulary change the legends had to state
activitygainswaiting,stalledandunknown; onlyidlenow means an agent is free for work. Each licenses a different next action, and foldingwaitingintoidlewould have been the same over-claim facing the other way.subordinate_status's description and the payload legend both say so.Verified vs not
proagentstore.online/console/contains the four-member vocabulary and the new parked-run label — so the console half is running in production.activityvalue on a real roster.subordinate_statusis an agent-side connector tool — reading it means messaging the instance, which is a state change. Recorded as a non-observation rather than assumed.70ea298e(PAS Coder, ~5.1h) and0cbf401f(Heartfull, ~5.4h) are both sitting innotify-coding-wait.70ea298eis the run this issue was filed about.Closing: every AC is delivered and guarded. If the roster is later observed still saying
workingfor a parked subordinate, that is a new fact and worth reopening on.fix(runs): a park states WHICH kind of deadline it has, so the handof…
fix(pilot): a mid-reply provider drop replays the journal instead of …
fix(state): GET /instances/:id/state says whether a RUN is live, not …
feat(console): what every instance is doing, in one call instead of o…
feat(console): a card says what its instance is doing, and only stall…