Skip to content

[Feature]: extract the Codex history manifest contract into a shared leaf #2436

Description

@Ingwannu

Area

Codex integration

What are you trying to accomplish?

Give the Codex history backup manifest one pure, shared schema-and-identity boundary that both restore and native-residue inspection consume.

What prevents this today?

The history provenance repair introduced by PR #2424 validates the same on-disk manifest contract independently in two places:

  • src/codex/history-provider.ts validates the manifest before restore and mutation.
  • src/codex/native-residue.ts repeats the version, database path, entry id, absolute rollout path, provider/source tuple, and hasUserEvent checks before deciding whether the manifest is managed residue.

The copies agree today, but they are already maintained through different constants and path-normalization code. A future source, provider tuple, or manifest version can therefore become restorable while the residue classifier rejects it, or vice versa. That would make the coordinator and restore path disagree about ownership of the same file.

This is also an ownership problem: the manifest format is a data contract, but its parsing rules currently live inside two larger state-transition modules. After #2424, history-provider.ts is approximately 1,569 lines and owns file patching, SQLite CAS, retries, rollout reconstruction, status probes, and manifest parsing together.

What should OpenCodex do?

Extract a small src/codex/history-manifest.ts leaf that owns only:

  • the versioned manifest and entry types;
  • platform-aware state-database path identity;
  • pure validation from unknown data to a parsed manifest or a typed failure reason;
  • the allowed provider/source provenance tuples;
  • shared invalid-case fixtures or helpers used by both restore and residue tests.

Keep filesystem policy with the callers: regular-file checks, reads, fingerprints, atomic writes, deletion, rollout inspection, SQLite access, retries, and mutation must remain in their existing owners. history-provider.ts should map the shared typed validation result into its strict restore result, while native-residue.ts should map the same result into clean, residue, or indeterminate.

The extraction must preserve the current manifest bytes, accepted and rejected inputs, Windows path behavior, fail-closed restore behavior, and native-residue classification.

Example usage or interface

type HistoryManifestValidation =
  | { ok: true; manifest: CodexHistoryBackupManifest }
  | { ok: false; reason: "schema" | "foreign-database" };

validateCodexHistoryBackupManifest(raw, expectedStateDbPath);

Both current callers should use this result instead of maintaining separate field-by-field checks.

Alternatives or workarounds

Keeping the two validators synchronized through duplicated tests works only while every future format change updates both lists correctly. Importing the entire history provider from the residue classifier would remove textual duplication but would pull mutation, SQLite, retry, and rollout-writing ownership into a read-only startup inspection path.

The smallest durable boundary is a pure leaf module with no filesystem or database side effects.

Additional context

  • Source of the duplicated contract: PR fix(codex): preserve routed history provenance on restore #2424.
  • src/codex/history-provider.ts: strict restore-time manifest validation and state-database path comparison.
  • src/codex/native-residue.ts: read-only manifest residue classification with a second copy of the same contract.
  • CodeRabbit also identified the drift risk during fix(codex): preserve routed history provenance on restore #2424 review; it is intentionally a follow-up because the duplication does not invalidate the current provenance bug fix.
  • This work must not reopen broad legacy relabeling or change the explicit ocx recover-history --legacy-openai --yes behavior.

Checks

  • I searched existing issues and documentation.
  • This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
  • I removed secrets and personal data.

Metadata

Metadata

Assignees

No one assigned

    Labels

    cliCLI, config inject, packaging flagsenhancementNew feature or requestlanded-via-maintainerOriginal PR closed after landing via a maintainer merge train

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions