# Area #7a — Visual World Rules SOT + scene_director A047 Absorption v1 Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** `prompts/_base/visual_world_rules/6.202605021400/rules_schema.json` 의 `rule_type` free-string + 6-token-"등" description 을 10-token canonical `enum` strict 화 (A096) + `director_notes` description 안 시나리오-specific example 추상화 (A097) + `system.md` 의 작품-specific 잔존 추상화 (A098) + `prompts/_base/scene_director/8.202604081200/system.md` A047 3 site (line 5-10 / 23-25 / 32-34) 본체 폐기 + principle-only rewrite — VWR rules[] / director_notes 를 physical-presence / visual-presence SOT 로 강제. STEP_MANIFEST `visual_world_rules.schema_version` 신설로 cp invalidation. 11-site downstream cascade = Area #7b 분리. helper 신설 0.

**Architecture:** signature-breaking transition. W0 = inventory + baseline + `prompt_version` field 신설 deferred decision (no commit, memo only). W1 = atomic 단일 commit — VWR `7.20260518HHMM/` prompt pack 2 file 신설 (`rules_schema.json` + `system.md`) + `step_manifest.py:130-141` `schema_version: 1` field 신설 + (deferred decision 결과 신설 결정 시) `prompt_version` field 추가. W2 = atomic 단일 commit — scene_director `9.20260518HHMM/system.md` 신설 (A047 3 site principle-only rewrite). W3 = 3 commit (residue gate 1 file / canary 1 file / closure cleanup spec+roadmap+memory). NO VLM 의무 (canary + residue gate 모두 schema/text/manifest verification only). prompt_loader latest version dir 자동 활성 (file fallback default) — version_registry MODULE_VERSIONS pin 영향 0 (visual_world_rules / scene_director 둘 다 MODULE_VERSIONS 에 absent, W0 verify).

**Tech Stack:** Python 3.12 / pytest 9.0.2, JSON Schema (`jsonschema` package; `enum` keyword reject 행위 Draft 4+ 공통), `app.modules.prompt_loader.load_prompt` + `load_schema` (`prompts/_base/<step>/<version>/<stem>`, numeric-desc latest via `_list_module_versions` + `_version_sort_key`), `app.core.step_manifest` (`schema_version` BLOCK via `step_runner._check_cp_mismatch`), `app.modules.llm.llm_client._validate_local_schema` (`llm_client.py:43`, `jsonschema.validate` wrapper — schema 안 `$schema` 미명시 시 library default validator).

**Spec reference:** `docs/superpowers/specs/2026-05-18-area-7a-visual-world-rules-sot-v1-design.md` (commit `92b81f2` APPROVED_FOR_PLAN, Codex iter 5 verdict).

---

## File Structure

### Prompts — Create (W1 / W2, 3 file 2 dir)

`HHMM` placeholders = W1/W2 commit 직전 UTC 현재 시각 4-digit (e.g., 14:32 UTC → `1432`). W1 과 W2 는 별도 wave 이므로 각각 자체 timestamp 결정 (서로 다른 HHMM 가능). 단일 wave 안 file 들은 동일 HHMM.

**W1 (VWR producer)**:
- Create: `prompts/_base/visual_world_rules/7.20260518HHMM/rules_schema.json`
- Create: `prompts/_base/visual_world_rules/7.20260518HHMM/system.md`

**W2 (scene_director consumer)**:
- Create: `prompts/_base/scene_director/9.20260518HHMM/system.md`

### Production code — Modify (W1, 1 file)

- Modify: `backend/app/core/step_manifest.py:130-141` (`visual_world_rules` entry — `schema_version` field 신설, value `1`)

### Tests — Create (W3a + W3b, 2 file)

- Create: `backend/tests/integration/test_area_7a_residue_gate.py` (residue gate, 4 strict gates + sanity = 7 test)
- Create: `backend/tests/integration/test_area_7a_no_drift.py` (canary, 8 test, no live LLM)

### Closure — Modify (W3c, repo-local)

- Modify: `docs/superpowers/specs/2026-05-18-area-7a-visual-world-rules-sot-v1-design.md` (status: design → closed, closure memo 추가)
- Modify: `docs/superpowers/specs/2026-05-16-track-b-semantic-debt-roadmap-design.md` §5.7 (Sub-A closed mark + carry table Area #7b/c/d/A099/A100/P021 명시)
- Modify: `docs/visual-reliability-audit/2026-05-14-semantic-string-routing-debt-audit/02-prompt-side-hygiene-audit.md` (A047/A096/A097/A098 status mark `closed (Area #7a)`)

### External (W3c, repo 밖 — git stage 절대 금지)

- Create (non-git write only): `/Users/manta/.claude/projects/-Users-manta-Documents-Projects-TheRoad-I1/memory/session_20260518_area_7a_v1_closure.md`
- Modify (non-git write only): `/Users/manta/.claude/projects/-Users-manta-Documents-Projects-TheRoad-I1/memory/MEMORY.md`

본 2 external memory file 은 controller 의 `~/.claude/...` 영역 — repo (`/Users/manta/Documents/Projects/TheRoad-I1`) 외부. `git add` 절대 불가 — write-only + filesystem 정상 작성 확인 step 만. W3c commit content 는 repo-local docs 3 file 만.

### Optional — W0 inventory memo

- Create (optional, non-commit): `docs/superpowers/specs/2026-05-18-area-7a-w0-inventory.md` — Area #4 W0 measurement format 정합 (2-4k bytes 추정). W0 결과 (`prompt_version` deferred decision + baseline pytest scope + version_registry verify) 기록. W3c closure 시 spec body 안 verbatim 흡수 또는 별도 stage.

---

## Atomicity Doctrine

**W0 = no commit.** inventory + baseline verify + deferred decision 결정만. memo file (optional) stage 는 W3c closure 시.

**W1 = 1 task = 1 atomic commit.** VWR prompt pack (rules_schema.json + system.md) + step_manifest.py `schema_version` 신설 모두 단일 commit. signature-breaking (schema enum strict 화 = cp invalidation) — split 시 mixed window (v7 prompt active + cp manifest 옛 stamp 잔존) 발생, KeyError risk. atomic 의무 (Tier 1/2 carry lesson 정합).

**W2 = 1 task = 1 commit.** scene_director v9 system.md 신설. analyze_schema.json 변경 0 (scene_director schema 영향 없음, A047 본체는 system.md only).

**W3 = 3 commit** (W3a canary / W3b residue gate / W3c closure cleanup). 책임 분리.

deferred decision (`prompt_version` field 신설) 결과 — **per-wave ownership** (Codex iter 1 F4 흡수: W1 = VWR fields only, W2 = scene_director fields only, atomic boundary 보존):

**Decision (W0 S8 verdict 2026-05-19, Codex APPROVED via plan amend 3)**: **신설 안함**. Reason (1-line): STEP_MANIFEST entry-level prompt_version field 사용 site 0 (grep step_manifest.py 안 prompt_version = 0), cp invalidation = manifest.schema_version only (`_check_cp_mismatch` line 1517), `prompt_loader` numeric desc latest 자동 활성으로 manifest pin 영향 0. → W1 변경 = `schema_version: 1` 만, W2 step_manifest 변경 = 0.

아래 양 branch 는 history reference — 본 area scope = "신설 안함" branch (line ~72) 만 active.

- **신설 결정 시** (resolved → not applied):
  - W1 commit 안 `step_manifest.py` 의 `visual_world_rules` entry 에만 `prompt_version: "7.20260518HHMM"` 추가. scene_director 변경 0.
  - W2 commit 안 `step_manifest.py` 의 `scene_director` entry 에만 `prompt_version: "9.20260518HHMM"` 추가. VWR 변경 0.
  - W1/W2 각 commit 의 step_manifest 변경 = 본 wave 가 owner 인 prompt directory 의 entry only.
  - (option) 별도 unit test (`backend/tests/unit/test_step_manifest_v3.py` 같은 location) — W1 의 visual_world_rules.prompt_version 검증 + W2 의 scene_director.prompt_version 검증을 본 wave commit 안 같이 추가. cross-wave step_manifest entry edit 절대 금지.
- **신설 안함 결정 시** (active, W0 S8 verdict):
  - file fallback latest version default 만 의존 (prompt_loader numeric-desc latest via `_list_module_versions` reverse=True).
  - W1 의 step_manifest 변경 = visual_world_rules.schema_version=1 만.
  - W2 의 step_manifest 변경 = 0 (commit content = prompt file only).

W0 inventory step 8 에서 결정 + plan amend (이 file 안 Task 2 / Task 3 의 step_manifest 변경 wording 모두 동일하게 amend).

---

## Pre-Commit Verification Doctrine (W1 + W2)

W1 / W2 commit 직전 grep + structural verify 의무. 실패 시 commit 절대 금지.

### W1 (VWR producer) pre-commit verify

1. **Schema structural verify**: 새 `7.20260518HHMM/rules_schema.json` parse → `properties.rules.items.properties.rule_type.enum` length=10 + exact set 확인.
   ```bash
   python3 -c "import json; s = json.load(open('prompts/_base/visual_world_rules/7.20260518HHMM/rules_schema.json')); e = s['properties']['rules']['items']['properties']['rule_type']['enum']; assert e == ['possession', 'transformation', 'ghost', 'projection', 'superpower', 'body_deformation', 'time_period', 'costume', 'technology', 'other'], e; print('PASS')"
   ```
   Expected: `PASS`.

2. **Schema description residue grep** (description 안 6-token + "등" pattern 0):
   ```bash
   grep -E "possession, transformation, ghost, time_period, costume, technology 등" prompts/_base/visual_world_rules/7.20260518HHMM/rules_schema.json
   ```
   Expected: 0 hits.

3. **System.md canonical 10-token list verify**:
   ```bash
   grep -c "possession.*transformation.*ghost.*projection.*superpower.*body_deformation.*time_period.*costume.*technology.*other" prompts/_base/visual_world_rules/7.20260518HHMM/system.md
   ```
   Expected: ≥1 hit.

4. **System.md director_notes prose section 보존** (Carry-A099, 변경 0):
   ```bash
   grep -c "회상/F.B\|환각/현시\|CCTV·모니터·창문\|교차편집/몽타주\|안개·어둠" prompts/_base/visual_world_rules/7.20260518HHMM/system.md
   ```
   Expected: ≥5 hits (5-line generic principle examples 보존).

5. **System.md t2i_context demonym 보존** (Carry-A100, 변경 0):
   ```bash
   grep -c "region-derived demonym" prompts/_base/visual_world_rules/7.20260518HHMM/system.md
   ```
   Expected: ≥1 hit.

6. **STEP_MANIFEST schema_version field 신설 verify**:
   ```bash
   python3 -c "from backend.app.core.step_manifest import STEP_MANIFEST; vwr = STEP_MANIFEST['visual_world_rules']; assert vwr.get('schema_version') == 1, vwr; print('PASS')"
   ```
   Expected: `PASS`.

7. **Smoke pytest** (existing VWR test 통과):
   ```bash
   cd backend && pytest tests/ -k "visual_world_rules" -v
   ```
   Expected: all PASS (또는 known-baseline failure 만).

7 종 모두 PASS 시에만 W1 commit 진행.

### W2 (scene_director consumer) pre-commit verify

1. **A047 Site 1 residue grep** (line 5-10 top 4-item exclusion list 본체 0):
   ```bash
   grep -E "교차편집/몽타주|영상통화, CCTV, 방송 화면|V\.O\.\(보이스오버\)|변장, 쌍둥이 교체, 바디더블" prompts/_base/scene_director/9.20260518HHMM/system.md
   ```
   Expected: 0 hits.

2. **A047 Site 2 residue grep** (line 23-25 인물 판단 반복 0):
   ```bash
   grep "빙의/원격접속 중이면 실제 몸이 안 보이므로 제외" prompts/_base/scene_director/9.20260518HHMM/system.md
   ```
   Expected: 0 hits.

3. **A047 Site 3 residue grep** (line 32-34 parenthetical 0):
   ```bash
   grep "(빙의/원격/V.O. 등)" prompts/_base/scene_director/9.20260518HHMM/system.md
   ```
   Expected: 0 hits.

4. **VWR SOT reference token 1+ 존재** (principle-only rewrite verify):
   ```bash
   grep -E "visual_world_rules|VWR|rule_type" prompts/_base/scene_director/9.20260518HHMM/system.md
   ```
   Expected: ≥1 hit.

5. **출력 schema 본문 보존** (`present_entity_ids` / `primary_location` 결정 body 변경 0):
   ```bash
   grep -c "present_entity_ids\|primary_location" prompts/_base/scene_director/9.20260518HHMM/system.md
   ```
   Expected: ≥3 hits (출력 schema 설명 + 본문 매핑).

6. **Smoke pytest** (existing scene_director test 통과):
   ```bash
   cd backend && pytest tests/ -k "scene_director" -v
   ```
   Expected: all PASS (또는 known-baseline failure 만).

6 종 모두 PASS 시에만 W2 commit 진행.

---

## Task 1: W0 — Inventory + Baseline Measurement

**Files:**
- Read only (verify): `backend/app/modules/pipeline/visual_world_rules.py`
- Read only (verify): `backend/app/modules/pipeline/scene_director_v2.py`
- Read only (verify): `backend/app/core/step_manifest.py:130-141` + `:417-428`
- Read only (verify): `backend/app/core/version_registry.py`
- Read only (verify): `backend/app/modules/llm/llm_client.py:43-77` (`_validate_local_schema`)
- Read only (verify): `backend/app/modules/prompt_loader.py:130-225`
- Read only (verify): `backend/tests/_audit_outputs/semantic_string_debt/full_20260516_0105/active_prompt_findings_with_snippets.md:2017-2092`
- Optional create (non-commit): `docs/superpowers/specs/2026-05-18-area-7a-w0-inventory.md`

### S1 — VWR production runtime path verify

- [ ] **Step 1.1: extract_visual_rules call_structured path 확인**

Read: `backend/app/modules/pipeline/visual_world_rules.py` (line 14 `def extract_visual_rules(`, line 35-36 `load_prompt(_MODULE, "system")` + `load_schema(_MODULE, "rules_schema")`).

확인: VWR call_structured path = `load_prompt` (file fallback latest version) → `load_schema` (file fallback) → `call_structured(schema_name="visual_world_rules")` → LLM client local validation (`llm_client._validate_local_schema`).

- [ ] **Step 1.2: scene_director runtime path 확인**

Read: `backend/app/modules/pipeline/scene_director_v2.py` (line 27-28 `load_prompt(_MODULE, "system")` + `load_schema(_MODULE, "analyze_schema")`).

확인: scene_director production module path = `pipeline/scene_director_v2.py::direct_scenes()`. 동일 `prompt_loader` latest version dir 자동 활성. `director_steps.py:158` 이 `direct_scenes` import.

Legacy reference (변경 영향 0): `analysis_steps_legacy.py:763` 은 deprecated path — production 활성 X.

### S2 — STEP_MANIFEST current field set verify

- [ ] **Step 2.1: visual_world_rules entry field set 확인**

Read: `backend/app/core/step_manifest.py:130-141`.

확인 (Codex iter 3 F-2 evidence verify):
- field set = `label / category / order / default_model / provider / depends_on / fan_out / applicability / step_type / lifecycle`.
- `schema_version` field **absent** (없음).
- `prompt_version` field **absent**.

이 evidence = W1 의 `schema_version` 신설 (not "bump") 정확 wording 근거.

- [ ] **Step 2.2: scene_director entry field set 확인**

Read: `backend/app/core/step_manifest.py:417-428`.

확인:
- field set = `label / category / order / default_model / provider / depends_on / fan_out / applicability / step_type / lifecycle / requires_projection_sync_before_run` (W1-F12 추가).
- `prompt_version` field **absent**.
- `schema_version` field **absent**.

이 evidence = scene_director schema_version 변경 0 (analyze_schema.json 영향 없음, A047 본체는 system.md only).

### S3 — version_registry MODULE_VERSIONS pin verify

- [ ] **Step 3.1: visual_world_rules / scene_director pin 확인**

Read: `backend/app/core/version_registry.py:1-40` (`MODULE_VERSIONS` dict).

Grep:
```bash
grep -E "visual_world_rules|scene_director" backend/app/core/version_registry.py
```

Expected: 0 hits (controller verify: 2026-05-18 baseline, 둘 다 MODULE_VERSIONS pin absent).

확인: 본 area scope 안 version_registry sync 의무 없음 (둘 다 MODULE_VERSIONS pin 없음). prompt_version pin sync 영향 사이트 = 0.

### S4 — _validate_local_schema entry point verify

- [ ] **Step 4.1: jsonschema.validate wrapper 사용 확인**

Read: `backend/app/modules/llm/llm_client.py:43-77` (`_validate_local_schema` definition).

확인: `jsonschema.validate(instance=payload, schema=response_schema)` wrapper (line 63, raw call; schema 안 `$schema` 미명시 시 library default validator). `enum` keyword reject 행위 Draft 4+ 공통이므로 invalid `rule_type` value (예: `"invalid_value"`) → schema enum 위반 reject → typed error (`SchemaValidationError`).

이 evidence = W3b canary C1 의 deterministic 검증 entry point.

### S5 — prompt_loader latest version dir verify

- [ ] **Step 5.1: file fallback path 확인**

Read: `backend/app/modules/prompt_loader.py:130-132` (`load_prompt` file fallback) + `:167-180` (`_list_module_versions` numeric-desc sort via `_version_sort_key` reverse=True) + `:219-225` (`_resolve_stem_in_pack` numeric-desc latest).

확인: 새 `7.20260518HHMM/` (VWR) / `9.20260518HHMM/` (scene_director) dir 신설 시 numeric-desc order (`_version_sort_key` 기반) 로 자동 latest 결정 → 자동 활성. `step_manifest.prompt_version` field 영향 0 (현재 둘 다 absent).

### S6 — Audit output A096-A100 mapping verify

- [ ] **Step 6.1: audit row 본문 verify**

Read: `backend/tests/_audit_outputs/semantic_string_debt/full_20260516_0105/active_prompt_findings_with_snippets.md:2017-2092`.

확인 mapping (spec §2.2 정합):
- A047 = `scene_director/8.../system.md:5-10, 23-25, 32-34` (3 site).
- A096 = `visual_world_rules/6.../rules_schema.json:9-9` (rule_type description drift).
- A097 = `visual_world_rules/6.../rules_schema.json:20-20` (director_notes description scenario-specific).
- A098 = `visual_world_rules/6.../system.md:51-51` (rule_type 10-token list).
- A099 = `visual_world_rules/6.../system.md:57-73` (director_notes prose section) — **Carry-A099**.
- A100 = `visual_world_rules/6.../system.md:98-98` (t2i_context demonym) — **Carry-A100**.

drift 0 시 spec §2.2 정합 confirmed. drift 발견 시 spec amend before W1.

### S7 — Baseline pytest scope 식별

- [ ] **Step 7.1: VWR + scene_director + step_manifest test 찾기**

```bash
find backend/tests -name "*visual_world_rules*" -o -name "*scene_director*" -o -name "*step_manifest*" -o -name "*cp_mismatch*" 2>/dev/null
```

기록: 발견된 test file 목록. W1/W2 pre-commit smoke pytest scope 로 사용.

- [ ] **Step 7.2: baseline pytest 1회 실행**

```bash
cd backend && pytest tests/ -k "visual_world_rules or scene_director or step_manifest" -v 2>&1 | tail -30
```

기록: PASS / FAIL count + known-baseline failure 항목. W1/W2 commit 직전 비교 baseline.

### S8 — prompt_version field 신설 deferred decision

- [ ] **Step 8.1: 영향 사이트 grep**

```bash
grep -rn "prompt_version" backend/app/ --include="*.py" | head -20
```

기록: hit 있는 site (예: 다른 step entry, runner, prompt_loader 등).

- [ ] **Step 8.2: deferred decision 결정**

기준 (controller cross-analyze):
- hit 0 + observability metadata 신설 의도 0 → **prompt_version 신설 안함**. W1 변경 = `schema_version: 1` 만.
- hit 1+ (기존 entry 가 prompt_version 사용) + 정합 의무 → **prompt_version 신설**. W1 변경 = `schema_version: 1` + `prompt_version: "7.20260518HHMM"` (VWR), W2 변경 = `prompt_version: "9.20260518HHMM"` (scene_director).

결정 시 Codex MCP 의논 (pane %0). user ask 0. 결정 wording = "prompt_version 신설 안함 / 신설" + reason 1-line. plan 본 file 의 Task 2 (W1) + Task 3 (W2) 의 step_manifest 변경 wording 도 결정 결과로 amend.

### S9 — Archive prompt directory 보존 verify (rollback path)

- [ ] **Step 9.1: archive prompt dir 보존 확인**

```bash
ls prompts/_base/visual_world_rules/ prompts/_base/scene_director/
```

확인:
- VWR archive: `1.202603231200, 2.202603231200, 3.202604161200, 4.202604300936, 5.202605011300, 6.202605021400` (W1 후 archive 가 됨).
- scene_director archive: `6.202603251200, 7.202604031800, 8.202604081200` (W2 후 archive 가 됨).

residue gate 의 archive-excluded scope 정합 (§7 본문 참조).

### S10 — Optional W0 memo 작성

- [ ] **Step 10.1: W0 inventory memo (optional)**

Create (optional): `docs/superpowers/specs/2026-05-18-area-7a-w0-inventory.md`.

Body (4-section):
1. Production runtime path summary (S1 + S2 verify result).
2. version_registry / prompt_version pin status (S3 + S8 result).
3. Baseline pytest scope + count (S7 result).
4. prompt_version deferred decision verdict (S8 result + Codex 의논 결과).

본 memo file = optional. W3c closure 시 spec body 안 verbatim 흡수 또는 별도 git stage. W0 진행 중 commit 0.

---

## Task 2: W1 — VWR Producer Atomic (signature-breaking single commit)

**Files:**
- Create: `prompts/_base/visual_world_rules/7.20260518HHMM/rules_schema.json`
- Create: `prompts/_base/visual_world_rules/7.20260518HHMM/system.md`
- Modify: `backend/app/core/step_manifest.py:130-141`

**Atomic single commit** — schema enum strict 화 = cp invalidation. split 시 mixed window risk (v7 prompt active + cp manifest 옛 stamp 잔존).

### S1 — Determine W1 HHMM timestamp + create v7 directory

- [ ] **Step 1.1: UTC 현재 시각 4-digit HHMM 결정**

```bash
date -u +%H%M
```

Output example: `1432` (14:32 UTC). 이 값을 `HHMM` 로 사용 (W1 모든 placeholder substitute, 단일 commit 안 동일 timestamp).

- [ ] **Step 1.2: v7 prompt directory 신설**

```bash
mkdir -p prompts/_base/visual_world_rules/7.20260518HHMM
```

(`HHMM` 자리에 Step 1.1 결과 substitute. 예: `mkdir -p prompts/_base/visual_world_rules/7.202605181432`.)

### S2 — Create rules_schema.json v7

- [ ] **Step 2.1: rules_schema.json v7 신설**

Full content:

```json
{
  "type": "object",
  "properties": {
    "rules": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "rule_type": {
            "type": "string",
            "enum": [
              "possession",
              "transformation",
              "ghost",
              "projection",
              "superpower",
              "body_deformation",
              "time_period",
              "costume",
              "technology",
              "other"
            ],
            "description": "규칙 유형 (possession, transformation, ghost, projection, superpower, body_deformation, time_period, costume, technology, other)"
          },
          "description": {"type": "string", "description": "규칙 설명"},
          "visual_guideline": {"type": "string", "description": "시각적 표현 가이드라인"}
        },
        "required": ["rule_type", "description", "visual_guideline"],
        "additionalProperties": false
      }
    },
    "director_notes": {
      "type": "array",
      "items": {"type": "string"},
      "description": "씬 감독이 물리적 존재 여부를 판단할 때 참고할 구체적 가이드 (작품 전반에 적용 가능한 유형적 원칙만 — 특정 인물/씬/사건 묘사 금지)"
    },
    "era": {"type": "string", "description": "시대 배경"},
    "region": {"type": "string", "description": "지역/국가 배경"},
    "source_language": {"type": "string", "description": "시나리오 원문 언어 ISO 639-1 코드 소문자 2자 (예: ko, en, ja, zh). 후속 t2i prompt 단계가 이 언어로 본문을 작성하도록 강제한다."},
    "t2i_context": {"type": "string", "description": "T2I 프롬프트용 시각 스타일 요약 (영어 2~3문장, 기본 국적/인종 + 색감/조명/질감 톤 + 장르 무드. 구체적 지명/장소/건축양식 열거 금지)"}
  },
  "required": ["rules", "director_notes", "era", "region", "source_language", "t2i_context"],
  "additionalProperties": false
}
```

**Diff from `6.202605021400/rules_schema.json`**:
- `rule_type` field (line 9 → v7 line 9-22): `{type: string, description: "(possession, ..., 등)"}` → `{type: string, enum: [10-token], description: "(canonical 10-token enumeration)"}` (A096).
- `director_notes` field description (line 20 → v7 line ~28): `"...참고할 구체적 가이드 (예: A의 영혼이 B의 몸에 전이된 경우 A는 물리적 존재가 아님)"` → `"...참고할 구체적 가이드 (작품 전반에 적용 가능한 유형적 원칙만 — 특정 인물/씬/사건 묘사 금지)"` (A097, 시나리오-specific example → generic instruction 추상화).
- 다른 field (`rules.items.description / visual_guideline / era / region / source_language / t2i_context`) struct + description 변경 0.

### S3 — Create system.md v7

- [ ] **Step 3.1: system.md v7 신설**

`prompts/_base/visual_world_rules/6.202605021400/system.md` 본문을 base 로 copy → 다음 site 만 amend:

**Site 1 (line 30-46, rule_type 별 example 본문 안 작품-specific 잔존 추상화 — A098)**:
- 본문 내용 30-46 line 의 6 rule_type 별 example wording 보존 (10-enum 의 도메인 의미 LLM 학습 의무).
- 작품-specific 잔존 (한국 fantasy 작품명 등) 만 추상화 — controller 가 base read 후 grep 으로 잔존 식별. 잔존 발견 시 generic phrase 로 replace; 0 시 변경 0.
- canonical 10-token noun phrase (line 51) 와 본문 wording 정합 의무. 본문 안 noun phrase (`빙의/영혼 전이`, `변신/변형`, `유령/환영`, `투사/투영`, `초능력`, `신체 변형`) = canonical phrase (`possession`, `transformation`, `ghost`, `projection`, `superpower`, `body_deformation`) 의 한국어 도메인 의미 정합 보존.

**Site 2 (line 51, canonical 10-token list — A098)**:
- 변경 0. 본 area 의 SOT enumeration 자체.
- 본문 의무: `2. \`rule_type\`은 다음 중 선택: \`possession\`, \`transformation\`, \`ghost\`, \`projection\`, \`superpower\`, \`body_deformation\`, \`time_period\`, \`costume\`, \`technology\`, \`other\`` (verbatim).

**Site 3 (line 57-73, director_notes prose section — Carry-A099, 변경 0)**:
- 5-line generic principle examples (`회상/F.B / 환각/현시 / CCTV·모니터·창문 / 교차편집/몽타주 / 안개·어둠`) + bad-example 4-line 모두 보존.
- redesign 별도 area (Carry-A099) — 본 area 안 scope 외.

**Site 4 (line 95-100, t2i_context section — Carry-A100, 변경 0)**:
- `<region-derived demonym>` wording 보존.
- t2i_context demonym redesign 별도 area (Carry-A100) — 본 area 안 scope 외.

verbatim diff scope (controller 본문 패치 시점 확정):
- 작품-specific 잔존 추상화 = small surgical edit only (predicted 0-2 site).
- 잔존 0 시 file copy + no-op (rules_schema.json v7 신설 변경만 effective).

### S4 — Modify step_manifest.py (schema_version 신설)

- [ ] **Step 4.1: visual_world_rules entry 안 schema_version field 신설**

File: `backend/app/core/step_manifest.py:130-141`.

Before (line 130-141):
```python
    # 4. 시각적 세계관 규칙
    "visual_world_rules": {
        "label": "시각적 세계관 규칙",
        "category": "analysis",
        "order": 4,
        "default_model": "gpt",
        "provider": "openai",
        "depends_on": ["episode_summary"],
        "fan_out": False,
        "applicability": "always",
        "step_type": "transform",
        "lifecycle": "active",
    },
```

After (W1):
```python
    # 4. 시각적 세계관 규칙
    "visual_world_rules": {
        "label": "시각적 세계관 규칙",
        "category": "analysis",
        "order": 4,
        "default_model": "gpt",
        "provider": "openai",
        "depends_on": ["episode_summary"],
        "fan_out": False,
        "applicability": "always",
        "step_type": "transform",
        "lifecycle": "active",
        "schema_version": 1,  # 2026-05-18 (Area #7a W1): rules_schema.json v7 — rule_type 10-enum strict (possession/transformation/ghost/projection/superpower/body_deformation/time_period/costume/technology/other) + director_notes description abstraction. cp invalidation: incompatible-stamp cp blocked via _check_cp_mismatch; older unstamped/default-stamped cp may need force rerun.
    },
```

**Diff**: 1 new key (`schema_version`) + 1 comment. 기존 dict key 정합 보존.

- [ ] **Step 4.2: (deferred decision 결과 신설 결정 시) prompt_version field 추가**

만약 Task 1 S8 deferred decision = **prompt_version 신설**:
- `visual_world_rules` entry 안 같이 추가:
  ```python
  "prompt_version": "7.20260518HHMM",  # Step 1.1 실제 HHMM substitute
  ```

만약 Task 1 S8 deferred decision = **prompt_version 신설 안함**:
- 변경 없음 (Step 4.1 의 schema_version 만).

### S5 — Pre-commit verification (Pre-Commit Verification Doctrine W1 7 종)

- [ ] **Step 5.1: 7 종 verify 실행**

Pre-Commit Verification Doctrine (W1) 본문 7 종 (Schema structural / Schema description residue / System.md 10-token list / director_notes prose 보존 / t2i_context demonym 보존 / STEP_MANIFEST schema_version 신설 / Smoke pytest) 모두 PASS 확인.

실패 1 종 있을 시 commit 절대 금지 → 본문 fix → 7 종 재실행.

### S6 — W1 atomic commit

- [ ] **Step 6.1: git status 확인**

```bash
git status
```

확인: untracked = `prompts/_base/visual_world_rules/7.20260518HHMM/{rules_schema.json, system.md}` + modified = `backend/app/core/step_manifest.py`.

- [ ] **Step 6.2: path-limited git add**

```bash
git add prompts/_base/visual_world_rules/7.20260518HHMM/rules_schema.json \
        prompts/_base/visual_world_rules/7.20260518HHMM/system.md \
        backend/app/core/step_manifest.py
```

**절대 금지**: `git add -A` / `git add .` / sensitive file (DB / env / credentials) 자동 stage.

- [ ] **Step 6.3: W1 atomic commit**

```bash
git commit -m "$(cat <<'EOF'
feat(area-7a-visual-world-rules-sot-v1): W1 producer atomic — VWR v7 rule_type 10-enum strict + director_notes description abstraction + schema_version=1 신설

A096: rules_schema.json v7 — rule_type free-string + 6-token "등" description → enum strict
[10-token: possession/transformation/ghost/projection/superpower/body_deformation/
time_period/costume/technology/other] + description sync.

A097: director_notes description scenario-specific example ("예: A의 영혼이 B의 몸
에...") → generic instruction ("작품 전반에 적용 가능한 유형적 원칙만 — 특정 인물/
씬/사건 묘사 금지"). schema struct 변경 0.

A098: system.md 작품-specific 잔존 (있을 시) 추상화. line 51 canonical 10-token
list 보존.

STEP_MANIFEST visual_world_rules.schema_version=1 신설 (현재 absent → 1).
incompatible-stamp cp blocked via step_runner._check_cp_mismatch; older
unstamped/default-stamped cp may need force rerun.

director_notes prose section (line 57-73, Carry-A099) + t2i_context demonym
(line 98, Carry-A100) 변경 0.

prompt_version field = deferred decision (Task 1 S8 verdict 따름).

Spec: docs/superpowers/specs/2026-05-18-area-7a-visual-world-rules-sot-v1-design.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

(deferred decision = prompt_version 신설 결정 시 commit message 안 `prompt_version="7.20260518HHMM"` 신설 line 추가.)

- [ ] **Step 6.4: commit 확인**

```bash
git log -1 --stat
```

확인: 1 commit + 3 file changed (2 created + 1 modified).

### S7 — W1 Codex review 의뢰 (per-wave external)

- [ ] **Step 7.1: Codex pane %0 read guard**

```bash
# tmux_read %0 (50 lines)
```

- [ ] **Step 7.2: W1 commit Codex review 의뢰**

tmux_type %0 (single message, 본 task 직후 fresh):
```
Area #7a W1 commit review 의뢰.

Commit: <W1 commit hash>
Wave: W1 producer atomic (VWR v7 rule_type 10-enum strict + director_notes desc 추상화 + step_manifest schema_version=1).
Spec: docs/superpowers/specs/2026-05-18-area-7a-visual-world-rules-sot-v1-design.md (commit 92b81f2).

검토 항목:
1. rules_schema.json v7 의 rule_type.enum = exact 10-token + description sync (A096).
2. director_notes description abstraction = scenario leak 0 (A097).
3. system.md line 51 canonical 10-token list 보존 (A098).
4. director_notes prose section (line 57-73) 변경 0 (Carry-A099).
5. t2i_context section (line 95-100) 변경 0 (Carry-A100).
6. step_manifest.py visual_world_rules.schema_version=1 신설 (not bump).
7. atomic single commit (W2 / W3 cascade 0).
8. NO VLM (canary scope 0).
9. prompt_version deferred decision 결과 정합.

verdict: APPROVED / NEEDS_REVISION_MINOR / NEEDS_REVISION_MAJOR. minor finding 시 fix-up amend, major 시 W1 redo.

완료되면 끝났다고 claude mcp 호출해줘!
```

- [ ] **Step 7.3: tmux_keys Enter + tmux_read polling**

verdict 도착 후 controller cross-analyze. NEEDS_REVISION 시 fix-up amend (`git commit --amend` 또는 별도 fix-up commit 결정), APPROVED 시 W2 진입.

**Codex 의논 의무**: verdict 검증 (cross-analyze). verbatim 받아쓰지 말 것 ([[feedback_codex_mcp_discussion_workflow]] rule 7b).

---

## Task 3: W2 — scene_director Consumer Rewrite (A047 3 site, single commit)

**Files:**
- Create: `prompts/_base/scene_director/9.20260518HHMM/system.md`

**Atomic single commit** — A047 3 site 본체 폐기 + principle-only rewrite. analyze_schema.json 변경 0.

### S1 — Determine W2 HHMM timestamp + create v9 directory

- [ ] **Step 1.1: UTC 현재 시각 4-digit HHMM 결정**

```bash
date -u +%H%M
```

(W2 자체 HHMM — W1 HHMM 과 다를 수 있음.)

- [ ] **Step 1.2: v9 prompt directory 신설**

```bash
mkdir -p prompts/_base/scene_director/9.20260518HHMM
```

### S2 — Create system.md v9 (A047 3 site principle-only rewrite)

- [ ] **Step 2.1: system.md v9 신설**

`prompts/_base/scene_director/8.202604081200/system.md` 본문을 base 로 copy → 다음 3 site amend:

**Site 1 (line 5-10, 4-item exclusion list 본체 폐기 — A047)**:

Before:
```markdown
## 카메라에 보이지 않는 경우 (제외)

1. **편집 기법**: 교차편집/몽타주에서 다른 장소의 인물이 번갈아 보이는 것은 이 씬의 카메라가 아닌 다른 씬의 카메라
2. **원격 접속/매개**: 영상통화, CCTV, 방송 화면, 홀로그램, VR, 원격 조종/빙의 기술 — 실제 몸이 이 장소에 없음
3. **목소리만**: V.O.(보이스오버), 내레이션 — 카메라에 안 보임
4. **정체 대체**: 변장, 쌍둥이 교체, 바디더블 — 보이는 것은 다른 사람의 몸
```

After (principle-only):
```markdown
## 카메라에 보이지 않는 경우 (제외)

`visual_world_rules` 의 `rules[]` (각 항목의 `rule_type` 으로 SOT 정의된 유형적 시각 현상) 와 `director_notes` (작품 전반에 적용 가능한 유형적 비물리 판단 원칙) 가 인물/소품의 **물리적 존재 / 시각적 존재** 판단 기준이다. scene_director 는 사례 enumeration 자체 분류 안 하고, VWR 가 SOT 로 결정한 결과를 그대로 적용한다.

- VWR `rules[]` 의 `rule_type` 의 `visual_guideline` 이 해당 작품에서 비물리/매개 상태로 판단한 경우 → 제외.
- VWR `rules[]` 에 해당 rule_type 이 정의되지 않은 작품 = 일반 인물 처리 (default presence).
- VWR `director_notes` 안 유형적 원칙이 비물리 routing 하는 경우 → 제외.
```

**Q2 mapping-ban 의무 (Codex iter 1 F1 흡수)**: After block 안 사례 enumeration (예: `(소속 없는 영혼, 원격 매개 영상, 음성-only, 정체 대체)` / `(회상/환각/매개 표면)` 같은 paraphrase) 절대 금지. 사례 paraphrase 자체가 closed-list LLM instruction 재도입 — Q2 verdict 위반. principle 만 + SOT field reference (rule_type / visual_guideline / director_notes) 만 명시.

**Site 2 (line 23-25, 인물 판단 본문 안 빙의/원격접속 사례 enumeration 폐기 — A047)**:

Before:
```markdown
- **인물**: 이 씬의 카메라에 보이는가? 살아있든 죽었든, 의식이 있든 없든 — 카메라에 보이면 포함.
  - 빙의/원격접속 중이면 실제 몸이 안 보이므로 제외.
  - description을 참고하여 씬 텍스트의 표현과 매칭 (이름이 정확히 일치하지 않을 수 있음)
```

After:
```markdown
- **인물**: 이 씬의 카메라에 보이는가? 살아있든 죽었든, 의식이 있든 없든 — 카메라에 보이면 포함.
  - VWR `rules[]` / `director_notes` 가 비물리/매개 상태로 분류한 경우 (해당 rule_type 의 visual_guideline 또는 director_notes 원칙이 명시한 경우) 제외.
  - description을 참고하여 씬 텍스트의 표현과 매칭 (이름이 정확히 일치하지 않을 수 있음)
```

**Site 3 (line 32-34, 출력 본문 안 parenthetical 폐기 — A047)**:

Before:
```markdown
- `present_entity_ids`: 이 씬의 카메라에 **시각적으로 보이는** 엔티티의 **id** 배열.
  - 엔티티 목록에서 제공된 id를 그대로 사용
  - 카메라에 안 보이면 제외 (빙의/원격/V.O. 등)
```

After:
```markdown
- `present_entity_ids`: 이 씬의 카메라에 **시각적으로 보이는** 엔티티의 **id** 배열.
  - 엔티티 목록에서 제공된 id를 그대로 사용
  - 카메라에 안 보이면 제외 (VWR SOT 결정 따름)
```

**보존 site (변경 0)**:
- 출력 schema 설명 (line 26-30 `primary_location` 등) — `analyze_schema.json` reference 본체.
- `## 카메라에 보이면 포함` section (line 12-18) — affirmative inclusion 본체.
- `## 판단 방법` (line 20-28 본체) — affirmative judgment body.
- `## 출력` section 안 `primary_location` 결정 본문 (line 26-30 보존).

verbatim wording 의무: A047 mapping table (예: `편집 = projection` 등) **scene_director prompt 안 절대 금지** (Codex iter 2 verdict Q2). mapping 자체가 새 closed-list instruction.

### S3 — Pre-commit verification (Pre-Commit Verification Doctrine W2 6 종)

- [ ] **Step 3.1: 6 종 verify 실행**

Pre-Commit Verification Doctrine (W2) 본문 6 종 (Site 1 / Site 2 / Site 3 residue grep 0 + VWR SOT reference token 1+ + 출력 schema 본문 보존 + Smoke pytest) 모두 PASS 확인.

### S4 — W2 atomic commit

- [ ] **Step 4.1: git status 확인**

```bash
git status
```

확인: untracked = `prompts/_base/scene_director/9.20260518HHMM/system.md` only. (deferred decision = prompt_version 신설 결정 시 + `backend/app/core/step_manifest.py` modified 추가.)

- [ ] **Step 4.2: path-limited git add**

```bash
git add prompts/_base/scene_director/9.20260518HHMM/system.md
```

(deferred decision = prompt_version 신설 결정 시 + `git add backend/app/core/step_manifest.py`.)

- [ ] **Step 4.3: W2 atomic commit**

```bash
git commit -m "$(cat <<'EOF'
feat(area-7a-visual-world-rules-sot-v1): W2 consumer atomic — scene_director v9 A047 3 site principle-only rewrite (VWR SOT reference)

A047 3 site 본체 폐기 + principle-only rewrite:
- Site 1 (line 5-10, top 4-item exclusion list "편집 기법 / 원격 접속/매개 /
  목소리만 / 정체 대체") → VWR rules[].rule_type + director_notes 가
  physical-presence / visual-presence SOT 라는 principle 으로 대체.
- Site 2 (line 23-25, 인물 판단 안 "빙의/원격접속 중이면 실제 몸이 안 보이므로
  제외" 사례 enumeration) → VWR rules[] / director_notes 비물리/매개 분류
  결과 따름.
- Site 3 (line 32-34, 출력 안 "(빙의/원격/V.O. 등)" parenthetical) →
  "(VWR SOT 결정 따름)".

mapping table (예: "편집 = projection") scene_director prompt 안 절대 금지
(Codex iter 2 verdict Q2 — mapping 자체가 새 closed-list instruction).

scene_director analyze_schema.json 변경 0 (A047 본체는 system.md only).
출력 schema 설명 (present_entity_ids / primary_location 결정 본문) 보존.

Spec: docs/superpowers/specs/2026-05-18-area-7a-visual-world-rules-sot-v1-design.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

- [ ] **Step 4.4: commit 확인**

```bash
git log -1 --stat
```

확인: 1 commit + 1 file changed (1 created).

### S5 — W2 Codex review 의뢰 (per-wave external)

- [ ] **Step 5.1: Codex pane %0 read + W2 commit review 의뢰**

tmux_type %0 (single message, 본 task 직후 fresh):
```
Area #7a W2 commit review 의뢰.

Commit: <W2 commit hash>
Wave: W2 consumer atomic (scene_director v9 A047 3 site principle-only rewrite).
Spec: docs/superpowers/specs/2026-05-18-area-7a-visual-world-rules-sot-v1-design.md (commit 92b81f2).

검토 항목:
1. Site 1 (line 5-10) 4-item exclusion list 본체 폐기 + principle-only rewrite (VWR SOT reference).
2. Site 2 (line 23-25) 빙의/원격접속 사례 enumeration 폐기.
3. Site 3 (line 32-34) parenthetical (빙의/원격/V.O. 등) 폐기.
4. mapping table 안 들어감 (편집=projection 등, Q2 verdict 정합).
5. analyze_schema.json 변경 0.
6. 출력 schema 설명 + primary_location 결정 본문 보존.
7. VWR SOT reference token (visual_world_rules / VWR / rule_type) 1+ 존재.

verdict: APPROVED / NEEDS_REVISION_MINOR / NEEDS_REVISION_MAJOR.

완료되면 끝났다고 claude mcp 호출해줘!
```

verdict 도착 후 controller cross-analyze. APPROVED 시 W3 진입.

---

## Task 4a: W3a — Residue Gate Test (single commit)

**Files:**
- Create: `backend/tests/integration/test_area_7a_residue_gate.py`

### S1 — Create test_area_7a_residue_gate.py

- [ ] **Step 1.1: residue gate test 신설**

File: `backend/tests/integration/test_area_7a_residue_gate.py`

Full content:

```python
"""Area #7a residue gate — Visual World Rules SOT + scene_director A047 absorption.

4 strict residue gate (spec §7):
- Gate 1 (structural, JSON parse): VWR v7 rules_schema.json rule_type.enum exact 10-token set.
- Gate 2 (structural, JSON parse): VWR v7 rules_schema.json rule_type.description 안 6-token "등" pattern 0.
- Gate 3 (markdown parse): VWR v7 system.md 안 10-token canonical list 1+ + 6-token "등" residue 0.
- Gate 4 (regex, scene_director v9): A047 3 site signatures 0.

Archive excluded:
- VWR: 1.* ~ 6.202605021400/ excluded.
- scene_director: 6.* ~ 8.202604081200/ excluded.
"""

from __future__ import annotations

import json
import re
from pathlib import Path

import pytest

REPO_ROOT = Path(__file__).resolve().parents[3]  # backend/tests/integration/<this> → repo root (Codex iter 1 F2 흡수: parents[2] 는 backend/ 까지만 도달)
VWR_DIR = REPO_ROOT / "prompts" / "_base" / "visual_world_rules"
SCENE_DIRECTOR_DIR = REPO_ROOT / "prompts" / "_base" / "scene_director"

CANONICAL_RULE_TYPE_ENUM = [
    "possession",
    "transformation",
    "ghost",
    "projection",
    "superpower",
    "body_deformation",
    "time_period",
    "costume",
    "technology",
    "other",
]


def _latest_active_dir(base: Path, version_prefix: str) -> Path:
    """Return the lexicographically latest dir 안 version prefix matching one."""
    candidates = sorted(
        p for p in base.iterdir() if p.is_dir() and p.name.startswith(version_prefix)
    )
    assert candidates, f"no {version_prefix}* dir under {base}"
    return candidates[-1]


@pytest.fixture(scope="module")
def vwr_v7_dir() -> Path:
    return _latest_active_dir(VWR_DIR, "7.")


@pytest.fixture(scope="module")
def scene_director_v9_dir() -> Path:
    return _latest_active_dir(SCENE_DIRECTOR_DIR, "9.")


# --- Gate 1: VWR v7 rules_schema.json rule_type.enum exact 10-token ---


def test_gate_1_vwr_rule_type_enum_exact_ten(vwr_v7_dir: Path) -> None:
    schema_path = vwr_v7_dir / "rules_schema.json"
    schema = json.loads(schema_path.read_text(encoding="utf-8"))
    rule_type_def = schema["properties"]["rules"]["items"]["properties"]["rule_type"]
    assert rule_type_def["type"] == "string", rule_type_def
    assert rule_type_def["enum"] == CANONICAL_RULE_TYPE_ENUM, rule_type_def["enum"]


# --- Gate 2: VWR v7 rules_schema.json description 안 6-token "등" pattern 0 ---


def test_gate_2_vwr_rule_type_description_no_six_token_residue(vwr_v7_dir: Path) -> None:
    schema_path = vwr_v7_dir / "rules_schema.json"
    schema = json.loads(schema_path.read_text(encoding="utf-8"))
    rule_type_desc = schema["properties"]["rules"]["items"]["properties"]["rule_type"]["description"]
    # 6-token + "등" pattern (4 enum 누락): possession, transformation, ghost, time_period, costume, technology 등
    forbidden_six_token = re.compile(
        r"possession,\s*transformation,\s*ghost,\s*time_period,\s*costume,\s*technology\s*등"
    )
    assert not forbidden_six_token.search(rule_type_desc), rule_type_desc
    # description 안 canonical 10-token 정합 (10 enum 모두 substring)
    for token in CANONICAL_RULE_TYPE_ENUM:
        assert token in rule_type_desc, f"missing {token} in description: {rule_type_desc}"


# --- Gate 3: VWR v7 system.md 안 10-token canonical list 1+ + 6-token "등" 0 ---


def test_gate_3_vwr_system_md_canonical_list_and_no_residue(vwr_v7_dir: Path) -> None:
    system_md_path = vwr_v7_dir / "system.md"
    body = system_md_path.read_text(encoding="utf-8")
    # canonical 10-token enumeration 1+ (line 51 또는 정합 위치)
    canonical_list_pattern = re.compile(
        r"possession.*transformation.*ghost.*projection.*superpower.*body_deformation.*time_period.*costume.*technology.*other",
        re.DOTALL,
    )
    assert canonical_list_pattern.search(body), "missing canonical 10-token list in system.md"
    # 6-token + "등" pattern 0 (description drift residue)
    forbidden_six_token = re.compile(
        r"possession,\s*transformation,\s*ghost,\s*time_period,\s*costume,\s*technology\s*등"
    )
    assert not forbidden_six_token.search(body), "6-token + 등 residue in system.md"


# --- Gate 4: scene_director v9 A047 3 site signatures 0 ---


def test_gate_4_scene_director_a047_site1_exclusion_list(
    scene_director_v9_dir: Path,
) -> None:
    """Site 1 (line 5-10) 4-item exclusion list 본체 signature 0."""
    body = (scene_director_v9_dir / "system.md").read_text(encoding="utf-8")
    forbidden_patterns = [
        r"교차편집/몽타주에서 다른 장소의 인물이 번갈아",
        r"영상통화,\s*CCTV,\s*방송 화면,\s*홀로그램,\s*VR,\s*원격 조종/빙의",
        r"V\.O\.\(보이스오버\),\s*내레이션",
        r"변장,\s*쌍둥이 교체,\s*바디더블",
    ]
    for pat in forbidden_patterns:
        assert not re.search(pat, body), f"A047 Site 1 signature hit: {pat}"


def test_gate_4_scene_director_a047_site2_possession_remote_repeat(
    scene_director_v9_dir: Path,
) -> None:
    """Site 2 (line 23-25) 빙의/원격접속 사례 enumeration 0."""
    body = (scene_director_v9_dir / "system.md").read_text(encoding="utf-8")
    forbidden = r"빙의/원격접속 중이면 실제 몸이 안 보이므로 제외"
    assert not re.search(forbidden, body), "A047 Site 2 signature hit"


def test_gate_4_scene_director_a047_site3_parenthetical(
    scene_director_v9_dir: Path,
) -> None:
    """Site 3 (line 32-34) 출력 본문 안 parenthetical (빙의/원격/V.O. 등) 0."""
    body = (scene_director_v9_dir / "system.md").read_text(encoding="utf-8")
    forbidden = r"\(빙의/원격/V\.O\.\s*등\)"
    assert not re.search(forbidden, body), "A047 Site 3 parenthetical hit"


# --- Sanity: VWR SOT reference token in scene_director v9 ---


def test_scene_director_v9_references_vwr_sot(scene_director_v9_dir: Path) -> None:
    body = (scene_director_v9_dir / "system.md").read_text(encoding="utf-8")
    has_vwr_ref = re.search(r"visual_world_rules|VWR|rule_type", body)
    assert has_vwr_ref, "scene_director v9 must reference VWR SOT"
```

**Notes**:
- Archive (`6.202605021400/` VWR + `8.202604081200/` scene_director) = `_latest_active_dir` 가 `7.*` (VWR) / `9.*` (scene_director) prefix 만 검색하므로 자동 excluded.
- `projection / CCTV / ghost / camera / color` global ban = 의도적 누락 (Codex iter 2 verdict Q6 — legitimate use case 잔존 가능).

### S2 — pytest 실행

- [ ] **Step 2.1: residue gate 7 test 모두 PASS 확인**

```bash
cd backend && pytest tests/integration/test_area_7a_residue_gate.py -v
```

Expected: 7 PASS (Gate-1 1 + Gate-2 1 + Gate-3 1 + Gate-4 3 sub-tests (Site 1/2/3) + sanity 1 = 7 test).

실패 시 본문 fix (W1 또는 W2 redo + 본 test 재실행).

### S3 — W3a commit

- [ ] **Step 3.1: git add + commit**

```bash
git add backend/tests/integration/test_area_7a_residue_gate.py
git commit -m "$(cat <<'EOF'
test(area-7a-visual-world-rules-sot-v1): W3a residue gate — VWR v7 rule_type enum strict + system.md canonical list + scene_director v9 A047 3 site signature 0

4 strict residue gate (spec §7):
- Gate 1: VWR v7 rules_schema.json rule_type.enum exact 10-token (canonical
  possession/transformation/ghost/projection/superpower/body_deformation/
  time_period/costume/technology/other).
- Gate 2: VWR v7 rules_schema.json rule_type.description 안 6-token "등"
  pattern 0 + 10 enum 모두 substring.
- Gate 3: VWR v7 system.md 안 canonical 10-token list 1+ + 6-token "등"
  residue 0.
- Gate 4: scene_director v9 A047 3 site signatures 0 (Site 1 4-item exclusion
  list + Site 2 빙의/원격접속 사례 enumeration + Site 3 parenthetical (빙의/
  원격/V.O. 등)).

Archive (1.* ~ 6.202605021400/ VWR + 6.* ~ 8.202604081200/ scene_director)
= _latest_active_dir 가 7.* / 9.* prefix 만 검색하므로 excluded.

projection / CCTV / ghost / camera / color global ban = 의도적 누락 (Codex iter
2 Q6 — legitimate use case 잔존 가능).

Spec: docs/superpowers/specs/2026-05-18-area-7a-visual-world-rules-sot-v1-design.md §7

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Task 4b: W3b — Canary Test (single commit)

**Files:**
- Create: `backend/tests/integration/test_area_7a_no_drift.py`

### S1 — Create test_area_7a_no_drift.py

- [ ] **Step 1.1: canary test 신설**

File: `backend/tests/integration/test_area_7a_no_drift.py`

Full content:

```python
"""Area #7a canary — VWR SOT enforcement + scene_director A047 absorption + step_manifest schema_version invalidation.

5 canary (spec §5.4, no live LLM):
- C1: jsonschema.validate() invalid rule_type reject (deterministic).
- C2: VWR system.md ↔ rules_schema.json 10-token 정합 (structural).
- C3: scene_director v9 principle-only — VWR SOT reference token 1+ + A047 3 site signatures 0.
- C4: STEP_MANIFEST visual_world_rules.schema_version = 1 신설 (current None → 1, cp invalidation).
- C5: scene_director step load_prompt path 정합 (9.* dir 가 prompt_loader latest version 으로 자동 활성).

NO VLM 의무 — 모든 verification = schema/text/manifest only.
"""

from __future__ import annotations

import json
import re
from pathlib import Path

import jsonschema
import pytest

REPO_ROOT = Path(__file__).resolve().parents[3]  # backend/tests/integration/<this> → repo root (Codex iter 1 F2 흡수: parents[2] 는 backend/ 까지만 도달)
VWR_DIR = REPO_ROOT / "prompts" / "_base" / "visual_world_rules"
SCENE_DIRECTOR_DIR = REPO_ROOT / "prompts" / "_base" / "scene_director"

CANONICAL_RULE_TYPE_ENUM = [
    "possession",
    "transformation",
    "ghost",
    "projection",
    "superpower",
    "body_deformation",
    "time_period",
    "costume",
    "technology",
    "other",
]


def _latest_active_dir(base: Path, version_prefix: str) -> Path:
    candidates = sorted(
        p for p in base.iterdir() if p.is_dir() and p.name.startswith(version_prefix)
    )
    assert candidates, f"no {version_prefix}* dir under {base}"
    return candidates[-1]


@pytest.fixture(scope="module")
def vwr_v7_schema() -> dict:
    schema_path = _latest_active_dir(VWR_DIR, "7.") / "rules_schema.json"
    return json.loads(schema_path.read_text(encoding="utf-8"))


@pytest.fixture(scope="module")
def vwr_v7_system_md() -> str:
    system_md_path = _latest_active_dir(VWR_DIR, "7.") / "system.md"
    return system_md_path.read_text(encoding="utf-8")


@pytest.fixture(scope="module")
def scene_director_v9_system_md() -> str:
    system_md_path = _latest_active_dir(SCENE_DIRECTOR_DIR, "9.") / "system.md"
    return system_md_path.read_text(encoding="utf-8")


# --- C1: jsonschema.validate() invalid rule_type reject (deterministic) ---


def test_c1_invalid_rule_type_rejected_by_jsonschema(vwr_v7_schema: dict) -> None:
    """Codex iter 3 F-4 흡수: live LLM / router completion call 금지.
    `jsonschema.validate()` 직접 호출 (production `_validate_local_schema` parity) + synthetic invalid payload.
    """
    invalid_payload = {
        "rules": [
            {
                "rule_type": "invalid_value_not_in_enum",  # ← schema enum 위반
                "description": "test",
                "visual_guideline": "test",
            }
        ],
        "director_notes": [],
        "era": "modern",
        "region": "test",
        "source_language": "ko",
        "t2i_context": "test context",
    }
    with pytest.raises(jsonschema.ValidationError):
        jsonschema.validate(invalid_payload, vwr_v7_schema)


def test_c1_valid_rule_type_accepted(vwr_v7_schema: dict) -> None:
    """canonical 10 enum 모두 valid 확인."""
    for rule_type in CANONICAL_RULE_TYPE_ENUM:
        valid_payload = {
            "rules": [
                {
                    "rule_type": rule_type,
                    "description": "test",
                    "visual_guideline": "test",
                }
            ],
            "director_notes": [],
            "era": "modern",
            "region": "test",
            "source_language": "ko",
            "t2i_context": "test context",
        }
        jsonschema.validate(valid_payload, vwr_v7_schema)  # no raise


# --- C2: VWR system.md ↔ rules_schema.json 10-token 정합 (structural) ---


def test_c2_system_md_schema_rule_type_alignment(
    vwr_v7_schema: dict, vwr_v7_system_md: str
) -> None:
    """system.md (line 51 canonical list) ↔ rules_schema.json enum 10-token 정합."""
    schema_enum = vwr_v7_schema["properties"]["rules"]["items"]["properties"]["rule_type"]["enum"]
    assert schema_enum == CANONICAL_RULE_TYPE_ENUM
    for token in CANONICAL_RULE_TYPE_ENUM:
        assert token in vwr_v7_system_md, f"missing canonical token {token} in system.md"


# --- C3: scene_director v9 principle-only + A047 signatures 0 ---


def test_c3_scene_director_v9_principle_only(scene_director_v9_system_md: str) -> None:
    body = scene_director_v9_system_md
    assert re.search(r"visual_world_rules|VWR|rule_type", body), "scene_director v9 must reference VWR SOT"
    # A047 Site 1 / 2 / 3 signature 0 — residue gate 정합 sanity
    forbidden_signatures = [
        r"영상통화,\s*CCTV,\s*방송 화면",
        r"빙의/원격접속 중이면 실제 몸이 안 보이므로",
        r"\(빙의/원격/V\.O\.\s*등\)",
    ]
    for pat in forbidden_signatures:
        assert not re.search(pat, body), f"A047 signature residue: {pat}"


# --- C4: STEP_MANIFEST visual_world_rules.schema_version = 1 신설 + cp invalidation flow ---


def test_c4_step_manifest_schema_version_set() -> None:
    from app.core.step_manifest import STEP_MANIFEST

    vwr_entry = STEP_MANIFEST["visual_world_rules"]
    assert vwr_entry.get("schema_version") == 1, vwr_entry


def test_c4_cp_invalidation_flow_deterministic() -> None:
    """Codex iter 1 F3 흡수: spec §5.4 C4 의 "cp invalidation flow (incompatible
    stamp cp 차단 verify)" — deterministic StepRunner._check_cp_mismatch 호출.
    no live LLM / router completion call.
    """
    from app.core.step_manifest import STEP_MANIFEST
    from app.core.step_runner import StepRunner

    vwr_manifest = STEP_MANIFEST["visual_world_rules"]
    assert vwr_manifest["schema_version"] == 1

    class _ManifestStub:
        """Duck-type stub — _check_cp_mismatch 본문 line 1511-1516 schema_version
        분기 만 trigger. cp_hash 분기는 cp.config_hash 미지정 + manifest entry
        가 cp_hash 도 None 으로 skip.
        """
        manifest = vwr_manifest
        step_id = "visual_world_rules"
        project_config: dict = {}

    stub = _ManifestStub()

    # Incompatible stamp cp (cp_schema=2 vs current=1) → BLOCK (non-None mismatch reason).
    incompatible_result = StepRunner._check_cp_mismatch(stub, {"schema_version": 2})
    assert incompatible_result is not None, "incompatible-stamp cp must BLOCK"
    assert "schema_version mismatch" in incompatible_result, incompatible_result

    # Older unstamped cp (cp_schema absent) → None (legacy 호환 보장, force rerun 운영자 결정).
    legacy_result = StepRunner._check_cp_mismatch(stub, {})
    assert legacy_result is None, legacy_result

    # Matching cp (cp_schema=1) → None (no schema_version mismatch; cp_hash 분기는 absent 로 skip).
    match_result = StepRunner._check_cp_mismatch(stub, {"schema_version": 1})
    assert match_result is None, match_result


# --- C5: scene_director step load_prompt path 정합 (9.* auto-active) ---


def test_c5_scene_director_prompt_loader_latest_v9() -> None:
    """prompt_loader latest version dir 자동 결정 — 9.* dir 가 latest 로 활성."""
    from app.modules.prompt_loader import load_prompt

    system = load_prompt("scene_director", "system")
    # v9 의 principle-only rewrite 안에 들어가는 SOT reference token 1+
    assert re.search(r"visual_world_rules|VWR|rule_type", system), (
        "scene_director latest prompt must reference VWR SOT (v9 expected)"
    )


def test_c5_vwr_prompt_loader_latest_v7() -> None:
    """prompt_loader latest version dir 자동 결정 — 7.* dir 가 latest 로 활성."""
    from app.modules.prompt_loader import load_prompt, load_schema

    system = load_prompt("visual_world_rules", "system")
    # v7 의 canonical 10-token list (line 51) 본문 안 substring 1+
    assert "possession" in system and "transformation" in system

    schema = load_schema("visual_world_rules", "rules_schema")
    rule_type_def = schema["properties"]["rules"]["items"]["properties"]["rule_type"]
    assert rule_type_def["enum"] == CANONICAL_RULE_TYPE_ENUM
```

### S2 — pytest 실행

- [ ] **Step 2.1: canary 8 test 모두 PASS 확인**

```bash
cd backend && pytest tests/integration/test_area_7a_no_drift.py -v
```

Expected: 8 PASS (C1 invalid + C1 valid loop + C2 + C3 + C4 manifest schema_version + C4 cp invalidation flow (F3 흡수) + C5 scene_director + C5 VWR = 8 test).

### S3 — W3b commit

- [ ] **Step 3.1: git add + commit**

```bash
git add backend/tests/integration/test_area_7a_no_drift.py
git commit -m "$(cat <<'EOF'
test(area-7a-visual-world-rules-sot-v1): W3b canary — jsonschema deterministic VWR enum reject + system/schema 정합 + scene_director v9 principle + manifest schema_version + prompt_loader latest

5 canary (spec §5.4, no live LLM):
- C1: jsonschema.validate() invalid rule_type ("invalid_value_not_in_enum")
  → ValidationError raise. 10 canonical enum 모두 valid 확인 (loop).
- C2: VWR system.md ↔ rules_schema.json 10-token 정합 (structural).
- C3: scene_director v9 principle-only — VWR SOT reference token 1+ + A047
  3 site signatures (영상통화/CCTV / 빙의/원격접속 사례 enumeration / parenthetical
  "(빙의/원격/V.O. 등)") 0.
- C4: STEP_MANIFEST visual_world_rules.schema_version = 1 신설 (current None
  → 1) + cp invalidation flow (StepRunner._check_cp_mismatch deterministic
  duck-type stub — incompatible-stamp cp BLOCK / legacy unstamped cp None /
  matching cp None) — Codex iter 1 F3 흡수.
- C5: prompt_loader latest version dir 자동 결정 — VWR 7.* / scene_director
  9.* 자동 활성 verify (system + schema 본문 안 canonical token 1+).

NO VLM 의무 — 모든 verification = schema/text/manifest only.

Codex iter 3 F-4 흡수 — live router completion call 금지.
Codex iter 1 F3 흡수 — C4 cp invalidation flow 신규.

Spec: docs/superpowers/specs/2026-05-18-area-7a-visual-world-rules-sot-v1-design.md §5.4

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

---

## Task 4c: W3c — Closure Cleanup (single commit)

**Files (repo-local)**:
- Modify: `docs/superpowers/specs/2026-05-18-area-7a-visual-world-rules-sot-v1-design.md` (status: closed)
- Modify: `docs/superpowers/specs/2026-05-16-track-b-semantic-debt-roadmap-design.md` §5.7 (Sub-A closed mark)
- Modify: `docs/visual-reliability-audit/2026-05-14-semantic-string-routing-debt-audit/02-prompt-side-hygiene-audit.md` (A047/A096/A097/A098 status)

**Files (external, repo 밖 — git stage 절대 금지)**:
- Create: `/Users/manta/.claude/projects/-Users-manta-Documents-Projects-TheRoad-I1/memory/session_20260518_area_7a_v1_closure.md`
- Modify: `/Users/manta/.claude/projects/-Users-manta-Documents-Projects-TheRoad-I1/memory/MEMORY.md`

### S1 — Spec status close

- [ ] **Step 1.1: spec frontmatter status 갱신**

File: `docs/superpowers/specs/2026-05-18-area-7a-visual-world-rules-sot-v1-design.md`

Frontmatter (line 4) edit:

Before:
```yaml
status: design (brainstorming closure Codex iter 1+2 흡수 완료, awaiting Codex spec iter review)
```

After:
```yaml
status: closed (Codex iter 5 APPROVED_FOR_PLAN + W0-W3 execution APPROVED_FOR_PUSH + push 완료)
```

- [ ] **Step 1.2: spec body 끝 closure section 추가**

File 끝 append (after §10 Codex Iter Trace):

```markdown
## 11. Closure (2026-05-18+)

**Status**: closed (Codex iter 5 APPROVED_FOR_PLAN + W0-W3 atomic execution + Codex per-wave + range review APPROVED_FOR_PUSH + push 완료).

**Wave commits**:
- W1 (`<hash>`): VWR v7 producer atomic (rules_schema.json + system.md + step_manifest.schema_version=1).
- W2 (`<hash>`): scene_director v9 consumer atomic (A047 3 site principle-only).
- W3a (`<hash>`): residue gate test (4 strict + sanity).
- W3b (`<hash>`): canary test (5 항목, no live LLM).
- W3c (`<hash>`): closure cleanup (본 section + roadmap §5.7 + audit row mark).

**Closure verdict** (Codex iter 2 Q7 verbatim):
> "Area #7a closes when visual_world_rules v7 is the structured SOT for physical-presence/visual-world rule_type via 10-value schema enum + synchronized system instruction, scene_director v9 no longer teaches A047 scenario-specific exclusion lists and instead uses VWR rules/director_notes as the upstream principle source, active prompt/schema residue gates pass, canary validates rule_type enum enforcement and scene_director prompt residue, and no VLM validation is used."

**In-scope closed**:
- A047 (scene_director A047 3 site).
- A096 (rules_schema rule_type enum + description drift).
- A097 (rules_schema director_notes description small fix).
- A098 (system.md rule_type list).

**Carry (non-claim, follow-up)**:
- Area #7b (11-site VWR downstream prompt cascade).
- Area #7c (t2i_review v6 placeholder abstraction).
- Area #7d (cinematography closed-list SOT dual-site).
- Carry-A099 (VWR system.md director_notes prose redesign).
- Carry-A100 (VWR system.md t2i_context demonym redesign).
- Carry-P021 (scene_consistency enumeration broad, source unresolved).
```

### S2 — Roadmap §5.7 update

- [ ] **Step 2.1: roadmap §5.7 closure mark**

File: `docs/superpowers/specs/2026-05-16-track-b-semantic-debt-roadmap-design.md`

§5.7 sub-area (b) 본문 안 `Status` row 추가 또는 update:

```markdown
- **Sub-area (b) — Area #7a (visual_world_rules SOT 일원화 + scene_director A047 흡수)**: **closed (2026-05-18+, commit <range>)**. In-scope = A047/A096/A097/A098. Carry follow-up = Area #7b (11-site cascade) + Area #7c (t2i_review placeholder) + Area #7d (cinematography dual-site) + Carry-A099 (director_notes prose) + Carry-A100 (t2i_context demonym) + Carry-P021 (scene_consistency, source unresolved).
```

### S3 — Audit row status mark

- [ ] **Step 3.1: A047/A096/A097/A098 row mark "closed (Area #7a)"**

File: `docs/visual-reliability-audit/2026-05-14-semantic-string-routing-debt-audit/02-prompt-side-hygiene-audit.md`

해당 audit document 안 A047 / A096 / A097 / A098 row body 끝 `**Status: closed (Area #7a, 2026-05-18+)**` line 추가. A099 / A100 = `**Status: carry (Carry-A099 / Carry-A100)**` line 추가.

(controller 해당 file 의 row 위치 grep 후 surgical edit.)

### S4 — External memory file 작성 (repo 밖 — git stage 절대 금지)

- [ ] **Step 4.1: closure 종합 memo 작성**

File (repo 밖): `/Users/manta/.claude/projects/-Users-manta-Documents-Projects-TheRoad-I1/memory/session_20260518_area_7a_v1_closure.md`

Body skeleton:
```markdown
---
name: session-20260518-area-7a-v1-closure
description: "2026-05-18+ Area #7a (VWR SOT + scene_director A047 absorption) v1 closure 종합 — W1-W3c atomic execution + Codex per-wave + range review APPROVED_FOR_PUSH + push 완료."
metadata:
  node_type: memory
  type: project
---

**2026-05-18+ Area #7a v1 closure 종합 (Track B Tier 3 #1)**

## Final state
- push 완료 (`<range>` N commits).
- 4 wave (W0 inventory / W1 producer / W2 consumer / W3a residue / W3b canary / W3c closure).
- Codex iter X round APPROVED.

## Wave commits
- W1: `<hash>` VWR v7 producer atomic.
- W2: `<hash>` scene_director v9 consumer atomic.
- W3a: `<hash>` residue gate.
- W3b: `<hash>` canary.
- W3c: `<hash>` closure cleanup.

## Architecture
(production + carry table 정합)

## Closed boundary (in-scope vs carry)
(spec §1.3 정합)

## Codex iter trace
(W0-W3 per-wave + range review)

## 함정 (본 session 신규 학습)
(controller verify 후 1-10 항목)

## 다음 area candidate
(Area #7b / #7c / #7d / Carry-A099 / Carry-A100 / Carry-P021)

## References
(spec + roadmap + audit + production code path)

## 관련 memo
(이전 진입점 해소 + Tier 2 lesson 정합)
```

- [ ] **Step 4.2: MEMORY.md index 갱신**

File (repo 밖): `/Users/manta/.claude/projects/-Users-manta-Documents-Projects-TheRoad-I1/memory/MEMORY.md`

추가 1 line (semantic 위치):
```markdown
- [session_20260518_area_7a_v1_closure.md](session_20260518_area_7a_v1_closure.md) — **2026-05-18+ Area #7a (VWR SOT + scene_director A047 absorption) v1 closure 종합 (Track B Tier 3 #1, push 완료)** — ...
```

해소 표시 (이전 진입점 2개):
- `[next_session_area_7a_plan_drafting.md]` description 앞에 `**(해소됨 2026-05-18+, 결과 session_20260518_area_7a_v1_closure.md 참조)**` 추가.

### S5 — git status 확인 (path-limited)

- [ ] **Step 5.1: stage scope verify**

```bash
git status
```

확인: modified = 3 file (spec + roadmap + audit 02-) only. external memory file 은 `git status` 에 나타나지 않음 (`~/.claude/...` repo 밖).

- [ ] **Step 5.2: path-limited git add**

```bash
git add docs/superpowers/specs/2026-05-18-area-7a-visual-world-rules-sot-v1-design.md \
        docs/superpowers/specs/2026-05-16-track-b-semantic-debt-roadmap-design.md \
        docs/visual-reliability-audit/2026-05-14-semantic-string-routing-debt-audit/02-prompt-side-hygiene-audit.md
```

### S6 — W3c commit

- [ ] **Step 6.1: W3c commit**

```bash
git commit -m "$(cat <<'EOF'
docs(area-7a-visual-world-rules-sot-v1): W3c closure — spec status closed + roadmap §5.7 mark + audit A047/A096/A097/A098 closed status

W0-W3b atomic execution 완료 + Codex per-wave + range review APPROVED_FOR_PUSH.

Closure verdict (Codex iter 2 Q7 verbatim):
> "Area #7a closes when visual_world_rules v7 is the structured SOT for
> physical-presence/visual-world rule_type via 10-value schema enum +
> synchronized system instruction, scene_director v9 no longer teaches
> A047 scenario-specific exclusion lists and instead uses VWR rules/
> director_notes as the upstream principle source, active prompt/schema
> residue gates pass, canary validates rule_type enum enforcement and
> scene_director prompt residue, and no VLM validation is used."

In-scope closed: A047 (scene_director 3 site) + A096 (rules_schema rule_type
enum + desc) + A097 (director_notes desc small fix) + A098 (system.md
rule_type list).

Carry follow-up: Area #7b (11-site cascade) + Area #7c (t2i_review
placeholder) + Area #7d (cinematography dual-site) + Carry-A099 (director_notes
prose) + Carry-A100 (t2i_context demonym) + Carry-P021 (scene_consistency).

Spec: docs/superpowers/specs/2026-05-18-area-7a-visual-world-rules-sot-v1-design.md (status closed)
Roadmap: docs/superpowers/specs/2026-05-16-track-b-semantic-debt-roadmap-design.md §5.7 (Sub-A closed mark)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
```

### S7 — W0-W3c Codex range review + push

- [ ] **Step 7.1: Codex W0-W3c range review 의뢰**

tmux_type %0 (fresh):
```
Area #7a W0-W3c range review 의뢰.

Range: <W1 commit>..<W3c commit> (5 commits — W1 producer / W2 consumer / W3a residue / W3b canary / W3c closure).
Spec: docs/superpowers/specs/2026-05-18-area-7a-visual-world-rules-sot-v1-design.md.

검토 항목:
1. W1-W3c atomic boundary 정합 (W1/W2 single commit 보존, W3 3 commit 분리).
2. Residue gate (W3a) 4 strict + sanity 정합 vs spec §7.
3. Canary (W3b) 5 항목 정합 vs spec §5.4 (no live LLM, jsonschema deterministic).
4. Closure (W3c) verdict verbatim + roadmap §5.7 mark + audit row status.
5. Carry table (A099/A100/P021/7b/7c/7d) 명시.
6. NO VLM.
7. unrelated drift 0 (path-limited git add 정합).

verdict: APPROVED_FOR_PUSH / NEEDS_REVISION_MINOR / NEEDS_REVISION_MAJOR.

완료되면 끝났다고 claude mcp 호출해줘!
```

- [ ] **Step 7.2: APPROVED_FOR_PUSH 시 즉시 push (사용자 ask 0)**

[[feedback_push_no_user_ask]] 정합:

```bash
git push origin main
```

확인: `git log origin/main..HEAD` 결과 0 commits (모두 push).

- [ ] **Step 7.3: Closure 종합 memo 작성 완료 + 다음 진입점 memo**

External memory (Task 4c S4 본문) — commit hash + push range 채워 fix-up.

다음 진입점 memo (Area #7b 후속) 작성:
```
File: /Users/manta/.claude/projects/-Users-manta-Documents-Projects-TheRoad-I1/memory/next_session_area_7b_cascade_brainstorm.md
Body: Area #7b (11-site VWR downstream cascade) brainstorming 진입점 — Area #7a closed 후 follow-up Tier 3.
```

MEMORY.md index 추가.

---

## Self-Review (Plan author)

작성 후 controller self-review (subagent dispatch 금지, 단순 checklist):

1. **Spec coverage**:
   - §1.1 Purpose + §1.2 In-scope (4 surface group: A096 schema + A097 director_notes desc + A098 system list + A047 scene_director 3 site + STEP_MANIFEST) → Task 2 (W1) + Task 3 (W2) 매핑 ✓.
   - §1.3 Out-of-scope (Area #7b/c/d + Carry-A099/A100/P021 + 새 enum / helper 신설 0 + closed area 변경 0) → Task 2 (S3 site 보존 step) + Task 4c S1 closure section 명시 ✓.
   - §2 Audit Evidence (roadmap §5.7 + audit row A047/A096-A100) → Task 1 S6 verify step ✓.
   - §3 4-Layer Architecture (Producer schema + Producer prompt + Consumer prompt + Code constraint) → Task 2 (W1 schema + system + manifest) + Task 3 (W2 consumer) 매핑 ✓.
   - §4 Q1-Q7 Closure (helper 0 + 새 enum 0 + 11-site cascade carry + version/cp wording verbatim + residue gate 4 + closure verdict verbatim) → Task 2/3/4 cascade 정합 ✓.
   - §5 Wave Outline (W0/W1/W2/W3) → Task 1/2/3/4abc 매핑 ✓.
   - §6 Carry Table (Area #7b/c/d + Carry-A099/A100/P021) → Task 4c S1 closure section + S2 roadmap + S3 audit row 매핑 ✓.
   - §7 Residue Gate (4 strict + structural parse 우선) → Task 4a 본문 ✓.
   - §8 Closure Verdict (verbatim) → Task 4c S6 commit body + S1.2 closure section verbatim ✓.

2. **Placeholder scan**: TBD / TODO / "implement later" / "fill in details" / "Similar to Task N" 패턴 0 ✓ (controller grep verify 의무).

3. **Type consistency**:
   - `CANONICAL_RULE_TYPE_ENUM` list literal = Task 4a + Task 4b 동일 ✓.
   - HHMM placeholder substitute timing = W1 / W2 각각 (서로 다를 수 있음) ✓.
   - `STEP_MANIFEST["visual_world_rules"]` 접근 path = Task 2 S4 + Task 4b C4 동일 ✓.
   - `_latest_active_dir(base, prefix)` helper signature = Task 4a + Task 4b 동일 ✓.

4. **Spec/plan boundary**:
   - spec = decision-level (4-wave 분리 / enum 10-token canonical / Carry boundary / residue gate 4 항목).
   - plan = task-level (commit count / pytest command / commit message verbatim / pre-commit grep W1 7 종 / W2 6 종).
   - boundary 흐림 시 Codex iter 1 review 흡수.

5. **Codex MCP workflow**:
   - W1 commit → Codex per-wave review (Task 2 S7).
   - W2 commit → Codex per-wave review (Task 3 S5).
   - W3a/W3b/W3c commit → Codex W0-W3c range review (Task 4c S7).
   - 모든 review 의뢰 prompt 끝 `완료되면 끝났다고 claude mcp 호출해줘!` 의무.

6. **NO VLM hard constraint**:
   - canary (Task 4b) = jsonschema + structural + text only.
   - residue gate (Task 4a) = JSON parse + markdown parse + regex only.
   - live router completion / VLM 의존 0.

7. **사용자 ask 0**:
   - W0 deferred decision (Task 1 S8) = Codex 의논 + controller 자체 결정.
   - W1 / W2 / W3 진입 = controller 자체 결정.
   - W0-W3c range push = APPROVED_FOR_PUSH 후 즉시 push (Task 4c S7.2).
   - 사용자 ask = 심각 버그 only.
