BG 파이프라인 #3 분할 — plate partition (3a) + reference DAG (3b)

TheRoad Scene Lab · W21B-wave-4 · 2026-05-31 · Claude 초안 → Codex cross-review 2회 → doc-only APPROVE (D1/D4/D5 lock)

APPROVED v0.1 — doc-only 코드/DB/prompt/render 변경 0 design brief = contract/schema SOT

이 문서의 목적 — 사용자 4단계 BG 아키텍처 비전 중 #3(원본 plate 몇 개 + 어떤 샷에 + BG간 일관성 참조 트리)를 구현 가능한 두 레이어 3a (plate partition / base BG allocation) + 3b (reference DAG construction)로 분할 설계한다. 기존 shot_aware_bg_render_plan reference graph를 버리지 않고 그 위에 card-aware 필드/계약을 얹는다. 코드 변경 0. 이 brief는 Codex 리뷰 합의 → dry/mock → 구현 순서의 1단계다.

목차

§1 4단계 비전과 #3의 위치
§2 기존 reference graph(현 #3) 실측 — 무엇이 이미 있나
§3 핵심 공백 — geometry-only corroboration
§4 3a / 3b 분할 원칙
§5 3a — plate partition / base BG allocation
§6 3b — reference DAG construction
§7 절대 보존 계약 (demote-not-delete / W21B-w3 alias / C4 card)
§8 A(#1) / C(#4) 위치 — 지금 건드리지 않음
§9 결정 대기 / Codex 합의 포인트
§10 빌드 시퀀스 (doc-only → 구현)
§11 시나리오 leakage 없음 증명

§1 · 4단계 비전과 #3의 위치

사용자 비전(2026-05-31)을 Claude가 매핑하고 Codex가 합의한 4단계:

#단계현 상태이 wave 범위
1 (A)전체 씬+샷 종합 → 어떤 FP를 만들지 LLM 판단 background_master_plan(sub_location 휴리스틱 그룹핑) future wave §8 — scene_primary fix로 FP 품질 회복됨, 격상은 별도 wave
2FP 생성floor_plan_promptfloor_plan_render✅ 완료
3 (B)원본 BG 몇 개 + 각 샷 배정 + BG간 일관성 참조 트리 shot_aware_bg_render_plan reference graph 골격 존재 이 brief의 대상 → 3a + 3b로 분할
4 (C)render 시 substrate(FP+card vs card+ref image) 결정 → BG 생성 background_render(fp+prior_bg) + C4 card→prompt 3b 계약 이후 §8 — node별 substrate policy로 분리

§2 · 기존 reference graph(현 #3) 실측 — 무엇이 이미 있나

build_render_plan_for_fp의 현재 파이프라인 (실측, shot_aware_bg_render_plan.py):

이미 존재하는 계약 (3a/3b가 재사용할 골격)

관심사현재 담는 곳의미
plate 공유 (= 같은 원본 BG)render_action = reuse_existing_plate + reuse_target_bg_id 이진 결정. reuse 노드는 target의 픽셀을 copy-less alias (image 0)
reuse 자격 모드REUSE_ELIGIBLE_MODES = {same_physical_space_view, reference_derived}anchor / related_style_new_space / two_refs 는 reuse 불가
같은 공간 corroboration_corroborates_same_space geometry만: camera_unit 일치(hard floor) AND (look_at_unit 일치 OR marker overlap)
참조 트리 (DAG)reference_decision.selected_refs[] + mode + physical_space_id_per_ref 각 ref는 same-fp earlier bg, 최대 2개, DAG 보장(earlier-only 규칙)
render 순서node_index 오름차순reuse target은 항상 자기 child보다 먼저 결정됨
POV ground truthC3 projection 필드 (anchor_shot_id, projection_card_id/hash/state …) C2 카드의 검증된 same-space POV. 현재 corroboration엔 미반영
핵심 관찰 — 현 reference graph는 이미 plate 공유(reuse routing)DAG(selected_refs/mode) 두 관심사를 한 덩어리로 담고 있다. 3a/3b는 새 시스템이 아니라 이 두 관심사를 명시적으로 분리하고 card POV 신호를 주입하는 additive 레이어다.

§3 · 핵심 공백 — geometry-only corroboration

공백 B (사용자 일관성 목표 직결)_corroborates_same_spacecamera_unit / look_at_unit / marker overlap 즉 도면 geometry 신호만 본다. 같은 unit에 앉아 있어도 카메라가 실제로 무엇을 향하고 어떤 base 오브젝트가 프레임에 잡히는지(card POV)는 반영하지 않는다. → "같은 공간이지만 전혀 다른 뷰"를 plate로 합쳐버리거나, 반대로 합쳐도 되는 뷰를 놓친다. 이것이 사용자의 "fp와 배경 안 맞음" / BG간 일관성 결여의 한 축.

C2/C3가 이미 카드의 구조화 POV 증거를 만들어 둔다 — 이것이 corroboration에 주입할 결정적 신호다:

→ 3a는 이 구조화 band 집합으로 같은-plate 공유 가능 여부를 corroborate한다. 문자/substring 매칭이 아니라 enum band 집합 비교(§11).

§4 · 3a / 3b 분할 원칙

3a — plate partition3b — reference DAG
질문"원본 plate 몇 장? 어떤 bg/shot이 같은 base plate를 공유?""각 plate를 어떤 순서·어떤 ref 조합으로 렌더?"
기존 골격route_render_actions (reuse routing)reference_decision.selected_refs + mode
주입할 신호card POV bands (corroboration 확장)plate group 결과 + style/space continuity
출력(additive)plate_group_id / plate_anchor_bg_id / plate_anchor_shot_id / plate_shareability / needs_new_plate ref_tree_parents / render_order_index / ref_role_per_parent / max_refs(=2 보존)
분할 불변식
  1. LLM 신호 vs deterministic 정규화 분리 유지 — 현 설계처럼 LLM은 후보 신호(mode / low_delta candidate / selected_refs)만 emit, code가 corroborate+stamp. 3a/3b의 새 출력은 전부 deterministic stamp(card 구조화 필드 read), LLM 재호출 0.
  2. additive only — 기존 노드 shape 무변경. C3 PROJECTION_CARD_FIELDS 패턴 그대로(새 필드 튜플 추가, in-place mutate).
  3. 3a → 3b 단방향 — 3b는 3a의 plate group을 입력으로만 받는다. 순환 없음.
  4. fail-closed — card 증거는 geometry가 통과시킨 공유를 철회(withhold)만 할 수 있고, geometry가 거부한 병합을 강제하지 못한다. camera_unit hard floor가 최종 권위(§9-D1).
★ 실행 순서 (Codex Required 반영, 2026-05-31) — withhold가 canonical SOT에 반영되도록
초안의 "④ enrich 직후 additive pass"는 결함이었다: route_render_actions가 이미 canonical render_action을 확정한 뒤이고 background_render는 canonical render_action만 신뢰하므로, 그 뒤에 찍는 additive 필드로는 withhold가 실제 렌더에 반영되지 않는다. 반대로 3a가 render_action을 다시 쓰면 §7-B 재정의 금지와 충돌. → withhold는 canonical render_action 확정 전/내부에 들어가야 한다. 수정된 순서:
① LLM provider → graph.nodes
② validate_llm_output                         (DAG / refs / anchor — 무변경)
③ self-card band prep  (신규, pure)        reuse 무관. 각 노드의 self 카드만
                                              재조인(id==hash==card_id AND cp state==pass)
                                              → base band 집합 추출. render_action 불필요.
④ card-aware route_render_actions          기존 geometry corroboration
   (= route_render_actions_v2)                + ③ band corroboration(withhold)를
                                              통합 → canonical render_action /
                                              reuse_target_bg_id 최종 확정 (SOT)
⑤ enrich_nodes_with_projection_cards (C3)     route 이후 — reuse inheritance 포함
                                              projection 7필드 stamp (기존 그대로)
⑥ partition fields mirror (3a 출력)        canonical render_action을
                                              plate_group_id / needs_new_plate 로 MIRROR
                                              (SOT 아님 — 상위 표현)
⑦ build_reference_dag (3b)                 fresh plate 노드만 DAG에 넣어 ref tree
SOT 불변: 실제 렌더 SOT는 여전히 canonical render_action / reuse_target_bg_id. 3a의 plate_group_id/needs_new_plate는 그것을 mirror하는 상위 표현일 뿐. not_shareable_card_withheld로 강등된 노드는 router(④)가 render_new_plate로 stamp → W21B-w3 alias 계약 무손상.

★ withhold-only 정책 (Finding 2, 2a lock — 구현 dry-review 합의): ④ route_v2는 reuse를 닫기(withhold)만 하고, baseline router가 이미 거부한 downstream reuse를 재개방하지 않는다. 예: A→new, B가 A를 reuse하나 card 충돌로 new로 withhold되고 C가 B를 low-delta target하면 — baseline이 이미 C→B를 reuse-of-reuse(LLM contract 위반)로 routing_ok=False+diagnostic 거부했고, route_v2는 이를 보존(C는 render_new_plate, fail-closed). card evidence가 간접적으로 downstream reuse를 enable하지 않게 하는 v0 안전장치. withheld target의 downstream reuse 재개방은 future route_v3 / 2-pass router 작업.

§5 · 3a — plate partition / base BG allocation

위치: shot_aware_bg_render_plan 내부 pure helper 2개 — ③ self-card band prep + ④ card-aware route_render_actions_v2(withhold 통합) + mirror_plate_partition(canonical render_action mirror). 별도 step 아님(§9-D4). withhold는 ④ router 내부에서 일어나고, 3a 출력 필드(⑥)는 그 결과를 mirror만 한다.

입력 (전부 기존 cp / 노드 필드에서)

입력출처
card visible base objectsC2 cp visible_items[] where marker_layer="base" → {band 집합}
camera pose노드 camera_decision.camera_unit / look_at_unit + card camera_pose_source
shot intent / 관련성per_bg_readiness applies_to_shots + card anchor shot
floor-plan groupfp_id (same-fp 경계는 이미 강제됨)
reuse candidate inputsLLM 후보(mode, same_physical_space_low_delta_candidate / low_delta_reuse_target_bg_id) + geometry facts(per_bg_render_facts_by_bg_id, camera_decision). canonical render_action은 ④에서 확정 — 입력 아님.
projection stateC3 projection_card_state / projection_card_id / hash

출력 (additive per-node 필드 — 제안)

PLATE_PARTITION_FIELDS = (
    "plate_group_id",        # 이 bg가 귀속되는 원본 plate의 안정 id
                             #   = render target bg_id (new면 self, reuse면 target)
    "plate_anchor_bg_id",    # 픽셀을 생산하는 bg (= group 대표)
    "plate_anchor_shot_id",  # plate POV ground-truth를 정의하는 샷
    "plate_shareability",    # enum (아래)
    "needs_new_plate",       # bool — 이 bg가 fresh render anchor면 True,
                             #   group anchor를 alias하면 False
)

plate_shareability vocabulary (fail-closed ladder)

의미조건
exclusive혼자 = 자기 plate anchorrender_new_plate AND 공유 멤버 없음
shareable_card_corroboratedcard POV까지 일치 → 강한 공유 geometry corroborate ✓ AND 양쪽 모두 pass card AND base band 집합 corroborate ✓
shareable_geometry_onlygeometry만으로 공유 (현 동작과 동일) geometry corroborate ✓ 이나 card 증거 부재/비-pass → card는 침묵(withhold 아님)
not_shareable_card_withheldcard POV가 geometry 공유를 철회 geometry corroborate ✓ 이나 양쪽 pass card의 base marker가 horizontal left↔right 모순(D5 final) → 병합 거부 → needs_new_plate=True
not_shareable애초에 공유 후보 아님geometry corroborate ✗ (camera_unit 불일치 등)
★ 핵심 fail-closed 규칙 (§9-D1 합의 대상)

base band corroboration (좌표 없는 구조화 비교)

두 pass 카드의 base 항목을 (expected_label-by-marker_number, horizontal_band, depth_band, visibility, confidence) 튜플 집합으로 본다.

★ D5 FINAL lock (dry §10-2 실측 반영, Codex 합의 2026-05-31) — v0 hard-withhold = horizontal left↔right 모순만. 다음을 모두 만족하는 base marker일 때만: depth foreground↔background 모순은 diagnostic-only(depth_band_conflict_diagnostic 기록, canonical render_action 불변). 조건 미달/depth-only면 withhold 안 함 → shareable_geometry_only 유지(보수적 = reuse 보존).
왜 depth는 diagnostic-only인가 — dry §10-2 real-card 증거
같은 bg L05B09의 두 샷 카드(S5_Shot1 vs S5_Shot7) 14 공유 base marker 중, horizontal은 전부 일치(둘 다 left)인데 depth만 2건(mn=4, mn=14)이 foreground↔background로 갈렸다 — 양쪽 confidence가 0.80~0.88로 높았는데도. 같은 공간을 두 번 본 VLM의 depth_band instability. 단일 depth marker로 plate를 쪼개면 false split이 된다 → v0 hard-withhold는 horizontal 충돌만, depth는 진단 기록만. (future: depth withhold는 opt-in + conf≥0.85 + 다중 marker 동시 충돌 시에만 재검토.)

marker_number는 evidence join 키로만 쓰고 prose/prompt로 surface 금지(card schema 명시). band는 enum 집합 비교 — 문자열 의미판단 아님(§11).

W21B-w3 매핑 — 3a의 plate group(anchor + members)은 현 reuse 의미와 1:1. needs_new_plate=False = 기존 reuse_existing_plate, plate_anchor_bg_id = 기존 reuse_target_bg_id. 3a는 reuse 의미를 바꾸지 않는다 — 그룹을 명시화하고 병합 결정에 card corroboration을 더할 뿐. not_shareable_card_withheld로 강등된 노드는 단순히 render_action=render_new_plate로 남는다(기존 경로).

§6 · 3b — reference DAG construction

위치: — 3a mirror(⑥) 이후 새 deterministic pass build_reference_dag(...). 입력 = 확정된 canonical render_action(④) 기반 fresh plate 집합 + 기존 selected_refs/mode.

입력

출력 (additive per-node 필드 — fresh-plate 노드에만)

REFERENCE_DAG_FIELDS = (
    "ref_tree_parents",     # List[str] — 이 plate가 조건으로 거는 부모 plate bg_id
                            #   (selected_refs에서 검증·정규화, 최대 2)
    "ref_role_per_parent",  # {parent_bg_id: "style" | "space_continuity"}
    "render_order_index",   # int — DAG topological 순서 (alias 멤버는 부모 뒤)
    "max_refs",             # int = 2 (보존; 계약 상수)
)

render_order 규칙

  1. topological: 부모 plate가 자식보다 먼저. earlier-only 규칙이 이미 acyclic 보장 → 위상 정렬은 node_index로 충분(검증만).
  2. reuse alias 멤버는 자기 group anchor가 렌더된 직후 순서 — alias는 image 0이므로 순서는 catalog 가시성 목적.
  3. fp_seeded_anchor(identity anchor)는 항상 render_order_index=0 후보(루트).

ref_role_per_parent — style vs space-continuity

modeparent role 의미
same_physical_space_viewspace_continuity — 같은 방 다른 뷰, plate 일관성 유지가 목적
reference_derivedspace_continuity 또는 style (parent의 physical_space_id로 판정)
related_style_new_spacestyle — 새 방이지만 톤/재질 계승
two_refs_distinct_spaces두 parent = 서로 다른 space (validator가 이미 distinct 강제)

role은 mode + physical_space_id_per_ref에서 deterministic 도출 — LLM 재호출/문자 판단 없음.

3b는 무엇을 새로 만드나 — selected_refs는 이미 "어떤 ref"를 담는다. 3b는 "어떤 순서 + 각 ref가 무슨 역할"을 명시화하고, 3a가 alias로 흡수한 노드를 DAG에서 제외해 실제 렌더할 plate만의 깨끗한 ref tree를 만든다. 이것이 §8 C(render input policy)의 입력 계약이 된다.

§7 · 절대 보존 계약

A · camera_decision = demote-not-delete

camera_decision삭제 금지. 현재 _corroborates_same_space(camera_unit/look_at_unit), validate_llm_output, router가 모두 read한다. 3a는 camera_decision을 계속 hard-floor 신호로 사용하고 그 위에 card band를 얹는다. projection 필드가 forward SOT이지만 camera_decision은 compat/audit/geometry-floor로 살아있다.

B · W21B-w3 reuse / copy-less alias 계약

background_render_step.py:934~ 의 alias 의미 불변:

C · C4 card 필드 재사용 계약

§8 · A(#1) / C(#4) 위치 — 지금 건드리지 않음

A (#1, FP 결정 격상) = upstream future wave. background_master_plan의 sub_location 휴리스틱을 LLM 종합 판단으로 격상하는 일. scene_primary fix로 FP 품질이 회복됐으므로 이 wave에서 건드리지 않는다. 3a/3b는 현 master_plan 출력을 입력으로 받는다.
C (#4, render input policy) = 3b 계약 이후. 3b가 ref_tree를 확정한 뒤, node별 substrate policy로 분리 구현: 이 brief에서는 위치만 잡고 구현은 3b 계약 합의 후 별도 brief. C는 3b의 ref_tree_parents/ref_role_per_parent를 입력으로 받는다.

§9 · 결정 — Codex 합의 반영 (2026-05-31)

Codex cross-review 1회 — Required(실행 순서/SOT 문구) 반영 완료(§4-order). D1/D4/D5 lock, D2/D3/D6 합의.

ID질문결정 (lock)
D1card POV 증거가 병합을 강제도 할 수 있나, 철회만 하나? LOCK — 철회만 (fail-closed). card는 geometry-passed 공유를 withhold만, override-merge 불가. 단 withhold는 ④ route_render_actions_v2 내부에서 canonical render_action 확정 전에 적용(Codex Required).
D2card-corroborated 공유에 양쪽 pass card 필수? 한쪽만 pass면? 합의 — 양쪽 pass 필수 → 아니면 shareable_geometry_only(현 동작 보존). 위조 pass 상속 0.
D33a/3b 출력을 checkpoint에 새 필드로? SCHEMA_VERSION bump? 합의 — additive persisted 필드 → SCHEMA_VERSION 8→9 bump, manifest sync. (C3가 7→8 했던 패턴)
D43a/3b를 build_render_plan_for_fp 내부 pass로? 별도 step으로? LOCK — 별도 step 반대, 같은 shot_aware_bg_render_plan 내부 pure helper. 순서 = validate → ③ band prep → ④ card-aware route → ⑤ C3 enrich → ⑥ partition mirror → ⑦ 3b DAG (§4-order).
D5base band 모순 판정 임계 — 무엇을 withhold 근거로? FINAL LOCK(dry 실측 반영) — v0 hard-withhold = horizontal left↔right: 같은 base marker 양쪽 존재 AND 양쪽 visibility∈{visible,partial} AND 양쪽 confidence≥0.6 AND horizontal left↔right. depth fg↔bg는 diagnostic-only(canonical 불변). 근거 = dry §10-2 real card에서 같은 bg 두 샷이 conf 0.80~0.88에도 depth만 갈림(VLM depth instability).
D6card 부재 fp-less / direct-plate BG는 3a에서? 합의projection_card_state ∈ {not_applicable, not_available} → card corroboration skip, geometry-only partition(현 동작). 실패 처리 아님.

§10 · 빌드 시퀀스 (doc-only → 구현)

  1. 이 brief → Codex cross-review(Required 반영 완료) → §9 D1/D4/D5 lock 현 단계 — 재확인 대기
  2. dry/mock 완료 2026-05-31 — production plan cp(17노드, reuse 3) + real-VLM card 3장으로 ③ band prep + ④ route_v2 offline 계산(runner /tmp/w21b_wave4_3a_route_v2_dry.py, production write 0). 결과 전부 PASS: (A) card 없으면 17노드 baseline과 byte-identical = OFF/no-card는 behavioral no-op, contract만 확장 / (C) synthetic reuse 쌍 horizontal 모순→render_new_plate 강등, depth-only→reuse 유지+diagnostic, occluded/low-conf 가드 / (B) real card depth instability 발견 → D5 final lock(§5). conf 분포 n=45 median 0.78, 임계 0.6→95% eligible.
  3. ③+④ 구현_self_card_base_bands(band prep, reuse 무관) + route_render_actions_v2(geometry + card withhold 통합, canonical render_action SOT) + validator + focused TDD (deterministic 영역만)
  4. ⑥ 3a mirror 구현mirror_plate_partition → PLATE_PARTITION_FIELDS(canonical render_action mirror, SOT 아님)
  5. ⑦ 3b 구현build_reference_dag + REFERENCE_DAG_FIELDS + render_order 검증
  6. step wiring — SCHEMA_VERSION 8→9 bump + manifest sync, default 경로 byte-identical 보존(card subsystem OFF → ④는 기존 route_render_actions와 동일 출력)
  7. C (#4) brief — 별도 문서, 3b ref_tree 입력
TDD 범위 한계 — 3a/3b deterministic partition/DAG 로직은 TDD 유효. 그러나 BG 이미지 일관성 완성도는 TDD로 못 올린다 — visual review + 실험 반복으로만. 기계적 PASS 카운트를 "일관성 검증"으로 보고하지 않는다.

§11 · 시나리오 leakage 없음 증명