feat(catalog): activate three_persona_benefits frame (IMP-04 #4 / 1 of 7)

Reason : V4 use_as_is=1 (frame_number=14, frame_id=1171281191).
Pattern : cards-3col-persona — 발주자/시공자/설계자 3 주체 각 benefit.

- Append `three_persona_benefits` contract to frame_contracts.yaml after
  the existing F13/F29/F16 entries (Codex Catch 1/4: YAML order = trace
  selection surface)
- Reuse existing builder primitives: items_with_role + quadrant_item
  parser. No new entry in PAYLOAD_BUILDERS / ITEM_PARSERS.
  Output dict shape: payload.personas = [{label, body, color_class}, ...]
- Add families/three_persona_benefits.html partial:
  - Pure CSS (no Figma raster img tags) per memory rule
    `feedback_blocks_must_be_css.md`
  - PROMOTED colors per persona (#285b4a client / #445a2f constructor /
    #743002 designer) from Figma TEXT layers
  - NOT PROMOTED: col_bg_texture / overlay / 하단 사진 / 원형 뱃지 inner-outer
    image — all replaced by CSS approximation (pill badge + colored
    border + check-style text-line bullets)
  - Token-fixed typography (zone-title / sub-title / caption / body)
  - data-frame-id="1171281191" data-template-id attributes
- Add bundled smoke fixture for three_persona_benefits to
  scripts/smoke_frame_render.py
- visual_hints.min_height_px = 280 (initial estimate between F13=230 and
  F29=345 for 3-card text-heavy layout). Refine during batch full
  pipeline if needed.
- accepted_content_types = [text_block] only (rich types not routed yet
  per IMP-03 scope-lock).

Verification :
- isolated Jinja StrictUndefined smoke (scripts/smoke_frame_render.py
  --self-check) : PASS=4/4 (existing 3 + new persona, 3889 chars)
- regression run on MDX 03 (env OFF + rich OFF) : PASS — MDX 03 V4
  rank-1 still F13/F29 so the new entry does not affect existing flow

scope-lock 15 conditions all honored (no V4 / mapper / Phase R' / Step
6+ changes; per-frame 6-step gate complete; YAML order preserved).

Refs Gitea #4 (IMP-04 A-2 Catalog 확장)
This commit is contained in:
2026-05-13 06:56:35 +09:00
parent 2717a0a3a6
commit 556b4486ae
3 changed files with 223 additions and 0 deletions

View File

@@ -179,3 +179,66 @@ bim_issues_quadrant_four:
empty_body: []
# implicit slot_order = [quadrant_1=TL, quadrant_2=TR, quadrant_3=BL, quadrant_4=BR]
# 위치(TL/TR/BL/BR) 매핑은 partial template (families/bim_issues_quadrant_four.html) 결정.
# ─── IMP-04 catalog 확장 (Gitea #4 scope-lock 7 frame) ───
# 본 entry 들은 기존 3 entry (F13/F29/F16) *뒤에* append — YAML order 가
# `list(load_frame_contracts().values())` 의 B4 trace 선택 순서 surface.
# Reason 명시 (Codex Catch 5) : 각 frame 의 V4 use_as_is/light_edit 신호.
three_persona_benefits:
# Reason : V4 use_as_is=1 (frame_number=14, frame_id=1171281191).
# Pattern : cards-3col-persona (발주자/시공자/설계자 3 주체 각 benefit).
template_id: three_persona_benefits
frame_id: 1171281191
family: cards
source_shape: top_bullets
cardinality:
strict: 3 # 3 persona = strict.
overflow_policy: abort_or_review
# 순서 기반 color theme (Figma 원본 색).
# client = 발주자 (#285b4a 그린 계열), constructor = 시공자 (#445a2f 올리브),
# designer = 설계자 (#743002 적갈) — partial 의 .f14b__col--{role} 매핑.
role_order:
- client
- constructor
- designer
# min_height_px : initial estimate (3-card 텍스트 밀도 기준, F13=230 vs F29=345 사이).
# smoke 통과 후 batch full-pipeline render 시 실측 갱신.
visual_hints:
min_height_px: 280
accepted_content_types:
- text_block
# Frame Slot 선언 (SPEC v1 §3 layer B). 3 persona = 3 sub-zone.
sub_zones:
- id: persona_1
role: main_text
accepts: [text_block]
cardinality: { strict: 1 }
partial_target_path: ".f14b__cols > .f14b__col:nth-child(1)"
- id: persona_2
role: main_text
accepts: [text_block]
cardinality: { strict: 1 }
partial_target_path: ".f14b__cols > .f14b__col:nth-child(2)"
- id: persona_3
role: main_text
accepts: [text_block]
cardinality: { strict: 1 }
partial_target_path: ".f14b__cols > .f14b__col:nth-child(3)"
payload:
title:
source: section.title
# builder 재사용 — F13 의 `items_with_role` + F16 의 `quadrant_item` parser.
# quadrant_item = {label, body:[{text,indent}]} → persona card body 정합.
builder: items_with_role
builder_options:
item_parser: quadrant_item # ITEM_PARSERS entry (F16 재사용)
array_root: personas # payload.personas = list of items
role_field: color_class # role_order[i] → item.color_class