Донести detector signals до repair prompt
This commit is contained in:
parent
92e075157f
commit
50d4daf64e
|
|
@ -171,7 +171,7 @@ The status payload also exposes `effective_stage_status`, `effective_stage_statu
|
||||||
|
|
||||||
Use `python scripts/stage_agent_loop.py continue --manifest docs/orchestration/<stage_loop>.json` as the safe one-command continuation layer. From a cold start it materializes `domain_pack_loop.command.txt` without launching the long live loop; after a GUI review it can prepare a repair iteration and materialize `run-repair --dry-run` automatically; it will not run the real coder pass unless `--execute-repair` is passed, and it waits for a `--run-id assistant-stage1-<id>` when the next required step is post-repair rerun/ingest validation.
|
Use `python scripts/stage_agent_loop.py continue --manifest docs/orchestration/<stage_loop>.json` as the safe one-command continuation layer. From a cold start it materializes `domain_pack_loop.command.txt` without launching the long live loop; after a GUI review it can prepare a repair iteration and materialize `run-repair --dry-run` automatically; it will not run the real coder pass unless `--execute-repair` is passed, and it waits for a `--run-id assistant-stage1-<id>` when the next required step is post-repair rerun/ingest validation.
|
||||||
|
|
||||||
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, detector result signals, 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, detector result signals, and sample user-facing failures without forcing the coder to reread the entire GUI conversation first. `prepare-repair` preserves those detector signals in the repair iteration plan, prompt, and checklist so the coder sees the concrete detector, issue code, message, and evidence preview.
|
||||||
|
|
||||||
For live stage-pack failures, prefer `lead_coder_handoff.md` over immediately preparing a coder pass. The intent is: strong business audit first, Lead Codex code repair second, same replay/GUI validation third.
|
For live stage-pack failures, prefer `lead_coder_handoff.md` over immediately preparing a coder pass. The intent is: strong business audit first, Lead Codex code repair second, same replay/GUI validation third.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1899,6 +1899,9 @@ def build_stage_repair_iteration_plan(
|
||||||
if isinstance(target, dict)
|
if isinstance(target, dict)
|
||||||
]
|
]
|
||||||
sample_findings = handoff.get("sample_findings") if isinstance(handoff.get("sample_findings"), list) else []
|
sample_findings = handoff.get("sample_findings") if isinstance(handoff.get("sample_findings"), list) else []
|
||||||
|
detector_result_signals = (
|
||||||
|
handoff.get("detector_result_signals") if isinstance(handoff.get("detector_result_signals"), list) else []
|
||||||
|
)
|
||||||
candidate_files = list(
|
candidate_files = list(
|
||||||
dict.fromkeys(
|
dict.fromkeys(
|
||||||
path
|
path
|
||||||
|
|
@ -1937,6 +1940,9 @@ def build_stage_repair_iteration_plan(
|
||||||
"auto_propose_gate": (
|
"auto_propose_gate": (
|
||||||
handoff.get("auto_propose_gate") if isinstance(handoff.get("auto_propose_gate"), dict) else {}
|
handoff.get("auto_propose_gate") if isinstance(handoff.get("auto_propose_gate"), dict) else {}
|
||||||
),
|
),
|
||||||
|
"detector_result_signals": [
|
||||||
|
signal for signal in detector_result_signals if isinstance(signal, dict)
|
||||||
|
][:8],
|
||||||
"primary_repair_targets": enriched_targets,
|
"primary_repair_targets": enriched_targets,
|
||||||
"candidate_files": candidate_files,
|
"candidate_files": candidate_files,
|
||||||
"sample_findings": [finding for finding in sample_findings if isinstance(finding, dict)][:8],
|
"sample_findings": [finding for finding in sample_findings if isinstance(finding, dict)][:8],
|
||||||
|
|
@ -2009,6 +2015,19 @@ def build_stage_repair_checklist(plan: dict[str, Any]) -> str:
|
||||||
f"- `{target.get('severity')}` `{target.get('problem_layer')}` / `{target.get('issue_code')}`: "
|
f"- `{target.get('severity')}` `{target.get('problem_layer')}` / `{target.get('issue_code')}`: "
|
||||||
f"{target.get('occurrences')} occurrence(s)"
|
f"{target.get('occurrences')} occurrence(s)"
|
||||||
)
|
)
|
||||||
|
detector_signals = (
|
||||||
|
plan.get("detector_result_signals") if isinstance(plan.get("detector_result_signals"), list) else []
|
||||||
|
)
|
||||||
|
if detector_signals:
|
||||||
|
lines.extend(["", "## Detector Signals"])
|
||||||
|
for signal in detector_signals:
|
||||||
|
if not isinstance(signal, dict):
|
||||||
|
continue
|
||||||
|
issue_codes = ", ".join(dcl.normalize_string_list(signal.get("issue_codes"))) or "n/a"
|
||||||
|
lines.append(
|
||||||
|
f"- `{signal.get('status')}` `{signal.get('detector')}` `{issue_codes}`: "
|
||||||
|
f"{str(signal.get('message') or '').strip()}"
|
||||||
|
)
|
||||||
lines.extend(["", "## Acceptance"])
|
lines.extend(["", "## Acceptance"])
|
||||||
acceptance = plan.get("acceptance_rerun") if isinstance(plan.get("acceptance_rerun"), dict) else {}
|
acceptance = plan.get("acceptance_rerun") if isinstance(plan.get("acceptance_rerun"), dict) else {}
|
||||||
for item in acceptance.get("after_patch", []) if isinstance(acceptance.get("after_patch"), list) else []:
|
for item in acceptance.get("after_patch", []) if isinstance(acceptance.get("after_patch"), list) else []:
|
||||||
|
|
|
||||||
|
|
@ -900,6 +900,14 @@ class StageAgentLoopTests(unittest.TestCase):
|
||||||
"issue_codes": ["business_direct_answer_missing"],
|
"issue_codes": ["business_direct_answer_missing"],
|
||||||
"rerun_matrix": ["failed_scenario", "direct_answer_surface_pack", "accepted_smoke_pack"],
|
"rerun_matrix": ["failed_scenario", "direct_answer_surface_pack", "accepted_smoke_pack"],
|
||||||
"auto_propose_gate": {"allowed": True, "reason": "auto_coder_gate_passed"},
|
"auto_propose_gate": {"allowed": True, "reason": "auto_coder_gate_passed"},
|
||||||
|
"detector_result_signals": [
|
||||||
|
{
|
||||||
|
"detector": "business_direct_answer_missing_signal",
|
||||||
|
"status": "fail",
|
||||||
|
"issue_codes": ["business_direct_answer_missing"],
|
||||||
|
"message": "Direct answer is missing on the first line.",
|
||||||
|
}
|
||||||
|
],
|
||||||
"primary_repair_targets": [
|
"primary_repair_targets": [
|
||||||
{
|
{
|
||||||
"problem_layer": "answer_shape_mismatch",
|
"problem_layer": "answer_shape_mismatch",
|
||||||
|
|
@ -937,6 +945,7 @@ class StageAgentLoopTests(unittest.TestCase):
|
||||||
self.assertIn("business_direct_answer_missing", plan["issue_codes"])
|
self.assertIn("business_direct_answer_missing", plan["issue_codes"])
|
||||||
self.assertIn("direct_answer_surface_pack", plan["rerun_matrix"])
|
self.assertIn("direct_answer_surface_pack", plan["rerun_matrix"])
|
||||||
self.assertTrue(plan["auto_propose_gate"]["allowed"])
|
self.assertTrue(plan["auto_propose_gate"]["allowed"])
|
||||||
|
self.assertEqual(plan["detector_result_signals"][0]["detector"], "business_direct_answer_missing_signal")
|
||||||
self.assertIn("ingest-gui-run", plan["acceptance_rerun"]["ingest_command_template"])
|
self.assertIn("ingest-gui-run", plan["acceptance_rerun"]["ingest_command_template"])
|
||||||
|
|
||||||
def test_handle_prepare_repair_materializes_prompt_and_checklist(self) -> None:
|
def test_handle_prepare_repair_materializes_prompt_and_checklist(self) -> None:
|
||||||
|
|
@ -965,6 +974,14 @@ class StageAgentLoopTests(unittest.TestCase):
|
||||||
"question_quality_score": 100,
|
"question_quality_score": 100,
|
||||||
"review_markdown": "review.md",
|
"review_markdown": "review.md",
|
||||||
"repair_targets_json": "repair_targets.json",
|
"repair_targets_json": "repair_targets.json",
|
||||||
|
"detector_result_signals": [
|
||||||
|
{
|
||||||
|
"detector": "business_direct_answer_missing_signal",
|
||||||
|
"status": "fail",
|
||||||
|
"issue_codes": ["business_direct_answer_missing"],
|
||||||
|
"message": "Direct answer is missing on the first line.",
|
||||||
|
}
|
||||||
|
],
|
||||||
"primary_repair_targets": [
|
"primary_repair_targets": [
|
||||||
{
|
{
|
||||||
"problem_layer": "business_utility_gap",
|
"problem_layer": "business_utility_gap",
|
||||||
|
|
@ -988,12 +1005,13 @@ class StageAgentLoopTests(unittest.TestCase):
|
||||||
iteration_dir = stage_dir / "repair_iterations" / "repair_001"
|
iteration_dir = stage_dir / "repair_iterations" / "repair_001"
|
||||||
plan_exists = (iteration_dir / "repair_iteration_plan.json").exists()
|
plan_exists = (iteration_dir / "repair_iteration_plan.json").exists()
|
||||||
prompt = (iteration_dir / "repair_prompt.md").read_text(encoding="utf-8")
|
prompt = (iteration_dir / "repair_prompt.md").read_text(encoding="utf-8")
|
||||||
checklist_exists = (iteration_dir / "repair_checklist.md").exists()
|
checklist = (iteration_dir / "repair_checklist.md").read_text(encoding="utf-8")
|
||||||
|
|
||||||
self.assertEqual(exit_code, 0)
|
self.assertEqual(exit_code, 0)
|
||||||
self.assertTrue(plan_exists)
|
self.assertTrue(plan_exists)
|
||||||
self.assertTrue(checklist_exists)
|
|
||||||
self.assertIn("business_answer_too_verbose", prompt)
|
self.assertIn("business_answer_too_verbose", prompt)
|
||||||
|
self.assertIn("business_direct_answer_missing_signal", prompt)
|
||||||
|
self.assertIn("Direct answer is missing", checklist)
|
||||||
|
|
||||||
def test_handle_run_repair_dry_run_materializes_coder_command(self) -> None:
|
def test_handle_run_repair_dry_run_materializes_coder_command(self) -> None:
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue