diff --git a/loopx/cli_commands/project_lifecycle_refresh_state.py b/loopx/cli_commands/project_lifecycle_refresh_state.py index 6899989660..c5057ff3ec 100644 --- a/loopx/cli_commands/project_lifecycle_refresh_state.py +++ b/loopx/cli_commands/project_lifecycle_refresh_state.py @@ -813,7 +813,13 @@ def handle_refresh_state_command( raise RuntimeError( "exact settlement readback unexpectedly returned not-found" ) - settlement_result = settlement_readback.delivery + # A combined retry may already be settled, with no owed child to + # dispatch. Retain its complete receipts from the same typed read. + settlement_result = ( + settlement_readback.settlement + if getattr(args, "settle_turn", False) and settlement_readback.spend.failure is None + else settlement_readback.delivery + ) attach_settlement_progress( payload, settlement_readback, registry_path=registry_path, runtime_root=runtime_root, goal_ref=goal_ref, diff --git a/tests/control_plane/test_refresh_settle_turn.py b/tests/control_plane/test_refresh_settle_turn.py index e286a96e8f..a7113125ed 100644 --- a/tests/control_plane/test_refresh_settle_turn.py +++ b/tests/control_plane/test_refresh_settle_turn.py @@ -113,26 +113,31 @@ def test_same_turn_progress_or_completion_and_replay_without_extra_guard( validation, writeback = plan["ordered_steps"][:2] assert validation["command_condition"] == "todo_deliverable_complete" assert " --delivery-boundary semantic_closeout " in writeback["semantic_closeout_command_template"] - vision_args = [] if finish_todo: rc, complete = _run_generated_cli(validation["command_template"].replace( "", "Independent validation of the original deliverable."), registry_path=registry) assert rc == 0, complete - vision = tmp_path / "vision.json" - vision.write_text(json.dumps({ - "goal_id": GOAL_ID, "agent_id": AGENT_ID, "state": "vision_patch_proposed", - "vision_patch": {"acceptance_summary": "Original scoped validation completed; wider Goal remains open."}, - "path_delta": {"outcome": "continue", "prior_assumption": "The scoped deliverable needed validation.", - "observed_reality": "The original deliverable passed independent validation.", - "retained": ["Original requirements"], "evidence_refs": ["evidence:validation"]}, - })) - vision_args = ["--agent-vision-json", str(vision)] - command = writeback["command_template"] + vision = tmp_path / "vision.json" + vision.write_text(json.dumps({ + "schema_version": "goal_vision_replan_contract_v0", + "goal_id": GOAL_ID, "agent_id": AGENT_ID, "state": "vision_patch_proposed", + "vision_patch": {"acceptance_summary": ( + "Original scoped validation completed; wider Goal remains open." if finish_todo else + "Verified progress advances the original task; its remaining acceptance stays open.")}, + "path_delta": {"outcome": "continue", "prior_assumption": "The scoped deliverable needed validation.", + "observed_reality": ( + "The original deliverable passed independent validation." if finish_todo else + "A bounded verified outcome advances work without completing the deliverable."), + "retained": ["Original requirements"], "evidence_refs": ["evidence:validation"]}, + })) + command = writeback["semantic_closeout_command_template"] for key, value in {"": "verified_slice", "": "implementation", - "": "outcome_progress", "": "semantic_closeout" if finish_todo else "in_flight_continuation"}.items(): + "": "outcome_progress", "": shlex.quote(str(vision))}.items(): command = command.replace(key, value) + words = shlex.split(command) + assert words[words.index("--delivery-boundary") + 1] == "semantic_closeout" command += " " + shlex.join(["--settle-turn", "--no-global-sync", "--suppress-external-sinks", - "--delivery-workspace-path", str(project), *vision_args]) + "--delivery-workspace-path", str(project)]) # A hostile ambient executable must not select another checkout/accounting owner. trap = tmp_path / "bin" trap.mkdir() @@ -144,8 +149,19 @@ def test_same_turn_progress_or_completion_and_replay_without_extra_guard( assert rc == 0, json.dumps(result, ensure_ascii=False) assert result["settlement_progress"]["state"] == "settled" assert result["settlement_identity"] == plan["identity"] + receipts = result["settlement_result"]["receipts"] + assert [row["step_kind"] for row in receipts] == ["validation", "durable_writeback", "quota_spend"], replay + assert all(row["status"] == "committed" and row["effect_id"] == plan["identity"]["effect_id"] + for row in receipts) assert _spend_run_count(runtime) == 1 assert result.get("idempotent_replay", False) is replay + ordinary_args = shlex.split(command) + ordinary_args.remove("--settle-turn") + rc, ordinary = _run_generated_cli(shlex.join(ordinary_args), registry_path=registry) + assert rc == 0 and ordinary["settlement_progress"]["state"] == "settled", ordinary + assert [row["step_kind"] for row in ordinary["settlement_result"]["receipts"]] == [ + "validation", "durable_writeback"] + assert _spend_run_count(runtime) == 1 rc, todo = _run_cli(registry, runtime, "todo", "list", "--goal-id", GOAL_ID, "--todo-id", TODO_ID) assert rc == 0 and todo["todo"]["status"] == ("done" if finish_todo else "open") events = runtime / "goals" / GOAL_ID / "rollout-events.jsonl"