Skip to content

Orchestrator reports why a child stopped, not just that it stopped - #538

Merged
TheGreatAxios merged 4 commits into
mainfrom
cl-6903-orchestrator-reports-it-looped-it-churned-it-cancelled-with
Aug 23, 2026
Merged

Orchestrator reports why a child stopped, not just that it stopped#538
TheGreatAxios merged 4 commits into
mainfrom
cl-6903-orchestrator-reports-it-looped-it-churned-it-cancelled-with

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Collaborator

Problem

When a child agent is terminated by the repetition guard, stall watchdog, salvage caps, or operator cancel, the parent only learned "it stopped" — triage required reading raw trace files.

What already existed

Most of the plumbing was in place: forcedStopReport builds a full Summary/Findings/Blockers/Paths salvage envelope per stop class, the repetition path already led Findings with the looped window snippet + repeat count, and appendSubAgentParentHints stacks per-class re-dispatch hints. The gaps were (1) no machine-readable reason field — only prose in Summary, (2) the child's terminal state (SubAgentSession) carried no reason at all for forced stops that land as status "done", and (3) the TUI's finished-lane transcript row printed a bare "done"/"cancelled".

What this adds (minimal diff on top of the existing plumbing)

  • SubAgentReport gains an optional stopped field, rendered as a dedicated first line of the envelope: Stopped: repetition — window "Groaning. " × 1363. Survives the parse/format normalization round-trip; a nested salvage report quoted under Findings cannot leak its own line as the outer reason.
  • Each termination path attaches a distinct reason + detail: repetition (window snippet × repeat count), stall abort (seconds quiet after the nudge), deadline (ms elapsed), turn-budget (turns used/max), no-progress (identical-call count), operator cancel (recorded cancel reason). Thrash / no-ship / never-acted / never-edited / incomplete-report carry their bare reason token.
  • SubAgentSession.stopReason records the reason on the child's terminal state — parsed from the report's Stopped line on complete(), and set to cancelled — <reason> on cancel(). The bare pre-progress cancel message to the parent now includes the recorded reason too.
  • The fleet transcript row for a finished child (observeFleet) announces <lane> stopped — <reason> as an attention line instead of a silent "done" or a bare "cancelled".

Tests

  • Stopped-line propagation for repetition (window × count), cancel, and turn-budget through forcedStopReport → parse/format round-trip.
  • Session store: complete() captures the report's reason; cancel() records the cancel reason; clean completes stay reason-free.
  • Task tool: pre-progress cancel surfaces the recorded reason to the parent.
  • Fleet report: stopped-done and cancelled lanes announce the reason.

tsc --noEmit, build, and the full suite (5122 pass / 0 fail) are green. The repo-wide prettier --check / eslint drift in bun run lint is pre-existing on main (the CI lint job already fails there); changed files introduce no new findings.

Fixes CL-6903

Forced stops (repetition guard, stall abort, salvage caps, operator
cancel) now attach a machine-readable reason to the child's terminal
state. The report envelope gains a dedicated Stopped: line (e.g.
'Stopped: repetition — window "Groaning. " × 1363'), the session
store records it as stopReason, and the fleet transcript row announces
'lane stopped — <reason>' instead of a bare done/cancelled.
@linear-code

linear-code Bot commented Aug 23, 2026

Copy link
Copy Markdown

CL-6903

@TheGreatAxios
TheGreatAxios merged commit 799b205 into main Aug 23, 2026
5 checks passed
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.

1 participant