CLI errors reach the operator as stack traces, and 'fleet start' abandons the roster at the first failed agent #1247

Open
opened 2026-08-16 05:26:12 +00:00 by fred · 1 comment
Collaborator

Three related defects in packages/mosaic/src/cli.ts, all in the same file and the same conversation

Split out of #1244 (fleet start reports success over dead panes) deliberately: #1244's change is scoped to the launcher's own diagnosis, while all three of these sit on the shared path that every CLI caller uses. Changing them inside that PR would have widened its blast radius past what its tests measure.

1. runChecked throws bare

runChecked throws without a caught-and-formatted path, so a failing child command reaches the operator as a Node stack trace with the real message buried in it.

The reason this cannot be fixed alone: program.parse() (cli.ts:622) is synchronous. A rejected async action therefore surfaces as an unhandled rejection no matter how nicely the inner code words its error. Fixing the throw without also giving parse an await/catch boundary yields a cleaner message that still arrives under a stack trace — better text, same bad shape.

2. Pre-existing throw new Error("Invalid runtime …")

Same shape, and it is the error an operator is most likely to hit, since it fires on a typo'd --runtime. A typo should print one line and exit non-zero, not print a trace.

3. fleet start's per-agent loop aborts on the first failure

The loop stops at the first agent that fails to launch. On a single-runtime roster this is merely unhelpful. On a mixed-runtime roster — which is the configuration the whole fleet MVP is built for — it is actively misleading: one agent whose runtime binary is missing halts the run, and every agent after it in roster order is reported as nothing at all. The operator fixes the named one, re-runs, and discovers the next. Serial discovery of a parallel problem.

Should attempt every agent and report an aggregate: how many launched, which failed and why, exit non-zero if any did.

Acceptance

Tests must assert the operator-visible text and exit code, not that a throw happened. A test that asserts toThrow() passes equally well over the current bad output and over the fix; it measures the mechanism instead of the contract. For the loop: a roster with a failing agent that is not last, asserting the later agents were still attempted and appear in the summary.

Provenance

Raised by scooby reviewing #1244; fred committed to filing it as a separate PR in comment 22616 on that PR.

## Three related defects in `packages/mosaic/src/cli.ts`, all in the same file and the same conversation Split out of #1244 (fleet start reports success over dead panes) deliberately: #1244's change is scoped to the launcher's own diagnosis, while all three of these sit on the **shared** path that every CLI caller uses. Changing them inside that PR would have widened its blast radius past what its tests measure. ### 1. `runChecked` throws bare `runChecked` throws without a caught-and-formatted path, so a failing child command reaches the operator as a Node stack trace with the real message buried in it. The reason this cannot be fixed alone: `program.parse()` (`cli.ts:622`) is **synchronous**. A rejected async action therefore surfaces as an unhandled rejection no matter how nicely the inner code words its error. Fixing the throw without also giving `parse` an await/catch boundary yields a cleaner message that still arrives under a stack trace — better text, same bad shape. ### 2. Pre-existing `throw new Error("Invalid runtime …")` Same shape, and it is the error an operator is *most* likely to hit, since it fires on a typo'd `--runtime`. A typo should print one line and exit non-zero, not print a trace. ### 3. `fleet start`'s per-agent loop aborts on the first failure The loop stops at the first agent that fails to launch. On a single-runtime roster this is merely unhelpful. On a **mixed-runtime roster** — which is the configuration the whole fleet MVP is built for — it is actively misleading: one agent whose runtime binary is missing halts the run, and every agent after it in roster order is reported as nothing at all. The operator fixes the named one, re-runs, and discovers the next. Serial discovery of a parallel problem. Should attempt every agent and report an aggregate: how many launched, which failed and why, exit non-zero if any did. ## Acceptance Tests must assert the **operator-visible text and exit code**, not that a throw happened. A test that asserts `toThrow()` passes equally well over the current bad output and over the fix; it measures the mechanism instead of the contract. For the loop: a roster with a failing agent that is not last, asserting the later agents were still attempted and appear in the summary. ## Provenance Raised by scooby reviewing #1244; fred committed to filing it as a separate PR in comment 22616 on that PR.
Author
Collaborator

Same root as #1247 — fix them together. @scooby's read, and I agree: the raw stack trace here is the program.parse()-is-synchronous problem at cli.ts:622 that #1247 scopes around runChecked. One cause, two symptoms. Fixing either alone leaves the trace in place.

This pairing is the highest-value of the three CLI-surface bugs (#1249/#1250/#1251), because the diagnostics #1244 exists to produce are exactly the ones the stack trace buries. #1244 made fleet start fail honestly and print a precise code= line; right now the operator never sees it.

Flagging on both issues so it does not get half-solved twice.

**Same root as #1247 — fix them together.** @scooby's read, and I agree: the raw stack trace here is the `program.parse()`-is-synchronous problem at `cli.ts:622` that #1247 scopes around `runChecked`. One cause, two symptoms. Fixing either alone leaves the trace in place. This pairing is the highest-value of the three CLI-surface bugs (#1249/#1250/#1251), because the diagnostics #1244 exists to produce are exactly the ones the stack trace buries. #1244 made `fleet start` fail honestly and print a precise `code=` line; right now the operator never sees it. Flagging on both issues so it does not get half-solved twice.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#1247