refactor(persona): F14 2nd refinement — promote raster assets (IMP-04 Track A F2 re-do)

Second calibration refinement after Codex round 22 F2 finding (commit 2fcd8bb
was over-broad CSS-only over-generalization). Approach re-locked via Codex
rounds 23/24 + Claude rounds 23/25 with per-asset case-by-case promotion
policy (round 13 §2.3 restored).

Asset decisions for F14 (Codex round 24 agreement) :

PROMOTED RASTER (Phase Z `copy_assets()` infra — pipeline.py:746 — handles
delivery from figma_to_html_agent/blocks/1171281191/assets/ to runtime
{run_dir}/assets/three_persona_benefits/) :
- col_bg_texture (1 PNG, 3 col 공유)
- bottom photos × 3 (실사 사진, CSS equivalent 불가)
- badge outer × 3 (round ring image)
- badge inner × 3 (round disk image)
→ 10 raster assets total

PROMOTED CSS (디자인 의도, CSS 충분) :
- 발주자/시공자/설계자 accent color (#285b4a / #445a2f / #743002)
- col-overlay solid tint (#d6e7c4 / #e1efe1 / #d0c0ad, opacity 0.8)
- title gradient (#000#883700)
- bullet check marker (✓ unicode)

NOT PROMOTED : 한자/장식 텍스트 (Figma deco, MDX 무관)

min_height_px : 290 → 320 (badge 70 + body 210 + photo 36 + padding 30,
F29 345 class). Codex round 13 §2.2 derive + confirm method.

Partial structure :
- 3-column grid (Phase Z flex/grid, not Figma absolute positioning)
- per column: badge (raster outer/inner + CSS text overlay) → bullet
  body (CSS check marker) → bottom photo (raster, opacity 0.7)
- col-bg texture as ::before background image, overlay tint as ::after
- isolation: isolate + z-index layering for proper raster + overlay stacking

Verification :
- python scripts/smoke_frame_render.py --self-check : PASS 4/4 (persona
  refined 7446 chars, was 5314 in commit 2fcd8bb)

V2 validation gap (Codex round 24 §3 anticipation) :
- python run_mdx03_pipeline.py --phase-z2 --mdx samples/mdx/02. DX의 시행
  목표 및 기대효과.mdx --run-id imp04_persona_v2_mdx02 : FAIL
- TypeError 'NoneType' object is not subscriptable at contract["payload"]
- Root cause : MDX 02 section 02-1 V4 rank-1 = construction_goals_three_
  circle_intersection (frame 12), not yet in catalog. Pipeline aborts at
  02-1 before reaching 02-2.2 (persona target).
- Chain dependency : MDX 02 acceptance requires construction_goals
  activation first.
- F1/F2/F3 classification of this blocker pending Codex round 26 review.

Visual rendering of F14 deferred to either:
(a) construction_goals activation first (Track A priority reorder)
(b) V3 synthetic MDX with persona-only content + V4 evidence extension
(c) V4 dev override / IMP-06 section-override mechanism

This commit progresses the asset-promoted F14 partial. F14 acceptance
gate (actual render inspection) remains open pending the V2 chain
dependency resolution.

scope-lock guardrails honored : 32-frame target, no V4 logic change, no
Phase R' regression, no MDX03/MDX02 hardcoding, no other partial/builder
modified.

Refs Gitea #4 (IMP-04 Track A F2 re-do, persona 2nd refinement)
This commit is contained in:
2026-05-13 10:49:20 +09:00
parent 2fcd8bb94a
commit a1c06b779a
2 changed files with 142 additions and 68 deletions

View File

@@ -206,13 +206,13 @@ three_persona_benefits:
- constructor
- designer
# min_height_px : derive + confirm (Codex round 13 §2.2).
# min_height_px : derive + confirm (Codex round 13 §2.2 + round 24 asset 분류).
# Figma frame 1927 px @ scale 0.49213 → 948 px adapted (full frame).
# Phase Z slide-body ≤ 585 px → adaptive content fit.
# Content density (3 col × 7 bullets + badge) → F29 (345) class 보다 가벼움.
# Derived = 290 (between F13=230 and F29=345). confirm via smoke + sample run.
# Content density 2nd refinement : badge raster 70 + bullet body ~210 + photo strip 36 + padding 30
# = 346 (F29 class). 안전 buffer 포함 = **320**. confirm via V2 MDX 02 validation.
visual_hints:
min_height_px: 290
min_height_px: 320
accepted_content_types:
- text_block

View File

@@ -7,46 +7,60 @@ Visual Provenance — figma_to_html_agent/blocks/1171281191/ (frame 14)
Frame 14 = "주체별 기대효과" (cards-3col-persona, 2601×1927 px, scale 0.49213).
3 컬럼 카드 : 발주자 / 시공자 / 설계자 각 persona 별 *목표* + bullet 7 개 안팎.
본 partial 은 `figma_to_html_agent/blocks/1171281191/index.html` (184 lines) 을
base 로 Phase Z 규약 (Jinja slot + token CSS + slide-base wrap + pure CSS) adapt.
base 로 Phase Z 규약 (Jinja slot + token CSS + slide-base wrap) adapt.
PROMOTED (Figma 색 / 디자인 의도 → CSS 로 보존) :
- 발주자 accent color : Figma TEXT #285b4a (dark green) — index.html L80
- 시공자 accent color : Figma TEXT #445a2f (olive) — index.html L81
- 설계자 accent color : Figma TEXT #743002 (red-brown) — index.html L82
- 발주자 overlay tint : #d6e7c4 (opacity-80) — index.html L45
- 시공자 overlay tint : #e1efe1 — index.html L46
- 설계자 overlay tint : #d0c0ad — index.html L47
- title gradient : #000 → #883700 (F13 zone-title 와 동일 family)
- badge 원형 의도 : index.html L60-69 (396×397 round badges) → CSS round
- bullet check 의도 : index.html L94 (32×32 check icon) → CSS ::before unicode
- section divider line : 카드 별 색 border 위계 (F13 pillar bar 답습)
본 commit (Track A 1/16 second refinement, F2 re-do post-Codex round 22 catch) :
asset-promotion policy = per-asset case-by-case (round 13 §2.3 + round 24 합의).
strict CSS-only over-generalization (first refinement `2fcd8bb`) 정정.
NOT PROMOTED (Figma 데코지만 MDX 무관 — memory rule `feedback_blocks_must_be_css.md`
+ 기존 F13/F29/F16 partials 의 strict CSS-only convention) :
- col_bg_texture.png (3 컬럼 BG 텍스처) — index.html L113-115
- col-overlay raster (실제 데이터 = solid 색) index.html L44-47 → CSS 단색
- 하단 사진 (photo--left/mid/right) 3 개 — index.html L122-123, 177-178
- 원형 뱃지 outer / inner image (badge-outer/inner) — index.html L62-69 → CSS round
- 체크 아이콘 image (bullet-icon img) — index.html L94-99 → CSS unicode
PROMOTED — RASTER (Phase Z `copy_assets()` 가 figma_to_html_agent/blocks/1171281191/
assets/ → {run_dir}/assets/three_persona_benefits/ 복사) :
- col_bg_texture (4a17cd1d...png) — 3 컬럼 BG 텍스처. CSS noise/gradient 근사 보다 fidelity 보존.
- bottom photo client (d2c070f2...png)발주자 카드 하단 사진
- bottom photo constructor (2a6a58e7...png) — 시공자 카드 하단 사진
- bottom photo designer (39113493...png) — 설계자 카드 하단 사진
- badge outer client (77f31997...png) — 발주자 round badge outer ring
- badge inner client (e64c967d...png) — 발주자 round badge inner disk
- badge outer constructor (1550ec75...png)
- badge inner constructor (85beaf9d...png)
- badge outer designer (9ac089fa...png)
- badge inner designer (4b534ccf...png)
→ 10 raster (1 BG + 3 photos + 6 badge). Codex round 24 tightening : badge =
default promote (asset-capable path 검증).
PROMOTED — CSS (Figma 색/디자인 의도 → CSS 으로 충분) :
- 발주자 accent color : Figma TEXT #285b4a (dark green) — index.html L80
- 시공자 accent color : Figma TEXT #445a2f (olive) — index.html L81
- 설계자 accent color : Figma TEXT #743002 (red-brown) — index.html L82
- col-overlay tint : #d6e7c4 / #e1efe1 / #d0c0ad (opacity-80 — solid 색, CSS equivalent)
- title gradient : #000 → #883700 (F13 zone-title 와 동일 family)
- bullet check marker : ✓ unicode (실제 png 6896d5c2 와 동등)
NOT PROMOTED (MDX 무관 + Figma 데코) :
- 한자/장식 텍스트 (Figma 안 deco) — index.html 에도 없음. N/A.
ADAPTED :
- Figma 65/50/40px → token-fixed (zone-title 13 / sub-title 12 / body 11)
- 396×397 absolute-positioned round badge → flex card-header 안 CSS round
- Figma `zoom: 0.49213` + absolute positioning → Phase Z flex/grid (zone fit)
- 7 bullets vertical absolute layout → flex column 안 자동 spacing
- Figma `line-height: 85/50px` → token-fixed `--lh-body`
- Figma 65/50/40px → token-fixed (zone-title 13 / sub-title 12 / caption / body 11)
- Figma 396×397 absolute round badge → flex card-header 안 round image
- Figma `zoom: 0.49213` absolute positioning → Phase Z flex column (zone fit)
- Figma 7 bullets absolute layout → flex column gap auto spacing
─────────────────────────────────────────────────────────────────────────────
min_height_px derivation (Codex round 13 §2.2 — derive + confirm) :
Figma frame total height = 1927 px @ scale 0.49213 → 948 px adapted
But Phase Z slide-body height ≤ 585 px → adaptive content fit
Content density : 3 col × 7 bullets + badge → similar to F29 (345) class
Initial derived value = 290 (between F13=230 and F29=345)
→ confirm via smoke + sample run before bulk activation (Track A calibration)
min_height_px derivation (round 13 §2.2 — derive + confirm) :
Figma frame 1927 px @ scale 0.49213 → 948 px adapted (full Figma fit).
Phase Z slide-body ≤ 585 px → adaptive content fit.
Content density (3 col × 7 bullets + badge + photo) → F29 (345) 보다 약간 ↑.
Derived = 320 (badge 30 + bullet body ~210 + photo strip ~50 + padding 30).
Confirm via smoke + V2 validation (MDX 02 sample run).
─────────────────────────────────────────────────────────────────────────────
slots : title, personas[].{label, body, color_class}
- color_class ∈ {client, constructor, designer} (role_order 따라)
- body = list[{text:str, indent:int}] (parse_quadrant_item 출력)
- persona suffix "목표" = frame 의 visual 언어 (slot 미사용, partial 내부 노출)
- persona suffix "목표" = frame 의 visual 언어 (slot 미사용, partial 노출)
─────────────────────────────────────────────────────────────────────────────
Asset path runtime resolution :
Phase Z `copy_assets(template_id, run_dir)` (phase_z2_pipeline.py:746) copies
figma_to_html_agent/blocks/1171281191/assets/ → {run_dir}/assets/three_persona_benefits/
→ partial 의 src="assets/three_persona_benefits/<filename>.png" 가 runtime 에서 resolve.
#}
<style>
@@ -65,7 +79,7 @@ slots : title, personas[].{label, body, color_class}
-webkit-background-clip: text; background-clip: text;
color: transparent;
flex-shrink: 0;
filter: drop-shadow(0 0 3px rgba(50,44,30,0.4)); /* index.html L41-44 의 title 처리 답습 */
filter: drop-shadow(0 0 3px rgba(50,44,30,0.4));
}
.f14b__cols {
display: grid;
@@ -81,55 +95,75 @@ slots : title, personas[].{label, body, color_class}
border-radius: 12px;
overflow: hidden;
min-height: 0;
isolation: isolate;
}
/* per-persona color theme : Figma TEXT 색 + overlay tint promote */
.f14b__col--client {
border-color: #285b4a; /* PROMOTED */
background: linear-gradient(180deg, rgba(214,231,196,0.5) 0%, rgba(214,231,196,0.15) 100%); /* PROMOTED L45 */
/* col-bg 텍스처 (PROMOTED raster) */
.f14b__col::before {
content: ""; position: absolute; inset: 0;
background: url("assets/three_persona_benefits/4a17cd1dddaba8a220b706df3ec052d2bfde4f47.png") center/cover no-repeat;
opacity: 0.6;
z-index: -2;
}
.f14b__col--constructor {
border-color: #445a2f; /* PROMOTED */
background: linear-gradient(180deg, rgba(225,239,225,0.5) 0%, rgba(225,239,225,0.15) 100%); /* PROMOTED L46 */
}
.f14b__col--designer {
border-color: #743002; /* PROMOTED */
background: linear-gradient(180deg, rgba(208,192,173,0.5) 0%, rgba(208,192,173,0.15) 100%); /* PROMOTED L47 */
/* col-overlay (CSS solid tint per persona — index.html L44-47) */
.f14b__col::after {
content: ""; position: absolute; inset: 0;
opacity: 0.8;
z-index: -1;
}
.f14b__col--client { border-color: #285b4a; }
.f14b__col--constructor { border-color: #445a2f; }
.f14b__col--designer { border-color: #743002; }
.f14b__col--client::after { background: #d6e7c4; } /* PROMOTED CSS */
.f14b__col--constructor::after { background: #e1efe1; } /* PROMOTED CSS */
.f14b__col--designer::after { background: #d0c0ad; } /* PROMOTED CSS */
/* badge — index.html L60-82 의 round badge (396×397 px outer + 302×303 inner + 65/50px text)
adapt : flex header 안 CSS round shape + persona name + "목표" sub */
/* badge — PROMOTED raster (outer + inner ring image, name + 목표 text overlay) */
.f14b__badge {
position: relative;
display: flex; flex-direction: column;
align-items: center; justify-content: center;
padding: 8px 10px;
border-bottom: 2px solid currentColor;
padding: 6px;
flex-shrink: 0;
text-align: center;
background: rgba(255,255,255,0.6);
aspect-ratio: 1 / 1;
width: 70px; height: 70px;
margin: 6px auto 4px;
}
.f14b__col--client .f14b__badge { color: #285b4a; }
.f14b__col--constructor .f14b__badge { color: #445a2f; }
.f14b__col--designer .f14b__badge { color: #743002; }
.f14b__badge-name {
font-size: var(--font-sub-title);
font-weight: 700;
.f14b__badge-outer, .f14b__badge-inner {
position: absolute; inset: 0;
width: 100%; height: 100%;
object-fit: contain;
}
.f14b__badge-inner {
width: 78%; height: 78%;
top: 11%; left: 11%;
}
.f14b__badge-text {
position: relative; z-index: 1;
display: flex; flex-direction: column;
align-items: center;
text-align: center;
line-height: 1;
}
.f14b__badge-name {
font-size: var(--font-caption);
font-weight: 700;
letter-spacing: -0.04em;
}
.f14b__badge-suffix {
font-size: var(--font-caption);
font-size: 0.65rem;
font-weight: 500;
line-height: 1.2;
margin-top: 2px;
opacity: 0.85;
}
.f14b__col--client .f14b__badge-text { color: #285b4a; }
.f14b__col--constructor .f14b__badge-text { color: #445a2f; }
.f14b__col--designer .f14b__badge-text { color: #743002; }
/* body — index.html L102-104 의 bullet-list (7 bullets per persona) adapt :
flex column + check-style ::before. raster icon NOT PROMOTED. */
/* body — bullet list. check marker CSS (PROMOTED CSS, raster 6896d5c2 와 동등) */
.f14b__body {
flex: 1 1 auto;
overflow: hidden;
padding: 8px 10px 10px;
padding: 4px 10px;
color: #1a1a1a;
min-height: 0;
display: flex; flex-direction: column;
@@ -142,9 +176,8 @@ slots : title, personas[].{label, body, color_class}
position: relative;
padding-left: 14px;
}
/* CSS check marker — index.html L94-99 (bullet_check.png) 의 의도 보존 */
.f14b__body .text-line--bullet::before {
content: "\2713"; /* ✓ U+2713 — check mark */
content: "\2713";
position: absolute;
left: 0; top: 0;
font-weight: 700;
@@ -152,6 +185,21 @@ slots : title, personas[].{label, body, color_class}
.f14b__col--client .f14b__body .text-line--bullet::before { color: #285b4a; }
.f14b__col--constructor .f14b__body .text-line--bullet::before { color: #445a2f; }
.f14b__col--designer .f14b__body .text-line--bullet::before { color: #743002; }
/* bottom photo — PROMOTED raster (per persona 실사 사진, CSS 불가) */
.f14b__photo {
flex-shrink: 0;
width: 100%; height: 36px;
overflow: hidden;
border-radius: 0 0 8px 8px;
margin-top: 2px;
}
.f14b__photo img {
width: 100%; height: 100%;
object-fit: cover;
opacity: 0.7; /* index.html L53 — opacity 0.70 */
display: block;
}
</style>
<div class="f14b" data-frame-id="1171281191" data-template-id="three_persona_benefits">
@@ -159,15 +207,41 @@ slots : title, personas[].{label, body, color_class}
<div class="f14b__cols">
{% for persona in slot_payload.personas %}
<div class="f14b__col f14b__col--{{ persona.color_class }}">
{# badge — raster outer/inner + CSS text overlay #}
<div class="f14b__badge">
<span class="f14b__badge-name">{{ persona.label | safe }}</span>
<span class="f14b__badge-suffix">목표</span>
{% if persona.color_class == "client" %}
<img class="f14b__badge-outer" src="assets/three_persona_benefits/77f319979c880da34ff3d423fcd97827f636c01e.png" alt="">
<img class="f14b__badge-inner" src="assets/three_persona_benefits/e64c967dd00302bfbef6cfbcbb4f7a4db5d9d96c.png" alt="">
{% elif persona.color_class == "constructor" %}
<img class="f14b__badge-outer" src="assets/three_persona_benefits/1550ec755fa7922dcfc1c90135a570d6b9df82cc.png" alt="">
<img class="f14b__badge-inner" src="assets/three_persona_benefits/85beaf9dfc17b7ed4620729a086ba22143606517.png" alt="">
{% elif persona.color_class == "designer" %}
<img class="f14b__badge-outer" src="assets/three_persona_benefits/9ac089fa9c5106b6b26d47727003641bb56ba4b0.png" alt="">
<img class="f14b__badge-inner" src="assets/three_persona_benefits/4b534ccf4e945fbe7436a0d8d96a6deffcfe5cef.png" alt="">
{% endif %}
<div class="f14b__badge-text">
<span class="f14b__badge-name">{{ persona.label | safe }}</span>
<span class="f14b__badge-suffix">목표</span>
</div>
</div>
{# body — bullets with CSS check marker #}
<div class="f14b__body">
{% if persona.body %}
{% for line in persona.body %}<div class="text-line text-line--bullet{% if line.indent > 0 %} text-line--indent-{{ line.indent }}{% endif %}">{{ line.text | safe }}</div>{% endfor %}
{% endif %}
</div>
{# bottom photo — per persona 실사 사진 #}
<div class="f14b__photo">
{% if persona.color_class == "client" %}
<img src="assets/three_persona_benefits/d2c070f200af83f563976b6b0f309d38321d204d.png" alt="">
{% elif persona.color_class == "constructor" %}
<img src="assets/three_persona_benefits/2a6a58e7bf7a645b5ede65115feb2890ccc414d1.png" alt="">
{% elif persona.color_class == "designer" %}
<img src="assets/three_persona_benefits/39113493f6e3ae76d766e86e293b0f0dcbf55d91.png" alt="">
{% endif %}
</div>
</div>
{% endfor %}
</div>