fix(project-context): read managed session-render outputs at the bound the writer can emit - #69
Merged
Conversation
…d the writer can emit Session render wrote a home's instruction target with no size bound while the guard that reads it back capped at 256 KiB, so a home whose instruction corpus grew past the cap wedged permanently and silently: every later plan/apply for that home threw PROJECT_CONTEXT_INPUT_TOO_LARGE before it could do any work. observeProjectContextSessionGuard hashes every path in projectContextSessionGuardPaths, which includes paths.target — the very file session render writes. currentFileHash carved out session-render-manifest.json at 8 MiB and left the target at the 256 KiB default, and session-apply.ts and session-render.ts contain no size guard at all (0 hits for byteLength/maxBytes/ TOO_LARGE), so the writer was unbounded and the reader was not. Measured on station01 against a real 273,860-byte ~/.codex/AGENTS.md; only the binary differs: installed 0.4.19 session plan rc=1 PROJECT_CONTEXT_INPUT_TOO_LARGE: managed input exceeds 262144 bytes this build session plan rc=0 Full write-then-read cycle, clean home, 42 configs: apply writes 355,607 bytes rc=0; installed 0.4.19 refuses to re-plan rc=1; this build re-plans rc=0 and re-applies idempotently rc=0. Name the two bounds instead of repeating a magic number in three places. FOREIGN_INPUT_MAX_BYTES (256 KiB) keeps guarding input this tool did not author. SESSION_MANAGED_OUTPUT_MAX_BYTES applies to the files it does author, whose size is set by the corpus it is asked to write. managedObservationMaxBytes is now the single decision point, used by currentFileHash, anchoredFileObservation and both planProjectContext/composeProjectContextSessionRender reads of paths.target — which had the same latent wedge on the project-context path. This does not widen any content-parsing read: the project-context fragment keeps PROJECT_CONTEXT_MAX_RENDERED_BYTES (4 KiB) and json records keep their own bound. Tests fail without the fix (verified by reintroducing the defect: 2 fail, both new behavioural tests) and pass with it. The 5 pre-existing failures in src/cli/output.test.ts and src/cli/session.test.ts reproduce identically on unmodified ca63d8a and are untouched by this change. Agent: Silvanus
andrei-hasna
added a commit
that referenced
this pull request
Aug 7, 2026
chore(release): prepare instructions 0.4.23 Carries 340aeca, which stops the renderer writing an instruction home larger than it will later agree to read. The codex home has had no instruction update since 2026-08-07T14:47:43Z and cannot be repaired by any published version, because the wedge is on reading the file it already has. Diff is package.json 0.4.22 -> 0.4.23 plus a CHANGELOG entry. No logic. REVIEW DECISION, RECORDED RATHER THAN SILENT. This mechanical release PR was NOT sent to an independent adversarial reviewer, and that is a decision rather than an omission. The substance it releases was independently reviewed and returned GO on #69, with the reviewer confirming the classifier's input is tool-constructed at every call site and the tightened fragment and foreign-input bounds intact. This PR adds no logic to review. Whether mechanical release PRs need their own review pass is an open fleet question that nobody has ruled on; a silent skip and a reasoned one look identical afterwards, so this is the reasoned one. Release gates exercised on the branch before merge: version 0.4.23 free on the registry; bun install --frozen-lockfile --minimum-release-age 604800 rc=0 over 158 packages; typecheck rc=0; build rc=0; PUBLISH_HOLD absent at origin/main, with package.json at 3604 bytes as the positive control. The lockfile check mattered — bun.lock's root workspace block carries no version key, so the bump cannot desync the frozen install, and that gate would otherwise have failed after the tag was pushed. Base resolved from the branch: origin/main 340aeca; merge-tree produced a tree identical to the reviewed head, so what was verified is what lands. Agent: Silvanus
Contributor
Author
|
[REVIEW] GO — #69 @ b2401ae — lens: full-build, reviewer fable-instructions-render- (1 of 1) Dispatch note: the review brief named hasna-internal/platform#69, which does not exist (API 404; that repo's highest PR is #60). The described scope — @hasna/instructions, PROJECT_CONTEXT_INPUT_TOO_LARGE past the 256KiB read-back cap on flattened codex renders — matches this PR exactly, so the verdict is posted here. Independently verified, not taken from the PR body:
Non-blocking findings (P2/P3, follow-ups not gates):
Not checked: other machines' installed versions and render state (station01 only); opencode's staleness cause beyond the PR body's own correction; the 5 pre-existing suite failures on ca63d8a (out of scope, untouched files). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The defect
Session render writes a home's instruction target with no size bound, while the guard that reads it back caps at 256 KiB. A home whose instruction corpus grows past the cap wedges permanently and silently: every later
plan/applyfor that home throwsPROJECT_CONTEXT_INPUT_TOO_LARGEbefore it can do any work. The renderer produces a file that stops its own next run.observeProjectContextSessionGuard(project-context.ts:653) hashes every path inprojectContextSessionGuardPaths(:3206), which includespaths.target(:3214) — the file session render itself writes.currentFileHashcarved outsession-render-manifest.jsonat 8 MiB and left the target on the 256 KiB default.session-apply.tsandsession-render.tscontain no size guard at all (0 hits forbyteLength|maxBytes|TOO_LARGE), so writer unbounded, reader bounded.Measured (station01, todos
b46ca2a3)Two-sided control, only variable = presence of the oversized file:
Same real file, only the binary differs:
Full write-then-read cycle, clean home, 42 configs:
Live impact:
~/.codex/AGENTS.mdis 273,860 B and its home has not re-rendered since14:47:43Z— 4 sources behind.Why raise the bound rather than shrink the corpus
The cap is not a content-size control, and never was. The same corpus already ships uncapped to
claudehomes, becauseadapterMode— not corpus size — decides whether the cap is reachable:native-importsflattened-markdownSplitting adapters deliver the identical payload at rc=0 with no cap check. So the cap bounds nothing about prompt size; it fires only on adapters that flatten. And the content is not padding — measured redundancy is under 1% (1,246 B duplicated title/
Source:preamble across 9 of 41 sources; 180 B cross-source verbatim at 120-char normalized windows).Whether ~292 KB of instructions should be prepended to every prompt is a real question — it is a corpus-curation decision, tracked separately. Silently freezing two homes is not a way to enforce it, and it enforces nothing while
claudestill receives the full payload.The change
Name the two bounds instead of repeating a magic number in three places:
FOREIGN_INPUT_MAX_BYTES(256 KiB) — unchanged, keeps guarding input this tool did not author.SESSION_MANAGED_OUTPUT_MAX_BYTES— for files it does author, whose size is set by the corpus it is asked to write. Same value as the existingsession-render-manifest.jsoncarve-out, so this follows an in-repo precedent rather than inventing a bound.managedObservationMaxBytes(relativePath)is now the single decision point, used bycurrentFileHash,anchoredFileObservation, and bothplanProjectContext/composeProjectContextSessionRenderreads ofpaths.target— which carried the same latent wedge on theproject-contextpath.No content-parsing read is widened. The project-context fragment keeps
PROJECT_CONTEXT_MAX_RENDERED_BYTES(4 KiB) and json records keep their own bound. The widening applies only to reads whose purpose is hashing a file this tool wrote.Tests
Four tests in
project-context.test.ts. Proven to fail without the fix: reintroducing the defect gives63 pass, 2 fail, and the two failures are exactly the new behavioural tests. With the fix,65 pass, 0 fail.every managed session-render output gets the managed bounditeratesSESSION_MANAGED_OUTPUT_PATHS, so adding a guard path without adding it to the list fails the suite — that is the class-level protection, not just this instance.typecheckrc=0.Pre-existing failures, not from this change
Full suite:
622 pass, 5 fail. All 5 reproduce identically on unmodifiedca63d8a(verified by stashing this change and re-running the same two files):src/cli/output.test.ts(3) andsrc/cli/session.test.ts(2). Untouched here.Correction to the originating report
The bug report grouped
codexandopencodeas both wedged. Only codex is.projectContextRuntimeForSessionToolreturnsnullfor opencode, so opencode never reaches this guard — measuredsession planrc=0 andsession apply --dry-runrc=0 against its own 274,060 BAGENTS.md. opencode's staleness has a different cause: renders are launch-triggered per profile via@hasna/accountsconfigs session apply, and no opencode session has launched since14:43Z.Todos:
b46ca2a3Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.