310 lines
15 KiB
Python
310 lines
15 KiB
Python
"""Regression coverage for explicit legacy PR root migration."""
|
|
from __future__ import annotations
|
|
|
|
from contextlib import nullcontext
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
import yaml
|
|
|
|
from testing.tests.test_hermes_cli_support import HERMES, _load
|
|
|
|
|
|
ROOT = Path(__file__).parents[2]
|
|
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")
|
|
protocol = sys.modules["execution_pool_protocol"]
|
|
recovery = _load("publication_retry_recovery")
|
|
|
|
|
|
class NativeKanban:
|
|
"""Minimal native task lookup surface used by the operator seed."""
|
|
|
|
def __init__(self, task_ids: set[str]) -> None:
|
|
self.task_ids = task_ids
|
|
|
|
def scoped_current_board(self, _board: str):
|
|
return nullcontext()
|
|
|
|
def connect(self, *, board: str):
|
|
return type("Connection", (), {"close": lambda self: None})()
|
|
|
|
def get_task(self, _connection, task_id: str):
|
|
return {"id": task_id} if task_id in self.task_ids else None
|
|
|
|
|
|
def _pull(root, head: str | None = None) -> bytes:
|
|
"""Build only the canonical open PR shape the seed accepts."""
|
|
return json.dumps({
|
|
"state": "open",
|
|
"head": {"ref": root.ref, "sha": head or root.head,
|
|
"repo": {"full_name": f"titan/{root.project}"}},
|
|
"base": {"ref": root.base, "repo": {"full_name": f"titan/{root.project}"}},
|
|
}).encode()
|
|
|
|
|
|
def test_seed_registry_exactly_matches_flux_broker_adoptions():
|
|
"""The board integrity table and broker ledger share one reviewed scope."""
|
|
document = yaml.safe_load(
|
|
(ROOT / "services/hermes-scm-broker/task-branch-adoptions-configmap.yaml").read_text()
|
|
)
|
|
deployed = json.loads(document["data"]["task-branch-adoptions.json"])
|
|
expected = {f"{root.project}/{root.ref}": root.adoption() for root in seed.ROOTS}
|
|
assert deployed == expected
|
|
assert "t_cf89a2ec" in {root.root_task_id for root in seed.ROOTS}
|
|
assert deployed["atlas-iac/feature/hermes-next-hux"]["latest_head"] == (
|
|
"98c7c6184f6edfe3cdac228529c2287584db3006"
|
|
)
|
|
|
|
|
|
def test_seed_requires_native_root_and_matching_live_canonical_pr(tmp_path, monkeypatch):
|
|
"""A stale PR or fabricated root leaves the board-local integrity DB untouched."""
|
|
root = seed.ROOTS[1]
|
|
monkeypatch.setattr(state, "KANBAN_ROOT", tmp_path / "boards")
|
|
missing = NativeKanban(set())
|
|
assert seed.seed_root(missing, root, lambda _path: (_ for _ in ()).throw(AssertionError)) == "missing-task"
|
|
|
|
native = NativeKanban({root.root_task_id})
|
|
assert seed.seed_root(native, root, lambda _path: _pull(root, "a" * 40)) == "live-pr-mismatch"
|
|
assert state.get_root(root.board, root.root_task_id) is None
|
|
|
|
assert seed.seed_root(native, root, lambda _path: _pull(root)) == "seeded"
|
|
assert state.get_root(root.board, root.root_task_id) == root.lineage
|
|
assert state.get_live_head(root.board, root.root_task_id) == root.head
|
|
|
|
|
|
def test_seed_rerun_preserves_same_owned_newer_state_and_approval(tmp_path, monkeypatch):
|
|
"""A static migration record never rewinds a later verified continuation head."""
|
|
root = seed.ROOTS[1]
|
|
monkeypatch.setattr(state, "KANBAN_ROOT", tmp_path / "boards")
|
|
native = NativeKanban({root.root_task_id})
|
|
assert seed.seed_root(native, root, lambda _path: _pull(root)) == "seeded"
|
|
newer = "b" * 40
|
|
state.record_submission(root.board, root.root_task_id, root.lineage, newer)
|
|
state.set_ready(root.board, root.root_task_id, newer)
|
|
|
|
assert seed.seed_root(native, root, lambda _path: (_ for _ in ()).throw(AssertionError)) == "already-seeded"
|
|
assert state.get_live_head(root.board, root.root_task_id) == newer
|
|
with state._connect(root.board) as connection:
|
|
assert connection.execute(
|
|
"SELECT ready_for_human_merge,ready_commit FROM supervisor_roots"
|
|
).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()
|
|
|
|
kanban.connect = lambda **_kwargs: type("BadOutcomeConnection", (), {
|
|
"execute": lambda _self, _sql, _args: SimpleNamespace(fetchone=lambda: (8, "blocked", "failed")),
|
|
"close": lambda _self: None,
|
|
})()
|
|
with pytest.raises(ValueError, match="exact blocked run"):
|
|
bootstrap._native_guard()
|
|
|
|
|
|
def test_publication_bootstrap_assignment_uses_ordinal_authority(tmp_path, monkeypatch):
|
|
"""The operator attestation has the source mediator's derived authority."""
|
|
master = b"m" * 32
|
|
key_file = tmp_path / "pool-key"
|
|
key_file.write_bytes(master)
|
|
key_file.chmod(0o600)
|
|
assignment = {"root_task_id": bootstrap.ROOT, "continuation_kind": "repair"}
|
|
monkeypatch.setattr(bootstrap, "_pool_record", lambda _pool: (assignment, "a" * 64, 1))
|
|
monkeypatch.setattr(bootstrap, "_native_guard", lambda: None)
|
|
|
|
envelope = json.loads(bootstrap.signed_assignment(tmp_path / "pool.db", key_file))
|
|
derived = protocol.derive_ordinal_key(master, bootstrap.ORDINAL)
|
|
assert protocol.verify_envelope(derived, envelope, expected_kind="assignment")["payload"] == assignment
|
|
with pytest.raises(protocol.ProtocolError):
|
|
protocol.verify_envelope(master, envelope, expected_kind="assignment")
|
|
with pytest.raises(protocol.ProtocolError):
|
|
protocol.verify_envelope(
|
|
protocol.derive_ordinal_key(master, 1), envelope, expected_kind="assignment"
|
|
)
|
|
|
|
seen = []
|
|
monkeypatch.setattr(bootstrap, "_run9_guard", lambda _pool: seen.append("run9"))
|
|
normalized = json.loads(bootstrap.signed_normalized_assignment(tmp_path / "pool.db", key_file))
|
|
assert seen == ["run9"]
|
|
assert protocol.verify_envelope(derived, normalized, expected_kind="assignment")["payload"] == assignment
|
|
|
|
|
|
def test_publication_retry_normalization_preserves_all_evidence_except_title(tmp_path, monkeypatch):
|
|
"""The broker-cap correction is fenced to run 9 and one sealed receipt."""
|
|
board, root_id, child_id, baseline, head = (
|
|
"soteria", "t_root", "t_child", "a" * 40, "b" * 40
|
|
)
|
|
lineage = state.Lineage(root_id, "hermes-repair/cache", "https://scm.bstein.dev/titan/soteria/pulls/3", "soteria", "main")
|
|
monkeypatch.setattr(state, "KANBAN_ROOT", tmp_path / "boards")
|
|
state.record_submission(board, root_id, lineage, baseline)
|
|
state.record_child(board, child_id, root_id, root_id, "repair", baseline, "replace cache literals")
|
|
structured = {
|
|
"status": "completed", "summary": "界" * 300,
|
|
"changed_files": ["internal/k8s/job_manifests.go"], "tests_run": [],
|
|
"artifacts": [], "findings": [], "blockers": [],
|
|
}
|
|
source = {
|
|
"board": board, "task_id": child_id, "run_id": "8", "worker_ordinal": 0,
|
|
"attempt": 1, "root_task_id": root_id, "repo_url": "https://scm.bstein.dev/titan/soteria.git",
|
|
"branch": lineage.branch, "base_branch": "main",
|
|
}
|
|
old_title, body = "界" * 170, json.dumps(structured, sort_keys=True)
|
|
old = {"source": source, "baseline_sha": baseline, "head": head, "title": old_title, "body": body,
|
|
"structured": structured, "result_digest": retry.receipt_digest(structured, old_title, body)}
|
|
binding = {name: source[name] for name in ("board", "task_id", "run_id", "worker_ordinal", "attempt")}
|
|
state.record_publication_retry(board, child_id, binding, old)
|
|
state.record_publication_retry_provenance(board, child_id, "c" * 64)
|
|
state.issue_publication_retry(board, child_id, "9")
|
|
title = recovery.canonical_title(structured["summary"])
|
|
fixed = {**old, "title": title, "result_digest": retry.receipt_digest(structured, title, body)}
|
|
|
|
sealed = recovery.normalize(board, child_id, "9", "c" * 64, fixed)
|
|
assert len(title) == 169 and len(title.encode()) == 507 and len(sealed) == 64
|
|
assert state.publication_retry(board, child_id, "9")["title"] == title
|
|
assert recovery.normalize(board, child_id, "9", "c" * 64, fixed) == sealed
|
|
|
|
changed = {**fixed, "body": body + "!"}
|
|
changed["result_digest"] = retry.receipt_digest(structured, title, changed["body"])
|
|
with pytest.raises(ValueError, match="evidence changed"):
|
|
recovery.normalize(board, child_id, "9", "c" * 64, changed)
|
|
|
|
assert state.reissue_publication_retry(board, child_id, "9") is True
|
|
assert recovery.normalize(board, child_id, "9", "c" * 64, fixed) == sealed
|
|
monkeypatch.setattr(bootstrap, "BOARD", board)
|
|
monkeypatch.setattr(bootstrap, "CHILD", child_id)
|
|
monkeypatch.setattr(bootstrap, "_receipt", lambda _path: fixed)
|
|
monkeypatch.setattr(bootstrap, "_pool_record", lambda _path: ({}, "c" * 64, 1))
|
|
monkeypatch.setattr(bootstrap, "_run9_guard", lambda _path: None)
|
|
assert bootstrap.normalize_reissue(tmp_path / "receipt.json", tmp_path / "pool.db") == sealed
|
|
assert bootstrap.normalize_reissue(tmp_path / "receipt.json", tmp_path / "pool.db") == sealed
|
|
|
|
|
|
def test_normalized_assignment_accepts_real_shaped_native_run_row(tmp_path, monkeypatch):
|
|
"""The one-time run-9 guard handles sqlite Row values without weakening it."""
|
|
database = tmp_path / "pool.db"
|
|
with __import__("sqlite3").connect(database) as connection:
|
|
connection.execute(
|
|
"CREATE TABLE assignments(board,task_id,run_id,payload_json,result_json,state,worker_ordinal)"
|
|
)
|
|
connection.execute(
|
|
"INSERT INTO assignments VALUES(?,?,?,?,?,?,?)", (
|
|
bootstrap.BOARD, bootstrap.CHILD, "9", json.dumps({"scm_resume": {}}),
|
|
json.dumps({"structured": {"status": "blocked"}, "scm_submission": None}),
|
|
"finalized", bootstrap.ORDINAL,
|
|
)
|
|
)
|
|
|
|
class NativeRow:
|
|
def __iter__(self):
|
|
return iter((9, "blocked", "blocked"))
|
|
|
|
class Connection:
|
|
def execute(self, _sql, _args):
|
|
return SimpleNamespace(fetchone=lambda: NativeRow())
|
|
|
|
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))
|
|
bootstrap._run9_guard(database)
|
|
|
|
|
|
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 = (
|
|
"soteria", "t_root", "t_child", "a" * 40, "b" * 40
|
|
)
|
|
lineage = state.Lineage(
|
|
root_id, "hermes-repair/cache", "https://scm.bstein.dev/titan/soteria/pulls/3",
|
|
"soteria", "main",
|
|
)
|
|
monkeypatch.setattr(state, "KANBAN_ROOT", tmp_path / "boards")
|
|
state.record_submission(board, root_id, lineage, baseline)
|
|
state.record_child(board, child_id, root_id, root_id, "repair", baseline, "replace cache literals")
|
|
structured = {
|
|
"status": "completed", "summary": "Replace cache literals.",
|
|
"changed_files": ["internal/k8s/job_manifests.go"], "tests_run": ["go test ./..."],
|
|
"artifacts": [], "findings": [], "blockers": [],
|
|
}
|
|
title, body = "Replace cache literals", "verified completion evidence"
|
|
receipt = {
|
|
"source": {
|
|
"board": board, "task_id": child_id, "run_id": "8", "worker_ordinal": 0,
|
|
"attempt": 1, "root_task_id": root_id,
|
|
"repo_url": "https://scm.bstein.dev/titan/soteria.git",
|
|
"branch": lineage.branch, "base_branch": "main",
|
|
},
|
|
"baseline_sha": baseline, "head": head, "title": title, "body": body,
|
|
"structured": structured, "result_digest": retry.receipt_digest(structured, title, body),
|
|
}
|
|
binding = {name: receipt["source"][name] for name in (
|
|
"board", "task_id", "run_id", "worker_ordinal", "attempt"
|
|
)}
|
|
state.record_publication_retry(board, child_id, binding, receipt)
|
|
assert state.publication_retry(board, child_id, "")["head"] == head
|
|
state.issue_publication_retry(board, child_id, "9")
|
|
assert state.publication_retry(board, child_id, "9")["source"]["worker_ordinal"] == 0
|
|
with pytest.raises(retry.PublicationRetryError, match="already issued"):
|
|
state.publication_retry(board, child_id, "10")
|
|
monkeypatch.setattr(state.time, "time", lambda: 1_000)
|
|
assert state.reissue_publication_retry(board, child_id, "9") is True
|
|
# A failed native park can replay the same terminal result without spending
|
|
# another retry or extending the backoff.
|
|
assert state.reissue_publication_retry(board, child_id, "9") is True
|
|
state.issue_publication_retry(board, child_id, "9")
|
|
with pytest.raises(retry.PublicationRetryError, match="backoff"):
|
|
state.publication_retry(board, child_id, "")
|
|
monkeypatch.setattr(state.time, "time", lambda: 1_301)
|
|
assert state.publication_retry(board, child_id, "")["head"] == head
|
|
state.issue_publication_retry(board, child_id, "10")
|
|
# Completion advances the trusted root head before the terminal receipt is
|
|
# marked resolved; a finalize replay must not revalidate its old baseline.
|
|
state.record_submission(board, child_id, lineage, head)
|
|
state.issue_publication_retry(board, child_id, "10")
|
|
state.resolve_publication_retry(board, child_id, "10")
|
|
state.resolve_publication_retry(board, child_id, "10")
|
|
assert state.publication_retry(board, child_id, "") is None
|