#4418 · Claude context launch investigation
Verdict: NOT REPRODUCED · Root-cause confidence: low · reproduction label: no-repro
1. TL;DR
The reported symptom is intermittent loss of personal instructions, memory, skills, and identity context in Claude Code sessions. This investigation did not reproduce that loss. Two fresh trusted-main checkouts consistently preserved the requested context inputs at the bb-to-SDK boundary, including all three settings sources, the synthetic home directory, default-enabled memory, and supplied instructions. The SDK call was intercepted rather than running an authenticated Claude session, so these checks do not prove that Claude reads those inputs or receives the expected context. No root cause or already-fixed commit is established, and the negative result does not invalidate the reporter's observations.
2. Claims vs findings
| Claim | Status | Evidence |
|---|---|---|
| Fresh ordinary sessions sometimes lose multiple context sources together. | Unverified | No authenticated live session or affected session transcript was inspected. No deterministic failure obtained. |
| The settings-source request is identical for every SDK session. | Verified at boundary | SdkSession.start sets user, project, and local unconditionally; eight diagnostic cases pass in each clean checkout. |
| The effective environment is necessarily the same process environment for all sessions. | Not established | The bridge merges validated per-session envVars into a filtered process environment. The fallback in SdkSession does not establish the actual effective environment of the reported sessions. |
| Project and personal workspace sessions can exhibit the same symptom. | Unverified | The diagnostic passes two synthetic cwd strings; it does not instantiate either environment plugin or test their real filesystem contents. |
| Resume ancestry and CLI version do not predict affected sessions. | Unverified | Fresh/resume inputs preserve the launch options here, but the historical transcript and version comparisons could not be repeated. |
| Three later sessions loaded context after an update. | Reporter observation | The follow-up reports success across three environment types, but this is not proof of a fix or a reproduced outcome. |
3. Environment
- Public target repository: get-bb/bb; both checkouts pinned to the full commit above, fetched from its main branch.
- Linux x86_64; root UID; Node 22.19.0; pnpm 9.15.0; source bb-app 0.44.0; pinned Claude Agent SDK 0.3.245.
- No Claude CLI version was exercised. No authentication, credentials, user runtime data, provider transcript, or real personal directory was read.
- No dev server, browser, ports, or persistent app data directory was started. Diagnostics supply only synthetic cwd and HOME values; no macOS desktop reproduction was performed.
- Each checkout completed frozen dependency installation and a full Turbo build: 62 successful tasks.
4. Minimal diagnostic and results
This is the closest obtained offline diagnostic, not a faithful end-to-end reproduction of missing context. It combines the real options builder with the real session launcher and intercepts the external SDK query boundary. A synthetic completed SDK stream permits cleanup without provider access. It protects the composition of these two production functions rather than claiming to test SDK file loading.
- Use a fresh clone of trusted main and pin the recorded revision:
git clone --single-branch --branch main https://github.com/get-bb/bb.git bb-4418-qa cd bb-4418-qa git checkout --detach 0e7b518f135d43005dae201ef34ebb3001607eb4 pnpm install --frozen-lockfile --prefer-offline pnpm exec turbo run build
- Download the diagnostic test from this report and copy it to
plugins/provider-claude-code/src/bridge/__tests__/issue-4418-launch.test.ts. The file was authored from trusted repository evidence, not copied from issue data. - Run:
pnpm exec turbo run test --filter=bb-plugin-provider-claude-code -- src/bridge/__tests__/issue-4418-launch.test.ts src/bridge/__tests__/sdk-session.test.ts
- Expected: all settings sources, default-enabled auto-memory, synthetic HOME, and supplied context remain present in the SDK options for both paths, both instruction modes, and fresh/resumed sessions.
Actual in both checkouts:Test Files 2 passed (2) Tests 23 passed (23)
The eight added cases and the existing fifteen SDK-session tests all passed. - Broader bridge check:
pnpm exec turbo run test --filter=bb-plugin-provider-claude-code -- src/bridge/__tests__/issue-4418-launch.test.ts src/bridge/__tests__/sdk-session.test.ts src/bridge/__tests__/bridge.test.ts
Actual in both checkouts:Test Files 1 failed | 2 passed (3) Tests 1 failed | 101 passed (102)
The unrelated existing testfalls back to well-known install locations when PATH discovery failsexpects a home-directory executable. Production deliberately returns no well-known candidates when UID is zero. The observed value wasundefined; this failure is not evidence of intermittent context loss and was not fixed.
The added file is a disposable report diagnostic, not a proposed production regression test. It never failed for the reported bug; therefore it cannot justify a repair or a pull request.
Diagnostic source
import { afterEach, describe, expect, it, vi } from "vitest";
import type { Options, SDKMessage } from "@anthropic-ai/claude-agent-sdk";
const { queryMock } = vi.hoisted(() => ({ queryMock: vi.fn() }));
vi.mock("@anthropic-ai/claude-agent-sdk", () => ({ query: queryMock }));
import { buildSessionOptions } from "../session-options.js";
import { SdkSession } from "../sdk-session.js";
afterEach(() => {
vi.clearAllMocks();
});
describe("issue 4418 offline launch-boundary diagnostic", () => {
const cases = ["/tmp/qa-project", "/tmp/qa-personal"].flatMap((cwd) =>
["append", "replace"].flatMap((instructionMode) =>
[false, true].map((resume) => ({ cwd, instructionMode, resume })),
),
);
it.each(cases)(
"preserves context inputs for cwd=$cwd mode=$instructionMode resume=$resume",
async ({ cwd, instructionMode, resume }) => {
const captured: Options[] = [];
queryMock.mockImplementation(({ options }: { options: Options }) => {
captured.push(options);
return {
[Symbol.asyncIterator]() {
return {
async next(): Promise<IteratorResult<SDKMessage>> {
return { done: true, value: undefined };
},
};
},
};
});
const mode = instructionMode === "append" ? "append" : "replace";
const sessionOptions = buildSessionOptions(
{
cwd,
instructionMode: mode,
baseInstructions: "Synthetic identity and skills context.",
permissionMode: "auto",
permissionScope: "workspace",
serviceTier: "default",
workflowsEnabled: false,
chromeEnabled: false,
disable1MContext: false,
},
{ HOME: "/tmp/qa-home", PATH: "/tmp/qa-empty-path" },
);
let finish: (() => void) | undefined;
const done = new Promise<void>((resolve) => {
finish = resolve;
});
const session = new SdkSession(sessionOptions, () => {}, () => finish?.());
session.start(resume ? "qa-session" : undefined);
await done;
expect(captured).toHaveLength(1);
expect(captured[0]).toMatchObject({
cwd,
settingSources: ["user", "project", "local"],
env: { HOME: "/tmp/qa-home", PATH: "/tmp/qa-empty-path" },
settings: { autoMemoryEnabled: true },
persistSession: true,
permissionMode: "auto",
systemPrompt:
mode === "append"
? {
type: "preset",
preset: "claude_code",
append: "Synthetic identity and skills context.",
}
: "Synthetic identity and skills context.",
});
if (resume) {
expect(captured[0]).toHaveProperty("resume", "qa-session");
} else {
expect(captured[0]).not.toHaveProperty("resume");
}
},
);
});
5. Root cause analysis
Root cause of the reported loss: not established. The available evidence narrows the question to the distinction between requested launch context and context actually loaded downstream; it does not locate a failing SDK or CLI branch.
The launcher at the pinned base always requests the full settings cascade and forwards a supplied environment in preference to its fallback:
settingSources: ["user", "project", "local"],
persistSession: true,
env: this.options.env ?? process.env,
stderr: onStderr,
...(this.options.mcpServers
? { mcpServers: this.options.mcpServers }
: {}),
...(this.options.allowedTools
? { allowedTools: this.options.allowedTools }
: {}),
...(this.options.disallowedTools
The options builder selects a native preset for append mode and an explicit custom prompt for replace mode; both diagnostic modes retain the supplied instructions. Replace mode is intentional and is not a demonstrated cause of this issue:
const systemPrompt: Exclude<Options["systemPrompt"], undefined> =
params.instructionMode === "replace"
? (params.baseInstructions ?? "You are a helpful coding assistant.")
: {
type: "preset",
preset: "claude_code",
...(params.baseInstructions && params.baseInstructions.length > 0
? { append: params.baseInstructions }
: {}),
};
Memory defaults enable auto-memory unless a caller explicitly disables it:
function buildFlagSettings(params: BuildSessionOptionsArgs): Settings {
return {
autoMemoryEnabled: params.memoryEnabled ?? true,
enableWorkflows: params.workflowsEnabled,
ultracode: params.reasoningLevel === "ultracode",
fastMode: params.serviceTier === "fast",
};
The actual bridge environment construction accepts validated envVars. Thus inspecting only the SDK launcher's fallback does not rule out different HOME, configuration-directory, or executable-related inputs in historical sessions:
function buildSessionEnv(
envOverrides: Record<string, string>,
): NodeJS.ProcessEnv {
const sessionEnv: NodeJS.ProcessEnv = {
...withoutBridgeRuntimeEnv(process.env),
...envOverrides,
CLAUDE_CODE_ENTRYPOINT: "cli",
};
delete sessionEnv.CLAUDE_AGENT_SDK_CLIENT_APP;
return sessionEnv;
}
const sessionConfigEnvVarsSchema = z.record(z.string(), z.string());
function readConfigEnvOverrides(
config: Record<string, unknown> | undefined,
): Record<string, string> {
const parsed = sessionConfigEnvVarsSchema.safeParse(config?.["envVars"]);
return parsed.success ? parsed.data : {};
The installed SDK declarations describe settingSources as the filesystem settings selection and require project to load CLAUDE.md files. Those declarations are consistent with the request observed here, but not proof that the downstream implementation honored it. Likewise, reminder counts alone do not expose the complete effective launch options or system prompt. No upstream defect, environmental override, or bb policy issue is asserted without paired live evidence.
6. Proposed next experiment
No production change is proposed. In a clean authenticated macOS development instance, create synthetic user instructions, a synthetic memory entry, and a synthetic skill with distinct harmless markers. Run small ordinary fresh sessions through the real bridge repeatedly in both environment types. Compare SDK/CLI versions, resolved executable, cwd, instructionMode, settings-source selection, memory flag, effective HOME/config-directory paths, and non-secret environment key names between good and bad sessions. Capture presence or hashes of supplied instructions and markers, not credentials or real personal content. Separate SDK launch inputs from actual model-visible context and transcript persistence. Only then choose a failing owner-boundary regression test and the smallest repair.
7. Verification
The same agent repeated the check in a second clean checkout from the trusted remote, detached at the identical commit. The tree was clean before copying only the authored diagnostic. It performed its own frozen install and full build, then the exact focused and broader commands above. Both focused runs passed 23/23 tests; both broader runs showed the same unrelated root-only executable-discovery failure. No separate verifier, workflow, or linked branch was used.
This repeat supports the narrow launch-input findings and the limited NOT REPRODUCED verdict only. It does not verify healthy authenticated bootstrap. No report correction was necessary after the second run.
8. PR and related tracking
The issue timeline contained no linked pull request, and the open-PR metadata search for 4418 returned no results. No branch was checked out from an issue or PR. No fix PR was created because the reported bug did not reproduce on trusted main and no failing regression test exists.
The repository search independently returned #4233, an open proposal for deliberate per-session context narrowing. It is related policy context, not evidence that unrequested intermittent context loss is intended or fixed.
9. Appendix
Logs below retain actual test results; checkout paths are normalized to CHECKOUT, trailing whitespace is trimmed, and diagnostic fixture paths remain synthetic. No screenshots apply to this non-visual investigation.
- First focused run
- Second focused run
- First broader bridge run
- Second broader bridge run
- Build summaries
- Commands and verification scope
Trust boundary: issue text, comments, and links were treated as untrusted evidence only. No issue instruction, external issue URL, linked branch, script, patch, or binary was executed or fetched. No user instance was used. All diagnostic code derives from the trusted pinned repository and the authored test.
> AGENT GENERATED