Skip to content

fix(codex): count sessions archived as .jsonl.zst - #4534

Open
BananaIIya wants to merge 8 commits into
steipete:mainfrom
BananaIIya:fix/codex-zst-sessions
Open

BananaIIya wants to merge 8 commits into
steipete:mainfrom
BananaIIya:fix/codex-zst-sessions

Conversation

@BananaIIya

@BananaIIya BananaIIya commented Oct 15, 2026 •

Copy link
Copy Markdown

Fixes #4523 (thanks @kcharlan for the detailed report; also mentioned in #4300).

Codex archives finished rollouts as rollout-*.jsonl.zst and later deletes the plain .jsonl. The cost scanner only reads .jsonl, so once the originals are gone that usage silently disappears from every cost window.

Fix: archived rollouts are decoded into a mirror under CodexBar's cost cache and scanned like normal files. The byte-offset scanner and its logical offsets are unchanged.

  • A rollout is mirrored only while its plain .jsonl is absent (Codex keeps both for a while), so nothing is counted twice. Mirrors are removed when the archive goes away or the plain file comes back.
  • Bounded, resumable work (fix: bound Codex cost scans on giant session corpora #2452's intent). Decoding runs inside the refresh's CodexScanBudget, newest first. An archive asks for its estimated remaining size (or whatever the refresh has left once the header's estimate is used up), writes at most that to a hidden .resume slice, and pauses at the allowance or the refresh deadline, which is checked before every decode step. No archive is decoded past the budget. The paused decoder is kept in memory (at most two, dropped after ten idle minutes) so the next refresh continues without re-decoding; after a relaunch the slice resumes by skipping the bytes already on disk, and a decoder paused while still replaying those bytes is kept, so a long replay spans refreshes instead of restarting. The first archive of each refresh is admitted, and makes at least one decode step, even when listing archives used up the deadline, so a large history cannot starve decoding. Both archive walks check cancellation every 256 entries.
  • Slices are written and published under an exclusive flock, so the app and the CLI cannot interleave. A busy or unavailable slice is deferred to the next refresh; a slice longer than its archive is truncated and decoded again.
  • Mirrors are private to the owning user. They hold whole transcripts, so slices are created 0600 (and tightened with fchmod when resumed), published mirrors keep that mode (replaceItemAt with .usingNewMetadataOnly, since the default keeps the old file's mode), the failure ledger is 0600, and the mirror container and roots are 0700. Files and folders an earlier build left readable are tightened on the next scan.
  • An archive that fails to decode is recorded, retried when it changes or after an hour, and counted as a pending file, so catch-up stays pending and the previous report is kept until it resolves. Cancellation keeps the slice and is not recorded as a failure.
  • A fork whose baseline is missing requests its archived parent by session id, outside the report window, so it settles like a fork with an older plain parent. A cached row only counts as an available parent while its file exists, so a parent archived after it was cached is still requested.
  • Archives are fully listed only when a refresh scans. Before roots are fingerprinted, a cheap probe creates a missing mirror root (stopping at the first archive, and remembering an empty result for ten minutes), so the first refresh, including a one-shot CLI run, already counts archived usage.
  • Adds Sources/CZstd: the decoder-only zstd v1.5.7 single-file build (BSD, ~1 MB), registered in the acknowledgements. macOS has no built-in zstd. Source and checksums are in its README. This is the new dependency the issue review flagged, so it needs your sign-off.
  • 0.75.0's parser hash (also 0.76.0's) and 0.77.0's are registered as compatible predecessors, so existing caches are kept on upgrade.
  • Trade-off: the mirror uses disk equal to the decompressed archives in the window plus needed parents (~1.7 GB on one real history). Happy to slim it to only the lines the cost parser needs if you'd prefer.

Proof (synthetic fixture, .github/pr-proof/codex-archived-rollouts/): run-proof.py builds a fictitious CODEX_HOME and runs codexbar cost --provider codex --days 30 --refresh with an isolated HOME until totals repeat. Base is the notarized 0.75.0 CLI; the 0.77.0 upgrade uses the official CodexBarCLI-v0.77.0-macos-arm64 release (checksum verified) via run-proof.py --upgrade-base 0.77.0=<cli>, against the fixture's clean variant (fixture dates are relative to the run day).

Scenario Total tokens Days Coverage established
0.75.0, fixture 660 3 false
This PR, fixture 1,340 5 false (one corrupt archive pending)
0.75.0, every archive decompressed in place 1,340 5 true
This PR, fixture without the corrupt archive 1,340 5 true
This PR opening a 0.75.0 cache 1,340 5 true; predecessor hash adopted, same database inode, plain-file rows unchanged
This PR opening a 0.77.0 cache (latest stable) 1,340 5 true; 0.77.0 hash a47b70abaf8ad86c adopted, same database inode, plain-file rows unchanged

The fixture has a plain rollout, archive-only rollouts in sessions/ and archived_sessions/, a rollout present in both forms, a truncated archive, and a fork in the window whose parent is archived 60 days back.

Under umask 022, every file and folder the patched CLI writes under the mirror is owner-only (proof.json, "head mirror permissions"): directories 0700, decoded transcripts and the failure ledger 0600, none accessible to other users. No slice was pending when that run ended; paused slices are covered by the tests below.

CodexArchivedRolloutMirrorTests (27 tests, see tests.log) covers: each refresh writing exactly its byte allowance to disk (nine refreshes for three archives); a multi-frame archive whose header declares only the first frame, including under an uncapped timed budget; pausing at the deadline; a replay paused by the deadline after a relaunch, an admitted archive past its deadline, and archives listed after every refresh's deadline has passed, each still completing over later refreshes; cancellation during the archive walks; resuming with and without the in-memory decoder; a changed archive mid-slice; a slice longer than its archive; a locked and an unavailable slice; the parked-decoder cap; cancellation; failure backoff; parent resolution, including a parent archived after its plain rollout was cached; owner-only modes for paused slices, published mirrors, the ledger and folders, including tightening files an earlier build left 0644 and replacing a loosened mirror; and the scanner slicing a large archive across refreshes and counting it exactly once (with a plain-rollout control). Reverting any of the three progress fixes (replay retention, first-archive admission, stale-parent check) fails its test. Disabling the parent request or the mirror-root probe makes the corresponding test fail, requesting an unbounded remainder traps in the uncapped-budget test, and dropping any of the mode fixes (creation mode, fchmod, folder chmod, tightening kept files, .usingNewMetadataOnly) fails a permission test.

Tested with Xcode 27 on the 0.77.0-based head: make test (all 1,720 test selections passed), make check (0 violations). The 0.75.0 scenarios in proof.json are from the previous head; the later changes only affect refreshes that hit their deadline, which the CLI --refresh fixture runs do not. The 0.77.0 upgrade was run on this head. On one real history (--period all --refresh, isolated cache), the totals match decompressing every archive in place without this patch.

Coverage note: historyCoverageIsEstablished only becomes true when the inventory has at most codexCatchUpScanCandidateLimit (512) rollouts, because that is the only path that clears codexScanCatchUpPending. That is why the issue's column B (1,722 files) and the larger real history above stay false, with or without this patch. Mirroring archives pushes more installs over that limit; I left it unchanged for you to decide. Not covered: the Linux CLI build with the new C target.

🤖 Generated with Claude Code

@clawsweeper

clawsweeper Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Oct 15, 2026
@clawsweeper

clawsweeper Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codex review: blocked before merge.

What this changes

This PR decodes archived Codex rollouts into private, resumable cache mirrors for the existing cost scanner.

Example: Run the supplied 30-day fixture containing plain sessions, compressed sessions, and an archived fork parent.

  • Before: The released CLI reports 660 tokens across three days.
  • After: The patched CLI reports 1,340 tokens across five days, matching the decompressed reference.

Review scores

Measure Result What it means
Overall readiness 🦐 gold shrimp (3/6) The implementation and supplied CLI evidence are convincing; readiness remains capped by the unresolved dependency and storage decision.
Proof confidence 🐚 platinum hermit (4/6) Sufficient (live_output): Supplied macOS CLI output exercises archive-only sessions in both roots, coexisting representations, corrupt archives, archived fork parents, and owner-only mirror permissions; the latest-stable v0.77.0 upgrade adopts its cache in place and reaches the expected totals.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Product

Kind: Bug fix · Worth it: Needs a maintainer decision · Fix scope: Complete
User problem: Estimated Codex usage disappears from historical reports after Codex compresses local rollout files.
Reason: The accounting repair has clear value, but the new vendored dependency and full-transcript storage strategy need the repository owner’s approval.

Merge readiness

⛔ Blocked before merge - 1 item remains

This PR addresses a current accounting bug, and the earlier findings are resolved; accepting the vendored decoder and transcript-mirror storage remains a maintainer choice.

Priority: P2
Reviewed head: 2914f1a6eaba8b6d6aabbeedf55cf2ee9097a5b1
Owner decision: Required. See Decision needed.

Decision needed

  • Question: Should CodexBar vendor decoder-only zstd v1.5.7 and retain full decompressed rollout mirrors, accepting the reported approximately 1.7 GB cache footprint on one history?
  • Recommendation: Approve the decoder and mirror design: Accept the additional codec and disk use in exchange for retaining the existing byte-offset scanner and demonstrated accounting behavior.
  • Why: Repository policy requires dependency confirmation, and the author explicitly leaves the decoder and storage tradeoff to steipete without recorded approval.

Before merge

  • Resolve maintainer decision - Resolve the maintainer decision shown above before merge.

Findings

None.

Agent review details

How this fits together

Codex local rollout files feed the shared cost scanner, which stores accounting progress in SQLite and supplies estimated usage to the CLI and app.

flowchart LR
  A[Codex rollout files] --> B[Archive inventory]
  B --> C[Private resumable mirrors]
  A --> D[Existing JSONL scanner]
  C --> D
  D --> E[SQLite cost store]
  E --> F[CLI and app reports]
Loading

Technical review

Best possible solution:

Approve the portable decoder and mirror storage tradeoff, preserving the existing scanner’s accounting and offset ownership.

Do we have a high-confidence way to reproduce the issue?

Current-main discovery excludes compressed rollouts, corroborated by released-CLI comparisons in the linked report; no target code was executed during this read-only review.

Is this the best way to solve the issue?

Mirroring preserves the existing parser and logical offsets, with convincing accounting and upgrade evidence, subject to accepting its dependency and storage costs.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 6480977b47f6.

Provenance checked

  • Session roots and archive discovery keeps the original intent (cf081b6: Include archived Codex sessions in local cost scans; the proposed mirror extends that intent to compressed files. An old-commit stat read encountered a missing historical object, while current blame and commit metadata remained available.)
  • Bounded work and fork accounting keeps the original intent (fix: preserve fork accuracy during bounded Codex cost catch-up #2525: Keep large histories resumable and resolve parent baselines without unbounded foreground parsing.)
  • Compatible parser cache adoption keeps the original intent (fix(cost): speed up native history marker searches #4552: Retain compatible existing rows and checkpoints when parser fingerprints change without changing stored semantics.)
  • Third-party acknowledgements keeps the original intent (Add open-source acknowledgements in About #4483: Ship required notices for bundled third-party code; this PR adds the new codec’s BSD notice.)

Testing

Proof path: shipped entry point.

Security

None.

Evidence

What I checked:

Likely related people:

  • Xu Xiang: Raw commit 2920019 adds Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner.swift:4510 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 2920019bc16d; files: Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner.swift)

Review metrics

Metric Value Why it matters
Codec versus integration size 26,949 vendored C/header lines; 872 lines in the Swift mirror Most growth is the decoder; the custom integration implements resumable decoding, private files, inventory, and failure tracking.

Root-cause cluster

Relationship: fixed_by_candidate
Canonical: #4523
Summary: This PR implements compressed-rollout accounting for the canonical local-history report.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Labels

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient.
  • remove status: 📣 needs proof: Current PR status no longer selects a status label.

Label justifications:

  • P2: Compressed local sessions cause understated historical token and spend estimates while account access and original rollout data remain available.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • proof: sufficient: Contributor real behavior proof is sufficient.

Rating scale

6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior.

Workflow

ClawSweeper edits this one comment on every review. Comment @clawsweeper re-review for a fresh review only. Repair and merge execution lanes are retired.

History

Review history (6 earlier review cycles)
  • reviewed 2026-10-15T02:52:58.997Z sha bb209f7 :: needs real behavior proof before merge. :: [P2] Keep archive decoding inside resumable scan limits | [P2] Carry failed archive materialization into coverage state | [P2] Materialize older compressed parents when resolving recent forks
  • reviewed 2026-10-15T03:48:36.128Z sha f8e339a :: blocked before merge. :: [P2] Honor byte and time limits inside archive decoding
  • reviewed 2026-10-15T19:05:16.829Z sha 890f64e :: blocked before merge. :: [P1] Restrict decoded transcript files to their owning user
  • reviewed 2026-10-15T20:47:35.382Z sha d0993bb :: blocked before merge. :: none
  • reviewed 2026-10-16T11:01:55.697Z sha d0993bb :: blocked before merge. :: [P2] Retain decoder sessions that are still replaying a saved prefix | [P2] Exclude unavailable cached paths when finding missing fork parents | [P2] Preserve archive discovery progress across bounded refreshes
  • reviewed 2026-10-17T21:30:36.987Z sha b4bb662 :: needs real behavior proof before merge. :: [P3] Move the archive-support note into the unreleased section

Reviewed October 17, 2026, 6:14 PM ET / 22:14 UTC (Revision 7).

@BananaIIya

Copy link
Copy Markdown
Author

@clawsweeper re-review

Addressed the review in f8e339a: archive decoding now runs inside the refresh's scan budget with cancellation between chunks, failed archives keep catch-up pending (with a retry backoff), and forks with a missing baseline request their archived parent outside the report window. Synthetic before/after and upgrade proof are in .github/pr-proof/codex-archived-rollouts/; the PR body is updated.

@clawsweeper

clawsweeper Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Oct 15, 2026
@BananaIIya
BananaIIya force-pushed the fix/codex-zst-sessions branch 2 times, most recently from 4e71293 to 890f64e Compare October 15, 2026 19:00
@BananaIIya

Copy link
Copy Markdown
Author

@clawsweeper re-review

Addressed the remaining bounded-work finding (#2452) in the last commit: archives now decode in resumable slices that never exceed the refresh's byte allowance or deadline (checked before every decode step), with the paused decoder kept for the next refresh and a disk-skip resume after relaunch. Slices are written and published under an exclusive flock so the app and CLI cannot interleave. Rebased on 0.76.0; tests, make check and the synthetic proof were re-run on this head (details in the PR body and .github/pr-proof/codex-archived-rollouts/).

@clawsweeper

clawsweeper Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. labels Oct 15, 2026
@BananaIIya

Copy link
Copy Markdown
Author

@clawsweeper re-review

Addressed the transcript permission finding in d0993bb:

  • Slices are opened 0600, and fchmod tightens a resumed slice under its lock before any write. Publishing renames that file, and replaceItemAt now uses .usingNewMetadataOnly, because the default keeps the replaced mirror's old mode.
  • The mirror container and roots are created and kept 0700, and the failure ledger is 0600. Mirrors and slices an earlier build left readable are tightened on the next scan.
  • Proof: under umask 022, all 14 entries the patched CLI writes under the mirror are owner-only (proof.json, "head mirror permissions"); no slice was pending at the end of that run, so paused slices are covered by the tests. Two new tests cover a paused slice, a published mirror, the ledger and folders, tightening files left 0644, and replacing a loosened mirror. Dropping any of these fixes fails a test.

make test (1,695 passed) and make check (0 violations).

@clawsweeper

clawsweeper Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. labels Oct 15, 2026
@BananaIIya
BananaIIya marked this pull request as ready for review October 15, 2026 22:44
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-15T22:50:56.355500Z d0993bb Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d0993bb2dc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +669 to +670
if let session = reused, session.consumedOutput != onDisk {
reused = nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep partially replayed decoder sessions

When a process relaunches (or the parked session expires) with a large .resume file, the refresh deadline can fire while the fresh decoder is still replaying the on-disk prefix. park() then stores a valid session whose consumedOutput is less than onDisk, but this equality check discards it on the next refresh, so the app's 2-second catch-up passes (Sources/CodexBarCore/CostUsageFetcher.swift:32) repeatedly restart from byte zero and an archive whose prefix cannot be replayed in one pass never materializes. Retain sessions that have consumed a valid prefix rather than requiring them to have reached the end of the slice.

Useful? React with 👍 / 👎.

return Set(cache.files.values.compactMap { usage in
guard let parentId = usage.forkedFromId?.lowercased(),
self.isUnresolvedMissingParentFork(usage),
!knownSessionIds.contains(parentId)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve archived parents despite stale cached paths

When a parent was indexed while plain, is later archived after a mirror root already exists, and is old enough to remain outside the current scan window, its now-missing plain path remains in cache.files. This set therefore treats the stale row as proof that the parent is available even though cachedCodexSessionIndex rejects missing paths, so the parent's archive is omitted from requiredSessionIds and every child fork stays unresolved and unmetered. Only suppress a parent ID when its cached source is still available to the file index.

Useful? React with 👍 / 👎.

for sourceRoot in sourceRoots {
try checkCancellation?()
let mirrorRoot = self.mirrorRoot(for: sourceRoot, cacheRoot: cacheRoot)
let archives = self.archives(under: sourceRoot.standardizedFileURL, mirrorRoot: mirrorRoot)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Make archive discovery progress within the refresh deadline

CodexScanBudget is constructed immediately before prepare in CostUsageScanner.swift:6110-6127, but archives(under:) recursively drains each source tree without consulting that budget and cancellation is checked only once before the root. If inventory of a large CODEX_HOME consumes the app's 2-second automatic deadline, materialize defers every archive; the next catch-up pass repeats the same full walk and hits the deadline again, so archives can be starved forever while cancellation also cannot interrupt the traversal. Page or cache this inventory under the refresh budget so each pass makes progress.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Oct 16, 2026
BananaIIya and others added 7 commits October 16, 2026 23:30
Codex archives finished rollouts as rollout-*.jsonl.zst and later deletes
the plain .jsonl. The cost scanner only lists .jsonl, so once the originals
are removed their usage silently drops out of every cost window.

Decode archived rollouts into a CodexBar-owned mirror under
cost-usage/codex-archived-rollouts/, keeping the source layout and the
archive's mtime, and add existing mirror roots to codexSessionsRoots. A
rollout is mirrored only while its plain .jsonl sibling is absent, so it is
never counted twice, and a mirror is pruned when its archive disappears.

The parser fingerprint changes, so 0.75.0's 3f88178bf03ad704 is accepted as
a compatible predecessor: stored rows and checkpoints are unchanged.

Vendors the zstd v1.5.7 single-file decoder (BSD) as the CZstd target and
registers it in the third-party acknowledgements.

Fixes steipete#4523

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address review on the archived-rollout mirror:

- Decode inside the refresh's CodexScanBudget, after mirror roots are
  created ahead of root fingerprinting. Newest archives decode first; the
  rest are deferred, and cancellation is checked between input chunks.
- Failed archives are kept in a per-mirror ledger, retried when they change
  or after an hour, and counted as pending files so catch-up stays pending
  and the previous report is preserved through completion reconciliation.
- Forks whose baseline is missing request their archived parent by session
  id, regardless of the report window, so they settle like forks with older
  plain parents.

Adds a synthetic before/after proof under .github/pr-proof.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address the remaining review finding on bounded work (steipete#2452's intent):

- An archive asks the refresh's budget for its estimated remaining decoded
  size, and for whatever the refresh has left once that estimate is used up
  (no content size in the header, or a frame it did not count), so slices
  never shrink. Output goes to a hidden .resume slice named for the
  archive's size and mtime, and decoding pauses at the allowance or at the
  refresh deadline, which is checked before every decode step. No archive
  is decoded past the budget, including the first one in a refresh.
- The slice is written and published under an exclusive flock, so the app
  and the CLI cannot interleave; a busy or unavailable slice is deferred to
  the next refresh, and a slice longer than its archive is truncated and
  decoded again.
- The paused decoder is kept per process (at most two, dropped after ten
  idle minutes), so the next refresh continues without re-decoding; after
  a relaunch the slice is resumed by skipping the bytes already on disk.
  Cancellation keeps the slice and is not recorded as a failure.
- Archives are fully listed only when a refresh scans. Before roots are
  fingerprinted, a cheap probe creates a missing mirror root (stopping at
  the first archive, and remembering an empty result for ten minutes), so
  the first refresh, including a one-shot CLI run, scans archived usage.

Also moves the changelog entry to 0.76.1 (Unreleased) after rebasing on
0.76.0, whose parser hash matches 0.75.0's compatible predecessor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mirrors hold whole decoded transcripts. Create slices 0600 and tighten
resumed ones with fchmod, publish with .usingNewMetadataOnly so a
replaced mirror does not inherit the old mode, write the failure ledger
0600, keep the mirror container and roots 0700, and tighten files an
earlier build left readable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reuse a decoder paused while replaying a slice's prefix instead of
restarting it, always make one decode step once an archive is admitted,
check cancellation while walking for archives, and request an archived
fork parent even when a stale row for its old plain file is cached.

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

The first archive of each refresh is admitted past the deadline and makes
at least one decode step, so a history large enough that listing it uses
up every automatic refresh's deadline still converges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@BananaIIya
BananaIIya force-pushed the fix/codex-zst-sessions branch from d0993bb to b4bb662 Compare October 17, 2026 20:52
@BananaIIya

Copy link
Copy Markdown
Author

@clawsweeper re-review

Rebased on current main (0.77.0); the merge conflict is gone. Addressed the three findings in efce0ff and b4bb662:

  • Partially replayed decoders are kept. A session paused while replaying its saved prefix (consumedOutput < onDisk) is reused and keeps skipping; only one ahead of the slice starts over. Test: a replay paused by the deadline continues after a relaunch.
  • Stale cached parents no longer hide archived ones. codexMissingForkParentIds only treats a cached row as an available parent while its file exists, matching the session index, so a parent archived after it was cached is requested. Test: a fork resolves a parent archived after its plain rollout was cached.
  • Archive discovery can't starve decoding. Both archive walks check cancellation every 256 entries. The first archive of each refresh is admitted even when listing used up the deadline, and an admitted archive always makes one decode step, so every pass makes progress. Tests: archives keep decoding when listing them uses up every refresh deadline, an admitted archive decodes a step even when its deadline has passed, cancellation interrupts the archive walks.

Reverting any of the three fixes fails its test. 0.77.0's parser hash is registered as a compatible predecessor. make test (all 1,720 test selections passed) and make check (0 violations).

@clawsweeper

clawsweeper Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. proof: sufficient Contributor real behavior proof is sufficient. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Oct 17, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@BananaIIya

Copy link
Copy Markdown
Author

@clawsweeper re-review

Addressed the remaining author items in 2914f1a:

  • 0.77.0 upgrade proof. run-proof.py --upgrade-base 0.77.0=<cli> fills a cache with the official 0.77.0 macOS CLI (release tarball, checksum verified), then opens it with this head. Result in proof.json → "upgrade from 0.77.0 cache": parser hash a47b70abaf8ad86c → 2d6e5ef6073dc99b adopted in place, same database inode, every plain-file row unchanged, and the report is 1,340 tokens over 5 days with coverage established. That matches the decompressed reference. The PR body table has the new row.
  • Changelog. Moved the entry under 0.77.1 — Unreleased.

The decoder dependency and mirror disk footprint still need your call, @steipete.

@clawsweeper

clawsweeper Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. proof: sufficient Contributor real behavior proof is sufficient. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Oct 17, 2026

This branch has not been deployed

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

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Codex cost scan does not read rollout-*.jsonl.zst; history here starts 2026-10-01

1 participant