# v1 에서 산문 **소비와 검수**를 끊는다 — 1단계

> ★**제목을 좁혔다** (2026-08-27 Codex BLOCK-1). 처음엔 「안 쓸 산문을
> 안 만든다」로 썼는데 **거짓이다** — 이 판은 `detail_steps.py` 도
> scene_detail schema 도 한 줄 안 바꿨다. **큰 t2i 산문 저작은 그대로
> 돈이 나간다.** 줄인 것은 그 산문의 **소비**(dependency 입력)와
> **검수**(t2i_review 씬 갈래)뿐이다. 실제 lean 저작은 다음 판이다.

감사 3-C 의 품질 갈래는 [닫았다](2026-08-27-audit-3c-closed.md) — 6샷
5축 대조에서 유실 증거가 0 이고 최종 프롬프트가 오히려 더 자세하다.
살아 있는 문제는 **비용**이다: `scene_detail` 이 v1 이 안 쓸 t2i 산문을
저작하고, 그 산문을 검수하는 데까지 돈을 쓴다.

## 무료 실측 넷 (구현 전)

    ① `_shot_ve` fallback     49판 3,737샷에서 **0건**
                              (키 없음 0 · 빈 목록 260은 정상 SOT)
    ② outfit 배정             3,446건 **전부 허용 set 안**, 위반 0
                              phase3=허용 후보 SOT · detail=샷별 선택 SOT
    ③ dependency 의 고유 정보  **프레이밍** — description 에는 없다
                              그런데 `shot_staging` 에 typed 로 있다
    ④ t2i_review              `mutation=0`, 최근 창 12콜에서 씬 갈래 76%

★②는 처음에 「불일치 8건」으로 쟀는데 **측정이 틀렸다.** 계약이
 「한 씬에 인물당 outlook **최대 2개**」인데 단일 dict 로 받아 두 번째
 값을 덮어썼다. multimap 으로 다시 재니 위반 0 이고, 둘 이상 허용된
 (씬,인물) 조합이 실제로 21개 있다. → `outfit_assignments` 는 **남긴다.**

## 이 판이 하는 것

### ① dependency 입력을 typed 프레이밍으로 — **v1 에서만**

그 스텝의 1순위 기준은 **카메라 이동/확대 연속** — 「같은 순간의 같은
공간·같은 피사체를 다른 프레이밍으로」(팩 system 문안). `description` 은
순간·인물·소품만 말하고 **프레이밍을 안 말한다.**

종전에는 그 재료를 `scene_detail.t2i_variations[0].t2i_prompt`
(호출당 832~1,160자 산문)로 받았다. 같은 정보가 `shot_staging` 에
**typed 로** 있다 — `camera_direction` 은 최종 이미지 프롬프트의
`CAMERA` 절에 들어가는 바로 그 문장이고 `framing_scale` 은 enum SOT 다.

    Description: <shot_validator description>
    Framing:     <framing_scale> / <camera_direction>

싣는 것은 **둘뿐**이다 — `perspective` 나 staging 전문은 안 싣는다.

★그 스텝은 `shot_staging` CP 를 이미 읽었지만 **LLM 뒤**에만 썼다
 (`_build_framing_map`, close 정책). 이 판이 그것을 **LLM 입력**으로
 쓰는 첫 자리라 `depends_on` 에 `shot_staging` 을 명시했다.

★**legacy(off)는 종전 그대로다** — 그 경로는 `t2i_variations` 를 이미지에
 실제로 쓴다(`scene_image_service.py:397-404` 의 v1 분기 반대편).
 v1 6샷 실측으로 legacy 입력까지 바꿀 근거가 없다 (Codex BLOCK-1).

★**프레이밍이 없으면 유료 호출 전에 선다.** 이 판은 그것을 핵심 대체
 재료로 삼으므로, 비었는데 그대로 부르면 1순위 기준을 **재료 없이**
 판단하게 된다 — 조용히 나빠지고 돈은 나간다. 종전 enum 검증
 (`_build_framing_map`)은 **호출 뒤** close 정책에만 있었다
 (Codex BLOCK-4). 이제 검증된 map 을 한 번만 만들어 LLM 입력과
 close 후처리가 같은 것을 쓴다.

### ② t2i_review 의 씬 갈래를 v1 에서 생략

그 갈래는 `scene_detail` 의 t2i 산문을 검수하는데 v1 은 그 산문을
이미지에 안 쓴다. `mutation=0` 이고 산출을 읽는 소비자도 없다.

★**「검사했는데 결함 0」으로 읽히면 안 된다.** `scene_fixes=0` 만 남기면
 건너뛴 것이 **깨끗한 것**으로 오독된다. 그래서 반환에
 `scene_review_status` 를 넣는다 — `"skipped_v1_typed_sot"` / `"ran"`.

★**그 신원이 체크포인트까지 가야 한다.** 모듈이 돌려줘도
 `T2iReviewStep._execute` 가 버리면 CP 에는 `scene_detected=0` 만 남아
 정확히 막으려던 오독이 난다 — step 반환에도 싣는다 (Codex BLOCK-2).

되돌릴 손잡이: `t2i_review_skip_scene_on_v1=False`.
legacy(`still_recipe_mode="off"`)는 **그대로 돈다** — 그 경로는 t2i
산문을 실제로 쓴다.

## 무료 검증 (이미지 0장 · LLM 0콜)

골목 끝 6샷의 dependency LLM 입력을 옛/lean 으로 조립해 나란히 쟀다.

    S1sh1   951자 → 551자 (-42%)      S2sh3  1,042자 → 432자 (-59%)
    S1sh2   925자 → 513자 (-45%)      S3sh1  1,040자 → 467자 (-55%)
    S2sh1 1,046자 → 495자 (-53%)      S3sh2    946자 → 433자 (-54%)

    합계  5,950자 → 2,891자  (**-51%**)

**6/6 샷에 프레이밍이 있다** — 결손 0. 즉 재료가 사라진 것이 아니라
같은 것을 typed 로 받는다.

### ③ 두 계약을 지문에 접는다

입력 계약이 바뀌었는데 지문이 안 움직이면 **옛 CP 가 새 계약인 것처럼**
current 로 읽혀 조용히 skip 된다 (Codex BLOCK-3).

    shot_dependency_t2i  v1 이면 `llm_input_contract="v1_typed_framing"`
    t2i_review           v1+skip 이면 `review_scope="entity_only_v1"`

둘 다 **legacy 에서는 칸을 안 만든다** — byte-identical. 실측으로 v1 과
off 의 값이 갈리는 것을 확인했다. 완주판은 mismatch BLOCK 으로 서게
두고 force 하지 않는다.

## 아직 안 한 것

참조 edge 가 실제로 달라지는지는 **LLM 호출이 있어야** 안다. 순서는
Codex 확정대로다: 옛/lean 을 같은 6샷에 태워 dependency 결과를 비교하고,
**edge 가 달라진 샷만** 소수 실제 A/B + 사람 눈. 같으면 이미지 재생성은
하지 않는다. 완주판 force 금지, 새 주행·canary 에만 적용.

주행 단위 절대 절감량도 구현 canary 뒤에 잰다 — ④의 76%는 「최근 창
12콜에서 관찰」로만 남긴다.
