Добавить подсказки следующего шага stage-loop

This commit is contained in:
dctouch 2026-05-09 12:51:05 +03:00
parent a3378a3d52
commit 37d33bd6e6
3 changed files with 75 additions and 1 deletions

View File

@ -139,6 +139,8 @@ It stores the GUI review under `artifacts/domain_runs/stage_agent_loops/<stage_i
- `continue_repair_from_gui_review_p1` when the run is semantically usable but still noisy, over-broad, or poorly layered;
- `manual_gui_confirmation_or_stage_close` when the GUI run is clean enough for final human confirmation.
`stage_loop_summary.json` also includes `next_step_guidance.command_templates`, so the next operator or agent pass can continue from machine-readable commands instead of re-inferring the workflow from prose.
It also writes `stage_repair_handoff.md/json` next to the stage summary. That handoff is the preferred input for the next coder pass: it lists primary repair targets and sample user-facing failures without forcing the coder to reread the entire GUI conversation first.
To prepare the next repair iteration from that handoff, run:

View File

@ -208,7 +208,7 @@ def build_stage_summary(stage_manifest: dict[str, Any], loop_dir: Path) -> 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 <stage_manifest.json>",
"python scripts/stage_agent_loop.py run-repair --manifest <stage_manifest.json> --dry-run",
],
"continue_repair_from_gui_review_p1": [
"python scripts/stage_agent_loop.py prepare-repair --manifest <stage_manifest.json>",
"python scripts/stage_agent_loop.py run-repair --manifest <stage_manifest.json> --dry-run",
],
"execute_repair_without_dry_run_or_review_command": [
"python scripts/stage_agent_loop.py run-repair --manifest <stage_manifest.json>",
],
"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 <stage_manifest.json> --run-id assistant-stage1-<new_id>",
],
"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,

View File

@ -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: