---
title: C7 — shot_dependency_t2i subject_kind structured signal v1 (fix-critical-1 Tier β #4)
date: 2026-05-20+
status: closed (C7 v1 W1-W2 complete — Codex per-wave APPROVED + W1-W2 range review APPROVED_FOR_PUSH)
roadmap_ref: docs/superpowers/specs/2026-05-16-track-b-semantic-debt-roadmap-design.md §5.12 (Area #12 — shot_dependency_t2i schema description vs system.md drift) + docs/fix-critical-1/index.html C7 section (locked persistent guide)
fix_critical_doc_ref: docs/fix-critical-1/index.html C7 section (lines 821-908)
audit_ref: roadmap §5.12 audit rows A059 / A060 / A061 / A063 (5월 16일 full audit triage — backend/tests/_audit_outputs/semantic_string_debt/full_20260516_0105/, untracked artifact; tracked closure marker = roadmap §5.12 + fix-critical-1 C7)
prerequisites:
  - C1 perception_mode enum SOT v1 (closed 2026-05-20+) — 변경 0.
  - C2 owned_object_usage[] echo v1 (closed 2026-05-20+) — 변경 0.
  - C3 detail_steps focus rewrite 폐기 v1 (closed 2026-05-20+, origin/main HEAD `1e8cc2a`) — HEAD anchor. C3 `_strip_invalid_sids` 변경 0.
  - Area D-next-min (shot_dependency_t2i v7 + 2-enum keep_elements, commit `5f3da38`) — closed boundary, 본체 보존.
non_supersedes:
  - C6 #10 color_palette_intent / C9 L-7 body-light verb / C4 #8 background classifier / C5 #9 action segment SOT / C8 #7 잔여 sub-area split / C10 future structured carries.
---

# C7 — shot_dependency_t2i subject_kind structured signal v1

## 1. Purpose + Scope

### 1.1 Purpose

**roadmap §5.12 (Area #12) + audit A059/A060/A061/A063**: `shot_dependency_t2i` producer 가 LLM 으로 emit 하는 `keep_elements[]` entry 는 `{label, kind}` 형태. `kind` 는 JSON schema `enum [environment, static_prop]` 로 schema-strict 하지만, `label` 은 `type:string` 일 뿐 — "인물/캐릭터/신체 묘사 금지" 는 **schema 의 enforcement 가 아니라 prompt instruction only**.

3가지 결함:

1. **False enforcement claim (drift)** — `system.md:148` 이 `"다음 label 패턴은 enum 위반으로 fail-fast 거부됩니다"` 라고 명시하지만, `backend/app/core/keep_elements.py:validate_keep_elements_entry()` 는 `label` 의 `isinstance(str)` + `kind ∈ KEEP_ELEMENT_KINDS` 만 검사 — **label 내용에 대한 fail-fast 경로 0**. prompt 가 코드에 없는 동작을 주장. roadmap §5.12 가 분류한 "schema description ↔ system.md drift" (G2 instance).
2. **Silent miss** — producer LLM 이 `{"label": "unconscious man lying on the floor", "kind": "static_prop"}` 처럼 인물 묘사 label + 유효 `kind` 를 emit 하면 `validate_keep_elements_entry()` / `validate_keep_elements()` 전부 통과. 인물 상태 보존 책임이 별도 layer (`scene_consistency` / `character_state_variant` / `semantic_contract_router`) 인데, keep_elements 에 인물이 새어 들어가도 검출 0.
3. **Non-enforced lexicon ban** — `system.md:156` 의 11-단어 ban list (`human / person / character / body / figure / man / woman / detective / prisoner / child / person silhouette`) 는 prompt instruction only — code/schema enforcement 0.

본 spec v1 = **OQ-C7 option (a) — `keep_elements[].subject_kind` structured signal 신설** (Codex C7 entry sanity 결정). producer LLM 이 각 entry 의 인물/비인물 여부를 explicit enum 으로 emit → code 가 enum 을 소비 + `human_or_character_reference` 면 fail-fast. 코드에 lexicon/substring banned-list validator 를 추가하지 않음 — open-world semantic 판단은 LLM-produced structured SOT, code 는 enum 만 소비 ([[feedback_llm_based_judgment]] 정합).

### 1.2 In-scope

- **producer v8 prompt-pack 신규 dir** (`prompts/_base/shot_dependency_t2i/8.<W1_UTC>/`):
  - `schema.json` — `keep_elements[].items.properties` 에 `subject_kind` enum field 신설 (`required` 에 포함, `additionalProperties:false` 유지). `kind` 2-enum + `label` string 본체 보존.
  - `schema.json` description cleanup — `keep_elements` `kind` description 의 `PERSON/CHARACTER/BODY ... FORBIDDEN` prose → `subject_kind` cross-reference 1줄. `ignore_elements` description 의 closed-list `(no 'if...', 'when...', 'only if')` 열거 → 일반 instruction (system.md cross-ref).
  - `system.md` — `subject_kind` emit instruction section 신설. `:148` false `"fail-fast 거부됩니다"` claim 을 `subject_kind` 기반 TRUE 진술로 정정. `:156` 11-lexicon ban list 폐기. `:161-163` enum-외-값 / 인물 지칭 단어 instruction → schema/`subject_kind` cross-reference.
- **version cascade** (output shape change — 신규 required field):
  - `backend/app/core/version_registry.py` — `MODULE_VERSIONS["shot_dependency_t2i"]` `1.4.0` → `1.5.0` + `_MODULE_INFO` `prompt_dependency` `shot_dependency_t2i/v7` → `/v8` + `updated_at`.
  - `backend/app/core/step_manifest.py` — `STEP_MANIFEST["shot_dependency_t2i"]["schema_version"]` `3` → `4` + 주석.
  - `backend/app/core/steps/scene_context_loader.py` — `_load_dependencies()` 의 accepted `schema_version` literal `3` → `4` + envelope-mismatch `AppError` message 정정 (v7/schema_version 3 을 legacy 로 안내). **제어 흐름 (envelope mismatch → AppError raise) 불변** — accepted literal/message 만 갱신 (Codex spec review BLOCKING 1 흡수 — schema_version 4 cp 를 loader 가 legacy 오판해 차단하는 consumer-break 방지).
  - cp invalidation accepted — schema_version 3→4 mismatch 시 `shot_dependency_t2i` 는 `_LEGACY_SCHEMA_BUMP_ALLOWLIST` (`{entity_t2i}` only) 밖이므로 resume 시 BLOCK → 운영자 명시 force re-run (D6 T5d 패턴, step_manifest `:755` 주석 설계). **allowlist 변경 0**.
- **code consume** — `backend/app/core/keep_elements.py`:
  - `KEEP_ELEMENT_SUBJECT_KINDS` frozenset 신설 (`{non_human_visual_element, human_or_character_reference}`).
  - `validate_keep_elements_entry()` 에 `subject_kind` presence + enum check + `subject_kind == "human_or_character_reference"` → AppError fail-fast (routing layer 안내 message). `KEEP_ELEMENT_KINDS` + `kind` enum check 본체 보존.
- **C7 integrated test 신설** (`backend/tests/integration/test_c7_shot_dependency_subject_kind.py`) — no live LLM, NO VLM. helper unit + v8 schema/version source canary (G1-G9, §7). C1/C2/C3 `test_c{1,2,3}_*` 명명 정합.
- **test blast radius 흡수** — 신규 required field `subject_kind` 도입으로 깨지는 기존 keep_elements 구성 test 전수 정정 (`test_area_d_next_shot_dependency_t2i.py` B1-B7 v8 alignment 포함 — §5.1 / §8 R3, plan 이 grep 으로 정확 enumerate).
- **W2 closure docs atomic**.

### 1.3 Out-of-scope (Codex narrow scope guard 흡수)

- **code-side substring/lexicon banned-noun validator**: 추가 안 함 — Codex narrow guard 명시. C7 의 enforcement 는 producer-emit `subject_kind` enum 소비 only. label string 을 code 가 정규식/사전 매칭하지 않음 ([[feedback_llm_based_judgment]] Semantic Regex Ban).
- **`KEEP_ELEMENT_KINDS` / `kind` enum 본체**: `{environment, static_prop}` 2종 보존. 3번째 `kind` (`character` / `immobilized_character` / `pose`) 재도입 0.
- **Area D-next-min (commit `5f3da38`) closed boundary 본체**: keep_elements `{label, kind}` 2-enum 설계 보존 — `subject_kind` 는 그 위에 차원 추가 (`kind` = 환경/소품 axis, `subject_kind` = 인물/비인물 axis, orthogonal).
- **`label_category` 안**: 기각 — `kind` 와 의미 중복 (doc + Codex 명시).
- **OQ-C7 option (b) `contains_person_reference: bool`**: 미채택 — Codex 결정 (a) `subject_kind` enum (§3.1). bool naming 의 구체적 consumer 필요성 미발견.
- **shot_dependency_t2i_step.py `_process_llm_result` / `_handle_llm_call_for_loc` 제어 흐름**: 변경 0 — entry 별 validation 은 기존대로 `validate_keep_elements_entry()` 위임. helper 가 새 field 를 검사하면 step 은 자동으로 그 fail-fast 를 re-raise (`except AppError: raise` 기존 경로).
- **consumer loader 제어 흐름**: `scene_context_loader` / `scene_reference_service` / `scene_checkpoint_loaders` 의 분기 구조 + `validate_keep_elements()` 위임 경로 = 변경 0. 단 `scene_context_loader._load_dependencies()` 의 accepted `schema_version` literal·message 는 v8 정합 (3→4) 으로 갱신 — control flow (envelope mismatch → raise) 는 유지, accepted 값만 이동 (§5.1 W1-B, Codex spec review BLOCKING 1 흡수). helper 강화로 인한 entry 검증 강도 상승은 4 consumer 전부에 자연 전파 (§4.2).
- **`ignore_elements` field 의 동작/타입**: 변경 0 — description wording cleanup (closed-list 열거 제거) only, behavior 불변.
- **VLM / live LLM / semantic regex / open-world phrase regex**: 모두 ban.
- **C1·C2·C3 변경 사항 / C4·C5·C6·C8·C9·C10**: 별도 area.

## 2. W0 Evidence Inventory (live, HEAD `1e8cc2a`)

### 2.1 L1 — producer schema `prompts/_base/shot_dependency_t2i/7.202605151200/schema.json`

`dependencies[].items.properties.location_refs.items.properties.keep_elements` (:30-49):

| 줄 | 현재 상태 |
|----|-----------|
| `:35-38` | `label`: `{type: string, description: "Visual description of the non-human element ... no person/character/body descriptions ..."}` — string only, 인물 묘사 금지는 description 안 instruction. |
| `:39-43` | `kind`: `{type: string, enum: [environment, static_prop], description: "... PERSON/CHARACTER/BODY descriptions are FORBIDDEN — character state ... handled by separate layers ..."}` — enum schema-strict, 단 description 안 person-ban prose 가 별도 layer 책임을 prose 로 서술. |
| `:45` | `required: ["label", "kind"]` |
| `:46` | `additionalProperties: false` |
| `:28` | `ignore_elements` description = `"... No add/replace/adjust instructions. No conditional phrasing (no 'if...', 'when...', 'only if'). ..."` — closed-list 열거 (roadmap §5.12 G2 drift 대상). |

`kind` enum 만 schema enforcement — `label` 안 인물 단어는 schema invariant 아님.

### 2.2 L2 — producer prompt `prompts/_base/shot_dependency_t2i/7.202605151200/system.md`

| 줄 | 현재 상태 | 분류 |
|----|-----------|------|
| `:117-156` | `### 절대 규칙 — 인물 (person / character / body) 묘사 절대 금지` section | prompt instruction |
| `:146-148` | `### ❌ 금지 — 어떤 형태든 인물 묘사 포함 금지` + `다음 label 패턴은 enum 위반으로 fail-fast 거부됩니다:` | **FALSE CLAIM** — code 는 label 내용 fail-fast 안 함. |
| `:149-154` | `❌ {"label": "unconscious man ...", "kind": "static_prop"}` 류 인물 label 예시 6개 (모두 valid `kind`) | 예시 — subject_kind 도입 후 재서술 대상 |
| `:156` | `human / person / character / body / figure / man / woman / detective / prisoner / child / person silhouette 등 인물 지칭 어떠한 단어도 label 에 포함 금지.` | **11-lexicon ban — code/schema enforcement 0** |
| `:161` | `kind 값은 위 2개 중 정확히 하나 — 그 외 값 금지 (immobilized_character / character / pose 등 enum 외 값 절대 금지).` | description-in-instruction (roadmap §5.12 essence) |
| `:163` | `label 에 인물 지칭 단어 (human/person/character/body/...) 포함 금지.` | lexicon 재열거 |

### 2.3 L3 — code `backend/app/core/keep_elements.py`

- `KEEP_ELEMENT_KINDS: frozenset = frozenset({"environment", "static_prop"})` (:24-26) — Area D-next-min closed boundary.
- `validate_keep_elements_entry(entry, *, label_source, error_code)` (:29-84) — 검사 순서: (1) `not isinstance(entry, dict)` → error / (2) `"label" not in entry or "kind" not in entry` → `"missing required keys"` / (3) `not isinstance(entry["label"], str)` → `"label must be str"` / (4) `entry["kind"] not in KEEP_ELEMENT_KINDS` → `"must be one of"`. **`label` 내용 검사 0** — 인물 단어 label + valid kind 통과.
- `validate_keep_elements(keep_elements, *, ...)` (:87-130) — list 순회 + str entry 면 legacy-v5 error code, 그 외 `validate_keep_elements_entry()` 위임. 본체 변경 불필요.
- module docstring (:3) `"producer (shot_dependency_t2i, schema v7)"` — v8 로 갱신 (cosmetic).

### 2.4 L4 — consumers

`validate_keep_elements_entry` / `validate_keep_elements` 호출 site:

| site | 호출 |
|------|------|
| `backend/app/core/steps/shot_dependency_t2i_step.py:56-60` | `_process_llm_result()` 가 entry 별 `validate_keep_elements_entry()` — producer-side L1 SOT. `except AppError: raise` (:116-119) 로 shape 위반 re-raise. |
| `backend/app/core/steps/scene_context_loader.py:128-142` | `_load_dependencies()` — **envelope `schema_version != 3` → `AppError` (`expected 3`) hard gate** (Area D-next-min). v8 (schema_version 4) cp 를 legacy 오판해 차단 → **W1 이 accepted literal/message 를 4 로 갱신 의무** (Codex BLOCKING 1). 이어서 entry 별 `validate_keep_elements()` (:158). |
| `backend/app/services/scene_reference_service.py:1006, 1025` | `validate_keep_elements()` — envelope schema_version gate 없음. helper 강화 자연 전파. |
| `backend/app/services/scene_checkpoint_loaders.py:90-113` | `load_shot_dependency_map()` — **envelope schema_version check 없음** (L2 entry-level only). entry 별 `validate_keep_elements()` (:108). non-empty keep_elements v7 cp 의 `subject_kind` 누락은 helper 가 검출하나, **all-empty keep_elements v7 cp 는 검사할 entry 가 없어 통과** (§3.4 OQ-C7-4). |

helper (`validate_keep_elements_entry`) 강화는 4 consumer 전부에 자동 전파 — entry helper site 별도 수정 불필요. 단 `scene_context_loader` 의 envelope schema_version literal 은 §5.1 W1-B 에서 별도 갱신.

### 2.5 version artifacts

- `version_registry.py:39` — `MODULE_VERSIONS["shot_dependency_t2i"] = "1.4.0"` (주석: Area D-next-min, v7 prompt + schema_version=3).
- `version_registry.py:141-144` — `_MODULE_INFO["shot_dependency_t2i"] = {prompt_dependency: "shot_dependency_t2i/v7", updated_at: "2026-05-15"}`.
- `step_manifest.py:740-758` — `STEP_MANIFEST["shot_dependency_t2i"]`, `schema_version: 3` (:757).
- `step_manifest.py:71` — `_LEGACY_SCHEMA_BUMP_ALLOWLIST = frozenset({"entity_t2i"})` — `shot_dependency_t2i` 미포함 (변경 0).
- prompt dir 최신 = `prompts/_base/shot_dependency_t2i/7.202605151200/`. `load_prompt`/`load_schema` 는 latest numbered dir resolve — v8 dir 신설 시 자동 채택.

### 2.6 기존 test surface (W1 정정 대상)

- `backend/tests/services/test_area_d_next_shot_dependency_t2i.py` — B1 (`:39-61` v7 schema kind enum, `startswith("7.")` dir filter), B2 (`:68-105` `validate_keep_elements_entry` malformed/valid), B3 (`:125-233` `_process_llm_result`/`_handle_llm_call_for_loc` payload), B4 (`:240-248` `schema_version == 3`), B5 (`:255-261` `MODULE_VERSIONS == "1.4.0"` + `prompt_dependency v7` + `updated_at`), B6 (`:268-286` v7 system.md grep, `startswith("7.")` filter), B7 (`:293-326` legacy immobilized entry).
  - B1/B6 의 `startswith("7.")` filter 는 v8 dir 추가 후에도 v7 dir 만 resolve → production(v8) 미검증 stale. W1 이 v8 로 갱신.
  - B4/B5 = version literal pin → W1 version cascade 와 동시 갱신.
  - B2/B3/B7 의 valid keep_elements entry 는 `subject_kind` 부재 → helper 강화 후 fail → W1 동시 정정.
- 기타 keep_elements entry 구성 test (`test_detail_steps_keep_elements.py` / `test_scene_checkpoint_loaders.py` / `test_scene_context_loader_load_dependencies.py` / `test_scene_reference_service.py` / `test_forward_zoom_targets.py` / `test_render_prompt_card*.py` / `test_g4_1_card_consumer_wiring.py` / `test_scene_reference_service_close_ref_usage_matrix.py` / `test_prompt_service_label_routing.py` / `test_phase4_iter7_stabilization.py`) — `validate_keep_elements*` 경로를 타는 것만 정정 대상. plan 이 grep + 실제 validation 경로 확인으로 정확 enumerate (§8 R3).

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

### 3.1 OQ-C7 — structured signal naming

**결정: option (a) `subject_kind` enum** (Codex C7 entry sanity 명시 결정).

- `keep_elements[].subject_kind`, `required`, enum 정확히 `["non_human_visual_element", "human_or_character_reference"]`.
- 근거: `kind` (`environment`/`static_prop` = 환경/소품 axis) 와 orthogonal 한 명시적 structured semantic signal (인물/비인물 axis). AppError message 가 자기설명적 (`expected non_human_visual_element, got human_or_character_reference`) + keep_elements 외 routing 안내 자연스러움. bool polarity 모호성 회피.
- option (b) `contains_person_reference: bool` 기각 — Codex 명시 ("current evidence does not show a concrete consumer need for boolean naming"). `label_category` 기각 — `kind` 의미 중복 (doc 명시).

### 3.2 OQ-C7-2 — schema_version / version cascade

**결정: schema_version 3 → 4 + version_registry bump + cp invalidation accepted** (Codex 권장).

- `subject_kind` = 신규 `required` output field → output shape change → schema_version bump 의무.
- `STEP_MANIFEST["shot_dependency_t2i"]["schema_version"]` 3 → 4.
- `MODULE_VERSIONS["shot_dependency_t2i"]` `1.4.0` → `1.5.0` (minor — output field 추가) + `prompt_dependency` `/v7` → `/v8` + `updated_at`.
- `shot_dependency_t2i` 는 `_LEGACY_SCHEMA_BUMP_ALLOWLIST` 밖 → 기존 v7 cp 는 resume 시 BLOCK, 운영자 명시 force re-run (D6 T5d 설계 의도 — step_manifest `:755` 주석). **allowlist 변경 0**.

### 3.3 OQ-C7-3 — wave 구조 (entry-sanity proposal 3-wave → 2-wave 조정)

**결정: 2-wave — W1 = producer v8 prompt-pack + version cascade + code consume + 전체 test atomic / W2 = closure docs.**

Codex C7 entry sanity narrow guard #2: *"11-단어 prompt list 는 subject_kind 가 schema-required 이고 **consumed** 된 후에만 active v8 에서 제거"*. 3-wave (W1 prompt-pack / W2 code / W3 closure) 로 split 하면 11-lexicon 제거(prompt) 와 consume(code) 가 다른 wave 로 갈라져 v8 `system.md` 를 두 wave 가 연속 편집하거나, "제거됨·미consume" 1-commit window 가 발생 — guard 와 충돌. 따라서:

- producer prompt-pack (schema + system.md, 11-lexicon 제거 포함) + code consume + version cascade + 전체 test 를 **W1 단일 atomic commit** 으로 묶음 — `subject_kind` 의 schema-required·consumed 가 동시 landing, guard trivially 충족.
- C3 (small-tier) 의 W1-full-implementation + W2-closure shape 와 동일 precedent.
- W1 file 수가 많을 경우 (prompt 2 new + version 2 modify + code 1 modify + test N) Codex plan review 가 추가 split 판단 — 본 spec 은 2-wave 를 기준안으로 제시, Codex spec review 에서 확정.

### 3.4 OQ-C7-4 — `scene_checkpoint_loaders` all-empty keep_elements v7 cp (Codex spec review IMPORTANT 1)

**결정: 의도적 허용 — `load_shot_dependency_map()` 에 envelope schema_version check 추가 안 함. plan 이 본 rationale 을 명시 기재 + 확정.**

`scene_checkpoint_loaders.load_shot_dependency_map()` (`:90-113`) 는 envelope schema_version check 없이 entry-level `validate_keep_elements()` 만 수행 (docstring `:74` "L2 intermediate fail-fast"). 따라서:
- non-empty keep_elements v7 cp → entry helper 가 `subject_kind` 누락 검출 → `AppError`. ✓
- **all-empty keep_elements v7 cp** (모든 location_ref 의 keep_elements 가 `[]`) → 검사할 entry 0 → 통과.

allow rationale:
1. **semantic no-op** — keep_elements 가 empty 면 carry 되는 entry 0 → `subject_kind` enforcement 대상 0, 인물 leak surface 0. helper 가 못 잡는 유일 case 가 본질적으로 benign.
2. **primary gate 가 이미 차단** — v7 cp (schema_version 3) 는 (a) `shot_dependency_t2i` step resume 시 `_check_cp_mismatch` schema_version 3≠4 → BLOCK (allowlist 밖), (b) `scene_context_loader._load_dependencies()` envelope gate (W1-B 후 `!= 4` raise) — 정상 pipeline 에서 v7 cp 는 consume 전 force re-run 강제됨.
3. **scope 정합** — `load_shot_dependency_map` 에 신규 envelope check 추가는 C7 narrow remit (subject_kind structured signal) 밖. envelope gate 부재는 본 loader 의 기존 설계 (entry-level only).

plan 은 OQ-C7-4 를 명시 기재 (Codex IMPORTANT 1 — "최소한 rationale 필요"). envelope check 추가가 필요하다고 plan/Codex 가 판단하면 별도 narrow scope 으로 재논의.

### 3.5 OQ-잔여

없음 — Codex C7 entry sanity 의 OQ-C7 결정 + narrow guard + spec review BLOCKING 1 / IMPORTANT 1 전부 §1.2 / §1.3 / §3 / §4 / §5 흡수.

## 4. Invariants

### 4.1 Structured signal SOT invariant
인물/비인물 판단은 producer LLM 이 `subject_kind` enum 으로 emit → code 는 enum 값만 소비. code 어디에도 `label` string 에 대한 정규식 / 사전(lexicon) / substring 매칭 0.

### 4.2 Fail-fast on person reference invariant
`subject_kind == "human_or_character_reference"` 인 keep_elements entry 는 `validate_keep_elements_entry()` 가 `AppError` raise — silent pass 0. message 는 keep_elements 가 인물/캐릭터 ref 를 담을 수 없음 + routing 책임 layer (`scene_consistency` / `character_state_variant` / `semantic_contract_router`) 를 명시.

### 4.3 kind enum 본체 보존 invariant
`KEEP_ELEMENT_KINDS = {environment, static_prop}` frozenset + `validate_keep_elements_entry()` 의 `kind` enum check 본체 불변. 3번째 `kind` (`character`/`immobilized_character`/`pose`) 재도입 0. `subject_kind` 는 신규 field 로 **추가**될 뿐 `kind` 검사를 대체/변경하지 않음.

### 4.4 Schema strict invariant
v8 `schema.json` keep_elements item: `properties` = `{label, kind, subject_kind}`, `required = ["label", "kind", "subject_kind"]`, `additionalProperties: false`. `subject_kind` enum = `KEEP_ELEMENT_SUBJECT_KINDS` 와 정확히 일치 (single SOT — drift 방지, B1-형 test).

### 4.5 Output shape change → version cascade invariant
`subject_kind` required field 추가 = output shape change. `schema_version` 3→4 + `MODULE_VERSIONS` + `prompt_dependency` + `updated_at` 동시 갱신. v8 prompt dir = 신규 dir (`프롬프트 파일 덮어쓰기 금지` — CLAUDE.md). v7 dir 보존.

### 4.6 No false enforcement claim invariant
v8 `system.md` / `schema.json` 어디에도 code 가 뒷받침하지 않는 enforcement 주장 0 — `:148` 의 `"fail-fast 거부됩니다"` 는 `subject_kind` 기반으로 정정되어 실제 code 동작과 일치. `:156` 11-lexicon ban list 는 v8 active prompt 에서 제거 (subject_kind schema-required·consumed 이후).

### 4.7 Closed-world contract regex 정합 invariant
helper 가 사용하는 regex/매칭은 closed-world contract (enum membership check) only — `subject_kind ∈ KEEP_ELEMENT_SUBJECT_KINDS` 는 frozenset membership, open-world 판단 아님. 4-Gate (Semantic Regex Ban) 정합.

### 4.8 NO VLM / no live LLM invariant
C7 코드·테스트 어디에도 VLM 의존 / live LLM 호출 없음. C7 integrated test = helper unit + v8 schema/version source canary only.

## 5. W1-W2 Implementation Plan

### 5.1 W1 — producer v8 prompt-pack + version cascade + code consume + test (1 atomic commit)

**W1-A — producer v8 prompt-pack** (`prompts/_base/shot_dependency_t2i/8.<W1_UTC>/`, 2 new file):

- `8.<W1_UTC>` = v7 dir 의 schema.json/system.md copy 기반 + 아래 변경. `<W1_UTC>` = W1 실행 시점 `date -u +%Y%m%d%H%M` (C2 N-exec-2 / Carry-P021 N-exec-2 lesson — UTC dimension 명시).
- `schema.json`:
  - keep_elements item `properties` 에 `subject_kind` 추가 — `{type: string, enum: ["non_human_visual_element", "human_or_character_reference"], description: <인물/비인물 분류 + keep_elements 는 non_human_visual_element 만 허용 + human_or_character_reference 는 routing 대상>}`.
  - `required` → `["label", "kind", "subject_kind"]`. `additionalProperties: false` 유지.
  - `kind` description 의 `PERSON/CHARACTER/BODY ... FORBIDDEN ...` prose → `subject_kind` cross-reference 1줄 (`environment`/`static_prop` 정의는 보존).
  - `ignore_elements` description (`:28`) 의 `(no 'if...', 'when...', 'only if')` 열거 → 일반 instruction (`see system.md ignore_elements rules` 류). behavior 불변.
- `system.md`:
  - keep_elements section 에 **`subject_kind` emit instruction** 추가 — 각 entry 에 인물/비인물 분류를 정직하게 emit, keep_elements 는 `non_human_visual_element` 만 담고 인물/캐릭터는 별도 layer routing.
  - `:148` `"다음 label 패턴은 enum 위반으로 fail-fast 거부됩니다"` → `subject_kind` 기반 TRUE 진술로 정정 (인물 묘사 label 은 `subject_kind=human_or_character_reference` 로 분류되어 거부됨).
  - `:156` 11-lexicon ban list 폐기 — `subject_kind` 분류 instruction 으로 대체.
  - `:161-163` enum-외-값 / 인물 지칭 단어 instruction → schema enum / `subject_kind` cross-reference 로 정리.
  - `environment` / `static_prop` 정의·예시는 ≥3 회 유지 (B6-형 test 정합).

**W1-B — version cascade + consumer envelope** (3 modify):

- `version_registry.py` — `MODULE_VERSIONS["shot_dependency_t2i"]` `1.4.0` → `1.5.0` + 주석 갱신. `_MODULE_INFO` `prompt_dependency` `/v7` → `/v8` + `updated_at` → W1 날짜.
- `step_manifest.py` — `STEP_MANIFEST["shot_dependency_t2i"]["schema_version"]` `3` → `4` + 주석 (C7 subject_kind output field 추가 — output shape change).
- `scene_context_loader.py` (Codex spec review BLOCKING 1) — `_load_dependencies()` `:129` `if schema_v != 3:` → `!= 4:`; `:133-140` envelope-mismatch `AppError` message 의 `expected 3 (Area D-next-min bump ...)` → `expected 4 (C7 subject_kind bump ...)` + legacy 안내에 v7/schema_version 3 포함. **제어 흐름·error code (`step.scene_context_loader.legacy_keep_elements_cp`) 불변** — accepted literal + message 만 갱신. method docstring `:112-120` 의 `schema_version=3` 언급도 4 로 정정.

**W1-C — code consume** (`backend/app/core/keep_elements.py`, 1 modify):

- `KEEP_ELEMENT_SUBJECT_KINDS: frozenset = frozenset({"non_human_visual_element", "human_or_character_reference"})` 신설 (`KEEP_ELEMENT_KINDS` 인접).
- `validate_keep_elements_entry()`:
  - presence check (step 2) 에 `subject_kind` 포함 — `"label"`/`"kind"`/`"subject_kind"` 중 누락 시 `"missing required keys"`.
  - `kind` enum check (step 4) 후 `subject_kind` 검사 추가: (5a) `subject_kind not in KEEP_ELEMENT_SUBJECT_KINDS` → AppError `"subject_kind must be one of ..."`. (5b) `subject_kind == "human_or_character_reference"` → AppError fail-fast — message 에 keep_elements 가 인물/캐릭터 ref 불가 + routing layer 명시.
  - `KEEP_ELEMENT_KINDS` / `kind` enum check 본체 불변.
- module docstring `schema v7` → `schema v8` (cosmetic). legacy-v5 str message 의 version 언급은 정확성 유지 범위에서 갱신 (선택).

**W1-D — test** (`test_area_d_next_shot_dependency_t2i.py` + `test_scene_context_loader_load_dependencies.py` modify + `test_c7_shot_dependency_subject_kind.py` new):

- `test_area_d_next_shot_dependency_t2i.py` — B1/B6 dir filter v7 → v8, B4 `schema_version == 4` (+ test 명 갱신), B5 `MODULE_VERSIONS == "1.5.0"` / `prompt_dependency "shot_dependency_t2i/v8"` / `updated_at` 갱신, B2/B3/B7 의 keep_elements entry 에 valid `subject_kind` 추가. B7 (legacy immobilized) 의도 보존.
- `test_scene_context_loader_load_dependencies.py` (Codex spec review BLOCKING 1) — module docstring `:1-17` 의 `schema_version != 3` contract 문구 → 4 정정. happy path `test_v7_valid_cp_returns_dependencies` / `test_v7_valid_empty_keep_elements_passes` → cp `schema_version` 3→4 + keep_elements entry 에 valid `subject_kind` 추가. shape-violation test (`test_v7_cp_legacy_string_keep_elements_raises` / `_missing_keep_elements_key_raises` / `_invalid_kind_enum_raises` / `_legacy_immobilized_character_kind_raises` / `_missing_label_field_raises`) → envelope `schema_version` 3→4 (envelope gate 가 먼저 raise 하지 않도록) + 의도된 위반 entry 만 잔존. **신규 test** — legacy v7 cp (schema_version 3) 가 이제 `legacy_keep_elements_cp` 로 raise (accepted = 4 전환 증명). legacy v5/v6 raise test (schema_version absent/1/2) 는 `!= 4` 라 그대로 유효 — 보존.
- `test_c7_shot_dependency_subject_kind.py` 신설 — G1-G9 (§7). helper unit + v8 schema/version canary 포함, no live LLM, NO VLM.
- test blast radius — §2.6 / §8 R3 의 기타 test 중 `validate_keep_elements*` 경로를 타는 것 전수 정정. plan 이 grep 으로 정확 list 산출.

**W1 gate**: 신규 `test_c7_*` 전수 PASS + `test_area_d_next_shot_dependency_t2i.py` 전수 PASS + keep_elements / shot_dependency_t2i / scene_context_loader / scene_reference_service / scene_checkpoint_loaders 관련 test 전수 PASS + 백엔드 전체 회귀 (pre-existing failure 제외 동일 — clean HEAD `1e8cc2a` git stash 대조). `PYTHONPATH=backend backend/.venv/bin/python|pytest`, repo-root cwd (C1/C2/C3 N-exec 정합). W1 = 1 atomic commit.

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

- 본 spec frontmatter `status` → closed + §10 Closure.
- C7 plan frontmatter `status` → closed + plan Closure section.
- roadmap `2026-05-16-track-b-semantic-debt-roadmap-design.md` §5.12 — C7 closure entry 갱신 (§5.13/5.14/5.15 C1/C2/C3 정합 형식 — Status closed / Fix applied / Boundary preserve / Closure verify commit chain).
- `docs/fix-critical-1/index.html` C7 marker — badge ✓ CLOSED + priority list C7→CLOSED / C6→NEXT + priority matrix row 4 status + C7 section closure note.
- audit doc marker — A059-A063 는 untracked full_20260516 triage 출처라 tracked-file marker 의무 없음 (C3 N-5 lesson — audit area 번호 ≠ 실제 site, W2 가 tracked audit 01/02 의 shot_dependency_t2i row 존재 여부 verify 후 있으면만 marker). 없으면 roadmap §5.12 + fix-critical-1 C7 가 tracked closure marker.
- **W2 self-hash 금지** (C2 N-W4-2 / C3 정합) — closure docs 안 W2 자체 commit hash literal 없음. "W1 commit" / "W1-W2" wording, 실제 hash 는 push 후 external memory 기록.

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

## 6. Boundary Preserve

- `KEEP_ELEMENT_KINDS` frozenset + `validate_keep_elements_entry()` 의 `kind` enum check 본체 — 변경 0.
- `validate_keep_elements()` list 순회 + legacy-v5 str branch — 제어 흐름 변경 0.
- `shot_dependency_t2i_step.py` `_process_llm_result` / `_handle_llm_call_for_loc` / forward-ref 검증 / provider failure fallback — 변경 0.
- consumer loader 제어 흐름 — `scene_context_loader` / `scene_reference_service` / `scene_checkpoint_loaders` 의 분기·`validate_keep_elements()` 위임 구조 = 변경 0. **단** `scene_context_loader._load_dependencies()` 의 envelope `schema_version` accepted literal·message 는 v8 정합으로 갱신 (3→4, §5.1 W1-B) — control flow·error code 보존, accepted 값만 이동.
- v7 prompt dir (`7.202605151200/`) — 보존 (덮어쓰기 0).
- `_LEGACY_SCHEMA_BUMP_ALLOWLIST` — 변경 0 (`{entity_t2i}` only).
- C1 perception_mode helper / C2 owned path·sentinel v2 / C3 `_strip_invalid_sids` — 변경 0.
- Area C directionality_class enum SOT — 변경 0.

## 7. Test Gates (G1-G9)

| Gate | 내용 |
|------|------|
| **G1** | valid 비인물 entry 통과 — `validate_keep_elements_entry({"label": "wooden bench against the wall", "kind": "environment", "subject_kind": "non_human_visual_element"}, ...)` no raise. `static_prop` variant 포함. |
| **G2** | person reference fail-fast — `subject_kind == "human_or_character_reference"` entry → `AppError` raise, message 에 routing layer (`scene_consistency` / `character_state_variant` / `semantic_contract_router`) 명시. |
| **G3** | `subject_kind` 누락 fail-fast — `{"label": "x", "kind": "environment"}` (subject_kind 부재) → `AppError` `"missing required keys"`. |
| **G4** | invalid `subject_kind` 값 fail-fast — `{"label": "x", "kind": "environment", "subject_kind": "unknown"}` → `AppError` `"subject_kind must be one of ..."`. |
| **G5** | boundary preserve — 기존 invalid `kind` 여전히 fail-fast (`{"label": "x", "kind": "immobilized_character", "subject_kind": "non_human_visual_element"}` → `AppError` `"must be one of"`); valid `kind` 2종 + `KEEP_ELEMENT_KINDS == {environment, static_prop}` 불변. |
| **G6** | v8 schema strict — `8.<W1_UTC>/schema.json` keep_elements item: `subject_kind` enum == `KEEP_ELEMENT_SUBJECT_KINDS` (single SOT), `required == [label, kind, subject_kind]`, `additionalProperties == false`, `kind` enum == `KEEP_ELEMENT_KINDS` (2종 불변). |
| **G7** | version cascade — `STEP_MANIFEST["shot_dependency_t2i"]["schema_version"] == 4`; `MODULE_VERSIONS["shot_dependency_t2i"] == "1.5.0"`; `get_module_info` `prompt_dependency == "shot_dependency_t2i/v8"`; v8 dir 존재; `_LEGACY_SCHEMA_BUMP_ALLOWLIST == {entity_t2i}` 불변. |
| **G8** | prompt residue canary — v8 `system.md` 에 false `"fail-fast 거부됩니다"` claim 부재 (subject_kind 기반 정정 wording 만) + 11-lexicon ban list 부재 + `subject_kind` emit instruction 존재. v8 `schema.json` `ignore_elements` description 의 `'only if'` 류 closed-list 열거 부재. |
| **G9** | `scene_context_loader._load_dependencies()` envelope (Codex spec review BLOCKING 1 — consumer-break 방지) — v8 cp (`schema_version: 4` + `subject_kind` 포함 keep_elements) 통과; v7 cp (`schema_version: 3`) → `AppError` `step.scene_context_loader.legacy_keep_elements_cp` (message `expected 4`). |

## 8. Risk + Open Issues

- **R1 — W1→W2 사이 enforcement window 없음**: §3.3 결정으로 schema-required·consumed·11-lexicon 제거가 W1 단일 commit 동시 landing — "주장만 있고 enforcement 없음" / "lexicon 제거됐는데 미consume" intermediate state 0.
- **R2 — cp invalidation**: v7 cp (schema_version 3) 는 v8 (schema_version 4) 와 mismatch → resume 시 BLOCK (allowlist 밖). 운영자 force re-run 의무 — C2/C1 의 schema_version bump cp invalidation 정책과 동일 (의도된 동작, D6 T5d). W1 gate 백엔드 회귀로 cp-dependent test 영향 확인.
- **R3 — test blast radius**: 신규 required field `subject_kind` 도입 시 `validate_keep_elements*` 경로를 타는 모든 기존 test (keep_elements entry 를 valid fixture 로 구성) 가 `"missing required keys"` 로 fail. §2.6 의 ~10 test 파일 중 실제 validation 경로를 타는 것을 plan 이 grep + 호출 경로 추적으로 정확 enumerate → W1 동시 정정. helper-validation 을 안 타고 단순 fixture 로만 쓰는 것 (소비 consumer 가 re-validate 안 함) 은 정정 불필요 — plan 이 구분. jsonschema 로 sample data 를 v8 schema 에 validate 하는 test 가 있으면 동일 정정.
- **R4 — B1/B6 dir filter**: `test_area_d_next_shot_dependency_t2i.py` B1/B6 의 `startswith("7.")` filter 는 v8 추가 후 stale v7 만 resolve. W1 이 v8 로 갱신 — production 검증 유지. (Carry-P021 historical-pin 과 달리 C7 은 invariant 가 v8 에도 성립하므로 historical pin 아닌 v8 갱신.)
- **OQ-잔여**: 없음 — §3.4.

## 9. Doctrine references

- [[feedback_llm_based_judgment]] — 4-Gate. C7 = Structured SOT Required (producer `subject_kind` enum emit) + Semantic Regex Ban (code-side lexicon validator 금지) + No Silent Fallback (`human_or_character_reference` fail-fast).
- [[feedback_no_vlm_dependency]] — NO VLM. C7 코드·테스트 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 구분.
- [[feedback_push_no_user_ask]] — Codex APPROVED_FOR_PUSH 시 즉시 push.
- [[project_fix_critical_1_persistent]] — fix-critical-1 Tier β #4, C7 → C6 priority.

## 10. Closure

**C7 v1 W1-W2 closed (2026-05-20+).** Codex per-wave APPROVED + W1-W2 range review APPROVED_FOR_PUSH.

### Commit chain
- **W1** `c8d2391` — keep_elements[].subject_kind structured signal: v8 prompt-pack (`8.202605200618/{schema.json,system.md}`) + version cascade (version_registry 1.4.0→1.5.0 + prompt_dependency v8, step_manifest schema_version 3→4) + scene_context_loader envelope 3→4 + `validate_keep_elements_entry` subject_kind consume + C7 test G1-G9 + blast radius (11 files, 726+/130-).
- **W1 fix-up 1** `e4ee3ed` — scene_context_loader `keep_elements_missing_key` message `schema v7`→`v8` (Codex W1 per-wave non-blocking residue, message wording only).
- **W2** — closure docs atomic (spec/plan status closed + roadmap §5.12 + fix-critical-1 C7 marker + audit 01 §6 #15 closure note).

### Gate result
- W1.1-W1.5 PASS — C7 integration G1-G9 9 passed, targeted 회귀 204 passed.
- W1.6 백엔드 전체 회귀 4056 passed — 잔여 5 failure 는 clean HEAD `1e8cc2a` baseline 과 동일 pre-existing (text_cleaner ImportError / analysis_dispatch_service / prompt_service, C7 무관 — git stash 로 baseline 대조 확인). C7 유발 신규 failure 0.

### Codex review trace
C7 entry sanity `GO_FOR_C7_SPEC_DRAFTING` + OQ-C7 (a) `subject_kind` → spec review `NEEDS_REVISION_BLOCKING` (BLOCKING 1 scene_context_loader hard gate consumer-break + IMPORTANT 1 scene_checkpoint_loaders all-empty keep_elements) → spec amend 1 → spec re-review `APPROVED_FOR_PLAN` → plan review `APPROVED_FOR_EXECUTION` → W1 ENTRY SANITY `APPROVED_TO_ENTER` → (fresh 세션) W1 entry sanity recheck `GO_FOR_W1_ATOMIC_EXECUTION` → W1 per-wave `APPROVED_FOR_W2_ENTRY` (non-blocking residue 1 → W1 fix-up 1) → W1-W2 range review `APPROVED_FOR_PUSH`.

### 함정 (W1 execution)
- **N-exec-1** — W1.6 full regression 의 5 잔여 failure 는 git stash 로 clean HEAD `1e8cc2a` baseline 대조해야 pre-existing 임을 증명. 전체 suite 2회(약 15분) 대신 5개 specific test 만 baseline 에서 재실행해 효율 확보.
- **N-exec-2** — v8 prompt 예시 일관성: output shape 가 3-field 가 되면 schema array description / system.md 작성규칙('두 필드'→'세 필드') / kind 정의 예시 literal 까지 전부 3-field 로 동반 갱신해야 prompt 가 wrong shape 를 가르치지 않음. zoom_in_detail 가이드 예시의 body-part(wrist) 참조는 subject_kind 정합이 깨지므로 비인물 prop 으로 reword (E1-E6 의 자연 확장).
- **N-exec-3** — scene_context_loader `keep_elements_missing_key` message 의 `schema v7` 잔재는 plan §3.4 가 enumerate 안 한 site. envelope literal 외 동일 파일 내 모든 version 언급 string 을 grep 해야 함 (W1 fix-up 1 로 흡수).

### carry priority post-C7
fix-critical-1 진입 순서 LOCKED — 다음 = **C6 #10 detail_steps 색감-감정** (Tier β #5, roadmap §5.10). 이후 C9 L-7 body-light verb → C4 #8 background classifier → C5 #9 action segment SOT → C8 #7 잔여 sub-area split → C10 future structured carries.
