diff --git a/WORKBOARD.yaml b/WORKBOARD.yaml index 5c31e95..3c25484 100644 --- a/WORKBOARD.yaml +++ b/WORKBOARD.yaml @@ -486,3 +486,8 @@ items: status: validated source: docs/LEGACY-INGEST.md owner: "" + - id: CTO-WORK-098 + title: Stage 5 Target Sandbox Stale State Gate + status: validated + source: docs/STAGE5-TARGET-SANDBOX-STALE-CASE-STATE.md + owner: "" diff --git a/tools/validate_cto_child.py b/tools/validate_cto_child.py index 1c6a94e..ddd8973 100644 --- a/tools/validate_cto_child.py +++ b/tools/validate_cto_child.py @@ -16,6 +16,7 @@ REQUIRED_FILES = [ "WORKBOARD.yaml", "CONTEXT.md", "docs/LEGACY-INGEST.md", + "docs/STAGE5-TARGET-SANDBOX-STALE-CASE-STATE.md", ".sot/00-START/CTO-WORKSPACE-INTENT.md", ".sot/03-PROTOCOLS/CTO-CASE-BACKEND-BRIEF.md", ".sot/03-PROTOCOLS/CTO-ARCHITECTURE-BRIEF-CLOSEOUT-PRD.md", @@ -904,6 +905,21 @@ REQUIRED_STAGE5_TARGET_FORBIDDEN_ACTIONS = [ "cortex_core_mutation", ] +REQUIRED_STAGE5_TARGET_STALE_STATE_PHRASES = [ + "Local planning evidence only. Not Core authority. Not Runtime authority.", + "Status: stale-state-declared.", + "/home/svrnty/workspaces/cortex-os/cto-stage5-target-sandbox", + "The target sandbox is still not move-ready", + "`python3 -m pytest -q` in the target sandbox: `11 passed`.", + "`python3 tools/validate_cto_stage5_target_sandbox_child.py` in the target", + "`python3 tools/validate_cto_child.py` in CTO: `ok: true`.", + "No live process matched the target path", + "not archive-stable completion evidence", + "Do not edit the target sandbox only to make cleanup look cleaner.", + "Movement remains deferred until CTO replaces the absolute live path dependency", + "No hard delete, Core mutation, Harness rerun, Case rerun, provider", +] + REQUIRED_PROVIDER_ADMISSION_PRD_PHRASES = [ "Local planning SOT only. Not a Core Protocol. Not active Core authority.", "https://github.com/workos/case.git", @@ -2665,6 +2681,14 @@ def main() -> int: if key.lower() in {"api_key", "apikey", "access_token", "token", "secret", "password", "credential_value"}: errors.append(f"stage5_target_admission_forbidden_secret_key:{key}") + stage5_target_stale_state = ROOT / "docs/STAGE5-TARGET-SANDBOX-STALE-CASE-STATE.md" + if stage5_target_stale_state.is_file(): + text = stage5_target_stale_state.read_text(encoding="utf-8") + for phrase in REQUIRED_STAGE5_TARGET_STALE_STATE_PHRASES: + checked.append(f"stage5_target_stale_state_phrase:{phrase}") + if phrase not in text: + errors.append(f"missing_stage5_target_stale_state_phrase:{phrase}") + stage6_prd = ROOT / ".sot/03-PROTOCOLS/CTO-CASE-STAGE6-CANDIDATE-DEFAULT-PRD.md" if stage6_prd.is_file(): text = stage6_prd.read_text(encoding="utf-8") @@ -2999,6 +3023,10 @@ def main() -> int: 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 ["CTO-WORK-098"]: + 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-001": "validated", "CTO-WORK-002": "validated", @@ -3094,6 +3122,7 @@ def main() -> int: "CTO-WORK-095": "validated", "CTO-WORK-096": "validated", "CTO-WORK-097": "validated", + "CTO-WORK-098": "validated", } for issue_id, expected in expected_statuses.items(): checked.append(f"workboard_status:{issue_id}:{expected}") @@ -3196,6 +3225,8 @@ def main() -> int: errors.append("workboard_missing_codex_retention_archive_source") if "docs/LEGACY-INGEST.md" not in text: errors.append("workboard_missing_legacy_ingest_source") + if "docs/STAGE5-TARGET-SANDBOX-STALE-CASE-STATE.md" not in text: + errors.append("workboard_missing_stage5_target_stale_state_source") payload = { "ok": not errors,