feat(#17): generic fallback 탈출 — V4 evidence 확장 + renderable-aware provisional + 게이트 순서 버그 수정
1. V4 evidence 확장 (pipeline_17b_extend_missing_sections.py): '결과물이 아니라 프로세스' 원칙 — pipeline_17 과 동일 평가 코드로 누락 3개 섹션(01-intro/05-1/05-2)만 평가해 v4_full32_result.yaml 병합 (기존 무접촉, blind/ANSWER_MAP 불변). 결과: 05-1 → F20 light_edit 0.77 (design-matched!), 01-intro/05-2 → all-reject (catalog gap 정직 노출 — F19 가 05 주제와 이름까지 일치하나 partial 없음 → #2 프로모션 최우선 근거) 2. renderable-aware provisional (IMP-30 u1 정밀화): rank-1 무조건 승격 → partial 존재 AND (비-reject OR verbatim builder 보유) 첫 후보 승격. reject+builder 미보유 renderable 의 mapper 네이티브 렌더는 원문 drop 위험 (F23 1-atom 손실 실측) — 원문 보존 > design 개선 우선순위. 3. 게이트 순서 버그 수정 (_apply_quality_gate_downgrades 추출): T28.5d popup 승격 후 재계산 경로에 quality gate 강등 3종(coverage/forbidden/ consistency) 미적용 → 텍스트 손실이 overall=PASS 로 통과 (mdx05 실측). 양 경로 공통 헬퍼로 통일 — mdx04 의 #16 시점 PASS 일부가 이 버그 덕이었음을 정직하게 정정 (현재 PARTIAL + frame mismatch 라벨, 텍스트는 완전). 최종 5-MDX: 전부 missing_atoms=0 (무손실) / 01·03 PASS / 02·04·05 PARTIAL(정직 frame-mismatch 라벨) / mdx01 readiness not_ready→needs_review 개선. 게이트: 1061 passed. SHA baseline 재캡처 (정당 변경). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
63f4479b05
commit
9b79bf7538
6 files changed
+1868
-58
No files matched your search
+121
-52
@@ -664,6 +664,21 @@ def _build_verbatim_compare_table_2col(unit) -> dict:
|
||||
}
|
||||
|
||||
|
||||
# issue #17 — verbatim builder 를 보유한 template 집합 (아래 dispatch 와 동기).
|
||||
# renderable-aware provisional 이 reject 라벨 후보를 승격해도 안전한지(원문
|
||||
# 무손실 코드 렌더 가능한지) 판단하는 데 사용.
|
||||
_VERBATIM_BUILDER_TEMPLATE_IDS: frozenset[str] = frozenset({
|
||||
"three_parallel_requirements",
|
||||
"three_persona_benefits",
|
||||
"construction_goals_three_circle_intersection",
|
||||
"construction_bim_three_usage",
|
||||
"bim_dx_comparison_table",
|
||||
"bim_issues_quadrant_four",
|
||||
"sw_dependency_four_problems",
|
||||
"pre_construction_model_info_stacked",
|
||||
})
|
||||
|
||||
|
||||
def _emergency_p4b_build_verbatim_slot_payload(
|
||||
unit, template_id: str, override_slot_count: Optional[int] = None,
|
||||
) -> Optional[dict]:
|
||||
@@ -2797,16 +2812,41 @@ def lookup_v4_match_with_fallback(
|
||||
# trace entries are left intact (their skip reasons remain accurate).
|
||||
# Default-off keeps IMP-05 behavior byte-identical.
|
||||
if allow_provisional:
|
||||
rank_1_judgment = judgments[0]
|
||||
# issue #17 — renderable-aware provisional. rank 순서는 유지하되
|
||||
# 승격 가능 조건: partial HTML 존재 (IMP-95 u6 partial_exists 원칙)
|
||||
# AND (비-reject 라벨 OR verbatim builder 지원). 근거 실측:
|
||||
# - partial 없는 frame 승격 → mapper FitError → verbatim 복구 →
|
||||
# selected_frame_not_applied 라벨 (mdx05 05-2 rank-1 F6)
|
||||
# - reject 라벨 + verbatim builder 없는 frame 의 mapper 네이티브
|
||||
# 렌더는 원문 drop 위험 (mdx05 05-2 F23: 1 atom 손실 실측 —
|
||||
# 원문 보존이 design 개선보다 우선)
|
||||
# 조건 만족 후보가 없으면 기존대로 rank-1 (동작 보존 — verbatim
|
||||
# 복구가 텍스트 무손실을 보장).
|
||||
provisional_judgment = judgments[0]
|
||||
provisional_rank = 1
|
||||
for _rank, _judgment in enumerate(judgments, start=1):
|
||||
_tid = str(_judgment.get("template_id") or "")
|
||||
if not _b4_partial_exists(_tid):
|
||||
continue
|
||||
_label = str(_judgment.get("label") or "")
|
||||
if _label == "reject" and _tid not in _VERBATIM_BUILDER_TEMPLATE_IDS:
|
||||
continue
|
||||
provisional_judgment, provisional_rank = _judgment, _rank
|
||||
break
|
||||
provisional_match = _v4_match_from_judgment(
|
||||
section_id, rank_1_judgment, rank=1
|
||||
section_id, provisional_judgment, rank=provisional_rank
|
||||
)
|
||||
provisional_match.selection_path = "provisional_rank_1"
|
||||
_prov_path = (
|
||||
"provisional_rank_1"
|
||||
if provisional_rank == 1
|
||||
else f"provisional_renderable_rank_{provisional_rank}"
|
||||
)
|
||||
provisional_match.selection_path = _prov_path
|
||||
provisional_match.fallback_reason = trace["fallback_reason"]
|
||||
provisional_match.provisional = True
|
||||
trace.update({
|
||||
"selection_path": "provisional_rank_1",
|
||||
"selected_rank": 1,
|
||||
"selection_path": _prov_path,
|
||||
"selected_rank": provisional_rank,
|
||||
"selected_template_id": provisional_match.template_id,
|
||||
"selected_frame_id": provisional_match.frame_id,
|
||||
"selected_label": provisional_match.label,
|
||||
@@ -5870,6 +5910,63 @@ def _compute_density_gate(zones_data: list[dict], overflow: dict) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def _apply_quality_gate_downgrades(
|
||||
slide_status: dict,
|
||||
*,
|
||||
rendered_text_coverage: dict,
|
||||
forbidden_rendered_syntax: dict,
|
||||
applied_render_consistency: dict,
|
||||
) -> None:
|
||||
"""issue #17 — quality gate 3종의 overall 강등을 단일 헬퍼로.
|
||||
|
||||
두 slide_status 조립 경로(최초 + T28.5d popup 승격 후 재계산)가 동일한
|
||||
강등 규칙을 공유해야 한다 — 재계산 경로에 이 블록이 없어서 텍스트
|
||||
손실(rendered_text_coverage fail)이 overall=PASS 로 통과하는 버그가
|
||||
있었음 (mdx05 05-2 F23 렌더 1-atom 손실 실측).
|
||||
"""
|
||||
if not rendered_text_coverage.get("passed"):
|
||||
slide_status.setdefault("quality_gate_failures", []).append(
|
||||
"rendered_text_coverage_missing"
|
||||
)
|
||||
slide_status.setdefault("visual_fail_reasons", []).append(
|
||||
"rendered_text_coverage_missing: "
|
||||
f"{rendered_text_coverage.get('missing_atoms_count')} source text atom(s) "
|
||||
"not visible in final.html"
|
||||
)
|
||||
if slide_status.get("overall") == "PASS":
|
||||
slide_status["overall"] = "PARTIAL_COVERAGE"
|
||||
|
||||
if not forbidden_rendered_syntax.get("passed"):
|
||||
slide_status.setdefault("quality_gate_failures", []).append(
|
||||
"forbidden_rendered_syntax"
|
||||
)
|
||||
slide_status.setdefault("visual_fail_reasons", []).append(
|
||||
"forbidden_rendered_syntax: "
|
||||
f"{forbidden_rendered_syntax.get('forbidden_syntax_count')} JSX/CSS/code "
|
||||
"fragment(s) visible in final.html"
|
||||
)
|
||||
if slide_status.get("overall") == "PASS":
|
||||
slide_status["overall"] = "PARTIAL_COVERAGE"
|
||||
|
||||
if not applied_render_consistency.get("passed"):
|
||||
for failure in applied_render_consistency.get("failures") or []:
|
||||
slide_status.setdefault("quality_gate_failures", []).append(failure)
|
||||
if applied_render_consistency.get("frame_mismatch_count"):
|
||||
slide_status.setdefault("visual_fail_reasons", []).append(
|
||||
"selected_frame_not_applied: "
|
||||
f"{applied_render_consistency.get('frame_mismatch_count')} zone(s) "
|
||||
"rendered with a different template than Step 6 selected"
|
||||
)
|
||||
if "final_layout_positions_mismatch" in (applied_render_consistency.get("failures") or []):
|
||||
slide_status.setdefault("visual_fail_reasons", []).append(
|
||||
"final_layout_positions_mismatch: "
|
||||
f"expected {applied_render_consistency.get('expected_positions')} but "
|
||||
f"final.html has {applied_render_consistency.get('final_positions')}"
|
||||
)
|
||||
if slide_status.get("overall") == "PASS":
|
||||
slide_status["overall"] = "PARTIAL_COVERAGE"
|
||||
|
||||
|
||||
def _compute_design_readiness(
|
||||
*,
|
||||
units: list[CompositionUnit],
|
||||
@@ -11310,53 +11407,17 @@ def run_phase_z2_mvp1(
|
||||
density_gate=density_gate,
|
||||
)
|
||||
slide_status["design_readiness"] = design_readiness
|
||||
if not rendered_text_coverage.get("passed"):
|
||||
slide_status.setdefault("quality_gate_failures", []).append(
|
||||
"rendered_text_coverage_missing"
|
||||
)
|
||||
slide_status.setdefault("visual_fail_reasons", []).append(
|
||||
"rendered_text_coverage_missing: "
|
||||
f"{rendered_text_coverage.get('missing_atoms_count')} source text atom(s) "
|
||||
"not visible in final.html"
|
||||
)
|
||||
if slide_status.get("overall") == "PASS":
|
||||
slide_status["overall"] = "PARTIAL_COVERAGE"
|
||||
|
||||
# IMP-47B u8 — Surface Step 12 AI repair outcomes through slide_status.
|
||||
# Composes u4 gather errors + u5 apply_status + u7 coverage_invariant
|
||||
# into a single ``ai_repair_status`` axis the frontend (u11) reads to
|
||||
# render human_review notifications. Auto pipeline first
|
||||
# ([[feedback_auto_pipeline_first]]) — no review_queue insertion;
|
||||
# explicit status enum + human_review_required flag.
|
||||
if not forbidden_rendered_syntax.get("passed"):
|
||||
slide_status.setdefault("quality_gate_failures", []).append(
|
||||
"forbidden_rendered_syntax"
|
||||
)
|
||||
slide_status.setdefault("visual_fail_reasons", []).append(
|
||||
"forbidden_rendered_syntax: "
|
||||
f"{forbidden_rendered_syntax.get('forbidden_syntax_count')} JSX/CSS/code "
|
||||
"fragment(s) visible in final.html"
|
||||
)
|
||||
if slide_status.get("overall") == "PASS":
|
||||
slide_status["overall"] = "PARTIAL_COVERAGE"
|
||||
|
||||
if not applied_render_consistency.get("passed"):
|
||||
for failure in applied_render_consistency.get("failures") or []:
|
||||
slide_status.setdefault("quality_gate_failures", []).append(failure)
|
||||
if applied_render_consistency.get("frame_mismatch_count"):
|
||||
slide_status.setdefault("visual_fail_reasons", []).append(
|
||||
"selected_frame_not_applied: "
|
||||
f"{applied_render_consistency.get('frame_mismatch_count')} zone(s) "
|
||||
"rendered with a different template than Step 6 selected"
|
||||
)
|
||||
if "final_layout_positions_mismatch" in (applied_render_consistency.get("failures") or []):
|
||||
slide_status.setdefault("visual_fail_reasons", []).append(
|
||||
"final_layout_positions_mismatch: "
|
||||
f"expected {applied_render_consistency.get('expected_positions')} but "
|
||||
f"final.html has {applied_render_consistency.get('final_positions')}"
|
||||
)
|
||||
if slide_status.get("overall") == "PASS":
|
||||
slide_status["overall"] = "PARTIAL_COVERAGE"
|
||||
# IMP-47B u8 주석 이력 보존 — Surface Step 12 AI repair outcomes through
|
||||
# slide_status (아래 ai_repair_status 블록). quality gate downgrade 3종은
|
||||
# issue #17 에서 _apply_quality_gate_downgrades 로 추출 (popup 승격 후
|
||||
# 재계산 경로에도 동일 적용 — 미적용 시 text 손실이 PASS 로 통과하는
|
||||
# 게이트 순서 버그가 있었음: mdx05 실측).
|
||||
_apply_quality_gate_downgrades(
|
||||
slide_status,
|
||||
rendered_text_coverage=rendered_text_coverage,
|
||||
forbidden_rendered_syntax=forbidden_rendered_syntax,
|
||||
applied_render_consistency=applied_render_consistency,
|
||||
)
|
||||
|
||||
presentation_fit = _build_presentation_fit_report(
|
||||
overflow=overflow,
|
||||
@@ -11448,6 +11509,14 @@ def run_phase_z2_mvp1(
|
||||
slide_status["density_gate"] = density_gate
|
||||
slide_status["design_readiness"] = design_readiness
|
||||
slide_status["presentation_fit"] = presentation_fit
|
||||
# issue #17 — 재계산 경로에도 quality gate 강등 적용 (미적용 시
|
||||
# text 손실이 overall=PASS 로 통과하던 게이트 순서 버그).
|
||||
_apply_quality_gate_downgrades(
|
||||
slide_status,
|
||||
rendered_text_coverage=rendered_text_coverage,
|
||||
forbidden_rendered_syntax=forbidden_rendered_syntax,
|
||||
applied_render_consistency=applied_render_consistency,
|
||||
)
|
||||
slide_status["technical_pass"] = bool(
|
||||
slide_status.get("rendered")
|
||||
and slide_status.get("full_mdx_coverage")
|
||||
|
||||
Reference in new issue
Block a user