Skip to content

fix(codex): render committed compaction as a timestamped boundary - #1386

Merged
Juliusolsson05 merged 5 commits into
mainfrom
fix/codex-compaction-boundary
Sep 27, 2026
Merged

Juliusolsson05 merged 5 commits into
mainfrom
fix/codex-compaction-boundary

Conversation

@Juliusolsson05

@Juliusolsson05 Juliusolsson05 commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Fixes #1289.

Plan: docs/plans/2026-09-27-codex-compaction-boundary.md (first commit).

Problem, measured on about 1,500 local compacted rollout lines

  • Compaction never rendered. A compacted line's payload has no type, so mapCodexRolloutToFeedEntries's early return swallowed every one, and the compacted branch never ran.
  • The dead branch had two more bugs:
    • The boundary had no timestamp. rendering/model/order.ts sorts timestamp-less rows to the end, so it would paint at the bottom of the feed.
    • It replayed replacement_history, the context Codex retains: developer instructions, AGENTS.md, earlier user prompts, and from 0.155 an encrypted compaction summary item. That would repaint prompts already in the feed: 17,326 of 20,343 sampled replacement messages duplicate an earlier user message.
  • The boundary embedded the whole payload as compactMetadata: retained prompts and a 13–23 KB encrypted blob, kept on a feed entry and in every debug bundle. Nothing in the app reads a Codex boundary's metadata.

Change

  • Map compaction first. compacted is handled before the payload-type guard. It maps to a timestamped boundary, plus the summary when the CLI wrote a readable message: 56 of about 1,500, all older CLIs; 0.15x encrypts it.
  • No replay. replacement_history is not mapped. Known gap, not a regression: 82 local rollouts are resumed files that start with compacted, so there the retained prompts are the only copy of the earlier conversation. Before this PR no compacted line rendered at all. A line-at-a-time mapper cannot tell that case apart, so it is tracked in bug(codex): a resumed rollout that starts with compacted hides its retained earlier prompts #1393.
  • Agent read API (review a). A compaction summary is a synthetic type: 'user' entry. orchestrationMcp.ts's tail summary, full message list and cheap count now skip it through one type-guard predicate, matching the feed, the preview and latestUserPrompts. This also fixes the same latent case for Claude summaries.
  • No payload on the boundary. No compactMetadata, which also gives one stable rendering shape.
  • Catalog. codex.entry.system-compact-boundary.v1 and codex.entry.compact-summary.v1 are added, with the same shared.compaction dispositions as Claude's durable entries.
  • Sweep fix. The curated-fixture sweep (bundleShapeSweep.ts) now names non-conversation entries type:subtype, like the bundle sweep and EntryRow. No curated fixture held a system entry before, so a catalog entry for system:compact_boundary could never match its own fixture.

Tests

The fixture is two real compacted lines: 0.157.0 (empty message, encrypted compaction item) and an older one with a readable summary. Keys, item types, roles and ids are verbatim. Every other string is replaced by the same number of x characters, and privacy assertions ran over the result.

compaction.committed.evidence.renderer.test.tsx checks:

  • 0.157 maps to exactly one boundary, carrying the line's timestamp;
  • the boundary carries no retained history;
  • the older line maps to boundary then summary, both timestamped;
  • both render through shared.compaction and classify known-claimed;
  • the fixture's cases carrier equals the mapper output.

orchestrationMcp.compactSummary.test.ts (red before the fix): the real older line, through the real mapper, gives messageCount 0 and no messages. A real prompt beside it is still reported.

Red on main: six of the seven tests in the two new files. The mapper returns []; the one green test is the real-prompt control. (Review c counted it; an earlier body said three.)

Known follow-ups

Verification

  • npx tsc -b clean at ed37d2d. 27ba05ca changes only comments.
  • src/renderer/src/workspace and src/providers/codex: 205 files, 1,512 tests.
  • src/providers, src/renderer/src/rendering and src/renderer/src/features/feed: 158 files, 1,141 tests, including shapes.coverage.
  • scripts/audit-rendering-shapes.mts exits 0.

🤖 Generated with Claude Code

