#!/usr/bin/env python3 """Validate the child-local Cortex OS CTO workspace.""" from __future__ import annotations import json import re 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", "sot/03-PROTOCOLS/CTO-HARNESS-EVIDENCE-INTERFACE-CONTRACT.md", "sot/03-PROTOCOLS/CTO-CASE-SOURCE-ADMISSION-RECORD.md", "sot/03-PROTOCOLS/CTO-CASE-ADAPTER-CONTRACT.md", "sot/03-PROTOCOLS/CTO-CASE-FAILURE-FIXTURE-MATRIX.md", "sot/03-PROTOCOLS/CTO-CASE-STAGED-PROOF-GATES.md", "sot/03-PROTOCOLS/CTO-CASE-STAGE1-GATED-ENGINE-PRD.md", "sot/03-PROTOCOLS/CTO-CASE-STAGE1-GATED-ENGINE-ISSUES.md", "sot/03-PROTOCOLS/CTO-CASE-STAGE2-ARTIFICIAL-FIXTURE-PRD.md", "sot/03-PROTOCOLS/CTO-CASE-STAGE2-ARTIFICIAL-FIXTURE-ISSUES.md", "sot/03-PROTOCOLS/CTO-CASE-PROVIDER-ADMISSION-PRD.md", "sot/03-PROTOCOLS/CTO-CASE-PROVIDER-ADMISSION-ISSUES.md", "sot/03-PROTOCOLS/CTO-CASE-PROVIDER-BUILD-PRD.md", "sot/03-PROTOCOLS/CTO-CASE-PROVIDER-BUILD-ISSUES.md", "sot/03-PROTOCOLS/CTO-CASE-MODEL-PROVIDER-ADMISSION-PRD.md", "sot/03-PROTOCOLS/CTO-CASE-MODEL-PROVIDER-ADMISSION-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", ] REQUIRED_EVIDENCE_INTERFACE_PHRASES = [ "Local planning SOT only. Not a Core Protocol. Not active Core authority.", "This contract does not authorize Runtime behavior", "The Harness Evidence Interface is the only accepted proof surface for backend comparison.", "report.json", "events.normalized.jsonl", "patch.diff", "test.log", "trace.jsonl", "artifact_digests", "SHA-256", "run_started_at", "run_finished_at", "backend_exit_code", "allowed_writes_passed", "approval.requested", "approval.granted", "approval.denied", "Case may recommend. Case must not approve itself.", "No merge, push, deploy, close, or real-repo mutation is allowed without explicit task-contract permission.", "fail closed", ] REQUIRED_SOURCE_ADMISSION_PHRASES = [ "Local planning SOT only. Not a Core Protocol. Not active Core authority.", "This record does not authorize Runtime behavior", "https://github.com/workos/case.git", "7959ac917cdeb0983b4aaa20bb9f42021747fed8", "2026-05-31", "git clone --depth 1 https://github.com/workos/case.git", "License status is unresolved.", "package private: true", "Status: not admitted for execution.", "Current allowed execution mode: planning-only.", "do not mutate the Case repository", "do not vendor Case source into Cortex OS Core", "No moving branch reference may be used as proof without a pinned commit or tag.", ] REQUIRED_ADAPTER_CONTRACT_PHRASES = [ "Local planning SOT only. Not a Core Protocol. Not active Core authority.", "This contract does not authorize Runtime behavior", "register backend id `case` as a gated engine before execution", "require the harness to accept `--engine case` only when explicitly enabled", "prevent a parallel runner path outside the existing harness seam", "`case` must be disabled by default.", "No missing gate may degrade to a warning. Missing gate means blocked.", "Adapter Input Contract", "Adapter Output Contract", "CTO Eligibility Decision", "`selected_backend`", "`denied_backends`", "`required_gates`", "`missing_gates`", "`allowed_mutation_mode`", "Case must not create or override the Eligibility Decision.", "Case may recommend. Case must not approve itself or select its own authority.", "This contract deliberately does not create `case-engine.sh`.", ] REQUIRED_FAILURE_FIXTURE_PHRASES = [ "Local planning SOT only. Not a Core Protocol. Not active Core authority.", "This matrix does not authorize Runtime behavior", "This matrix is planning-only and does not run Case.", "no-diff", "disallowed-file", "failed-tests", "missing-test-command", "missing-event", "reviewer-reject", "approval-denied", "timeout", "dirty-starting-tree", "dirty-ending-tree", "artifact-write-failure", "provider-unavailable", "`no_diff`", "`disallowed_file_change`", "`verification_failed`", "`missing_test_command`", "`missing_required_event`", "`reviewer_rejected`", "`approval_denied`", "`dirty_starting_tree`", "`dirty_ending_tree`", "`artifact_write_failure`", "`provider_unavailable`", "report.json", "events.normalized.jsonl", "patch.diff", "test.log", "trace.jsonl", "artifact_digests", "freshness proof", "fail closed", ] REQUIRED_STAGED_PROOF_PHRASES = [ "Local planning SOT only. Not a Core Protocol. Not active Core authority.", "Default status is earned, not assumed.", "Stages must be completed in order.", "Missing evidence means blocked, not partially accepted.", "Stage 1 - Gated Case Engine", "Stage 2 - Artificial Fixture", "Stage 3 - Copied Repo Fixture", "Stage 4 - Disposable Sandbox Repo", "Stage 5 - Owned Noncritical Repo", "Stage 6 - Candidate Default", "Allowed mutation scope: none.", "Allowed mutation scope: copied artificial case only.", "Allowed mutation scope: copied local repository fixture only.", "Allowed mutation scope: disposable repository only.", "Allowed mutation scope: explicitly owned low-risk repository only.", "Allowed mutation scope: scoped real-repo use only.", "fake, Codex, and Pi where applicable", "Case matches or beats existing lanes on report shape", "Case matches or beats existing lanes on failure closure", "Any future implementation must start with Stage 1", "must not skip to real-repo execution", ] REQUIRED_STAGE1_PRD_PHRASES = [ "Local planning SOT only. Not a Core Protocol. Not active Core authority.", "Stage 1 must prove only the smallest safe executable behavior", "no-op gated `case` backend path", "It does not run Case", "Stage 1 keeps allowed mutation scope as `none`.", "`case` is disabled by default.", "Missing gate produces blocked status, not warning.", "CTO_HARNESS_ALLOW_CASE=1", "backend.gate.blocked", "case_process_started: false", "source_admission_status: not_admitted", "backend/case-gate.log", "Stage 1 gate runs before case workspace copy", "No files under harness source checkout, target repo, Case source, vendor source, or Cortex Core are changed by the Stage 1 run.", "fake remains the default validation lane", "/home/svrnty/workspaces/hermes/cto/harness", "Stage 1 does not make Case executable", ] REQUIRED_STAGE1_ISSUE_IDS = [ "CTO-WORK-009", "CTO-WORK-010", ] REQUIRED_STAGE2_PRD_PHRASES = [ "Local planning SOT only. Not a Core Protocol. Not active Core authority.", "Stage 2 must prove only one narrow executable behavior", "copied artificial fixture", "Stage 2 allowed mutation scope is `copied artificial case only`.", "CTO_HARNESS_ALLOW_CASE=1", "CTO_HARNESS_CASE_STAGE=2", "Missing Stage 2 gate means blocked, not warning.", "runtime_workspace_root", "run_artifact_dir", "Fake remains the default validation lane.", "No Target Repository path is inspected or copied.", "task contract contains no Target Repository path", "source_admission_status: not_admitted", "allowed_writes_passed", "report.md", "backend raw logs under `backend/`", "backend: case", "case_process_started", "changed_files", "blockers", "artifact digests", "freshness proof", "same-run fake baseline", "no diff", "disallowed file", "failed tests", "missing test command", "missing required event", "provider unavailable", "python3 harness/runner/validate-case-stage2.py --harness-root harness --json", "python3 harness/runner/validate-case-stage1.py --harness-root harness --json", "Stage 2 does not authorize copied repo, sandbox repo, owned repo, default backend, WebUI product, or Core promotion behavior.", ] REQUIRED_STAGE2_ISSUE_IDS = [ "CTO-WORK-011", "CTO-WORK-012", ] REQUIRED_PROVIDER_ADMISSION_PRD_PHRASES = [ "Local planning SOT only. Not a Core Protocol. Not active Core authority.", "https://github.com/workos/case.git", "7959ac917cdeb0983b4aaa20bb9f42021747fed8", "Package status: `private: true`.", "Public npm package `case` is not WorkOS Case.", "Case CLI binary name is `ca`", "Case setup requires Bun.", "bun run build:binary", "dist/ca", "ca run --task ", "ca session --task ", "CTO_HARNESS_CASE_BIN", "WorkOS Case expects a task file and a Case command shape, not an arbitrary prompt string.", "The provider path must be explicit and durable", "The adapter must not pass the task brief as a raw positional prompt.", "The task file must contain no Target Repository path for Stage 2.", "Missing Bun, missing `dist/ca`, wrong commit, wrong command shape, or wrong task file means blocked.", "Case may execute only inside the Harness Evidence Interface.", "Case may recommend. Case must not approve itself.", "Stage 2 pass report exists only after a real `ca run --task ` execution.", "Fake remains the default validation lane.", "Do not install or use the unrelated npm `case` package.", "Do not treat Case as CTO authority.", ] REQUIRED_PROVIDER_ADMISSION_ISSUE_IDS = [ "CTO-WORK-013", "CTO-WORK-014", ] REQUIRED_PROVIDER_BUILD_PRD_PHRASES = [ "Local planning SOT only. Not a Core Protocol. Not active Core authority.", "no durable admitted `ca` executable exists", "Node `v20.19.5`, no local `bun` executable", "no `dist/ca`, no PATH `ca`", "https://github.com/workos/case.git", "7959ac917cdeb0983b4aaa20bb9f42021747fed8", "discovery evidence only", "durable WorkOS Case `ca` executable", "source pin and SHA-256 digest", "CTO_HARNESS_CASE_BIN", "bun run build:binary", "dist/ca", "unrelated npm `case` package", "ca run --task --mode unattended", "allowed paths, forbidden actions, verification command, and evidence expectations", "Missing Bun blocks before build; it does not degrade to warning.", "missing credentials", "Stage 2 with `CTO_HARNESS_CASE_BIN=` produces a pass report only through real Case execution.", "Current Hermes source admission status remains `not_admitted` until the provider build report is recorded.", "Same-run fake baseline comparison remains required", "report.json", "report.md", "events.normalized.jsonl", "trace.jsonl", "patch.diff", "test.log", "backend raw logs", "artifact digests", "freshness proof", "source_admission_status", "case_process_started", "backend_exit_code", "allowed_writes_passed", "changed_files", "blockers", "No Cortex Core, Case source, vendor source, or Target Repository file is mutated by admission.", "harness/evals/health.sh --json", "Stage 2 moves from provider-unavailable blocked status to a real Case pass report", "9811f870af2f85616e359d42ba70566c9af08ca20d8660456929a56ec761513f", "20260531T233721Z-r1-string-slugify-1814067", "CTO-WORK-016` remains blocked because no real Case Stage 2 pass report exists", "CTO-WORK-017 - Case Provider Timeout Fail-Closed Route", "CTO_HARNESS_CASE_TIMEOUT_SECONDS", "backend/provider-timeout.txt", "provider_timeout_fail_closed", "d23c492 Fail closed on Case provider timeout", "anthropic", "claude-sonnet-4-6", "20260531T234205Z-r1-string-slugify-1834617/report.json", "CTO_HARNESS_CASE_MODEL_PROVIDER", "CTO_HARNESS_CASE_MODEL", "backend/provider-model-not-admitted.txt", "model_provider_gate_blocks", "4500082 Gate Case execution on admitted model", ] REQUIRED_PROVIDER_BUILD_ISSUE_IDS = [ "CTO-WORK-015", "CTO-WORK-016", "CTO-WORK-017", "CTO-WORK-018", ] REQUIRED_MODEL_PROVIDER_ADMISSION_PRD_PHRASES = [ "Local planning SOT only. Not a Core Protocol. Not active Core authority.", "CTO-WORK-018", "anthropic", "claude-sonnet-4-6", "unadmitted external model path", "explicit admitted provider/model pair", "credential source class", "allowed network class", "admission timestamp", "CTO_HARNESS_CASE_MODEL_PROVIDER", "CTO_HARNESS_CASE_MODEL", "backend/provider-model-not-admitted.txt", "unadmitted provider/model blocks before `case_process_started`", "models.default", "CASE_DATA_DIR/config.json", "ca run --task --mode unattended", "case_model_provider", "case_model", "case_model_admission_status", "case_process_started", "allowed_writes_passed", "changed_files", "blockers", "Fake remains the default validation lane.", "Same-run fake baseline comparison remains required.", "No secrets appear in task file, argv, report, trace, backend logs, SOT, or commits.", "4500082 Gate Case execution on admitted model", "model_provider_gate_blocks", "f39d8ab Require admitted Case model pair", "CTO_HARNESS_CASE_MODEL_ADMISSION_FILE", "Env provider/model is now a requested pair, not admission authority.", "The admission JSON is the authority for real Case Stage 2 model admission.", "admitted", "missing_admission", "mismatch", "invalid_admission", "not_admitted", "report.md", "Case stdout/stderr", "20260531T235421Z-r1-string-slugify-1875638", "20260531T235448Z-r1-string-slugify-1876884", "`CTO-WORK-020` remains blocked because no real provider/model has been approved and no real Case Stage 2 pass report exists.", "Decision Record Template For CTO-WORK-020", "`decision_status`: `not_decided`, `external_provider_approved`, or `local_provider_required`.", "`provider_class`: `external_anthropic` or `local_case_compatible`.", "`provider`: exact provider string, or empty while blocked.", "`model`: exact model string, or empty while blocked.", "`approval_source`: JP approval reference or governed Core route reference.", "`credential_source_class`: credential class only; no secret value.", "`allowed_network_class`: allowed network class for this provider.", "`review_trigger`: expiry, date, or condition that forces review.", "`evidence_sources`: references to existing admission/build evidence, not copied runtime evidence.", "`effect`: `CTO-WORK-020 remains blocked until admitted provider/model and real Stage 2 pass report exist`.", "`not_decided`: no provider/model may run.", "`local_provider_required`: no external provider may run; create a Case-compatible local provider route first.", "`external_provider_approved`: may proceed only when the approval source, credential source class, allowed network class, and admission JSON are recorded.", ] REQUIRED_MODEL_PROVIDER_ADMISSION_ISSUE_IDS = [ "CTO-WORK-019", "CTO-WORK-020", ] REQUIRED_MODEL_PROVIDER_ADMISSION_ISSUE_PHRASES = [ "f39d8ab Require admitted Case model pair", "`f39d8ab` proves admission gating implementation only; it is not a real Case Stage 2 pass.", "CTO_HARNESS_CASE_MODEL_ADMISSION_FILE", "admission JSON is the authority", "Missing admission blocks before `case_process_started`.", "Mismatched admission blocks before `case_process_started`.", "case_model_provider", "case_model", "case_model_admission_status", "admitted", "missing_admission", "mismatch", "invalid_admission", "not_admitted", "report.md", "Case stdout/stderr", "20260531T235421Z-r1-string-slugify-1875638", "20260531T235448Z-r1-string-slugify-1876884", "`CTO-WORK-020` remains blocked until a provider/model is explicitly approved and real Case Stage 2 produces a pass report.", "CTO-WORK-020 Decision Record Template", "This template belongs to `CTO-WORK-020`; it is not a new provider approval.", "`decision_status`: `not_decided`, `external_provider_approved`, or `local_provider_required`.", "`provider_class`: `external_anthropic` or `local_case_compatible`.", "`provider`: exact provider string, or empty while blocked.", "`model`: exact model string, or empty while blocked.", "`approval_source`: JP approval reference or governed Core route reference.", "`credential_source_class`: credential class only; no secret value.", "`allowed_network_class`: allowed network class for this provider.", "`review_trigger`: expiry, date, or condition that forces review.", "`evidence_sources`: references to existing admission/build evidence, not copied runtime evidence.", "`effect`: `CTO-WORK-020 remains blocked until admitted provider/model and real Stage 2 pass report exist`.", ] def workboard_status(text: str, issue_id: str) -> str | None: pattern = rf"- id: {re.escape(issue_id)}\n(?: .+\n)*? status: ([^\n]+)" match = re.search(pattern, text) return match.group(1).strip() if match else None 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}") evidence_interface = ROOT / "sot/03-PROTOCOLS/CTO-HARNESS-EVIDENCE-INTERFACE-CONTRACT.md" if evidence_interface.is_file(): text = evidence_interface.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("evidence_interface_missing_not_promoted_frontmatter") for phrase in REQUIRED_EVIDENCE_INTERFACE_PHRASES: checked.append(f"evidence_interface_phrase:{phrase}") if phrase not in text: errors.append(f"missing_evidence_interface_phrase:{phrase}") source_admission = ROOT / "sot/03-PROTOCOLS/CTO-CASE-SOURCE-ADMISSION-RECORD.md" if source_admission.is_file(): text = source_admission.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("source_admission_missing_not_promoted_frontmatter") for phrase in REQUIRED_SOURCE_ADMISSION_PHRASES: checked.append(f"source_admission_phrase:{phrase}") if phrase not in text: errors.append(f"missing_source_admission_phrase:{phrase}") adapter_contract = ROOT / "sot/03-PROTOCOLS/CTO-CASE-ADAPTER-CONTRACT.md" if adapter_contract.is_file(): text = adapter_contract.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("adapter_contract_missing_not_promoted_frontmatter") for phrase in REQUIRED_ADAPTER_CONTRACT_PHRASES: checked.append(f"adapter_contract_phrase:{phrase}") if phrase not in text: errors.append(f"missing_adapter_contract_phrase:{phrase}") failure_matrix = ROOT / "sot/03-PROTOCOLS/CTO-CASE-FAILURE-FIXTURE-MATRIX.md" if failure_matrix.is_file(): text = failure_matrix.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("failure_matrix_missing_not_promoted_frontmatter") for phrase in REQUIRED_FAILURE_FIXTURE_PHRASES: checked.append(f"failure_matrix_phrase:{phrase}") if phrase not in text: errors.append(f"missing_failure_matrix_phrase:{phrase}") staged_proof = ROOT / "sot/03-PROTOCOLS/CTO-CASE-STAGED-PROOF-GATES.md" if staged_proof.is_file(): text = staged_proof.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("staged_proof_missing_not_promoted_frontmatter") for phrase in REQUIRED_STAGED_PROOF_PHRASES: checked.append(f"staged_proof_phrase:{phrase}") if phrase not in text: errors.append(f"missing_staged_proof_phrase:{phrase}") stage1_prd = ROOT / "sot/03-PROTOCOLS/CTO-CASE-STAGE1-GATED-ENGINE-PRD.md" if stage1_prd.is_file(): text = stage1_prd.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("stage1_prd_missing_not_promoted_frontmatter") for phrase in REQUIRED_STAGE1_PRD_PHRASES: checked.append(f"stage1_prd_phrase:{phrase}") if phrase not in text: errors.append(f"missing_stage1_prd_phrase:{phrase}") stage1_issues = ROOT / "sot/03-PROTOCOLS/CTO-CASE-STAGE1-GATED-ENGINE-ISSUES.md" if stage1_issues.is_file(): text = stage1_issues.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("stage1_issues_missing_not_promoted_frontmatter") if "Local planning SOT only. Not a Core Protocol. Not active Core authority." not in text: errors.append("stage1_issues_missing_local_planning_notice") for issue_id in REQUIRED_STAGE1_ISSUE_IDS: checked.append(f"stage1_issue_id:{issue_id}") if issue_id not in text: errors.append(f"missing_stage1_issue_id:{issue_id}") stage2_prd = ROOT / "sot/03-PROTOCOLS/CTO-CASE-STAGE2-ARTIFICIAL-FIXTURE-PRD.md" if stage2_prd.is_file(): text = stage2_prd.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("stage2_prd_missing_not_promoted_frontmatter") for phrase in REQUIRED_STAGE2_PRD_PHRASES: checked.append(f"stage2_prd_phrase:{phrase}") if phrase not in text: errors.append(f"missing_stage2_prd_phrase:{phrase}") stage2_issues = ROOT / "sot/03-PROTOCOLS/CTO-CASE-STAGE2-ARTIFICIAL-FIXTURE-ISSUES.md" if stage2_issues.is_file(): text = stage2_issues.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("stage2_issues_missing_not_promoted_frontmatter") if "Local planning SOT only. Not a Core Protocol. Not active Core authority." not in text: errors.append("stage2_issues_missing_local_planning_notice") for issue_id in REQUIRED_STAGE2_ISSUE_IDS: checked.append(f"stage2_issue_id:{issue_id}") if issue_id not in text: errors.append(f"missing_stage2_issue_id:{issue_id}") provider_admission_prd = ROOT / "sot/03-PROTOCOLS/CTO-CASE-PROVIDER-ADMISSION-PRD.md" if provider_admission_prd.is_file(): text = provider_admission_prd.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("provider_admission_prd_missing_not_promoted_frontmatter") for phrase in REQUIRED_PROVIDER_ADMISSION_PRD_PHRASES: checked.append(f"provider_admission_prd_phrase:{phrase}") if phrase not in text: errors.append(f"missing_provider_admission_prd_phrase:{phrase}") provider_admission_issues = ROOT / "sot/03-PROTOCOLS/CTO-CASE-PROVIDER-ADMISSION-ISSUES.md" if provider_admission_issues.is_file(): text = provider_admission_issues.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("provider_admission_issues_missing_not_promoted_frontmatter") if "Local planning SOT only. Not a Core Protocol. Not active Core authority." not in text: errors.append("provider_admission_issues_missing_local_planning_notice") for issue_id in REQUIRED_PROVIDER_ADMISSION_ISSUE_IDS: checked.append(f"provider_admission_issue_id:{issue_id}") if issue_id not in text: errors.append(f"missing_provider_admission_issue_id:{issue_id}") provider_build_prd = ROOT / "sot/03-PROTOCOLS/CTO-CASE-PROVIDER-BUILD-PRD.md" if provider_build_prd.is_file(): text = provider_build_prd.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("provider_build_prd_missing_not_promoted_frontmatter") for phrase in REQUIRED_PROVIDER_BUILD_PRD_PHRASES: checked.append(f"provider_build_prd_phrase:{phrase}") if phrase not in text: errors.append(f"missing_provider_build_prd_phrase:{phrase}") provider_build_issues = ROOT / "sot/03-PROTOCOLS/CTO-CASE-PROVIDER-BUILD-ISSUES.md" if provider_build_issues.is_file(): text = provider_build_issues.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("provider_build_issues_missing_not_promoted_frontmatter") if "Local planning SOT only. Not a Core Protocol. Not active Core authority." not in text: errors.append("provider_build_issues_missing_local_planning_notice") for issue_id in REQUIRED_PROVIDER_BUILD_ISSUE_IDS: checked.append(f"provider_build_issue_id:{issue_id}") if issue_id not in text: errors.append(f"missing_provider_build_issue_id:{issue_id}") model_provider_admission_prd = ROOT / "sot/03-PROTOCOLS/CTO-CASE-MODEL-PROVIDER-ADMISSION-PRD.md" if model_provider_admission_prd.is_file(): text = model_provider_admission_prd.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("model_provider_admission_prd_missing_not_promoted_frontmatter") for phrase in REQUIRED_MODEL_PROVIDER_ADMISSION_PRD_PHRASES: checked.append(f"model_provider_admission_prd_phrase:{phrase}") if phrase not in text: errors.append(f"missing_model_provider_admission_prd_phrase:{phrase}") model_provider_admission_issues = ROOT / "sot/03-PROTOCOLS/CTO-CASE-MODEL-PROVIDER-ADMISSION-ISSUES.md" if model_provider_admission_issues.is_file(): text = model_provider_admission_issues.read_text(encoding="utf-8") if "core_promotion_status: not-promoted" not in text: errors.append("model_provider_admission_issues_missing_not_promoted_frontmatter") if "Local planning SOT only. Not a Core Protocol. Not active Core authority." not in text: errors.append("model_provider_admission_issues_missing_local_planning_notice") for issue_id in REQUIRED_MODEL_PROVIDER_ADMISSION_ISSUE_IDS: checked.append(f"model_provider_admission_issue_id:{issue_id}") if issue_id not in text: errors.append(f"missing_model_provider_admission_issue_id:{issue_id}") for phrase in REQUIRED_MODEL_PROVIDER_ADMISSION_ISSUE_PHRASES: checked.append(f"model_provider_admission_issue_phrase:{phrase}") if phrase not in text: errors.append(f"missing_model_provider_admission_issue_phrase:{phrase}") 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}") for issue_id in REQUIRED_STAGE1_ISSUE_IDS: checked.append(f"workboard_id:{issue_id}") if issue_id not in text: errors.append(f"missing_workboard_id:{issue_id}") for issue_id in REQUIRED_STAGE2_ISSUE_IDS: checked.append(f"workboard_id:{issue_id}") if issue_id not in text: errors.append(f"missing_workboard_id:{issue_id}") for issue_id in REQUIRED_PROVIDER_ADMISSION_ISSUE_IDS: checked.append(f"workboard_id:{issue_id}") if issue_id not in text: errors.append(f"missing_workboard_id:{issue_id}") for issue_id in REQUIRED_PROVIDER_BUILD_ISSUE_IDS: checked.append(f"workboard_id:{issue_id}") if issue_id not in text: errors.append(f"missing_workboard_id:{issue_id}") for issue_id in REQUIRED_MODEL_PROVIDER_ADMISSION_ISSUE_IDS: checked.append(f"workboard_id:{issue_id}") if issue_id not in text: errors.append(f"missing_workboard_id:{issue_id}") expected_statuses = { "CTO-WORK-002": "validated", "CTO-WORK-003": "validated", "CTO-WORK-004": "validated", "CTO-WORK-005": "validated", "CTO-WORK-006": "validated", "CTO-WORK-007": "validated", "CTO-WORK-008": "validated", "CTO-WORK-009": "validated", "CTO-WORK-010": "validated", "CTO-WORK-011": "validated", "CTO-WORK-012": "blocked", "CTO-WORK-013": "validated", "CTO-WORK-014": "validated", "CTO-WORK-015": "validated", "CTO-WORK-016": "blocked", "CTO-WORK-017": "validated", "CTO-WORK-018": "validated", "CTO-WORK-019": "validated", "CTO-WORK-020": "blocked", } for issue_id, expected in expected_statuses.items(): checked.append(f"workboard_status:{issue_id}:{expected}") actual = workboard_status(text, issue_id) if actual != expected: errors.append(f"workboard_status_mismatch:{issue_id}:expected_{expected}:actual_{actual}") if "CTO-HARNESS-EVIDENCE-INTERFACE-CONTRACT.md" not in text: errors.append("workboard_missing_evidence_interface_contract_source") if "CTO-WORK-004" in text and "status: validated" not in text: errors.append("workboard_cto_work_004_not_validated") if "CTO-CASE-SOURCE-ADMISSION-RECORD.md" not in text: errors.append("workboard_missing_source_admission_record_source") if "CTO-CASE-ADAPTER-CONTRACT.md" not in text: errors.append("workboard_missing_adapter_contract_source") if "CTO-CASE-FAILURE-FIXTURE-MATRIX.md" not in text: errors.append("workboard_missing_failure_matrix_source") if "CTO-CASE-STAGED-PROOF-GATES.md" not in text: errors.append("workboard_missing_staged_proof_gates_source") if "CTO-CASE-STAGE1-GATED-ENGINE-PRD.md" not in text: errors.append("workboard_missing_stage1_prd_source") if "CTO-CASE-STAGE1-GATED-ENGINE-ISSUES.md" not in text: errors.append("workboard_missing_stage1_issues_source") if "CTO-CASE-STAGE2-ARTIFICIAL-FIXTURE-PRD.md" not in text: errors.append("workboard_missing_stage2_prd_source") if "CTO-CASE-STAGE2-ARTIFICIAL-FIXTURE-ISSUES.md" not in text: errors.append("workboard_missing_stage2_issues_source") if "CTO-CASE-PROVIDER-ADMISSION-PRD.md" not in text: errors.append("workboard_missing_provider_admission_prd_source") if "CTO-CASE-PROVIDER-ADMISSION-ISSUES.md" not in text: errors.append("workboard_missing_provider_admission_issues_source") if "CTO-CASE-PROVIDER-BUILD-PRD.md" not in text: errors.append("workboard_missing_provider_build_prd_source") if "CTO-CASE-PROVIDER-BUILD-ISSUES.md" not in text: errors.append("workboard_missing_provider_build_issues_source") if "CTO-CASE-MODEL-PROVIDER-ADMISSION-PRD.md" not in text: errors.append("workboard_missing_model_provider_admission_prd_source") if "CTO-CASE-MODEL-PROVIDER-ADMISSION-ISSUES.md" not in text: errors.append("workboard_missing_model_provider_admission_issues_source") 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())