Skip to content

fix(projection): settle Codex passes after session project_path collisions - #4326

Open
ScriptedAlchemy wants to merge 6 commits into
masterfrom
fix/4279-codex-project-path-collision
Open

ScriptedAlchemy wants to merge 6 commits into
masterfrom
fix/4279-codex-project-path-collision

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Fixes #4279

What was broken

Two defects in the Codex session projection made a collision turn into a pass that never settles.

  1. Wrong collision. reconcile_session_rows_detailed treated two different concrete project_path values for one session as a conflict. A session row written earlier under another working directory (hook payload, LCM, older build) therefore refused every later message of that session as projection session output collided ... project_path. Each refused message is parked as a terminal collision and its text is never projected.
  2. Parked rows counted as pending work. projection_queue_has_items counted terminal rows. They are never drained again, but the host drain reported deferred for them forever, so every user-scope pass ended Backpressured / ingest_pass_backpressured. That is the historical_retry:ingest_pass_backpressured in the report. It also means rows already parked on a beta.88 profile keep every pass backpressured until this lands.

Fix

  • A stored concrete project_path stays when a session names another cwd, like the title and annotations. Each message row keeps its own source. transcript_path, lineage and project_key conflicts still report a collision.
  • projection_queue_has_items ignores rows parked on the terminal deadline.

Evidence

User-scope Codex pass over a synthetic rollout whose session row already exists under another cwd (transcript_ingest_suite):

passes until clean messages projected
master never (6 of 6 passes Backpressured { admitted_units: 1, rejected_units: 1 }) 0
this PR 1 (Complete) 2

Mutation checks:

  • Revert the project_path change: user_scope_projects_a_codex_rollout_whose_cwd_differs_from_the_stored_session fails with still retrying: ... ingest_pass_backpressured.
  • Revert the projection_queue_has_items filter: user_scope_settles_after_a_genuine_codex_session_collision fails with the same message, and genuine_session_collision_is_recorded_and_later_sessions_project fails with "a parked terminal collision is not pending work".

Verified after merging origin/master: tracedecay-global-db and tracedecay-sessions unit_test, //crates/tracedecay:transcript_ingest_suite, :session_suite, clippy (--config=clippy) and rustfmt_check_2024 for the touched crates.

About the CPU in the report

The reporter's beta.88 (950bf21) does not contain #4153, #4154 or #4173, all merged 2026-10-16/17. They address the bulk of the sustained load: #4154 removed a per-replay-sweep GROUP BY over all observations that wrote about 148 GB of TEMP in 4 hours, and #4153 removed the whole-store projection output-state rebuild that stalled large stores. #4173 fixes the mimalloc owner-heap crash and does not affect CPU. This PR removes the unending backpressure retry and the dropped messages. It does not claim to account for the full 1.4 cores.

Not in this PR

  • Rows already parked terminal on an affected profile are not re-armed, so their messages need a projection rebuild to come back.
  • The progress-counter request (provider/stage pending bytes) is a separate feature.
  • Two live rollout files sharing one Codex session id share one source identity and are read as rewrites of each other.

Devin Review

@changeset-bot

changeset-bot Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: d0fde25

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

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

Comment on lines +1160 to +1166
// Two concrete working directories name one session: it was read
// through another rollout, or a resumed rollout moved its cwd. The
// stored cwd stays, like the session's title and annotations, and each
// message row keeps its own source. Replaying the same records in the
// same order therefore reaches the same row, instead of dropping every
// later message of the session as a collision.
actual.project_path.clone()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Later working directories lose scoped messages

When a profile session moves directories, reconcile_session_rows_detailed keeps the first path for every message. Scoped searches filter by that session path, hiding later messages from their directory.

Learn more

A session row contains one project path, but the same profile session can now contribute messages from several working directories. The project-scoped message count and search join every message to that single row and select by its path push_project_identity_predicate. Keeping the first directory lets the projection finish but does not preserve the directory of later messages for these reads. Git history attribution also falls back to the session path session_activity_rows.

Example: A profile Codex session emits message A under /work/one and message B under /work/two. The stored session path remains /work/one. A search scoped to /work/two returns neither message, while /work/one includes both.

Recommended fix: Preserve each message's working-directory identity in a queryable projection and use that identity for directory-scoped message reads. Keep the session-level fallback only for messages without their own directory evidence; cover a session spanning two directories with scoped search and count tests.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

This branch has not been deployed

No deployments
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.

beta.88: sustained >1-core CPU after 15h, slow history catch-up and repeated Codex project_path collisions

1 participant