121 lines
5.2 KiB
Python
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())
|