Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """Verify repo handoff metadata for Agent Bus and future Codex sessions.""" | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import os | |
| import re | |
| import subprocess | |
| from datetime import datetime, timezone | |
| from pathlib import Path | |
| from typing import Any | |
| ROOT = Path(__file__).resolve().parents[1] | |
| DEFAULT_JSON = ROOT / "docs" / "assets" / "agent-handoff-report.json" | |
| DEFAULT_MD = ROOT / "docs" / "assets" / "agent-handoff-report.md" | |
| SCHEMA = "proofframe.agent_handoff.v1" | |
| EXPECTED_BRANCH = "feature/backblaze-proofframe" | |
| EXPECTED_TEAM_ID = "proofframe-hackathon-58c62c50" | |
| BUS_CLI = Path.home() / ".codex" / "tools" / "codex-agent-bus" / "bin" / "agent-bus" | |
| WORK_PATH_RE = re.compile(r"Work only inside `([^`]+)`") | |
| BRANCH_RE = re.compile(r"Current branch:\s*`([^`]+)`") | |
| TEAM_ID_RE = re.compile(r"Team id:\s*`([^`]+)`") | |
| def utc_now() -> str: | |
| return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") | |
| def normalize_path(raw_path: str) -> Path: | |
| return Path(raw_path).expanduser().resolve(strict=False) | |
| def running_in_ci() -> bool: | |
| return os.environ.get("GITHUB_ACTIONS") == "true" or os.environ.get("CI") == "true" | |
| def documented_repo_name_matches(root: Path, work_path: str | None) -> bool: | |
| return bool(work_path) and normalize_path(str(work_path)).name == root.name | |
| def extract(pattern: re.Pattern[str], text: str) -> str | None: | |
| match = pattern.search(text) | |
| return match.group(1) if match else None | |
| def check_item(check_id: str, label: str, ok: bool, detail: str, evidence: str) -> dict[str, Any]: | |
| return { | |
| "id": check_id, | |
| "label": label, | |
| "ok": ok, | |
| "detail": detail, | |
| "evidence": evidence, | |
| } | |
| def load_agents_text(root: Path) -> tuple[str, bool]: | |
| path = root / "AGENTS.md" | |
| try: | |
| return path.read_text(encoding="utf-8"), True | |
| except FileNotFoundError: | |
| return "", False | |
| def run_bus_json(root: Path, args: list[str]) -> tuple[dict[str, Any] | None, str | None]: | |
| result = subprocess.run( | |
| [str(BUS_CLI), *args], | |
| cwd=root, | |
| capture_output=True, | |
| text=True, | |
| check=False, | |
| ) | |
| if result.returncode != 0: | |
| return None, result.stderr.strip() or result.stdout.strip() or "agent-bus command failed." | |
| try: | |
| return json.loads(result.stdout), None | |
| except json.JSONDecodeError: | |
| return None, "agent-bus command did not return JSON." | |
| def resolve_agent_summary(root: Path, session_id: str) -> dict[str, Any]: | |
| payload, error = run_bus_json(root, ["resolve", session_id]) | |
| if error: | |
| return { | |
| "session_id": session_id, | |
| "resolved": False, | |
| "cwd": None, | |
| "cwd_matches_repo": False, | |
| "status": "resolve_error", | |
| "detail": error, | |
| } | |
| agent = (payload or {}).get("agent") or {} | |
| cwd = agent.get("cwd") | |
| cwd_matches = bool(cwd) and normalize_path(str(cwd)) == root.resolve() | |
| return { | |
| "session_id": session_id, | |
| "resolved": True, | |
| "agent_id": agent.get("agent_id"), | |
| "name": agent.get("name"), | |
| "cwd": cwd, | |
| "cwd_matches_repo": cwd_matches, | |
| "status": agent.get("status"), | |
| "stale": bool(agent.get("stale")), | |
| "last_seen": agent.get("last_seen"), | |
| "detail": ( | |
| "Agent registry cwd matches this repo." | |
| if cwd_matches | |
| else "Agent registry cwd is stale, missing, or unresolved." | |
| ), | |
| } | |
| def active_role_summaries(root: Path, roles: dict[str, Any]) -> list[dict[str, Any]]: | |
| summaries: list[dict[str, Any]] = [] | |
| for role_name, role in sorted(roles.items()): | |
| session_id = role.get("session_id") or role.get("thread_id") | |
| if role.get("status") != "active" or not session_id: | |
| continue | |
| summary = resolve_agent_summary(root, str(session_id)) | |
| summary.update( | |
| { | |
| "role": role_name, | |
| "role_status": role.get("status"), | |
| "thread_id": role.get("thread_id"), | |
| } | |
| ) | |
| summaries.append(summary) | |
| return summaries | |
| def bus_team_summary(root: Path, team_id: str = EXPECTED_TEAM_ID) -> dict[str, Any]: | |
| if not BUS_CLI.exists(): | |
| return { | |
| "checked": True, | |
| "ok": False, | |
| "status": "unavailable", | |
| "team_id": team_id, | |
| "detail": f"Agent Bus CLI not found at {BUS_CLI}.", | |
| "active_roles": [], | |
| "active_role_cwd_ok": False, | |
| } | |
| payload, error = run_bus_json(root, ["team", "show", team_id]) | |
| if error: | |
| return { | |
| "checked": True, | |
| "ok": False, | |
| "status": "error", | |
| "team_id": team_id, | |
| "detail": error, | |
| "active_roles": [], | |
| "active_role_cwd_ok": False, | |
| } | |
| team = (payload or {}).get("team") or {} | |
| project = team.get("project") | |
| project_matches = bool(project) and normalize_path(str(project)) == root.resolve() | |
| active_roles = active_role_summaries(root, team.get("roles") or {}) | |
| active_role_cwd_ok = bool(active_roles) and all(role.get("cwd_matches_repo") for role in active_roles) | |
| return { | |
| "checked": True, | |
| "ok": project_matches, | |
| "status": "current" if project_matches else "stale", | |
| "team_id": team.get("team_id") or team_id, | |
| "project": project, | |
| "expected_project": str(root.resolve()), | |
| "active_roles": active_roles, | |
| "active_role_cwd_ok": active_role_cwd_ok, | |
| "detail": ( | |
| "Agent Bus team project path matches this repo." | |
| if project_matches | |
| else "Agent Bus team project path is stale or missing; AGENTS.md is the repo authority." | |
| ), | |
| } | |
| def build_report( | |
| root: Path = ROOT, | |
| *, | |
| check_bus: bool = False, | |
| strict_bus: bool = False, | |
| team_id: str = EXPECTED_TEAM_ID, | |
| ) -> dict[str, Any]: | |
| root = root.resolve() | |
| agents_text, agents_present = load_agents_text(root) | |
| work_path = extract(WORK_PATH_RE, agents_text) if agents_present else None | |
| branch = extract(BRANCH_RE, agents_text) if agents_present else None | |
| documented_team_id = extract(TEAM_ID_RE, agents_text) if agents_present else None | |
| expected_root = str(root) | |
| normalized_work_path = str(normalize_path(work_path)) if work_path else None | |
| ci = running_in_ci() | |
| work_path_matches_repo = ( | |
| documented_repo_name_matches(root, work_path) | |
| if ci | |
| else bool(work_path) | |
| and normalized_work_path == expected_root | |
| ) | |
| work_path_exists = ( | |
| documented_repo_name_matches(root, work_path) | |
| if ci | |
| else bool(work_path) | |
| and normalize_path(work_path).exists() | |
| ) | |
| work_path_detail = ( | |
| f"AGENTS path is {work_path!r}; CI workspace is {expected_root!r}; repo name matches." | |
| if ci and work_path_matches_repo | |
| else f"AGENTS path is {work_path!r}; expected {expected_root!r}." | |
| ) | |
| work_path_exists_detail = ( | |
| f"AGENTS path is {work_path!r}; CI cannot require the developer-local absolute path to exist." | |
| if ci and work_path_exists | |
| else f"AGENTS path is {work_path!r}." | |
| ) | |
| checks = [ | |
| check_item( | |
| "agents_present", | |
| "AGENTS.md is present", | |
| agents_present, | |
| "AGENTS.md found." if agents_present else "AGENTS.md is missing.", | |
| "AGENTS.md", | |
| ), | |
| check_item( | |
| "work_path_matches_repo", | |
| "AGENTS work path matches this repo", | |
| work_path_matches_repo, | |
| work_path_detail, | |
| "AGENTS.md", | |
| ), | |
| check_item( | |
| "work_path_exists", | |
| "AGENTS work path exists", | |
| work_path_exists, | |
| work_path_exists_detail, | |
| "AGENTS.md", | |
| ), | |
| check_item( | |
| "branch_matches", | |
| "AGENTS branch matches current project branch", | |
| branch == EXPECTED_BRANCH, | |
| f"AGENTS branch is {branch!r}; expected {EXPECTED_BRANCH!r}.", | |
| "AGENTS.md", | |
| ), | |
| check_item( | |
| "team_id_matches", | |
| "AGENTS team id matches ProofFrame team", | |
| documented_team_id == team_id, | |
| f"AGENTS team id is {documented_team_id!r}; expected {team_id!r}.", | |
| "AGENTS.md", | |
| ), | |
| ] | |
| bus = ( | |
| bus_team_summary(root, team_id) | |
| if check_bus | |
| else { | |
| "checked": False, | |
| "ok": None, | |
| "status": "skipped", | |
| "team_id": team_id, | |
| "detail": "Pass --check-bus for local durable Agent Bus metadata inspection.", | |
| "active_roles": [], | |
| "active_role_cwd_ok": None, | |
| } | |
| ) | |
| if strict_bus: | |
| checks.append( | |
| check_item( | |
| "bus_project_matches_repo", | |
| "Durable Agent Bus project path matches this repo", | |
| bool(bus.get("ok")), | |
| str(bus.get("detail")), | |
| "agent-bus team show", | |
| ) | |
| ) | |
| ok = all(item["ok"] for item in checks) | |
| return { | |
| "schema": SCHEMA, | |
| "created_at": utc_now(), | |
| "ok": ok, | |
| "mode": "handoff_ready" if ok else "handoff_mismatch", | |
| "repo_root": expected_root, | |
| "environment": { | |
| "ci": ci, | |
| "github_actions": os.environ.get("GITHUB_ACTIONS") == "true", | |
| }, | |
| "agents": { | |
| "path": "AGENTS.md", | |
| "work_path": work_path, | |
| "normalized_work_path": normalized_work_path, | |
| "branch": branch, | |
| "team_id": documented_team_id, | |
| }, | |
| "bus": bus, | |
| "checks": checks, | |
| "next_actions": next_actions(checks, bus), | |
| } | |
| def next_actions(checks: list[dict[str, Any]], bus: dict[str, Any]) -> list[str]: | |
| actions: list[str] = [] | |
| failed = {item["id"] for item in checks if not item["ok"]} | |
| if "work_path_matches_repo" in failed or "work_path_exists" in failed: | |
| actions.append("Update AGENTS.md so Work only inside points to the current repo path.") | |
| if "branch_matches" in failed: | |
| actions.append("Update AGENTS.md Current branch when the long-lived feature branch changes.") | |
| if "team_id_matches" in failed: | |
| actions.append("Update AGENTS.md Team id if a new ProofFrame Agent Bus team is created.") | |
| if bus.get("status") == "stale": | |
| actions.append("Do not trust stale durable Agent Bus project metadata; use AGENTS.md as repo authority.") | |
| if bus.get("checked") and bus.get("active_roles") and not bus.get("active_role_cwd_ok"): | |
| actions.append("Reattach active Agent Bus roles with --cwd pointing to the current repo path.") | |
| if not actions: | |
| actions.append("Handoff metadata is ready for future Codex or Agent Bus sessions.") | |
| return actions | |
| def render_markdown(report: dict[str, Any]) -> str: | |
| lines = [ | |
| "# ProofFrame Agent Handoff Report", | |
| "", | |
| f"Mode: `{report['mode']}`", | |
| f"OK: `{str(report['ok']).lower()}`", | |
| f"Created: `{report['created_at']}`", | |
| f"Repo root: `{report['repo_root']}`", | |
| "", | |
| "## AGENTS.md", | |
| "", | |
| f"- Work path: `{report['agents']['work_path']}`", | |
| f"- Branch: `{report['agents']['branch']}`", | |
| f"- Team id: `{report['agents']['team_id']}`", | |
| "", | |
| "## Agent Bus", | |
| "", | |
| f"- Checked: `{str(report['bus']['checked']).lower()}`", | |
| f"- Status: `{report['bus']['status']}`", | |
| f"- Detail: {report['bus']['detail']}", | |
| f"- Active role cwd ok: `{str(report['bus'].get('active_role_cwd_ok')).lower()}`", | |
| "", | |
| "### Active Roles", | |
| "", | |
| "| Role | Session | Status | CWD matches repo | CWD |", | |
| "| --- | --- | --- | --- | --- |", | |
| ] | |
| if report["bus"].get("active_roles"): | |
| for role in report["bus"]["active_roles"]: | |
| lines.append( | |
| "| " | |
| f"{role.get('role')} | " | |
| f"`{role.get('session_id')}` | " | |
| f"`{role.get('status')}` | " | |
| f"`{str(role.get('cwd_matches_repo')).lower()}` | " | |
| f"`{role.get('cwd')}` |" | |
| ) | |
| else: | |
| lines.append("| none | `n/a` | `n/a` | `n/a` | `n/a` |") | |
| lines.extend( | |
| [ | |
| "", | |
| "## Checks", | |
| "", | |
| "| Status | Check | Detail | Evidence |", | |
| "| --- | --- | --- | --- |", | |
| ] | |
| ) | |
| for item in report["checks"]: | |
| status = "OK" if item["ok"] else "FAIL" | |
| lines.append(f"| {status} | {item['label']} | {item['detail']} | `{item['evidence']}` |") | |
| lines.extend(["", "## Next Actions", ""]) | |
| lines.extend(f"- {action}" for action in report["next_actions"]) | |
| lines.append("") | |
| return "\n".join(lines) | |
| def write_outputs(report: dict[str, Any], json_path: Path, markdown_path: Path) -> None: | |
| json_path.parent.mkdir(parents=True, exist_ok=True) | |
| markdown_path.parent.mkdir(parents=True, exist_ok=True) | |
| json_path.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8") | |
| markdown_path.write_text(render_markdown(report), encoding="utf-8") | |
| def build_parser() -> argparse.ArgumentParser: | |
| parser = argparse.ArgumentParser(description="Verify ProofFrame Agent Bus handoff metadata.") | |
| parser.add_argument("--json-out", type=Path, default=DEFAULT_JSON) | |
| parser.add_argument("--markdown-out", type=Path, default=DEFAULT_MD) | |
| parser.add_argument("--check-bus", action="store_true", help="Inspect local durable Agent Bus metadata.") | |
| parser.add_argument("--strict-bus", action="store_true", help="Fail if local durable Bus project path is stale.") | |
| return parser | |
| def main() -> None: | |
| args = build_parser().parse_args() | |
| report = build_report(ROOT, check_bus=args.check_bus, strict_bus=args.strict_bus) | |
| write_outputs(report, args.json_out, args.markdown_out) | |
| print( | |
| json.dumps( | |
| { | |
| "ok": report["ok"], | |
| "mode": report["mode"], | |
| "json": str(args.json_out), | |
| "markdown": str(args.markdown_out), | |
| "bus_status": report["bus"]["status"], | |
| "failed_checks": [item["id"] for item in report["checks"] if not item["ok"]], | |
| }, | |
| indent=2, | |
| ) | |
| ) | |
| if not report["ok"]: | |
| raise SystemExit(1) | |
| if __name__ == "__main__": | |
| main() | |