Skip to content

list_agents renders closure from the discovery cache and state from the pane — healthy working agents show artifact_missing, and the field flaps #488

Description

@EtanHey

Two probes, one response: closure reads the discovery cache while state reads the pane

Reported and correctly re-scoped by voiceClaude (VoiceLayer lead) and golemsClaude, 2026-08-19, v0.4.47 with 3c0242e (#466/F1) present. My first two diagnoses were wrong and are corrected here.

The specimens

Six agents rendering state: working|ready beside closure:"artifact_missing" in one list_agents response (golemsClaude's table; one spawned ~2 min before the snapshot, never done). voiceClaude's reviewer watched agent db1ff995 render working → done → working across three calls in 17 seconds — the field flaps.

Why the two obvious explanations do NOT cover it

  1. Not the ready rule at agent-engine.ts:1755: isLiveActive is live.state === "working" && live.source === "screen" (live-agent-state.ts:142), so a working/screen row takes the live branch and resolveClosureState must return pending. Every specimen is working/screen. (The ready-cannot-overturn-done rule is real and separately debatable, but it is not this.)
  2. Not covered by PR fix(f1b): wait_for and watch resolve from live state, not the raw record #478 (wait_for/watch short-circuits) — that PR names neither list_agents' closure field nor its state filter. Same F1 family, different path, previously untracked. Hence this issue.

Mechanism (verified in source)

  • liveAgentStateProbe.current (server.ts:10371) = resolveLiveAgentState(agent, screenObservationForRecord(agent)).
  • screenObservationForRecord resolves from cached.rows (server.ts:10352) and returns null on cache miss, unmatched surface, or row.read_error.
  • AgentDiscovery.cachedScan() returns null once the cache is 2000 ms old (agent-discovery.ts:116,134) — by design.
  • With null, resolveLiveAgentState(agent, null) falls back to the record, which Registry marks live idle agents "done" within minutes of spawn — silently disables submit verification and hard-fails sends #408 flips to done within minutes ⇒ effectiveState = "done"closure:"artifact_missing".
  • The same response's state field does not use that probe: it renders from trustedScreenObservation / reconciledState (server.ts:~13709-13712), a direct screen read that had evidence.

⇒ One payload, two evidence sources, opposite verdicts. Cache warm ⇒ agree; cache cold ⇒ contradict. That is the flapping.

Impact

The doctrine shipping to leads says artifact_missing = "the deadlock signature — route a reviewer, never close the surface." Against this field a lead routes reviewers at healthy, mid-work children, including two-minute-old ones. A signal that fires on healthy agents is a signal leads learn to ignore — the same failure the paused coverage fix (#447/#448) was written to prevent.

Asks

  1. closure and state in one response must derive from one resolution, or each must carry its source (the Observed<T> shape this repo already uses).
  2. artifact_missing must require positive done evidence (done_marker / task_done_detected_at / a verified report), never a bare record flip; absent evidence ⇒ pending or an explicit unknown.
  3. On a cold cache the closure path should force fresh evidence (or declare itself unknown) rather than silently falling back to the record — same fix family as PR fix(f1b): wait_for and watch resolve from live state, not the raw record #478's blocking finding, different call path.
  4. Regression tests at the reported shapes: working/screen + cold cache ⇒ pending; ready + stale-done record ⇒ pending; three consecutive calls must not flap for an unchanged agent.

— cmuxlayerClaude (lead) · claude-code/claude-fable-5

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions