Skip to content

fix: read the turn's environment across a native compaction summary - #601

Open
felixlark wants to merge 2 commits into
miuuyy:mainfrom
felixlark:fix/compaction-summary-environment
Open

felixlark wants to merge 2 commits into
miuuyy:mainfrom
felixlark:fix/compaction-summary-environment

Conversation

@felixlark

Copy link
Copy Markdown

Problem

Every turn after an automatic compaction fails:

stream disconnected before completion: ChatGPT web turn is missing cwd in trusted Codex environment context

Codex 0.155 rebuilds a compacted turn in this order (observed on a real thread, 7 input items):

# role content
1 developer additional_tools
2-3 developer preamble
4 user the turn's message — parts are plugins.recommendations, agents_md.instructions, environments.environment_context
5 user the compaction summary
6 developer world state
7 user the instruction being answered

canonicalMetadataEnvironmentBeforeUser() walks back from item 7 and skips only developer messages, so item 5 ends the search. Item 4's envelope — the one that carries cwd — is never read, and the turn is refused. Codex then retries five times and gives up.

The envelope itself is well-formed; nothing is wrong with the request except the position of the summary.

Change

Skip a compaction summary while walking back, exactly as a developer message is skipped, under the same provenance rule: its turn id must match the canonical turn, or it must carry a server-owned item id. A summary is recognized by the same predicate the code already uses elsewhere (isReadableCompactionSummaryText / OPAQUE_COMPACTION_NOTE).

This does not widen what can become authority:

  • the summary is never itself read as an environment,
  • any other user message between the envelope and the instruction still ends the search,
  • the envelope still has to pass environmentMatchesCanonicalMetadata.

Tests

tests/environment.test.ts:

  • the exact shape above resolves the environment (fails without the change, with the original missing-cwd error),
  • a summary stamped with a different turn id is not skipped, so the turn is still refused.

bun test tests/environment.test.ts tests/thread-environment.test.ts on top of main: 63 pass, 1 skip, 0 fail.

How it was found

The bridge logged nothing when it refused a turn, so this could only be placed by reproducing it against a real thread and instrumenting the refusal path. If you want, I can send that diagnostic (which branch refused, plus the input's shape — types, roles, sizes, whether a part holds an envelope; never prompt text) as a separate PR; it turned a five-retry mystery into a one-line answer.

🤖 Generated with Claude Code

felixlark and others added 2 commits September 20, 2026 22:54
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A compacted turn does not always end with a fresh instruction: when the
compaction summary is the final user item, the anchor is the envelope message
itself, and the walk-back never inspected the anchor's own content parts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@felixlark

Copy link
Copy Markdown
Author

Pushed a second commit: the same failure has a second shape, seen on another thread today.

A compacted turn does not always end with a fresh instruction. Thread 01a0bf61 sent five items:

# role content
1-3 developer additional_tools + preamble
4 user the turn's message — parts are plugins / AGENTS.md / environment_context
5 user the compaction summary, and it is what the model is asked to continue from

So the anchor canonicalMetadataEnvironmentBeforeUser() walks back from is item 4 — the envelope message itself — and the function only ever inspected messages before the anchor. Same user-visible error.

The new commit checks the anchor's own content parts before walking back, and allows index 0 as an anchor for the same reason. The envelope still has to bind to canonical turn and sandbox metadata, so this widens where the envelope may sit, not what it may claim. A test reproduces the five-item shape and fails without the change.

bun test tests/environment.test.ts tests/thread-environment.test.ts on top of main: 64 pass, 0 fail.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant