---
title: Area #5 — Reference Phrase / Phantom Guard Replacement (Track B Tier 2 #2)
date: 2026-05-18
status: design (brainstorming closure, awaiting Codex spec iter review)
roadmap_ref: docs/superpowers/specs/2026-05-16-track-b-semantic-debt-roadmap-design.md §5.5
prerequisites:
  - Area B-min `render_contracts.required_refs` / `attached_meta` identity contract (closed, commit `e400e9c`) — boundary, preserved
  - framing_scale enum SOT v1 (closed, commit `53dd180`) — `is_close_framing` boolean consumer
non_supersedes:
  - Area #11 prompt_service `_classify_label` 9-branch substring routing (separate wave)
  - Area #7 Prompt Hygiene v2 (active prompt closed-list cleanup beyond Area #5 surfaces)
  - Area #6 T2I review `.replace()` mutation redesign (Area #5 only neutralizes phantom phrase category, full mutation redesign deferred)
---

# Area #5 — Reference Phrase / Phantom Guard Replacement (v1)

## 1. Purpose + Scope

### 1.1 Purpose

`backend/app/core/ref_contract_validator.py` 의 step 6 `phantom guard` 가 LLM emit final prompt prose 안에서 `"from the reference"` phrase 를 30/60자 window nearest-token classifier (3 noun list: `_CHARACTER_TOKENS` / `_BACKGROUND_TOKENS` / `_OBJECT_TOKENS`) 로 character / background / object 로 open-world 분류 후, attached_meta 에 해당 kind 가 없으면 `RefContractError` raise. open-world semantic regex / lexicon 기반 판단 = gate 1 (Semantic Regex Ban) + gate 3 (Structured SOT Required) 위반 본체.

본 area = step 6 phantom guard 의 open-world semantic classifier 를 LLM emit structured `reference_phrase_kinds` (per-variation, enum array) sidecar 비교로 대체. step 1-5 (closed-world `required_refs` ↔ `attached_meta` exact match) 은 그대로 보존.

### 1.2 In-scope

**Code (6 rows / 5 surface groups)**:

> `HHMM` placeholders are implementation-time timestamp placeholders; W1 must replace them with the actual prompt directory timestamp before commit.

| Site | File | Lines | Action |
|---|---|---|---|
| Validator step 6 본체 | `backend/app/core/ref_contract_validator.py` | 47-190, 425-457 | step 6 rewrite (sidecar 비교) + helper trio + token list + classifier 모두 atomic delete |
| Single caller | `backend/app/services/scene_generation_coordinator.py` | 1287-1291 | `validate_attached_refs(..., reference_phrase_kinds=...)` keyword-only required arg 추가 |
| Variation caller | `backend/app/services/scene_generation_coordinator.py` | 1393-1410 | same signature + Fix C typed failure dict (`_failure_reason=REF_CONTRACT_VIOLATION`) 보존 |
| `render_prompt_card` close framing forbidden | `backend/app/core/steps/render_prompt_card.py` | 1253-1261, 1276 | closed-list (`"the existing X" / "from the reference" / "use the X from the reference" / "preserving the same room perspective" / "matching the reference camera" / ...`) 폐기, principle-only rewrite |
| `render_prompt_card` owned positive recommendation | `backend/app/core/steps/render_prompt_card.py` | 1290-1306 | `'the door from the reference'` (line 1292, 1300) positive recommendation literal 폐기, principle-only rewrite |
| `prompt_service` translation guard | `backend/app/services/prompt_service.py` | 368-369 (phantom 부분만) | `"'from the reference', 'from Reference image N' 같은 표현은 절대 새로 만들지 마세요"` literal 폐기. translation-preservation principle 으로 대체. `_classify_label` 본체 (lines 75-116) 는 Area #11 carry. |

**Prompt (3 surface)**:

| Surface | Path | Lines | Action |
|---|---|---|---|
| `scene_detail` producer | `prompts/_base/scene_detail/25.202605161320/` | new `26.20260518HHMM/` | schema v25 → v26: `t2i_variations[].reference_phrase_kinds` required field 추가. `detail_schema.json` + `system.md` only (loader path `load_prompt("scene_detail", "system")` + `load_schema("scene_detail", "detail_schema")` `detail_steps.py:1184-1185` 일관, 신규 `user.md` 추가 0). closed-list 가르치지 않고 추상 원칙만 |
| `scene_image` translate prompt | `prompts/_base/scene_image/3.202605101200/translate_prompt.md` | 23-27 (PHANTOM PHRASE PROHIBITION block) | closed enumeration 폐기, `4.20260518HHMM/translate_prompt.md` 신규: numbered image reference semantics preservation principle + sidecar contract reference만 |
| `t2i_review` scene prompt + schema | `prompts/_base/t2i_review/4.202605150957/{scene_system.md, scene_schema.json}` | scene_system.md:31-49 (issue type `close_framing_existing_ref` section) + scene_schema.json:27-36 (enum value `"close_framing_existing_ref"`) | 두 파일 같이 version-bump (`5.20260518HHMM/`) — scene_system.md section delete + scene_schema.json enum 항목 삭제. prompt-only fix 시 enum 잔존하면 category 재발 가능. mutation-based phantom phrase fixer 폐기. sidecar ownership = `scene_detail` only |

**Version bump (cp invalidation 강제)**:

| Constant | From | To |
|---|---|---|
| `SCENE_DETAIL_SCHEMA_VERSION` | `10` | `11` |
| `SCENE_DETAIL_PROMPT_VERSION` | `"25.202605161320"` | `"26.20260518HHMM"` (실제 wave 진입 timestamp) |
| `scene_image` translate prompt dir | `3.202605101200` | `4.20260518HHMM` |
| `t2i_review` scene prompt dir | `4.202605150957` | `5.20260518HHMM` |

`render_prompt_card.py` + `prompt_service.py` = code-only, prompt-pack version 없음 — module version / roadmap / commit body 로 추적.

### 1.3 Out-of-scope (Non-claim, closure 미선언)

| Item | Reason | Carry destination |
|---|---|---|
| `prompt_service._classify_label` 9/10-branch substring routing | label dispatch layer, ref-phrase classifier 와 다른 축 | **Area #11** (roadmap §5.11, P084-P086 primary + P087 dispatch 부분) |
| `scene_detail_owned_judge` `anchor_reference` closed-list (A045) | owned background object redraw judge — 별도 축 (redraw_violation vs anchor_reference judge) | **Area #7** (Prompt Hygiene v2) 또는 별도 owned-judge cleanup wave |
| t2i_review `.replace()` mutation 전반 | Area #5 = 본 area 의 phantom phrase category 4 (`close_framing_existing_ref`) 중립화만. blind mutation 본체 redesign (target/suggestion → structured patch ID / regen-trigger) 는 별도 area | **Area #6** (T2I review mutation redesign, roadmap §5.6) |
| 모든 `"from the reference"` literal global removal | t2i_review issue category 5/6 ( physical_inconsistency / other), code comment, doc, archived prompt 등에 legitimate reference 잔존 가능 | Area #5 residue gate scope = `production code (Area #5 in-scope sites) + active prompt (3 surface) + docs/architecture (roadmap §5.5/§11/§14 update)` 만. legacy specs/history / archived prompt / unrelated literal = residue scope 외. |
| Area B-min `required_refs` / `attached_meta` identity contract | closed area boundary (commit `e400e9c`) — step 1-5 exact match logic preserve | 변경 0 |
| Caller behavior (`scene_generation_coordinator` single propagate vs variation typed failure dict) | retry/skip semantics 보존 (Fix C 2026-05-10) — Area #5 = signature 확장만, behavior 변경 0 | 변경 0 |

## 2. Audit Evidence

### 2.1 Roadmap §5.5 verbatim

```
audit row: code P006 (primary) / prompt A058, A089 (primary). related: P084-P087 = Area #11
본질: ref_contract_validator.py:47-457 의 _CHARACTER_TOKENS / _BACKGROUND_TOKENS / _OBJECT_TOKENS (3 noun list)
     + classify_from_the_reference 30/60자 window nearest-token classifier. "from the reference" phrase 의
     character/background/object 분류가 token rfind 로 결정.
Fix scope: attached_refs[] metadata + render_contracts.required_refs 비교로 전환. classify_from_the_reference 폐기 또는 warning-only 격하.
Cost: medium
Dependency: independent (audit 00-index.md §6.3 graph)
Closed area boundary: attached_refs / render_contracts.required_refs (Area B-min closed, commit e400e9c) — consumer 보존, validator 만 변경
Risk: "from the reference" phrase 는 LLM-emit prose. 폐기 시 LLM 이 alternative phrase 사용 → 새 silent-miss 발생 가능.
      prompt-side dual-sync (scene_image translate_prompt PHANTOM PHRASE PROHIBITION, A058) 동시 정리 의무
```

### 2.2 Audit finding row 본문 (triage_report.md 발췌)

| Row | Status | Tier | Site | Class | Snippet |
|---|---|---|---|---|---|
| P006 | CONFIRMED | P1 | `backend/app/core/ref_contract_validator.py:49-457` | semantic_string_judgment | `_CHARACTER_TOKENS, _GENERIC_VERB_TOKENS, classify_from_the_reference, and validate_attached_refs` |
| P024 | CONFIRMED | P2 | `backend/app/core/steps/render_prompt_card.py:1253-1261, 1276, 1292, 1300, 1306` | llm_closed_list_instruction | close-framing forbidden phrase list + non-close positive recommendation `'the door from the reference'` |
| P087 (phantom 부분만) | CONFIRMED | P2 | `backend/app/services/prompt_service.py:368-369` | scenario_dependent_prompt | `"'from the reference', 'from Reference image N' 같은 표현은 절대 새로 만들지 마세요 (phantom guard 충돌)."` |
| A058 | CONFIRMED | P1 | `prompts/_base/scene_image/3.202605101200/translate_prompt.md:23-26` | semantic_string_judgment | `PHANTOM PHRASE PROHIBITION (strict): ... phrases trigger a downstream phantom-reference validator and will fail the render` |
| A089 | CONFIRMED | P1 | `prompts/_base/t2i_review/4.202605150957/scene_system.md:34-39` | semantic_string_judgment | `the existing X, the reference X, from the reference image, use the X from the reference` |

### 2.3 Q0-light trace (validate_attached_refs producer)

**Single path** (`scene_generation_coordinator.py:1287-1291`):

```python
validate_attached_refs(
    _rpc, labeled_refs, attached_meta, full_prompt,
    is_close_framing=_is_close_framing,
    chain_bg_lookup=_chain_bg_lookup,
)
```

`full_prompt = _build_final_scene_prompt(var_t2i, labeled_refs, cached_style_context, entity_text_map)` 조합. `var_t2i` = LLM emit T2I prompt (scene_detail step 결과) — phantom guard 의 핵심 string source.

**Variation path** (`scene_generation_coordinator.py:1393-1410`):

```python
try:
    validate_attached_refs(
        rpc, labeled_refs, attached_meta, _full_prompt,
        is_close_framing=is_close_framing,
        chain_bg_lookup=chain_bg_lookup,
    )
except RefContractError as _ref_exc:
    return {
        "_failure_reason": "REF_CONTRACT_VIOLATION",
        "_failure_detail": str(_ref_exc)[:500],
    }
```

variation 단위 fail-fast, typed failure dict 반환 — Fix C 보존 의무.

Phantom guard input before Area #5 = `full_prompt` (string only). Area #5 decision = sidecar is passed as keyword-only `reference_phrase_kinds` arg; prompt-embedded sidecar is rejected. § 3.1 signature 확장 + § 3.3 caller cascade 본문 참조.

### 2.4 t2i_review mutation staleness risk (Codex Risk surfaced 2026-05-18)

`t2i_review/4.202605150957/scene_system.md:31-39` 의 issue type 4 (`close_framing_existing_ref`) = LLM emit `{target, suggestion}` pair → `t2i_review_step.py:48-91, 249-250` 가 prompt body `target.replace(target, suggestion)` blind mutation.

→ `reference_phrase_kinds` sidecar 가 scene_detail 에서 emit 되어도, t2i_review 가 prompt body 를 mutate 하면 sidecar 가 stale 됨 (mutation 후 phrase set 이 emit 시점과 불일치).

**해결 옵션**:
- (1) **t2i_review issue category 4 자체 폐기** — sidecar ownership scene_detail only. Area #5 v1 채택. mutation-based phantom phrase fixer 완전 제거.
- (2) t2i_review mutation 시 sidecar 도 동시 update — Area #6 (mutation redesign) 영역. scope inflation, 본 area 외.

**결정 = (1)**. Area #6 의 본질 = `.replace()` blind mutation 전체 redesign. Area #5 = phantom phrase category 만 중립화 — t2i_review/4.202605150957 의 v4 6 issue type (`missing_ethnicity`, `proper_noun_translated`, `awkward_translation`, `close_framing_existing_ref`, `physical_inconsistency`, `unshared_fg_bg_actors`) 중 `close_framing_existing_ref` 1개만 제거 → v5 = 5 issue type (`missing_ethnicity`, `proper_noun_translated`, `awkward_translation`, `physical_inconsistency`, `unshared_fg_bg_actors`). 나머지 5 category 는 phantom phrase 와 무관 — Area #6 carry. 본 area 안에서 처리하는 것은 issue category `close_framing_existing_ref` 만 (prompt section delete + schema enum 항목 삭제 같이).

## 3. 4-Layer Architecture

| Layer | Component | Action |
|---|---|---|
| **Producer** | `scene_detail` v26 schema | `t2i_variations[].reference_phrase_kinds: array<enum>` required field 추가. LLM 이 각 variation 의 t2i_prompt 안에서 reference image 를 가리키는 phrase 가 있을 때 해당 kind (character / background / prop) 를 set 으로 emit. empty array = phrase 없음. closed-list 가르치지 말고 추상 원칙: "if final prompt body references any character/background/prop visible only via reference images, declare the kind(s) in `reference_phrase_kinds` set" |
| **Helper trio + token list + classifier** | `ref_contract_validator.py:47-190` | atomic delete: `_CHARACTER_TOKENS` / `_BACKGROUND_TOKENS` / `_OBJECT_TOKENS` / `_FROM_THE_REFERENCE_RE` / `_PLURAL_REFERENCE_IMAGES_RE` / `_PHRASE_LOCAL_WINDOW` / `_PHRASE_FALLBACK_WINDOW` / `_GENERIC_VERB_TOKENS` / `_GENERIC_NOUN_TOKENS` / `_classify_window` / `_is_plural_reference_images` / `_has_generic_instruction_signal` / `classify_from_the_reference` — 13 identifier |
| **Consumer (validator step 6 rewrite)** | `ref_contract_validator.py:425-457` | step 6 = sidecar `reference_phrase_kinds` 와 `attached_meta` 의 kind set exact 비교. 각 kind 에 대해 attached_meta 안에 해당 kind 가 1개 이상 있는지 확인 (character → ("character", "character_outlook", "character_state") 중 하나, background → ("background", "background_prev_shot") 중 하나, prop → ("prop") 만). **Step 6 는 legacy close-framing background skip 을 보존하지 않는다**; close-framing shot 이 background 를 declare 하면 attached background meta 가 필요하고, 없으면 producer contract violation. `RefContractError` signature 동일. error message wording 갱신 (phrase position pos= 제거, "missing structured kind=X" 형식). |
| **Prompt + Code Constraints** | A058 / A089 / P024 / P087 phantom 부분 | closed-list enumeration 폐기, principle-only rewrite. translation 단계 (P087) 는 sidecar 보존 원칙 (translate 가 var_t2i 변형해도 reference_phrase_kinds 영향 없도록 prose-only 번역). render_prompt_card constraints 는 close framing / non-close 모두 closed phrase enumeration 제거. |

### 3.1 `validate_attached_refs` signature 확장

**Before** (current):
```python
def validate_attached_refs(
    rpc: Optional[Dict[str, Any]],
    labeled_refs: List[Tuple[str, bytes]],
    attached_meta: List[Tuple[str, str]],
    prompt: str,
    is_close_framing: bool,
    *,
    chain_bg_lookup: Optional[Callable[[str], Optional[str]]] = None,
) -> None:
```

**After** (Area #5 v1):
```python
def validate_attached_refs(
    rpc: Optional[Dict[str, Any]],
    labeled_refs: List[Tuple[str, bytes]],
    attached_meta: List[Tuple[str, str]],
    prompt: str,
    is_close_framing: bool,
    *,
    chain_bg_lookup: Optional[Callable[[str], Optional[str]]] = None,
    reference_phrase_kinds: List[str],  # keyword-only required, per-variation
) -> None:
```

`reference_phrase_kinds` = LLM emit `t2i_variations[].reference_phrase_kinds` 의 직접 전달. type = `list[str]`, values = `{"character", "background", "prop"}` subset, uniqueItems.

**fail-fast (No Silent Fallback)**:
- `reference_phrase_kinds is None` → `RefContractError("reference_phrase_kinds missing — producer (scene_detail v26) emit required, no silent fallback")`. Gate 4 violation 차단.
- enum 외 value → `RefContractError("reference_phrase_kinds invalid enum value: <val>")`.
- non-list type → `RefContractError("reference_phrase_kinds malformed: must be list, got <type>")`.

`prompt` arg 보존 — error message context (caller 가 어떤 variation 의 어떤 prompt body 가 위반했는지 trace 용). prompt body 자체에 대한 noun-list / regex / window classifier 적용 0.

### 3.2 step 6 rewrite (after)

```python
    # 6. Sidecar phantom guard — reference_phrase_kinds vs attached_meta kind exact 비교.
    if reference_phrase_kinds is None:
        raise RefContractError(
            "reference_phrase_kinds missing — producer (scene_detail v26) "
            "emit required, no silent fallback (Area #5 v1 §3.1)"
        )
    if not isinstance(reference_phrase_kinds, list):
        raise RefContractError(
            f"reference_phrase_kinds malformed: must be list, "
            f"got {type(reference_phrase_kinds).__name__}={reference_phrase_kinds!r}"
        )
    _ALLOWED_KINDS = ("character", "background", "prop")
    for kind in reference_phrase_kinds:
        if kind not in _ALLOWED_KINDS:
            raise RefContractError(
                f"reference_phrase_kinds invalid enum value: {kind!r} "
                f"(allowed: {_ALLOWED_KINDS})"
            )
        if kind == "character" and not _meta_has_kind(
            attached_set, ("character", "character_outlook", "character_state")
        ):
            raise RefContractError(
                f"reference_phrase_kinds declares kind={kind!r} but no character "
                f"meta attached — attached={sorted(attached_set)}"
            )
        if kind == "background" and not _meta_has_kind(
            attached_set, ("background", "background_prev_shot")
        ):
            raise RefContractError(
                f"reference_phrase_kinds declares kind={kind!r} but no background meta — "
                f"attached={sorted(attached_set)}"
            )
        if kind == "prop" and not _meta_has_kind(attached_set, ("prop",)):
            raise RefContractError(
                f"reference_phrase_kinds declares kind={kind!r} but no prop meta — "
                f"attached={sorted(attached_set)}"
            )
```

Notes:
- **Sidecar 는 strict exact compare — legacy `is_close_framing` skip 보존 X**. background 가 명시적으로 declare 되면 close framing 여부와 무관하게 attached meta 의 ("background", ...) 또는 ("background_prev_shot", ...) 필요. close framing 에서는 producer 가 empty array 또는 non-background kinds 만 emit 의무 — close framing 에서 background declare = producer contract violation, validator 가 즉시 fail-fast. closure §6 의 "exact attached_meta comparison" wording 보존 정합.
- step 4 (background `required_refs` exact match) 의 legacy `is_close_framing` skip 은 step 4 안에서만 보존 — step 6 sidecar 와 다른 axis. step 4 = `required_refs` (manifest contract), step 6 = `reference_phrase_kinds` (LLM declare). 두 axis 분리.
- `_meta_has_kind` helper 보존 (`ref_contract_validator.py:193-197`). 기존 함수 재사용.
- prompt arg 는 step 6 안에서 read 0 — phantom guard 가 prompt prose parse 안 함. prompt arg 보존 = error message context 용 (caller 가 어떤 variation 의 어떤 prompt body 가 위반했는지 trace 용).

### 3.3 caller cascade

**Decision**: `still_data["_reference_phrase_kinds"]` side-effect injection 폐기. `build_scene_attached_refs` (helper) 가 selected variation 의 `reference_phrase_kinds` 를 explicit 4th element 로 return — 같은 variation 의 `var_t2i` 와 같은 return path 로 일관 흐름. caller signature = direct unpack.

**Single path** (`scene_generation_coordinator.py:1287-1291`):

```python
full_prompt, labeled_refs, attached_meta, reference_phrase_kinds = build_scene_attached_refs(
    still=still,
    still_data=still_data,
    visible_entities=visible_entities,
    # ... (other args 동일)
)
validate_attached_refs(
    _rpc, labeled_refs, attached_meta, full_prompt,
    is_close_framing=_is_close_framing,
    chain_bg_lookup=_chain_bg_lookup,
    reference_phrase_kinds=reference_phrase_kinds,
)
```

`build_scene_attached_refs` 는 `var_t2i` 선택과 동일한 variation 의 `reference_phrase_kinds` 를 반환 의무 (선택 logic = 기존 패턴 그대로 보존, return shape 만 확장). selected variation 의 `reference_phrase_kinds` field 가 missing 이면 `KeyError` → `RefContractError` fail-fast (stale scene_detail v25 checkpoint signal). silent `[]` fallback 금지.

**Variation path** (`scene_generation_coordinator.py:1393-1410`):

```python
# var dict 에서 직접 추출 (scene_detail v26 required field — KeyError = stale producer schema)
reference_phrase_kinds = var["reference_phrase_kinds"]
try:
    validate_attached_refs(
        rpc, labeled_refs, attached_meta, _full_prompt,
        is_close_framing=is_close_framing,
        chain_bg_lookup=chain_bg_lookup,
        reference_phrase_kinds=reference_phrase_kinds,
    )
except RefContractError as _ref_exc:
    ...  # Fix C typed failure dict 그대로 (`_failure_reason=REF_CONTRACT_VIOLATION`)
```

`build_scene_attached_refs` (helper) 의 return shape 변경 — 현재 `(full_prompt, labeled_refs, attached_meta)` 3-tuple → `(full_prompt, labeled_refs, attached_meta, reference_phrase_kinds)` 4-tuple. `build_scene_attached_refs` helper definition is updated at `scene_generation_coordinator.py:209-343`; the single-path unpack site is `:1237`; validator call sites are `:1287-1291` and `:1393-1410`. **No 209/245/425 unpack-site claim** (cascade grep verify, production unpack = 1 site only). W0 cascade grep pre-step 강제.

**No `still_data` side-effect injection**: `still_data["_reference_phrase_kinds"]` 같은 보조 field 추가 0. helper return path 단일 SOT.

### 3.4 prompt principle wording (3 surface)

#### 3.4.1 `scene_detail` v26 `system.md` 신규 instruction

```
REFERENCE PHRASE KIND DECLARATION (per-variation):
- For each `t2i_variations[i].t2i_prompt`, emit `reference_phrase_kinds`: a set of kind labels indicating which kinds of reference image content the prompt body relies on.
- Allowed values: "character", "background", "prop".
- Set membership criteria: declare a kind if the prompt body relies on character/background/prop appearance supplied by numbered image mappings in the ID → REFERENCE IMAGE MAPPING.
- empty array = the prompt body does not use any reference-image attribution.
- This is a producer contract — do not enumerate forbidden phrases. The downstream validator compares the declared set against attached metadata kinds; you only need to declare truthfully what the prompt body relies on.
- No phrase examples are needed; this is a structural declaration, not a phrase enumeration task.
```

#### 3.4.2 `scene_image/4.20260518HHMM/translate_prompt.md` (replace lines 23-27)

```
REFERENCE PRESERVATION:
- Preserve numbered image reference semantics ("the character/object shown in image N") exactly. Do not introduce or invent new reference-kind attribution that the original Korean prompt did not contain.
- Translation must remain prose-only — do not alter the reference structure of the prompt.
- The producer (scene_detail v26) declares `reference_phrase_kinds` per variation; this translation step does not modify that declaration.
```

closed-list `"from the reference" / "from Reference image N" / "from a reference image"` 등 enumeration 폐기.

#### 3.4.3 `t2i_review/5.20260518HHMM/{scene_system.md, scene_schema.json}` (issue category `close_framing_existing_ref` 폐기)

scene_system.md 의 `### 4. close_framing_existing_ref` 전체 section (lines 31-49) 폐기. v4 의 6 issue type (`missing_ethnicity`, `proper_noun_translated`, `awkward_translation`, `close_framing_existing_ref`, `physical_inconsistency`, `unshared_fg_bg_actors`) → v5 = 5 issue type 남음 (`missing_ethnicity`, `proper_noun_translated`, `awkward_translation`, `physical_inconsistency`, `unshared_fg_bg_actors`). 나머지 5 category 는 phantom phrase 와 무관 — Area #6 carry, 본 area 변경 0.

**scene_schema.json:27-36 enum 동시 수정 의무** — `"close_framing_existing_ref"` enum 값 삭제 (prompt-only fix 시 enum 잔존하면 LLM 이 category 재발 emit 가능). prompt + schema 같이 v5 bump.

#### 3.4.4 `render_prompt_card.py` lines 1253-1306 rewrite

close framing forbidden phrase enumeration (1253-1261) + non-close owned positive phrase (1290-1306) literal 폐기. principle-only:

```python
constraints = [
    "this shot uses close framing — the background reference is intentionally omitted at composition time",
    "describe the close subject and its immediate surroundings without relying on the absent background reference",
    "use [L##: ...] block for frame-edge surface description",
]
```

owned + camera sub-cases (A/B/C/D) constraints 도 동일하게 principle-only rewrite. `'the door from the reference'` 같은 positive literal recommendation 폐기. owned_objects 의 reference 의무는 sidecar `reference_phrase_kinds` 가 declare.

#### 3.4.5 `prompt_service.py:365-374` rewrite (translation guard)

```python
system_prompt=(
    "T2I 프롬프트 번역 전문가. 한국어를 영어로 변환하세요. "
    "'the character/object shown in image N' 표현은 정확히 보존하세요. "
    "원문에 없던 reference attribution 표현을 새로 추가하지 마세요. "
    "(producer 가 declare 한 reference_phrase_kinds 와 mismatch 차단)"
),
```

closed-list `"'from the reference', 'from Reference image N' 같은 표현은 절대 새로 만들지 마세요 (phantom guard 충돌)."` 폐기. `_classify_label` 본체 (`prompt_service.py:75-116`) = Area #11 carry, 본 area 변경 0.

## 4. Closed Area Boundary (preserved)

| Closed area | Commit | Preservation 의무 |
|---|---|---|
| Area B-min `required_refs` / `attached_meta` identity contract | `e400e9c` | step 1-5 exact match 보존. `_normalize_required_refs` (line 199-280) 변경 0. `validate_attached_refs` step 1 (invariant 1) / step 2 (rpc shape strict) / step 3 (character_outlook strict) / step 3a (prop strict subset) / step 4 (background exact + prev_shot lineage) / step 5 (readiness_policy consistency) 변경 0 |
| framing_scale enum SOT v1 | `53dd180` | `is_close_framing` boolean consumer 보존은 step 4 `required_refs` background handling 에 한정. Step 6 `reference_phrase_kinds` 는 strict attached_meta compare 이며 close-framing skip 없음. framing_scale 자체 변경 0. |
| Area #4 Scene Consistency Element Scope v7 | `fe300bf` | element_scope enum SOT 보존, scene_consistency_step 변경 0 (cross-cut 없음) |
| Area D-next-min `KEEP_ELEMENT_KINDS` | `5f3da38` | 변경 0 (cross-cut 없음) |
| Area #1 ID/outlook reference policy SOT | `94bfdae` | `subject_reference_policy` consumer 보존 (`scene_detail` v25 → v26 bump 시 subject_reference_policy required 보존). prompt fix 도 area #1 wording 보존 — 신규 closed-list 도입 0. |
| Area #2 State/gaze 분리 | `f5d83ba` | `gaze_direction_kind` / `gaze_target_id` / `subject_state` 3-field 보존, semantic_contract_router 변경 0 |
| Area #3 Visibility/physical presence v6 | `97c4471` | shot_director mutation 박탈 보존, prompt v6 Korean grammar bundle + BP guard 보존 |

## 5. Canonical Principle Mapping Table (Tier 1 lesson #2)

| Audit finding | Spec canonical clause | Prompt / Schema / Code wording | Regression test marker |
|---|---|---|---|
| P006 — `_CHARACTER_TOKENS`/`_BACKGROUND_TOKENS`/`_OBJECT_TOKENS` 3 noun list + `classify_from_the_reference` 30/60자 window classifier | §1.1 step 6 phantom guard open-world semantic regex 폐기 + §3.2 sidecar 비교 rewrite | `ref_contract_validator.py:47-190` 13 identifier atomic delete | `tests/services/test_ref_contract_validator.py` (기존 test class `TestPhantomGuard*` → `TestSidecarPhantomGuard*` rewrite) + new fixture: variation with `reference_phrase_kinds=["character"]` + no character meta → RefContractError, with character meta → PASS |
| P024 — `render_prompt_card.py:1253-1261 / 1290-1306` closed-list `"the existing X"` etc | §3.4.4 principle-only rewrite | constraints list literal 폐기, principle wording substitute | `tests/core/steps/test_render_prompt_card.py` cascade: constraints list 안 `"from the reference"` literal count == 0 + `"the door from the reference"` literal count == 0 |
| P087 phantom 부분 — `prompt_service.py:368-369` `"'from the reference' 만들지 마세요"` | §3.4.5 principle-only rewrite | system_prompt wording substitute | `tests/services/test_prompt_service.py` cascade: translate system_prompt 안 closed-list literal 0 |
| A058 — `scene_image/3.../translate_prompt.md:23-27` PHANTOM PHRASE PROHIBITION strict | §3.4.2 new principle wording (v4) | translate_prompt.md substitute | active prompt residue gate: `rg "PHANTOM PHRASE PROHIBITION" prompts/_base/scene_image/4.*/` = 0 hits |
| A089 — `t2i_review/4.../scene_system.md:31-49` close_framing_existing_ref issue category | §3.4.3 category 폐기 (v5) | scene_system.md section delete | active prompt residue gate: `rg "close_framing_existing_ref" prompts/_base/t2i_review/5.*/` = 0 hits |
| A089 schema cascade — `t2i_review/4.202605150957/scene_schema.json:27-36` enum value `"close_framing_existing_ref"` | §3.4.3 schema enum 동시 삭제 (v5) | scene_schema.json enum entry delete | active prompt residue gate: `rg "close_framing_existing_ref" prompts/_base/t2i_review/5.*/scene_schema.json` = 0 hits |

## 6. Closure verdict (verbatim, Codex 권고 채택)

> Area #5 closure complete when scene_detail v26 emits required per-variation structured `reference_phrase_kinds`; `ref_contract_validator` no longer classifies prompt prose with noun lists, regex phrase windows, or nearest-token heuristics; step 6 compares each variation's `reference_phrase_kinds` against `attached_meta` kind exact matches while preserving step 1-5 `required_refs` / `attached_meta` identity checks; active Area #5 surfaces (`scene_image` translate prompt, `t2i_review` scene prompt, `render_prompt_card` reference constraints, and `prompt_service` translation guard) no longer teach closed phrase enumerations as validator workarounds; cp invalidation/version bump forces scene_detail v26 rerun; residue gates and canary fixtures pass. No VLM-based validation used.

### 6.1 Non-claim (closure 미선언)

- `prompt_service._classify_label` 9/10-branch substring routing fixed = **NO** (Area #11)
- `scene_detail_owned_judge` prompt hygiene fixed = **NO** (Area #7 또는 owned-judge carry)
- 모든 `"from the reference"` literal global removal = **NO** (legitimate reference 잔존 가능, archived prompts 변경 0)
- t2i_review `.replace()` blind mutation 본체 redesign = **NO** (Area #6, 본 area 는 issue category 4 중립화만)
- Area B-min `required_refs` / `attached_meta` identity contract change = **NO** (변경 0)

## 7. 4-Gate Compliance Matrix

| Gate | Compliance | Evidence |
|---|---|---|
| Gate 1 Semantic Regex Ban | ✓ | `_CHARACTER_TOKENS` / `_BACKGROUND_TOKENS` / `_OBJECT_TOKENS` / `_FROM_THE_REFERENCE_RE` / 30/60자 window classifier 모두 atomic delete. step 6 새 로직 = enum literal 비교만 (`kind == "character"` 등 closed-world ID match) |
| Gate 2 Prompt Closed-List Ban | ✓ | A058 PHANTOM PHRASE PROHIBITION closed enumeration / A089 close_framing_existing_ref closed phrase list / P024 close-framing forbidden phrase list / P087 phantom 부분 closed-list 모두 폐기. v26/v4/v5 prompt = principle-only |
| Gate 3 Structured SOT Required | ✓ | `scene_detail v26` schema 안 `t2i_variations[].reference_phrase_kinds: array<enum>` required field 추가. additionalProperties:false (기존 schema 정책 일관). |
| Gate 4 No Silent Fallback | ✓ | `reference_phrase_kinds is None` → RefContractError fail-fast. enum 외 value / non-list type 도 fail-fast. cp invalidation = schema_version 10 → 11 + prompt_version v25 → v26 강제 producer rerun |

## 8. Wave outline

| Wave | Scope | Atomic deliverable | Codex review |
|---|---|---|---|
| **W0** | trace + canary inventory + cascade enumeration pre-step + t2i_review mutation-staleness risk 명시 | `docs/superpowers/specs/2026-05-18-area-5-...md` 본 spec + cascade grep table (callee → caller, fixture, test, doc, archived prompt 분리) + canary 5 scenario (3 kind scenarios [character/background/prop] + empty array + close-framing strict violation) | Codex spec iter |
| **W1** | scene_detail v26 producer schema + prompt | `prompts/_base/scene_detail/26.20260518HHMM/{detail_schema.json, system.md}` (loader path 일관, 신규 `user.md` 추가 0) + `SCENE_DETAIL_SCHEMA_VERSION=11` + `SCENE_DETAIL_PROMPT_VERSION="26.20260518HHMM"` + `t2i_variations[].reference_phrase_kinds` required. test cascade: existing `tests/services/test_scene_detail_*` 의 fixture 가 v26 shape 로 cascade. visible_entities_validator (if applicable) 가 reference_phrase_kinds shape 검증. | Codex per-wave |
| **W2** | `validate_attached_refs` keyword arg + step 6 rewrite + legacy 13 identifier atomic delete | `ref_contract_validator.py:47-190` delete + step 6 rewrite (line 425-457 replace) + signature 확장 (line 283-309 update). `tests/services/test_ref_contract_validator.py` rewrite — phantom guard test class 전체 sidecar 기반으로 cascade. classify_from_the_reference direct test (line 26-108 area) 모두 신규 fixture 로 cascade. | Codex per-wave |
| **W3** | caller cascade (single + variation) + 4 prompt sync + code-side constraints rewrite | `scene_generation_coordinator.py:1287-1291 + 1393-1410` signature 확장 + `build_scene_attached_refs` helper definition update (`:209-343`) + single-path unpack (`:1237`) 4-tuple cascade — `still_data` side-effect injection 0. `prompts/_base/scene_image/4.20260518HHMM/translate_prompt.md` + `prompts/_base/t2i_review/5.20260518HHMM/{scene_system.md, scene_schema.json}` + `render_prompt_card.py:1253-1306` rewrite + `prompt_service.py:365-374` rewrite. test cascade. | Codex per-wave |
| **W4** | residue gate + canary 5 + roadmap §5.5/§11/§14 update + closure memo | residue gate (production code + active prompt + docs/architecture 분리, strict 0 / strict 0 / explicit references-only 형식). canary 5 scenario (3 kind scenarios [character/background/prop] + empty array + close-framing strict violation) PASS verify (mock LLM emit fixture). roadmap §5.5 ✓ closed marker + §14 다음 진입점 update. closure memo `session_20260518_area_5_reference_phrase_phantom_guard_sot_v1_closure.md` 작성. | Codex range review (W1-W4 종합) |

## 9. Residue gate scope (Tier 2 BLOCKING pre-step #3)

| Scope | Strict 0 file/pattern | Notes |
|---|---|---|
| Production code — 4 항목 split (in-scope file 만, global production literal 0 주장 금지) | (1) `ref_contract_validator.py`: 13 legacy identifier (`_CHARACTER_TOKENS` / `_BACKGROUND_TOKENS` / `_OBJECT_TOKENS` / `_FROM_THE_REFERENCE_RE` / `_PLURAL_REFERENCE_IMAGES_RE` / `_PHRASE_LOCAL_WINDOW` / `_PHRASE_FALLBACK_WINDOW` / `_GENERIC_VERB_TOKENS` / `_GENERIC_NOUN_TOKENS` / `_classify_window` / `_is_plural_reference_images` / `_has_generic_instruction_signal` / `classify_from_the_reference`) = 0 hits. (2) `scene_generation_coordinator.py`: `validate_attached_refs(` call site 모두 explicit `reference_phrase_kinds=` keyword 동반 0 missing. (3) `render_prompt_card.py:1240-1320`: Area #5 closed-list literal (`"the existing X"` / `"use the X from the reference"` / `"matching the reference camera"` / `"the door from the reference"` 등) = 0 hits. (4) `prompt_service.py:365-374`: phantom prohibition closed-list literal = 0 hits | Codex iter 2 I-1 pattern (Area #4) 준수 — `hits=0; for ...; if rg ...; then hits=1; fi; done; test "$hits" -eq 0` form 강제 (`&& exit 1 \|\| true` silent pass 금지). **Global production `"from the reference"` literal 0 주장 금지** — Area #5 in-scope file 4 그룹만 strict scope. 다른 production file 의 legitimate reference (도구 instructional context 등) 잔존 가능 |
| Active prompt | `prompts/_base/scene_image/4.*/`, `prompts/_base/t2i_review/5.*/`, `prompts/_base/scene_detail/26.*/` 안 (a) `"PHANTOM PHRASE PROHIBITION"` 0, (b) `"close_framing_existing_ref"` 0, (c) `"from the reference"` / `"from Reference image N"` enumeration 0, (d) `"the existing X"` closed-list 0 | active prompt = `<latest version dir>` 만. archived (`3.202605101200/`, `4.202605150957/`, `25.202605161320/`) = residue gate 외 (history 보존) |
| Docs / architecture | roadmap §5.5 ✓ closed marker + §11 closed area 추가 + §14 다음 진입점 update + 본 spec body | Area #4 §11 pattern (closed area boundary 표) 일관 |
| **Out-of-residue** | `docs/superpowers/specs/2026-05-09-attached-reference-identity-contract-design.md` (archived spec) / archived prompt versions / `_audit_outputs/` audit run 출력 / legacy comment in unrelated module | history / non-active surface |

## 10. Risk + Mitigation

| Risk | Source | Mitigation |
|---|---|---|
| LLM 이 `reference_phrase_kinds` 를 잘못 emit (false-negative 위험) | Area #5 핵심 risk — sidecar 가 ground-truth source 됨, 잘못 emit 시 step 6 우회 가능 | (a) producer prompt instruction = principle-only, 추상 원칙. (b) cp invalidation 강제 = producer rerun. (c) step 4 legacy close-framing behavior 와 step 6 structured sidecar behavior 를 분리: step 4 는 기존 `required_refs` background close handling 보존, step 6 은 declared `background` kind 를 strict attached_meta compare (close framing 무관). (d) canary 5 scenario (3 kind + empty + strict violation) (W4) verify. |
| caller signature 확장 시 import 순환 / signature backward-incompatibility | `validate_attached_refs` signature 확장 — 2 caller (single + variation) cascade 의무 | cascade grep pre-step (W0) — `rg "validate_attached_refs" backend/` 의 모든 call site enumerate. 본 area = 2 site, 외부 import 0 (verified Q0-1 trace). |
| t2i_review mutation 이후 sidecar stale | Codex Risk 2026-05-18: t2i_review 가 prompt body mutate 하면 sidecar 가 mutation 후 phrase set 과 불일치 | issue category 4 (`close_framing_existing_ref`) 폐기 = sidecar ownership scene_detail only. t2i_review 의 다른 category (1/2/3/5/6) 는 phantom phrase 와 무관 — sidecar 변경 0. Area #6 (mutation redesign) 으로 carry. |
| Area B-min identity contract 침범 | step 1-5 exact match 보존 의무 | spec §4 closed area boundary 표 + W2 rewrite scope = step 6 만 (`ref_contract_validator.py:425-457`), line 283-417 변경 0. test cascade = step 1-5 기존 test 모두 PASS 보존. |
| caller behavior 변경 (Fix C typed failure dict) | variation path retry/skip semantics — `_failure_reason=REF_CONTRACT_VIOLATION` typed dict 의무 | spec §3.3 variation path 본문 = Fix C wording 보존. behavior 변경 0. |
| residue gate scope inflation | Tier 1 lesson — closure narrow 의무 | §1.3 Out-of-scope 명시 5 항목 + §9 residue gate strict 0 / out-of-residue 분리 표. archived prompt / spec history / unrelated literal = out-of-residue. |

## 11. Codex iter loop expectation (10+ iter, Area #4 pattern donor)

| Stage | Iter | Expected verdict |
|---|---|---|
| spec | 1 | NEEDS_REVISION_BLOCKING or MINOR — wording drift / cascade undercount / Q0 trace 정합 / non-claim 명시 부족 fix 가능 |
| spec | 2 | NEEDS_REVISION_MINOR or APPROVED — minor wording |
| spec | 3 | APPROVED_FOR_PLAN |
| plan | 1 | NEEDS_REVISION_MAJOR — residue grep form / cascade enumeration / canary deterministic / fixture migration scope |
| plan | 2 | APPROVED_FOR_EXECUTION |
| W0 / cascade prep | review note | included in plan handoff (본 spec body + cascade grep table 자체 = W0 deliverable, separate execution wave 아님) |
| W1 | per-wave | APPROVED_FOR_W2 |
| W2 | per-wave | APPROVED_FOR_W3 |
| W3 | per-wave | APPROVED_FOR_W4 |
| W4 | range review | APPROVED_FOR_PUSH or NEEDS_REVISION_MINOR (Codex 직접 amend 요청) |

## 12. Cross-document sync (closure 시 의무)

- `docs/superpowers/specs/2026-05-16-track-b-semantic-debt-roadmap-design.md` §5.5 row → `### 5.5 Area #5 — Reference phrase / phantom guard replacement ✓ closed C-5 (Tier 2 #2, 2026-05-18+)` marker. Fix scope 본문 = "v1 closure complete" 명시.
- 같은 spec §11 closed area 표 → Area #5 추가 (commit hash 채워서).
- 같은 spec §14 다음 진입점 → Tier 2 #3 후보 update.
- closure memo `session_20260518_area_5_reference_phrase_phantom_guard_sot_v1_closure.md` 작성.
- MEMORY.md index update.

## 13. 함정 (Tier 1 + Area #4 carry + Area #5 신규)

**Tier 1 + Area #4 carry 26** (Area #1/#2/#3/#4 closure memo reference 보존):

1. Plan template cascade enumeration defect → BLOCKING pre-step #1 강제
2. Wording iteration cascade → BLOCKING pre-step #2 강제
3. tmux_message + Enter 의무 (양방향 trigger)
4. 사용자 결정 ask 전 Codex 의논 의무 (memory rule 7a maximally strict)
5. VLM 절대 금지
6. Closure wording MAY/MUST NOT 분리 spec 의무
7. Push gate strict
8. Roadmap §5.X stale wording carry
9-16. Area #1/#2/#3 carry (closure memo reference)
17-26. Area #4 신규 함정 (element_id naming convention / W3 fixture cascade undercount / scene_context_loader signature 보존 / archived fixture 변경 0 / canary deterministic / render_contracts boundary 보존 / Codex iter 1 C1 wording 재도입 risk / Codex iter 2 residue grep silent pass / Codex plan iter 1 canary deterministic / assert_fresh_llm_evidence contract validator)

**Area #5 신규 함정 후보**:

27. **sidecar staleness through prompt mutation** — t2i_review `.replace()` blind mutation 이 prompt body 변형 후 sidecar 가 mutation 결과 반영 X. 해결 = issue category 4 폐기 (Area #5), 다른 mutation = Area #6 carry. 위반 시 sidecar 가 ground-truth 역할 X, gate 4 silent fallback 발생.

28. **variation-level sidecar 의무, shot-level union 금지** — `rpc.asset_requirements` 안 single list 형식은 shot-level union → false-positive (variation A 에 없는 kind 가 variation B 때문에 검증됨). per-variation field 만 채택 (Codex 2026-05-18 발견).

29. **`reference_phrase_kinds` missing / None silent fallback 차단** — caller 가 `reference_phrase_kinds=` keyword 를 누락하거나, `build_scene_attached_refs` 가 4th return 값을 누락/None 으로 반환하거나, variation dict 에 required field 가 없을 때 default `[]` 로 흡수하면 step 6 가 우회됨. Gate 4 위반. → keyword-only required + None fail-fast + selected variation field direct read 강제 (still_data side-effect injection 0).

30. **`build_scene_attached_refs` 4-tuple cascade undercount risk** — single path helper return shape 변경 시 caller 의 unpacking + 의존 test fixture cascade enumeration 의무. Tier 1 lesson #1 cascade grep pre-step 강제.

31. **closed-list 폐기 후 LLM alternative phrasing risk** — A058 / A089 / P024 / P087 closed-list 제거 후 LLM 이 unforeseen phrase 사용. 그러나 sidecar 가 ground-truth → step 6 가 phrase-blind 이므로 false-positive 발생 X. 그러나 false-negative 발생 가능 (LLM 이 reference attribution 을 declare 안 함). → producer prompt instruction principle wording 정확성 + canary 5 scenario (3 kind + empty + strict violation) verify 의무.

32. **scene_detail v26 schema 추가 시 기존 scene_detail v25 fields 보존**. `subject_reference_policy` (Area #1) + `gaze_direction_kind` / `gaze_target_id` / `subject_state` (Area #2 — shot_staging schema, scene_detail consumer) + `element_scope` (Area #4) 모두 보존. **Area #3 (Visibility/physical presence) 는 scene_detail schema field 추가 0** — shot_director mutation 박탈 + prompt v6 Korean grammar bundle 만 (no `visibility_decisions` preservation requirement exists). v26 schema = v25 superset + `t2i_variations[].reference_phrase_kinds` 추가만.

33. **`prompt` arg 보존 vs phantom guard 가 prose parse 안 함** — Area #5 = signature 의 prompt arg 보존 (error message context) but step 6 안에서 prompt body read 0. spec wording = "phantom guard does not parse prompt prose; prompt arg is used solely for error message context"

34. **`is_close_framing` axis 분리 의무** — step 4 `required_refs` background logic 의 legacy close-framing handling 은 보존하지만, step 6 `reference_phrase_kinds` 는 strict exact compare 이므로 background close skip 없음. 두 axis 를 섞으면 structured sidecar false-negative 재발. framing_scale enum SOT v1 (closed C-2) boundary 보존은 step 4 한정.

35. **Codex iter 1 finding 흡수 시 wording drift 차단** — Area #4 lesson 1 (Codex 의 본문 fix 시 §6 dependency graph / §13 함정 / 다른 section cross-document grep). 본 spec 도 동일 — fix-up 후 grep verify 의무.

36. **roadmap §5.5 / §11 / §14 update 의무 (Codex Risk 5)** — W4 closure 시 roadmap stale wording carry 함정 (Tier 1 carry #8). 자동 점검 의무.

## 14. References

- **Track B roadmap (Area #5 boundary)**: `docs/superpowers/specs/2026-05-16-track-b-semantic-debt-roadmap-design.md` §5.5 / §6 / §7.2 / §10 / §13 / §14
- **Area #1 pattern donor**: `session_20260516_area_1_id_outlook_reference_policy_sot_v1_closure.md` (4-layer + atomic delete + cp invalidation)
- **Area #2 pattern donor**: `session_20260517_area_2_state_gaze_separation_closure.md` (helper SOT split + 4 reviewer round)
- **Area #3 pattern donor**: `session_20260518_area_3_visibility_physical_presence_sot_v1_closure.md` (shot_director mutation 박탈 + prompt Korean grammar bundle)
- **Area #4 pattern donor (직접 선행)**: `session_20260518_area_4_scene_consistency_element_scope_sot_v1_closure.md` (Tier 2 first wave — BLOCKING 5 pre-step + canary 3 deterministic + Codex iter 10 loop)
- **framing_scale prerequisite**: `session_20260515_framing_scale_enum_sot_v1_closure.md` (`is_close_framing` boolean consumer)
- **Area B-min boundary**: `session_20260513_area_b_render_contracts.md` (`required_refs` / `attached_meta` identity contract)
- **Audit evidence**: `backend/tests/_audit_outputs/semantic_string_debt/full_20260516_0105/triage_report.md` (P006 / A058 / A089 / P024 / P087 본문)
- **D5 identity-level contract spec**: `docs/superpowers/specs/2026-05-09-attached-reference-identity-contract-design.md` §4.4 (step 6 phantom guard original design — Non-goals: "classify_from_the_reference 알고리즘 재설계 X" 정책 override 의무. 본 area = 그 정책 override 의 area 진입점.)
- **Codex 의논 hard rule**: `feedback_codex_mcp_discussion_workflow.md` (rule 7a maximally strict)
- **VLM 금지**: `feedback_no_vlm_dependency.md`
- **4-gate LLM-based judgment**: `feedback_llm_based_judgment.md`
- **subagent dispatch policy**: `feedback_subagent_model_opus.md`

---

**Status**: spec design closure (brainstorming Q1-Q7 모두 닫힘). Codex spec iter 1 review 진입 의무.

**Codex 의논 자체 채택 7 항목 (사용자 ask 0, rule 7a maximally strict)**:

1. Sequencing = #5 먼저, #11 boundary carry
2. Fix scope = (D) Hybrid refined, `reference_phrase_kinds` per-variation enum array
3. A045 = Area #7 carry
4. P024 + P087 phantom 부분만 Area #5 in-scope (split)
5. (β) keyword-only required arg (rpc 안 X — shot-level union false-positive risk)
6. variation-level emit 의무 (per-variation)
7. t2i_review issue category 4 폐기 (sidecar ownership scene_detail only, Area #6 carry 회피)