Juliusolsson05 and others added 2 commits September 27, 2026 01:22
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A compacted rollout line's payload has no type, so the mapper's early
return swallowed every one (about 1,500 local lines) and Codex compaction
never rendered. The dead branch also had two latent bugs. The boundary
had no timestamp, so it would sort to the bottom of the feed. And it
replayed replacement_history: the retained instructions and earlier
prompts, which repainted prompts already in the feed (17,326 of 20,343
sampled replacement messages).

Compaction is now handled before the type guard. It maps to a
timestamped boundary, plus the summary only when the CLI wrote a
readable message (older CLIs; 0.15x encrypts it). replacement_history is
never replayed, and the boundary no longer embeds the whole payload
(retained prompts plus a 13-23 KB encrypted blob) as compactMetadata,
which nothing reads.

Codex catalog entries are added for the durable boundary and summary.
The curated-fixture sweep now names system entries type:subtype like the
bundle sweep, which no curated fixture had exercised.

Fixes #1289

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ument the resumed-compaction gap

Review a of #1386: the agent read API counted the synthetic compaction
summary as a user message. The mapper comment claimed retained history is
always a duplicate; resumed rollouts that start with compacted are the
exception (follow-up #1393).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Disposition for review a (head ed37d2d)

  • P2, fixed.
    • orchestrationMcp.ts skips compaction summaries in the tail summary, the full message list and the cheap count, through one isAgentMessageEntry type guard.
    • The fail-first test orchestrationMcp.compactSummary.test.ts feeds the fixture's real readable line through the real mapper and gets messageCount 0. A real prompt beside it still counts.
    • Both tests are red without the fix.
  • P1, filed as bug(codex): a resumed rollout that starts with compacted hides its retained earlier prompts #1393; not fixed here.
    • It is not a regression. On main, every compacted line is swallowed by the payload-type guard, so these 82 resumed files show nothing today. With this PR they gain a boundary.
    • A correct fix needs the loader to know it is mapping from file offset 0. A paged older-history load can start a page with a compacted line whose predecessors sit in the previous page, so a line-at-a-time rule would either duplicate prompts or miss them.
    • The mapper's WHY comment no longer claims retained history is always a duplicate. It names the exception and bug(codex): a resumed rollout that starts with compacted hides its retained earlier prompts #1393, and the body states the gap.
  • Curated-sweep subtype parity note: no fixture has that shape, so no change.

…w c nit)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Disposition for review c (MERGE-READY; head 27ba05ca)

  • 1 (LOW): a live compaction can paint "Conversation compacted" twice. Filed as bug(codex): a live compaction can show "Conversation compacted" twice #1401, not fixed here.
    • It needs the live semantic compaction block to be suppressed by the committed boundary, as Claude's compaction-synthesis rule does. That is a ledger change outside this mapper fix.
    • It is cosmetic and clears on reload.
  • 2 (LOW): body accuracy. Fixed. "Red on main" now says six of seven.
  • 3 (INFO): bug(codex): a resumed rollout that starts with compacted hides its retained earlier prompts #1393 disposition. Judged sound; no change.
  • 4 (NIT): stale "compact boundaries carry no timestamp" comments. Fixed.
    • In committedRecords.ts and activity.ts: Claude boundaries have no timestamp, and Codex boundaries now carry theirs, which correctly advances the cursor and the activity time.
    • Comment-only change at 27ba05ca.

…ordinal (review b)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Disposition for review b (MERGE-READY; head df5cd682)

@Juliusolsson05

Copy link
Copy Markdown
Owner Author

READY (W1). Head df5cd682. Exact-head CI is green. Every reviewer is MERGE-READY: a (verify-report-a), b (report-b) and c (report-c). Dispositions are posted for all three; follow-ups are #1393 (fix in #1410, stacked on this PR) and #1401. merge-gate.sh --member: GATE PASS. Mergeable and clean.

@Juliusolsson05
Juliusolsson05 merged commit 1ec3f1f into main Sep 27, 2026
2 checks passed
Juliusolsson05 added a commit that referenced this pull request Sep 27, 2026
@Juliusolsson05
Juliusolsson05 deleted the fix/codex-compaction-boundary branch September 27, 2026 13:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

class:C8-rendering Rendering order/duplicates provider:codex Affects Codex integration sev:P3 Minor type:bug Something works wrong

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Codex compaction never renders; the compacted branch is dead and unsafe to simply enable

1 participant