codex-rollout-shrinker is a local CLI and .NET 10 library for safely
creating a compacted Codex child, shrinking its cold rollout, and restoring that
child from an explicit verified backup.
The original V0 workflow remains available when a Codex frontend exposes the required controls:
- Fork the source thread.
- Switch to the fork and run context compaction.
- Wait for compaction to finish.
- Close the frontend so its Codex backend releases the rollout writer.
- Run
inspect, then the explicitly confirmedshrinkcommand with the fork thread ID. - Reopen the same fork thread in Codex.
The V1 command automates the missing frontend operations while every ordinary Codex process is closed:
codex-rollout-shrinker fork-compact-shrink <source-thread-id> \
[--codex <absolute-path>] [--codex-home <path>] \
[--sqlite-home <absolute-path>] [--skip-history-cache-reset] [--yes]
The selected Codex app-server creates and registers one persistent fork,
compacts it, and exits gracefully. The production command currently sends an
eligible legacy child through the existing V0 transaction. The approved
paginated extension instead detaches an eligible reference-backed child and
requires a second same-binary app-server reconciliation, final source/child
fences, and by default a verified opaque global thread-history cache
backup/removal before success. Default paginated reset requires an explicit
--sqlite-home, which both app-server instances must independently resolve.
--skip-history-cache-reset omits only the final cache transaction and allows
the target to be omitted; both instances still resolve and compare the actual
SQLite home. Skip does not promise a fresh GUI projection or a rebuild merely
from restarting Codex. The extension supports
standalone paginated sources with absent/null history_base and exact
zero-based local ordinals. In either branch the source may be a root or ordinary
fork, remains byte-identical, and is never shrunk in place.
The V0 transformation preserves the child's first canonical session_meta
record and the smallest raw JSONL suffix containing both the newest usable
compacted checkpoint and Codex's required user-turn TurnContext replay
anchor.
Legacy thread_rolled_back history is supported only when an EOF-to-rollback
reverse-segment barrier proves that the physically newest usable checkpoint
survives, and the ordinary minimal retained suffix excludes every rollback
marker. The tool does not apply rollback counts or rewrite dead history.
Legacy rollouts from Codex 0.153.x may contain token_usage_record accounting
metadata. These records are recognized and retained byte-for-byte when inside
the selected suffix. The checkpoint's usage snapshot and model-invisible
guardian_history are also preserved; the latter can legitimately retain old
text inside the checkpoint. Unknown top-level item types still cause refusal.
Standalone-paginated sources also accept this accounting metadata without
letting it advance or repair turn readiness; canonical ordinals and all other
source checks remain mandatory. Linked children also accept the observed
0.153 usage-before-checkpoint and settings-after-checkpoint positions, without
relaxing compaction completion. See the 0.153 compatibility evidence
and WSL follow-up.
WP1 implements a read-only, fail-closed inspector. It discovers one active legacy fork rollout, validates every JSONL record, selects the newest usable compaction checkpoint, and reports the exact raw byte ranges a later writer would retain. The safe suffix may begin before the checkpoint when that is required to preserve resume settings.
WP2 implements an internal, non-publishing image builder. It streams the entire
inspected source, rechecks its length and SHA-256, copies the selected raw byte
ranges into an empty caller-owned stream, and independently parses the result
through the same semantic analysis used by inspect. It does not create a
discoverable rollout or claim public Codex resume equivalence.
WP3 implements an internal immutable backup bundle. A unique directory outside both Codex session trees receives an exact source copy and a strict V1 manifest binding the source digest to the already verified WP2 replacement digest. Unix bundles are owner-private; Windows bundles inherit the Codex-home ACL. The source backup is reread and hash-checked before the bundle is published. Verification remains independently reusable by the transaction services.
WP4 implements the internal Codex-compatible per-thread writer lock. It uses
the same coordination and target paths as Codex, native flock on Linux, and
native LockFileEx on Windows. Two-way disposable-home canaries passed against
the public Codex 0.147.0 Linux x64 and Windows x64 binaries. This does not add a
mutating command.
WP5a implements internal durable atomic rollout publication. It creates one
same-directory, non-JSONL staging file, durably flushes and independently
verifies it, revalidates the source plan and immutable backup binding, and then
uses one File.Replace operation. Linux synchronizes the parent directory;
Windows flushes a native directory handle and reports best-effort directory
metadata durability. Linux additionally requires the source owner to match the
effective UID and preserves exact UID/GID identity plus the allowed POSIX mode;
ACLs and extended attributes are not yet part of the proof. Once publication
begins, every ambiguous failure is
reported as outcome unknown without rollback. Native synthetic suites passed
on Linux ext4 and Windows NTFS.
WP5b implements the internal shrink transaction coordinator. It holds the WP4 writer lock across fresh plan equality, file-backed candidate construction and durable verification, immutable backup creation, WP5a publication and final verification, and transaction scratch cleanup. It never removes a published backup or retries/rolls back an outcome-unknown publication. The integrated synthetic suite passed on Linux ext4 and native Windows NTFS.
WP6 implements internal hash-guarded restore. It verifies an explicit immutable backup before classifying state, rediscovers the active rollout rather than trusting the manifest as write authority, treats an exact source hash as an already-restored no-op, and restores only over the exact manifest replacement hash. Restoration uses one durably flushed same-directory non-JSONL staging file and one atomic publisher call under the existing writer lock. Once that call begins, ambiguous failures are outcome unknown without retry or rollback. Backup reads also reject Linux FIFOs without blocking. The WP6 synthetic suite and extended native paths passed on Windows NTFS, including 262- and 386-character rollout paths; in this WP6 validation, the public-Codex canary remained an explicitly opt-in skip.
WP7 adds a test-only public Codex cold-resume and next-request canary. The official Codex 0.147.0 Linux x64 and Windows x64 binaries both cold-resumed a synthetic legacy fork after the real shrink transaction, completed a model turn against a bounded IPv4-loopback Responses mock, and listed the unchanged thread ID/path. The captured request contained the checkpoint replacement, retained suffix, and new turn exactly once in order, and contained no discarded-prefix marker. The existing public writer-lock canary also passed through the shared notification-preserving app-server helper. This completes WP7 on both primary platforms.
WP8 exposes the complete mutating surface. shrink prints the exact inspector
plan before accepting either an exact interactive phrase or --yes, then passes
that same plan to the lock-bound transaction. restore requires an explicit
manifest and never searches for a latest backup. Stable content-free results,
typed outcome-unknown backup evidence, cancellation, and exit-code contracts
passed with disposable synthetic homes on Linux ext4 and native Windows NTFS.
The first real live trial subsequently passed on a manually created fork: the
active rollout fell from 14,869,588 to 228,680 bytes, the same ID/path resumed
in the VS Code frontend, and ordinary turns continued to append without any
shrinker-written SQLite state. Aggregate timings and evidence limits are
recorded separately from the synthetic interoperability proof.
The rollback extension preserves every existing mutation and restore contract. Its synthetic suite covers typed rollback payloads, reverse-segment edge cases, minimal candidate reanalysis, and an exact shrink/backup/restore round trip. A rollback-specific opt-in public Codex canary is committed; sanitized real rollback-rollout validation remains a separate pending evidence gate. The new canary passed against the local public Codex 0.147.0 Linux binary.
V1 implements fork-compact-shrink as a fail-closed orchestration layer. It
confirms before the first persistent fork, structurally probes the selected
binary's generated app-server schema for excludeTurns, sends a persistent
fork with turn hydration excluded, requires one coherent context-compaction
turn, then uses a metadata-only child readback to prove idle status and stable
lineage/path. Only a sticky clean shutdown—stdin closed, process exit zero, and
both output streams settled before the deadline—permits V0. The app-server is
the only SQLite writer; the C# process never opens SQLite or edits database
bytes. Paginated mode has one later opaque-file exception that backs up and
removes the derived cache only after both app-servers and all final fences. The
full chain is intentionally non-atomic: a failure after fork submission retains
the possible child, reports content-free phase evidence, and never retries or
automatically deletes it. Codex 0.147.0 remains valid V0 evidence but lacks the
required V1 fork schema. The first V1 release target is 0.2.0-rc.1. The
selected public Codex 0.151.0-alpha.7.2 passed the opt-in production
full-chain canary on
Linux ext4: a rollback-bearing root source was forked, compacted, cleanly shut
down, shrunk with a verified backup, cold-resumed, continued for one model turn,
and resolved through thread/list, while the source remained byte-identical.
The two captured model requests proved that rolled-back content stayed absent.
The separate 0.147.0 negative capability canary passed by refusing that binary
before fork. On native Windows/NTFS, npm-installed public Codex 0.151.0
passed npm PATH resolution, schema probing, and the same production full-chain
canary. Sanitized real-rollout validation remains pending.
A production contract is now approved for a distinct standalone-paginated
branch. It preserves the legacy V0 path, but requires exact source/frontier and
ordinal proofs, a raw-span detach/rebase transaction with manifest V2, atomic
publication, and same-binary resume/list reconciliation plus final hash fences.
The implementation and sanitized synthetic success/failure matrix are complete.
The selected executable is bound by canonical path plus complete-file SHA-256
across both app-server lifetimes. The public Codex 0.151.0 Linux command chain
passed in a disposable home against a private real-shape standalone paginated
source, including fork, compact, V2 backup, atomic detach/rebase, reused-home
resume/summary-list reconciliation, and final fences. The 2026-09-11
follow-up also passed Linux 0.153.4 cold
Skip through the public library, including a separate real-model continued
turn. Native-Windows/NTFS paginated execution and GUI freshness remain separate
unverified gates; cross-published packages do not establish them.
The final 2026-08-31 ordinary suite passed 598 tests with 17 opt-in canaries
skipped. The pinned public Codex schema checks passed 3/3: 0.151.0 exposed
both the legacy fork/compact and paginated reconciliation subsets, while
0.147.0 was refused before fork as expected.
Codex 0.151 may retain a stale thread_history_1.sqlite projection after the
paginated child rollout is detached and rebased. The paginated mainline now
finishes with the same verified opaque global cache transaction used by the
standalone experiment: after the second app-server exits cleanly and every
final fence passes, it backs up and removes main/WAL/SHM without opening SQLite.
Success prints both the detach and cache manifests, reports
gui-history-visibility: pending-cache-rebuild-on-next-codex-start, and asks
the operator to start the GUI or exactly one Codex instance so Codex can rebuild
the cache. It never fabricates a model turn or starts a third app-server.
Codex 0.151 can list the same empty-preview row inside an explicitly assigned
thread section. Such an assignment may provide immediate content-free GUI
surfacing in clients that render sections, but it is a user-visible organization
mutation outside fork-compact-shrink authorization. An explicitly experimental
cold-home command is available for operator testing:
codex-rollout-shrinker experimental-section-surface <thread-id> \
[--codex <absolute-path>] [--codex-home <path>] [--yes]
It creates or reuses the exact Shrunk sessions section through the selected
app-server and assigns one standalone paginated user fork without a model turn.
Close every Codex process using that home first. Success proves persistent
section assignment and unchanged rollout bytes; restart the GUI to determine
whether that client build renders the section. The command never claims global
recent-list eligibility and never moves a thread already assigned to a different
section. Its non-atomic create/move safety contract is
design/experimental-section-surface-v0.md.
For the separate Codex 0.151 stale paginated-projection problem, an experimental global cold-cache command is also available:
codex-rollout-shrinker experimental-reset-thread-history-cache \
--sqlite-home <absolute-path> [--yes]
Close every Codex CLI, GUI, extension host, and app-server using that SQLite
home before preview and keep them closed until completion. The command treats
thread_history_1.sqlite plus optional -wal/-shm peers as opaque files. It
rejects unknown sidecars and non-regular paths, holds the complete previewed set
through removal, and publishes a length/SHA-256 verified backup before deleting
SHM, WAL, and main in that order. Linux backups are owner-private; Windows
backups inherit the SQLite-home ACL. It never runs SQL or edits database bytes.
On success, start exactly one Codex instance and verify the target thread before
returning to normal use; recreation remains Codex's work.
This is a global cache action: unrelated paginated threads may be lazily
reprojected. The exact-process scan and whole-file guards are strong cold-state
preflights, not SQLite-compatible coordination proof, and a Codex startup race
remains. The standalone command remains useful for recovery and testing; the
paginated mainline reuses the same transaction only after reconciliation and
all final fences. Its backup-only, partial-removal, and outcome-unknown contracts
are specified in
design/experimental-thread-history-cache-reset-v0.md.
If this final cache stage fails, the new child remains detached and reconciled;
the tool never retries or deletes it. Pre-removal and backup-only cases exit 6,
partial removal exits 6 with exact removed/remaining names, and an unknown main
removal exits 4. Both manifests are reported whenever the cache backup exists.
Mutating success receipts are rendered before stdout publication. If stdout
then fails, exit 5 and a best-effort content-free stderr receipt preserve the
successful status and exact thread, rollout, and verified manifest evidence.
Both shrink and restore outcome-unknown results carry that same complete
absolute evidence. If stdout fails, the same pre-rendered outcome-unknown block
falls back to stderr; failure of both channels still returns exit 4 without a
retry. The tool does not use session_meta.cli_version as a false future-binary
gate; operators must rerun the public compatibility canaries for new Codex
versions.
codex-rollout-shrinker inspect <thread-id> [--codex-home <path>]
codex-rollout-shrinker shrink <thread-id> [--codex-home <path>] [--yes]
codex-rollout-shrinker restore <thread-id> --backup <manifest-path> [--codex-home <path>]
codex-rollout-shrinker fork-compact-shrink <source-thread-id> [--codex <absolute-path>] [--codex-home <path>] [--sqlite-home <absolute-path>] [--skip-history-cache-reset] [--yes]
codex-rollout-shrinker experimental-section-surface <thread-id> [--codex <absolute-path>] [--codex-home <path>] [--yes]
codex-rollout-shrinker experimental-reset-thread-history-cache --sqlite-home <absolute-path> [--yes]
Build and run the focused synthetic suite with .NET 10:
dotnet build CodexRolloutShrinker.slnx
dotnet test CodexRolloutShrinker.slnx --no-build
Pin the public Linux x64 Codex versions used by the release gates beneath the
ignored artifacts/codex-canaries/ directory:
./eng/provision-codex-canaries.sh
source artifacts/codex-canaries/env.sh
The provisioner installs exact npm versions 0.151.0 and 0.147.0, validates
their native executable versions, reuses complete existing installs, and
refuses incomplete or symlinked destinations. The generated env.sh selects
the compatible full-chain binary and the legacy pre-fork rejection binary for
eng/release.sh; neither binary is tracked by Git.
Reference src/CodexRolloutShrinker.Core/CodexRolloutShrinker.Core.csproj from a
sibling .NET 10 project, or create a local NuGet package:
dotnet pack src/CodexRolloutShrinker.Core/CodexRolloutShrinker.Core.csproj -o artifacts/library
The package ID is CodexRolloutShrinker.Core; its version follows
Directory.Build.props. Add that local feed when restoring your consumer.
There are no production dependencies beyond the .NET base class library, and
consumers do not reference the CLI executable.
The following call is the same one executed by the independent package consumer. The three required program arguments are source session ID, Codex home, and selected Codex binary path. An optional fourth argument is the absolute SQLite target: supplying it requests default cache reset; omitting it selects Skip.
var result = await ForkCompactShrinker.RunAsync(
Guid.Parse(args[0]),
new ForkCompactShrinkOptions
{
CodexHome = args[1],
CodexPath = args[2],
SqliteHome = args.Length == 4 ? args[3] : null,
SkipHistoryCacheReset = args.Length == 3,
});Use CodexRolloutShrinker.Core.Automation. A non-null result.NewSessionId
means the requested workflow completed; select that ID when the host next
starts Codex. On failure, retain result.Status, CacheReset, and Evidence;
an absent success ID never authorizes retrying a possible persistent fork.
Evidence preserves an observed child ID, verified backup manifest, surviving
publication staging, and classified cache/shutdown facts without transcript
contents. The public call itself is the explicit apply action; it does not read
Console or ask for a second confirmation.
The host must keep the selected Codex home cold and serialize calls. Skip does not enable use alongside a still-running app-server for that home. Default cache reset is global: an existing exact Codex process now causes refusal before the first owned app-server or fork. Its final process/file guards still run; a failure in that final stage retains the detached child and reports no success ID. Source rollouts and recovery backups remain, so child byte savings are not net disk savings across the entire home.
Final verification evidence distinguishes reconciliation_final_fence from
reconciliation_storage_fence and preserves the inspection failure code.
An additional rollout path does not imply that source or child bytes changed;
either failure still returns PublishedUnverified without a success ID.
The library contract defines all
result states, cancellation behavior, and input defaults. The runnable consumer
is built outside the repository from a real local PackageReference, without
test friend access, and exercised against synthetic homes by:
dotnet test CodexRolloutShrinker.slnx --filter FullyQualifiedName~PackageConsumerProbeTests
After committing the implementation and recording the Linux test result, build commit-pinned development packages from the repository root:
./eng/dev-release.sh --linux-test-evidence "dotnet test: <result>"
The script does not publish to GitHub or run the test suite. It requires a clean
non-ignored worktree, rechecks the commit and worktree after the build, and
writes framework-dependent linux-x64 and win-x64
archives beneath artifacts/dev/v<version>-<short-commit>/, together with the
runtime requirements, checksums, and a BUILDINFO.txt containing the full
commit and supplied Linux evidence. It runs the extracted Linux launcher's
usage smoke test and checks the cross-published Windows launcher's PE magic, but
does not claim native Windows validation.
eng/release.sh is the fail-closed release entry point for the currently
validated linux-x64 and win-x64 targets. It requires a clean main, an
explicit semantic version equal to the project version, and any existing local
or remote tag to point to HEAD. Formatting, the complete ordinary test suite,
both framework-dependent single-file publishes, archives, and SHA-256 checksums
are completed before it pushes main or the tag. A published release also
requires absolute executable paths in CODEX_LOCK_INTEROP_BINARY and
CODEX_LEGACY_CAPABILITY_BINARY: it reruns the compatible V1 full-chain/schema
gates and the legacy pre-fork rejection gate before publishing. Published
executables require the x64 .NET 10 Runtime (Microsoft.NETCore.App 10.0), but
not the SDK.
Those target statements describe packaging and do not substitute for platform
interoperability evidence. The V1 public-Codex full chain has passed on Linux
and native Windows/NTFS for the exact recorded artifacts.
Build and inspect the release assets without touching GitHub:
./eng/release.sh 0.2.0-rc.2 --build-only
After authenticating GitHub CLI with an account that can write origin, create
the tag when absent, push main and the tag, and publish the release:
gh auth login
export CODEX_LOCK_INTEROP_BINARY=/absolute/path/to/compatible/codex
export CODEX_LEGACY_CAPABILITY_BINARY=/absolute/path/to/legacy/codex
./eng/release.sh 0.2.0-rc.2
The release assets do not bundle Codex. fork-compact-shrink requires a
separately installed compatible Codex executable; inspect, shrink, and
restore do not. Without --codex, the tool resolves codex from PATH. On
Windows it recognizes both a native codex.exe and the standard npm
codex.cmd entrypoint. On Linux it recognizes the standard npm
codex symlink to @openai/codex/bin/codex.js. The npm launcher itself is not
executed: the tool selects the architecture-matched native executable from the
bounded adjacent npm package layouts, then binds that regular file by path and
SHA-256. Unknown symlink or script launcher shapes remain refused; for an
unsupported package-manager layout, pass the absolute packaged native path with
--codex. Windows app-server metadata may report a canonical rollout with the
\\?\ extended drive/UNC spelling; the tool normalizes those exact forms for
comparison while continuing to reject device namespaces, aliases, and paths
outside the selected active sessions tree. --codex remains available for an
explicit absolute path. A hyphenated
version such as 0.2.0-rc.2 is published as a GitHub prerelease.
Assets are written beneath ignored artifacts/release/v0.2.0-rc.2/. Each
archive contains one platform launcher and RUNTIME_REQUIREMENTS.txt. The
release script rejects a launcher above 5 MiB so an accidental return to
bundled-runtime output cannot silently pass. A rerun is safe
after failures before release creation; a pre-existing published release is
reported without replacing any remote asset. The script does not move an
existing tag, overwrite a release, sign binaries, publish NuGet packages, or
claim support for unvalidated runtime identifiers.
The historical v0.1.0 assets were self-contained. Starting with v0.1.1,
GitHub Release assets are framework-dependent to avoid duplicating roughly
70 MiB of .NET runtime in each platform executable.
The accepted V0 limitations are intentional:
- only cold, local, legacy-history fork rollouts;
- no SQLite writes;
- no conversion into an independent session-tree root;
- no attempt to render the compaction recap as a visible transcript message;
- no full rollback replay, non-contiguous dead-turn deletion, or checkpoint rewrite beyond the proven legacy safe subset;
- fail closed on unfamiliar storage shapes.
The V1 legacy path keeps that byte-transformation boundary. Its implemented paginated extension is a separate raw-span transformation for strict standalone paginated user sources, not a relaxation of V0. Neither branch makes the external app-server/model/hook/SQLite chain atomic, mutates the source rollout, accepts Codex 0.147.0 for the new command, or cleans up partial children.
See the V0 design for the complete contract. The fork/compact orchestration, schema and source-readiness gates, state machine, partial-child semantics, and validation matrix are specified in the V1 design. The library API, optional history-cache reset, result contract, and consumer validation are described in the library V1 contract. Implementation and platform evidence are recorded in the library acceptance note. The mode-specific standalone paginated transformation, manifest V2, publication, reconciliation, and failure contract is specified in the paginated detachment V1 design. The exact Linux and native Windows public-binary full-chain evidence is recorded in the V1 public Codex canary note. The initial real-shape investigation of a paginated reference child and a detached-copy prototype is recorded in the paginated detachment research note. The legacy rollback subset and its reverse-segment proof are specified in the rollback note. The exact WP4 protocol, evidence, and rerun command are recorded in the writer-lock interoperability note. WP5a platform semantics and evidence are recorded in the atomic replacement note. WP5b lock-bound transaction semantics and failure evidence are recorded in the shrink transaction note. WP6 hash-guarded recovery semantics and evidence are recorded in the restore transaction note. WP7 public cold-resume and next-request evidence is recorded in the public Codex resume canary note. WP8 CLI confirmation, result, and recovery semantics are recorded in the mutating CLI note. The first real-rollout result and sanitized upstream issue data are recorded in the live rollout evidence note. The global opaque-file cache reset experiment is specified in the experimental cache reset note.
The recommended implementation is C# targeting .NET 10. The production CLI uses only the .NET base class library. GitHub Releases publish compact, framework-dependent single-file binaries for Windows and Linux and require the x64 .NET 10 Runtime on the target system. A self-contained publish remains possible for an operator who explicitly needs one.
Python would make the first parser shorter, but .NET is a better fit for this tool's hard parts: distributing one executable, handling Windows paths and file replacement, expressing explicit data contracts, and testing native lock compatibility on both Windows and Unix.
Real rollout snapshots live under research/private/, which is ignored by Git.
See research/README.md before handling those files.