---
title: C9 — scene_detail Rule B body-light verb forbidden list 추상화 (generic semantic principle) v1 (fix-critical-1 Tier β #6)
date: 2026-05-20+
status: closed (C9 v1 — W1-W2 atomic execution 완료, Codex W1 per-wave APPROVED_FOR_W2_ENTRY. W1 commit 04016da. §10 Closure 참조.)
roadmap_ref: docs/superpowers/specs/2026-05-16-track-b-semantic-debt-roadmap-design.md (Track B §5.16 신설 예정 — C9) + docs/fix-critical-1/index.html C9 section (locked persistent guide)
fix_critical_doc_ref: docs/fix-critical-1/index.html C9 section (lines 968-1004)
audit_ref: docs/visual-reliability-audit/2026-05-14-semantic-string-routing-debt-audit/03-llm-side-heuristic-audit.md L-7 (lines 127-132 — 신규 단독 finding) + §8 Fix 방향 line 190 + carry follow-up line 218
prerequisites:
  - C1 perception_mode enum SOT v1 / C2 owned_object_usage[] echo v1 / C3 detail_steps focus rewrite v1 / C7 shot_dependency_t2i subject_kind v1 / C6 detail_steps 색감-감정 closed-list 폐기 v1 — 모두 closed + pushed (origin/main HEAD `81bde22`). HEAD anchor. 변경 0.
non_supersedes:
  - C4 #8 background classifier / C5 #9 action segment SOT / C8 #7 잔여 sub-area split / C10 future structured carries.
---

# C9 — scene_detail Rule B body-light verb forbidden list 추상화 v1

## 1. Purpose + Scope

### 1.1 Purpose

**audit 03 L-7 (신규 단독 finding)**: `prompts/_base/scene_detail/<latest>/system.md` 의 `## 조명·색조가 신체를 변형하지 않게 (Rule B)` 섹션에 **빛/색이 신체에 작용하는 동사의 closed verb list** 와 **forbidden / recommended phrasing 예시 목록** 이 하드코딩되어 있다.

audit 03 L-7 (`03-llm-side-heuristic-audit.md:127-132`):

> **classifier instruction 요약**: 빛이 신체에 작용하는 closed verb list 5-entry — `cut / slice / split / carve / bisect` + body part 조합 금지 + explicit forbidden phrasing 예시. LLM 에게 forbidden phrase 분류 instruction.
> **4-tuple**: P2 / plausible / small / independent (low blast radius)

audit §8 Fix 방향 (line 190): *"pure LLM-side phrase reasoning hint — schema/code 변경 없이 prompt rewriting 으로 generic semantic instruction 으로 변환 가능 (v1 외 but small fix)."*

이 closed verb list 는 producer LLM (scene_detail) 에게 `cut / slice / split / carve / bisect` 라는 **닫힌 영문 토큰 집합** 을 금지 대상으로 주입한다. 문제는 LLM 이 정확히 그 5개 토큰만 좁게 회피하고 동급 동사 (`sever`, `cleave`, `gash`, `slash` 등) 는 자유롭게 쓸 수 있다는 것 — 닫힌 목록이 *전수* 라는 착각을 만든다. 추가로 `### 권장 표현` 의 positive verb list (`falls on / washes / reflects on / catches on / pools on / glows across`) 역시 동일한 닫힌 enumeration 이다. Track B **Prompt Closed-List Ban** 위반 (4-Gate, [[feedback_llm_based_judgment]]).

본 spec v1 = **Rule B 섹션의 closed verb list + forbidden 예시 + recommended 예시 + recommended verb list 4개 닫힌 enumeration 을 단일 generic semantic principle 로 치환** — "빛/색은 표면에 머무는 효과, 신체를 물리적으로 절단하듯 쓰지 마라" 라는 *의도* 는 보존하되, 닫힌 토큰 목록과 예문 목록을 제거한다. Codex C9 entry sanity (`APPROVED_FOR_SPEC_DRAFTING_WITH_NARROW_GUARDS`, 본 session) 의 narrow scope guard 흡수.

### 1.2 In-scope

- **scene_detail prompt-pack v31 신규 디렉토리 신설** — `prompts/_base/scene_detail/31.<W1_UTC>/` (`system.md` rewrite + `detail_schema.json` v30→v31 byte-identical sibling copy). CLAUDE.md "프롬프트 파일 덮어쓰기 금지 — 새 버전 디렉토리로 관리" 준수.
- **`system.md` Rule B 섹션 (`:74-96`) rewrite** — closed verb list (`:74` / `:95`) + forbidden 예시 5건 (`:78-82`) + recommended 예시 5건 (`:86-90`) + recommended verb list (`:94`) + sub-header 3개 (`### 절대 금지 표현` / `### 권장 표현` / `### 핵심 원칙`) 를 모두 제거하고, 단일 generic semantic principle 문단으로 치환. 섹션 header (`:70`) + WHY 문단 (`:72`) 보존.
- **version cascade 4-point sync** — `SCENE_DETAIL_PROMPT_VERSION` (detail_steps.py) + `MODULE_VERSIONS["scene_detail_composer"]` + `_MODULE_INFO["scene_detail_composer"]["prompt_dependency"]` + v31 디렉토리. (§3.4)
- **alignment / version test 5 file v30→v31 갱신** (W0 grep 스냅샷 14 assert/occurrence — 비권위, W1 `rg` enumerate 가 SSOT, occurrence coverage 완전성 의무) — `test_scene_detail_continuity_alignment.py` / `test_scene_detail_spatial_alignment.py` / `test_g4_5a_spatial_integration.py` / `test_prompt_versions.py` / `test_area_7b_no_drift.py`. (§2.3)
- **C9 regression test 신설** — v31 Rule B 섹션 path-scoped residue gate + version sync gate + boundary preserve gate. **W1 동일 commit**.
- **W2 closure docs atomic**.

### 1.3 Out-of-scope (Codex C9 entry sanity scope guard 흡수)

- **`detail_schema.json` 내용 변경 — 0**. C9 = prompt text only. schema field/enum/description 변경 없음. v31 `detail_schema.json` 은 v30 의 **byte-identical sibling copy** (sha256 동일 — G5). `SCENE_DETAIL_SCHEMA_VERSION` (현 12) + `STEP_MANIFEST["scene_detail"]["schema_version"]` (현 12) 모두 **변경 0**.
- **`detail_steps.py` instruction string / code logic — 0**. C9 는 `SCENE_DETAIL_PROMPT_VERSION` 상수 줄 (`:107`) 1줄 + 주석만 변경. `_analyze_one` 등 code 로직 불변. C6 가 변경한 `[조명/색감 판단]` block (`:2324-2328`) 과 무관 — 별 영역, 변경 0.
- **`step_manifest.py` scene_detail entry — schema_version 12 보존, prompt 버전 pin 없음 → 변경 0**.
- **WHY 문단 (`system.md:72`)**: "T2I 모델은 빛이 신체 부위를 자른다·관통한다·갈라낸다 같은 표현을 물리적 절단으로 해석한다 ..." — closed-list classifier 가 아니라 T2I 오해석 *rationale*. **보존** (byte-identical). C9 residue gate 대상 아님 (Codex OQ-C9-3).
- **boundary preserve (무관 context — 변경/grep-ban 대상 아님)**: `system.md:240` `## 실루엣 묘사 규칙 — 평면 cutout 금지` (실루엣 평면화 규칙, "cut" 부분 문자열 무관) + `system.md:439` `a slice of the windshield visible` (framing 예시, "slice" 무관). C9 residue gate 는 이 두 줄에 false-hit 하면 안 된다 — §3.3 path-scoped + 닫힌-form literal 검사.
- **scene_detail 의 다른 섹션 / 다른 prompt-pack stem / 다른 step 의 body-light 유사 instruction**: 본 area 미포함. audit 03 L-7 = scene_detail Rule B 단독 site.
- **VLM / live LLM / semantic regex**: 모두 ban ([[feedback_no_vlm_dependency]]).
- **C1·C2·C3·C6·C7 변경 사항 / C4·C5·C8·C10**: 별도 area.

## 2. W0 Evidence Inventory (live, HEAD `81bde22`)

### 2.1 Violation site — `prompts/_base/scene_detail/30.202605200303/system.md:70-96`

현재 active prompt-pack = **v30** (`30.202605200303`). fix-critical-1 index.html 의 `scene_detail/28...` 은 stale — C1 (v29) → C2 (v30) bump 이후 현재 v30. Rule B 섹션 (`:70-96`):

| 줄 | 내용 | 분류 |
|----|------|------|
| `:70` | `## 조명·색조가 신체를 변형하지 않게 (Rule B)` header | **보존** |
| `:72` | WHY — T2I 물리적 절단 오해석 rationale | **보존** (byte-identical, gate 대상 아님) |
| `:74` | "조명·색조 효과는 ... 표면 ... 동사(`"cut", "slice", "split", "carve", "bisect"`)를 신체 부위와 결합하지 마라" — **closed verb list (quoted-comma form)** | **C9 TARGET — 추상화** |
| `:76` | `### 절대 금지 표현 — 빛/색이 신체를 자르듯이` sub-header | **C9 TARGET — 제거** |
| `:78-82` | forbidden phrasing 예시 **5건** (`red light cuts across her eyes` / `crimson spill cuts across her features` / `a beam slicing through his face` / `colored light splits her cheek in half` / `shadow carving across the mouth`) | **C9 TARGET — 제거** |
| `:84` | `### 권장 표현 — 빛이 표면에 닿는 묘사` sub-header | **C9 TARGET — 제거** |
| `:86-90` | recommended phrasing 예시 5건 | **C9 TARGET — 제거** |
| `:92` | `### 핵심 원칙` sub-header | **C9 TARGET — 제거** |
| `:94` | recommended verb list (`falls on / washes / reflects on / catches on / pools on / glows across`) — **closed positive enumeration** | **C9 TARGET — 추상화** |
| `:95` | closed verb list 반복 (`cut / slice / split / carve / bisect` slash form) | **C9 TARGET — 제거** |
| `:96` | "색감의 강도는 형용사로 ... 동사로 신체를 자르지 마라" | **C9 TARGET — generic 문단에 흡수** |

- code/schema 소비자 0 (audit 03 L-7 "code dual-sync: 없음 — pure LLM-side"). 본 섹션은 producer prompt-pack 텍스트 only.
- 위반 = `:74-96` 23줄 블록. header `:70` + WHY `:72` 불변.

### 2.2 Stale source 정정 (4-layer source map)

| Layer | source | 명시 | 실제 (HEAD `81bde22`) |
|-------|--------|------|----------------------|
| L1 | `docs/fix-critical-1/index.html` C9 (`:982-984`) | `scene_detail/28.../system.md` | **v30 `30.202605200303`** (STALE — C1/C2 bump 미반영) |
| L1 | index.html C9 essence (`:971`) + step (`:995`) | "4 forbidden phrasing 예시" / `:78-81` | **실제 5건 `:78-82`** (STALE) |
| L2 | audit 03 L-7 (`:127`/`:129`) | `scene_detail/system.md:71-77`, "4 explicit forbidden phrasing 예시" | 실제 Rule B `:70-96`, forbidden 예시 **5건** (STALE — audit 작성 시점 v23 기준 추정) |
| L3 | code | 없음 (pure LLM-side) | — |
| L4 | schema | 없음 (Rule B 는 schema field 아님) | — |

W2 closure docs 에서 index.html C9 marker 갱신 시 실제 버전/줄번호/예시 개수로 정정 (false source mapping 방지).

### 2.3 Version cascade — 4-point sync + test surface

scene_detail prompt-pack 파일 (`system.md`) 편집 → CLAUDE.md "프롬프트 파일 덮어쓰기 금지" → v31 신규 디렉토리 의무. 4-point sync (production 4 site):

| # | site | 현재 (v30) | C9 목표 (v31) |
|---|------|-----------|--------------|
| 1 | `prompts/_base/scene_detail/<dir>/` | `30.202605200303/` | `31.<W1_UTC>/` 신규 (system.md rewrite + detail_schema.json sibling) |
| 2 | `backend/app/core/steps/detail_steps.py:107` `SCENE_DETAIL_PROMPT_VERSION` | `"30.202605200303"` | `"31.<W1_UTC>"` + 주석 |
| 3 | `backend/app/core/version_registry.py:34` `MODULE_VERSIONS["scene_detail_composer"]` | `"1.30.0"` | `"1.31.0"` + 주석 |
| 4 | `backend/app/core/version_registry.py:126` `_MODULE_INFO["scene_detail_composer"]["prompt_dependency"]` | `"scene_detail/v30"` | `"scene_detail/v31"` + 주석 |

`SCENE_DETAIL_SCHEMA_VERSION` + `STEP_MANIFEST["scene_detail"]["schema_version"]` (12) — **변경 0** (schema 무변경).

version-pin test = **5 file — 각 file 안 `v30` / `1.30.0` / `scene_detail/v30` / `startswith("30.")` / `NEW_DIR_EXPECTED ... "30."` occurrence 를 W1 에서 `rg -n` 으로 전수 grep enumerate 해 빠짐없이 갱신**. **아래 표의 assert 개수·줄번호는 W0 grep 스냅샷 — 권위 SSOT 아님. W1 은 표의 개수를 신뢰하지 말고 5 file 을 직접 재grep 해 모든 occurrence 를 확인·갱신해야 한다** (file 수가 아니라 occurrence coverage 완전성이 version cascade 누락 회귀 0 의 조건):

| test file | v30 assert occurrence (W0 grep 스냅샷 — 비권위) | C9 갱신 |
|-----------|----------------------------------------------|---------|
| `backend/tests/prompts/test_scene_detail_continuity_alignment.py` | (a) `:82` `MODULE_VERSIONS["scene_detail_composer"] == "1.30.0"` (b) `:120` `prompt_dependency == "scene_detail/v30"` (c) `:141` `SCENE_DETAIL_PROMPT_VERSION.startswith("30.")` — **3 assert** + module docstring (`:15-23`) + per-test docstring | `"1.31.0"` / `v31` / `"31."` + docstring |
| `backend/tests/prompts/test_scene_detail_spatial_alignment.py` | (a) `:85` `MODULE_VERSIONS["scene_detail_composer"] == "1.30.0"` (b) `:123` `prompt_dependency == "scene_detail/v30"` (c) `:148` `SCENE_DETAIL_PROMPT_VERSION.startswith("30.")` (d) `:153` `SCENE_DETAIL_PROMPT_VERSION.startswith("30.")` (**startswith assert 2회** — `:148` + `:153`) — **4 assert** + module docstring (`:15-23`) + per-test docstring | `"1.31.0"` / `v31` / `"31."` (×2) + docstring |
| `backend/tests/integration/test_g4_5a_spatial_integration.py` | `Test4WayAlignment` (`:558-614`): (a) `:593` `startswith("30.")` (b) `:600` `MODULE_VERSIONS == "1.30.0"` (c) `:609` `prompt_dependency == "scene_detail/v30"` + docstring `:562-568`. config_hash surrogate test (`:~754-778`): (d) `:771` `current.startswith("30.")` + message. — **4 assert / 2 test** | `"31."` / `"1.31.0"` / `v31` (4곳) |
| `backend/tests/test_prompt_versions.py` | (a) `:216` `SCENE_DETAIL_PROMPT_VERSION.startswith("30.")` (`test_detail_steps_constants_match`) (b) `:227` `MODULE_VERSIONS["scene_detail_composer"] == "1.30.0"` (`test_version_registry_scene_detail_composer_1_28_0`) — **2 assert** + history 주석 | `"31."` / `"1.31.0"`. 함수명 `..._1_28_0` 은 이미 stale (assert 는 `1.30.0`) — 본 v1 미개명, history 주석만 동기, rename out-of-scope |
| `backend/tests/integration/test_area_7b_no_drift.py` | `:50` `NEW_DIR_EXPECTED["scene_detail"] = "30."` (C1 latest-activation canary `test_c1_prompt_loader_latest_activation`) — **1 occurrence** + 주석 (`:47-49`) | `"31."` + 주석 |

W0 grep 스냅샷 assert 개수 = continuity 3 + spatial 4 + g4_5a 4 + prompt_versions 2 + area_7b_no_drift 1 = **14 assert/occurrence** (+ 각 file 의 module/per-test docstring·history 주석). **이 개수는 비권위 스냅샷** — W1 은 `rg -n` 으로 5 file 전수 재grep 해 모든 occurrence 를 직접 확인·갱신한다. 각 assert 의 stale 오류 메시지 (continuity/spatial 의 `!= '1.28.0'` / `!= 'scene_detail/v28'`, g4_5a 의 `not v29` / `1.28.0 → 1.30.0`, prompt_versions history 주석) 도 W1 에서 v31 기준 동기.

**자동 latest-pick 으로 v31 통과 (코드 변경 0, W1 regression 으로 verify)**: `test_scene_detail_id_policy_alignment.py` 는 `_list_module_versions("scene_detail")[0]` 로 latest 를 자동 픽 → v31 을 검사하나, (a) schema 검사는 v31 detail_schema.json = v30 byte-identical sibling 이라 통과, (b) `latest_major >= 13` → 31 통과, (c) system.md composite_id 검사는 Rule B 와 무관 섹션이라 통과.

**v31 신규 dir 에 영향 없음 (prefix/major pin)**: `test_area_7b_residue_gate.py` (scene_detail `"28."` prefix pin — historical) / `test_area_5_residue_gate.py` (`scene_detail/28.*` glob pin) / `test_c2_owned_object_usage_echo.py` (`_resolve_latest_major_pack("scene_detail","30")` major pin) / `test_c1_perception_mode_enum_sot.py` (`...,"29")` major pin) / `test_prompt_versions.py` v14/v19/v23 prefix-pin test / `test_scene_detail_v21_grep_gate.py` (v21 pin) — 모두 v31 미매치, 변경 0.

### 2.4 Rule B 콘텐츠 의존 test — 부재

`cut/slice/carve/bisect` 등 Rule B body-light verb literal 을 검사하는 production test 0건 (`test_visible_entities_validator.py` 의 carve/bisect 언급은 validator 자체 pattern carve-out — scene_detail prompt 무관). Rule B 섹션 텍스트 변경에 따른 기존 test 회귀 surface 없음. C9 residue gate 가 신규로 v31 Rule B 섹션을 path-scoped 검사한다.

## 3. OQ Resolution (Codex C9 entry sanity 흡수)

### 3.1 OQ-C9-1 — rewrite scope

**결정: 옵션 (c) narrow Rule B rewrite — `:74-96` 4개 닫힌 enumeration 모두 추상화.**

forbidden closed verb list (`:74`/`:95`) 만 좁게 제거하면 `:94` recommended verb list 가 닫힌 positive enumeration 으로 잔존해 같은 위반이 재발한다 (Codex C9 entry sanity 명시). 따라서 Rule B 섹션 (`:70-96`) 내부로 scope 를 한정하되, 실질 rewrite 대상 = `:74-96` 전체 — forbidden verb list + forbidden 예시 5건 + recommended 예시 5건 + recommended verb list + sub-header 3개. header `:70` + WHY `:72` 보존. 섹션 *밖* (`:240` cutout / `:439` windshield slice) 은 무관 context — 변경 0, residue gate 대상 0.

### 3.2 OQ-C9-2 — 예시 처리 + 치환 wording

**결정: 옵션 (a) 예시 전폐 — forbidden 5건 + recommended 5건 모두 제거, generic semantic principle 만.**

Area #7c 의 "TEMPLATE ONLY / illustrative 예시 허용" precedent 와 달리, C9 의 예시는 **실제 T2I 영문 문구** 라 LLM 이 그대로 차용/회피하는 닫힌 prime 으로 작동한다 — C6 의 closed-list 전폐 precedent 가 더 정합 (Codex C9 entry sanity). 자연어 예문 목록 대신 non-exhaustive generic principle 만 남긴다.

**치환 wording (W1 확정 — Codex spec review preferred variant 흡수 예정)**:

Rule B 섹션 v31 최종 형태 (header + WHY 보존, `:74-96` → generic 문단 1개):

```markdown
## 조명·색조가 신체를 변형하지 않게 (Rule B)

T2I 모델은 "빛이 신체 부위를 자른다·관통한다·갈라낸다" 같은 표현을 **물리적 절단**으로 해석한다. 결과 이미지에 눈을 가로지르는 붉은 띠, 얼굴이 갈라진 듯한 색 분리, 입이 잘려나간 듯한 그림자가 생긴다.

조명·색조 효과는 빛이 닿거나 비치는 **표면**(눈에 보이는 표면·재질·영역)에 머무는 묘사로 적는다. 빛이나 색이 신체 부위를 물리적으로 변형하거나 절단하는 행위처럼 작용한다고 쓰지 마라 — 그런 표현은 표면에 닿는 빛이 아니라 신체 변형으로 해석된다. 색감의 강도는 형용사로 조절하고, 동사로 신체를 자르듯 쓰지 마라.
```

- 닫힌 영문 토큰 목록 (`cut/slice/split/carve/bisect`), forbidden/recommended 예문 목록, recommended verb list 모두 0.
- "표면에 머무는 묘사" / "물리적으로 변형하거나 절단하는 행위처럼 쓰지 마라" = light-on-surface 의도 + body-deformation 금지 의도 보존 — audit 가 문제삼은 것은 *닫힌 토큰 목록* 이지 의도가 아님. 닫힌 enumeration 0, generic semantic principle only.
- (Codex spec review IMPORTANT 1 흡수) `표면` 의 괄호 보조어는 `눈에 보이는 표면·재질·영역` 추상형만 — 구 v30 의 `(피부 일부, 옷, 벽, 가구)` 같은 구체 surface mini-list 를 재도입하지 않는다 ("examples 전폐" doctrine 정합).
- "변형하거나 절단하는" 은 한국어 prose 의 **개념 서술** (LLM 이 토큰 매칭하는 닫힌 영문 목록 아님) — Codex entry sanity 가 제시한 principle 수준 ("신체 부위를 물리적으로 절단/분할/관통하는 동작처럼 쓰지 말라") 과 정합.

### 3.3 OQ-C9-3 — WHY 문단 + residue gate scope

**결정: WHY `:72` 보존 (byte-identical), C9 residue gate 대상 아님.**

`:72` 는 closed-list classifier 가 아니라 T2I 오해석 rationale (Codex C9 entry sanity 동의). residue gate 는 v31 system.md 의 **Rule B 섹션** (header `## 조명·색조가...` 부터 다음 `## ` 헤더 직전까지) 로 path-scoped — 닫힌-form literal (`"cut", "slice"` quoted-comma form / `cut / slice / split / carve / bisect` slash form) 만 검사. WHY 문단의 한국어 "자른다·관통한다·갈라낸다" 는 검사 대상 아님 (bare 영문 토큰 grep-ban 금지 — `:240` cutout / `:439` windshield 오검출 방지).

### 3.4 OQ-C9-4 — wave 구조

**결정: 2-wave (C6/C7 precedent).**

- **W1** = v31 prompt-pack rewrite + version cascade + alignment test 갱신 + C9 regression test — **단일 atomic commit**.
- **W2** = closure docs atomic.

### 3.5 OQ-C9-5 — MODULE_VERSIONS bump

**결정: `scene_detail_composer` `1.30.0` → `1.31.0` (minor bump).**

새 `scene_detail/v31` prompt_dependency 와 맞추는 minor bump = 기존 v29/v30 패턴 (v29 → 1.29.0, v30 → 1.30.0) 과 정합 (Codex C9 entry sanity). schema 무변경이지만 prompt-pack 버전과 MODULE_VERSIONS minor 가 lockstep 이라는 기존 4-way alignment 계약을 따른다. `SCENE_DETAIL_SCHEMA_VERSION` / `STEP_MANIFEST` schema_version = 12 유지.

## 4. Invariants

### 4.1 No closed-list / closed-enumeration invariant
v31 `system.md` Rule B 섹션 어디에도 (a) 닫힌 영문 동사 목록 `"cut", "slice", "split", "carve", "bisect"` (quoted-comma form) 또는 `cut / slice / split / carve / bisect` (slash form), (b) forbidden phrasing 예문 목록, (c) recommended phrasing 예문 목록, (d) recommended verb list (`falls on / washes / reflects on / catches on / pools on / glows across`) 잔존 0.

### 4.2 Intent-preserve invariant
"빛/색은 표면에 머무는 효과로 묘사 + 신체를 물리적으로 절단하듯 쓰지 마라" 라는 Rule B *의도* 는 generic semantic principle 문단으로 보존.

### 4.3 Header + WHY preserve invariant
`system.md` Rule B 섹션 header (`## 조명·색조가 신체를 변형하지 않게 (Rule B)`) + WHY 문단 (T2I 물리적 절단 오해석 rationale) byte-for-byte 보존.

### 4.4 Section boundary preserve invariant
Rule B 섹션 직후 `## 크기·치수 — 실 수치 금지, 상대 비율만` 섹션 + system.md 의 다른 모든 섹션 (`:240` 실루엣 cutout / `:439` windshield slice 포함) byte-for-byte 불변.

### 4.5 Schema byte-identical invariant
v31 `detail_schema.json` = v30 `detail_schema.json` sha256 동일 (byte-identical sibling copy). `SCENE_DETAIL_SCHEMA_VERSION` / `STEP_MANIFEST["scene_detail"]["schema_version"]` = 12 불변.

### 4.6 4-point version sync invariant
v31 디렉토리 + `SCENE_DETAIL_PROMPT_VERSION` (`"31."` prefix) + `MODULE_VERSIONS["scene_detail_composer"]` (`"1.31.0"`) + `prompt_dependency` (`"scene_detail/v31"`) lockstep.

### 4.7 NO VLM / no live LLM invariant
C9 코드·테스트 어디에도 VLM 의존 / live LLM 호출 없음. C9 regression test = 파일 read + 문자열 검사 + sha256 only.

### 4.8 No schema/code-logic change invariant
`detail_schema.json` 내용, `detail_steps.py` code 로직, `step_manifest.py` entry — 변경 0. C9 = prompt text rewrite + version 상수 동기화만.

## 5. W1-W2 Implementation Plan

### 5.1 W1 — v31 prompt-pack rewrite + version cascade + test (atomic commit)

prompt-pack rewrite + version cascade + test 갱신/신설을 **단일 atomic commit** 으로 묶는다.

**W1.1 — v31 디렉토리 신설**: `prompts/_base/scene_detail/31.<W1_UTC>/`
- `system.md` — v30 system.md 전체 복사 후 Rule B 섹션 `:74-96` 을 §3.2 generic 문단으로 치환. 나머지 모든 줄 byte-identical.
- `detail_schema.json` — v30 에서 byte-identical 복사 (sha256 동일).

**W1.2 — version cascade 4-point sync** (§2.3 표):
- `detail_steps.py:107` `SCENE_DETAIL_PROMPT_VERSION` → `"31.<W1_UTC>"` + 주석 (C9 v1).
- `version_registry.py:34` `MODULE_VERSIONS["scene_detail_composer"]` → `"1.31.0"` + 주석 (C9 v1).
- `version_registry.py:126` `prompt_dependency` → `"scene_detail/v31"` + 주석.

**W1.3 — alignment / version test 5 file v30→v31 갱신** (§2.3 표 — W0 grep 스냅샷 14 assert/occurrence, 비권위) — W1 에서 5 file 안 `v30` / `1.30.0` / `scene_detail/v30` / `startswith("30.")` / `NEW_DIR_EXPECTED ... scene_detail` occurrence 를 `rg -n` 으로 전수 grep enumerate 후 **빠짐없이** 갱신 (표 개수 신뢰 금지 — `rg` 결과가 SSOT). assert literal + stale 오류 메시지 (`!= 'scene_detail/v28'` / `!= '1.28.0'` 등) + docstring/history 주석 모두 v31 기준 동기. occurrence 누락 = W1 regression (Gate W1 으로 잡힘).

**W1.4 — C9 regression test 신설** — `backend/tests/prompts/test_c9_scene_detail_body_light_verb.py` (신규). Gate G1-G7 (§7). 파일 read + 문자열 검사 + sha256 only, no live LLM, NO VLM.

**W1 gate**:
- C9 신규 test PASS (G1-G7).
- alignment/version test 5 file 전수 PASS (§2.3 — W0 스냅샷 14 assert/occurrence, W1 `rg` enumerate 후 빠짐없이 갱신).
- scene_detail / detail_steps 관련 test 전수 PASS (특히 `test_scene_detail_id_policy_alignment.py` — 자동 latest-pick v31 통과 확인).
- 백엔드 전체 회귀 — pre-existing failure 제외 동일 (clean HEAD `81bde22` 대조).
- repo-root cwd, `PYTHONPATH=backend backend/.venv/bin/python -m pytest` 의무.
- W1 = prompt-pack 신규 2 file + code 2 file (detail_steps.py / version_registry.py) + test 6 file (5 갱신 + 1 신설) → 1 atomic commit.

### 5.2 W2 — Closure docs atomic (path-limited)

- 본 spec frontmatter `status` → closed + §10 Closure 기재.
- C9 plan frontmatter `status` → closed + plan Closure section.
- roadmap `2026-05-16-track-b-semantic-debt-roadmap-design.md` — **§5.16 신설** (C9 closed entry; 인접 C6 §5.10 / C7 §5.12 형식 정합).
- `docs/fix-critical-1/index.html` C9 marker — badge ✓ CLOSED + priority list C9→CLOSED / C4→NEXT + priority matrix row 6 status + **stale 정정** (`scene_detail/28...` → v30/v31, "4 forbidden phrasing" → 5건, `:78-81` → `:78-82`).
- audit `03-llm-side-heuristic-audit.md` — L-7 row 에 closure marker + §8/§9/carry follow-up line (C9 항목) 갱신. audit `01-`/`02-` 에는 L-7 row 부재 — marker 대상 아님.
- **W2 self-hash 금지** — closure docs 안 W2 자체 commit hash literal 없음. "W1-W2" / "W2 closure commit" wording. 실제 hash 는 push 후 external memory 기록.

**W2 gate**: closure docs 정합 + W1-W2 range review.

## 6. Boundary Preserve

- `system.md` Rule B header (`:70`) + WHY 문단 (`:72`) — byte-for-byte 불변.
- `system.md` `:240` `## 실루엣 묘사 규칙 — 평면 cutout 금지` + `:439` `a slice of the windshield visible` — 무관 context, 불변. C9 residue gate false-hit 금지.
- `system.md` 의 Rule B 외 모든 섹션 — 불변.
- `detail_schema.json` — v30→v31 byte-identical.
- `detail_steps.py` `[조명/색감 판단]` block (`:2324-2328`, C6 영역) + `_analyze_one` code 로직 — 변경 0.
- `step_manifest.py` scene_detail entry / `SCENE_DETAIL_SCHEMA_VERSION` — 변경 0.
- C1 perception_mode / C2 owned echo / C3 `_strip_invalid_sids` / C6 색감 instruction / C7 keep_elements subject_kind — 변경 0.
- v30 (및 그 이전) prompt-pack 디렉토리 — 변경 0 (새 v31 디렉토리만 추가).

## 7. Test Gates (G1-G7)

C9 regression test = `backend/tests/prompts/test_c9_scene_detail_body_light_verb.py` (신규). v31 system.md 를 read 해 Rule B 섹션을 slice (header `## 조명·색조가...` ~ 다음 `## ` 직전) 후 검사.

| Gate | 내용 |
|------|------|
| **G1** | closed verb list 부재 — v31 Rule B 섹션 slice 에 `"cut", "slice"` (quoted-comma form 안정 substring) + `cut / slice / split / carve / bisect` (slash form) literal 0. **path-scoped** (v31 system.md Rule B 섹션만; `:240`/`:439` 제외) — bare 영문 토큰 global grep 금지. |
| **G2** | forbidden/recommended 예문 + recommended verb list 부재 — Rule B 섹션 slice 에 `cuts across her eyes` / `slicing through his face` / `pools on` / `glows across` 등 예문·verb-list 안정 substring marker 0. |
| **G3** | generic principle 존재 — Rule B 섹션 slice 에 치환 문단 안정 substring marker (`눈에 보이는 표면·재질·영역` + `신체 변형으로 해석된다`) 포함. 문단 전체 byte assert 아님 (wording 동결 의도 아님). |
| **G4** | header + WHY 보존 — v31 system.md 에 `## 조명·색조가 신체를 변형하지 않게 (Rule B)` header + WHY 안정 substring (`물리적 절단`으로 해석) 포함. |
| **G5** | schema byte-identical — v31 `detail_schema.json` sha256 == v30 `detail_schema.json` sha256. |
| **G6** | 4-point version sync — `SCENE_DETAIL_PROMPT_VERSION.startswith("31.")` + `MODULE_VERSIONS["scene_detail_composer"] == "1.31.0"` + `prompt_dependency == "scene_detail/v31"` + v31 디렉토리 존재. |
| **G7** | section boundary 보존 — v31 system.md 에 `## 실루엣 묘사 규칙 — 평면 cutout 금지` (`:240` 무관 줄) + `a slice of the windshield visible` (`:439` 무관 줄) + `## 크기·치수` (Rule B 직후 섹션) 그대로 존재 (블록 폐기/오삭제 아님). |

## 8. Risk + Open Issues

- **R1 — bare 영문 토큰 grep self-hit**: `cut` 은 `cutout` (`:240`) 의 부분 문자열, `slice` 는 `slice of the windshield` (`:439`) 의 부분 문자열. C9 residue gate (G1) 는 반드시 (a) v31 system.md 의 Rule B 섹션으로 path-scoped slice, (b) 닫힌-form literal (`"cut", "slice"` quoted-comma / slash-form) 검사 — bare `cut` / `slice` 토큰 grep 금지. 두 보호 동시 적용.
- **R2 — generic 치환의 producer 영향**: 닫힌 예문/verb-list 제거 후 producer LLM 이 표면-한정 빛 묘사를 스스로 generate 한다 — Track B "code/prompt 가 닫힌 목록으로 표현을 선점하지 않는다" 원칙상 의도된 결과. WHY 문단 + generic principle 이 의도를 받친다. W1 백엔드 회귀로 downstream exact-match 의존 부재 확인 (§2.4).
- **R3 — version cascade 누락 위험**: 4-point sync 중 하나라도 누락 시 alignment test 5종이 fail-fast 로 잡는다 (의도된 방어). W1 에서 5 test 정확 줄번호 grep enumerate 의무 — 본 spec 의 줄번호는 W0 시점 grep 결과, W1 재확인.
- **OQ-잔여**: 없음 — Codex C9 entry sanity 5개 질문 (OQ-C9-1~5) 모두 resolve (§3).

## 9. Doctrine references

- [[feedback_llm_based_judgment]] — 4-Gate. C9 = **Prompt Closed-List Ban** 적용 (body-light closed verb list + 예문 목록 + recommended verb list 폐기). open-world semantic (빛/색 묘사) 판단은 producer LLM 소관, prompt 는 닫힌 목록 미주입.
- [[feedback_no_vlm_dependency]] — NO VLM. C9 코드·테스트 VLM 의존 0.
- [[feedback_codex_mcp_discussion_workflow]] / [[feedback_codex_mcp_claude_mcp_response_trigger]] — 매 wave Codex MCP per-wave review, tmux-bridge raw typing + prefix/suffix.
- [[feedback_no_user_ask_codex_only]] — OQ·결정 사용자 ask 0, Codex MCP 의논.
- [[feedback_channel_separation_4way]] — 4채널 fact source 구분.
- [[project_fix_critical_1_persistent]] — fix-critical-1 Tier β #6, C9 → C4 priority.

## 10. Closure (2026-05-20+)

**Status: closed** — C9 v1 W1-W2 atomic execution 완료. fix-critical-1 Tier β #6, audit 03 L-7 단독 finding 해소.

### 10.1 Commit chain
- W1 `04016da` — scene_detail v31 prompt-pack (Rule B body-light verb closed list + 예시 목록 추상화 → generic semantic principle) + version cascade 4-point + alignment test 5 file + C9 regression test. 10 file, 963+/80-.
- W2 closure commit — 본 closure docs atomic (path-limited 5 docs). hash 는 push 후 external memory 기록 (W2 self-hash 금지).

### 10.2 Gate result
- W1.1 C9 regression test G1-G7 — 7 passed.
- W1.2 alignment/version test 5 file — 99 passed.
- W1.3 scene_detail·detail_steps regression — 210 passed, 4 skipped.
- W1.4 residue assertion heredoc — `Gate W1.4 OK`.
- W1.5 백엔드 전체 회귀 — 5 failed / 4065 passed / 37 skipped. 5 failures = clean HEAD `81bde22` git stash 대조 동일 (`test_analysis_dispatch_service` ×1 / `test_prompt_service` ×1 / `test_text_cleanup` ×3 — `clean_text` ImportError, untracked 신규 파일). C9 신규 failure 0.

### 10.3 Codex review trace
- C9 W1 entry sanity — `GO / APPROVED_TO_ENTER_W1` (live checkout 재확인, narrow execution guard 5건).
- C9 W1 per-wave review — `APPROVED_FOR_W2_ENTRY` (blocking/important 0).
- C9 W1-W2 range review — W2 closure commit 후 기재 (push 후 external memory).

### 10.4 함정 (lessons)
- **N-1 alignment occurrence 실제 16 (≠ §2.3 의 14)**: §2.3 / plan §3.5 의 rg 패턴이 `latest_major == "30"` assert 2건 (continuity / spatial 의 loader-resolves test) 을 누락. W1 에서 broad 30-token scan 으로 발견·보정. §2.3 가 명시했듯 표의 assert 개수는 **비권위 W0 스냅샷** — W1 의 전수 rg enumerate 가 SSOT 라는 점이 실제 적용된 사례. 향후 version cascade W 의 rg 패턴은 `== "<major>"` (dot 없는 major literal) 도 포함할 것.
- **N-2 boundary preserve 는 string boundary 기준**: v31 Rule B 섹션 축약으로 system.md 의 하위 줄번호가 모두 위로 이동 — v30 의 `:240` 실루엣 cutout / `:439` windshield slice 는 v31 에서 다른 줄번호. boundary preserve 검증·기술은 줄번호가 아니라 **문자열 boundary** (`## 실루엣 묘사 규칙 — 평면 cutout 금지` / `a slice of the windshield visible`) 로 표현 (G7 도 문자열 검사).
- **N-3 version_registry updated_at 주석 동기**: plan §3.4 는 `prompt_dependency` 줄만 명시했으나 같은 `_MODULE_INFO["scene_detail_composer"]` dict 의 `updated_at` 주석 (C2 v1 서술) 도 self-contradiction 방지 위해 C9 로 동기 (`updated_at` 값 `2026-05-20` 유지).
- **N-4 per-test deep changelog 미갱신 패턴**: continuity/spatial 의 per-test docstring deep changelog history list 는 C1·C2 도 미갱신한 패턴 — C9 도 동일하게 assert·current-value claim·module docstring 4-point-sync block 만 v31 동기, deep changelog 는 미변경 (over-rewrite scope creep 회피).

### 10.5 carry priority
C9 closed → 다음 = C4 (#8 background classifier, Tier β #7, roadmap §5.8). 잔여 = C4/C5/C8/C10.
