Skip to content

Commit 1c67753

Browse files
authored
Merge pull request #4841 from loopx-project/codex/checkpoint-supplement-recovery
fix(refresh): admit checkpoint-only validator recovery
2 parents cb634f0 + 2929e87 commit 1c67753

2 files changed

Lines changed: 149 additions & 4 deletions

File tree

‎loopx/state_refresh.py‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,7 @@ def refresh_state_run(
883883
settlement_readback = None
884884
refresh_recovery = None
885885
prior_writeback_run = None
886+
checkpoint_supplement = False
886887
if todo_id or normalized_replan_obligation_id or turn_instance_id:
887888
if not turn_scoped_settlement_qualified:
888889
raise ValueError(
@@ -929,6 +930,9 @@ def refresh_state_run(
929930
if not refresh_recovery:
930931
raise RuntimeError("settlement readback omitted refresh recovery admission")
931932
prior_writeback_run = settlement_readback.writeback_run
933+
checkpoint_supplement = bool(
934+
refresh_recovery["decision"] == "supplement_checkpoint"
935+
)
932936
recovery_payload = refresh_recovery_payload(
933937
settlement_readback, registry_path=registry_path, runtime_root=runtime_root,
934938
goal_id=safe_goal_id, dry_run=dry_run,
@@ -1122,7 +1126,16 @@ def refresh_state_run(
11221126
effective_autonomous_replan_recorded = (
11231127
replan_qualification.autonomous_replan_recorded
11241128
)
1125-
if normalized_delivery_outcome in ACCOUNTABLE_DELIVERY_OUTCOMES:
1129+
# read_heartbeat_settlement admits checkpoint_supplement only for a
1130+
# checkpoint-only retry of an already committed writeback with the
1131+
# exact Goal/Agent/Todo/Turn and delivery identity. It rejects replayed
1132+
# mutations and conflicting vision decisions before this fence. The
1133+
# supplement repairs only the missing vision decision, so terminal
1134+
# validation remains attached to the original Todo completion.
1135+
if (
1136+
normalized_delivery_outcome in ACCOUNTABLE_DELIVERY_OUTCOMES
1137+
and not checkpoint_supplement
1138+
):
11261139
require_accountable_completion_validation(
11271140
state_text,
11281141
todo_fields=todo_fields,
@@ -1146,9 +1159,6 @@ def refresh_state_run(
11461159
completion_todo_id=completion_todo_id,
11471160
autonomous_replan_recorded=effective_autonomous_replan_recorded,
11481161
)
1149-
checkpoint_supplement = bool(
1150-
refresh_recovery and refresh_recovery["decision"] == "supplement_checkpoint"
1151-
)
11521162
if checkpoint_supplement and not vision_checkpoint.get("satisfied"):
11531163
raise ValueError(
11541164
"checkpoint supplement did not satisfy the missing decision; "

‎tests/control_plane/test_refresh_checkpoint_recovery.py‎

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
AGENT_ID,
1616
REPO_ROOT,
1717
GOAL_ID,
18+
SELECTED_REPLAN_TODO_ID,
1819
TODO_ID,
1920
TURN_ID,
21+
_configure_selected_todo_replan_fixture,
22+
_initialize_git_checkout,
2023
_run_cli,
2124
_write_fixture,
2225
_spend_run_count,
@@ -292,6 +295,138 @@ def test_same_turn_checkpoint_supplement_is_idempotent(tmp_path: Path, decision:
292295
assert _spend_run_count(runtime) == 1
293296

294297

298+
def test_checkpoint_only_recovery_bypasses_open_todo_completion_validation(
299+
tmp_path: Path,
300+
) -> None:
301+
project, runtime, registry = _write_fixture(tmp_path)
302+
_configure_selected_todo_replan_fixture(project, registry)
303+
_initialize_git_checkout(project)
304+
state_path = project / f".codex/goals/{GOAL_ID}/ACTIVE_GOAL_STATE.md"
305+
state_text = state_path.read_text(encoding="utf-8")
306+
selected_marker = (
307+
f"todo_id={SELECTED_REPLAN_TODO_ID} status=open "
308+
"task_class=advancement_task action_kind=validate "
309+
f"claimed_by={AGENT_ID} -->"
310+
)
311+
assert selected_marker in state_text
312+
state_path.write_text(
313+
state_text.replace(
314+
selected_marker,
315+
selected_marker.replace(" -->", " validation_command=pytest -->"),
316+
1,
317+
),
318+
encoding="utf-8",
319+
)
320+
turn_id = "turn-checkpoint-open-validator"
321+
binding = (
322+
"--agent-id",
323+
AGENT_ID,
324+
"--todo-id",
325+
SELECTED_REPLAN_TODO_ID,
326+
"--turn-instance-id",
327+
turn_id,
328+
)
329+
rc, guard = _run_cli(
330+
registry,
331+
runtime,
332+
"quota",
333+
"should-run",
334+
"--codex-app",
335+
"--goal-id",
336+
GOAL_ID,
337+
"--agent-id",
338+
AGENT_ID,
339+
"--turn-instance-id",
340+
turn_id,
341+
"--scan-path",
342+
str(project),
343+
cwd=project,
344+
)
345+
assert rc == 0, guard
346+
assert guard["decision"] == "autonomous_replan_required"
347+
assert guard["selected_todo"]["todo_id"] == SELECTED_REPLAN_TODO_ID
348+
349+
delivery = (
350+
"refresh-state",
351+
"--goal-id",
352+
GOAL_ID,
353+
*binding,
354+
"--classification",
355+
"validated_progress",
356+
"--delivery-batch-scale",
357+
"implementation",
358+
"--delivery-outcome",
359+
"outcome_progress",
360+
"--delivery-workspace-path",
361+
str(project),
362+
"--progress-result-class",
363+
"advanced",
364+
"--progress-surface-id",
365+
"surface:checkpoint-recovery",
366+
"--progress-probe-kind",
367+
"probe:checkpoint-recovery",
368+
"--progress-evidence-id",
369+
"evidence:checkpoint-recovery",
370+
"--no-global-sync",
371+
"--suppress-external-sinks",
372+
)
373+
mutations = (
374+
"--autonomous-replan-recorded",
375+
"--repair-delta-kind",
376+
"successor_or_supersede",
377+
)
378+
rc, first = _run_cli(registry, runtime, *delivery, *mutations, cwd=project)
379+
assert rc == 0, first
380+
assert first["appended"] is True
381+
assert first["vision_checkpoint"]["decision"] == "missing_required"
382+
383+
vision = (
384+
"--vision-summary",
385+
"Continue the accepted checkpoint recovery scope.",
386+
"--vision-acceptance",
387+
"The exact recovery preserves validation and identity fences.",
388+
)
389+
rc, rejected = _run_cli(
390+
registry, runtime, *delivery, *mutations, *vision, cwd=project
391+
)
392+
assert rc == 1, rejected
393+
assert (
394+
rejected["refresh_recovery"]["reason"]
395+
== "checkpoint_supplement_must_not_repeat_mutations"
396+
)
397+
398+
wrong_binding = list(delivery)
399+
wrong_todo_index = wrong_binding.index(SELECTED_REPLAN_TODO_ID)
400+
wrong_binding[wrong_todo_index] = "todo_chain_000000000001"
401+
rc, wrong_identity = _run_cli(
402+
registry, runtime, *wrong_binding, *vision, cwd=project
403+
)
404+
assert rc == 1, wrong_identity
405+
assert "settlement binding does not match" in wrong_identity["error"]
406+
407+
rc, repaired = _run_cli(registry, runtime, *delivery, *vision, cwd=project)
408+
assert rc == 0, repaired
409+
assert repaired["appended"] is True
410+
assert repaired["refresh_recovery"]["decision"] == "supplement_checkpoint"
411+
assert repaired["vision_checkpoint"]["satisfied"] is True
412+
assert repaired["settlement_identity"] == first["settlement_identity"]
413+
414+
rc, conflict = _run_cli(
415+
registry,
416+
runtime,
417+
*delivery,
418+
"--vision-summary",
419+
"Choose a conflicting recovery path.",
420+
cwd=project,
421+
)
422+
assert rc == 1, conflict
423+
assert (
424+
conflict["refresh_recovery"]["reason"]
425+
== "committed_vision_decision_conflict"
426+
)
427+
assert _spend_run_count(runtime) == 0
428+
429+
295430
def test_invalid_supplement_leaves_original_writeback_intact(tmp_path: Path):
296431
project, runtime, registry = _write_fixture(tmp_path)
297432
binding = (

0 commit comments

Comments
 (0)