Skip to content

test(sources): reproduce membership-replay conflict at growth-chain scale - #3666

Merged
Sinity merged 1 commit into
masterfrom
feature/fix/polylogue-5iz4-growth-chain-replay-fixture
Aug 3, 2026
Merged

test(sources): reproduce membership-replay conflict at growth-chain scale#3666
Sinity merged 1 commit into
masterfrom
feature/fix/polylogue-5iz4-growth-chain-replay-fixture

Conversation

@Sinity

@Sinity Sinity commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a regression test reproducing the real production shape behind polylogue-5iz4 (many incremental full-snapshot captures of one growing Codex session, plus a same-identity duplicate arriving from a second "incident recovery" path) and demonstrates that MembershipReplayConflictError is a transient, recoverable refusal, not a permanent one.

Problem

polylogue-5iz4's AC required a redacted structural fixture reproducing the real 804-revision Codex session's crash shape before the retry-eligibility fix in PR #3646 could be verified against something closer to the real shape than the original small #2718 pin (2-3 message sessions). PR #3646 fixed the retry-eligibility bug (a stale parse_error text that permanently excluded the raw from every future rebuild) but did not add the growth-chain-scale fixture the bead's AC calls for, and its own scope note said it could not claim the retried classification actually succeeds without live evidence.

Reading the live archive read-only (/realm/db/polylogue, mode=ro) confirmed the real 22 full-revision blobs for this session form ONE clean, unforked byte-prefix chain (every smaller blob is an exact prefix of every larger one, verified pairwise) and that raw_revision_heads/sessions are empty for this identity today. Reproducing the actual MembershipReplayConflictError needed the same mechanism as the existing test_bundle_replay_respects_unconvertible_single_session_head pin (a same-identity bundle arriving at a second path, colliding with a QUARANTINED dangling append fragment hanging off the accepted head), scaled up to a growth chain with many messages instead of 2-3.

Solution

Added test_growing_file_incident_recovery_duplicate_recovers_after_head_advances to tests/unit/sources/test_live_batch_support.py. It:

  1. Establishes an accepted head via 25 real incremental single-session ingests (growth-chain scale).
  2. Introduces a same-identity "incident recovery" bundle at a second path plus a dangling QUARANTINED append fragment, reproducing MembershipReplayConflictError (fail-closed, correct behavior).
  3. Confirms the recorded parse_error carries the retry-eligible MembershipReplayConflictError: marker storage/repair.py recognizes (polylogue-5iz4 / fix(storage): make membership-replay conflicts retry-eligible by type #3646).
  4. Clears the interfering accepted head (matching the live archive's own empty raw_revision_heads row for this identity) and retries — the same durable raw now succeeds and reaches the index with the expected message_count.

Notable finding during construction: storage/repair.py's offline repair_raw_materialization reprocesses every retained typed-'full' raw for a logical_source_key on every pass (including the accepted head's own cohort), which re-establishes the interfering head before ever reaching the colliding raw in the same pass — so the recovery in this test is demonstrated via the live watcher's own retry path (_ingest_full_paths_sync again), not via repair_raw_materialization. That offline-repair gap is noted directly in the test as a real follow-up rather than papered over.

Root cause conclusion for the live production session: the retry-eligibility fix already merged in #3646 is the actual fix. #3646's own read-only investigation already confirmed the codex parser succeeds on the real 90.8MB blob and that raw_revision_heads has zero rows for this identity today, so a fresh rebuild-index pass over the real archive should reach the simple byte-chain replay path directly (the real content is one clean chain) without ever re-tripping the membership-governance guard. No further production code change was found necessary this session; this PR is fixture/regression-test work only.

Verification

  • devtools test tests/unit/sources/test_live_batch_support.py -k test_growing_file_incident_recovery_duplicate_recovers_after_head_advances — 1 passed, verified stable across 4 repeated runs.
  • devtools test tests/unit/sources/test_live_batch_support.py -k "revision_replay or membership" — 7 passed.
  • devtools test tests/unit/storage/test_repair.py -k membership — 2 passed.
  • devtools verify --quick — exit 0.

Ref polylogue-5iz4

…cale

Problem: polylogue-5iz4's AC required a structural fixture reproducing
the real 804-revision Codex session's crash shape (many incremental
full-snapshot captures of one growing file, plus a same-identity
duplicate from a second "incident recovery" path) before the fix in
#3646 could be verified against something closer to the real shape than
the original small #2718 pin (2-3 message sessions).

Investigation: reading the live archive read-only confirmed the real
22 full-revision blobs for this session form ONE clean, unforked byte-
prefix chain (every smaller blob is an exact prefix of every larger
one) and that raw_revision_heads/sessions are empty for this identity
today. Reproducing the actual MembershipReplayConflictError therefore
needed the SAME mechanism as test_bundle_replay_respects_unconvertible_
single_session_head (a same-identity bundle arriving at a second path,
colliding with a QUARANTINED dangling append fragment hanging off the
accepted head), scaled to a growth chain with many messages rather than
2-3, plus a working demonstration that the guard is a transient,
recoverable refusal, not a permanent one, per its own docstring: once
the accepted head no longer interferes, a retry over the SAME durable
raw succeeds and reaches the index with a plausible message_count.

Solution: added
test_growing_file_incident_recovery_duplicate_recovers_after_head_
advances to tests/unit/sources/test_live_batch_support.py. Notable
finding during construction: storage/repair.py's offline
repair_raw_materialization reprocesses every retained typed-'full' raw
for a logical_source_key on every pass (including the accepted head's
own cohort), which re-establishes the interfering head before ever
reaching the colliding raw in the same pass -- so this recovery can only
be demonstrated here via the live watcher's own retry path
(_ingest_full_paths_sync again), not via repair_raw_materialization.
That offline-repair gap is noted in the test as a real follow-up, not
papered over.

Root cause conclusion for the live production session: the retry-
eligibility fix already merged in #3646 (dedicated
MembershipReplayConflictError type + storage/repair.py recognizing its
parse_error prefix) is the actual fix -- PR #3646's own read-only
investigation already confirmed the codex parser succeeds on the real
90.8MB blob and that raw_revision_heads has zero rows for this identity
today, so a fresh rebuild-index pass over the real archive should reach
the SIMPLE byte-chain replay path directly (the real content is one
clean chain) without ever re-tripping the membership-governance guard.
No further production code change was found necessary this session;
this PR is fixture/regression-test work only.

Verification:
- devtools test tests/unit/sources/test_live_batch_support.py -k
  test_growing_file_incident_recovery_duplicate_recovers_after_head_advances
  -- 1 passed (verified stable across 4 repeated runs).
- devtools test tests/unit/sources/test_live_batch_support.py -k
  "revision_replay or membership" -- 7 passed.
- devtools test tests/unit/storage/test_repair.py -k membership -- 2 passed.
- devtools verify --quick -- 20260803T172208Z-quick-3717286-f717e8bf,
  exit 0.

Ref polylogue-5iz4

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@Sinity, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 23 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5428e020-9e6c-45c8-b4e4-55e3223adc37

📥 Commits

Reviewing files that changed from the base of the PR and between 5fe0f62 and 04f83e1.

📒 Files selected for processing (1)
  • tests/unit/sources/test_live_batch_support.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Sinity
Sinity merged commit 3af8223 into master Aug 3, 2026
3 checks passed
@Sinity
Sinity deleted the feature/fix/polylogue-5iz4-growth-chain-replay-fixture branch August 3, 2026 17:36
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.

1 participant