The engine plane cannot be read truthfully: capture strips the disambiguating evidence, advertises impossible values, and diagnostics calls a refusing engine healthy
Three defects on one question — is the engine actually working? — which is the half of #580 that was never closed.
1. MCP strips exactly the fields that make idle falsifiable
workers/mcp/src/index.ts:854 projects the response to {sessionId, runState, pane}, dropping runnerConnected, alive, ready, authPrompt.
The API returns runState: "idle" for three different worlds and disambiguates with precisely those siblings: routes/coding.ts:234 (no connection) and :240 (runner never answered) both collapse to idle. So over MCP, "the engine is idle", "the runner is gone" and "the probe failed" are one indistinguishable answer.
2. The tool advertises two values it cannot emit
index.ts:840 — "Also returns run state (idle/working/offline)."
The runner's vocabulary is "idle" | "thinking" | "responding" (packages/browser-runner/src/coding/runtime.ts:82). working is not a member, and offline is manufactured only by the timeline route (routes/coding-feed.ts:82), never by /capture.
coding-feed.ts:74-85 has the corrected vocabulary (ended/offline/unknown) with an explicit comment that unknown must not collapse into idle — so two surfaces sit over one probe with two vocabularies, and the older one is the one MCP exposes.
3. coding_diagnostics reports 0 issues for a stalled engine
Measured live on bd43f4de: healthySessions: 1, issueCount: 0, issues: [], live.alive: true, runState: "idle" — while the captured pane's last two lines are You've hit your weekly limit · resets Aug 17 at 4pm and [error].
The same instance's board says "Needs you" and its run says engine_limit. Both are right. The issue rule (lib/coding-diagnostics.ts:245-251) fires only on runner/relay faults, so an engine that is up but refuses to work is invisible to the tool whose stated purpose is "debug why sessions are offline or stuck".
Why this misleads into a wrong action
An owner debugging a stuck agent is sent to the tool named for it, told everything is healthy, and given a runState that cannot distinguish "thinking hard" from "the machine is gone". This is the exact question that cost 4.35 hours on 2026-08-15.
Acceptance criteria
coding_session_capture carries enough to distinguish engine-idle, runner-absent and probe-failed. If payload size is the reason for the projection, say so and choose deliberately — index.ts:854 currently drops them silently.
- The description names only values the code can emit. A guard asserts the advertised vocabulary equals the emitted one, denominator stated per ADR 0002 over every tool that publishes a state enum — this is the third such mismatch found today.
- One vocabulary over one probe:
coding-feed.ts:74-85 and routes/coding.ts:234,240 agree, and unknown never collapses into idle.
coding_diagnostics reports an engine that is up but refusing as an issue, with the reason. issueCount: 0 beside a board card reading "Needs you" is the defect.
- Red demonstrated on the live case: a weekly-limit pane must not yield
issueCount: 0.
Verified vs inferred
- Verified: every
file:line; the live bd43f4de values; the runner's three-member union; the coding-feed.ts comment.
- Inferred: that the projection at
index.ts:854 was for payload size. Not stated in the code — AC1 should establish the intent before widening it.
The engine plane cannot be read truthfully:
capturestrips the disambiguating evidence, advertises impossible values, anddiagnosticscalls a refusing engine healthyThree defects on one question — is the engine actually working? — which is the half of #580 that was never closed.
1. MCP strips exactly the fields that make
idlefalsifiableworkers/mcp/src/index.ts:854projects the response to{sessionId, runState, pane}, droppingrunnerConnected,alive,ready,authPrompt.The API returns
runState: "idle"for three different worlds and disambiguates with precisely those siblings:routes/coding.ts:234(no connection) and:240(runner never answered) both collapse toidle. So over MCP, "the engine is idle", "the runner is gone" and "the probe failed" are one indistinguishable answer.2. The tool advertises two values it cannot emit
index.ts:840— "Also returns run state (idle/working/offline)."The runner's vocabulary is
"idle" | "thinking" | "responding"(packages/browser-runner/src/coding/runtime.ts:82).workingis not a member, andofflineis manufactured only by the timeline route (routes/coding-feed.ts:82), never by/capture.coding-feed.ts:74-85has the corrected vocabulary (ended/offline/unknown) with an explicit comment thatunknownmust not collapse intoidle— so two surfaces sit over one probe with two vocabularies, and the older one is the one MCP exposes.3.
coding_diagnosticsreports 0 issues for a stalled engineMeasured live on
bd43f4de:healthySessions: 1,issueCount: 0,issues: [],live.alive: true,runState: "idle"— while the captured pane's last two lines areYou've hit your weekly limit · resets Aug 17 at 4pmand[error].The same instance's board says "Needs you" and its run says
engine_limit. Both are right. The issue rule (lib/coding-diagnostics.ts:245-251) fires only on runner/relay faults, so an engine that is up but refuses to work is invisible to the tool whose stated purpose is "debug why sessions are offline or stuck".Why this misleads into a wrong action
An owner debugging a stuck agent is sent to the tool named for it, told everything is healthy, and given a
runStatethat cannot distinguish "thinking hard" from "the machine is gone". This is the exact question that cost 4.35 hours on 2026-08-15.Acceptance criteria
coding_session_capturecarries enough to distinguish engine-idle, runner-absent and probe-failed. If payload size is the reason for the projection, say so and choose deliberately —index.ts:854currently drops them silently.coding-feed.ts:74-85androutes/coding.ts:234,240agree, andunknownnever collapses intoidle.coding_diagnosticsreports an engine that is up but refusing as an issue, with the reason.issueCount: 0beside a board card reading "Needs you" is the defect.issueCount: 0.Verified vs inferred
file:line; the livebd43f4devalues; the runner's three-member union; thecoding-feed.tscomment.index.ts:854was for payload size. Not stated in the code — AC1 should establish the intent before widening it.