Skip to content

refactor(sessions)!: delete the Codex v1 source-cursor replay - #2730

Merged
ScriptedAlchemy merged 1 commit into
masterfrom
fleet/legacy-api-sweep-ingest
Sep 30, 2026
Merged

ScriptedAlchemy merged 1 commit into
masterfrom
fleet/legacy-api-sweep-ingest

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

What was wrong

Codex admission read the retired per-session source cursor (ObservationSourceIdentityV1::for_provider(codex, session)) on every pass. When the v2 cursor from codex_observation_source_v2 lagged it, legacy_cursor_matches_current_file + replay_advanced_current_user_messages (CodexAdmissionMode::CurrentUserMessageReplay) replayed current user messages into v2. That is a data migration from an old identity into the new one, which the product rules forbid.

Why deleting it cannot duplicate observations

What changed

  • codex/observation.rs: admission reads only the v2 source. Deleted CodexAdmissionMode, replay_advanced_current_user_messages, legacy_cursor_matches_current_file, the v1 cursor read, and the replay-frame branches.
  • jsonl_observation_admission.rs: deleted the replay-only plumbing: with_required_start_cursor, with_max_end_offset, JsonlFrameAdmission::durable_unless_observation_exists, and the per-frame skip_if_observation_exists precheck path.
  • HostAdmission::observation_receipt: removed along with its production (tracedecay-host-admission) and in-memory impls and HostAdmissionOutcome::observation_point_read_unavailable. Its only caller was the replay precheck.
  • Deleted the tests that only protected the replay: replay_boundary_tests, legacy_current_message_migration_records_receipted_duplicate_coverage, and the three runtime_acceptance_suite legacy-migration tests.

Proof

Fail before / pass after. retired_source_cursor_neither_gates_nor_narrows_canonical_admission (sessions lib) seeds a v1 Codex cursor covering the whole rollout into an admitted store. It then drives the production hook entry try_admit_codex_jsonl_observations_for_project_with_admission twice and asserts literal counts: 3 frames persisted and 3 observations on the first pass, 0 persisted and still 3 observations on the second.

  • origin/master: FAILED with assertion failed: !first.source_deferred, because the v1 cursor turned the pass into a deferred user-message replay.
  • This branch: ok.

Typed operator state for a store carrying v1 cursor + rows. retired_codex_source_cursor_store_refuses_sessions_with_scoped_reset (runtime_acceptance_suite) does the following:

  • Admits a 3-frame rollout into a registered profile store and seeds the v1 cursor.
  • Ages the store to the pre-fix(observation): reset stores predating unified identity #2555 shape (removes the unified-identity marker).
  • Reopens it and asserts ResetRequired { authority: "observations", reason: "observation rows predate the unified observation identity and cannot be read; reset the profile so ingestion can rebuild them from host transcripts" }, with the observations row count exactly 3 before and after.

Suites:

Suite Result
cargo test -p tracedecay-sessions --lib 566 passed
-p tracedecay-host-admission --lib 91 passed
-p tracedecay --features test-helpers --test runtime_acceptance_suite -- codex 6 passed
--test transcript_ingest_suite -- codex (with the debug CLI built) 33 passed
  • cargo clippy -p tracedecay-sessions -p tracedecay-host-admission --all-targets -- -D warnings: clean.
  • cargo clippy -p tracedecay --features test-helpers --test runtime_acceptance_suite -- -D warnings: clean.
  • cargo fmt --all -- --check: clean.
  • No cfg(windows) code or Windows-only callers touched.

Runtime journey. Debug CLI from this branch, isolated HOME/TRACEDECAY_DATA_DIR, one daemon under systemd-run --user --scope -p MemoryMax=6G -p MemorySwapMax=1G, synthetic Codex rollout plus Cursor transcript:

$ tracedecay init
initialized …/journey/project; daemon code-index reconciliation requested
$ tracedecay sessions import --project-path $P
session import scheduled (session-sync.a736db17…); historical catch-up has remaining work 2
$ tracedecay sessions refresh begin --project-path $P --session-id 019a0000-0000-7000-8000-00000000c0de --provider codex --source 3 --target 3 --json
{ "outcome": "started", "handle": "srh_68a9a5ff…", "scope": "project", … }
$ tracedecay sessions refresh status … --json
{ "outcome": "complete", "receipt": { "coverage": { "visible": 2, … }, "frontier": { "committed_through": 3, "observed_through": 3 }, … "reason": { "kind": "caught_up" } … } }
$ tracedecay sessions search quartzledger --provider codex --project-path $P
[codex] proj_30f94f100d16c44d assistant: The quartzledger reconciliation is consistent.
[codex] proj_30f94f100d16c44d user: Codex journey: audit the quartzledger reconciliation

Fixes #2700

BREAKING CHANGE: Codex admission ignores cursors of the retired per-session source identity; stores that still carry them beside observation rows must be reset with tracedecay wipe --stale --yes.

Codex admission read the retired per-session source cursor on every pass
and, when the v2 cursor lagged it, replayed current user messages into the
v2 source. That carried data from an old identity into the new one.

Only binaries before the unified observation identity wrote that cursor,
and a store holding observation rows without the unified-identity marker
is already refused with the scoped `tracedecay wipe --stale --yes` reset.
Admission now reads only the v2 source, and the replay-only JSONL
admission plumbing (required start cursor, end-offset bound, existence
precheck) and `HostAdmission::observation_receipt` are gone.

BREAKING CHANGE: Codex admission ignores cursors of the retired
per-session source identity; stores that still carry them beside
observation rows must be reset with `tracedecay wipe --stale --yes`.
@changeset-bot

changeset-bot Bot commented Sep 30, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e1d5589

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@ScriptedAlchemy
ScriptedAlchemy merged commit 611207f into master Sep 30, 2026
6 of 7 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the fleet/legacy-api-sweep-ingest branch September 30, 2026 06:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sessions: Codex v1-source replay is a data migration path

1 participant