diff --git a/services/hermes/scripts/bootstrap_soteria_publication_retry.py b/services/hermes/scripts/bootstrap_soteria_publication_retry.py index b50fcdd5..e9d580b3 100644 --- a/services/hermes/scripts/bootstrap_soteria_publication_retry.py +++ b/services/hermes/scripts/bootstrap_soteria_publication_retry.py @@ -13,7 +13,7 @@ from pathlib import Path from typing import Any import supervisor_state -from execution_pool_protocol import payload_digest, read_key, sign_envelope +from execution_pool_protocol import derive_ordinal_key, payload_digest, read_key, sign_envelope BOARD = "soteria" @@ -144,7 +144,8 @@ def signed_assignment(pool_database: Path, key_file: Path) -> bytes: """Attest the verified retained assignment with the current coordinator key.""" assignment, _raw_sha, attempt = _pool_record(pool_database) _native_guard() - return json.dumps(sign_envelope(read_key(key_file), "assignment", { + key = derive_ordinal_key(read_key(key_file), ORDINAL) + return json.dumps(sign_envelope(key, "assignment", { "board": BOARD, "task_id": CHILD, "run_id": RUN_ID, "worker_ordinal": ORDINAL, "attempt": attempt, }, assignment), separators=(",", ":"), sort_keys=True).encode() diff --git a/testing/tests/test_hermes_legacy_scm_roots.py b/testing/tests/test_hermes_legacy_scm_roots.py index 444e9a41..09f86ee0 100644 --- a/testing/tests/test_hermes_legacy_scm_roots.py +++ b/testing/tests/test_hermes_legacy_scm_roots.py @@ -19,6 +19,7 @@ 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"] class NativeKanban: @@ -137,6 +138,27 @@ def test_publication_bootstrap_accepts_only_the_verified_historical_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" + ) + + 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 = (