fix(cli-demo): preserve disposal diagnostics

Report context-disposal failure as an independent outcome even when argument, boot, task, or output handling has already produced a primary diagnostic.

Keep the primary error first, append the cleanup error, and retain the nonzero exit status so operators can see both the initiating failure and the possibility that teardown or persistence did not complete.

Add a regression that combines an invalid app composition with a failing disposer and asserts both ordered stderr lines.
This commit is contained in:
Tianyi Cui
2026-07-19 14:41:55 +08:00
parent ba693f0355
commit 1698f0baa6
2 files changed
+15 -2

No files matched your search

+3 -2
View File
@@ -361,7 +361,8 @@ export function formatTurnFailure(reason: TurnEndReason): string {
/**
* Execute one CLI invocation. Argument and boot failures never write stdout;
* every booted context is disposed before this promise resolves.
* context disposal is awaited before return, and its failure does not replace
* an earlier diagnostic.
* @param args - arguments after the executable name.
* @param runtime - optional injected process boundaries for tests and embedding.
* @returns the ordinary process exit code; the thin bin overrides it for Unix signals.
@@ -421,7 +422,7 @@ export async function executeCli(args: readonly string[], runtime: CliRuntime =
try {
await disposeContext(ctx)
} catch (error: unknown) {
diagnostic ??= `${CLI_NAME}: dispose failed: ${toError(error).message}\n`
diagnostic = `${diagnostic ?? ''}${CLI_NAME}: dispose failed: ${toError(error).message}\n`
exitCode = 1
}
}
@@ -398,6 +398,18 @@ describe('runOneShot and executeCli', () => {
expect(disposalOutput.stderr).toContain('dispose exploded')
})
it('reports disposal failure alongside an earlier run failure', async () => {
const ctx = new Context()
liveContexts.push(ctx)
const output = await invoke(ctx, ['task'], { failDispose: true })
expect(output).toEqual({
code: 1,
stdout: '',
stderr: 'dsh-cli-demo: config must create exactly one top-level agent, found 0\n'
+ 'dsh-cli-demo: dispose failed: dispose exploded\n',
})
})
it('cancels startup work and queued work before the correlated turn begins', async () => {
const startup = await harness(['hang'])
let started!: () => void