# GROUNDING-V2 · D — 구현 순서

> 2026-08-31. owner 표·계약은 `2026-08-31-grounding-v2-D-owner-table.md`.
> 여기는 **무엇을 언제 짓는가**만 적는다.
>
> ★가르는 기준은 하나다 — **활성 경로가 그 순간 무엇을 보는가.**
> 미리 지어도 아무도 안 보면 inert 이고, 하나라도 보기 시작하면 **그 판은
> 원자적이어야 한다**(반만 켠 판이 제일 나쁘다 — 실제로 한 번 만들었다).

## 0. 지금까지 (무료·inert, 2026-08-31 끝남)

    grounding_host_context      세 상태 계약 + 결정적 합집합       시험 38
    grounding_reference_bundle  맥락+상세 덧붙임 + 면제 못 뚫는 계약  시험 25
    배경 자리 용량 확인          계약 층 · production helper 끝점    시험 14
    프롬프트 도달 확인 ②③        각 Image N·역할 · 역할 재사용 범위   시험 9
    owner_of_final_id           가장 긴 접두 + 숫자                (이미 있음)

★전부 `app/` 활성 경로가 **안 부른다**(AST 로 확인).

## 1. 아직 **inert 로 더 지을 수 있는 것** — 세 덩이

### 1-A. `location_part` 를 만드는 쪽 (producer 계약)

    grounding_chunk 팩에 `host_context` 를 **required** 로 넣는다
      → ★**새 팩 버전** + **chunk acquisition identity 변경**이 따라온다
    `resolve_rows` 가 `host_context` 를 `normalize()` 로 내린다
    `reduce_episode` 가 구간 간 `reconcile()` 을 태운다
    adapter 의 `OWNER_TO_MERGE_KEY` 에 `location_part` 자리를 만든다

★이 셋은 **v2_chunk 모드에서만** 도는 코드다. `v2_chunk` 는 아직
`PLANNED_MODES` 라 아무 프로젝트도 못 고른다 — 그래서 inert 다.

### 1-B. 받는 쪽 목록 (sync 계약)

    MATERIALIZABLE_OWNER_TYPES        + location_part
    entity_metadata._ALLOWED_ENTITY_TYPES + location_part (중립 shape 갈래)
    EntitySyncService 의 여섯 자리      + location_parts
      is_cp_syncable · data key · prepass · conflict query ·
      _TYPE_DEFS · stale cleanup
    ★접두 counter 를 `owner_of_final_id`/`OWNER_PREFIX` **한 계약**으로
    ★canon 재사용: `existing_by_short_id` 먼저 + `(entity_type,name)` 보조,
      두 열쇠가 다른 canon 이면 **fail-closed**
    ★`outlook` 은 **안 넣는다**

★이것들은 목록을 **넓히기만** 한다. `location_part` 행이 **아직 안 생기므로**
(1-A 가 꺼져 있으니) 활성 경로의 동작은 **한 글자도 안 바뀐다**.
★단, 접두 counter 수리는 **지금도 값이 바뀐다** — `LP` 가 없어도 파서가
바뀌면 기존 `L##` 계산이 같아야 한다. **전후 등가 시험**을 반드시 넣는다.

### 1-C. 관계 (`part_of`)

    entity_relation CP schema 에 `relation_type` 명시
      (`visual_variant | part_of`)
    RelationSyncService 가 **타입별로** desired/existing/delta 를 가른다
      ★`part_of` sync 가 `visual_variant` 를 stale 로 지우면 안 된다(그 반대도)

★`part_of` 행이 아직 안 오므로 동작 무변. **타입별 분리는 지금도 값이
바뀌므로** `visual_variant` 만 있는 판에서 **전후 등가 시험**을 넣는다.

## 2. **한꺼번에** 해야 하는 것 — 원자적 cutover 한 판

아래는 **하나라도 먼저 켜면 반만 켠 판**이 된다.

    ①`grounding_mode` 의 받는 집합에 `v2_chunk` 를 넣는다
       ★이것만으로 옛 유료 스텝 넷이 켜진다 — 실제로 겪었다
    ②새 producer/adapter ON · 옛 `grounding_plan`/`grounding_research`
       **no-call**
       ★`episode_reference_policy` 의 source·dependency 를 **새 중앙
        acquisition CP** 의 `final_id` + raw `status` + `outcome` 으로 바꾼다
    ③`EntityRelationStep` 을 **결정적 no-call projection** 으로
       ★adapter 가 그 CP 를 **직접 쓰지 않는다**
    ④§4b 두 곳(`episode_reference_policy_step.py:174` ·
       `render_prompt_card.py:2197`)을 **한 계약**으로 모으고
       `owner_of_final_id` 한 벌을 쓴다
    ⑤general scene 경로가 `insert_bundle_refs` 를 **실제로 부른다**
    ⑥validator 가 조사 `selected` 에 **정확 부착**을 요구한다
       ★클로즈업·기존 plate 면제와 **무관하게**
       ★`reference_unavailable` 은 required 에서 **뺀다**
    ⑦`REF_ROLE_VALUES` 에 상세 역할 선언 + 갈래 문안
    ⑧`generation_difficulty` 임시 다리 제거
       ★새 SOT 와 소비자가 **동시에 선** 뒤에만
    ⑨stamp bump · invalidation

★⑤⑥⑦은 서로 묶여 있다 — 부르는데 역할이 없으면 `RefRoleError` 로 서고,
요구만 하고 안 부르면 모든 판이 막힌다.

## 3. 그 뒤 — 확인 순서

    무료  ⑤⑥⑦ 이 켜진 판에서 `기존 plate/prev + 맥락 + 상세`가
          네 평행 목록·validator·프롬프트 `Image N`/역할까지 **다 간다**
    유료  ④실제 provider 에 **들어가는 이미지 수**를 센다
          ★상한은 다른 파이프의 2를 베끼거나 임의로 박지 말고
           **provider/dispatch 계약 한 곳**에서. 넘으면 **조용히 안 자른다**
    사람  고증 정확성·이미지 품질 — 검토 화면과 근거를 남긴다

## 4. 되돌리는 길

`v2_chunk` 를 받는 집합에서 빼면 ①~⑨가 **통째로** 꺼진다.
★그래서 ①이 **마지막**이어야 한다 — 나머지를 다 심어 두고 마지막에 켠다.
★`generation_difficulty` 제거(⑧)만은 되돌리기가 어렵다. 그것은 새 SOT 와
소비자가 **한 판에서 같이 서는 것**을 본 뒤에만 한다.
