Skip to content

fix(maintenance): harden durable schema currency gate (#3856) - #3856

Closed
Sinity wants to merge 3 commits into
masterfrom
feature/fix/schema-currency-gate-hardening
Closed

fix(maintenance): harden durable schema currency gate (#3856)#3856
Sinity wants to merge 3 commits into
masterfrom
feature/fix/schema-currency-gate-hardening

Conversation

@Sinity

@Sinity Sinity commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

Harden the rebuild durable-schema currency boundary across local rebuild, daemon bulk transaction, CLI, and daemon HTTP paths. This PR implements the code slice tracked by polylogue-dudtn and leaves live migration and deployment under polylogue-9qnzy and polylogue-a7gmk.

Problem

The initial gate checked only source and user tiers. Audit could be missing or mismatched, schema could drift after archive ownership acquisition, daemon bulk bookkeeping could start without the check, and the empty-source CLI path returned before validation. The daemon also emitted a generic HTTP 500 instead of the preflight diagnostic.

Solution

Use the canonical DURABLE_MIGRATION_TIERS set, recheck after ownership acquisition, guard daemon bulk setup before bookkeeping, remove the empty-source bypass, reject --preflight --daemon, return a structured HTTP 409 diagnostic, and document migration of audit.db. Add file-backed regressions for every path and update the operator fixture setup.

Verification

Eight focused schema-gate regression tests passed in 1.71s. devtools verify --quick passed all 24 steps twice, including the pre-push run. No production migration, reset, rebuild, daemon restart, or archive mutation was performed.

Acceptance criteria

Criterion Status Evidence
Canonical durable predicate includes audit Satisfied DURABLE_MIGRATION_TIERS probe
Early and post-ownership guards Satisfied source mismatch and ownership-race tests
Daemon bulk guard Satisfied no bookkeeping files on mismatch
Empty-source and daemon preflight paths Satisfied CLI regressions
Structured daemon conflict Satisfied HTTP 409 diagnostic regression
Anti-vacuity Satisfied eight file-backed SQLite tests
Live migration and deployment Intentionally outside this implementation Bead polylogue-9qnzy and polylogue-a7gmk

Ref polylogue-dudtn. Context: polylogue-9qnzy.

Sinity and others added 2 commits August 6, 2026 10:38
Problem
A rebuild accepted a source.db whose user_version lagged the package that
would parse and rebuild from it. The live archive reached that state at
source v28 while the installed package expected v24 and master expects v29.

What changed
The rebuild route now checks source.db and user.db before provenance,
ownership, or candidate creation. A read-only --preflight option exposes the
same structured diagnostic. index.db remains exempt because rebuilding it is
the operation's purpose.

Compatibility/migration
Operators must migrate durable tiers and deploy the matching package before
rebuilding index.db. The maintenance runbook records the ordered recovery
sequence.

Co-Authored-By: Codex <noreply@openai.com>
Problem
The initial currency gate checked only source and user tiers, leaving audit,
daemon bulk transaction setup, an ownership-acquisition race, and the CLI
empty-source path outside the durable schema boundary. The daemon surfaced a
currency mismatch as an unstructured 500 response.

What changed
The gate now derives every durable migration tier from the canonical set,
rechecks after archive ownership is acquired, and runs before daemon bulk
bookkeeping. The CLI delegates empty sources to the guarded operation and
rejects daemon preflight. Currency errors carry the original diagnostic to
the daemon HTTP route with conflict semantics.

Compatibility/migration
Operators must bring source, user, and audit durable tiers to the deployed
package versions before a rebuild. No migration or live archive mutation is
performed by this change.

Co-Authored-By: Codex <noreply@openai.com>
@coderabbitai

coderabbitai Bot commented Aug 6, 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: 2 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: ab1174de-5c60-407d-8eb3-13c6e536a6f7

📥 Commits

Reviewing files that changed from the base of the PR and between 685f2ca and 1ac4749.

📒 Files selected for processing (9)
  • docs/maintenance.md
  • polylogue/cli/commands/maintenance/_rebuild_index.py
  • polylogue/daemon/bulk_rebuild.py
  • polylogue/daemon/http.py
  • polylogue/maintenance/rebuild_index.py
  • tests/unit/cli/test_archive_maintenance_cli.py
  • tests/unit/daemon/test_bulk_rebuild_ownership.py
  • tests/unit/daemon/test_daemon_http_contracts.py
  • tests/unit/maintenance/test_rebuild_index_ownership.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 changed the title fix(maintenance): harden durable schema currency gate fix(maintenance): harden durable schema currency gate (#3856) Aug 6, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 38d02ade5c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"""
from polylogue.maintenance.rebuild_index import require_rebuild_schema_currency

require_rebuild_schema_currency(root)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Initialize all durable tiers in the remaining status fixture

This unconditional gate breaks tests/unit/maintenance/test_rebuild_status.py::test_falls_back_to_the_daemon_well_known_operation_id_by_default: its _init_empty_source() creates only source.db, so the new audit/user probes report missing and raise RebuildSchemaCurrencyError before the expected daemon transaction is created. The commit updates similar fixtures but misses this existing caller, leaving the affected unit suite failing.

Useful? React with 👍 / 👎.

@Sinity Sinity Aug 6, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Addressed in follow-up PR #3858, commit 61ba5bf. The _init_empty_source fixture now initializes source, user, and audit. The focused status regression passed with 1 passed and 5 deselected.

Comment thread docs/maintenance.md Outdated
Comment on lines +61 to +62
`migrate-tier source` and `migrate-tier user` when the target package requires
them, then deploy that exact package. Run the preflight above and require a

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include audit in the migration recovery command

When audit.db is the tier reported as mismatched, following this recovery procedure and migrating only source/user leaves audit unchanged, so the documented preflight remains blocked and the rebuild still cannot run. Since the preceding paragraph explicitly makes audit part of the gate and migrate-tier accepts every canonical durable tier, the recovery sequence must also direct operators to migrate audit when required.

Useful? React with 👍 / 👎.

@Sinity Sinity Aug 6, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Addressed in follow-up PR #3858, commit 61ba5bf. The maintenance recovery sequence now includes migrate-tier audit --initialize-missing, alongside the source and user migration commands.

"""
from polylogue.maintenance.rebuild_index import require_rebuild_schema_currency

require_rebuild_schema_currency(root)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Recheck schema currency after daemon ownership acquisition

If this call waits for an existing archive owner and that owner advances a durable schema before releasing the lock, this pre-lock check observes the old version, but the function acquires ownership and proceeds to transaction resolution without checking currency again. The post-lock provenance validation does not compare the live user/audit versions, so this can create or resume daemon generation bookkeeping under a package that no longer owns the durable schemas; repeat the currency check immediately after assert_owns_archive_location, as the local rebuild path already does.

Useful? React with 👍 / 👎.

@Sinity Sinity Aug 6, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Addressed in follow-up PR #3858. Commit 61ba5bf rechecks durable currency immediately after assert_owns_archive_location; commit b28a8f8 adds the later check before page selection and source-row consumption. The focused ownership tests passed with 2 passed and 2 deselected, and the page-selection race passed with 1 passed and 8 deselected.

Problem
The rebuild schema-currency gate checks source, user, and audit tiers, while
operator text still described only source and user.

What changed
Align the maintenance recovery instructions and rebuild preflight help with the
canonical durable-tier set.

Compatibility
Documentation and CLI help only. The maintenance behavior is unchanged.

Co-Authored-By: Codex <noreply@openai.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1ac4749772

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

click.echo("No source.db raw_sessions rows found.")
return
if plan_only:
raw_count = _count_source_raw_sessions(root)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle empty-source receipts before rendering counters

When a schema-current archive contains no raw rows, the local non-plan path now reaches rebuild_index_from_source_sync, which returns an empty-source receipt with an empty replay mapping. Its serialized payload therefore lacks classified_full_count, replayed_logical_source_count, and quarantined_raw_count, so the default plain-output formatting below raises KeyError instead of reporting the empty archive. Preserve an empty-source branch after the currency guard or render this receipt status without assuming replay counters.

Useful? React with 👍 / 👎.

@Sinity Sinity Aug 6, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Addressed in follow-up PR #3858, commit 61ba5bf. The local CLI handles the real empty-source receipt before replay-counter formatting. Its exact plain-output regression passes.

Sinity added a commit that referenced this pull request Aug 6, 2026
## Summary

Publish the durable Bead record that gives the schema-currency
implementation its own execution authority. The code and tests remain in
PR #3856; this PR carries only the tracker graph update.

## Problem

Polylogue-9qnzy is the live migration and deployment sequence and
explicitly excludes code changes. Without a separate implementation
Bead, PR #3856 could not carry a truthful scope record or bind its
implementation to the graph.

## Solution

Add polylogue-dudtn with the reviewed schema-currency implementation
scope and make it a blocking prerequisite of polylogue-9qnzy. The Bead
records the seven implementation acceptance criteria and leaves live
migration, deployment, and operation receipts in their existing Beads.

## Verification

The exported `.beads/issues.jsonl` contains the new Bead and the durable
blocking edge. The versioned PR-scope carrier below validates against
the exact head and committed Bead snapshot. No product code or
production data changes are included.

Ref polylogue-dudtn. Context: polylogue-9qnzy.

<!-- polylogue-pr-scope:v1
{
  "assigned_beads": [
    "polylogue-dudtn"
  ],
"beads_digest":
"927c820cb198b222d1d5fb169c7027d81567e9223f2563d6facb4a93913daa95",
  "dispositions": [
    {
      "bead_id": "polylogue-dudtn",
      "disposition": "partial",
      "evidence": [
        {
          "kind": "commit",
          "ref": "c9d12abcf"
        },
        {
          "kind": "receipt",
"ref": "tracker publication only; implementation is carried by PR #3856"
        }
      ],
      "successors": [
        "polylogue-9qnzy"
      ]
    }
  ],
  "head_sha": "66b1bf3d7cecbbf04b5b886a41a3d82d8c7c436c",
"scope_digest":
"3213a7c3f8ddcc65fac807b2723cf8749f9331c8efdc6bdf197ad07e24e7ecd7",
  "version": 1
}
-->
Sinity added a commit that referenced this pull request Aug 6, 2026
## Summary

Publish the durable Bead record that gives the schema-currency
implementation its own execution authority. The code and tests remain in
PR #3856; this PR carries only the tracker graph update.

## Problem

Polylogue-9qnzy is the live migration and deployment sequence and
explicitly excludes code changes. Without a separate implementation
Bead, PR #3856 could not carry a truthful scope record or bind its
implementation to the graph.

## Solution

Add polylogue-dudtn with the reviewed schema-currency implementation
scope and make it a blocking prerequisite of polylogue-9qnzy. The Bead
records the seven implementation acceptance criteria and leaves live
migration, deployment, and operation receipts in their existing Beads.

## Verification

The exported `.beads/issues.jsonl` contains the new Bead and the durable
blocking edge. The versioned PR-scope carrier below validates against
the exact head and committed Bead snapshot. No product code or
production data changes are included.

Ref polylogue-dudtn. Context: polylogue-9qnzy.

<!-- polylogue-pr-scope:v1
{
  "assigned_beads": [
    "polylogue-dudtn"
  ],
"beads_digest":
"927c820cb198b222d1d5fb169c7027d81567e9223f2563d6facb4a93913daa95",
  "dispositions": [
    {
      "bead_id": "polylogue-dudtn",
      "disposition": "partial",
      "evidence": [
        {
          "kind": "commit",
          "ref": "c9d12abcf"
        },
        {
          "kind": "receipt",
"ref": "tracker publication only; implementation is carried by PR #3856"
        }
      ],
      "successors": [
        "polylogue-9qnzy"
      ]
    }
  ],
  "head_sha": "66b1bf3d7cecbbf04b5b886a41a3d82d8c7c436c",
"scope_digest":
"3213a7c3f8ddcc65fac807b2723cf8749f9331c8efdc6bdf197ad07e24e7ecd7",
  "version": 1
}
-->
Sinity added a commit that referenced this pull request Aug 6, 2026
## Summary

This follow-up combines the schema-currency and topology-proof lineages
from PRs #3856 and #3850, then repairs the remaining RW acceptance gaps.
Rebuild admission now checks every durable tier at each
ownership-sensitive boundary, missing durable tiers have an atomic
recovery path, topology quarantine preserves child content, and proof
receipts bind a stable SQLite snapshot. Ref #3850 and #3856.

## Problem

The predecessor branches had concrete execution blockers. Their fixtures
omitted the audit tier, the recovery runbook omitted audit
initialization, daemon bulk could consume a source page after its last
durable-currency check, and local empty-source output assumed replay
counters that are absent by design. Cycle quarantine could also run
after prefix extraction, so a rejected parent could silently remove
copied child content. Finally, the topology census hashed files before
proving which SQLite snapshot it read and accepted several ambiguous or
fabricated topology evidence shapes.

## Solution

The schema lane now enumerates every canonical durable migration tier,
rechecks currency after archive ownership and again before daemon page
selection, preserves the empty-source CLI receipt, and documents source,
user, and audit recovery. `migrate-tier --initialize-missing` constructs
the canonical fresh database in memory, copies it directly into an
anonymous inode, fsyncs it, and publishes it with atomic no-replace
semantics. Existing or concurrently created targets remain untouched.

The topology lane classifies cycles before prefix-tail extraction and
preserves the full child transcript for quarantined or over-budget
parent walks. Stored cycle evidence must close through the asserted edge
and match the current projection. Unresolved and quarantined readers
remain child-local. The census groups unresolved samples by complete
edge identity, distinguishes walk exhaustion from cycle proof, and
rejects stale or contradictory projections without penalizing a valid
alternate resolved edge.

The receipt route begins a read transaction and establishes its snapshot
before the baseline file hash. A second observer checks `data_version`
for concurrent WAL commits, while before and after chunked hashes bind
`index.db` and its sidecars without whole-file allocation.

Five independent adversarial passes reviewed the complete branch. Four
produced repairs. The final pass raised only the intentionally empty
replay mapping, which the exact PR #3856 review permits the implemented
status branch to handle and which the production CLI regression covers.

## Acceptance criteria

| ID | Criterion | Result | Evidence or residue |
| --- | --- | --- | --- |
| S1 | Gate every canonical durable tier before receipt consumption or
candidate creation. | Satisfied | Early local guard, post-ownership
local guard, daemon transaction guard, daemon page-selection guard, HTTP
guard, and all-tier fixtures. |
| S2 | Recheck currency after archive ownership. | Satisfied | Local and
daemon ownership-race tests inject a mismatch after ownership and prove
refusal before mutation. |
| S3 | Preserve empty-source CLI behavior and include audit recovery. |
Satisfied | Exact plain-output regression and maintenance commands for
source, user, and audit. |
| S4 | Initialize only a missing durable tier without exposing
replaceable staged bytes. | Satisfied | Canonical in-memory image,
anonymous inode, fsync, atomic no-replace link, identity check,
directory fsync, and concurrent-creator tests. |
| S5 | Avoid production database mutation in this lane. | Satisfied |
All runtime tests use temporary archives. No live command was run. |
| T1 | Preserve copied child content when a proposed parent is
quarantined. | Satisfied | Production writer and sync plus async reader
regression; removing the pre-slice classification loses the asserted
transcript. |
| T2 | Distinguish proven cycles from bounded-walk exhaustion. |
Satisfied | Typed writer outcome, projection-backed evidence validation,
and a 1,024-hop acyclic fixture. |
| T3 | Keep unresolved and quarantined composition child-local. |
Satisfied for temporary archives | Production readers plus same-child
multi-edge census coverage. Live sampling remains unobserved. |
| T4 | Bind topology evidence to the exact stable read snapshot. |
Satisfied for temporary WAL fixtures | Snapshot established before
hashing, observer `data_version` check, chunked database and sidecar
hashes, quiescent and concurrent WAL tests. |
| T5 | Record exact live topology evidence before claiming operational
completion. | Partial | `polylogue-live-operation-receipts` remains open
for the approved live census. |

## Verification

- `devtools test tests/unit/cli/test_archive_maintenance_cli.py -k
'migrate_tier_cli or rebuild_index_preflight or
rebuild_index_empty_source_still or
rebuild_index_rejects_daemon_schema_preflight'`: 12 passed, 55
deselected.
- `devtools test tests/unit/cli/test_archive_maintenance_cli.py -k
'migrate_tier_cli_initializes_only_an_absent_durable_tier or
migrate_tier_cli_missing_initialization_refuses_an_existing_tier or
migrate_tier_cli_missing_initialization_loses_publish_race_without_replacement
or migrate_tier_cli_exposes_no_named_staging_inode_before_publication'`:
4 passed, 66 deselected.
- `devtools test tests/unit/daemon/test_bulk_rebuild_ownership.py -k
'rejects_schema_mismatch_before_transaction_bookkeeping or
rechecks_schema_currency_after_ownership'`: 2 passed, 2 deselected.
- `devtools test tests/unit/daemon/test_bulk_rebuild.py -k
daemon_bulk_pass_rechecks_schema_currency_in_page_selection_hold`: 1
passed, 8 deselected.
- `devtools test tests/unit/maintenance/test_rebuild_status.py -k
falls_back_to_the_daemon_well_known_operation_id_by_default`: 1 passed,
5 deselected.
- `devtools test
tests/unit/storage/test_topology_cycle_quarantine_live.py
tests/unit/devtools/test_lineage_validation.py`: 20 passed at
`6e2996668`.
- `devtools test tests/unit/devtools/test_lineage_validation.py`: 16
passed after the final multi-edge repair.
- `devtools verify --quick`: 24 steps, exit 0 in 105.41 seconds at
`d34e663cb` (`20260806T111901Z-quick-119573-0540dbf0`).
- `devtools verify --seed-testmon --skip-slow`: static and policy steps
passed, but the inherited full-suite seed ended with 258 failures,
19,631 passes, 3 skips, and 8 errors across more than 100 files. None of
the acceptance files failed. The seed remains incomplete, so the default
affected selector could not run.

## Live receipt still required

No production database was opened or mutated. After merge and
deployment, `polylogue-live-operation-receipts` must run the read-only
topology census against the exact active or approved candidate index
generation and retain its snapshot-bound receipt. `polylogue-a7gmk` must
also verify the shipped package and migrate every durable tier to
current before the managed rebuild. This PR does not assign
`polylogue-9qnzy` because its AC explicitly requires no code changes
under that bead.

<!-- polylogue-pr-scope:v1
{
  "assigned_beads": [
    "polylogue-topology-live-proof"
  ],
"beads_digest":
"ac5775b4d1215a3b38f0070c4e694bef389803aacd4a4d355f4d8173d3ce9b06",
  "dispositions": [
    {
      "bead_id": "polylogue-topology-live-proof",
      "disposition": "partial",
      "evidence": [
        {
          "kind": "commit",
          "ref": "d34e663cbfb1cd7eb628ab51f48f6574e5c90b1b"
        },
        {
          "kind": "test",
"ref": "tests/unit/devtools/test_lineage_validation.py and
tests/unit/storage/test_topology_cycle_quarantine_live.py"
        },
        {
          "kind": "command",
"ref": "devtools verify --quick: 24 steps, exit 0, run
20260806T111901Z-quick-119573-0540dbf0"
        },
        {
          "kind": "receipt",
"ref": "docs/evidence/polylogue-topology-live-proof-2026-08-06.md:
synthetic candidate only; live receipt explicitly unobserved"
        }
      ],
      "successors": [
        "polylogue-live-operation-receipts"
      ]
    }
  ],
  "head_sha": "d34e663cbfb1cd7eb628ab51f48f6574e5c90b1b",
"scope_digest":
"80257ffd894ba2700bce1c520f1e5465e95cef06376bba24ee9039635fe9867b",
  "version": 1
}
-->

---------

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
@Sinity

Sinity commented Aug 6, 2026

Copy link
Copy Markdown
Owner Author

This branch is fully represented in merged PR #3858. The ancestry comparison shows all three #3856 commits reproduced there, plus the durable-tier, ownership, empty-source, daemon, and recovery follow-up repairs. The implementation scope is therefore present in the merged train; live migration remains separately tracked. This stale duplicate PR is being removed from the active frontier.

@Sinity Sinity closed this Aug 6, 2026
@Sinity
Sinity deleted the feature/fix/schema-currency-gate-hardening branch August 10, 2026 13:11
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