Показать причины блокировки auto-coder в handoff
This commit is contained in:
parent
81acca3332
commit
06e035eadf
|
|
@ -5508,6 +5508,7 @@ def build_lead_coder_handoff(
|
|||
def build_lead_coder_handoff_markdown(handoff: dict[str, Any]) -> str:
|
||||
artifact_refs = handoff.get("artifact_refs") if isinstance(handoff.get("artifact_refs"), dict) else {}
|
||||
human_meaning = handoff.get("human_meaning") if isinstance(handoff.get("human_meaning"), dict) else {}
|
||||
auto_coder_gate = handoff.get("auto_coder_gate") if isinstance(handoff.get("auto_coder_gate"), dict) else {}
|
||||
lines = [
|
||||
"# Lead Codex repair handoff",
|
||||
"",
|
||||
|
|
@ -5534,6 +5535,43 @@ def build_lead_coder_handoff_markdown(handoff: dict[str, Any]) -> str:
|
|||
f"- issue_codes: `{', '.join(normalize_string_list(handoff.get('issue_codes'))) or 'n/a'}`",
|
||||
f"- rerun_matrix: `{', '.join(normalize_string_list(handoff.get('rerun_matrix'))) or 'n/a'}`",
|
||||
"",
|
||||
]
|
||||
if auto_coder_gate:
|
||||
lines.extend(
|
||||
[
|
||||
"## Auto-Coder Gate",
|
||||
f"- allowed: `{bool(auto_coder_gate.get('allowed'))}`",
|
||||
f"- reason: `{auto_coder_gate.get('reason') or 'n/a'}`",
|
||||
f"- focus_id: `{auto_coder_gate.get('focus_id') or 'n/a'}`",
|
||||
]
|
||||
)
|
||||
blocking_reasons = normalize_string_list(auto_coder_gate.get("blocking_reasons"))
|
||||
lines.extend([f"- blocking_reason: `{item}`" for item in blocking_reasons[:12]] or ["- blocking_reason: `none`"])
|
||||
if len(blocking_reasons) > 12:
|
||||
lines.append(f"- blocking_reason_extra_count: `{len(blocking_reasons) - 12}`")
|
||||
issue_contracts = (
|
||||
auto_coder_gate.get("issue_catalog_contracts")
|
||||
if isinstance(auto_coder_gate.get("issue_catalog_contracts"), dict)
|
||||
else {}
|
||||
)
|
||||
if issue_contracts:
|
||||
lines.extend(["", "## Auto-Coder Catalog Contracts"])
|
||||
for issue_code, contract in sorted(issue_contracts.items()):
|
||||
if not isinstance(contract, dict):
|
||||
continue
|
||||
lines.extend(
|
||||
[
|
||||
f"- issue_code: `{issue_code}`",
|
||||
f" expected_contract: `{contract.get('expected_answer_contract') or 'n/a'}`",
|
||||
f" root_layers: `{', '.join(normalize_string_list(contract.get('root_layers'))) or 'n/a'}`",
|
||||
f" allowed_patch_targets: `{', '.join(normalize_string_list(contract.get('allowed_patch_targets'))) or 'n/a'}`",
|
||||
f" forbidden_patch_targets: `{', '.join(normalize_string_list(contract.get('forbidden_patch_targets'))) or 'n/a'}`",
|
||||
f" rerun_matrix: `{', '.join(normalize_string_list(contract.get('rerun_matrix'))) or 'n/a'}`",
|
||||
]
|
||||
)
|
||||
lines.append("")
|
||||
lines.extend(
|
||||
[
|
||||
"## Human Meaning",
|
||||
f"- user_intent_summary: {human_meaning.get('user_intent_summary') or 'n/a'}",
|
||||
f"- expected_direct_answer: {human_meaning.get('expected_direct_answer') or 'n/a'}",
|
||||
|
|
@ -5541,6 +5579,7 @@ def build_lead_coder_handoff_markdown(handoff: dict[str, Any]) -> str:
|
|||
"",
|
||||
"## Primary Focus",
|
||||
]
|
||||
)
|
||||
assigned_focus = handoff.get("assigned_primary_focus") if isinstance(handoff.get("assigned_primary_focus"), dict) else {}
|
||||
if assigned_focus:
|
||||
candidate_files = normalize_string_list(assigned_focus.get("candidate_files"))
|
||||
|
|
|
|||
|
|
@ -860,6 +860,7 @@ def build_stage_summary(
|
|||
"last_deterministic_gate_ok": last_iteration.get("deterministic_gate_ok"),
|
||||
"last_deterministic_gate_reason": last_iteration.get("deterministic_gate_reason"),
|
||||
"latest_business_audit": repo_relative(Path(str(last_iteration.get("business_audit_path")))) if last_iteration.get("business_audit_path") else None,
|
||||
"latest_auto_coder_gate": repo_relative(Path(str(last_iteration.get("auto_coder_gate_path")))) if last_iteration.get("auto_coder_gate_path") else None,
|
||||
"latest_lead_coder_handoff": repo_relative(Path(str(last_iteration.get("lead_coder_handoff_markdown_path") or loop_state.get("latest_lead_coder_handoff_markdown_path")))) if (last_iteration.get("lead_coder_handoff_markdown_path") or loop_state.get("latest_lead_coder_handoff_markdown_path")) else None,
|
||||
"latest_lead_coder_handoff_json": repo_relative(Path(str(last_iteration.get("lead_coder_handoff_path") or loop_state.get("latest_lead_coder_handoff_path")))) if (last_iteration.get("lead_coder_handoff_path") or loop_state.get("latest_lead_coder_handoff_path")) else None,
|
||||
"loop_accepted_gate": bool(last_iteration.get("accepted_gate")),
|
||||
|
|
@ -970,6 +971,7 @@ def build_stage_handoff_markdown(summary: dict[str, Any]) -> str:
|
|||
f"- next_action: `{summary.get('next_action')}`",
|
||||
f"- loop_dir: `{summary.get('loop_dir')}`",
|
||||
f"- latest_business_audit: `{summary.get('latest_business_audit') or 'n/a'}`",
|
||||
f"- latest_auto_coder_gate: `{summary.get('latest_auto_coder_gate') or 'n/a'}`",
|
||||
f"- latest_lead_coder_handoff: `{summary.get('latest_lead_coder_handoff') or 'n/a'}`",
|
||||
f"- stop_reason: {summary.get('stop_reason') or 'n/a'}",
|
||||
"",
|
||||
|
|
|
|||
|
|
@ -288,6 +288,53 @@ class DomainCaseLoopLeadHandoffTests(unittest.TestCase):
|
|||
gate["blocking_reasons"],
|
||||
)
|
||||
|
||||
def test_lead_handoff_markdown_surfaces_auto_coder_gate_blockers(self) -> None:
|
||||
handoff = {
|
||||
"repair_mode": "lead-handoff",
|
||||
"loop_id": "demo_loop",
|
||||
"iteration_id": "iteration_00",
|
||||
"quality_score": 42,
|
||||
"target_score": 88,
|
||||
"loop_decision": "partial",
|
||||
"deterministic_gate_ok": False,
|
||||
"deterministic_gate_reason": "repair_targets_remaining=P0:1",
|
||||
"artifact_refs": {
|
||||
"business_audit": "artifacts/domain_runs/demo/business_audit.md",
|
||||
"analyst_verdict": "artifacts/domain_runs/demo/analyst_verdict.json",
|
||||
"repair_targets": "artifacts/domain_runs/demo/repair_targets.json",
|
||||
"auto_coder_gate": "artifacts/domain_runs/demo/auto_coder_gate.json",
|
||||
"pack_dir": "artifacts/domain_runs/demo/pack",
|
||||
},
|
||||
"issue_codes": ["business_direct_answer_missing"],
|
||||
"rerun_matrix": ["failed_scenario", "accepted_smoke_pack"],
|
||||
"human_meaning": {"user_intent_summary": "User needs a direct answer."},
|
||||
"top_repair_targets": [],
|
||||
"candidate_files": [],
|
||||
"lead_instructions": [],
|
||||
"auto_coder_gate": {
|
||||
"allowed": False,
|
||||
"reason": "target_missing_evidence_paths:pack:s01",
|
||||
"focus_id": "answer_shape|composeStage",
|
||||
"blocking_reasons": ["target_missing_evidence_paths:pack:s01"],
|
||||
"issue_catalog_contracts": {
|
||||
"business_direct_answer_missing": {
|
||||
"expected_answer_contract": "direct_answer_surface_v1",
|
||||
"root_layers": ["answer_surface"],
|
||||
"allowed_patch_targets": ["llm_normalizer/backend/src/services/address_runtime/composeStage.ts"],
|
||||
"forbidden_patch_targets": ["routing rewrites"],
|
||||
"rerun_matrix": ["failed_scenario", "accepted_smoke_pack"],
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
markdown = dcl.build_lead_coder_handoff_markdown(handoff)
|
||||
|
||||
self.assertIn("## Auto-Coder Gate", markdown)
|
||||
self.assertIn("target_missing_evidence_paths:pack:s01", markdown)
|
||||
self.assertIn("## Auto-Coder Catalog Contracts", markdown)
|
||||
self.assertIn("direct_answer_surface_v1", markdown)
|
||||
|
||||
def test_analyst_priority_targets_become_lead_repair_targets(self) -> None:
|
||||
repair_targets = {
|
||||
"pack_id": "demo_pack",
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@ class StageAgentLoopTests(unittest.TestCase):
|
|||
"accepted_gate": False,
|
||||
"deterministic_gate_ok": False,
|
||||
"business_audit_path": str(iteration_dir / "business_audit.md"),
|
||||
"auto_coder_gate_path": str(iteration_dir / "auto_coder_gate.json"),
|
||||
"lead_coder_handoff_markdown_path": str(iteration_dir / "lead_coder_handoff.md"),
|
||||
"coder_status": "lead_handoff_required",
|
||||
}
|
||||
|
|
@ -249,6 +250,7 @@ class StageAgentLoopTests(unittest.TestCase):
|
|||
self.assertEqual(summary["next_action"], "lead_coder_repair_required")
|
||||
self.assertIn("lead_coder_handoff", summary["latest_lead_coder_handoff"])
|
||||
self.assertIn("business_audit", summary["latest_business_audit"])
|
||||
self.assertIn("auto_coder_gate", summary["latest_auto_coder_gate"])
|
||||
|
||||
def test_build_stage_summary_blocks_close_when_repair_lacks_validation(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
|
|
|
|||
Loading…
Reference in New Issue