# Spatial Continuity Enforcement — Design & Consultation Plan

> **Status:** CONSULTATION PHASE
> **Date:** 2026-03-08
> **Goal:** Automated spatial continuity enforcement in the prompt pipeline so generated frames maintain consistent screen direction, 180-degree rule compliance, and punch-in consistency across shots.

## Two Features

### Feature 1: Cross-Shot Spatial Orientation
- **180-degree rule**: Characters maintain consistent screen positions across cuts within a scene
- **Screen direction continuity**: Movement direction preserved across cuts
- **Shot/reverse-shot**: Camera sides alternate properly for dialogue coverage
- **Cross-episode continuity**: Cliffhanger resolution shots reference previous episode's final frame (not needed for new locations)

### Feature 2: Punch-In Consistency
- ECU/CU shots derived from wider shots should look like crops (matching lighting, angle, wardrobe, composition)
- Best approach TBD — Gemini consultation needed (text guidance vs reference image vs both)

### Feature 3: Shot Insertion (Board Grid)
- Add shots (SH02A) between existing shots
- Reorder shots via drag
- Handle downstream naming cascade (plans, board state, output paths)

## What Already Exists

### Spatial Data (per-shot, NOT cross-shot validated)
- `spatial_data.camera_side`: A/B tracking
- `spatial_data.screen_direction`: 5-value enum (L→R, R→L, center, toward, away)
- `spatial_data.character_relationships`: interaction type, dominant character, relative scale
- `asset_data.characters[].screen_position`: left/center/right/background

### Scene Locks Pipeline (implemented, untested on production data)
- `extract_scene_visual_dna()` in `lib/keyframe_context.py` — extracts lighting/palette/atmosphere from first keyframe
- Injection points in prompt_engine.py, keyframe_context.py, previz_context.py
- Feature flags: `enable_scene_visual_locks`, `enable_moodboard_to_text` (both enabled)
- Storage: `projects/{project}/state/starsend/scene_locks.json`

### Prompt Engine Spatial Injection (minimal)
- `camera_direction_guard` text injected based on screen_direction
- Two-character shots get LEFT SIDE / RIGHT SIDE framing
- Structured lighting per shot (motivator, direction, quality, color_temp)
- NO cross-shot comparison or adjacent-shot reference

### Backfill (basic)
- `tools/backfill_storyboard.py` — infers spatial_data from context
- Alternates camera_side A↔B for shot/reverse-shot
- Hardcodes two-char shots as left↔right
- No real continuity validation

## What's Missing
1. **No cross-shot spatial comparison** in prompt compilation
2. **No punch-in reference** (wider shot → ECU)
3. **No cross-episode frame reference** for cliffhanger resolutions
4. **No shot insertion** support in data architecture
5. **No spatial validation** in preflight checks

## Consultation Sequence

### Phase 1: Gemini Consultation (3 rounds)
**Engine:** Gemini 3.1 Pro (knows NBP internals)
**Focus:** Model-specific behavior questions

Questions for Gemini:
1. **Screen direction enforcement**: Does NBP respond to text instructions like "character faces screen-left" reliably? Or does it need a reference image showing the character in that position? What prompt patterns work best?
2. **Punch-in consistency**: For an ECU that should look like a crop from a wider shot — is it better to (a) pass the wider shot as a reference image, (b) describe the wider shot's composition in text, or (c) both? How does NBP handle "match the lighting and angle from this reference"?
3. **Reference image slot budget**: Scene locks + character refs + location moodboard already compete for image slots. If we add "adjacent shot hero frame" as another ref, how should we prioritize? Does NBP weight refs by position (recency bias)?
4. **Cross-episode continuity**: For cliffhanger resolutions, can we pass the previous episode's final frame as a reference? Will NBP maintain spatial consistency from a reference image alone, or do we need explicit text reinforcement?
5. **180-rule in practice**: When generating shot/reverse-shot pairs, what's the most reliable way to get NBP to flip the camera angle while maintaining character positions? Text instruction? Mirrored reference? Both?

### Phase 2: Opus Consultation (2-3 rounds)
**Engine:** Claude Opus 4.6
**Focus:** Architecture review + code-level implementation plan

Questions for Opus:
1. Review the proposed injection points — where exactly in the prompt pipeline should cross-shot spatial data be injected?
2. How should the adjacent-shot loader work? (load full scene shots, or just prev/next?)
3. Data architecture for shot insertion (SH02A naming, downstream cascade, migration)
4. Cross-episode frame reference storage and lookup
5. Code-level review of specific functions to modify

### Phase 3: Implementation
Based on consultation findings, implement:
- Cross-shot spatial injection in prompt_engine.py
- Punch-in reference (per Gemini's recommendation)
- Cross-episode continuity for cliffhanger resolutions
- Shot insertion in board grid (if scoped in)

### Phase 4: Validation
- Generate test previz with spatial enforcement active
- Verify 180-rule compliance across scene cuts
- Test punch-in consistency (ECU from wider shot)
- Test cross-episode continuity

### Phase 5: Gemini Bug-Stomp (2-3 rounds)
- Package implementation + test results
- Gemini reviews for model-specific issues
- Fix any behavioral problems

## Key Files

| File | Role |
|------|------|
| `lib/prompt_engine.py` | Prompt compilation — injection point for spatial data |
| `lib/previz_context.py` | Previz context assembly — 8-slot bundle |
| `lib/keyframe_context.py` | Scene locks extraction + keyframe context |
| `lib/jit_prompt.py` | JIT hydrator — token resolution |
| `lib/scene_planner.py` | Shot routing |
| `tools/backfill_storyboard.py` | Spatial data inference |
| `lib/preflight.py` | Pre-generation validation |
| `editors/tabs/board.js` | Board grid (shot insertion UI) |
| `editors/review_server.py` | API endpoints |
| `config/prompts/flash_scene_dna_v1.0.txt` | Scene DNA extraction prompt |
