teleo-infrastructure/scripts/build_working_leo_authorized_apply_runbook.py
twentyOne2x 637d285ecb
Some checks are pending
CI / lint-and-test (push) Waiting to run
Add Leo production preflight collector
2026-07-10 03:39:44 +02:00

347 lines
16 KiB
Python

#!/usr/bin/env python3
"""Build the Working Leo authorized production-apply runbook.
This is a runbook generator, not an executor. It reads the retained integrated
packet manifest and readiness proof, then emits the exact preflight/apply/
postflight/rollback order an operator can use after explicit production apply
authorization. It never opens a database connection and never runs SQL.
"""
from __future__ import annotations
import argparse
import hashlib
import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
import verify_working_leo_apply_readiness as readiness
import working_leo_integrated_packet as integrated_packet
REPO_REPORT_DIR = Path("docs/reports/leo-working-state-20260709")
DEFAULT_OUT = REPO_REPORT_DIR / "working-leo-authorized-apply-runbook-current.json"
DEFAULT_MARKDOWN_OUT = REPO_REPORT_DIR / "working-leo-authorized-apply-runbook-current.md"
PHASE_FILES: dict[str, dict[str, str]] = {
"mapped-rich-base": {
"preflight": "production-preflight.sql",
"apply": "production-apply-authorized-commit.sql",
"postflight": "production-postflight.sql",
"rollback": "production-delete-rollback.sql",
},
"cross-surface-anchor": {
"preflight": "cross-surface-preflight.sql",
"apply": "cross-surface-apply-authorized-commit.sql",
"postflight": "cross-surface-postflight.sql",
"rollback": "cross-surface-delete-rollback.sql",
},
"rio-strategy-context": {
"preflight": "rio-strategy-preflight.sql",
"apply": "rio-strategy-apply-authorized-commit.sql",
"postflight": "rio-strategy-postflight.sql",
"rollback": "rio-strategy-delete-rollback.sql",
},
"governance-concept-schema": {
"preflight": "governance-concept-preflight.sql",
"apply": "governance-concept-apply-authorized-commit.sql",
"postflight": "governance-concept-postflight.sql",
"rollback": "governance-concept-delete-rollback.sql",
},
"helmer-7powers": {
"preflight": "helmer-7powers-production-preflight.sql",
"apply": "helmer-7powers-production-apply-authorized-commit.sql",
"postflight": "helmer-7powers-production-postflight.sql",
"rollback": "helmer-7powers-production-delete-rollback.sql",
},
}
POST_APPLY_REGRESSION_COMMANDS: list[dict[str, Any]] = [
{
"id": "direct_claim_handler_suite",
"description": "Re-run the non-posting VPS GatewayRunner direct-claim suite after production postflight.",
"command_template": ".venv/bin/python scripts/run_leo_direct_claim_handler_suite.py",
"expected_artifact": str(REPO_REPORT_DIR / "telegram-handler-direct-claim-suite-current.json"),
"mutates_production_db": False,
"posts_to_telegram": False,
},
{
"id": "direct_claim_strict_score",
"description": "Strict-score the retained direct-claim replies; expected score is 6/6.",
"command_template": (
".venv/bin/python scripts/working_leo_open_ended_benchmark.py "
"--results-json docs/reports/leo-working-state-20260709/telegram-handler-direct-claim-suite-current.json "
"--include-direct-claim-followups "
"--out docs/reports/leo-working-state-20260709/telegram-handler-direct-claim-suite-score-current.json "
"--markdown-out docs/reports/leo-working-state-20260709/telegram-handler-direct-claim-suite-score-current.md"
),
"expected_artifact": str(REPO_REPORT_DIR / "telegram-handler-direct-claim-suite-score-current.md"),
"mutates_production_db": False,
"posts_to_telegram": False,
},
{
"id": "focused_local_regression_tests",
"description": "Verify benchmark scoring, bridge skill surfaces, and runbook invariants after artifact refresh.",
"command_template": (
".venv/bin/python -m pytest "
"tests/test_working_leo_open_ended_benchmark.py "
"tests/test_hermes_leoclean_skill_surfaces.py "
"tests/test_build_working_leo_authorized_apply_runbook.py -q"
),
"expected_artifact": "terminal pytest output",
"mutates_production_db": False,
"posts_to_telegram": False,
},
{
"id": "gateway_service_readback",
"description": "Read back the VPS Leo gateway service state after postflight and regressions.",
"command_template": (
"systemctl show leoclean-gateway.service "
"-p ActiveState -p SubState -p MainPID -p NRestarts -p ExecMainStartTimestamp --no-pager"
),
"expected_artifact": "retained service readback with ActiveState=active and NRestarts reviewed",
"mutates_production_db": False,
"posts_to_telegram": False,
},
{
"id": "production_receipt_validator",
"description": "Validate the retained production-apply receipt against the authorized runbook requirements.",
"command_template": (
".venv/bin/python scripts/validate_working_leo_production_receipt.py "
"--receipt docs/reports/leo-working-state-20260709/working-leo-production-apply-receipt-current.json"
),
"expected_artifact": str(REPO_REPORT_DIR / "working-leo-production-apply-receipt-validation-current.md"),
"mutates_production_db": False,
"posts_to_telegram": False,
},
]
PRODUCTION_APPLY_RECEIPT_REQUIRED_FIELDS: list[str] = [
"explicit_authorization_record",
"operator_identity",
"git_commit_sha",
"preflight_output_paths",
"apply_output_paths",
"postflight_output_paths",
"expected_after_apply_counts",
"actual_after_apply_counts",
"telegram_or_handler_regression_paths",
"direct_claim_score_path",
"service_readback",
"cleanup_readback",
"rollback_packet_paths",
"known_residual_risks",
]
PRE_APPLY_VALIDATION_COMMANDS: list[dict[str, Any]] = [
{
"id": "readonly_production_preflight_collector",
"description": (
"Collect a production baseline for every preflight SQL file through a read-only transaction wrapper. "
"During an authorized apply window, still run each phase preflight again immediately before its phase."
),
"command_template": (
".venv/bin/python scripts/collect_working_leo_production_preflight.py --execute "
"--out docs/reports/leo-working-state-20260709/working-leo-production-preflight-current.json "
"--markdown-out docs/reports/leo-working-state-20260709/working-leo-production-preflight-current.md"
),
"expected_artifact": str(REPO_REPORT_DIR / "working-leo-production-preflight-current.md"),
"mutates_production_db": False,
"applies_production_packet": False,
},
]
def _sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def _file_entry(report_dir: Path, packet_id: str, phase: str) -> dict[str, Any]:
filename = PHASE_FILES[packet_id][phase]
path = report_dir / filename
exists = path.exists()
entry: dict[str, Any] = {
"packet_id": packet_id,
"phase": phase,
"file": filename,
"path": str(path),
"exists": exists,
"ok": False,
}
if not exists:
entry["error"] = "missing"
return entry
text = path.read_text(encoding="utf-8")
entry.update(
{
"bytes": len(text.encode("utf-8")),
"sha256": _sha256(path),
"has_transaction_boundary": "begin;" in text.lower() and "commit;" in text.lower(),
"command_template": f'psql "$TELEO_DATABASE_URL" -v ON_ERROR_STOP=1 -f {path}',
}
)
entry["ok"] = bool(entry["bytes"] > 0)
return entry
def _phase_entries(report_dir: Path, packet_ids: list[str], phase: str) -> list[dict[str, Any]]:
return [_file_entry(report_dir, packet_id, phase) for packet_id in packet_ids]
def build_runbook(report_dir: Path = REPO_REPORT_DIR) -> dict[str, Any]:
manifest = integrated_packet.build_manifest(report_dir)
readiness_report = readiness.build_readiness(report_dir)
apply_order = manifest["apply_order"]
rollback_order = manifest["rollback_order"]
preflight = _phase_entries(report_dir, apply_order, "preflight")
apply = _phase_entries(report_dir, apply_order, "apply")
postflight = _phase_entries(report_dir, apply_order, "postflight")
rollback = _phase_entries(report_dir, rollback_order, "rollback")
all_files = preflight + apply + postflight + rollback
checks = {
"readiness_passes": readiness_report["ready_for_authorized_production_apply_packet"] is True,
"readiness_says_not_executed": readiness_report["production_apply_executed"] is False,
"readiness_says_not_authorized": readiness_report["production_apply_authorization_present"] is False,
"apply_order_matches_manifest": apply_order == [packet["id"] for packet in integrated_packet.PACKETS],
"rollback_order_matches_manifest": rollback_order == list(reversed(apply_order)),
"all_phase_files_exist": all(item["exists"] for item in all_files),
"all_phase_files_non_empty": all(item["ok"] for item in all_files),
"all_apply_and_rollback_sql_have_transactions": all(
item.get("has_transaction_boundary") is True for item in apply + rollback
),
"post_apply_regression_commands_present": all(
item.get("command_template") and item.get("expected_artifact") for item in POST_APPLY_REGRESSION_COMMANDS
),
"pre_apply_validation_commands_present": all(
item.get("command_template") and item.get("expected_artifact") for item in PRE_APPLY_VALIDATION_COMMANDS
),
"production_apply_receipt_schema_present": all(PRODUCTION_APPLY_RECEIPT_REQUIRED_FIELDS),
}
ready = all(checks.values())
return {
"generated_at_utc": datetime.now(timezone.utc).isoformat(),
"mode": "operator_runbook_generation_only",
"report_dir": str(report_dir),
"status": "ready_not_executed" if ready else "not_ready",
"ready_for_authorized_operator": ready,
"production_apply_executed": readiness_report["production_apply_executed"],
"production_apply_authorization_present": readiness_report["production_apply_authorization_present"],
"required_authorization_boundary": (
"Do not run apply commands unless the operator explicitly authorizes production DB apply for the integrated "
"Working Leo packet set in dependency order."
),
"operator_environment": {
"required_env": ["TELEO_DATABASE_URL"],
"runbook_generation_runs_sql": False,
"operator_commands_run_sql_if_executed": True,
"runbook_only": True,
},
"checks": checks,
"expected_after_apply_counts": manifest["expected_after_apply_counts"],
"phases": {
"preflight_order": preflight,
"apply_order": apply,
"postflight_order": postflight,
"rollback_order": rollback,
},
"post_apply_required_actions": [
"Run every postflight SQL file and retain output.",
"Run the post-apply regression command bundle and retain every artifact.",
"Record production apply receipt, row counts, service MainPID/NRestarts, and cleanup readback.",
],
"pre_apply_validation_commands": PRE_APPLY_VALIDATION_COMMANDS,
"post_apply_regression_commands": POST_APPLY_REGRESSION_COMMANDS,
"production_apply_receipt_required_fields": PRODUCTION_APPLY_RECEIPT_REQUIRED_FIELDS,
"claim_ceiling": (
"This runbook is proof that the authorized apply sequence is explicit and ordered. It does not execute SQL, "
"does not mutate production, and does not replace explicit production apply authorization."
),
}
def write_markdown(path: Path, runbook: dict[str, Any]) -> None:
lines = [
"# Working Leo Authorized Apply Runbook",
"",
f"Generated UTC: `{runbook['generated_at_utc']}`",
f"Mode: `{runbook['mode']}`",
f"Status: `{runbook['status']}`",
f"Ready for authorized operator: `{runbook['ready_for_authorized_operator']}`",
f"Production apply executed: `{runbook['production_apply_executed']}`",
f"Production apply authorization present: `{runbook['production_apply_authorization_present']}`",
"",
"## Authorization Boundary",
"",
runbook["required_authorization_boundary"],
"",
"## Checks",
"",
]
lines.extend(f"- `{key}`: `{value}`" for key, value in runbook["checks"].items())
for title, key in [
("Preflight Order", "preflight_order"),
("Apply Order", "apply_order"),
("Postflight Order", "postflight_order"),
("Rollback Order", "rollback_order"),
]:
lines.extend(["", f"## {title}", ""])
for index, entry in enumerate(runbook["phases"][key], start=1):
lines.append(f"{index}. `{entry['packet_id']}` -> `{entry['file']}`")
lines.append(f" - `{entry['command_template']}`")
lines.extend(["", "## Expected Counts After Apply", ""])
lines.extend(f"- `{key}`: `{value}`" for key, value in sorted(runbook["expected_after_apply_counts"].items()))
lines.extend(["", "## Pre-Apply Validation Commands", ""])
for item in runbook["pre_apply_validation_commands"]:
lines.append(f"- `{item['id']}`: {item['description']}")
lines.append(f" - command: `{item['command_template']}`")
lines.append(f" - artifact: `{item['expected_artifact']}`")
lines.append(f" - mutates production DB: `{item['mutates_production_db']}`")
lines.append(f" - applies production packet: `{item['applies_production_packet']}`")
lines.extend(["", "## Post-Apply Required Actions", ""])
lines.extend(f"- {item}" for item in runbook["post_apply_required_actions"])
lines.extend(["", "## Post-Apply Regression Commands", ""])
for item in runbook["post_apply_regression_commands"]:
lines.append(f"- `{item['id']}`: {item['description']}")
lines.append(f" - command: `{item['command_template']}`")
lines.append(f" - artifact: `{item['expected_artifact']}`")
lines.append(f" - mutates production DB: `{item['mutates_production_db']}`")
lines.append(f" - posts to Telegram: `{item['posts_to_telegram']}`")
lines.extend(["", "## Production Apply Receipt Required Fields", ""])
lines.extend(f"- `{item}`" for item in runbook["production_apply_receipt_required_fields"])
lines.extend(["", "## Claim Ceiling", "", runbook["claim_ceiling"], ""])
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("\n".join(lines), encoding="utf-8")
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--report-dir", type=Path, default=REPO_REPORT_DIR)
parser.add_argument("--out", type=Path, default=DEFAULT_OUT)
parser.add_argument("--markdown-out", type=Path, default=DEFAULT_MARKDOWN_OUT)
return parser.parse_args(argv)
def main(argv: list[str] | None = None) -> int:
args = parse_args(argv)
runbook = build_runbook(args.report_dir)
args.out.parent.mkdir(parents=True, exist_ok=True)
args.out.write_text(json.dumps(runbook, indent=2, sort_keys=True) + "\n", encoding="utf-8")
write_markdown(args.markdown_out, runbook)
print(
json.dumps(
{
"out": str(args.out),
"markdown_out": str(args.markdown_out),
"status": runbook["status"],
"ready_for_authorized_operator": runbook["ready_for_authorized_operator"],
"production_apply_executed": runbook["production_apply_executed"],
},
indent=2,
sort_keys=True,
)
)
return 0 if runbook["ready_for_authorized_operator"] else 1
if __name__ == "__main__":
raise SystemExit(main())