Добавить status-команду для stage-loop
This commit is contained in:
parent
f628266e44
commit
73b9053bab
|
|
@ -95,6 +95,7 @@ python scripts/stage_agent_loop.py run --manifest docs/orchestration/<stage_loop
|
||||||
python scripts/stage_agent_loop.py ingest-gui-run --manifest docs/orchestration/<stage_loop>.json --run-id assistant-stage1-<id>
|
python scripts/stage_agent_loop.py ingest-gui-run --manifest docs/orchestration/<stage_loop>.json --run-id assistant-stage1-<id>
|
||||||
python scripts/stage_agent_loop.py prepare-repair --manifest docs/orchestration/<stage_loop>.json
|
python scripts/stage_agent_loop.py prepare-repair --manifest docs/orchestration/<stage_loop>.json
|
||||||
python scripts/stage_agent_loop.py run-repair --manifest docs/orchestration/<stage_loop>.json --dry-run
|
python scripts/stage_agent_loop.py run-repair --manifest docs/orchestration/<stage_loop>.json --dry-run
|
||||||
|
python scripts/stage_agent_loop.py status --manifest docs/orchestration/<stage_loop>.json
|
||||||
python scripts/stage_agent_loop.py summarize --manifest docs/orchestration/<stage_loop>.json
|
python scripts/stage_agent_loop.py summarize --manifest docs/orchestration/<stage_loop>.json
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -141,6 +142,8 @@ It stores the GUI review under `artifacts/domain_runs/stage_agent_loops/<stage_i
|
||||||
|
|
||||||
`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.
|
`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.
|
||||||
|
|
||||||
|
Use `python scripts/stage_agent_loop.py status --manifest docs/orchestration/<stage_loop>.json` as the cheap read-only checkpoint before continuing a stage. It prints the current next action, closing gate, latest GUI run, latest repair coder status, and latest repair validation status without modifying artifacts.
|
||||||
|
|
||||||
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.
|
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:
|
To prepare the next repair iteration from that handoff, run:
|
||||||
|
|
|
||||||
|
|
@ -335,6 +335,10 @@ def build_next_step_guidance(next_action: str) -> dict[str, Any]:
|
||||||
"inspect_gui_review_status": [
|
"inspect_gui_review_status": [
|
||||||
"inspect run_review.md and repair_targets.json manually",
|
"inspect run_review.md and repair_targets.json manually",
|
||||||
],
|
],
|
||||||
|
"run_stage_loop_or_ingest_gui_run": [
|
||||||
|
"python scripts/stage_agent_loop.py run --manifest <stage_manifest.json> --dry-run",
|
||||||
|
"python scripts/stage_agent_loop.py ingest-gui-run --manifest <stage_manifest.json> --run-id assistant-stage1-<id>",
|
||||||
|
],
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
"next_action": next_action,
|
"next_action": next_action,
|
||||||
|
|
@ -1098,6 +1102,58 @@ def handle_plan(args: argparse.Namespace) -> int:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def build_stage_status(stage_manifest: dict[str, Any], stage_dir: Path) -> dict[str, Any]:
|
||||||
|
summary_path = stage_dir / "stage_loop_summary.json"
|
||||||
|
summary = load_json_object(summary_path, "Existing stage summary") if summary_path.exists() else {}
|
||||||
|
latest_gui_review = summary.get("latest_gui_review") if isinstance(summary.get("latest_gui_review"), dict) else {}
|
||||||
|
latest_repair_execution = (
|
||||||
|
summary.get("latest_repair_execution")
|
||||||
|
if isinstance(summary.get("latest_repair_execution"), dict)
|
||||||
|
else {}
|
||||||
|
)
|
||||||
|
latest_repair_validation = (
|
||||||
|
summary.get("latest_repair_validation")
|
||||||
|
if isinstance(summary.get("latest_repair_validation"), dict)
|
||||||
|
else {}
|
||||||
|
)
|
||||||
|
stage_closing_gate = (
|
||||||
|
summary.get("stage_closing_gate")
|
||||||
|
if isinstance(summary.get("stage_closing_gate"), dict)
|
||||||
|
else {}
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"schema_version": "stage_agent_loop_status_v1",
|
||||||
|
"stage_id": stage_manifest["stage_id"],
|
||||||
|
"module_name": stage_manifest.get("module_name"),
|
||||||
|
"title": stage_manifest.get("title"),
|
||||||
|
"stage_dir": repo_relative(stage_dir),
|
||||||
|
"summary_exists": bool(summary),
|
||||||
|
"loop_final_status": summary.get("loop_final_status"),
|
||||||
|
"accepted_gate": summary.get("accepted_gate"),
|
||||||
|
"loop_accepted_gate": summary.get("loop_accepted_gate"),
|
||||||
|
"stage_closing_gate": stage_closing_gate or None,
|
||||||
|
"next_action": summary.get("next_action") or "run_stage_loop_or_ingest_gui_run",
|
||||||
|
"next_step_guidance": summary.get("next_step_guidance") or build_next_step_guidance("run_stage_loop_or_ingest_gui_run"),
|
||||||
|
"latest_gui_run_id": latest_gui_review.get("run_id"),
|
||||||
|
"latest_gui_business_status": latest_gui_review.get("overall_business_status"),
|
||||||
|
"latest_repair_coder_status": latest_repair_execution.get("coder_status"),
|
||||||
|
"latest_repair_dry_run": latest_repair_execution.get("dry_run"),
|
||||||
|
"latest_validation_run_id": latest_repair_validation.get("validation_run_id"),
|
||||||
|
"latest_validation_status": latest_repair_validation.get("validation_status"),
|
||||||
|
"accepted_after_repair": latest_repair_validation.get("accepted_after_repair"),
|
||||||
|
"summary_path": repo_relative(summary_path) if summary_path.exists() else None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def handle_status(args: argparse.Namespace) -> int:
|
||||||
|
stage_manifest_path = repo_path(args.manifest)
|
||||||
|
stage_manifest = load_stage_manifest(stage_manifest_path)
|
||||||
|
stage_dir = stage_dir_for(repo_path(args.output_root), stage_manifest["stage_id"])
|
||||||
|
payload = build_stage_status(stage_manifest, stage_dir)
|
||||||
|
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def handle_summarize(args: argparse.Namespace) -> int:
|
def handle_summarize(args: argparse.Namespace) -> int:
|
||||||
stage_manifest_path = repo_path(args.manifest)
|
stage_manifest_path = repo_path(args.manifest)
|
||||||
stage_manifest = load_stage_manifest(stage_manifest_path)
|
stage_manifest = load_stage_manifest(stage_manifest_path)
|
||||||
|
|
@ -1200,6 +1256,10 @@ def build_parser() -> argparse.ArgumentParser:
|
||||||
summarize_parser.add_argument("--loop-dir")
|
summarize_parser.add_argument("--loop-dir")
|
||||||
summarize_parser.set_defaults(func=handle_summarize)
|
summarize_parser.set_defaults(func=handle_summarize)
|
||||||
|
|
||||||
|
status_parser = subparsers.add_parser("status", help="Print current stage summary, gates, and next action.")
|
||||||
|
add_common_args(status_parser)
|
||||||
|
status_parser.set_defaults(func=handle_status)
|
||||||
|
|
||||||
ingest_parser = subparsers.add_parser(
|
ingest_parser = subparsers.add_parser(
|
||||||
"ingest-gui-run",
|
"ingest-gui-run",
|
||||||
help="Attach an existing assistant-stage1 GUI run review to the stage loop summary.",
|
help="Attach an existing assistant-stage1 GUI run review to the stage loop summary.",
|
||||||
|
|
|
||||||
|
|
@ -542,6 +542,56 @@ class StageAgentLoopTests(unittest.TestCase):
|
||||||
self.assertEqual(stage_summary["next_action"], "execute_repair_without_dry_run_or_review_command")
|
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])
|
self.assertIn("run-repair", stage_summary["next_step_guidance"]["command_templates"][0])
|
||||||
|
|
||||||
|
def test_build_stage_status_summarizes_current_gate_and_validation(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
root = Path(tmp)
|
||||||
|
stage_dir = root / "stage_runs" / "agent_loop"
|
||||||
|
write_json(
|
||||||
|
stage_dir / "stage_loop_summary.json",
|
||||||
|
{
|
||||||
|
"stage_id": "agent_loop",
|
||||||
|
"loop_final_status": "accepted",
|
||||||
|
"accepted_gate": False,
|
||||||
|
"loop_accepted_gate": True,
|
||||||
|
"next_action": "rerun_same_stage_or_gui_and_ingest_result",
|
||||||
|
"stage_closing_gate": {
|
||||||
|
"status": "blocked_pending_repair_validation",
|
||||||
|
"passed": False,
|
||||||
|
},
|
||||||
|
"latest_gui_review": {
|
||||||
|
"run_id": "assistant-stage1-before",
|
||||||
|
"overall_business_status": "fail",
|
||||||
|
},
|
||||||
|
"latest_repair_execution": {
|
||||||
|
"coder_status": "patched",
|
||||||
|
"dry_run": False,
|
||||||
|
},
|
||||||
|
"latest_repair_validation": {
|
||||||
|
"validation_run_id": "assistant-stage1-rerun",
|
||||||
|
"validation_status": "failed_p0",
|
||||||
|
"accepted_after_repair": False,
|
||||||
|
},
|
||||||
|
"next_step_guidance": {
|
||||||
|
"command_templates": ["rerun the same GUI/session or stage semantic pack"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
status = stage_loop.build_stage_status(
|
||||||
|
{
|
||||||
|
"stage_id": "agent_loop",
|
||||||
|
"module_name": "Agent Loop",
|
||||||
|
"title": "Agent Loop",
|
||||||
|
},
|
||||||
|
stage_dir,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertTrue(status["summary_exists"])
|
||||||
|
self.assertEqual(status["next_action"], "rerun_same_stage_or_gui_and_ingest_result")
|
||||||
|
self.assertEqual(status["stage_closing_gate"]["status"], "blocked_pending_repair_validation")
|
||||||
|
self.assertEqual(status["latest_repair_coder_status"], "patched")
|
||||||
|
self.assertEqual(status["latest_validation_status"], "failed_p0")
|
||||||
|
|
||||||
def test_resolve_stage_repair_iteration_auto_prepares_from_handoff(self) -> None:
|
def test_resolve_stage_repair_iteration_auto_prepares_from_handoff(self) -> None:
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
root = Path(tmp)
|
root = Path(tmp)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue