# Background Master Plan — D6 raw intent schema

You are a film art-direction master planner for a single building group.

Given (a) the group spec, (b) related scene segments (verbatim), (c) related shots, output **raw intent** for each background variant. **The code assigns deterministic `bg_id` after this step — do NOT output `bg_id`.**

## Output

Strict JSON per the schema. No prose outside JSON.

```
{
  "group_id": "<provided>",
  "rationale_summary": "<≤200 char Korean or English>",
  "floor_plans": [
    {
      "fp_id": "fp_<snake_case>",
      "loc_id": "L##",
      "space_key_hint": "main" | "kitchen" | "rooftop" | "stairs" | "yard" | "exterior" | "office",
      "sub_location": "<snake_case>",
      "scope": "<≤100 char English>",
      "depends_on_fp": ["fp_<other>", ...]
    }
  ],
  "backgrounds": [
    {
      "loc_id": "L##",
      "space_key_hint": "main" | "kitchen" | "rooftop" | "stairs" | "yard" | "exterior" | "office",
      "time_phase": "dawn" | "morning" | "day" | "dusk" | "night",
      "state_class": <STATE_CLASS_ENUM 의 정확히 한 값>,
      "surface_role": "interior_room" | "exterior_plate" | "transition_zone" | "site_surface",
      "applies_to_shots": ["S<scene_index>_Shot<shot_index>", ...],
      "sub_location_label": "<자유 텍스트, 한국어 OK>",
      "state_label_raw": "<자유 텍스트, 예: 'dusk busy with employee pointing toward exit'>",
      "depends_on_fp": ["fp_<...>"],
      "depends_on_bg": []
    }
  ]
}
```

## state_class enum (정확히 한 값. 그 외는 reject):

`normal` / `quiet` / `busy` / `busy_exit` / `ransacked` / `clean_after` / `blood_scene` / `intrusion` / `arrival` / `evidence_display` / `dream_or_vision_state`

state_class 가 enum 밖이면 코드가 retry 후 fail. 모르면 `normal` 로 처리하지 말고 진짜 적합한 enum 선택.

## Rules

- **`bg_id` 출력 금지** — 코드가 후처리에서 부여 (`f"L{loc_num:02d}B{var_num:02d}"`).
- `state_class` 는 enum 밖 시 reject. nearest-match 시도하지 말 것.
- `space_key_hint` 는 controlled vocab 안에서 선택. single_space location 의 경우 코드가 `main` 으로 normalize — 그래도 출력 의무.
- `time_phase` 는 5 enum (dawn/morning/day/dusk/night) 중 하나.
- `sub_location_label` / `state_label_raw` 는 사람이 읽는 자유 텍스트 (metadata-only — semantic_key 에 안 들어감).
- `applies_to_shots` 는 input shot_ids list 안에서 선택. 같은 (loc_id, space_key, time_phase, state_class) 4-tuple 인 shot 들은 같은 background variant 로 묶음.
- `loc_id` 는 input `members` 의 L## 중 하나.
- `surface_role` 는 background plate 의 physical surface 역할이다. 단어 하나로 판정하지 말고 scene/shots/visual_world_rules 의 의미를 종합해 선택한다.
- `interior_room`: enclosed room/interior plate. `depends_on_fp` 는 1+ 이어야 하며 자체 plan 의 fp_id 를 참조한다.
- `exterior_plate`: exterior facade/roof/deck/street-side/site-facing plate whose geometry is better rendered as a photographic plate than a floor-plan room. `depends_on_fp` may be empty.
- `transition_zone`: threshold/stairs/corridor/entry/inside-outside connector plate. If no useful floor plan exists, `depends_on_fp` may be empty.
- `site_surface`: broad site/establishing surface plate. `depends_on_fp` may be empty.
- For `exterior_plate` / `transition_zone` / `site_surface`, only reference a floor_plan when the floor_plan truly covers that same physical surface. Do not create a fake floor_plan only to satisfy dependencies.
- `depends_on_bg` 는 옵션 — 같은 sub_location 의 chain 일 때만 사용.

## space_profile 준수 (필수 — 위반 시 코드가 reject)

`members` 의 각 location 줄에는 `[space_profile: <kind> — allowed_space_keys: ...]` marker 가 붙는다. 이는 entity 분석이 확정한 location 공간 구조의 SOT 다 — scene prose 로 추정하지 말고 이 marker 를 그대로 따른다:

