wip: phase_z2 evidence 파이프라인 + matching 실험(phase2~26) + 프론트 trace 패널 진행분 스냅샷

- src: phase_z2 composition/mapper/pipeline/placement_planner/retry, ai_fallback(prompts/schema/validate), mdx_text_atoms 신규
- Front: PipelineTracePanel 신규, FramePanel/SlideCanvas/Home/designAgentApi 등 갱신 + 테스트 4종 추가
- templates/phase_z2: catalog(component_expansion_registry, node_slot_mapping 신규), frames, families, slide_base 갱신
- tests/matching: phase2~26 매칭 실험 스크립트·리포트·온톨로지 전체 (미커밋 진행분)
- tests: b4_v4 evidence, task5~28.5 시리즈, regression(imp95 baseline) 등 신규 테스트 대량 추가
- docs/reference: MDX 구조 인벤토리, MDX→Frame 구조 계약 문서
- scripts: mdx 계약/parity/coverage/viewport 체크, gitea comment, run sync 유틸
- .gitignore: tmp*.json, chromedriver, .orchestrator, *.pkl, Front_test* 등 임시/스냅샷 제외

미완성 작업의 보존용 스냅샷 커밋 (2026-07-02)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-02 17:03:42 +09:00
co-authored by Claude Opus 4.8
parent 97b7833a1b
commit b836e79ee1
527 changed files with 673036 additions and 717 deletions
+27 -2
View File
@@ -29,8 +29,12 @@ SYSTEM_PROMPT = (
f" 3. proposal_kind MUST be one of: {_ALLOWED_KINDS}.\n"
f" 4. Do NOT propose any of: {_FORBIDDEN_KINDS}.\n"
" 5. Do NOT change frame_id — V4 rank-1 frame is locked.\n"
" 6. Keep declared frame slots (text/table/image/details) populated.\n"
" 7. Respect Internal Region containment; place content units within "
" 6. Preferred Task-12 output is proposal_kind='design_adaptation_plan': "
"plan frame/layout/repeat changes only; do NOT output final text slots.\n"
" 7. Do NOT create titles, labels, summaries, body text, or slot content. "
"The code layer will place MDX verbatim text.\n"
" 8. Keep declared frame slots (text/table/image/details) accounted for.\n"
" 9. Respect Internal Region containment; place content units within "
"the declared region only."
)
@@ -73,6 +77,27 @@ def build_ai_fallback_prompt(
"figma_partial_json": figma_partial_json,
"internal_region": internal_region,
"mdx_text_READ_ONLY": mdx_text,
"task12_output_contract": {
"preferred_proposal_kind": ProposalKind.DESIGN_ADAPTATION_PLAN.value,
"allowed_operation_examples": [
"increase_repeat_count",
"split_group_across_repeat_blocks",
"rebalance_zone_ratio",
"compact_spacing",
"use_repeatable_frame",
],
"forbidden_content_fields": [
"text",
"title",
"label",
"body",
"slots",
"mdx_text",
"summary",
"raw_html",
"raw_css",
],
},
}
return {
"system": SYSTEM_PROMPT,
+2
View File
@@ -4,6 +4,7 @@ Whitelisted proposal kinds (Stage 2 plan):
- builder_options_patch : zone/frame builder option overrides
- partial_overrides : Internal Region / Frame Slot content overrides
- slot_mapping_proposal : restructuring proposal (content unit mapping)
- design_adaptation_plan : structure-only plan; code applies verbatim MDX
Forbidden output forms (rejected by validator):
- mdx_text (MDX read-only — `feedback_ai_isolation_contract`)
@@ -23,6 +24,7 @@ class ProposalKind(str, Enum):
BUILDER_OPTIONS_PATCH = "builder_options_patch"
PARTIAL_OVERRIDES = "partial_overrides"
SLOT_MAPPING_PROPOSAL = "slot_mapping_proposal"
DESIGN_ADAPTATION_PLAN = "design_adaptation_plan"
FORBIDDEN_KINDS: frozenset[str] = frozenset(
+50
View File
@@ -27,6 +27,43 @@ class AiFallbackValidationError(ValueError):
_SLOT_KINDS = (ProposalKind.PARTIAL_OVERRIDES, ProposalKind.SLOT_MAPPING_PROPOSAL)
_DESIGN_PLAN_FORBIDDEN_CONTENT_KEYS: frozenset[str] = frozenset(
{
"body",
"label",
"labels",
"mdx_text",
"new_text",
"raw_css",
"raw_html",
"slot_payload",
"slots",
"summary",
"text",
"title",
}
)
def _find_forbidden_design_plan_keys(value: Any, path: str = "payload") -> list[str]:
"""Return content-bearing keys that would let AI generate text.
Task 12 contract: design_adaptation_plan may describe structure changes,
but code remains the sole layer that maps MDX verbatim text into slots.
"""
hits: list[str] = []
if isinstance(value, dict):
for key, nested in value.items():
key_str = str(key)
nested_path = f"{path}.{key_str}"
if key_str in _DESIGN_PLAN_FORBIDDEN_CONTENT_KEYS:
hits.append(nested_path)
hits.extend(_find_forbidden_design_plan_keys(nested, nested_path))
elif isinstance(value, list):
for idx, nested in enumerate(value):
hits.extend(_find_forbidden_design_plan_keys(nested, f"{path}[{idx}]"))
return hits
def validate_proposal(
proposal: AiFallbackProposal,
@@ -73,6 +110,19 @@ def validate_proposal(
"from payload.slots (text/table/image/details must remain populated)."
)
if proposal.proposal_kind is ProposalKind.DESIGN_ADAPTATION_PLAN:
operations = payload.get("operations")
if not isinstance(operations, list) or not operations:
raise AiFallbackValidationError(
"design adaptation plan: payload.operations must be a non-empty list."
)
forbidden_paths = _find_forbidden_design_plan_keys(payload)
if forbidden_paths:
raise AiFallbackValidationError(
"design adaptation plan: AI must not emit content-bearing fields "
f"{forbidden_paths}; code maps MDX verbatim text."
)
region_id = payload.get("region_id")
if region_id is not None and internal_region is not None:
declared_region_id = internal_region.get("id")