Keep Codex Fast
Use this skill to inspect and safely maintain local Codex state. The goal is to reduce local drag without surprising the user or losing continuity.
Primary principle: preserve continuity before applying changes. For active repo chats the user may continue, recommend a comprehensive handoff document and reactivation prompt before archiving anything.
Safety Rules
- Inspect before mutating.
- The first run must be report-only. Report mode must not write files, create backups, move folders, or change local Codex state.
- Back up before applying changes. Use
--backup-onlywhen the user wants backups without moving or changing local state. - Archive or move files instead of deleting them. Do not permanently delete user chats, logs, worktrees, memories, skills, plugins, or automations.
- Write manifests and restore scripts when sessions/worktrees are moved.
- If Codex is running, default to report-only. Apply changes only after Codex is closed or when the user explicitly accepts waiting for Codex to exit.
- Never modify or copy credential files unless the user explicitly asks for that. Back up memory/skill/plugin/automation files before touching local state.
- Treat backup folders as private local artifacts because they can contain Codex metadata. Do not ask users to publish or share backups unless they have reviewed them first.
- Do not print raw thread IDs, chat titles, local paths, or process paths unless the user asks for details or runs
--details. - Before applying changes, tell the user to create handoff docs for active repo chats they may continue.
- Before archiving any active repo chat the user may want to continue, recommend creating a comprehensive handoff doc plus a reactivation prompt.
- Do not archive old-but-important active repo chats until the user either confirms a handoff exists or confirms they do not need one.
Mental Model
There are three modes:
- Inspect: report-only, no writes.
- Maintain: normal
--apply; backs up, archives non-pinned sessions older than 60 days, repairs oversized SQLite display metadata, archives closed spawned-child sessions older than 3 days, immediately losslessly compresses archived sessions, compresses all but the newest expanded maintenance backup, moves stale worktrees, rotates logs, prunes dead config, and normalizes paths. - Apply opt-outs:
--no-repair-thread-metadata-bloatretains oversized display metadata, and--no-archive-closed-spawn-childrenretains closed spawned-child sessions. The rollout transcript stays intact either way. - Optional malformed-task archive:
--apply --archive-malformed-local-tasks; archives active no-user-event local task sessions with suspicious workspace roots such as/or OS temp folders after backup. - Closed spawned-child archive: normal
--applyarchives active child sessions whosethread_spawn_edges.statusisclosed, are at least 3 days old, and are not pinned.
Default Workflow
- Reassure the user: the first run is read-only, privacy-safe, and the skill archives instead of deleting when changes are later applied.
- Run the bundled script in report mode:
scripts/run-python3.sh keep_codex_fast.py
The launcher works when python3 is absent from the shell PATH: it checks common Homebrew, system, and Python.org locations. Set KEEP_CODEX_FAST_PYTHON to an absolute interpreter path for another installation.
- Summarize:
- active session size
- archived session size
- cold archived-session compression candidates, existing compressed sessions, and conflicts
- expanded maintenance-backup compression candidates, existing compressed backup sets, and the retained newest restore point
- largest active sessions
- thread metadata bloat: active title/preview character totals, max title/preview lengths, and over-limit counts
- malformed local task candidates: no-user-event active local tasks with suspicious workspace roots
- closed spawned-child candidates: completed subagent child sessions that are still active/unarchived
- stale worktree candidates
- log size
- bad Windows
\\?\path counts - config project prune candidates
- top Node/dev processes
- Before applying changes, recommend that the user create handoffs for all active repo chats they may continue. Explain that handoffs let them archive heavy chats and resume from docs in fresh threads.
- Identify large/old active repo chats that may still matter. For each one the user wants to continue, create or update:
- a repo-local handoff doc
- a reactivation prompt that can start a fresh chat without losing the thread
- If the user wants to apply the recommended maintenance, ask them to close Codex or use
--wait-for-codex-exit, then run:
scripts/run-python3.sh keep_codex_fast.py --apply
If the user has just created a handoff inside an old chat, explain that --archive-older-than-days uses updated_at by default. Use --archive-age-field created_at when the user's intent is "archive chats created before the threshold even if a recent handoff updated them":
scripts/run-python3.sh keep_codex_fast.py --apply --archive-older-than-days 60 --archive-age-field created_at
If the user wants to archive exactly one confirmed session, use a targeted archive instead of a broad --archive-older-than-days 0 sweep:
scripts/run-python3.sh keep_codex_fast.py --apply --archive-thread-id THREAD_ID
or:
scripts/run-python3.sh keep_codex_fast.py --apply --archive-rollout-path /path/to/rollout.jsonl
- Verify after applying:
scripts/run-python3.sh keep_codex_fast.py
- Ask whether the user wants a recurring report-only reminder:
- weekly for heavy Codex use across many repos/terminals
- biweekly for lighter use
- no reminder if they prefer manual maintenance
If the user wants automation and the Codex app automation tool is available, create only a recurring report/reminder automation. Do not recommend recurring mutating maintenance, because automation cannot know whether the user created handoffs. The prompt must say not to pass --apply, not to archive/move/prune/rotate/normalize/delete/mutate local state, and to remind the user that manual apply should happen only after handoffs are confirmed and Codex is closed.
What Apply Does
- Backs up important metadata under
KEEP_CODEX_FAST_BACKUP_ROOTwhen that configured location is available with at least 5 GiB free. Without a configured root, or when it is unavailable or below the threshold, it uses the private local fallback~/.codex/backups/maintenance/and reports the reason. It never silently defaults bulk backups to cloud-synced Documents. - Losslessly packages and compresses all older
keep-codex-fast-*backup directories after creating the newest restore point. It verifies the Zstandard stream and decompressed tar SHA-256 before retiring an expanded source, writes a private manifest, and reports progress for every backup. Use--no-maintenance-backup-compressto opt out. - Writes executable restore scripts and prints copy-paste-safe restore commands using the exact Python interpreter that ran maintenance.
- Archives old non-pinned sessions to
~/.codex/archived_sessions/. - Losslessly compresses archived rollout JSONL with multithreaded Zstandard, including sessions archived by the current run, verifies each compressed stream before removing its uncompressed source, and writes a private manifest. Use
--no-cold-compressto opt out of this part of normal apply, or--cold-compress-older-than-days Nto retain a warm uncompressed window. - Uses
updated_atfor age-based session archiving by default, orcreated_atwith--archive-age-field created_at. - Supports targeted session archiving with
--archive-thread-idor--archive-rollout-path, still backup-first and archive-only. - Normalizes Windows extended paths like
\\?\C:\...inside local SQLite text fields and selected metadata files such asconfig.toml. - Prunes missing/temp project blocks from
config.tomland writes UTF-8 without BOM. - Moves stale worktrees to
~/.codex/archived_worktrees/. - Rotates
logs_2.sqlite*andlog/codex-tui.loginto~/.codex/archived_logs/only when above the threshold. - Reports heavy Node processes without killing them.
- Repairs pathological active thread titles and
first_user_messagepreviews during--applyafter backup;--no-repair-thread-metadata-bloatreports without repairing. - Reports malformed active local task sessions with
has_user_event=0and suspiciouscwdvalues. It only archives them when the user explicitly opts in with--archive-malformed-local-tasks. - Archives closed spawned-child sessions older than 3 days during
--apply, excluding pinned sessions and sessions outside~/.codex/sessions;--no-archive-closed-spawn-childrenreports without archiving.
Report mode does none of those mutations. It only prints counts and pseudonymous candidates. Use --details when raw IDs, titles, or paths are needed for diagnosis.
Recommended Policy
- Keep the last 60 days of non-pinned chats active by default.
- Use handoff docs for important old threads.
- Start fresh threads from handoff docs instead of repeatedly resuming giant chats.
- Run weekly maintenance if Codex is used daily across many repos/terminals.
- Treat archived rollout JSONL as cold storage during normal apply. Newly archived sessions are compressed immediately by default; active sessions are never compressed.
- Offer weekly or biweekly report-only reminders after the first successful apply; do not assume the user wants recurring maintenance.
- When in doubt, leave a chat active or ask the user. Never archive a chat that is pinned, current, or explicitly marked as still needed without a handoff.
- Treat title/preview repair as metadata repair only. The full rollout transcript remains in the session JSONL; bounded SQLite fields are for list/navigation display.
- Treat malformed local task archiving as cleanup for synthetic/no-user-event sessions. It should not target normal chats with user events.
- Treat closed spawned-child archiving as cleanup for completed subagent children only. It must not target open spawn edges, parent threads, pinned threads, or ordinary sidebar chats just because they are old.
Cold Archived Sessions
Normal report mode counts archived rollout JSONL eligible under the configured compression age (all archived sessions by default) and reports their uncompressed size, already-compressed count and size, and source/destination conflicts. It does not write manifests or compress files.
Normal --apply treats archived rollout JSONL as cold storage immediately, including sessions archived by that run. It compresses one file at a time with multithreaded Zstandard level 1, verifies the decompressed size and SHA-256 digest, leaves files unchanged when Zstandard is not smaller, atomically installs useful .jsonl.zst files, and only then removes each original .jsonl. Each run writes a private manifest under the archived-session root. A failure stops further compression but leaves every completed file independently restorable. The zstd command must be installed and available on PATH. Set --cold-compress-older-than-days N when a warm uncompressed archive window is preferred.
Cold archived tasks remain indexed, but Codex cannot open their rollout until it is restored. Restore one task after Codex is closed:
scripts/run-python3.sh codex_session_cold_storage.py restore --thread-id THREAD_ID --apply
Existing restore-sessions.py helpers generated by normal maintenance recognize cold .jsonl.zst files and restore them transparently. The standalone and generated restorers retain support for legacy .jsonl.gz files. Use --no-cold-compress for a particular apply when direct opening of every archived task matters more than disk space.
Thread Metadata Bloat
Codex Desktop can become slow when threads.title or threads.first_user_message stores a full prompt/history-sized value instead of a display title or preview. This affects the thread list/navigation path before the UI renders anything.
The script reports active thread count, total title/preview characters, maximum title/preview length, active titles over the configured title limit, and active previews over the configured preview limit and over 10k characters.
Normal --apply repairs metadata-bloat candidates after backup and only when Codex is not running:
scripts/run-python3.sh keep_codex_fast.py --apply
That bounds active threads.title and threads.first_user_message values. Defaults are 120 characters for titles and 240 characters for previews. If a thread already has a friendly name in session_index.jsonl, the repair writes that name back into the SQLite display title instead of replacing it with a shortened prompt, including already-bounded prompt fallback titles from earlier repairs.
The targeted repair manifest stores the old full title/preview values so the change can be reversed. Treat thread-metadata-repairs.jsonl, restore-thread-metadata.py, and the whole backup folder as private local artifacts.
This is a local maintenance workaround for metadata bloat. It does not solve app renderer hydration of very large rollout histories; that needs upstream staged/paged thread loading.
Historical SQLite Backup Archival
Use scripts/archive_codex_sqlite_backups.py when ~/.codex/backups/cli/state_5.sqlite.*.bak snapshots consume substantial space. This is deliberately separate from normal keep_codex_fast.py --apply: moving restore points across filesystems has its own approval, capacity, integrity, and restore boundary.
The helper is report-only by default. It inventories the backup family, retains the newest local restore point, and reports the eligible count and GiB without writing anything:
scripts/run-python3.sh archive_codex_sqlite_backups.py
Apply only after Codex Desktop and Codex CLI have exited. Name an archive root on a different mounted filesystem; the helper refuses same-filesystem archival because that would not recover source-disk space:
scripts/run-python3.sh archive_codex_sqlite_backups.py \
--apply \
--archive-root "/Volumes/External Drive/Codex Archive/sqlite-backups"
Use --wait-for-codex-exit when the command should remain pending until Codex closes. The helper emits flushed, timestamped heartbeats while waiting and progress for each validation, hash, copy, verification, manifest, and source-retirement stage. Before removing any source snapshot, it validates every candidate with SQLite quick_check, hashes the source, writes and fsyncs a verified archive copy, records a private JSONL manifest, and then removes only that historical source backup. It never archives the newest retained restore point. Archive roots and manifests contain private Codex metadata and must remain local.
The completion output prints an exact restore command. Restore also requires Codex to be closed and --apply; it verifies archive hashes, refuses conflicting destination files, copies missing backups into their original directory, and leaves the archive copies intact:
scripts/run-python3.sh archive_codex_sqlite_backups.py \
--restore-manifest "/path/to/codex-sqlite-backups-TIMESTAMP.jsonl" \
--apply
Maintenance Backup Compression
Set KEEP_CODEX_FAST_BACKUP_ROOT to opt into a preferred external or local backup root. Normal report mode uses that configured root only when its storage is available with at least 5 GiB free; otherwise it reports the private local fallback and the reason. It inventories expanded keep-codex-fast-* backup directories, existing .tar.zst archives, and the newest expanded restore point without writing anything. Normal --apply creates its fresh backup first, retains that newest restore point expanded, and compresses each older backup independently.
The helper packages one directory at a time, writes a temporary tar and Zstandard stream in the private compressed/ directory, verifies Zstandard integrity and the decompressed tar SHA-256, fsyncs the archive, writes a JSONL manifest, and only then retires the expanded source. It flushes progress at every stage and stops on the first failure. Finder-created .DS_Store residue must not be reported as fully retired unless the source directory is actually absent.
Run the dedicated report or apply path directly when only maintenance backups need attention:
scripts/run-python3.sh compress_codex_maintenance_backups.py
scripts/run-python3.sh compress_codex_maintenance_backups.py --apply
Restore one archive after Codex is closed. Restore verifies the compressed stream, rejects an existing destination, confines extraction to the expected top-level backup directory, and leaves the cold archive intact:
scripts/run-python3.sh compress_codex_maintenance_backups.py \
--restore-archive "/path/to/keep-codex-fast-TIMESTAMP.tar.zst" \
--apply
Malformed Local Task Sessions
Codex Desktop can become slow when active local task rows have no user event and an unusable workspace root, such as / or an OS temp folder. Third-party app-server integrations can create this state. The visible symptom is repeated No cwd found for local task log lines for those conversation IDs while Desktop thread-list rendering becomes sluggish.
The script reports these candidates in report mode and normal apply mode. Normal apply does not archive them. If the user explicitly opts in, after backups and only when Codex is not running, run:
scripts/run-python3.sh keep_codex_fast.py --apply --archive-malformed-local-tasks
That moves matching rollout JSONL files into ~/.codex/archived_sessions/, marks those rows archived in SQLite, and writes a restore manifest/script. The predicate requires has_user_event=0, an active/unarchived thread, a suspicious cwd, and a rollout file under ~/.codex/sessions.
Closed Spawned-Child Sessions
Subagent-heavy workflows can leave many completed child sessions active in SQLite. These can add sidebar and thread-loading work even after the subagent work is finished. The safe predicate is narrow: thread_spawn_edges.status = 'closed', the child thread is still active/unarchived, it is older than the configured age threshold, it is not pinned, and its rollout file is still under ~/.codex/sessions.
Report mode only reports these candidates. Normal --apply archives eligible closed spawned children after backup and only when Codex is not running:
scripts/run-python3.sh keep_codex_fast.py --apply
The default age threshold is 3 days. To retain these candidates during a one-off apply, use:
scripts/run-python3.sh keep_codex_fast.py --apply --no-archive-closed-spawn-children
This moves matching rollout JSONL files into ~/.codex/archived_sessions/, marks those child rows archived in SQLite, and writes a restore manifest/script. It does not archive open subagents, parent threads, pinned threads, or threads without a closed spawn edge.
Handoff Doc + Reactivation Prompt
For important active repo chats, create a handoff before archiving. Prefer a repo-local path such as docs/codex-handoffs/YYYY-MM-DD-topic.md or a user-approved docs location.
Use references/handoff-template.md when the user wants a concrete template.
A handoff document converts an old chat into durable project memory. It should let a fresh Codex thread continue after reading the repo and the handoff, without needing the original chat history.
Offer this prompt for each active repo chat the user may want to continue:
Create a comprehensive handoff document for this repo/session before I archive Codex history.
Include:
- repo/path and branch
- current goal
- what we already completed
- files touched or investigated
- commands/tests already run
- known errors, warnings, or failing checks
- open decisions
- constraints, user preferences, and do-not-touch areas
- the next 3-7 concrete steps
Also include a reactivation prompt I can paste into a fresh Codex chat so it can continue from this handoff without relying on the old chat context.
Save the handoff in a sensible repo-local place like docs/codex-handoffs/YYYY-MM-DD-topic.md unless this repo already has a better handoff location.
The handoff should capture:
- repo/path and branch
- current goal
- what was already done
- key files touched or investigated
- commands/tests already run
- known failures or warnings
- open decisions
- next 3-7 concrete steps
- any constraints, user preferences, or "do not touch" areas
Add a reactivation prompt at the top or bottom:
We are continuing from this handoff. Read this document first, inspect the current repo state, verify what still applies, and continue from the next steps without assuming the old chat context is available.
Exact Thread-ID Forensics
Use this path when the user supplies one exact Codex thread ID and asks what it is, why it was expensive, whether it is stale, or whether it should be archived.
- Try the Codex task tools first: exact
read_thread, then exactlist_threadslookup. - If the live index cannot resolve it, search the exact ID in
~/.codex/session_index.jsonl, then locate only the matching rollout under~/.codex/sessions/or~/.codex/archived_sessions/. - Extract only the evidence needed: title, origin/source, workspace, timestamps, automation identity, final assistant result, final
token_count,task_completeduration, and rollout file size. - Compare the task's claims with current project truth before judging its output. A completed task can still be wrong because it used a stale checkout or source.
- Report the diagnosis before proposing archive or repair. Do not mutate the session, automation, SQLite metadata, or rollout file unless the user approves that exact action.
Do not broaden an exact-ID request into a scan of every rollout. Treat live-index failure as a routing/index limitation, not evidence that the task does not exist. Raw IDs and private paths may be shown when the user supplied the ID and asked for forensic detail; otherwise keep the normal privacy-safe summary.
Automation Reminder Prompt
Offer this after the first report/apply/verify cycle:
Use $keep-codex-fast to create a recurring Codex maintenance reminder.
Schedule it weekly if I use Codex heavily, or biweekly if that seems safer.
The reminder should:
- run the keep-codex-fast report first
- never pass --apply or run mutating maintenance automatically
- never archive, move, prune, rotate, normalize, delete, or mutate local Codex state
- remind me to create comprehensive handoff docs and reactivation prompts for active repo chats before any manual apply
- summarize active session size, archived session size, extended path candidates, old session candidates, worktree candidates, log size, and top Node/dev processes
- summarize cold archived-session candidates and existing compressed-session size without compressing anything
- summarize malformed local task candidates without archiving them
- report heavy Node/dev processes without killing them
- tell me that manual apply should only happen after I confirm handoffs exist or are not needed and Codex is closed
Anti-Patterns
Avoid these behaviors:
- deleting sessions, logs, worktrees, memories, plugins, or skills permanently
- applying changes while Codex is actively writing the DB
- archiving important repo chats before creating handoff docs
- treating active history size as "bad" without checking whether the user needs continuity
- treating preview metadata repair as deletion of the actual rollout transcript
- compressing active session rollouts or changing SQLite rollout paths to compressed
.jsonl.zstor.jsonl.gzpaths - killing Node/dev processes automatically
- rewriting
config.tomlwithout a backup and parse check - writing UTF-8 TOML with a BOM on Windows
- promising speed gains as universal fact; frame improvements as local-state maintenance results
- making users feel like they did something wrong by using Codex heavily
User-Facing Caution
Tell users this does not permanently delete chats, worktrees, or logs. It moves them into archive folders and writes restore helpers. The only removed content is stale metadata, such as project entries pointing to folders that no longer exist, and even that happens after backing up config.toml.
Also tell users that normal --apply repairs thread title/preview bloat after backup while preserving the real transcript in rollout JSONL. Use --no-repair-thread-metadata-bloat when a one-off apply should only report those candidates.
Also tell users backup folders can contain private local Codex metadata, including old thread titles and first-message previews. They should keep backups local and avoid publishing or sharing them unless they have reviewed what is inside.
Tell users that cold archived sessions are losslessly preserved but cannot be opened directly until restored. Normal apply performs this compression only after Codex has closed, and active sessions are excluded.