atlas-iac/services/hermes/scripts/seed_legacy_scm_roots.py

121 lines
5.2 KiB
Python

#!/usr/bin/env python3
"""Seed verified, native roots for the reviewed legacy pull requests."""
from __future__ import annotations
from dataclasses import dataclass
import json
import re
from typing import Any, Callable
import scm_broker_client
import supervisor_state
from supervisor_lineage import Lineage
SHA = re.compile(r"[0-9a-f]{40}\Z")
@dataclass(frozen=True)
class Root:
"""One reviewed PR whose existing branch may be continued in place."""
board: str
root_task_id: str
project: str
ref: str
pr_number: int
base: str
head: str
@property
def pull_request(self) -> str:
return f"https://scm.bstein.dev/titan/{self.project}/pulls/{self.pr_number}"
@property
def lineage(self) -> Lineage:
return Lineage(self.root_task_id, self.ref, self.pull_request, self.project, self.base)
def adoption(self) -> dict[str, Any]:
"""Return the exact schema consumed by the broker's Flux registry."""
return {"repo": self.project, "ref": self.ref, "board": self.board,
"root_task_id": self.root_task_id, "latest_head": self.head,
"pr_number": self.pr_number}
# This reviewed registry mirrors task-branch-adoptions-configmap.yaml. The
# regression test keeps the board-side lineage seed and broker ownership ledger
# on the same exact PR/ref/head set.
ROOTS = (
Root("soteria", "t_f1593f8c", "soteria", "wt/t_f1593f8c", 11, "main", "0143d472469c8dfe44f23f1440123e27d415baae"),
Root("soteria", "t_c7c42600", "soteria", "hermes-repair/sonar-AZ9pTqVcN0JrBQvDGDs3", 3, "main", "51133b559ad62f324e45bc61587900f08156b733"),
Root("soteria", "t_f4f726e1", "soteria", "hermes-repair/sonar-AZ9pTqWRN0JrBQvDGDs4", 4, "main", "45170df566c2aa387f8b706c3a704abce5aa2557"),
Root("titan-iac", "t_26da4c88", "atlas-iac", "feature/t_26da4c88-titan-capacity-guardrails-v4", 53, "main", "3c6301d581bef0a1fce5284f9a28c1cf4c4a99ad"),
Root("titan-iac", "t_39cf1905", "atlas-iac", "feature/t_39cf1905-webui-build-token", 54, "main", "f94f96a7042ba863768938e0c1afa79406397b77"),
Root("titan-iac", "t_e9597d89", "atlas-iac", "feature/hermes-three-lane-placement", 17, "main", "48cbe13ee50ce3fcb07cea3fe8d088cfed349e0c"),
Root("titan-iac", "t_a6a22d7c", "atlas-iac", "feature/hermes-cli-process-reaping", 20, "main", "a242dcc786576ae1a18000cb4941836ff610dff2"),
Root("titan-iac", "t_c425c446", "atlas-iac", "fix/t_39cf1905-jenkins-controller-priority", 49, "main", "f997171b5526f104d2474022c2a56683ec2960c3"),
Root("titan-iac", "t_cf89a2ec", "atlas-iac", "feature/hermes-next-hux", 55, "main", "98c7c6184f6edfe3cdac228529c2287584db3006"),
Root("cassandra", "t_b89e3903", "cassandra", "handoff/generated-strategy-audit-20260813", 1, "main", "14c07111b6ed8d7fc529362eb34fa0afa0694325"),
)
def _live_head(root: Root, read: Callable[[str], bytes]) -> str:
"""Return a live canonical PR head, or an empty string on any mismatch."""
try:
pull = json.loads(read(f"/api/v1/repos/titan/{root.project}/pulls/{root.pr_number}"))
except (OSError, TypeError, ValueError):
return ""
if not isinstance(pull, dict):
return ""
head, base = pull.get("head"), pull.get("base")
canonical = f"titan/{root.project}"
valid = (
pull.get("state") == "open" and isinstance(head, dict) and isinstance(base, dict)
and head.get("ref") == root.ref and isinstance(head.get("repo"), dict)
and head["repo"].get("full_name") == canonical and base.get("ref") == root.base
and isinstance(base.get("repo"), dict) and base["repo"].get("full_name") == canonical
)
value = head.get("sha") if isinstance(head, dict) else ""
return value if valid and isinstance(value, str) and SHA.fullmatch(value) else ""
def seed_root(kanban_db: Any, root: Root, read: Callable[[str], bytes]) -> str:
"""Seed one absent state row after proving both native task and live PR."""
with kanban_db.scoped_current_board(root.board):
connection = kanban_db.connect(board=root.board)
try:
if kanban_db.get_task(connection, root.root_task_id) is None:
return "missing-task"
finally:
connection.close()
existing = supervisor_state.get_root(root.board, root.root_task_id)
if existing is not None:
return "already-seeded" if existing == root.lineage else "lineage-conflict"
if _live_head(root, read) != root.head:
return "live-pr-mismatch"
supervisor_state.record_submission(root.board, root.root_task_id, root.lineage, root.head)
return "seeded"
def run(kanban_db: Any, read: Callable[[str], bytes]) -> dict[str, int]:
"""Seed each independent root and return compact operator-visible counts."""
counts: dict[str, int] = {}
for root in ROOTS:
outcome = seed_root(kanban_db, root, read)
counts[outcome] = counts.get(outcome, 0) + 1
return counts
def main() -> int:
"""Run the explicit operator migration without changing nonmatching state."""
from hermes_cli import kanban_db
counts = run(kanban_db, scm_broker_client.read)
print(json.dumps(counts, sort_keys=True))
failures = {"missing-task", "lineage-conflict", "live-pr-mismatch"}
return 0 if not failures & counts.keys() else 1
if __name__ == "__main__":
raise SystemExit(main())