|
15 | 15 | AGENT_ID, |
16 | 16 | REPO_ROOT, |
17 | 17 | GOAL_ID, |
| 18 | + SELECTED_REPLAN_TODO_ID, |
18 | 19 | TODO_ID, |
19 | 20 | TURN_ID, |
| 21 | + _configure_selected_todo_replan_fixture, |
| 22 | + _initialize_git_checkout, |
20 | 23 | _run_cli, |
21 | 24 | _write_fixture, |
22 | 25 | _spend_run_count, |
@@ -292,6 +295,138 @@ def test_same_turn_checkpoint_supplement_is_idempotent(tmp_path: Path, decision: |
292 | 295 | assert _spend_run_count(runtime) == 1 |
293 | 296 |
|
294 | 297 |
|
| 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 | + |
295 | 430 | def test_invalid_supplement_leaves_original_writeback_intact(tmp_path: Path): |
296 | 431 | project, runtime, registry = _write_fixture(tmp_path) |
297 | 432 | binding = ( |
|
0 commit comments