diff --git a/services/hermes/scripts/bootstrap_soteria_publication_retry.py b/services/hermes/scripts/bootstrap_soteria_publication_retry.py index 4333b7b1..f4dfbde1 100644 --- a/services/hermes/scripts/bootstrap_soteria_publication_retry.py +++ b/services/hermes/scripts/bootstrap_soteria_publication_retry.py @@ -107,7 +107,10 @@ def _native_guard() -> supervisor_state.Lineage: task is None or _value(task, "status") != "blocked" or _value(task, "current_run_id") is not None or latest is None or latest[0] != int(RUN_ID) - or latest[1] not in {"done", "failed"} + # Run 8 predates the terminal-state classifier. Its retained native + # record is exactly blocked/blocked; accept only that historical + # terminal form in addition to the current terminal spellings. + or latest[1] not in {"blocked", "done", "failed"} or not isinstance(parents, (list, tuple, set)) or ROOT not in {str(value) for value in parents} ): raise ValueError("bootstrap native task is no longer the exact blocked run") diff --git a/testing/tests/test_hermes_legacy_scm_roots.py b/testing/tests/test_hermes_legacy_scm_roots.py index d0f16d21..34fd9215 100644 --- a/testing/tests/test_hermes_legacy_scm_roots.py +++ b/testing/tests/test_hermes_legacy_scm_roots.py @@ -5,6 +5,7 @@ from contextlib import nullcontext import json from pathlib import Path import sys +from types import SimpleNamespace import pytest import yaml @@ -17,6 +18,7 @@ sys.path.insert(0, str(HERMES / "scm-common/scripts")) state = _load("supervisor_state") seed = _load("seed_legacy_scm_roots") retry = sys.modules["publication_retry"] +bootstrap = _load("bootstrap_soteria_publication_retry") class NativeKanban: @@ -93,6 +95,41 @@ def test_seed_rerun_preserves_same_owned_newer_state_and_approval(tmp_path, monk ).fetchone() == (1, newer) +def test_publication_bootstrap_accepts_only_the_verified_historical_blocked_run(monkeypatch): + """The run-8 migration accepts its old blocked terminal state, not a live run.""" + lineage = state.Lineage( + bootstrap.ROOT, "hermes-repair/cache", "https://scm.bstein.dev/titan/soteria/pulls/3", + "soteria", "main", + ) + monkeypatch.setattr( + bootstrap.supervisor_state, "get_child", + lambda _board, _child: {"root_task_id": bootstrap.ROOT, "kind": "repair", "lineage": lineage}, + ) + + class Connection: + def execute(self, _sql, _args): + return SimpleNamespace(fetchone=lambda: (8, "blocked", "blocked")) + + def close(self): + return None + + kanban = SimpleNamespace( + scoped_current_board=lambda _board: nullcontext(), + connect=lambda **_kwargs: Connection(), + get_task=lambda _connection, _task: {"status": "blocked", "current_run_id": None}, + parent_ids=lambda _connection, _task: [bootstrap.ROOT], + ) + monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=kanban)) + assert bootstrap._native_guard() == lineage + + kanban.connect = lambda **_kwargs: type("BadConnection", (), { + "execute": lambda _self, _sql, _args: SimpleNamespace(fetchone=lambda: (9, "blocked", "blocked")), + "close": lambda _self: None, + })() + with pytest.raises(ValueError, match="exact blocked run"): + bootstrap._native_guard() + + def test_publication_retry_is_bound_once_and_never_falls_back_to_a_model(tmp_path, monkeypatch): """A mediator receipt can power one fresh ordinal-pinned publication only.""" board, root_id, child_id, baseline, head = (