cto/tools/validate_cto_child.py
2026-05-31 18:41:18 -04:00

127 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""Validate the child-local Cortex OS CTO workspace."""
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
REQUIRED_FILES = [
"AGENTS.md",
"README.md",
"WORKBOARD.yaml",
"CONTEXT.md",
"sot/00-START/CTO-WORKSPACE-INTENT.md",
"sot/03-PROTOCOLS/CTO-CASE-BACKEND-BRIEF.md",
"sot/03-PROTOCOLS/CTO-CASE-CANDIDATE-BACKEND-PRD.md",
"sot/03-PROTOCOLS/CTO-CASE-CANDIDATE-BACKEND-ISSUES.md",
]
REQUIRED_BRIEF_PHRASES = [
"Cortex governs.",
"Hermes controls.",
"Case executes.",
"Harness proves.",
"Core promotes only through SOT route.",
"Case is the candidate CTO execution backend, not the CTO authority layer.",
"This brief is child-local planning.",
]
REQUIRED_PRD_PHRASES = [
"Local planning SOT only. Not a Core Protocol. Not active Core authority.",
"Case Candidate Backend",
"Harness Evidence Interface",
"Case may recommend; CTO Harness records; Hermes or operator approval is the only human approval signal.",
"Candidate Cortex Work Packet is an unpromoted term.",
"Staged Proof Gates",
"Source Admission Requirements",
"Failure-Mode Matrix",
"Fake remains the default validation lane.",
]
FORBIDDEN_PRD_PHRASES = [
"Case should be the default real-repo execution backend",
"Case is the default real-repo execution backend",
]
REQUIRED_ISSUE_IDS = [
"CTO-WORK-003",
"CTO-WORK-004",
"CTO-WORK-005",
"CTO-WORK-006",
"CTO-WORK-007",
"CTO-WORK-008",
]
def main() -> int:
checked: list[str] = []
errors: list[str] = []
for rel in REQUIRED_FILES:
path = ROOT / rel
checked.append(rel)
if not path.is_file():
errors.append(f"missing_required_file:{rel}")
brief = ROOT / "sot/03-PROTOCOLS/CTO-CASE-BACKEND-BRIEF.md"
if brief.is_file():
text = brief.read_text(encoding="utf-8")
for phrase in REQUIRED_BRIEF_PHRASES:
checked.append(f"brief_phrase:{phrase}")
if phrase not in text:
errors.append(f"missing_brief_phrase:{phrase}")
if "core_promotion_status: not-promoted" not in text:
errors.append("brief_missing_not_promoted_frontmatter")
prd = ROOT / "sot/03-PROTOCOLS/CTO-CASE-CANDIDATE-BACKEND-PRD.md"
if prd.is_file():
text = prd.read_text(encoding="utf-8")
for phrase in REQUIRED_PRD_PHRASES:
checked.append(f"prd_phrase:{phrase}")
if phrase not in text:
errors.append(f"missing_prd_phrase:{phrase}")
for phrase in FORBIDDEN_PRD_PHRASES:
checked.append(f"prd_forbidden_phrase:{phrase}")
if phrase in text:
errors.append(f"forbidden_prd_phrase:{phrase}")
if "core_promotion_status: not-promoted" not in text:
errors.append("prd_missing_not_promoted_frontmatter")
issues = ROOT / "sot/03-PROTOCOLS/CTO-CASE-CANDIDATE-BACKEND-ISSUES.md"
if issues.is_file():
text = issues.read_text(encoding="utf-8")
if "Local planning SOT only. Not a Core Protocol. Not active Core authority." not in text:
errors.append("issues_missing_local_planning_notice")
if "core_promotion_status: not-promoted" not in text:
errors.append("issues_missing_not_promoted_frontmatter")
for issue_id in REQUIRED_ISSUE_IDS:
checked.append(f"issue_id:{issue_id}")
if issue_id not in text:
errors.append(f"missing_issue_id:{issue_id}")
board = ROOT / "WORKBOARD.yaml"
if board.is_file():
text = board.read_text(encoding="utf-8")
for issue_id in ["CTO-WORK-002", *REQUIRED_ISSUE_IDS]:
checked.append(f"workboard_id:{issue_id}")
if issue_id not in text:
errors.append(f"missing_workboard_id:{issue_id}")
payload = {
"ok": not errors,
"validator": "cto-child-v1",
"checked": checked,
"errors": errors,
"warnings": [],
}
print(json.dumps(payload, indent=2, sort_keys=True))
return 0 if not errors else 1
if __name__ == "__main__":
raise SystemExit(main())