| name | codex-provider-history-repair |
| description | Safely repair Codex Desktop conversation history after switching any model_provider. Use when old tasks, all-project conversations, workspace threads, or projectless chats disappear; when both root and sqlite state_5.sqlite files exist; or when SQLite, rollout session_meta, session_index.jsonl, and optional global-state metadata must be synchronized without changing chat bodies. |
Codex Provider History Repair
Safety Invariants
- Treat provider IDs as opaque, case-sensitive values. Never hard-code a
migration direction or provider allowlist.
- Migrate visible user main tasks to the current provider; do not restore each
task to its historical provider.
- Never choose the root or
sqlite/ state database merely because it exists,
has newer timestamps, or resembles the other database.
- Preserve every rollout byte after the first line and verify its length and
SHA-256 after replacement.
- A dry-run may run while Codex is open. Apply may run only after Codex Desktop
is fully exited, from an external terminal.
- Fail closed on database ambiguity/corruption, split histories, invalid
rollout or index metadata, an empty user-main-task set, or provider conflicts.
- Do not modify
.codex-global-state.json unless the user explicitly opts in.
Required Workflow
-
Inspect the app layer while Codex is open.
- Call
codex_app.list_threads first when available.
- Record the current user task ID. It is the strongest active-database and
provider signal.
- Record one old title or task ID for verification.
-
Run an initial dry-run:
python C:\Users\<you>\.codex\skills\codex-provider-history-repair\scripts\repair_codex_provider_history.py `
--codex-home C:\Users\<you>\.codex `
--active-thread-id <current-task-id>
-
Review the JSON.
- Confirm
state_db and state_db_selection.
- Inspect every candidate
quick_check,
threads_not_in_selected_db, and differing_threads value.
- Confirm
target_provider and target_provider_source match the provider
currently configured for Codex.
- Require empty
database_errors, rollout_errors,
session_index_errors, and global_state_errors.
- Review every warning. Similar unknown provider IDs may be typos.
safe_to_apply: true is only a preflight result; it is not proof that the
human choices are correct or that Codex is closed.
- If planning returns
mode: "error", inspect the top-level error; the
detailed candidate/error arrays exist only after a plan is built.
-
Resolve ambiguity rather than guessing.
- With two readable databases, automatic selection requires the current task
ID to occur in exactly one.
- If no task ID uniquely identifies the active database, independently
verify it and pass
--state-db.
- If the current task exists in both databases, require
--state-db.
- An unreadable canonical candidate blocks automatic mode. Explicit
--state-db may acknowledge it, but readable split histories still block.
- This script never merges database rows.
- If active/config/newest provider signals conflict, require the exact
--target-provider.
-
Fully exit Codex Desktop. Do not apply from the active Codex task.
-
In an external terminal, rerun the identical dry-run. The script does not
use a saved plan token, so this post-exit run must be reviewed again.
-
Without reopening Codex, apply with the same selectors:
python C:\Users\<you>\.codex\skills\codex-provider-history-repair\scripts\repair_codex_provider_history.py `
--codex-home C:\Users\<you>\.codex `
--active-thread-id <current-task-id> `
--apply `
--confirm-app-closed
--confirm-app-closed is a user assertion, not automatic process detection.
Never pass it while Codex Desktop is running.
- Reopen and verify.
- Call
codex_app.list_threads and find the old task.
- Call
codex_app.read_thread for one important restored task.
- Exit Codex again and repeat the dry-run. Expect one provider bucket,
sqlite_updates: 0, rollout_updates: 0, and no errors.
Explicit Overrides
Explicit database/provider selection must also begin with dry-run:
python .\scripts\repair_codex_provider_history.py `
--codex-home C:\Users\<you>\.codex `
--state-db C:\Users\<you>\.codex\state_5.sqlite `
--target-provider custom
After Codex is closed, rerun that command and only then append
--apply --confirm-app-closed.
Provider signals, when they agree, are considered in this order: explicit
target, active-task provider, top-level config.toml provider, newest selected
user-main-task provider. Conflicting non-explicit signals require an explicit
target. Exact-case mismatches are errors; similar unknown explicit IDs are
reported as warnings because valid provider IDs remain opaque.
Optional Global-State Merge
The default leaves .codex-global-state.json byte-for-byte unchanged. Add
--merge-global-state to both dry-run and apply only when its hints need repair.
The opt-in merge updates only IDs already evidenced by existing projectless or
workspace-hint state. It preserves unrelated records and ordering, does not
classify every task with a cwd as projectless, and does not infer saved
workspace roots from projectless evidence alone.
Repair Scope
Include only rows matching:
archived = 0
and source in ('vscode', 'cli')
and (thread_source is null or thread_source = 'user')
Synchronize:
- selected
state_5.sqlite: threads.model_provider;
- rollout first-line
session_meta.payload.model_provider;
session_index.jsonl by non-destructive ID merge;
- global-state hints only with
--merge-global-state.
Script Guarantees
Preflight runs PRAGMA quick_check, compares split-database IDs and shared-row
metadata, validates rollout type/ID/path/regular-file status, and validates every
existing index line. Rollout paths must be non-symlink files under sessions or
archived_sessions.
Apply creates an online SQLite backup, exact base64 first-line records, and full
copies of every rollout that will change. After BEGIN IMMEDIATE, it rechecks
the complete task plan fingerprint. It preserves unrelated index IDs, order,
and extra fields, and it does not replace valid existing index names/timestamps
with empty database metadata.
Before commit it verifies SQLite providers, rollout metadata, and rollout body
hashes. A pre-commit failure rolls back SQLite and restores modified files from
their backups. After a successful commit it performs no fallible reads and does
not attempt a misleading file rollback.
Keep the generated backup until app-layer verification succeeds.