# W21B-Wave-4 — Ultra-Minimal T2I FP: Micro-Spec & Placement Canary

> **Status:** doc-only draft (Claude 초안 → Codex cross-review 대기). 코드/API/image/DB 변경 0.
> **Date:** 2026-05-29
> **Owners:** Claude(구현·runner·canary) / Codex(방향·cross-review·guard)
> **Predecessor context:** `finding-fp-background-fidelity-gap`, `next-session-w21b-w4-minimal-t2i-fp-restart`

---

## 0. 한 줄 요약

기존 FP 접근(상세 도면을 T2I/SVG로 정확히 그리기)은 반복 실패했다. 이 wave는 FP의 **디테일이 아니라 역할(job)** 을 낮춘다: FP는 더 이상 "BG가 1:1로 맞춰야 하는 layout SOT"가 아니라, **중요 요소 소수(≤3)를 번호 붙은 단순 도형으로 표시한 느슨한 staging 힌트 / 번호 legend** 다. 큰 brief 전에, "T2I가 번호 붙은 단순 도형 ≤3개를 사람이 보기에 대략 맞는 상대 위치로 배치할 수 있는가?"를 **tiny placement canary 1회**로 먼저 검증한다.

---

## 1. 동기 (왜 또 방향을 바꾸나)

- **사용자 결정**: Path-S(LLM 좌표 emit → 코드 SVG 렌더) 거부 — "전혀 말이 안돼, 이미 실패한거야, 절대 LLM이 아직 못그려." Path-V/Hybrid 포함 좌표-SOT 라인 전면 보류.
- **핵심 진단(Claude+Codex 합의)**: 공간 배치(spatial placement) 문제는 사라지지 않고 담당자만 LLM→T2I로 **이동**할 뿐이다. T2I는 정밀 배치에서 추론 LLM보다 **더 약하다**. 따라서 minimal로 가더라도 "데코 drift"는 줄지만 "레이아웃 drift"는 자동으로 사라지지 않는다.
- **결론**: 진짜 레버는 디테일을 낮추는 게 아니라 **FP의 역할을 낮추고 + 판정 기준을 함께 재정의**하는 것. 이걸 acceptance/non-goal에 못 박지 않으면 같은 루프가 반복된다.

---

## 2. FP Contract 재정의 (이 wave의 정체성)

| | 기존(v6/v7/v8) | 새 ultra-minimal |
|---|---|---|
| FP의 역할 | BG가 충실히 재현할 layout 도면 / 좌표 SOT | 중요 요소 ≤3의 **느슨한 staging 힌트 + 번호 legend** |
| 정밀도 요구 | 가구/방/스케일까지 정확 | 번호 누락 없음 + 상대 위치가 "대략" 읽히면 OK |
| BG와의 관계 | 1:1 layout match 기대 | match 기대하지 않음 (coarse 힌트로만 소비) |
| 판정 방식 | FP PNG ↔ BG 사진을 나란히 "맞나" | **이 비교 자체를 폐기** |

> **★ 사용자 원래 불만("fp와 배경이 전혀 안 맞는다")의 판정 기준이 바뀐다.** 추상 도형 FP를 사진풍 BG와 1:1로 "맞나" 보는 것은 이 contract에서 **무의미**하다 — non-goal로 명시(§5).

---

## 3. Micro-Spec: Placement Canary

### 3.1 목적

단 하나의 질문에 답한다:

> **T2I가 실내/갇힌 공간에서 번호 붙은 단순 도형 ≤3개를, 사람이 보기에 대략 맞는 상대 위치로 배치할 수 있는가?**

### 3.2 Scope (엄격 제한) — Codex 합의 lock

- **대상 선정 (자동, 기존 신호만)**: active FP 풀에서 **indoor/confined 후보만** 필터.
  - 기준: ① selected-shot이 **실제 참조**하는 FP + ② base/fixture 요소 **≥3개** + ③ open exterior/site **제외**.
  - **2곳**: 1곳은 가장 단순한 confined/interior, 1곳은 더 복잡한 indoor/confined.
  - 특정 literal(L05/L09 등)은 **runner 입력/리포트에만** 남기고 production rule엔 넣지 않음 (§7).
- **요소 수**: 공간당 **≤3개** (중요 요소만).
- **importance 신호**: **기존** `numbered_elements` / dossier / selected-shot 신호만 재사용. **새 importance ontology 만들지 않음.** (사용자: "복잡한 기초정보 만들지 말 것.")
- **image cap = 4**: **2 spaces × 2 variants** (Codex 권고 — stochastic T2I라 1장만 보면 우연 실패/성공 구분 불가).
- **부작용 0**: 출력은 **`/tmp` artifact만**. DB / checkpoint / ImageAsset write / commit / push **변경 0**. 기존 active FP/BG row 미접촉.
- **flow**: production `floor_plan_render` force가 **아니라** isolated `/tmp` runner로 별도 minimal prompt 호출. production step에 새 wiring 삽입하지 않음.

### 3.3 Prompt 제약 (ultra-minimal) — Codex 합의 lock

**고정 prompt 골격** (요소 수 N은 공간별 치환):

```
flat white top-down schematic, exactly N simple shapes,
one shape per numbered element, number printed inside shape,
no other text, no furniture drawing, no room details,
no perspective, no shadows, no decoration,
keep shapes separated, rough relative positions only.
```

**요소 표기**: label prose를 길게 넣지 말고 **최소 관계어 + rank palette**만:

```
#1 red rectangle: entry-side
#2 blue rectangle: center
#3 green line: far-side
```

- **색 사용 O** (사용자 언급 + 번호 OCR/시각 구분 도움). 단 **의미색 금지**, **rank palette 고정** (1=red, 2=blue, 3=green). 흑백 fallback은 색이 문제를 만들 때만.
- 요소당 1도형 1번호.

> **금지**: 풍부한 도면 묘사, 가구 디테일, 방 여러 개 invent, 긴 prose, 원근. (생성물이 v6/v7류 상세 도면으로 회귀하면 그 자체가 spec 위반.)

### 3.4 Pass / Fail 판정 (낮춘 기준 + 기계화) — Codex 합의 lock

variant 단위 판정. 세 층:

1. **number correctness — hard gate**: 번호 N개가 누락/중복/문자깨짐 없이 도형 안에 정확히. 위반 시 즉시 **FAIL**.
2. **decoration/perspective regression — hard fail**: 데코·가구 디테일·원근·그림자 회귀 시 **FAIL** (v6/v7 도면 회귀 방지).
3. **relative position — human review**: 도형들의 상대 위치가 사람이 보기에 staging 힌트로 읽히는가. (BG exact match 요구 안 함.)

**공간 단위 집계** (2 variant/공간):

- 공간 PASS = 최소 **1 variant**가 1·2·3 모두 통과.
- 공간 FAIL = **둘 다** fail.
- **1/2 mixed** = prompt tighten 후 **1회 재시도** 후보.

**판정 주체**: 1·2는 mechanical(번호/회귀), 3과 최종은 사람(사용자) visual review. (이 영역은 deterministic 아님 — TDD/pass-count로 완성도 주장 금지. §7.)

### 3.5 분기 (canary 결과에 따라)

- **PASS** → §A/B/C/D 본 설계로 진행 (importance filter / minimal prompt pack / consume contract / optional VLM sanity audit).
- **FAIL** → 답은 "요소를 무한히 더 줄이기"가 **아니다**. **text-only staging hint path**를 동등 후보로 올린다: FP 이미지를 **아예 그리지 않고**, 중요 요소 목록/번호 legend를 **텍스트로 BG prompt에 직접** 먹인다. (※ Path-S 재추진 아님 — 렌더 자체를 빼는 별개 선택지.)

---

## 4. 본 설계 스케치 (PASS 시에만, 합의 후 상세화)

- **A. importance filter**: selected-shot 기준 어떤 공간에 FP가 필요한지 + 어떤 요소 ≤N만 남길지 (기존 신호 재사용, 새 ontology 금지).
- **B. minimal FP prompt pack**: shape vocabulary / 색 / 번호-only / no-decoration. 새 버전 디렉토리(덮어쓰기 금지).
- **C. consume contract**: BG generation이 minimal FP를 **어떻게** 읽는지 명시 — "번호 N = 어떤 요소가 대략 어디" coarse 힌트로만. (안 읽으면 정확도 무의미.)
- **D. (optional) VLM sanity audit**: SOT 아님 — 번호/도형 누락 sanity check 정도. 처음부터 gate로 올리지 않음.

---

## 5. Non-Goals (명시)

- ❌ FP를 BG가 1:1로 재현할 layout SOT로 취급.
- ❌ FP PNG ↔ BG 사진 exact layout match를 acceptance로 삼기.
- ❌ LLM/코드 좌표 SOT(Path-S/V/Hybrid) 재추진.
- ❌ 새 importance ontology / 풍부한 기초정보 / 상세 도면.
- ❌ 실외 일반 공간(실내/갇힌 공간만).

---

## 6. 실행 순서 (합의됨) — 2-phase runner

1. **이 micro-spec doc-only 확정** (Claude 초안 → Codex cross-review → 합의). ✅
2. **tiny placement canary — 2-phase** (실행=Claude, 이중실행 방지, Codex cross-review):
   - **Phase 1 — dry 선정 (image 0, spend 0)**: 대상 자동 선정만. `/tmp` JSON + 짧은 HTML. DB/checkpoint/ImageAsset write 0. 산출물 §6.1.
   - **Phase 2 — image (cap 4, 실제 spend)**: **사용자 확인 후에만**. `render_one_floor_plan` 직접 호출하되 production step/manifest/ImageAsset/session 미경유 isolated client. 결과 HTML 산출물 §6.2.
3. **결과로 분기 선택**: minimal T2I FP 계속 vs text-only staging hint (§3.5).
4. **본 설계**(§4 또는 text-only path)는 그 다음, 별도 brief.

> 비용(API/image)·DB·canary·commit·push는 각 단계 전 **합의 후** 진행.

### 6.1 Phase 1 (dry) 산출물 — Codex 합의 lock

`/tmp` JSON + 짧은 HTML, write 0. 최소 포함:

- **selected fp_id 2개** + 선정 이유 (simple/confined vs complex/confined).
- **제외된 후보 + 제외 이유** (exterior/site / selected-shot 참조 없음 / base·fixture < 3 등).
- 각 fp의 **chosen elements ≤3개**: 번호 / 짧은 label / relative hint / rank color.
- 각 fp의 **최종 prompt preview** (§3.3 골격 치환 결과).

### 6.2 Phase 2 (image) 결과 HTML — Codex 합의 lock

variant별로:

- **number hard gate** 통과 여부 (번호 누락/중복/깨짐).
- **decoration/perspective hard fail** 여부.
- **relative placement note** (human review용).
- 생성 PNG 4장 + 공간 단위 집계(§3.4).

---

## 6.3 활성 FP 풀 eligibility (★ filesystem manifest SOT, dry runner 직접계산 = ground truth)

> **★★ 검증 신뢰성 경고 (이 세션 4회 추정-오류 → 교훈)**: 이 표는 `/tmp/w21b_wave4_minimal_fp_dry.py`가 bare manifest를 **직접 계산해 통과한** 값만 적는다. 폐기된 잘못된 신호들(전부 추정이었음): ① DB `checkpoints` 테이블(존재 안 함) ② `plan.floor_plans[].scope`(enum 아니라 **산문 설명**) ③ `diagnostics.dropped_floor_plans`(active 8과 **무관한** 다른 fp: l10/l12/l15) ④ `dossier.dwelling_identity.scope_label`/`dwelling_kind`(**그런 필드 없음**). **유일하게 검증된 indoor 신호 = `backgrounds[].surface_role` (bg.depends_on_fp로 fp 귀속)**.

**`source_files` (SOT, 전부 bare manifest)** — `projects/4f948193…/checkpoints/episodes/dc70c0b3…/`:
- `floor_plan_prompt/manifest.json` → `data.floor_plans{}` (numbered_elements)
- `base_location_dossier/manifest.json` → `data.dossiers{}` (base_marker_inventory)
- `background_master_plan/manifest.json` → `data.plans{}.plan.backgrounds[].surface_role` + `.depends_on_fp`

**활성 FP 8종** — `dominant surface_role` = bg.depends_on_fp 경유:

| fp_id | numbered_elem | base_marker_inv | dominant surface_role | eligible |
|---|---|---|---|---|
| fp_l04_rooftop | 15 | 8 | exterior_plate | ❌ |
| fp_l04_stairs | 15 | 12 | transition_zone | ❌ |
| fp_l05_main | 35 | 22 | interior_room | ✅ |
| fp_l09_main | 12 | 12 | interior_room | ✅ |
| fp_l13_police_office | 18 | 14 | interior_room | ✅ |
| fp_l14_main | 12 | 12 | interior_room | ✅ |
| fp_l19_deck | 7 | 5 | exterior_plate | ❌ |
| fp_l20_wheelhouse | 9 | 7 | interior_room | ✅ |

- **선정 규칙 (deterministic, no-substring)**: eligible = `dominant surface_role == interior_room` → **l05/l09/l13/l14/l20**. complexity = numbered_elements. **simple = min(→ l20_wheelhouse ne9)**, **complex = max(→ l05_main ne35)**.
- **element 발췌 = structural-zone-first** (base_structural_unit zone을 number순 → 부족분 fixture/opening/furniture). canary 2 target은 Codex 합의 **operator_override**(runner-input only, NOT production rule) 적용; rule 결과는 alternate 병기:
  - l05_main: #2 shared living / #4 private sleeping / #5 bathroom service zone (alt rule: #1 threshold/#2 living/#3 kitchenette)
  - l20_wheelhouse: #1 cabin / #3 helm console / #4 forward windshield (alt rule: #1 cabin/#2 standing/#3 helm)

## 6.4 relative hint 소스 — Codex 합의 lock = (a)+(b) 둘 다

요소 **정밀 좌표 없음** (position_hint 자유텍스트만; 정량 좌표 아님). canary는 두 variant를 **둘 다** 돌려 실패 원인 분리:

- **variant A = `no_coordinate_spread`**: 좌표/zone 미제공. T2I **자체 공간 추론** 테스트.
- **variant B = `ordinal_zone_instruction`**: rank order로만 zone 배정 (1=left/2=center/3=right). T2I **명시 placement 지시-따르기** 테스트.

**cap 4**: 공간 2 × (A 1 + B 1). **해석 rubric**: A pass→자체 staging 가능(강한 긍정) / A fail·B pass→지시는 따르나 공간추론 못함→다음 과제="zone 어디서 얻나" / 둘 다 fail→minimal FP 접고 text-only staging hint 우선(특히 **B fail = 결정적**).

**가드**: ① B의 left/center/right는 **ground truth 아님 = 실험용 임의 instruction**, production 승격 금지 ② zone은 **rank order로만** 배정(label substring 매핑 안 함) ③ runner output에 `variant_type` + prompt preview + selected list + rubric 기록.

## 7. 가드레일 (프로젝트 표준 규칙)

- **scenario-leakage 금지**: L05/옥탑방/특정 marker 번호·문구를 prompt/코드/판정 logic에 넣지 않는다. generic contract term만. canary 대상 공간은 active 풀에서 동적으로 선택.
- **글자 단위 패턴 금지**: substring/조사/lexicon literal로 의미 추출·판정 금지.
- **TDD 한계**: T2I PNG 결과는 deterministic 아님 — pass-count로 완성도 주장 금지. 완성도 = 여러 경우 실험 반복 + 사용자/Codex visual review.
- **inert 보존**: v7 production diff / v8 layout-plan·semantic 모듈은 삭제하지 않고 보류(새 방향에 안 씀). W21B-w3(bee59d7+c4c890e) push 보류 유지.

---

## 8. 열린 결정 — Codex 합의 완료 (2026-05-29)

1. **대상 선정**: active indoor/confined 풀에서 selected-shot 참조 FP + base/fixture ≥3 + exterior/site 제외 → 단순 1 + 복잡 1. literal은 runner/report만. → §3.2 반영.
2. **variation**: 2 spaces × 2 variants = **cap 4**. → §3.2 반영.
3. **prompt 문구**: 고정 골격 lock. → §3.3 반영.
4. **색**: 사용 O, rank palette 고정(1 red/2 blue/3 green), 의미색 금지, 흑백 fallback. → §3.3 반영.

**추가 합의(Codex)**: pass/fail 기계화(§3.4 — number hard gate / decoration-perspective hard fail / position human) + canary는 production force 아닌 isolated `/tmp` runner, write 0 재명시(§3.2).
