diff --git a/polylogue/storage/sqlite/durable_change_train.py b/polylogue/storage/sqlite/durable_change_train.py index 25cf0eab15..1dda7033ed 100644 --- a/polylogue/storage/sqlite/durable_change_train.py +++ b/polylogue/storage/sqlite/durable_change_train.py @@ -1126,6 +1126,10 @@ def _refresh_released_source_train_continuity_locked( _require_nonempty(operation_id, label="source mutation operation id") _require_nonempty(evidence_ref, label="source continuity evidence ref") + if pre_mutation_evidence.tier is not ArchiveTier.SOURCE: + raise DurableSourceContinuitySemanticError( + "source continuity refresh requires source-tier pre-mutation evidence" + ) if not mutation_receipt.is_file() or mutation_receipt.is_symlink(): raise DurableChangeTrainError("source mutation receipt is not a real file") if not backup_manifest.is_file() or backup_manifest.is_symlink(): diff --git a/tests/unit/storage/test_blob_ref_liveness.py b/tests/unit/storage/test_blob_ref_liveness.py index 6e5566d220..27a0dfdad5 100644 --- a/tests/unit/storage/test_blob_ref_liveness.py +++ b/tests/unit/storage/test_blob_ref_liveness.py @@ -24,6 +24,7 @@ stage_blob_ref_liveness, ) from polylogue.storage.blob_store import BlobStore +from polylogue.storage.sqlite.archive_tiers import ARCHIVE_VERSION_BY_TIER from polylogue.storage.sqlite.archive_tiers.bootstrap import initialize_active_archive_root from polylogue.storage.sqlite.archive_tiers.source_write import deterministic_blob_hash, deterministic_raw_session_id from polylogue.storage.sqlite.archive_tiers.types import ArchiveTier @@ -290,10 +291,11 @@ def test_apply_refuses_a_fresh_mutation_while_continuity_recovery_is_pending(tmp def test_apply_refuses_an_unreleased_source_train_before_writing_a_receipt(tmp_path: Path) -> None: archive_root = _source_archive(tmp_path) manifest_root = archive_root / ".maintenance-state" / "durable-change-trains" - manifest_root.mkdir(parents=True) - manifest_root.joinpath("source-029.json").write_text( + manifest_root.mkdir(parents=True, exist_ok=True) + source_version = ARCHIVE_VERSION_BY_TIER[ArchiveTier.SOURCE] + manifest_root.joinpath(f"source-{source_version:03}.json").write_text( resources.files("polylogue.storage.sqlite.migrations.source") - .joinpath("029.train.json") + .joinpath(f"{source_version:03}.train.json") .read_text(encoding="utf-8"), encoding="utf-8", ) diff --git a/tests/unit/storage/test_durable_change_train.py b/tests/unit/storage/test_durable_change_train.py index 1015fc7fb3..7ff5b19eba 100644 --- a/tests/unit/storage/test_durable_change_train.py +++ b/tests/unit/storage/test_durable_change_train.py @@ -507,6 +507,15 @@ def record_refresh_fsync(path: Path) -> None: operation_id=_EMPTY_LIVENESS_DIGEST, evidence_ref="proof:mutation-1", ) + with pytest.raises(DurableSourceContinuitySemanticError, match="source-tier pre-mutation evidence"): + refresh_released_source_train_continuity( + tmp_path, + mutation_receipt=mutation_receipt, + backup_manifest=backup_manifest, + pre_mutation_evidence=replace(before, tier=ArchiveTier.USER), + operation_id=_EMPTY_LIVENESS_DIGEST, + evidence_ref="proof:mutation-wrong-tier", + ) operator_cwd = tmp_path / "operator-cwd" operator_cwd.mkdir() monkeypatch.chdir(tmp_path)