From 1698f0baa6226a945bc1c459a2a320e389f2cec2 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Sun, 19 Jul 2026 14:31:33 +0800 Subject: [PATCH] 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. --- packages/examples/cli-demo/src/cli.ts | 5 +++-- packages/examples/cli-demo/tests/cli.spec.ts | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/examples/cli-demo/src/cli.ts b/packages/examples/cli-demo/src/cli.ts index 63c08ae39f..3a8fecc7a8 100644 --- a/packages/examples/cli-demo/src/cli.ts +++ b/packages/examples/cli-demo/src/cli.ts @@ -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 } } diff --git a/packages/examples/cli-demo/tests/cli.spec.ts b/packages/examples/cli-demo/tests/cli.spec.ts index fcdf8a3004..8c41236829 100644 --- a/packages/examples/cli-demo/tests/cli.spec.ts +++ b/packages/examples/cli-demo/tests/cli.spec.ts @@ -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