### Task 2: geometry 팩 v4 발행 + selector 상향

**Files:**
- Create: `prompts/_base/outdoor_marker_geometry/4.<ts>/system.md`,
  `prompts/_base/outdoor_marker_geometry/4.<ts>/user_template.md`
- Modify: `backend/app/modules/pipeline/outdoor_marker_map.py:51-54`
  (`_ANCHOR_GEOMETRY_PACKS`/`_VIEW_GEOMETRY_PACKS` 에 `"4"` 편입),
  `:513-516` (스키마 게이트), `:548-550` (validator 게이트)
- Modify: `backend/app/core/steps/shot_conti_light_step.py:37`
  (`LANE_GEOMETRY_PACK_VERSION`), `:1151` 부근 (shot_text 공급)
- Test: `backend/tests/pipeline/test_outdoor_marker_map.py`

**Interfaces:**
- Consumes: T1 의 `_FACING_RELATION_GEOMETRY_PACKS`,
  `validate_marker_geometry(..., require_facing_relation, shot_text)`,
  `build_marker_geometry_schema(..., include_facing_relation)`
- Produces: `LANE_GEOMETRY_PACK_VERSION = "4"`;
  `run_marker_geometry(..., shot_text=...)` 가 대면 관계까지 검증한 뒤
  반환

- [ ] **Step 1: v4 디렉터리를 v3 사본으로 만든다**

```bash
cd /Users/manta/Documents/Projects/TheRoad-I1
TS=$(date +%Y%m%d%H%M)
cp -r prompts/_base/outdoor_marker_geometry/3.202607251321 \
      prompts/_base/outdoor_marker_geometry/4.$TS
ls prompts/_base/outdoor_marker_geometry/4.$TS
```
Expected: `system.md  user_template.md`

- [ ] **Step 2: v4 `system.md` 에 대면 관계 계약을 넣는다**

`prompts/_base/outdoor_marker_geometry/4.<ts>/system.md` 의 facing 문단
(`Finally author which way this subject is turned at this moment:` 로
시작해 `it always lies clearly away from it.` 로 끝나는 부분) 바로
뒤에 다음을 삽입한다:

```
   Then declare how this subject faces the CAMERA you author in (2),
   as camera_facing_relation:
     away_from_camera - the camera sees this subject from behind
     toward_camera    - the camera sees this subject from the front
     profile          - the subject is side-on to the camera
     unspecified      - the shot text does not say
   If the shot text states this for a subject (seen from behind, back
   turned, facing the lens, in profile, ...), you MUST declare the
   matching relation and place the facing point so the geometry agrees
   with it: for away_from_camera the facing point lies FARTHER from the
   camera origin than the subject does; for toward_camera it lies
   CLOSER; for profile it lies roughly sideways, neither farther nor
   nearer. A declared relation that the coordinates contradict is
   rejected and you will be asked to fix it.
   With any relation other than unspecified, also author
   camera_facing_evidence: the exact words from the shot text that say
   it, quoted verbatim in the original language. Do not paraphrase and
   do not invent a quote - it is checked against the shot text. When
   the relation is unspecified, camera_facing_evidence must be an empty
   string.
```

- [ ] **Step 3: 실패하는 테스트를 쓴다 — 팩 게이트**

`backend/tests/pipeline/test_outdoor_marker_map.py` 에 추가:

```python
def test_geometry_pack_v4_gates_facing_relation():
    from app.modules.pipeline.outdoor_marker_map import (
        _ANCHOR_GEOMETRY_PACKS,
        _FACING_RELATION_GEOMETRY_PACKS,
        _VIEW_GEOMETRY_PACKS,
        resolve_prompt_version,
    )

    # v4 는 v3 의 anchor·view 계약을 승계하고 대면 관계를 추가한다
    assert "4" in _ANCHOR_GEOMETRY_PACKS
    assert "4" in _VIEW_GEOMETRY_PACKS
    assert _FACING_RELATION_GEOMETRY_PACKS == {"4"}
    # v4 팩 디렉터리가 실재해야 selector 가 해석된다
    assert resolve_prompt_version("4").startswith("4.")


def test_geometry_pack_v4_system_declares_relation_contract():
    from app.modules.prompt_loader import load_prompt
    from app.modules.pipeline.outdoor_marker_map import (
        _MODULE,
        resolve_prompt_version,
    )

    sys_txt = load_prompt(
        _MODULE, "system", version=resolve_prompt_version("4"))
    for token in ("camera_facing_relation", "camera_facing_evidence",
                  "away_from_camera", "toward_camera", "profile",
                  "unspecified"):
        assert token in sys_txt
    # 발명 인용 금지 계약이 명문화돼 있어야 한다
    assert "checked against the shot text" in sys_txt
```

- [ ] **Step 4: 실패 확인**

Run: `cd backend && python -m pytest tests/pipeline/test_outdoor_marker_map.py -q -k "pack_v4"`
Expected: FAIL — `assert "4" in _ANCHOR_GEOMETRY_PACKS`

- [ ] **Step 5: 팩 집합에 v4 를 편입한다**

`outdoor_marker_map.py:51-54`:

```python
_ANCHOR_GEOMETRY_PACKS = {"2", "3", "4"}
```
```python
_VIEW_GEOMETRY_PACKS = {"3", "4"}
```

- [ ] **Step 6: 저작 루프에 게이트를 배선한다**

`outdoor_marker_map.py:513-516` 를 바꾼다:

```python
    _anchored = prompt_version in _ANCHOR_GEOMETRY_PACKS
    _viewed = prompt_version in _VIEW_GEOMETRY_PACKS
    _related = prompt_version in _FACING_RELATION_GEOMETRY_PACKS
    schema = build_marker_geometry_schema(
        include_anchors=_anchored, include_view=_viewed,
        include_facing_relation=_related)
```

`:548-550` 의 검증 호출을 바꾼다:

```python
        violations = validate_marker_geometry(
            result or {}, require_anchors=_anchored,
            require_view=_viewed, require_facing_relation=_related,
            shot_text=shot_text_for_relation)
```

`run_marker_geometry` 시그니처에 키워드 인자를 추가한다(default 로 기존
호출 불변):

```python
    shot_text_for_relation: str = "",
```

docstring 에 한 줄:

```python
    shot_text_for_relation (v4+): camera_facing_evidence 인용 대조용 SHOT
    TEXT 원문. v4 팩인데 미공급이면 모든 명시 relation 이 거부된다
    (fail-closed — 근거 없는 선언 통과 금지).
```

- [ ] **Step 7: 호출부에서 shot_text 를 공급하고 selector 를 올린다**

`backend/app/core/steps/shot_conti_light_step.py:37`:

```python
LANE_GEOMETRY_PACK_VERSION = "4"
```

`:1151` 부근의 `run_marker_geometry(...)` 호출에
`prompt_version=LANE_GEOMETRY_PACK_VERSION` 과 나란히 추가한다.
샷 설명은 같은 블록에서 스케치 프롬프트에도 넘기는 값이라 동일 표현을
쓴다(`:1233` 의 `shot_desc=vshot.get("description") or ""`):

```python
                    shot_text_for_relation=vshot.get("description") or "",
```

- [ ] **Step 8: 테스트 통과 확인**

Run: `cd backend && python -m pytest tests/pipeline/test_outdoor_marker_map.py tests/core/test_shot_conti_light_lane.py -q`
Expected: PASS

- [ ] **Step 9: 커밋**

```bash
git add prompts/_base/outdoor_marker_geometry \
        backend/app/modules/pipeline/outdoor_marker_map.py \
        backend/app/core/steps/shot_conti_light_step.py \
        backend/tests/pipeline/test_outdoor_marker_map.py
git commit -m "feat(lane): outdoor_marker_geometry 팩 v4 — 카메라 대면 관계 저작 계약+selector 배선

팩 발행과 selector 상향을 같은 커밋에 둔다(팩만 저작하고 상수를 올리지
않으면 사문화되는 실측 교훈 — canon v3·lane_plan v4)."
```

---