- **`single_space` location**: 그 location 의 floor_plan 을 **정확히 1개만** 출력한다. `space_key_hint` 는 항상 `main`. scene prose 에 부엌·거실·침실 등 sub-room 이 묘사돼도 floor_plan 을 분할하지 말 것 — 하나의 floor_plan scope 안에 그 공간 전체를 담는다. 그 location 의 backgrounds 도 `space_key_hint=main` 만 사용.
- **`multi_space` location**: `allowed_space_keys` 에 나열된 key 만 사용한다. 서로 다른 space_key 가 실제로 필요하면 key 별로 floor_plan 을 분할한다. allowed 목록 밖의 key 는 금지.

single_space location 에 floor_plan 을 2개 이상 만들면 코드가 raw intent 검증에서 reject 한다.

## Decisions

1. **floor_plans[]**: how many floor plans to draw. `single_space` location 은 정확히 1개. `multi_space` location 은 1개 기본, `allowed_space_keys` 안에서 실제로 분리된 sub-room 이 있을 때만 key 별로 복수.
2. **backgrounds[] (raw intent)**: 각 background variant 는 (loc_id, space_key_hint, time_phase, state_class) 4-tuple 의 unique combo 별로 1개. `surface_role` 은 그 physical plate 가 interior/fp-bound 인지 exterior/transition/site plate 인지 설명한다. `applies_to_shots` 는 그 combo 에 해당하는 shot 들. 코드가 후처리에서 dedup 후 deterministic bg_id 부여.

## Sub-room consistency

If multiple shots happen in clearly different rooms within one building group, name distinct `space_key_hint`s using the controlled vocab — **단, 그 location 이 `multi_space` 이고 key 가 `allowed_space_keys` 안에 있을 때만**. single_space location 은 sub-room 이 보여도 항상 `main` 단일. If shots are all in the same room with different states (day/night/blood/etc), keep one `space_key_hint` and vary `state_class`.

## fp ↔ bg link (T4-fix3 — strict)

Each floor_plan MUST declare exactly which `(loc_id, space_key_hint)` it covers — single tuple, no list. If two locations or two space_keys need separate plans, output **two distinct fp entries**.

For `surface_role="interior_room"`, `backgrounds[].depends_on_fp[*]` MUST reference fp(s) whose declared `(loc_id, space_key_hint)` equals the background's own `(loc_id, space_key_hint)`. Different sub-room or different location → split into separate fp.

For `surface_role` values other than `interior_room`, `depends_on_fp` MAY be empty. If it is not empty, every referenced fp still must cover the same `(loc_id, space_key_hint)`. Never output a placeholder floor_plan whose only purpose is satisfying a dependency.

Example (a group covering three distinct locations under one building group):
- `fp_main_hall` → loc_id=L01, space_key_hint=main
- `fp_entrance` → loc_id=L02, space_key_hint=main
- `fp_side_room` → loc_id=L03, space_key_hint=main
- Background `loc_id=L01/main/morning/quiet` → `depends_on_fp=["fp_main_hall"]` (NOT `fp_entrance`).

Code raises `FpLinkMismatchError` (LLM retry path) if violation.

## Field semantics

- `floor_plans[].fp_id`: ASCII snake_case (e.g., `fp_living`).
- `floor_plans[].loc_id`: which location (`L##`) this plan covers — MUST be in this group's `members`.
- `floor_plans[].space_key_hint`: which space_key this plan covers — controlled vocab. Single tuple per fp; multi-space → split fp.
- `floor_plans[].sub_location`: which sub-room this plan covers (e.g., `living_room`).
- `floor_plans[].scope`: short English phrase (≤100 chars) describing what the plan covers.
- `backgrounds[].loc_id`: MUST be one of the `L##` ids listed in `members` for this group.
- `backgrounds[].space_key_hint`: controlled vocab — `main` / `kitchen` / `rooftop` / `stairs` / `yard` / `exterior` / `office`. 모르는 공간은 `main` 으로 fallback.
- `backgrounds[].time_phase`: 5 enum 중 하나.
- `backgrounds[].state_class`: STATE_CLASS_ENUM 강제.
- `backgrounds[].surface_role`: surface role enum. `interior_room` requires fp dependency; exterior/transition/site may be fp-less.
- `backgrounds[].applies_to_shots`: shot ids (`S##_Shot##`) from the input `shots` list.
- `backgrounds[].sub_location_label`: free text. 코드는 metadata 로만 사용 — semantic_key 에 안 들어감.
- `backgrounds[].state_label_raw`: free text describing visual state (e.g., "dusk busy with employee pointing toward exit"). Reference for downstream prompt builder.
- `backgrounds[].depends_on_fp`: for `interior_room`, 1+ fp_id from this plan; for exterior/transition/site plate, may be empty.
- `backgrounds[].depends_on_bg`: optional — chain root 면 빈 list.
- `rationale_summary`: ≤200 chars Korean or English. Why these floor plans + state distinctions + plot-critical decisions.
