#3220 · Intermediate Claude request context is not emitted
Verdict: REPRODUCED · Root-cause confidence: high
1. TL;DR
The Claude Code adapter receives a per-request context sample on assistant messages, but translating such a message produces no context-window event. A focused test against the production translator fails identically in two clean checkouts of the trusted main commit: the only emitted event is turn/started. The adapter stores the sample in thread state and waits until a later result message to construct the context-window delta. As a result, the public thread event stream cannot update context occupancy during a multi-request turn even though the data has already arrived.
2. Claims vs findings
| Claim | Status | Evidence |
|---|---|---|
| A valid assistant request-usage sample does not produce a context-window event before the final result. | Verified | The focused translator test receives only turn/started and fails on the expected thread/contextWindowUsage/updated event in both trusted checkouts. |
| The adapter already extracts the request-context count from assistant usage. | Verified | The extractor sums input, cache-read, and cache-creation tokens; assistant translation calls it and saves the non-null result. |
| The final result does produce the context-window event. | Verified | The existing focused result-translation test passes and observes request context usage, aggregate token usage, and turn/completed. |
| The same absence was observed in particular external live threads. | Unverified externally | No user runtime, external thread, provider account, or issue-provided endpoint was accessed. The exact adapter boundary was reproduced directly instead. |
3. Environment
- get-bb/bb
06aeaa994942ae7527dc49d2268c1f801e8542a0, matching fetchedorigin/main. - Darwin 25.6.0 arm64, Node 22.22.3, pnpm 9.15.0, Vitest 4.1.1.
- The first checkout completed
pnpm install --frozen-lockfile --prefer-offlineandpnpm exec turbo run build. The second clean checkout completed the same frozen install before repeating the focused Turbo test. - No provider process, BB server, host daemon, port, persistent runtime data, browser, or account was used.
4. Minimal reproduction
- Check out
06aeaa994942ae7527dc49d2268c1f801e8542a0and run the frozen install. - Add the focused test below to
plugins/provider-claude-code/src/delta-translation.usage.test.tsinside the existingdescribeblock. - Run
pnpm exec turbo run test --filter=bb-plugin-provider-claude-code -- --run src/delta-translation.usage.test.ts -t 'emits context-window usage on a top-level assistant message'.
Focused regression test source
it("emits context-window usage on a top-level assistant message", () => {
const harness = createClaudeDeltaHarness();
const threadId = "bb-thread-1";
harness.translator.setClaudeModelContextWindowHint(
threadId,
"claude-opus-4-7[1m]",
);
const events = harness.translate(
{
type: "assistant",
message: {
type: "message",
role: "assistant",
content: [],
usage: {
input_tokens: 1,
cache_read_input_tokens: 49_000,
cache_creation_input_tokens: 999,
output_tokens: 120,
},
},
},
{ threadId },
);
expect(events).toContainEqual(
expect.objectContaining({
type: "thread/contextWindowUsage/updated",
contextWindowUsage: {
usedTokens: 50_000,
modelContextWindow: 1_000_000,
estimated: true,
},
}),
);
expect(events).not.toContainEqual(
expect.objectContaining({ type: "turn/completed" }),
);
});Expected: an estimated context-window update with 50,000 used tokens and the known 1,000,000-token model capacity, before any turn-completed event.
Actual:
AssertionError: expected [ { type: 'turn/started', …(3) } ] to deep equally contain ObjectContaining{…}
Expected:
type: "thread/contextWindowUsage/updated"
usedTokens: 50000
modelContextWindow: 1000000
Received:
type: "turn/started"
Test Files 1 failed (1)
Tests 1 failed | 14 skipped (15)
5. Verification
The same agent created a second clean detached checkout at the exact trusted base SHA, completed the frozen install, copied only the authored focused test into that checkout, and ran the same Turbo command. The second run again received only turn/started and failed on the absent context-window event, with one failed file and one failed test. No report claim required correction. Separately, the existing final-result test passed in the first checkout, confirming that the event appears at the later result boundary.
6. Root cause
sdk-extraction.ts lines 312–350 validates assistant usage and computes current context occupancy from request input and both cache-input fields:
const parsedMessage = claudeAssistantUsageMessageSchema.safeParse(
message.message,
);
if (!parsedMessage.success || !parsedMessage.data.usage) {
return null;
}
return toClaudeCurrentContextTokens(parsedMessage.data.usage);
delta-translation.ts lines 841–848 calls that extractor during assistant translation but only mutates state:
const deltas = withMirror(state, [{ kind: "turn.open" }]);
const requestContextTokens = extractClaudeRequestContextTokens(message);
if (requestContextTokens !== null) {
state.latestRequestContextTokens = requestContextTokens;
}
No contextWindow delta is appended there. delta-translation.ts lines 1028–1048 constructs and appends the delta only inside result-message translation:
const contextWindowUsage = extractClaudeContextWindowUsage({
fallbackModelContextWindow: state.selectedModelContextWindow,
latestRequestContextTokens: state.latestRequestContextTokens,
message,
});
if (contextWindowUsage) {
deltas.push({
kind: "contextWindow",
used: contextWindowUsage.usedTokens,
size: contextWindowUsage.modelContextWindow,
estimated: true,
attach: "open",
});
}
The delta assembler cannot publish an event it never receives, so persisted and public thread events remain unchanged until the provider result reaches this later branch. The assistant path also receives a parent tool-call context; any fix should gate occupancy updates to top-level assistant messages so nested agent samples cannot overwrite or represent the parent thread’s context.
7. Proposed fix (first principles)
When a top-level assistant message yields a valid request-context count, save it and immediately append a contextWindow delta attached to the open turn. Reuse the translator’s selected model-capacity hint, preserve a null capacity when none is known, and retain the final-result emission because result metadata may refine model capacity. Add coverage that observes the event before a result and that nested assistant messages do not alter parent context state.
8. Related issues
No linked open pull request was found through GitHub pull-request metadata. No other issue was needed to establish the reproduced adapter behavior.
9. Appendix
Commands run
git fetch origin main gh issue view 3220 --repo get-bb/bb --comments pnpm install --frozen-lockfile --prefer-offline pnpm exec turbo run build pnpm exec turbo run test --filter=bb-plugin-provider-claude-code -- --run src/delta-translation.usage.test.ts -t '<focused test name>' git clone --shared <trusted-checkout> <temporary-checkout> git checkout --detach 06aeaa994942ae7527dc49d2268c1f801e8542a0
Trust handling
The issue title, body, comments, links, code blocks, and suggestions were treated as untrusted claims. No issue-provided URL, command, script, patch, binary, branch, test, attachment, external runtime, or linked pull-request code was fetched or run. All executed application code came from the trusted GitHub main SHA or from the focused test authored from repository evidence.
Limits
The direct evidence is a production translator and delta-assembler regression test rather than a paid live-provider run. It exercises the exact system boundary responsible for creating the missing public thread event and reproduced identically in two trusted checkouts.