Files
codex-app-server/cloud/tests/test_audit_loop_driver.py
T
host 91b61e5302 audit: pure loop driver for the project-level audit rework loop (track 1)
LoopState + decide_audit + after_fix + apply_correction +
confirm_rebuild + emit, all pure: the driver returns Action
objects, the caller executes (subtask creation, merge/release
blocking, ledger persistence). Counts per (module, contract
version); pushbacks 1-3, 4th failure (> 3) rebuilds, 1 rebuild
and 1 targeted round per version, third-failure triage
(structural -> early rebuild, contract/design -> refreeze,
near-complete -> targeted), rebuild-only-implementation with
the old-test first gate, acceptance rebuilds pending user
confirmation, quota blocks consume nothing, cost threshold
escalates rebuilds to the user with full history, corrections
to pass do not unblock, ledger events match the #241 table
with CONDITIONAL mapped to fail at the boundary.

No server wiring, no release endpoint, no task creation, no
DB access in this track; wiring is the next track's job
against this interface.
2026-11-03 02:49:25 +00:00

579 lines
26 KiB
Python

"""Audit loop driver: pure decision logic for the project-level
audit rework loop (track 1 of the production built-in loop).
Pins the driver interface the next (wiring) track must use
verbatim, and the counting rules shared by #244 / #241 / #247:
pushbacks cover failures 1-3, the 4th failure (> 3, never >= 3)
rebuilds, rebuilds are 1 per module per contract version, the
3rd failure is triaged, a pass clears failures but not budgets,
a contract-version change clears everything, quota blocks consume
nothing, a correction to pass does not unblock, and ledger events
match the #241 field table.
Style follows the other cloud tests: unittest, stdlib only, import
with a sys.path fallback so the file also runs standalone.
"""
import sys
import unittest
from pathlib import Path
try:
from cloud.audit_loop_driver import (
ACTION_KINDS,
LEDGER_EVENT_TYPES,
Action,
LoopState,
apply_correction,
build_ledger_event,
confirm_rebuild,
decide_audit,
emit,
new_loop_state,
validate_ledger_event,
after_fix,
)
except ImportError:
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from cloud.audit_loop_driver import (
ACTION_KINDS,
LEDGER_EVENT_TYPES,
Action,
LoopState,
apply_correction,
build_ledger_event,
confirm_rebuild,
decide_audit,
emit,
new_loop_state,
validate_ledger_event,
after_fix,
)
def fail_verdict(**overrides):
verdict = {
"source": "audit",
"verdict": "fail",
"verdict_text": "audit fail: ci red",
"failures": [{"check": "ci_green", "detail": "ci red"}],
"passed_items": [],
"previous_artifacts": [{"path": "app.py", "sha256": "a" * 64}],
"reproduction_steps": ["run ci"],
}
verdict.update(overrides)
return verdict
def pass_verdict(**overrides):
verdict = {
"source": "audit",
"verdict": "pass",
"verdict_text": "audit pass",
"failures": [],
"passed_items": [],
}
verdict.update(overrides)
return verdict
def near_complete_verdict(**overrides):
verdict = fail_verdict(
failures=[{"check": "edge-case"}],
passed_items=[{"check": "p1"}, {"check": "p2"}, {"check": "p3"}],
broken_passed_items=[],
verdict_text="audit fail: one edge case left",
)
verdict.update(overrides)
return verdict
def drive(state, verdict, **kwargs):
return decide_audit(state, verdict, **kwargs)
class PushbackCountingTestCase(unittest.TestCase):
def test_fail_pushbacks_and_blocks(self):
state = new_loop_state("m1", "v1")
action = drive(state, fail_verdict())
self.assertEqual(action.kind, "pushback")
self.assertTrue(action.merge_blocked)
self.assertTrue(action.release_blocked)
self.assertEqual(action.next_state.failure_count, 1)
self.assertEqual(action.next_state.pushback_round, 1)
self.assertEqual(action.next_state.status, "pushback")
package = action.pushback_package
self.assertEqual(package["audit_verdict_text"], "audit fail: ci red")
self.assertEqual(package["failures"][0]["check"], "ci_green")
self.assertEqual(package["reproduction_steps"], ["run ci"])
# Full re-audit criteria, never just the failed items.
self.assertIn("ci_green", package["reaudit_criteria"])
self.assertIn("audit_signer_separation", package["reaudit_criteria"])
self.assertEqual(package["previous_artifacts"][0]["path"], "app.py")
self.assertEqual(
[e["event_type"] for e in action.ledger_events],
["audit_verdict", "audit_pushback_round"])
def test_exactly_three_still_pushes_back(self):
state = new_loop_state("m1", "v1")
for _ in range(3):
action = drive(state, fail_verdict())
state = action.next_state
self.assertEqual(action.kind, "pushback")
self.assertEqual(action.next_state.failure_count, 3)
self.assertEqual(action.next_state.pushback_round, 3)
def test_fourth_failure_rebuilds(self):
state = new_loop_state("m1", "v1")
for _ in range(3):
state = drive(state, fail_verdict()).next_state
action = drive(state, fail_verdict(retained_tests=["t1", "t2"]))
self.assertEqual(action.kind, "rebuild")
self.assertEqual(action.rebuild_no, 1)
self.assertEqual(action.next_state.rebuild_no, 1)
self.assertEqual(action.next_state.failure_count, 0)
self.assertEqual(action.next_state.pushback_round, 0)
self.assertEqual(action.next_state.status, "rebuilding")
self.assertEqual(action.next_state.retained_tests, ["t1", "t2"])
retained = action.pushback_package["retained_assets"]
self.assertEqual(retained["tests"], ["t1", "t2"])
self.assertTrue(retained["contract_unchanged"])
self.assertTrue(retained["design_unchanged"])
def test_rebuild_budget_exhausted_escalates(self):
state = new_loop_state("m1", "v1")
for _ in range(3):
state = drive(state, fail_verdict()).next_state
state = drive(state, fail_verdict()).next_state # rebuild 1
self.assertEqual(state.rebuild_no, 1)
for _ in range(3):
state = drive(state, fail_verdict()).next_state
action = drive(state, fail_verdict())
self.assertEqual(action.kind, "escalate")
self.assertEqual(action.escalation_target, "coordinator")
self.assertEqual(action.next_state.rebuild_no, 1)
self.assertEqual(action.next_state.status, "escalated")
self.assertFalse(action.escalation_grants_user_acceptance)
self.assertFalse(hasattr(action, "tick_acceptance"))
# Full history, not just the last round.
texts = [entry.get("verdict_text")
for entry in action.escalation_history]
self.assertIn("audit fail: ci red", texts)
self.assertGreaterEqual(len(action.escalation_history), 4)
def test_audit_and_acceptance_counts_combine_but_stay_separate(self):
state = new_loop_state("m1", "v1")
state = drive(state, fail_verdict()).next_state
state = drive(state, fail_verdict(source="acceptance")).next_state
action = drive(state, fail_verdict())
self.assertEqual(action.next_state.failure_count, 3)
self.assertEqual(action.next_state.audit_fail_count, 2)
self.assertEqual(action.next_state.acceptance_fail_count, 1)
def test_pass_clears_failures_keeps_rebuild_budget(self):
state = new_loop_state("m1", "v1")
for _ in range(3):
state = drive(state, fail_verdict()).next_state
state = drive(state, fail_verdict()).next_state # rebuild 1
state = drive(state, fail_verdict()).next_state # failure 1, new episode
action = drive(state, pass_verdict())
self.assertEqual(action.kind, "pass")
self.assertFalse(action.merge_blocked)
self.assertEqual(action.next_state.failure_count, 0)
self.assertEqual(action.next_state.pushback_round, 0)
self.assertEqual(action.next_state.rebuild_no, 1)
self.assertEqual(action.next_state.status, "passed")
def test_contract_version_change_clears_everything(self):
state = new_loop_state("m1", "v1")
for _ in range(3):
state = drive(state, fail_verdict()).next_state
state = drive(state, fail_verdict()).next_state # rebuild 1
state = drive(state, fail_verdict()).next_state # failure 1, v1
action = drive(state, fail_verdict(contract_version="v2"))
self.assertEqual(action.kind, "pushback")
self.assertEqual(action.next_state.contract_version, "v2")
self.assertEqual(action.next_state.failure_count, 1)
self.assertEqual(action.next_state.pushback_round, 1)
self.assertEqual(action.next_state.rebuild_no, 0)
self.assertFalse(action.next_state.targeted_used)
class ThirdFailureTriageTestCase(unittest.TestCase):
def _two_failures(self):
state = new_loop_state("m1", "v1")
for _ in range(2):
state = drive(state, fail_verdict()).next_state
return state
def test_structural_third_failure_early_rebuilds(self):
state = self._two_failures()
action = drive(state, fail_verdict(
failures=[{"check": "arch",
"structural_kind": "architecture_boundary"}],
verdict_text="audit fail: architecture boundary crossed"))
self.assertEqual(action.kind, "rebuild")
self.assertEqual(action.rebuild_no, 1)
assessments = [e for e in action.ledger_events
if e["event_type"] == "audit_third_failure_assessment"]
self.assertEqual(len(assessments), 1)
self.assertEqual(assessments[0]["payload"]["determination"], "early_rebuild")
self.assertEqual(assessments[0]["payload"]["failure_total"], 3)
self.assertEqual(assessments[0]["payload"]["max_extra_rounds"], 0)
def test_contract_root_third_failure_goes_to_refreeze(self):
state = self._two_failures()
action = drive(state, fail_verdict(root_cause_hint="contract"))
self.assertEqual(action.kind, "refreeze")
self.assertEqual(action.next_state.status, "refreeze")
self.assertEqual(action.next_state.rebuild_no, 0)
self.assertEqual(action.compat_hint, "audit_wiring_event:escalate_refreeze")
# No rebuild event, and no assessment row forced into the
# wrong determination (see #247: early_refreeze has no
# assessment determination).
kinds = [e["event_type"] for e in action.ledger_events]
self.assertNotIn("audit_rebuild", kinds)
self.assertNotIn("audit_third_failure_assessment", kinds)
def test_near_complete_gets_one_targeted_round_then_rebuilds(self):
state = self._two_failures()
action = drive(state, near_complete_verdict())
self.assertEqual(action.kind, "pushback")
self.assertEqual(action.scope, "targeted")
self.assertEqual(action.targeted_items, ["edge-case"])
self.assertTrue(action.next_state.targeted_used)
self.assertEqual(action.next_state.pushback_round, 3)
assessments = [e for e in action.ledger_events
if e["event_type"] == "audit_third_failure_assessment"]
self.assertEqual(assessments[0]["payload"]["determination"],
"continuation_round")
self.assertEqual(assessments[0]["payload"]["max_extra_rounds"], 1)
# One more failure is the 4th: rebuild, not a 4th pushback.
action = drive(action.next_state, fail_verdict())
self.assertEqual(action.kind, "rebuild")
def test_targeted_budget_not_replenished_by_pass(self):
state = self._two_failures()
state = drive(state, near_complete_verdict()).next_state
self.assertTrue(state.targeted_used)
state = drive(state, pass_verdict()).next_state
self.assertTrue(state.targeted_used)
for _ in range(2):
state = drive(state, fail_verdict()).next_state
action = drive(state, near_complete_verdict())
self.assertEqual(action.kind, "pushback")
self.assertEqual(action.scope, "normal")
self.assertIsNone(action.targeted_items)
def test_unknown_broken_passed_disqualifies_near_complete(self):
state = self._two_failures()
# broken_passed_items absent (unknown, not []) -> normal.
action = drive(state, fail_verdict(
failures=[{"check": "edge-case"}],
passed_items=[{"check": "p1"}, {"check": "p2"}, {"check": "p3"}]))
self.assertEqual(action.scope, "normal")
class RebuildGateAndAcceptanceTestCase(unittest.TestCase):
def _rebuilding_state(self):
state = new_loop_state("m1", "v1")
for _ in range(3):
state = drive(state, fail_verdict()).next_state
return drive(state, fail_verdict(retained_tests=["t1", "t2"])).next_state
def test_old_test_gate_missing_result_counts_as_failure(self):
state = self._rebuilding_state()
action = after_fix(state, {"reaudit_verdict": pass_verdict()})
self.assertEqual(action.kind, "pushback")
self.assertEqual(action.next_state.failure_count, 1)
self.assertEqual(action.next_state.status, "pushback")
checks = [item.get("check") for item in action.pushback_package["failures"]]
self.assertIn("rebuild_first_gate", checks)
def test_old_test_gate_failing_result_counts_as_failure(self):
state = self._rebuilding_state()
action = after_fix(state, {
"reaudit_verdict": pass_verdict(),
"retained_test_results": {"t1": True, "t2": False},
})
self.assertEqual(action.kind, "pushback")
self.assertEqual(action.next_state.failure_count, 1)
def test_old_test_gate_passing_allows_reaudit_pass(self):
state = self._rebuilding_state()
action = after_fix(state, {
"reaudit_verdict": pass_verdict(),
"retained_test_results": {"t1": True, "t2": True},
})
self.assertEqual(action.kind, "pass")
self.assertEqual(action.next_state.status, "passed")
def _acceptance_pending(self):
state = new_loop_state("m1", "v1")
for _ in range(3):
state = drive(state, fail_verdict(source="acceptance")).next_state
return drive(state, fail_verdict(source="acceptance"))
def test_acceptance_rebuild_unconfirmed_does_not_start(self):
action = self._acceptance_pending()
self.assertEqual(action.kind, "pending_user_confirmation")
self.assertTrue(action.next_state.pending_confirmation)
self.assertEqual(action.next_state.rebuild_no, 0)
self.assertEqual(action.next_state.status, "pending_user_confirmation")
notices = [e for e in action.ledger_events
if e["event_type"] == "audit_rebuild_notice"]
self.assertEqual(notices[0]["payload"]["confirmation_status"], "pending")
def test_acceptance_rebuild_confirmed_then_starts(self):
pending = self._acceptance_pending().next_state
action = confirm_rebuild(pending, user_confirmed=True)
self.assertEqual(action.kind, "rebuild")
self.assertEqual(action.rebuild_no, 1)
self.assertEqual(action.next_state.status, "rebuilding")
self.assertFalse(action.next_state.pending_confirmation)
notices = [e for e in action.ledger_events
if e["event_type"] == "audit_rebuild_notice"]
self.assertEqual(notices[0]["payload"]["confirmation_status"], "confirmed")
self.assertEqual(notices[0]["payload"]["confirmed_by"], "user")
def test_acceptance_rebuild_rejected_escalates_without_budget(self):
pending = self._acceptance_pending().next_state
action = confirm_rebuild(pending, user_confirmed=False)
self.assertEqual(action.kind, "escalate")
self.assertEqual(action.escalation_target, "coordinator")
self.assertEqual(action.next_state.rebuild_no, 0)
def test_confirm_without_pending_raises(self):
state = new_loop_state("m1", "v1")
with self.assertRaises(ValueError):
confirm_rebuild(state, user_confirmed=True)
class AfterFixTestCase(unittest.TestCase):
def test_after_fix_pass_clears(self):
state = new_loop_state("m1", "v1")
state = drive(state, fail_verdict()).next_state
action = after_fix(state, {"reaudit_verdict": pass_verdict()})
self.assertEqual(action.kind, "pass")
self.assertEqual(action.next_state.failure_count, 0)
def test_after_fix_failing_reaudit_goes_to_next_pushback(self):
state = new_loop_state("m1", "v1")
state = drive(state, fail_verdict()).next_state
action = after_fix(state, {"reaudit_verdict": fail_verdict()})
self.assertEqual(action.kind, "pushback")
self.assertEqual(action.next_state.failure_count, 2)
self.assertEqual(action.next_state.pushback_round, 2)
def test_after_fix_diff_only_raises(self):
state = new_loop_state("m1", "v1")
state = drive(state, fail_verdict()).next_state
with self.assertRaises(ValueError):
after_fix(state, {"diff": "--- a/x\n+++ b/x\n"})
with self.assertRaises(ValueError):
after_fix(state, {"diff_only": True,
"reaudit_verdict": pass_verdict()})
class CorrectionTestCase(unittest.TestCase):
def test_correction_to_pass_does_not_unblock(self):
state = new_loop_state("m1", "v1")
for _ in range(2):
state = drive(state, fail_verdict()).next_state
action = apply_correction(state, {
"original_verdict": "fail", "corrected_verdict": "pass",
"reviewer": "auditor-b", "original_entry_id": "e1",
"reason": "second look: evidence was sufficient",
})
# Counting view: effective count is 0 ...
self.assertEqual(action.next_state.failure_count, 0)
# ... but the gate view is still blocked until a fresh pass.
self.assertTrue(action.merge_blocked)
self.assertTrue(action.release_blocked)
self.assertEqual(action.next_state.status, "blocked")
self.assertTrue(action.requires_reaudit)
self.assertEqual(action.correction_action, "schedule_reaudit")
self.assertEqual(action.ledger_events[0]["event_type"], "audit_meta_review")
# Only a fresh passing verdict unblocks.
action = drive(action.next_state, pass_verdict())
self.assertEqual(action.kind, "pass")
self.assertFalse(action.merge_blocked)
def test_correction_to_fail_reblocks_at_once(self):
state = drive(new_loop_state("m1", "v1"), pass_verdict()).next_state
action = apply_correction(state, {
"original_verdict": "pass", "corrected_verdict": "fail",
"reviewer": "auditor-b", "original_entry_id": "e1",
"reason": "missed a failing check",
})
self.assertEqual(action.kind, "pushback")
self.assertTrue(action.merge_blocked)
self.assertEqual(action.next_state.failure_count, 1)
self.assertEqual(action.correction_action, "retrigger_pushback_and_reblock")
class QuotaAndCostTestCase(unittest.TestCase):
def test_quota_block_consumes_nothing(self):
state = new_loop_state("m1", "v1")
for _ in range(2):
state = drive(state, fail_verdict()).next_state
action = drive(state, fail_verdict(),
quota_check=lambda estimate: False)
self.assertEqual(action.kind, "quota_blocked")
self.assertIs(action.next_state, state)
self.assertEqual(action.next_state.failure_count, 2)
self.assertEqual(action.next_state.pushback_round, 2)
def test_quota_block_at_rebuild_consumes_no_rebuild(self):
state = new_loop_state("m1", "v1")
for _ in range(3):
state = drive(state, fail_verdict()).next_state
action = drive(state, fail_verdict(),
quota_check=lambda estimate: False)
self.assertEqual(action.kind, "quota_blocked")
self.assertEqual(action.next_state.rebuild_no, 0)
self.assertEqual(action.next_state.failure_count, 3)
def test_quota_check_exception_fails_closed(self):
state = new_loop_state("m1", "v1")
def _boom(estimate):
raise RuntimeError("quota service down")
action = drive(state, fail_verdict(), quota_check=_boom)
self.assertEqual(action.kind, "quota_blocked")
self.assertEqual(action.next_state.failure_count, 0)
def test_cost_threshold_escalates_rebuild_to_user(self):
state = new_loop_state("m1", "v1")
for _ in range(3):
state = drive(state, fail_verdict()).next_state
action = drive(state, fail_verdict(cost_estimate=500),
cost_threshold=100)
self.assertEqual(action.kind, "escalate")
self.assertEqual(action.escalation_target, "user")
self.assertEqual(action.next_state.rebuild_no, 0)
self.assertEqual(action.cost_estimate, 500)
self.assertGreaterEqual(len(action.escalation_history), 4)
self.assertFalse(action.escalation_grants_user_acceptance)
def test_cost_threshold_not_applied_to_pushback(self):
# #244 scope preserved: the threshold gates rebuild steps,
# a fix pushback is quota-checked only.
state = new_loop_state("m1", "v1")
action = drive(state, fail_verdict(cost_estimate=500),
cost_threshold=100)
self.assertEqual(action.kind, "pushback")
class TestChangeTestCase(unittest.TestCase):
def test_test_deletion_violation_does_not_advance(self):
state = new_loop_state("m1", "v1")
state = drive(state, fail_verdict()).next_state
action = drive(state, fail_verdict(test_changes=[
{"action": "delete", "test_id": "t1"}]))
self.assertEqual(action.kind, "escalate")
self.assertTrue(action.violations)
self.assertEqual(action.next_state.failure_count, 1)
self.assertEqual(action.next_state.pushback_round, 1)
def test_test_deletion_allowed_with_refreeze_and_audit(self):
state = new_loop_state("m1", "v1")
action = drive(state, fail_verdict(test_changes=[
{"action": "delete", "test_id": "t1",
"separate_audit_id": "audit-9"}],
refreeze_approved=True, separate_audit_ids=["audit-9"]))
self.assertEqual(action.kind, "pushback")
self.assertEqual(action.violations, [])
class LedgerEventTestCase(unittest.TestCase):
def test_all_seven_event_types_validate(self):
cases = {
"audit_verdict": {"module_id": "m1", "contract_version": "v1",
"verdict": "pass", "source": "audit", "failures": []},
"audit_pushback_round": {"module_id": "m1", "contract_version": "v1",
"round_no": 1, "trigger_items": ["ci_green"],
"pushback_package": {}, "round_status": "pushed"},
"audit_rebuild": {"module_id": "m1", "contract_version": "v1",
"rebuild_no": 1, "root_cause": "implementation",
"status": "decided", "retained_assets": {}},
"audit_third_failure_assessment": {
"module_id": "m1", "contract_version": "v1",
"determination": "continuation_round", "basis": {},
"failure_total": 3, "max_extra_rounds": 1},
"audit_test_change": {"module_id": "m1", "contract_version": "v1",
"test_changes": {"added": ["t9"], "modified": [],
"deleted": []},
"auditor": "auditor-a",
"contract_refrozen": False, "reason": "add only"},
"audit_meta_review": {"original_entry_id": "e1",
"original_verdict": "fail",
"corrected_verdict": "pass",
"misjudgment_found": True,
"reviewer": "auditor-b", "findings": [],
"triggered_process": "re-review"},
"audit_rebuild_notice": {"module_id": "m1", "contract_version": "v1",
"rebuild_no": 1,
"notification_content": "notice text",
"notified_party": "user",
"confirmation_status": "pending"},
}
for event_type, payload in cases.items():
self.assertEqual(validate_ledger_event(event_type, payload), [],
event_type)
event = emit(event_type, payload)
self.assertEqual(event["event_type"], event_type)
self.assertEqual(set(LEDGER_EVENT_TYPES), set(cases))
def test_unknown_event_type_and_missing_field_rejected(self):
self.assertTrue(validate_ledger_event("nope", {}))
with self.assertRaises(ValueError):
emit("audit_verdict", {"module_id": "m1"})
with self.assertRaises(ValueError):
emit("audit_verdict", {"module_id": "m1", "contract_version": "v1",
"verdict": "pass", "source": "audit"})
def test_conditional_maps_to_fail_and_keeps_original(self):
event = emit("audit_verdict", {
"module_id": "m1", "contract_version": "v1",
"verdict": "conditional", "source": "audit", "failures": []})
self.assertEqual(event["payload"]["verdict"], "fail")
self.assertEqual(event["payload"]["extra"]["original_verdict"],
"conditional")
# A driver verdict of conditional is a fail, not a pass.
action = drive(new_loop_state("m1", "v1"),
fail_verdict(verdict="conditional"))
self.assertEqual(action.kind, "pushback")
def test_test_change_hard_gate_in_emit(self):
payload = {"module_id": "m1", "contract_version": "v1",
"test_changes": {"added": [], "modified": ["t1"],
"deleted": []},
"auditor": "auditor-a", "contract_refrozen": False,
"reason": "drop a test"}
with self.assertRaises(ValueError):
emit("audit_test_change", payload)
payload["contract_refrozen"] = True
payload["refreeze_ref"] = "freeze-2"
emit("audit_test_change", payload) # no raise
def test_rebuild_notice_confirmed_needs_confirmer(self):
payload = {"module_id": "m1", "contract_version": "v1",
"rebuild_no": 1, "notification_content": "notice",
"notified_party": "user", "confirmation_status": "confirmed"}
with self.assertRaises(ValueError):
emit("audit_rebuild_notice", payload)
payload["confirmed_by"] = "user"
emit("audit_rebuild_notice", payload) # no raise
def test_action_kinds_are_bounded(self):
for kind in ACTION_KINDS:
self.assertIn(kind, ("pass", "pushback", "rebuild", "refreeze",
"escalate", "pending_user_confirmation",
"quota_blocked"))
action = drive(new_loop_state("m1", "v1"), fail_verdict())
self.assertIn(action.kind, ACTION_KINDS)
if __name__ == "__main__":
unittest.main()