CC: Cover Codex retention in CTO validator

This commit is contained in:
Svrnty
2026-06-04 14:59:18 -04:00
parent 68f071e1e6
commit a32a996e69
+160
View File
@@ -111,6 +111,14 @@ REQUIRED_FILES = [
".sot/03-PROTOCOLS/CTO-CASE-SPARK-ENDPOINT-CONFIG-PRD.md",
".sot/03-PROTOCOLS/CTO-CASE-SPARK-ENDPOINT-CONFIG-ISSUES.md",
".sot/03-PROTOCOLS/CTO-CASE-AGENT-PROTOCOL-BLOCKER.md",
".sot/03-PROTOCOLS/CTO-CODEX-RETENTION-DRY-RUN-PACKET.md",
".sot/03-PROTOCOLS/CTO-CODEX-RETENTION-POLICY-PACKET.md",
".sot/03-PROTOCOLS/CTO-CODEX-RETENTION-ARCHIVE-EXECUTOR-PACKET.md",
"tools/report_codex_retention_pressure.py",
"tools/plan_codex_retention_policy.py",
"tools/archive_codex_inactive_threads.py",
"tools/probe_codex_native_retention.py",
"tools/codex_ephemeral_exec.py",
]
REQUIRED_BRIEF_PHRASES = [
@@ -1656,6 +1664,105 @@ REQUIRED_CORE_ROUTE_ADMISSION_GUARD_CLOSEOUT_PHRASES = [
"candidate-only until the guard passes",
]
REQUIRED_CODEX_RETENTION_ISSUE_IDS = [
"CTO-WORK-093",
"CTO-WORK-094",
"CTO-WORK-095",
"CTO-WORK-096",
]
REQUIRED_CODEX_RETENTION_DRY_RUN_PHRASES = [
"Local planning SOT only. Not a Core Protocol. Not active Core authority.",
"Codex retention pressure is measurable without reading raw transcripts or mutating `~/.codex`.",
"metadata-only JSON",
"It does not read transcript bodies, update SQLite, delete files, vacuum databases, archive threads, mutate Core, start Runtime, read secrets, change Codex config, or claim product readiness.",
"Prevention default: use `codex exec --ephemeral` for disposable non-interactive worker runs.",
"Blocked without explicit operator approval:",
"direct `threads.archived` updates",
"session JSONL deletion",
"logs table deletion",
"SQLite vacuum or checkpoint",
"raw transcript import into Core",
"broad cleanup of `~/.codex`",
]
REQUIRED_CODEX_RETENTION_POLICY_PHRASES = [
"Local planning SOT only. Not a Core Protocol. Not active Core authority.",
"Codex retention cleanup now has a backup-first, approval-gated policy plan.",
"`python3 tools/plan_codex_retention_policy.py` emits metadata-only JSON.",
"`python3 tools/probe_codex_native_retention.py` checks installed Codex CLI help, feature flags, and local version cache.",
"native cleanup/archive/retention command advertised by installed CLI: false",
"prevention flag advertised: `codex exec --ephemeral`",
"Phase 1: backup `state_5.sqlite`, `logs_2.sqlite`, WAL, and SHM files.",
"Phase 2: archive-only candidate threads by DB flag only after explicit approval.",
"Phase 3: delete archived session JSONL only after separate destructive approval.",
"`python3 tools/codex_ephemeral_exec.py` builds disposable worker commands as `codex exec --ephemeral`.",
"The helper is prevention only. It does not archive threads, delete JSONL, truncate logs, checkpoint, vacuum, read transcript bodies, or mutate Core.",
"Blocked without explicit operator approval:",
"updating `threads.archived`",
"deleting session JSONL",
"SQLite checkpoint or vacuum",
"raw transcript read/import",
]
REQUIRED_CODEX_RETENTION_ARCHIVE_PHRASES = [
"Local planning SOT only. Not a Core Protocol. Not active Core authority.",
"Codex retention cleanup now has a guarded archive-only executor.",
"Default mode is dry-run.",
"Mutation requires an exact approval token.",
"It does not delete session JSONL, truncate logs, checkpoint, vacuum, read transcript bodies, or import transcripts into Core.",
"python3 tools/archive_codex_inactive_threads.py --check",
"python3 tools/probe_codex_native_retention.py --check",
"I approve CTO-WORK-095 archive-only Codex threads older than 7 days.",
"candidate selection reads only `id`, `rollout_path`, `updated_at`, `archived`, and file size",
"mutation is limited to `threads.archived=1` and `archived_at`",
"Still blocked without separate approval:",
"delete archived session JSONL",
"run SQLite checkpoint or vacuum",
"read raw transcript bodies",
]
REQUIRED_CODEX_RETENTION_TOOL_PHRASES = {
"tools/report_codex_retention_pressure.py": [
"metadata_only",
"raw_transcript_bodies_read",
"mutation_performed",
"blocked_without_approval",
],
"tools/plan_codex_retention_policy.py": [
"metadata_only",
"raw_transcript_bodies_read",
"raw_thread_text_fields_read",
"mutation_performed",
"approval_boundaries",
"false_effects",
],
"tools/archive_codex_inactive_threads.py": [
"approval_token_invalid",
"backup_codex_state",
"raw_transcript_bodies_read",
"session_jsonl_deleted",
"sqlite_checkpoint_or_vacuum",
"false_effects",
],
"tools/probe_codex_native_retention.py": [
"metadata_only",
"mutation_performed",
"native_retention_cleanup_command_advertised",
"ephemeral_prevention_advertised",
"false_effects",
],
"tools/codex_ephemeral_exec.py": [
"codex",
"exec",
"--ephemeral",
"--print-command",
"dangerously-bypass-approvals-and-sandbox",
"session_persistence_requested",
"false_effects",
],
}
def workboard_status(text: str, issue_id: str) -> str | None:
pattern = rf"- id: {re.escape(issue_id)}\n(?: .+\n)*? status: ([^\n]+)"
@@ -2758,6 +2865,45 @@ def main() -> int:
if phrase not in text:
errors.append(f"missing_provider_decision_record_phrase:{phrase}")
codex_retention_dry_run = ROOT / ".sot/03-PROTOCOLS/CTO-CODEX-RETENTION-DRY-RUN-PACKET.md"
if codex_retention_dry_run.is_file():
text = codex_retention_dry_run.read_text(encoding="utf-8")
for phrase in REQUIRED_CODEX_RETENTION_DRY_RUN_PHRASES:
checked.append(f"codex_retention_dry_run_phrase:{phrase}")
if phrase not in text:
errors.append(f"missing_codex_retention_dry_run_phrase:{phrase}")
if "source: CTO-WORK-093" not in text:
errors.append("codex_retention_dry_run_missing_source")
codex_retention_policy = ROOT / ".sot/03-PROTOCOLS/CTO-CODEX-RETENTION-POLICY-PACKET.md"
if codex_retention_policy.is_file():
text = codex_retention_policy.read_text(encoding="utf-8")
for phrase in REQUIRED_CODEX_RETENTION_POLICY_PHRASES:
checked.append(f"codex_retention_policy_phrase:{phrase}")
if phrase not in text:
errors.append(f"missing_codex_retention_policy_phrase:{phrase}")
if "source: CTO-WORK-094" not in text:
errors.append("codex_retention_policy_missing_source")
codex_retention_archive = ROOT / ".sot/03-PROTOCOLS/CTO-CODEX-RETENTION-ARCHIVE-EXECUTOR-PACKET.md"
if codex_retention_archive.is_file():
text = codex_retention_archive.read_text(encoding="utf-8")
for phrase in REQUIRED_CODEX_RETENTION_ARCHIVE_PHRASES:
checked.append(f"codex_retention_archive_phrase:{phrase}")
if phrase not in text:
errors.append(f"missing_codex_retention_archive_phrase:{phrase}")
if "source: CTO-WORK-095" not in text:
errors.append("codex_retention_archive_missing_source")
for rel, phrases in REQUIRED_CODEX_RETENTION_TOOL_PHRASES.items():
path = ROOT / rel
if path.is_file():
text = path.read_text(encoding="utf-8")
for phrase in phrases:
checked.append(f"codex_retention_tool_phrase:{rel}:{phrase}")
if phrase not in text:
errors.append(f"missing_codex_retention_tool_phrase:{rel}:{phrase}")
board = ROOT / "WORKBOARD.yaml"
if board.is_file():
text = board.read_text(encoding="utf-8")
@@ -2825,6 +2971,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 REQUIRED_CODEX_RETENTION_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-001": "validated",
"CTO-WORK-002": "validated",
@@ -2915,6 +3065,10 @@ def main() -> int:
"CTO-WORK-090": "validated",
"CTO-WORK-091": "validated",
"CTO-WORK-092": "validated",
"CTO-WORK-093": "validated",
"CTO-WORK-094": "validated",
"CTO-WORK-095": "validated",
"CTO-WORK-096": "validated",
}
for issue_id, expected in expected_statuses.items():
checked.append(f"workboard_status:{issue_id}:{expected}")
@@ -3009,6 +3163,12 @@ def main() -> int:
errors.append("workboard_missing_core_route_admission_guard_source")
if "CTO-CORE-ROUTE-ADMISSION-GUARD-CLOSEOUT.md" not in text:
errors.append("workboard_missing_core_route_admission_guard_closeout_source")
if "CTO-CODEX-RETENTION-DRY-RUN-PACKET.md" not in text:
errors.append("workboard_missing_codex_retention_dry_run_source")
if "CTO-CODEX-RETENTION-POLICY-PACKET.md" not in text:
errors.append("workboard_missing_codex_retention_policy_source")
if "CTO-CODEX-RETENTION-ARCHIVE-EXECUTOR-PACKET.md" not in text:
errors.append("workboard_missing_codex_retention_archive_source")
payload = {
"ok": not errors,