Skip to content

feat(acp/desktop): classify turn failures and persist them per agent#2240

Open
Orland00 wants to merge 4 commits into
block:mainfrom
Orland00:feat/1659-turn-error-classification
Open

feat(acp/desktop): classify turn failures and persist them per agent#2240
Orland00 wants to merge 4 commits into
block:mainfrom
Orland00:feat/1659-turn-error-classification

Conversation

@Orland00

Copy link
Copy Markdown

Closes #1659.

Problem

Failed turns still burn tokens; when timeouts, transport flakes, auth expiry and panics all collapse into one vanishing "Turn error" badge, that spend is impossible to attribute or reduce. As discussed on the issue, the backend already derives a granular outcome_label — the collapse happens in the desktop store, which handles turn_error/agent_panic in the same fall-through as turn_completed and discards the entire error payload.

Implementation

Two commits (split-able into separate PRs on request):

feat(acp) — additive error_class field on the turn_error and agent_panic payloads: a stable machine discriminant (timeout / transport / agent_error / protocol / exited / cancelled / panic) derived in a pure classify_turn_failure(&PromptOutcome) fn from the same data that drives outcome_label and the existing is_transport_error grouping. Existing payload fields untouched; old frontends unaffected. No central schema doc exists for observer event kinds (grepped docs/, AGENTS.md, NOSTR.md), so the fn's doc comment is the documentation site, matching the existing convention.

feat(desktop)activeAgentTurnsStore now splits the terminal-event fall-through: turn_completed clears a new per-agent lastFailureByAgent entry; turn_error/agent_panic persist {outcome, error, code, error_class, timestamp}. Both remain inside the pre-existing per-agent watermark gate, so the stale-replay guarantee (replayed turn_error with null turnId must not resurrect state) is preserved — covered by a new regression test. The persisted failure renders in ManagedAgentRow's existing StatusBlock through the existing friendlyTurnErrorCopy, next to the process-exit lastError line — no new UI surface. Clearing happens on the next successful completion, not on turn start: a turn starting proves nothing about health and would make the badge flicker.

Design notes:

  • errorClass is nullable on the frontend so a mixed-version fleet (older harness without the field) degrades gracefully to outcome.
  • Failure map participates in resetActiveAgentTurnsStore and the community-switch save/restore snapshot like the other four maps.

Testing

  • Rust: 9 unit tests covering every classify_turn_failure branch + payload assertions in the existing error_outcome_emission_tests; cargo test -p buzz-acp 580 passed, cargo clippy -p buzz-acp --all-targets -- -D warnings clean, cargo fmt --all -- --check clean, cargo check --workspace clean.
  • Desktop: 8 new store tests (persistence per class, clear-on-complete vs survive-start, per-agent scoping, stale-replay regression, community-switch round-trip); pnpm -C desktop test 3339 passed / 0 failed; typecheck and lint (biome) clean.
  • Ran locally: the commands above. Relying on upstream CI for the web/mobile matrix (mobile untouched).

Manual test: trigger any turn failure (e.g. kill the agent process mid-turn) → the agent row shows "Last turn error (exited): …" persistently; run a successful turn → the line clears.

Deferred

  • No component/e2e test targets ManagedAgentRow's new managed-agent-last-turn-error node (repo has no component-level tests for this row today) — happy to add a Playwright assertion if you want one.

@Orland00
Orland00 requested a review from a team as a code owner July 21, 2026 17:22
Orland00 added 2 commits July 21, 2026 11:23
Add a stable, machine-readable error_class discriminant (timeout,
transport, agent_error, protocol, exited, cancelled, panic, error) to
the turn_error and agent_panic observer payloads, additive alongside
the existing outcome/error/code fields.

Extracted into a pure classify_turn_failure(&PromptOutcome) so the
mapping is unit-testable independent of the emit call sites, and it
mirrors the existing outcome_label/is_transport_error groupings rather
than inventing a new taxonomy.

Signed-off-by: Orlando Lopez <orlando.lf19@gmail.com>
…discarding them

activeAgentTurnsStore previously treated turn_error/agent_panic the same as
turn_completed: endTurn() ran and the outcome/error/code payload was
discarded, so the "Working in #channel" badge just vanished with no trace
of the failure.

Add a per-agent TurnFailure record (outcome, error, code, errorClass,
timestamp) captured from the turn_error/agent_panic payload — including
the new additive error_class field from the backend commit — and exposed
via getLastTurnFailureForAgent / useLastTurnFailure. It is cleared only on
a subsequent turn_completed (a successful completion is the "agent is
healthy again" signal; a mere turn_started is not, so the badge doesn't
flicker off and back on). Both new branches sit behind the same per-agent
watermark gate as before, so the existing stale-replay guarantees (a
replayed turn_error/agent_panic with a null turnId must not resurrect or
re-trigger effects on a live/completed turn) are unchanged — verified with
a new regression test. The failure map is also folded into the
community-switch save/restore snapshot alongside the other four maps.

Wired into ManagedAgentRow's existing StatusBlock, next to the
process-exit-sourced `agent.lastError` surface, rendered through
friendlyTurnErrorCopy so JSON-RPC codes still get friendly copy.

Signed-off-by: Orlando Lopez <orlando.lf19@gmail.com>
@Orland00
Orland00 force-pushed the feat/1659-turn-error-classification branch from 2128600 to dc4869b Compare July 21, 2026 17:23
Orland00 added 2 commits July 21, 2026 13:05
Signed-off-by: Orlando Lopez <orlando.lf19@gmail.com>
Signed-off-by: Orlando Lopez <orlando.lf19@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Preserve and display classified agent turn failures instead of collapsing all failures to transient "Turn error"

1 participant