#2791 · Thread spawn output with a multi-line prompt
Verdict: NOT REPRODUCED · Root-cause confidence: low
1. TL;DR
The report claims that a successful non-JSON spawn can produce empty standard output when the prompt contains multiple lines. Trusted main did not show this behavior. The focused command test passed twice in clean checkouts. The command always writes the new ID before it writes the detailed thread block. The cause of the reported observation remains unknown.
2. Claims vs findings
| Claim | Status | Evidence |
|---|---|---|
| A successful non-JSON spawn can produce no ID output with a multi-line prompt. | Refuted on trusted main | The focused test passed in two clean checkouts at the recorded commit. |
| The prompt changes the output path. | Refuted on trusted main | The prompt only enters the SDK request. The output branch uses only the JSON flag and the returned thread. |
| The ID can appear on another stream. | Refuted on trusted main | The command uses console.log for both ID lines. Node writes these calls to standard output. |
3. Environment
- Repository:
get-bb/bb - Trusted commit:
688eb4251c5b5c53c5a56f85baae6d47966355ec - Host: Darwin 25.6.0, arm64
- Node:
v22.22.3 - Vitest:
4.1.1 - Provider process: not used
- Server and data directory: not used
The test uses the real CLI command registration and output code. It replaces only the server transport response.
4. Minimal reproduction
- Clone the trusted target repository and check out the recorded commit.
- Install the locked dependencies with
pnpm install --frozen-lockfile --prefer-offline. - Save the test below as
apps/cli/src/__tests__/command-output/thread-spawn-stdout-repro.test.ts. - Run
pnpm exec vitest run src/__tests__/command-output/thread-spawn-stdout-repro.test.tsfromapps/cli.
Expected if the report is correct:
AssertionError: expected output to contain the new thread ID
Actual:
Test Files 1 passed (1) Tests 1 passed (1)
The two asserted standard-output lines were:
Thread spawned: thread-multiline-prompt ID: thread-multiline-prompt
Repro files: test source, first run, and second run.
import { describe, expect, it, vi } from "vitest";
import {
collectLogLines,
runCommand,
setupCommandOutputTestEnvironment,
stubServerApi,
} from "../helpers/command-output-harness.js";
import type { CommandRegistrar } from "../helpers/command-output-harness.js";
import * as fixtures from "../helpers/command-output-fixtures.js";
import { registerThreadCommands } from "../../commands/thread/index.js";
describe("bb thread spawn standard output reproduction", () => {
setupCommandOutputTestEnvironment();
const register: CommandRegistrar = (program) =>
registerThreadCommands(program, () => "http://server");
it("prints the new thread ID for a multi-line prompt without JSON output", async () => {
const thread = fixtures.makeThread({
id: "thread-multiline-prompt",
projectId: "proj-1",
providerId: "claude-code",
status: "starting",
});
stubServerApi({ "v1.threads.$post": vi.fn(async () => thread) });
await runCommand(
[
"thread",
"spawn",
"--project",
"proj-1",
"--provider",
"claude-code",
"--prompt",
["first line", "second line"].join("\n"),
],
register,
);
expect(collectLogLines(vi.mocked(console.log))).toContain(
"Thread spawned: thread-multiline-prompt",
);
expect(collectLogLines(vi.mocked(console.log))).toContain(
" ID: thread-multiline-prompt",
);
});
});
Second clean verification
I created a second clean checkout. I set its origin to https://github.com/get-bb/bb.git. I fetched origin/main and verified that both commit IDs matched the recorded commit. I installed the locked dependencies again. The same focused test passed again. I made no report correction after this run.
5. Root cause
No root cause exists in the tested code path for an empty output result. After the SDK returns a thread, the command checks only the JSON flag. It then writes the first ID line and calls the detailed formatter. See the spawn output path.
if (outputJson(opts, thread)) return;
console.log(`Thread spawned: ${thread.id}`);
printThread(thread);
The formatter writes the same ID again. See the thread formatter.
console.log(` ID: ${thread.id}`);
History shows that the first ID write has existed since commit 5b9199822e92b206d6950dc9823ae449fe53b4d6 on 2026-03-25. A stale installed CLI, an outer wrapper, or stream capture could explain the observation. This report has no evidence for any one explanation.
6. Proposed fix
Do not change trusted main without a failing test. The next useful check must record bb --version, the resolved executable path, the exit code, and separate standard-output and standard-error files from an affected installation.
7. Related issues
#2620 covers file-backed and standard-input prompt support. It does not change the output branch tested here.
8. Appendix
No open pull request linked to this issue when the investigation started.
The first checkout used a direct target-repository clone. The second checkout used a clean local clone of the verified commit. Its origin was then set to the target repository. A fresh target fetch confirmed the commit again.
The full trusted build command was pnpm exec turbo run build. It completed with 18 successful tasks. The focused test did not change production code.
The issue title, body, and comments were treated only as untrusted claims. No command or external link from the issue data was used.