Skip to content

Report a failed agent's stderr, and stop a flaky test counting a restart - #752

Merged
lavaman131 merged 1 commit into
mainfrom
fix/connector-stderr-races
Oct 31, 2026
Merged

lavaman131 merged 1 commit into
mainfrom
fix/connector-stderr-races

Conversation

@lavaman131

Copy link
Copy Markdown
Collaborator

Fixes two connector tests that failed on Linux CI in unit shard 1/3 for #751, whose diff only moved the Atomic version. One was a real product bug; the other was a test that raced the connector's own restart.

An agent's exit could be reported without its stderr

startAgent in apps/connector/src/acp.ts built a failed start's report (how and the first line of stderr) once the child's exit event fired. Node emits exit as soon as the process ends, which can be before the last of its stderr has been read: stderr is a separate pipe, and close is the event that follows once the stdio streams have ended. So the report could name the exit and leave out agent: no credentials, and the person running chopin or reading chopin status would lose the one line that says why. an agent that exits before its session reports how, and one that starts says so hit it on CI here and in run 40825923232.

failure() now waits for the child to close after it exits, still capped at one second, so a process the agent left holding stderr open can't hold the report up. The exit status still comes from exit.

A new test, an agent that exits before its stderr reaches us still reports what it printed, makes the order deterministic: the agent exits at once and a process it left behind prints 300 ms later. It failed before the change (the report had no stderr) and passes after it.

The long-line test counted a legitimate second drop

a Planner stderr line too long to mask is dropped whole, so a credential split across it leaks nowhere expects the dropped-line marker once. passMasked in planner.ts is right: it emits one marker per over-long line however the line is split across chunks. What the test missed is that the backend it runs exits with 127, and the connector starts it again after a full-jitter backoff that is uniform from 0 to 1 s. The test stops the Planner as soon as the failure is recorded, polling every 50 ms. When the backoff was only a few milliseconds, the second start printed the long line again before the test aborted, and the marker was correctly written twice. Forcing the backoff to 0 reproduced Expected: 1, Received: 2 in 4 of 25 runs; CI's rate was lower because the backoff is random.

The test's backend now prints the long line only on its first start and idles on later ones, so a restart before the abort adds nothing. The assertions are unchanged. With the same zero backoff it failed 0 of 25 times.

Checks

  • bun test src/acp.test.ts 30 times: 0 failures. bun test src/planner.test.ts 30 times: 0 failures. The whole connector suite passes (268 pass, 14 skip, 0 fail).
  • The long-line test 30 times and the two ACP exit tests 30 times, pinned to one CPU with two busy loops: 0 failures.
  • bun run types and bun run ci pass.
  • Under a harsher load (one CPU, three busy loops) the agent's name and version come from its ACP initialize answer, at a check or a run timed out at the default 5 s in 2 of 30 runs. That is an unrelated test that starts real processes, and this PR doesn't touch it.

Two connector tests failed on Linux CI for #751, whose diff only moved the
Atomic version. One was a product bug; the other raced the connector's own
restart.

An agent's exit could be reported without its stderr. A failed start's
report names how the agent ended and the first line of what it printed. It
was built once the child's exit event fired, which Node can emit before the
last of its stderr has been read, so the report could say "exit 127" and
leave out why. Someone running chopin or reading chopin status lost the one
line that explains the failure. The report now waits for the child to close
after it exits, still within the one second it already allowed, so a
process the agent left holding stderr open cannot hold it up. The exit
status still comes from the exit event. A new test has the agent exit at
once and a process it left behind print 300 ms later; it failed before this
change and passes after it.

The long-line stderr test counted a second, legitimate drop. The Planner
exits with 127 in that test, and the connector starts it again after a
backoff that is uniform from zero to one second. The test stops the Planner
once the failure is recorded, polling every 50 ms, so a backoff of a few
milliseconds let the second start print the long line again, and its marker
was rightly written twice. The product counts one marker per over-long
line however it is split across chunks. The test's backend now prints the
line only on its first start and idles afterwards. Its assertions are
unchanged. With the backoff forced to zero the test failed 4 of 25 runs
before and 0 of 25 after.

Assistant-model: Claude Opus 5.5 (fast)
Assistant-workflow: inline
Assistant-verification: bun test unit passed: GIT_CONFIG_GLOBAL=/dev/null bun test src/acp.test.ts 30 runs and src/planner.test.ts 30 runs, 0 failures
Assistant-verification: bun test under load passed: taskset -c 0 with two busy loops, the long-line Planner test and the ACP exit tests 30 runs each, 0 failures
Assistant-verification: bun test unit passed: GIT_CONFIG_GLOBAL=/dev/null bun test in apps/connector (268 pass, 14 skip, 0 fail)
Assistant-verification: typecheck and lint passed: bun run types and bun run ci
Assistant-verification: bun test under heavier load failed: taskset -c 0 with three busy loops, src/acp.test.ts timed out 2 of 30 runs in the unrelated initialize name-and-version test at its 5 s default
Co-authored-by: Alex Lavaee <[email protected]>
@lavaman131
lavaman131 merged commit a357efc into main Oct 31, 2026
24 checks passed
@lavaman131
lavaman131 deleted the fix/connector-stderr-races branch October 31, 2026 20:16
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