# Previz Validation Gates & Context Re-Architecture — Implementation Plan

## Context

3-round Gemini 3.1 Pro consultation. Full transcript at `consultations/previz_validation_gates/`. Synthesis at `consultations/previz_validation_gates/SYNTHESIS.md`.

## Summary of Changes

Re-architect `previz_context.py` to use 8-slot ordering with split system instruction, shot-scoped bible, prop injection, shot-aware turnaround selection, and expression ref injection. Add two validation gates to `generate_previs.py`. Add structured rejection tag system to Production Console.

## The 8-Slot Context Architecture

```
[1] TEXT: Behavioral System Instruction (Primacy anchor)
    → Anti-spoiler rules, style constraints, DO NOT directives

[2] TEXT: Bible text (SHOT-SCOPED — only active characters/locations)

[3] TEXT: Shot sequence (N-1, current, N+1 — 3-shot window)

[4] IMAGES: Location moodboards (2-3 max)

[5] IMAGE: Prop reference (1 max, only if shot mentions prop)

[6] IMAGES: Character hero + 1 shot-aware turnaround (2 for previz)

[7] IMAGE: Expression ref (1, only if non-neutral emotion)

[8] TEXT: Generative Shot Directive (Recency anchor)
    → Camera angle, shot type, action, composition
    → [SHOT_TYPE_OVERRIDE] at absolute end
```

### Shot-Aware Turnaround Selection

Instead of always "hero + front", select the turnaround angle matching the shot's camera angle:

| Shot Context | Turnaround Angle |
|---|---|
| OTS — character is "shoulder" | `three_quarter_back` |
| OTS — character is "face" | `three_quarter` |
| Description: "behind", "back of", "walking away" | `back` |
| Description: "profile", "side view" | `side` |
| ECU/CU face shots, or default | `front` |

## Validation Gates

### Gate 1: Pre-Gen Text Critique (~$0.00005/call)
- Flash 3.1 text-only call validates `authored_prompt` BEFORE image gen
- Uses **Extractive CoT** (not evaluative) to dodge self-consistency bias:
  1. Extract characters from Shot Spec
  2. Extract characters from Authored Prompt
  3. List mismatches → FAIL if > 0
- Catches narrative spoilers, prompt bleed, wrong character inclusion

### Gate 2: Post-Gen VQA (~$0.0001/call)
- Flash Vision evaluates generated frame
- **CoT-forced**: describe image first, THEN grade 5 boolean checks:
  - `prop_present`, `prop_matches_description`
  - `camera_angle_matches_spec`
  - `character_matches_hero`
  - `unwanted_elements_present`
- Auto-triggers rejection on failure (no human needed)

### Relationship to Existing Keyframe Gates
Previz gates are **upstream filters** — they prevent wasting $0.134 on keyframes:
```
Previz Gate 1 (text):  prompt spoiler check       → $0.00005
Previz Gate 2 (VQA):   quick frame sanity check   → $0.0001
   ↓ approved previz advances
Keyframe Gate 1:       mechanical artifacts        → $0.001  (existing)
Keyframe Gate 2:       identity/wardrobe/comp      → $0.039  (existing)
   ↓ approved keyframe advances
Video Gate 3:          identity drift              → $0.117  (existing)
```

## 6 Structured Rejection Tags

| Tag | Automated Pipeline Action |
|-----|--------------------------|
| **Wrong Prop** | Inject prop ref at [5] + heavy weight recency text |
| **Narrative Spoiler** | Prompt for exclusion word → negative prompt at [1] |
| **Continuity Drift** | Inject approved N-1 frame at [4.5] as spatial ref |
| **Too Clean** | Lower temp 0.15 + texture modifiers at [8] |
| **Wrong Camera** | [SHOT_TYPE_OVERRIDE] at end of [8] |
| **Wrong Expression** | Inject expression ref at [7] + expression override text |

Store structured rejection log: `timestamp, episode_id, shot_id, tag_id, attempt_count`

## Implementation Order (7 Commits)

### Phase 1: Context Tools (Commits 1-2)

**Commit 1: `extract_mentioned_props()`**
- Add to `lib/previz_context.py`
- Scan shot `asset_data.props` for prop IDs
- Cross-reference against `casting_state.json` for available refs
- Return list of prop IDs that have reference images

**Commit 2: `scope_bible_to_shot()`**
- Add to `lib/previz_context.py`
- Takes full bible + shot's `asset_data` (characters, location_id)
- Returns filtered bible with only relevant character/location entries
- Prevents narrative bleed (the "dead body in SH02" root cause)

### Phase 2: Context Assembly (Commits 3-4)

**Commit 3: Refactor `build_previz_context()`**
- Split `build_system_instruction()` into:
  - `build_behavioral_preamble()` → slot [1] (anti-spoiler, style rules)
  - `build_generative_directive()` → slot [8] (camera, action, composition)
- Wire `scope_bible_to_shot()` for slot [2]
- Add prop injection at slot [5] using `extract_mentioned_props()` + `asset_manager.get_prop_ref()`
- Add `select_previz_turnaround()` — shot-aware angle selection for slot [6]
- Reduce turnarounds: hero + 1 angle (not full 5-angle set)
- Update docstring and content layout comment

**Commit 4: Wire expression ref injection**
- Add conditional expression ref at slot [7]
- Lookup from universal expression matrix based on `emotion_keyword` in shot's character data
- Only inject if emotion is non-neutral
- Expression AFTER character (prevents identity anchor on grayscale generic face)

### Phase 3: Validation Gates (Commits 5-6)

**Commit 5: Gate 1 — Pre-gen text critique**
- Add to `tools/generate_previs.py`, before image generation call
- Flash text-only call with extractive CoT prompt
- If FAIL, rewrite prompt and retry (1 attempt)
- Log gate results

**Commit 6: Gate 2 — Post-gen VQA**
- Add to `tools/generate_previs.py`, after image generation
- Flash Vision call with CoT-forced 5-check evaluation
- If critical check fails, auto-trigger rejection and regenerate
- Log gate results

### Phase 4: Rejection Loop (Commit 7)

**Commit 7: Rejection tag system**
- Backend: POST `/api/reject` endpoint with structured tags
- Frontend: Add tag UI to Dailies tab reject modal (`editors/tabs/dailies.js`)
- Analytics: Append rejection events to JSONL log
- Wire each tag to its modular prompt override action
- Wire N-1 frame injection for Continuity Drift tag (previz only)

## Key Files

| File | Changes |
|------|---------|
| `lib/previz_context.py` | Commits 1-4: new functions, refactored context assembly |
| `tools/generate_previs.py` | Commits 5-6: validation gates before/after generation |
| `editors/review_server.py` | Commit 7: rejection POST endpoint |
| `editors/tabs/dailies.js` | Commit 7: rejection tag UI |
| `prompts/flash_previz_v1.0.txt` | Commit 3: may need split into behavioral + generative |

## Working Directory

```
cd ~/Dropbox/CLAUDE_PROJECTS/starsend
```

## Verification

After all commits:
```bash
# Test previz generation with new context
python3 tools/generate_previs.py --project tartarus --episode 1 --shots 2 --dry-run

# Check gate logging
python3 tools/generate_previs.py --project tartarus --episode 1 --shots 2,5,7

# Run existing tests
python3 -m pytest tests/ -v
```
