From 37d33bd6e6f952cab79e63694239a91aec860914 Mon Sep 17 00:00:00 2001 From: dctouch Date: Sat, 9 May 2026 12:51:05 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D0=BF=D0=BE=D0=B4=D1=81=D0=BA=D0=B0=D0=B7=D0=BA=D0=B8?= =?UTF-8?q?=20=D1=81=D0=BB=D0=B5=D0=B4=D1=83=D1=8E=D1=89=D0=B5=D0=B3=D0=BE?= =?UTF-8?q?=20=D1=88=D0=B0=D0=B3=D0=B0=20stage-loop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain_scenario_loop_repo_adapter.md | 2 + scripts/stage_agent_loop.py | 71 ++++++++++++++++++- scripts/test_stage_agent_loop.py | 3 + 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/docs/orchestration/domain_scenario_loop_repo_adapter.md b/docs/orchestration/domain_scenario_loop_repo_adapter.md index dace5af..10f9138 100644 --- a/docs/orchestration/domain_scenario_loop_repo_adapter.md +++ b/docs/orchestration/domain_scenario_loop_repo_adapter.md @@ -139,6 +139,8 @@ It stores the GUI review under `artifacts/domain_runs/stage_agent_loops/ dict[ next_action = "user_decision_required" else: next_action = "continue_autonomous_or_fix_blocker" - return { + summary = { "schema_version": STAGE_SUMMARY_SCHEMA_VERSION, "stage_id": stage_manifest["stage_id"], "module_name": stage_manifest.get("module_name"), @@ -230,6 +230,60 @@ def build_stage_summary(stage_manifest: dict[str, Any], loop_dir: Path) -> dict[ "save_autorun_on_accept": bool(stage_manifest.get("save_autorun_on_accept", True)), "updated_at": now_iso(), } + summary["next_step_guidance"] = build_next_step_guidance(next_action) + return summary + + +def build_next_step_guidance(next_action: str) -> dict[str, Any]: + command_by_action: dict[str, list[str]] = { + "continue_repair_from_gui_review_p0": [ + "python scripts/stage_agent_loop.py prepare-repair --manifest ", + "python scripts/stage_agent_loop.py run-repair --manifest --dry-run", + ], + "continue_repair_from_gui_review_p1": [ + "python scripts/stage_agent_loop.py prepare-repair --manifest ", + "python scripts/stage_agent_loop.py run-repair --manifest --dry-run", + ], + "execute_repair_without_dry_run_or_review_command": [ + "python scripts/stage_agent_loop.py run-repair --manifest ", + ], + "rerun_same_stage_or_gui_and_ingest_result": [ + "rerun the same GUI/session or stage semantic pack", + "python scripts/stage_agent_loop.py ingest-gui-run --manifest --run-id assistant-stage1-", + ], + "rerun_or_inspect_repair_targets": [ + "inspect repair_execution_summary.json and repair_targets.json", + "rerun the same GUI/session if the patch intentionally made no code changes", + ], + "user_or_architecture_decision_required": [ + "inspect repair_coder_result.json and decide whether to change scope, architecture, or acceptance bounds", + ], + "inspect_repair_execution_result": [ + "inspect repair_execution_summary.json before continuing", + ], + "manual_gui_confirmation": [ + "run or review the saved AGENT autorun in the GUI for final human confirmation", + ], + "manual_gui_confirmation_or_stage_close": [ + "review the latest GUI run visually; close the stage only if the user-facing answers are acceptable", + ], + "stage_closed_without_manual_confirmation": [ + "archive the stage artifacts and continue to the next module", + ], + "user_decision_required": [ + "read stage_loop_handoff.md and resolve the recorded user decision point", + ], + "continue_autonomous_or_fix_blocker": [ + "inspect stage_loop_handoff.md and rerun stage_agent_loop.py run after resolving the blocker", + ], + "inspect_gui_review_status": [ + "inspect run_review.md and repair_targets.json manually", + ], + } + return { + "next_action": next_action, + "command_templates": command_by_action.get(next_action, ["inspect stage_loop_handoff.md"]), + } def build_stage_handoff_markdown(summary: dict[str, Any]) -> str: @@ -258,6 +312,19 @@ def build_stage_handoff_markdown(summary: dict[str, Any]) -> str: lines.extend(["", "## Acceptance invariants"]) invariants = summary.get("acceptance_invariants") or [] lines.extend([f"- {item}" for item in invariants] if invariants else ["- domain loop gate + analyst verdict"]) + next_step_guidance = ( + summary.get("next_step_guidance") + if isinstance(summary.get("next_step_guidance"), dict) + else {} + ) + if next_step_guidance: + lines.extend(["", "## Next Step Guidance"]) + commands = ( + next_step_guidance.get("command_templates") + if isinstance(next_step_guidance.get("command_templates"), list) + else [] + ) + lines.extend([f"- `{item}`" for item in commands] if commands else ["- inspect stage_loop_handoff.md"]) latest_gui_review = summary.get("latest_gui_review") if isinstance(summary.get("latest_gui_review"), dict) else {} if latest_gui_review: lines.extend( @@ -369,6 +436,7 @@ def build_repair_execution_stage_summary( "updated_at": now_iso(), } ) + base["next_step_guidance"] = build_next_step_guidance(next_action) return base @@ -495,6 +563,7 @@ def build_gui_review_stage_summary( }, } ) + base["next_step_guidance"] = build_next_step_guidance(next_action) latest_repair_validation = build_latest_repair_validation( previous_summary=previous_summary, review=review, diff --git a/scripts/test_stage_agent_loop.py b/scripts/test_stage_agent_loop.py index db37c7e..7957b82 100644 --- a/scripts/test_stage_agent_loop.py +++ b/scripts/test_stage_agent_loop.py @@ -134,6 +134,7 @@ class StageAgentLoopTests(unittest.TestCase): self.assertEqual(summary["loop_final_status"], "accepted") self.assertTrue(summary["manual_confirmation_required"]) self.assertEqual(summary["next_action"], "manual_gui_confirmation") + self.assertIn("GUI", summary["next_step_guidance"]["command_templates"][0]) def test_build_stage_summary_continues_when_loop_is_partial(self) -> None: with tempfile.TemporaryDirectory() as tmp: @@ -203,6 +204,7 @@ class StageAgentLoopTests(unittest.TestCase): self.assertEqual(summary["next_action"], "continue_repair_from_gui_review_p0") self.assertFalse(summary["accepted_gate"]) self.assertEqual(summary["latest_gui_review"]["repair_targets_count"], 1) + self.assertIn("prepare-repair", summary["next_step_guidance"]["command_templates"][0]) def test_gui_review_stage_summary_links_post_repair_validation(self) -> None: with tempfile.TemporaryDirectory() as tmp: @@ -451,6 +453,7 @@ class StageAgentLoopTests(unittest.TestCase): self.assertEqual(execution_summary["next_action"], "execute_repair_without_dry_run_or_review_command") self.assertTrue(stage_summary["latest_repair_execution"]["dry_run"]) self.assertEqual(stage_summary["next_action"], "execute_repair_without_dry_run_or_review_command") + self.assertIn("run-repair", stage_summary["next_step_guidance"]["command_templates"][0]) def test_resolve_stage_repair_iteration_auto_prepares_from_handoff(self) -> None: with tempfile.TemporaryDirectory() as tmp: