From 0efd7923bf7d4c894ebb8568ee4c249caf42b23c Mon Sep 17 00:00:00 2001 From: Sinity Date: Mon, 10 Aug 2026 16:54:46 +0200 Subject: [PATCH 1/2] refactor(raw-authority): remove legacy reset facade Problem: the guarded recovery CLI was the production consent surface, but a caller-less Python facade retained an undocumented direct reset entry point.\n\nWhat changed: delete the compatibility facade and keep its temporary SQLite safety tests on the recovery implementation. The CLI route test explicitly models a clean signed build so it reaches exact plan identity validation. --- polylogue/maintenance/raw_authority_reset.py | 111 ------------------ .../unit/cli/test_archive_maintenance_cli.py | 5 +- .../maintenance/test_raw_authority_reset.py | 20 ++-- 3 files changed, 16 insertions(+), 120 deletions(-) delete mode 100644 polylogue/maintenance/raw_authority_reset.py diff --git a/polylogue/maintenance/raw_authority_reset.py b/polylogue/maintenance/raw_authority_reset.py deleted file mode 100644 index 033cade504..0000000000 --- a/polylogue/maintenance/raw_authority_reset.py +++ /dev/null @@ -1,111 +0,0 @@ -"""Reset the raw-authority census planning ledger so the daemon rebuilds it. - -The raw-authority census tables (``raw_authority_censuses`` and its -``plans``/``blockers``/``census_plans``/``census_post_plans`` children) are -DERIVED convergence bookkeeping: each census normally chains to its predecessor -(``sequence_no + 1``), while bounded header retention may set the oldest -retained header's predecessor to NULL as the explicit compaction boundary. -The ledger also carries unresolved plans forward. The ACCEPTED -materialization state — ``raw_sessions.revision_authority`` and the index's -``raw_revision_heads`` / ``raw_revision_applications`` — lives OUTSIDE these -tables and is untouched here. - -When the ledger accumulates inconsistent carried-forward state (e.g. a stale-plan -blocker marks sibling plans ``CARRIED_FORWARD``, and later raw deletions drop -them out of the recomputed frontier so the finalize postflight -``persistent ⊄ post_ids`` throws — live incident 2026-07-22 after hook -de-inflation), no new census can finalize to become a clean baseline, and the -daemon defers every pass. Emptying the ledger removes the poisoned predecessor: -the next daemon pass builds census #1 fresh over the current raw set -(``predecessor = None``), with no carried-forward history and no stale blocker. - -``raw_authority_parser_census`` is intentionally KEPT — it holds resource-blocked -parser fingerprints keyed to raws (FK-cascaded from ``raw_sessions``), not -census-cycle bookkeeping, and the whale pass consumes it. -""" - -from __future__ import annotations - -from dataclasses import dataclass -from pathlib import Path - -from polylogue.maintenance.raw_authority_recovery import ( - RecoveryOperation, - apply_raw_authority_recovery, - inspect_raw_authority_recovery, -) - - -@dataclass(frozen=True, slots=True) -class RawAuthorityResetReport: - """Row counts removed from the census ledger (dry-run or applied).""" - - censuses: int - plans: int - blockers: int - census_plans: int - census_post_plans: int - applied: bool - - -def reset_raw_authority_census( - archive_root: Path, - *, - backup_manifest: Path | None = None, - dry_run: bool = True, -) -> RawAuthorityResetReport: - """Inspect or apply the guarded census-ledger recovery route.""" - plan = inspect_raw_authority_recovery( - archive_root, - RecoveryOperation.RESET_CENSUS, - backup_manifest=backup_manifest, - ) - report = apply_raw_authority_recovery(plan, backup_manifest=backup_manifest) if not dry_run else None - counts = plan.before_counts - - return RawAuthorityResetReport( - censuses=counts["raw_authority_censuses"], - plans=counts["raw_authority_plans"], - blockers=counts["raw_authority_blockers"], - census_plans=counts["raw_authority_census_plans"], - census_post_plans=counts["raw_authority_census_post_plans"], - applied=report is not None and report.status == "applied", - ) - - -@dataclass(frozen=True, slots=True) -class IndexSeedPruneReport: - """Index revision-authority read-model rows removed (dry-run or applied).""" - - revision_heads: int - revision_applications: int - applied: bool - - -def prune_orphaned_index_revision_seeds( - archive_root: Path, - *, - backup_manifest: Path | None = None, - dry_run: bool = True, -) -> IndexSeedPruneReport: - """Delete index raw-frontier seeds whose raw is gone from the source tier. - - ``raw_revision_heads`` / ``raw_revision_applications`` are the index's - (rebuildable) revision-authority read model. After a source raw is deleted - (hook de-inflation), the seeds referencing it become broken predecessor - chains — the daemon's raw-frontier integrity check reports them as violated - and cannot converge past them. Deleting the seeds whose ``accepted_raw_id`` / - ``raw_id`` no longer exists in ``source.raw_sessions`` restores a clean - frontier; seeds for present raws are untouched. - """ - plan = inspect_raw_authority_recovery( - archive_root, - RecoveryOperation.PRUNE_INDEX_SEEDS, - backup_manifest=backup_manifest, - ) - report = apply_raw_authority_recovery(plan, backup_manifest=backup_manifest) if not dry_run else None - return IndexSeedPruneReport( - revision_heads=len(plan.candidate_keys["raw_revision_heads"]), - revision_applications=len(plan.candidate_keys["raw_revision_applications"]), - applied=report is not None and report.status == "applied", - ) diff --git a/tests/unit/cli/test_archive_maintenance_cli.py b/tests/unit/cli/test_archive_maintenance_cli.py index cfa53fca68..43029d1619 100644 --- a/tests/unit/cli/test_archive_maintenance_cli.py +++ b/tests/unit/cli/test_archive_maintenance_cli.py @@ -1989,9 +1989,12 @@ def test_raw_authority_frontier_cli_rejects_removed_apply_options( def test_raw_authority_recovery_cli_refuses_plan_for_another_operation( - cli_workspace: dict[str, Path], cli_runner: CliRunner, tmp_path: Path + cli_workspace: dict[str, Path], cli_runner: CliRunner, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """The required operation is bound before a destructive plan artifact is consumed.""" + from polylogue.version import VERSION_INFO + + monkeypatch.setattr(VERSION_INFO, "dirty", False) plan = inspect_raw_authority_recovery(cli_workspace["archive_root"], RecoveryOperation.RESET_CENSUS) plan_file = tmp_path / "census-reset.plan.json" diff --git a/tests/unit/maintenance/test_raw_authority_reset.py b/tests/unit/maintenance/test_raw_authority_reset.py index a650ffa435..fa4c8f6ceb 100644 --- a/tests/unit/maintenance/test_raw_authority_reset.py +++ b/tests/unit/maintenance/test_raw_authority_reset.py @@ -25,10 +25,6 @@ resume_raw_authority_recovery, write_recovery_plan, ) -from polylogue.maintenance.raw_authority_reset import ( - prune_orphaned_index_revision_seeds, - reset_raw_authority_census, -) from polylogue.operations.mutation_transaction import OperationExecutor from polylogue.storage.sqlite.archive_tiers.bootstrap import initialize_active_archive_root from polylogue.storage.sqlite.archive_tiers.types import ArchiveTier @@ -37,6 +33,12 @@ from polylogue.version import VERSION_INFO +@pytest.fixture(autouse=True) +def _clean_recovery_build(monkeypatch: pytest.MonkeyPatch) -> None: + """Exercise recovery against the signed-build condition each test models.""" + monkeypatch.setattr(VERSION_INFO, "dirty", False) + + def _seed_ledger(source_db: Path) -> None: with sqlite3.connect(source_db) as conn: conn.execute( @@ -534,10 +536,11 @@ def test_census_reset_dry_run_does_not_mutate_and_apply_requires_backup(tmp_path _seed_ledger(tmp_path / "source.db") _seed_raw(tmp_path / "source.db", "r-keep") before = (tmp_path / "source.db").read_bytes() - reset_raw_authority_census(tmp_path, dry_run=True) + inspect_raw_authority_recovery(tmp_path, RecoveryOperation.RESET_CENSUS) assert (tmp_path / "source.db").read_bytes() == before + plan = inspect_raw_authority_recovery(tmp_path, RecoveryOperation.RESET_CENSUS) with pytest.raises(RawAuthorityRecoveryError, match="backup authority"): - reset_raw_authority_census(tmp_path, dry_run=False) + apply_raw_authority_recovery(plan) assert (tmp_path / "source.db").read_bytes() == before @@ -963,11 +966,12 @@ def test_index_recovery_actuator_receipt_retains_authorized_plan_identity( assert receipt.affected_count == 2 -def test_named_compatibility_facade_requires_index_backup(tmp_path: Path) -> None: +def test_index_prune_requires_index_backup(tmp_path: Path) -> None: initialize_active_archive_root(tmp_path) _seed_index_seeds(tmp_path) + plan = inspect_raw_authority_recovery(tmp_path, RecoveryOperation.PRUNE_INDEX_SEEDS) with pytest.raises(RawAuthorityRecoveryError, match="backup authority"): - prune_orphaned_index_revision_seeds(tmp_path, dry_run=False) + apply_raw_authority_recovery(plan) def test_storage_compatibility_helpers_refuse_direct_mutation(tmp_path: Path) -> None: From 4b7eaddcf16e68a29f92c34cbfe890e89a6eeab5 Mon Sep 17 00:00:00 2001 From: Sinity Date: Mon, 10 Aug 2026 17:23:24 +0200 Subject: [PATCH 2/2] test(raw-authority): assert rejected prune is non-mutating Problem: the backup-authority refusal test observed only the exception, so it did not prove the rejection preserved either seeded index copy. What changed: snapshot the active generation and archive index bytes before the real recovery call and assert both remain unchanged after the expected refusal. --- tests/unit/maintenance/test_raw_authority_reset.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/unit/maintenance/test_raw_authority_reset.py b/tests/unit/maintenance/test_raw_authority_reset.py index fa4c8f6ceb..d9be33c63e 100644 --- a/tests/unit/maintenance/test_raw_authority_reset.py +++ b/tests/unit/maintenance/test_raw_authority_reset.py @@ -968,10 +968,15 @@ def test_index_recovery_actuator_receipt_retains_authorized_plan_identity( def test_index_prune_requires_index_backup(tmp_path: Path) -> None: initialize_active_archive_root(tmp_path) - _seed_index_seeds(tmp_path) + active_index = _seed_index_seeds(tmp_path) + archive_index = tmp_path / "index.db" + before_active = active_index.read_bytes() + before_archive = archive_index.read_bytes() plan = inspect_raw_authority_recovery(tmp_path, RecoveryOperation.PRUNE_INDEX_SEEDS) with pytest.raises(RawAuthorityRecoveryError, match="backup authority"): apply_raw_authority_recovery(plan) + assert active_index.read_bytes() == before_active + assert archive_index.read_bytes() == before_archive def test_storage_compatibility_helpers_refuse_direct_mutation(tmp_path: Path) -> None: