Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/plananvil-codex-qualification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ jobs:
set -euo pipefail
cd "${QUALIFICATION_REPO}"
set +e
python3 tools/live_codex_qualification_harness_v2.py \
python3 tools/live_codex_qualification_harness_v3.py \
--root "${QUALIFICATION_REPO}" \
--source-commit "${GITHUB_SHA}" \
--run-id "${GITHUB_RUN_ID}" \
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ All notable changes to PlanAnvil are documented here.
- require the full Linux Codex qualification job to pass a system-`bubblewrap` user-namespace probe before C01-C16, so incompatible Podman runners fail fast instead of timing out capability-by-capability;
- update the qualification evidence uploader to the Node-24-native `actions/upload-artifact` v6 immutable SHA;
- use deterministic live-qualification harness setup for C01, C05, C09, and C14 so repository-skill discovery, stale handoff rejection, genuine auto-compaction, and writable auxiliary Git isolation are actually exercised;
- extend deterministic live qualification for C02, C09, C11, C13, C14, and C16 with explicit-only skill policy, current AGENTS precedence evidence, project-scoped SubagentStart semantics, bytecode-free PlanAnvil bootstrap, and real Git signing/hook failure diagnostics; C12 remains isolated for independent runtime verification;
- extend deterministic live qualification for C02, C09, C11, C13, C14, and C16 with explicit-only skill policy, current AGENTS precedence evidence, project-scoped SubagentStart semantics, bytecode-free PlanAnvil bootstrap, and real Git signing/hook failure diagnostics;
- make C12 a deterministic runtime byte-budget probe with redundant `project_doc_max_bytes` enforcement, secret head/tail markers, zero-tool automatic-loading evidence, and outer PlanAnvil full-file hash verification;
- require production releases to use a GitHub-verified signed annotated tag whose target is reachable from `main`;
- fail the production release gate closed when the release worktree is dirty or Git cleanliness cannot be verified;
- document the controlled self-hosted Codex qualification path and keep the previous sandbox procedure as a manual fallback.
Expand Down
55 changes: 55 additions & 0 deletions tests/test_live_codex_qualification_harness_v3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from __future__ import annotations

import importlib.util
from pathlib import Path
import sys
import unittest


ROOT = Path(__file__).resolve().parents[1]
MODULE_PATH = ROOT / "tools" / "live_codex_qualification_harness_v3.py"
sys.path.insert(0, str(ROOT / "tools"))
SPEC = importlib.util.spec_from_file_location("live_codex_qualification_harness_v3", MODULE_PATH)
assert SPEC and SPEC.loader
harness = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(harness)


class LiveCodexHarnessV3Tests(unittest.TestCase):
def test_only_c12_is_overridden(self) -> None:
self.assertEqual(harness.TARGET_CAPABILITIES, {"C12"})
self.assertIs(harness._ORIGINAL_CAPABILITY_RUNTIME, harness.prior.capability_runtime)

def test_fixture_places_secret_tail_beyond_budget(self) -> None:
data = harness._build_agents_fixture("HEADSECRET", "TAILSECRET")
self.assertEqual(len(data), harness.C12_FIXTURE_BYTES)
self.assertLess(data.index(b"HEADSECRET"), harness.C12_LIMIT_BYTES)
self.assertGreater(data.index(b"TAILSECRET"), harness.C12_LIMIT_BYTES)
self.assertEqual(data.index(b"C12_TAIL_TOKEN"), harness.C12_TAIL_OFFSET + 1)
self.assertNotIn(b"TAILSECRET", data[: harness.C12_LIMIT_BYTES])

def test_runtime_limit_is_redundantly_enforced(self) -> None:
source = MODULE_PATH.read_text(encoding="utf-8")
self.assertIn('project_doc_max_bytes = {C12_LIMIT_BYTES}', source)
self.assertIn('project_doc_max_bytes={C12_LIMIT_BYTES}', source)
self.assertIn('"--sandbox"', (ROOT / "tools" / "live_codex_qualification.py").read_text(encoding="utf-8"))
self.assertNotIn("--dangerously-bypass-approvals-and-sandbox", source)

def test_automatic_probe_rejects_tool_contamination(self) -> None:
source = MODULE_PATH.read_text(encoding="utf-8")
self.assertIn("Do not run shell commands, do not use tools", source)
self.assertIn('events.get("completed_command_items")', source)
self.assertIn("tail_observed", source)
self.assertIn("head_observed", source)

def test_plananvil_full_read_is_outer_deterministic_evidence(self) -> None:
source = MODULE_PATH.read_text(encoding="utf-8")
self.assertIn("from map_instructions import map_instructions", source)
self.assertIn("automatic_byte_limit", source)
self.assertIn("full_read", source)
self.assertIn("truncation_risk", source)
self.assertIn("outer deterministic PlanAnvil map_instructions", source)


if __name__ == "__main__":
unittest.main()
Loading