Execute receipt-bound durable release runs

This commit is contained in:
2026-07-22 21:37:18 +02:00
parent 7ecf1f17b0
commit 5381e37a9e
7 changed files with 1961 additions and 141 deletions

View File

@@ -55,7 +55,8 @@ class ReleaseEntrypointGateTests(unittest.TestCase):
self.assertIn("trusted_keys_from_keyring(\n target_keyring_payload", publisher)
self.assertIn("trusted_keys=publication_trust", publisher)
self.assertNotIn("trusted_keys=candidate_keys", publisher)
self.assertIn("write_module_directory(", publisher)
self.assertIn("module_directory_payloads(", publisher)
self.assertIn("verify_committed_publication(", publisher)
self.assertNotIn("shutil.copytree(candidate_modules", publisher)
def test_release_integration_honors_independent_module_versions(self) -> None:

View File

@@ -230,10 +230,14 @@ class ReleaseRepositoryTagTests(unittest.TestCase):
for repository in (self.repo, self.remote, campaign, campaign_remote):
self.assertFalse(ref_exists(repository, "refs/tags/v0.1.10"))
def test_api_requires_explicit_confirmation_and_ui_exposes_source_release(self) -> None:
with TestClient(create_app(workspace_root=self.workspace)) as client:
def test_api_keeps_legacy_source_release_preview_only(self) -> None:
with TestClient(
create_app(workspace_root=self.workspace, token="token")
) as client:
headers = {"X-Release-Console-Token": "token"}
rejected = client.post(
"/api/repositories/tag",
headers=headers,
json={
"repos": ["govoplan-core"],
"repo_versions": {"govoplan-core": "0.1.10"},
@@ -244,6 +248,7 @@ class ReleaseRepositoryTagTests(unittest.TestCase):
)
preview = client.post(
"/api/repositories/tag",
headers=headers,
json={
"repos": ["govoplan-core"],
"repo_versions": {"govoplan-core": "0.1.10"},
@@ -251,14 +256,47 @@ class ReleaseRepositoryTagTests(unittest.TestCase):
"push": True,
},
)
legacy_push = client.post(
"/api/repositories/push",
headers=headers,
json={
"repos": ["govoplan-core"],
"apply": True,
"confirm": "PUSH",
},
)
legacy_sync = client.post(
"/api/repositories/sync",
headers=headers,
json={
"repos": ["govoplan-core"],
"apply": True,
"confirm": "SYNC",
},
)
ui = client.get("/")
self.assertEqual(rejected.status_code, 400)
self.assertEqual(rejected.status_code, 409)
self.assertIn("durable release run", rejected.json()["detail"])
self.assertEqual(preview.status_code, 200)
self.assertEqual(409, legacy_push.status_code)
self.assertEqual(409, legacy_sync.status_code)
self.assertIn("durable", legacy_push.json()["detail"])
self.assertIn("durable", legacy_sync.json()["detail"])
self.assertEqual(preview.json()["repositories"][0]["repo"], "govoplan-core")
self.assertFalse(ref_exists(self.repo, "refs/tags/v0.1.10"))
self.assertIn('id="publishReleaseTags"', ui.text)
self.assertIn(
'id="publishReleaseTags" data-release-disabled="true" disabled',
ui.text,
)
self.assertIn('postJson("/api/repositories/tag"', ui.text)
self.assertIn(
'id="syncRepos" disabled data-release-disabled="true"', ui.text
)
self.assertIn(
'id="pushRepos" disabled data-release-disabled="true"', ui.text
)
self.assertIn("Signed Website Catalog", ui.text)
self.assertIn("Apply + Website Tag", ui.text)

View File

@@ -32,6 +32,12 @@ from govoplan_release.release_run import ( # noqa: E402
ReleaseRunStore,
_record_digest,
)
from govoplan_release.candidate_artifact import ( # noqa: E402
CandidateArtifactReceipt,
candidate_output_path,
harden_private_candidate_tree,
issue_candidate_receipt,
)
from server.app import ( # noqa: E402
create_app,
default_release_run_root,
@@ -51,8 +57,24 @@ class ReleaseRunStoreTests(unittest.TestCase):
self.root,
expected_workspace_fingerprint=WORKSPACE_FINGERPRINT,
)
self.runtime_trust = patch(
"server.app.require_trusted_release_runtime"
)
self.runtime_binding = patch(
"server.app.verify_release_runtime_binding"
)
self.source_bindings = patch(
"server.app.bind_plan_source_states",
side_effect=bind_test_plan_source_states,
)
self.runtime_trust.start()
self.runtime_binding.start()
self.source_bindings.start()
def tearDown(self) -> None:
self.source_bindings.stop()
self.runtime_binding.stop()
self.runtime_trust.stop()
self.temporary.cleanup()
def test_create_persists_private_immutable_plan_and_ordered_steps(self) -> None:
@@ -68,7 +90,7 @@ class ReleaseRunStoreTests(unittest.TestCase):
record_path = self.root / f"{run['run_id']}.json"
self.assertEqual("govoplan.release-run", reopened["schema"])
self.assertEqual(3, reopened["schema_version"])
self.assertEqual(4, reopened["schema_version"])
self.assertEqual(
{"govoplan-core": "1.2.3", "govoplan-files": "1.2.4"},
reopened["immutable"]["input"]["repo_versions"],
@@ -82,6 +104,119 @@ class ReleaseRunStoreTests(unittest.TestCase):
self.assertNotIn("processed_requests", reopened["state"])
self.assertNotIn("attempt_fingerprint", reopened["state"]["steps"][0])
def test_catalog_receipt_is_bounded_persisted_and_idempotent(self) -> None:
run_id = create_run(
self.store,
plan_snapshot=mutating_first_plan(),
request_id="receipt-create-request-0001",
)["run_id"]
attempt_id = "receipt-attempt-request-0001"
self.store.start_step(run_id, "core:tag", attempt_id=attempt_id)
receipt = {
"kind": "catalog_candidate",
"candidate_id": "candidate-" + "a" * 32,
"catalog_sha256": "b" * 64,
}
finished = self.store.finish_step(
run_id,
"core:tag",
attempt_id=attempt_id,
succeeded=True,
result_code="catalog_candidate_ready",
result_receipt=receipt,
)
replayed = self.store.finish_step(
run_id,
"core:tag",
attempt_id=attempt_id,
succeeded=True,
result_code="catalog_candidate_ready",
result_receipt=receipt,
)
self.assertEqual(receipt, finished["state"]["steps"][0]["result_receipt"])
self.assertEqual(finished, replayed)
with self.assertRaisesRegex(ReleaseRunConflict, "different recorded outcome"):
self.store.finish_step(
run_id,
"core:tag",
attempt_id=attempt_id,
succeeded=True,
result_code="catalog_candidate_ready",
result_receipt=receipt | {"catalog_sha256": "c" * 64},
)
def test_catalog_publication_receipt_binds_candidate_origin_commit_and_tag(
self,
) -> None:
run_id = create_run(
self.store,
plan_snapshot=mutating_first_plan(),
request_id="publication-receipt-create-0001",
)["run_id"]
attempt_id = "publication-receipt-attempt-0001"
self.store.start_step(run_id, "core:tag", attempt_id=attempt_id)
receipt = {
"kind": "catalog_publication",
"candidate_id": "candidate-" + "a" * 32,
"catalog_sha256": "b" * 64,
"keyring_sha256": "c" * 64,
"publication_commit_sha": "d" * 40,
"publication_tag_object_sha": "e" * 40,
"publication_tag_commit_sha": "d" * 40,
"branch": "main",
"tag_name": "catalog-stable-1",
"remote": "origin",
"remote_sha256": "f" * 64,
}
finished = self.store.finish_step(
run_id,
"core:tag",
attempt_id=attempt_id,
succeeded=True,
result_code="catalog_published",
result_receipt=receipt,
)
self.assertEqual(receipt, finished["state"]["steps"][0]["result_receipt"])
with self.assertRaisesRegex(
ReleaseRunConflict, "publication receipt identity"
):
self.store.finish_step(
run_id,
"core:tag",
attempt_id=attempt_id,
succeeded=True,
result_code="catalog_published",
result_receipt=receipt
| {"publication_tag_commit_sha": "0" * 40},
)
def test_schema_three_record_is_verified_then_migrated_privately(self) -> None:
run_id = create_run(
self.store,
request_id="legacy-schema-create-request-0001",
)["run_id"]
path = self.root / f"{run_id}.json"
payload = json.loads(path.read_text(encoding="utf-8"))
payload["schema_version"] = 3
for step in payload["state"]["steps"]:
step.pop("result_receipt")
payload["record_digest"] = _record_digest(payload)
path.write_text(json.dumps(payload), encoding="utf-8")
path.chmod(0o600)
migrated = self.store.get(run_id)
persisted = json.loads(path.read_text(encoding="utf-8"))
self.assertEqual(4, migrated["schema_version"])
self.assertEqual(4, persisted["schema_version"])
self.assertTrue(
all(step["result_receipt"] is None for step in persisted["state"]["steps"])
)
def test_zero_step_plan_is_rejected(self) -> None:
plan = release_plan()
plan["dry_run_steps"] = []
@@ -293,7 +428,7 @@ class ReleaseRunStoreTests(unittest.TestCase):
self.assertEqual(0, untouched["state"]["steps"][0]["attempt_count"])
accepted_plan = mutating_first_plan()
accepted_plan["notes"] = ["x" * (MAX_RECORD_BYTES - 3_000)]
accepted_plan["notes"] = ["x" * (MAX_RECORD_BYTES - 4_300)]
accepted_run_id = create_run(
self.store,
request_id="byte-capacity-accepted-create-0001",
@@ -334,7 +469,7 @@ class ReleaseRunStoreTests(unittest.TestCase):
) -> None:
plan = mutating_first_plan()
plan["dry_run_steps"][0]["mutating"] = False # type: ignore[index]
plan["notes"] = ["x" * (MAX_RECORD_BYTES - 3_000)]
plan["notes"] = ["x" * (MAX_RECORD_BYTES - 5_500)]
run_id = create_run(
self.store,
request_id="byte-capacity-read-only-create-0001",
@@ -747,7 +882,7 @@ class ReleaseRunStoreTests(unittest.TestCase):
listed = self.store.list(limit=1)
self.assertEqual([local_run["run_id"]], [item["run_id"] for item in listed])
def test_list_orders_verified_runs_by_updated_timestamp_before_unavailable(
def test_list_orders_verified_runs_by_creation_timestamp_before_unavailable(
self,
) -> None:
with patch(
@@ -786,11 +921,11 @@ class ReleaseRunStoreTests(unittest.TestCase):
listed = self.store.list(limit=3)
self.assertEqual(
[older["run_id"], newer["run_id"], corrupt["run_id"]],
[newer["run_id"], older["run_id"], corrupt["run_id"]],
[item["run_id"] for item in listed],
)
self.assertEqual("unavailable", listed[-1]["status"])
self.assertEqual(older["run_id"], self.store.list(limit=1)[0]["run_id"])
self.assertEqual(newer["run_id"], self.store.list(limit=1)[0]["run_id"])
def test_cursor_pagination_is_stable_and_keeps_unavailable_records_visible(
self,
@@ -822,6 +957,43 @@ class ReleaseRunStoreTests(unittest.TestCase):
with self.assertRaisesRegex(ReleaseRunConflict, "cursor is invalid"):
self.store.list_page(limit=1, cursor=f"{first['next_cursor']}tampered")
def test_cursor_does_not_skip_or_repeat_when_an_older_run_is_updated(
self,
) -> None:
created: list[dict[str, object]] = []
for index in range(3):
with patch(
"govoplan_release.release_run._timestamp",
return_value=f"2026-07-22T0{index}:00:00Z",
):
created.append(
create_run(
self.store,
request_id=f"cursor-update-create-request-{index:04d}",
)
)
first = self.store.list_page(limit=1)
with patch(
"govoplan_release.release_run._timestamp",
return_value="2026-07-22T12:00:00Z",
):
self.store.start_step(
created[0]["run_id"],
"core:preflight",
attempt_id="cursor-update-attempt-request-0001",
)
second = self.store.list_page(limit=1, cursor=first["next_cursor"])
third = self.store.list_page(limit=1, cursor=second["next_cursor"])
self.assertEqual(
[item["run_id"] for item in reversed(created)],
[
first["runs"][0]["run_id"],
second["runs"][0]["run_id"],
third["runs"][0]["run_id"],
],
)
def test_retention_prunes_only_oldest_completed_records_and_fails_closed(
self,
) -> None:
@@ -963,6 +1135,22 @@ class ReleaseRunStoreTests(unittest.TestCase):
class ReleaseRunApiTests(unittest.TestCase):
def setUp(self) -> None:
self.runtime_trust = patch("server.app.require_trusted_release_runtime")
self.runtime_binding = patch("server.app.verify_release_runtime_binding")
self.source_bindings = patch(
"server.app.bind_plan_source_states",
side_effect=bind_test_plan_source_states,
)
self.runtime_trust.start()
self.runtime_binding.start()
self.source_bindings.start()
def tearDown(self) -> None:
self.source_bindings.stop()
self.runtime_binding.stop()
self.runtime_trust.stop()
def test_api_exposes_and_validates_cursor_pagination(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
app = create_app(
@@ -1008,6 +1196,338 @@ class ReleaseRunApiTests(unittest.TestCase):
self.assertIsNone(second["next_cursor"])
self.assertEqual(409, invalid.status_code)
def test_executor_claims_before_effect_and_replays_lost_success_response(
self,
) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
app = create_app(
workspace_root=Path(temp_dir),
run_state_root=Path(temp_dir) / "runs",
candidate_root=Path(temp_dir) / "candidates",
token="token",
)
headers = {"X-Release-Console-Token": "token"}
with (
patch("server.app.build_dashboard", return_value=object()),
patch(
"server.app.build_selective_release_plan",
return_value=release_plan(),
),
patch(
"server.app.bind_plan_source_states",
side_effect=bind_test_plan_source_states,
),
patch(
"server.app.verify_repository_preflight_binding",
return_value=repository_receipt(),
),
patch(
"server.app.execute_repository_step",
return_value=({"status": "inspected"}, repository_receipt()),
) as executor,
TestClient(app) as client,
):
created = client.post(
"/api/release-runs",
headers=headers,
json=api_run_input(request_id="api-executor-create-0001"),
).json()
run_id = created["run_id"]
self.assertTrue(created["state"]["steps"][0]["executor"]["available"])
self.assertEqual(
"preflight", created["state"]["steps"][0]["executor"]["kind"]
)
payload = {"request_id": "api-executor-attempt-0001"}
executed = client.post(
f"/api/release-runs/{run_id}/steps/core:preflight/execute",
headers=headers,
json=payload,
)
replayed = client.post(
f"/api/release-runs/{run_id}/steps/core:preflight/execute",
headers=headers,
json=payload,
)
self.assertEqual(200, executed.status_code)
self.assertEqual("succeeded", executed.json()["state"]["steps"][0]["state"])
self.assertEqual("replayed", replayed.json()["execution_result"]["status"])
executor.assert_called_once()
def test_lost_finish_write_interrupts_without_reexecuting_effect(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
app = create_app(
workspace_root=Path(temp_dir),
run_state_root=Path(temp_dir) / "runs",
token="token",
)
headers = {"X-Release-Console-Token": "token"}
with (
patch("server.app.build_dashboard", return_value=object()),
patch(
"server.app.build_selective_release_plan",
return_value=release_plan(),
),
patch(
"server.app.bind_plan_source_states",
side_effect=bind_test_plan_source_states,
),
patch(
"server.app.verify_repository_preflight_binding",
return_value=repository_receipt(),
),
patch(
"server.app.execute_repository_step",
return_value=({"status": "inspected"}, repository_receipt()),
) as executor,
TestClient(app) as client,
):
run_id = client.post(
"/api/release-runs",
headers=headers,
json=api_run_input(request_id="api-lost-finish-create-0001"),
).json()["run_id"]
with patch.object(
app.state.release_runs,
"finish_step",
side_effect=ReleaseRunCorrupt("simulated durable write failure"),
):
first = client.post(
f"/api/release-runs/{run_id}/steps/core:preflight/execute",
headers=headers,
json={"request_id": "api-lost-finish-attempt-0001"},
)
replay = client.post(
f"/api/release-runs/{run_id}/steps/core:preflight/execute",
headers=headers,
json={"request_id": "api-lost-finish-attempt-0001"},
)
loaded = client.get(
f"/api/release-runs/{run_id}", headers=headers
).json()
self.assertEqual(409, first.status_code)
self.assertEqual(409, replay.status_code)
self.assertEqual("interrupted", loaded["state"]["steps"][0]["state"])
executor.assert_called_once()
def test_executor_exception_is_interrupted_and_never_retried_implicitly(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
app = create_app(
workspace_root=Path(temp_dir),
run_state_root=Path(temp_dir) / "runs",
token="token",
)
headers = {"X-Release-Console-Token": "token"}
with (
patch("server.app.build_dashboard", return_value=object()),
patch(
"server.app.build_selective_release_plan",
return_value=mutating_first_plan(),
),
patch(
"server.app.bind_plan_source_states",
side_effect=bind_test_plan_source_states,
),
patch("server.app.verify_repository_step_precondition"),
patch(
"server.app.execute_repository_step",
side_effect=RuntimeError("connection lost after push"),
) as executor,
TestClient(app) as client,
):
run_id = client.post(
"/api/release-runs",
headers=headers,
json=api_run_input(request_id="api-ambiguous-create-0001"),
).json()["run_id"]
payload = {
"request_id": "api-ambiguous-attempt-0001",
"confirm": "TAG",
}
first = client.post(
f"/api/release-runs/{run_id}/steps/core:tag/execute",
headers=headers,
json=payload,
)
second = client.post(
f"/api/release-runs/{run_id}/steps/core:tag/execute",
headers=headers,
json=payload,
)
loaded = client.get(
f"/api/release-runs/{run_id}", headers=headers
).json()
self.assertEqual(409, first.status_code)
self.assertEqual(409, second.status_code)
self.assertEqual("interrupted", loaded["state"]["steps"][0]["state"])
self.assertEqual(
"executor_outcome_unknown",
loaded["state"]["steps"][0]["result_code"],
)
executor.assert_called_once()
def test_catalog_execution_persists_and_consumes_only_issued_receipt(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
candidate_root = Path(temp_dir) / "candidates"
app = create_app(
workspace_root=Path(temp_dir),
run_state_root=Path(temp_dir) / "runs",
candidate_root=candidate_root,
token="token",
)
headers = {"X-Release-Console-Token": "token"}
def generated(**kwargs: object) -> tuple[dict[str, object], CandidateArtifactReceipt]:
candidate_id = str(kwargs["candidate_id"])
candidate = candidate_output_path(candidate_root, candidate_id)
channels = candidate / "channels"
channels.mkdir(parents=True)
(channels / "stable.json").write_text(
json.dumps({"channel": "stable", "signatures": [{}]}),
encoding="utf-8",
)
harden_private_candidate_tree(candidate)
return (
{"status": "ready"},
issue_candidate_receipt(
root=candidate_root,
candidate_id=candidate_id,
channel="stable",
),
)
with (
patch("server.app.build_dashboard", return_value=object()),
patch(
"server.app.build_selective_release_plan",
return_value=catalog_release_plan(),
),
patch(
"server.app.bind_plan_source_states",
side_effect=bind_test_plan_source_states,
),
patch(
"server.app.verify_catalog_publication_precondition",
return_value=repository_receipt(
repo="addideas-govoplan-website", target_tag=""
),
),
patch("server.app.default_signing_keys", return_value=("key=/private",)),
patch(
"server.app.generate_catalog_candidate", side_effect=generated
) as generator,
patch(
"server.app.publish_received_candidate",
side_effect=published_candidate_result,
) as publisher,
TestClient(app) as client,
):
run_id = client.post(
"/api/release-runs",
headers=headers,
json=api_run_input(request_id="api-candidate-create-0001"),
).json()["run_id"]
generated_response = client.post(
f"/api/release-runs/{run_id}/steps/catalog:selective-generator/execute",
headers=headers,
json={
"request_id": "api-candidate-generate-0001",
"confirm": "GENERATE",
},
)
with patch("server.app.default_signing_keys", return_value=()):
replayed_generation = client.post(
f"/api/release-runs/{run_id}/steps/catalog:selective-generator/execute",
headers=headers,
json={
"request_id": "api-candidate-generate-0001",
"confirm": "GENERATE",
},
)
published = client.post(
f"/api/release-runs/{run_id}/steps/catalog:validate-sign-publish/execute",
headers=headers,
json={
"request_id": "api-candidate-publish-0001",
"confirm": "PUSH",
},
)
receipt = generated_response.json()["state"]["steps"][0]["result_receipt"]
self.assertRegex(receipt["candidate_id"], r"^candidate-[0-9a-f]{32}$")
self.assertEqual(64, len(receipt["catalog_sha256"]))
self.assertEqual(200, replayed_generation.status_code)
self.assertEqual(
"replayed",
replayed_generation.json()["execution_result"]["status"],
)
generator.assert_called_once()
self.assertEqual(200, published.status_code)
self.assertEqual("completed", published.json()["state"]["status"])
publisher.assert_called_once_with(
candidate_path=candidate_root / receipt["candidate_id"],
candidate_receipt=receipt,
channel="stable",
workspace_root=Path(temp_dir),
remote="origin",
expected_website_receipt=repository_receipt(
repo="addideas-govoplan-website", target_tag=""
),
)
def test_legacy_catalog_endpoint_is_preview_only(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
app = create_app(workspace_root=Path(temp_dir), token="token")
with TestClient(app) as client:
response = client.post(
"/api/catalog-candidates/publish",
headers={"X-Release-Console-Token": "token"},
json={
"candidate_dir": "/tmp/untrusted",
"apply": True,
"push": True,
"confirm": "PUSH",
},
)
self.assertEqual(409, response.status_code)
self.assertIn("durable release run", response.json()["detail"])
def test_release_channels_and_durable_remote_are_strictly_validated(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
app = create_app(workspace_root=Path(temp_dir), token="token")
headers = {"X-Release-Console-Token": "token"}
with (
patch("server.app.build_dashboard") as dashboard,
TestClient(app) as client,
):
query = client.get(
"/api/dashboard", headers=headers, params={"channel": "../stable"}
)
created = client.post(
"/api/release-runs",
headers=headers,
json=api_run_input(
request_id="api-invalid-channel-create-0001",
channel="/absolute",
),
)
execute = client.post(
"/api/release-runs/not-a-run/steps/core:tag/execute",
headers=headers,
json={
"request_id": "api-invalid-remote-attempt-0001",
"confirm": "TAG",
"remote": "upstream",
},
)
self.assertEqual(422, query.status_code)
self.assertEqual(422, created.status_code)
self.assertEqual(422, execute.status_code)
dashboard.assert_not_called()
def test_token_guarded_create_list_read_resume_and_retry(self) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
root = Path(temp_dir) / "runs"
@@ -1205,6 +1725,10 @@ class ReleaseRunApiTests(unittest.TestCase):
"server.app.build_selective_release_plan",
return_value=mutating_first_plan(),
),
patch(
"server.app.reconciled_repository_receipt",
return_value=repository_receipt(),
),
TestClient(app) as client,
):
created = client.post(
@@ -1372,12 +1896,16 @@ class ReleaseRunApiTests(unittest.TestCase):
)
self.assertIn("Durable Run State", webui)
self.assertIn("State tracking only.", webui)
self.assertIn("Durable execution.", webui)
self.assertIn("Create Run from Selection", webui)
self.assertIn("Prepare Retry", webui)
self.assertIn("step.retry_available", webui)
self.assertIn("Record Reconciliation", webui)
self.assertIn("effect_succeeded", webui)
self.assertNotIn(
'step.id === "catalog:validate-sign-publish" ? "disabled"',
webui,
)
self.assertIn("renderReleaseRunStoreUnavailable", webui)
self.assertIn('const runsRequest = api("/api/release-runs")', webui)
self.assertIn("pendingReleaseRunPayload", webui)
@@ -1385,13 +1913,30 @@ class ReleaseRunApiTests(unittest.TestCase):
self.assertIn("recoverPendingRunCommands", webui)
self.assertIn("inFlightRunCommandId", webui)
self.assertIn("Run saved; list refresh unavailable", webui)
self.assertIn("This foundation tracks state only.", runbook)
self.assertIn("The run record is execution evidence only", runbook)
self.assertIn("isolated checkout", runbook)
self.assertIn("effect_absent", runbook)
self.assertIn("$XDG_STATE_HOME", runbook)
self.assertIn("same-directory", runbook)
self.assertIn("caller-generated `request_id`", runbook)
self.assertIn("reserves the two command-ledger", runbook)
def test_entire_release_console_script_is_valid_javascript(self) -> None:
node = shutil.which("node")
if node is None:
self.skipTest("Node.js is required for the Release Console UI check")
webui = (RELEASE_ROOT / "webui" / "index.html").read_text(encoding="utf-8")
match = re.search(r"<script>(?P<script>.*?)</script>", webui, re.DOTALL)
self.assertIsNotNone(match, "release console script element is missing")
result = subprocess.run( # noqa: S603
[node, "--check", "-"],
input=match.group("script") if match is not None else "",
check=False,
capture_output=True,
text=True,
)
self.assertEqual(0, result.returncode, result.stderr)
def test_ui_reuses_uncertain_ids_and_separates_saved_run_list_failure(
self,
) -> None:
@@ -1493,6 +2038,16 @@ async function api(_path) {
const reconcileRequest = pendingRunCommandRequest("reconcile:rr-known:core:tag:effect_succeeded", "reconcile-request");
assert(reconcileRequest.path.endsWith("/steps/core%3Atag/reconcile"), "reconciliation recovery path was not executable");
assert(reconcileRequest.body.confirm === "RECONCILE" && reconcileRequest.body.outcome === "effect_succeeded", "reconciliation recovery body changed");
const executeKey = "execute:rr-known:catalog:selective-generator:GENERATE";
const executeId = inFlightRunCommandId(executeKey, "execute", { signing_keys: ["release-key=/private/key"] });
assert(!values.get(pendingRunCommandsKey).includes("signing_keys"), "signing key path was persisted in session storage");
const executeRequest = pendingRunCommandRequest(executeKey, executeId);
assert(!Object.hasOwn(executeRequest.body, "signing_keys"), "execute replay reconstructed signing material");
values.set(pendingRunCommandsKey, JSON.stringify({ [commandKey]: commandOne, [executeKey]: { request_id: executeId, body: { signing_keys: ["legacy-secret"] } } }));
const migratedCommands = readInFlightRunCommands();
assert(migratedCommands[executeKey] === executeId, "legacy pending execute request ID was lost");
assert(!values.get(pendingRunCommandsKey).includes("legacy-secret"), "legacy persisted signing material was not purged");
clearInFlightRunCommand(executeKey, executeId);
failCreate = true;
const uncertainCommand = await recoverPendingRunCommands();
@@ -1591,6 +2146,64 @@ def run_input(
}
def repository_receipt(
*, repo: str = "govoplan-core", target_tag: str = "v1.2.3"
) -> dict[str, object]:
return {
"kind": "repository_state",
"repo": repo,
"head": "a" * 40,
"branch": "main",
"remote": "origin",
"remote_sha256": "b" * 64,
"worktree_clean": True,
"target_tag": target_tag,
"tag_object": None,
}
def published_candidate_result(
**kwargs: object,
) -> tuple[dict[str, object], dict[str, object]]:
candidate = kwargs["candidate_receipt"]
website = kwargs["expected_website_receipt"]
if not isinstance(candidate, dict) or not isinstance(website, dict):
raise AssertionError("publication test receipts are unavailable")
receipt = {
"kind": "catalog_publication",
"candidate_id": candidate["candidate_id"],
"catalog_sha256": candidate["catalog_sha256"],
"keyring_sha256": "c" * 64,
"publication_commit_sha": "d" * 40,
"publication_tag_object_sha": "e" * 40,
"publication_tag_commit_sha": "d" * 40,
"branch": website["branch"],
"tag_name": "catalog-stable-7",
"remote": "origin",
"remote_sha256": website["remote_sha256"],
}
return {"status": "published"}, receipt
def bind_test_plan_source_states(**kwargs: object) -> dict[str, object]:
plan = kwargs["plan"]
if not isinstance(plan, dict):
raise AssertionError("test plan is not an object")
steps = plan.get("dry_run_steps")
if not isinstance(steps, list):
raise AssertionError("test plan has no steps")
for step in steps:
if not isinstance(step, dict):
continue
if isinstance(step.get("repo"), str):
step["source_binding"] = repository_receipt()
elif step.get("id") == "catalog:validate-sign-publish":
step["source_binding"] = repository_receipt(
repo="addideas-govoplan-website", target_tag=""
)
return plan
def release_plan() -> dict[str, object]:
return {
"generated_at": "2026-07-22T00:00:00Z",
@@ -1641,5 +2254,32 @@ def mutating_first_plan() -> dict[str, object]:
return plan
def catalog_release_plan() -> dict[str, object]:
plan = release_plan()
plan["dry_run_steps"] = [
{
"id": "catalog:selective-generator",
"title": "Generate candidate",
"detail": "Build artifacts and sign a candidate.",
"command": "release-catalog selective",
"cwd": "/workspace/govoplan",
"mutating": True,
"repo": None,
"status": "planned",
},
{
"id": "catalog:validate-sign-publish",
"title": "Publish candidate",
"detail": "Publish only the receipt-bound candidate.",
"command": "release-catalog publish-candidate",
"cwd": "/workspace/govoplan",
"mutating": True,
"repo": None,
"status": "planned",
},
]
return plan
if __name__ == "__main__":
unittest.main()