# Automated Workflow V4 - One-Day Implementation Plan

## Goal

Transform the current multi-script microdrama V3 workflow into a **single orchestrator with task queue** that:
- Runs automatically until human feedback needed
- Manages dependencies and rebuilds downstream stages
- Logs all interactions for Claude context
- Can integrate into Telegram bot

**Target:** Working implementation in **one day** (8 hours)

---

## Current State (V3)

### Actual Stage Workflow (V3 - Corrected)

**Directory structure from current v3:**

```
Stage 0:  Initialize Take (00-init)
Stage 1:  Story (01-story)
Stage 2:  Understanding (02-understanding)               [REQUIRED]
Stage 3:  Script Generation (03-script)                  [REQUIRED]
Stage 4:  Assets Identification (04-assets)              [REQUIRED]
Stage 5:  Reference Search (05-references)               [OPTIONAL - Claude decides if skip, ask user approval]
Stage 6:  Characteristic Shot (06-characteristic)        [REQUIRED]
Stage 7:  Grid Generation (07-grids)                     [CRITICAL CHECKPOINT]
Stage 8:  Script Adaptation to Grid (08-script-adapted)
Stage 9:  Frame Extraction (09-frames)
Stage 10: Video Generation (10-videos)                   [Kling direct or Veo3 via APIs]
Stage 11: Caption Generation (11-captions)               [NEW - separate stage before assembly]
Stage 12: Assembly (12-assembly)
```

**API/Model Variations:**
- **Veo3**: laozhang (sloppy results, low resolution), pollo
- **Kling**: kling direct (JWT auth, working), pollo

User must be able to choose API/model for video generation.

### Stage Dependencies (V3 - Corrected)

```
00-init
    ↓
01-story
    ↓
02-understanding [REQUIRED]
    ↓
03-script [REQUIRED]
    ↓
04-assets [REQUIRED]
    ↓
05-references [OPTIONAL - skip only if Claude decides assets generic enough, ask user approval]
    |           [User can place own reference images in this dir]
    ↓
06-characteristic [REQUIRED]
    ↓
07-grids ← CRITICAL CHECKPOINT (character/scene definitions locked)
    ↓
08-script-adapted (analyzes grid, adapts script to actual frames)
    ↓
09-frames (extract 9 or 16 frames from grid)
    ↓
10-videos (generate video for each frame, Kling or Veo3)
    ↓
11-captions (generate caption overlay from script + video metadata)
    ↓
12-assembly (concat videos with captions)
```

**Parallel operations:**
- Stage 9: Extract all frames in parallel (9 or 16 extractions)
- Stage 10: Generate all videos in parallel (9 or 16 videos)

**Sequential feedback:**
- Each frame needs approval before moving to videos
- Each video needs approval before moving to assembly

**Reference Search Decision:**
After Stage 4 (assets), Claude analyzes if assets are generic enough to generate without references. If yes, prompt user for approval to skip Stage 5. User can always choose to add their own reference images.

### Current Scripts (V3)

```
scripts/
├── init_take_v3.py
├── run_stage_v3.py                    # Incomplete wrapper
├── generate_understanding_v3.py       # Stage 1
├── generate_script_v3.py              # Stage 2
├── generate_assets_v3.py              # Stage 3
├── search_references_v3.py            # Stage 4
├── generate_characteristic_image_v3.py # Stage 5
├── generate_grid_image_v3.py          # Stage 6
├── adapt_script_to_grid_v3.py         # Stage 7
├── extract_frames_v3.py               # Stage 8
├── generate_videos_kling_direct.py    # Stage 9
├── generate_captions_v3.py            # Stage 10
├── assemble_video_v3_kling.py         # Stage 11
├── assemble_video_v3.py               # OLD (Veo3, deprecated)
├── generate_videos_v3.py              # OLD (Veo3, deprecated)
├── generate_scene_frame_v3.py         # OLD (unused)
└── lib/
    ├── __init__.py
    ├── path_utils_v3.py
    ├── logging_utils_v3.py
    ├── metadata_utils_v3.py
    └── kling_api.py
```

---

## Problems to Solve

1. **Manual orchestration** - User runs each script manually
2. **No dependency tracking** - Regenerating frame doesn't auto-rebuild video
3. **Feedback not persistent** - Passed as CLI args, not stored in context
4. **Console output instead of logs** - No verbose generation.log
5. **No batch operations** - Can't approve 9 frames sequentially
6. **Scripts cluttered** - Mix of v2, v3, unused scripts
7. **No parent iteration tracking** - metadata.json doesn't record which iteration it was based on

## Important Requirements

**CRITICAL:** All iterations, generated files, videos, and logs must be preserved permanently. This is required for future system improvement. What appears as "file chaos" is intentional data collection.

**Iteration Tracking:** Every metadata.json must include `parent_iteration` field tracking which iteration it was based on. Current symlink auto-updates to new iteration, but user can manually revert to any previous iteration.

**Stage Requirements:**
- **REQUIRED stages** (cannot skip): 02-understanding, 04-assets, 06-characteristic
- **OPTIONAL stages** (only 05-references): Skip only if Claude determines assets are generic enough, then ask user for approval
- User can place own reference images in 05-references/ directory

**API/Model Flexibility:** Support multiple video generation backends:
- Kling direct (JWT auth) - currently working
- Kling via Pollo proxy
- Veo3 via laozhang (sloppy results, low-res)
- Veo3 via Pollo

---

## V4 Architecture (Simplified)

### Single Orchestrator

**File:** `scripts/orchestrator_v4.py`

```bash
# Start new story
python orchestrator_v4.py "my-story" --init

# Provide story and run until feedback needed
python orchestrator_v4.py "my-story" --feedback "A firefighter..." --run

# Continue after feedback
python orchestrator_v4.py "my-story" --feedback "approve" --run

# Regenerate specific stage
python orchestrator_v4.py "my-story" --stage 06-grids --feedback "make it darker" --run
```

### Task Queue (JSON)

**File:** `{take_path}/task_queue.json`

Simple JSON array (not JSONL, for easy editing):

```json
[
  {
    "id": 1,
    "stage": "00-init",
    "action": "init_story",
    "status": "completed",
    "result": "initialized",
    "timestamp": "2026-02-03T12:00:00Z"
  },
  {
    "id": 2,
    "stage": "03-script",
    "action": "generate",
    "status": "completed",
    "result": "001/script.json",
    "iteration": "001",
    "parent_iteration": null,
    "timestamp": "2026-02-03T12:05:00Z"
  },
  {
    "id": 3,
    "stage": "07-grids",
    "action": "generate",
    "status": "completed",
    "result": "002/grid.jpg",
    "iteration": "002",
    "parent_iteration": "001",
    "timestamp": "2026-02-03T12:10:00Z"
  },
  {
    "id": 4,
    "stage": "07-grids",
    "action": "await_feedback",
    "status": "waiting",
    "message": "CRITICAL: Review grid before proceeding (defines all characters)",
    "output": "07-grids/002/grid.jpg"
  },
  {
    "id": 5,
    "stage": "09-frames",
    "action": "extract_batch",
    "frame_count": 9,
    "status": "pending",
    "deps": [4]
  },
  {
    "id": 6,
    "stage": "09-frames",
    "action": "await_feedback",
    "frame_num": 1,
    "status": "pending",
    "deps": [5]
  },
  ...
  {
    "id": 14,
    "stage": "09-frames",
    "action": "await_feedback",
    "frame_num": 9,
    "status": "pending"
  },
  {
    "id": 15,
    "stage": "10-videos",
    "action": "generate_batch",
    "video_count": 9,
    "api": "kling-direct",
    "status": "pending",
    "deps": [6, 7, 8, 9, 10, 11, 12, 13, 14]
  }
]
```

**Parent Iteration Tracking:**
- Each task that creates an iteration records `parent_iteration` field
- If iteration 003 regenerated from iteration 002, metadata shows: `"parent_iteration": "002"`
- Allows tracing lineage of all generations
- Critical for understanding what feedback led to what changes

**Why JSON, not JSONL?**
- Easy to edit by hand if needed
- Can load entire queue at once (not huge, ~50 tasks max)
- Can use standard JSON tools

**Why not SQL?**
- Overkill for 50 tasks
- Want human-readable format
- Easy debugging (cat task_queue.json | jq)

### Feedback Log (JSONL)

**File:** `{take_path}/feedback.jsonl`

Append-only log of all user feedback:

```json
{"timestamp": "2026-02-03T12:15:00Z", "stage": "06-grids", "iteration": "001", "feedback_type": "regenerate", "text": "Make it darker, more ominous", "action": "create_iteration_002"}
{"timestamp": "2026-02-03T12:20:00Z", "stage": "08-frames", "frame_num": 3, "iteration": "001", "feedback_type": "edit", "text": "Character too young", "action": "nanobanana_edit_002"}
{"timestamp": "2026-02-03T12:30:00Z", "stage": "09-videos", "scene_num": 5, "iteration": "002", "feedback_type": "approve", "action": "continue"}
```

**Why JSONL here?**
- Append-only (never modified)
- Grows over time (may have 100+ entries)
- Easy to tail/grep

### Verbose Log (TEXT)

**File:** `{take_path}/generation.log`

Full verbose text log (like old microdrama pipeline):

```
2026-02-03 12:00:00 [INFO] Orchestrator started: story=truck-stop-revenge, take=003
2026-02-03 12:00:01 [INFO] Loading task queue: task_queue.json
2026-02-03 12:00:01 [INFO] Found 25 tasks: 3 completed, 1 waiting, 21 pending
2026-02-03 12:00:02 [DEBUG] Next task: id=4, stage=06-grids, action=await_feedback
2026-02-03 12:00:02 [INFO] WAITING FOR FEEDBACK: Review grid at 06-grids/001/grid.jpg
2026-02-03 12:00:02 [INFO] Run with --feedback "..." to continue
```

**Purpose:**
- Human-readable debugging log
- Includes API requests/responses (truncated)
- Timing information
- File operations

---

## JSON Querying Tools

### jq (Recommended)

**Install:**
```bash
sudo apt-get install jq
```

**Query examples:**
```bash
# Get all pending tasks
cat task_queue.json | jq '.[] | select(.status=="pending")'

# Get all feedback for stage 09-videos
cat feedback.jsonl | jq '. | select(.stage=="09-videos")'

# Count completed tasks
cat task_queue.json | jq '[.[] | select(.status=="completed")] | length'

# Get next waiting task
cat task_queue.json | jq '.[] | select(.status=="waiting") | .id' | head -1
```

### dasel (Alternative)

Easier syntax than jq:

```bash
# Install
sudo apt-get install dasel

# Query examples
dasel -f task_queue.json -r json '.[] | select(.status==pending)'
```

### Python (For Complex Logic)

```python
import json

# Load queue
with open('task_queue.json') as f:
    queue = json.load(f)

# Get next pending task with all deps completed
def get_next_task(queue):
    for task in queue:
        if task['status'] != 'pending':
            continue

        # Check dependencies
        deps_complete = True
        for dep_id in task.get('deps', []):
            dep_task = next(t for t in queue if t['id'] == dep_id)
            if dep_task['status'] != 'completed':
                deps_complete = False
                break

        if deps_complete:
            return task
    return None
```

**Update queue:**
```python
# Mark task completed
task['status'] = 'completed'
task['result'] = 'path/to/output'

# Save back
with open('task_queue.json', 'w') as f:
    json.dump(queue, f, indent=2)
```

---

## Iteration Preservation Strategy

**CRITICAL REQUIREMENT:** All iterations, generated files, videos, and logs must be preserved permanently.

```
/mnt/video/microdrama-v3/{story-name}/{take}/10-videos/scene_01/
├── 001/           # KEEP - First attempt
│   ├── video.mp4
│   ├── metadata.json  {"iteration": "001", "parent_iteration": null}
│   └── prompt.txt
├── 002/           # KEEP - Regenerated with feedback
│   ├── video.mp4
│   ├── metadata.json  {"iteration": "002", "parent_iteration": "001"}
│   └── prompt.txt
├── 003/           # KEEP - Another attempt
│   ├── video.mp4
│   ├── metadata.json  {"iteration": "003", "parent_iteration": "001"}
│   └── prompt.txt
├── 004/           # KEEP - Current iteration
│   ├── video.mp4
│   ├── metadata.json  {"iteration": "004", "parent_iteration": "003"}
│   └── prompt.txt
└── current → 004  # Symlink to active iteration (user can change manually)
```

**Why keep all iterations:**
- Future system improvement requires complete generation history
- Analysis of what prompts/feedback led to better outputs
- Training data for future models
- User can manually revert to any previous iteration

**Current symlink behavior:**
- Orchestrator auto-updates `current → new_iteration` when creating new iteration
- User can manually change: `ln -sfn 002 current` to use iteration 002
- All downstream operations use `current` symlink, so changing it affects regeneration

**No cleanup scripts:** Do NOT create cleanup/archiving scripts for iterations. Keep everything.

## Script Cleanup Strategy

### Option 1: Archive Old Scripts (Recommended)

Keep scripts dir clean for v4:

```bash
# Create archive
mkdir -p scripts/archive/{v2,v3,unused}

# Move old versions
mv scripts/*_v2.py scripts/archive/v2/
mv scripts/assemble_video_v3.py scripts/archive/v3/  # Old Veo3 version
mv scripts/generate_videos_v3.py scripts/archive/v3/  # Old Veo3 version
mv scripts/generate_scene_frame_v3.py scripts/archive/unused/

# Keep only v4 scripts
scripts/
├── orchestrator_v4.py           # NEW - Master orchestrator
├── init_take_v4.py              # Cleaned up from v3
├── stages/                      # NEW - Stage functions
│   ├── __init__.py
│   ├── stage_02_understanding.py
│   ├── stage_03_script.py
│   ├── stage_04_assets.py
│   ├── stage_05_references.py
│   ├── stage_06_characteristic.py
│   ├── stage_07_grids.py
│   ├── stage_08_script_adapt.py
│   ├── stage_09_frames.py
│   ├── stage_10_videos.py
│   ├── stage_11_captions.py
│   └── stage_12_assembly.py
├── lib/
│   ├── __init__.py
│   ├── paths.py                 # Path utilities
│   ├── logging.py               # Logging utilities
│   ├── metadata.py              # Metadata utilities
│   ├── queue.py                 # NEW - Task queue operations
│   ├── feedback.py              # NEW - Feedback processing
│   └── kling_api.py             # Kling API client
└── archive/                     # OLD scripts
    ├── v2/
    ├── v3/
    └── unused/
```

### Option 2: New Clean Directory

Start fresh:

```bash
# Create new clean directory
mkdir -p scripts-v4/{stages,lib}

# Copy only what's needed
cp scripts/lib/{path_utils_v3,logging_utils_v3,metadata_utils_v3,kling_api}.py scripts-v4/lib/

# Refactor and clean up naming
mv scripts-v4/lib/path_utils_v3.py scripts-v4/lib/paths.py
mv scripts-v4/lib/logging_utils_v3.py scripts-v4/lib/logging.py
mv scripts-v4/lib/metadata_utils_v3.py scripts-v4/lib/metadata.py

# Keep old scripts/ as archive
mv scripts scripts-v3-archive
mv scripts-v4 scripts
```

**Recommendation:** Option 1 (archive subdirectory) - cleaner, preserves history

---

## One-Day Implementation Plan

### Hour 1-2: Core Infrastructure

**Goal:** Basic orchestrator with queue loading/saving

**Tasks:**
1. Create `scripts/lib/queue.py`:
   - `load_queue(take_path) -> list`
   - `save_queue(take_path, queue)`
   - `get_next_task(queue) -> task`
   - `mark_completed(queue, task_id, result)`
   - `mark_waiting(queue, task_id, message)`
   - `add_task(queue, task)`

2. Create `scripts/lib/feedback.py`:
   - `append_feedback(take_path, stage, feedback_type, text)`
   - `parse_feedback(text) -> (type, text)`  # approve/regenerate/edit/skip

3. Create `scripts/lib/logging.py` (extend existing):
   - `setup_generation_log(take_path)` → creates generation.log with handler
   - `log_task_start(task)`
   - `log_task_complete(task, result)`
   - `log_feedback_request(task, message)`

4. Create `scripts/orchestrator_v4.py` skeleton:
   - Parse CLI args (--init, --feedback, --run, --stage)
   - Load task queue
   - Main loop: get next task, check if waiting, exit or execute

**Deliverable:** Can load queue, iterate tasks, save queue

### Hour 3-4: Task Execution

**Goal:** Execute stages by calling existing v3 scripts

**Tasks:**
1. Create `scripts/lib/executor.py`:
   ```python
   def execute_task(task: dict, story_name: str, take: str) -> str:
       """Execute task and return result path."""

       if task['stage'] == '02-script':
           result = subprocess.run([
               sys.executable,
               'scripts/generate_script_v3.py',
               story_name,
               '--take', take,
               '--feedback', task.get('feedback', '')
           ], capture_output=True)
           return f"02-script/{get_iteration()}/script.json"

       # ... similar for other stages
   ```

2. Wrap each v3 script call
3. Handle success/failure
4. Return result path

**Deliverable:** Can execute any stage

### Hour 5-6: Feedback Loop

**Goal:** Stop at feedback points, resume with feedback

**Tasks:**
1. Implement feedback checkpoints:
   - After each stage completion, add `await_feedback` task
   - Orchestrator stops when hitting `await_feedback`
   - User provides feedback via `--feedback`

2. Implement feedback processing:
   - Parse feedback type (approve/regenerate/etc.)
   - If approve: mark completed, continue
   - If regenerate: add new generation task with feedback, mark dependents pending
   - If edit: (for frames) add NanoBanana edit task

3. Test feedback loop with simple stages (understanding, script)

**Deliverable:** Feedback loop working for simple stages

### Hour 7: Batch Operations & Dependencies

**Goal:** Handle 9 frames and 9 videos with dependencies

**Tasks:**
1. Implement frame batch extraction:
   - Single task: `extract_batch` → extracts all 9 frames
   - Creates 9 `await_feedback` tasks (one per frame)
   - Orchestrator stops at each frame for approval

2. Implement video batch generation:
   - Waits for all 9 frame approvals
   - Single task: `generate_batch` → generates all 9 videos in parallel
   - Creates 9 `await_feedback` tasks (one per video)

3. Implement dependency rebuilding:
   - When frame 3 regenerated → mark video 3 and assembly as pending
   - When any video regenerated → mark assembly as pending

**Deliverable:** Batch frame/video operations working

### Hour 8: Testing & Documentation

**Goal:** End-to-end test with real story

**Tasks:**
1. Archive old scripts:
   ```bash
   mkdir -p scripts/archive/{v3,unused}
   mv scripts/*old* scripts/archive/
   ```

2. Run complete generation:
   ```bash
   python orchestrator_v4.py "test-story" --init
   python orchestrator_v4.py "test-story" --feedback "Short joke: A firefighter..." --run
   # Approve understanding
   python orchestrator_v4.py "test-story" --feedback "approve" --run
   # Approve script
   python orchestrator_v4.py "test-story" --feedback "approve" --run
   # ... continue through all stages
   ```

3. Test regeneration:
   ```bash
   # Regenerate grid with feedback
   python orchestrator_v4.py "test-story" --stage 06-grids --feedback "darker atmosphere" --run
   ```

4. Write README for orchestrator
5. Update CLAUDE.md with v4 workflow

**Deliverable:** Working end-to-end generation + docs

---

## Task Queue Initialization

When starting new story, orchestrator creates complete task queue:

```python
def initialize_task_queue(story_name: str, take: str, scene_count: int = 9, skip_references: bool = False) -> list:
    """Create task queue for new generation.

    Args:
        story_name: Story name
        take: Take number
        scene_count: Number of scenes (9 or 16)
        skip_references: Whether to skip reference search (Claude decision + user approval)
    """

    tasks = []
    task_id = 1

    # Stage 0: Init
    tasks.append({
        "id": task_id,
        "stage": "00-init",
        "action": "init_story",
        "status": "pending"
    })
    task_id += 1

    # Stage 1: Story
    tasks.append({
        "id": task_id,
        "stage": "01-story",
        "action": "await_feedback",
        "status": "pending",
        "deps": [task_id - 1],
        "message": "Provide story text"
    })
    story_feedback_id = task_id
    task_id += 1

    # Stage 2: Understanding [REQUIRED]
    tasks.append({
        "id": task_id,
        "stage": "02-understanding",
        "action": "generate",
        "status": "pending",
        "deps": [story_feedback_id]
    })
    task_id += 1

    tasks.append({
        "id": task_id,
        "stage": "02-understanding",
        "action": "await_feedback",
        "status": "pending",
        "deps": [task_id - 1],
        "message": "Review understanding.md"
    })
    understanding_feedback_id = task_id
    task_id += 1

    # Stage 3: Script [REQUIRED]
    tasks.append({
        "id": task_id,
        "stage": "03-script",
        "action": "generate",
        "status": "pending",
        "deps": [understanding_feedback_id]
    })
    task_id += 1

    tasks.append({
        "id": task_id,
        "stage": "03-script",
        "action": "await_feedback",
        "status": "pending",
        "deps": [task_id - 1],
        "message": "Review script.json"
    })
    script_feedback_id = task_id
    task_id += 1

    # Stage 4: Assets [REQUIRED]
    tasks.append({
        "id": task_id,
        "stage": "04-assets",
        "action": "generate",
        "status": "pending",
        "deps": [script_feedback_id]
    })
    task_id += 1

    tasks.append({
        "id": task_id,
        "stage": "04-assets",
        "action": "await_feedback",
        "status": "pending",
        "deps": [task_id - 1],
        "message": "Review assets.json"
    })
    assets_feedback_id = task_id
    task_id += 1

    # Stage 5: References [OPTIONAL - skip if Claude decides + user approves]
    if not skip_references:
        tasks.append({
            "id": task_id,
            "stage": "05-references",
            "action": "check_skip",
            "status": "pending",
            "deps": [assets_feedback_id],
            "message": "Claude: Are assets generic enough to skip reference search?"
        })
        task_id += 1

        tasks.append({
            "id": task_id,
            "stage": "05-references",
            "action": "search_or_skip",
            "status": "pending",
            "deps": [task_id - 1]
        })
        references_complete_id = task_id
        task_id += 1
    else:
        references_complete_id = assets_feedback_id

    # Stage 6: Characteristic [REQUIRED]
    tasks.append({
        "id": task_id,
        "stage": "06-characteristic",
        "action": "generate",
        "status": "pending",
        "deps": [references_complete_id]
    })
    task_id += 1

    tasks.append({
        "id": task_id,
        "stage": "06-characteristic",
        "action": "await_feedback",
        "status": "pending",
        "deps": [task_id - 1],
        "message": "Review characteristic shot"
    })
    characteristic_feedback_id = task_id
    task_id += 1

    # Stage 7: Grid (CRITICAL CHECKPOINT)
    tasks.append({
        "id": task_id,
        "stage": "07-grids",
        "action": "generate",
        "status": "pending",
        "deps": [characteristic_feedback_id]
    })
    task_id += 1

    tasks.append({
        "id": task_id,
        "stage": "07-grids",
        "action": "await_feedback",
        "status": "pending",
        "deps": [task_id - 1],
        "message": "CRITICAL: Review grid before proceeding (defines all characters, cannot change after)"
    })
    grid_feedback_id = task_id
    task_id += 1

    # Stage 8: Script adaptation
    tasks.append({
        "id": task_id,
        "stage": "08-script-adapted",
        "action": "generate",
        "status": "pending",
        "deps": [grid_feedback_id]
    })
    script_adapted_id = task_id
    task_id += 1

    # Stage 9: Frame extraction (batch)
    tasks.append({
        "id": task_id,
        "stage": "09-frames",
        "action": "extract_batch",
        "frame_count": scene_count,
        "status": "pending",
        "deps": [script_adapted_id]
    })
    frame_batch_id = task_id
    task_id += 1

    # Stage 9: Frame feedback (9 tasks, one per frame)
    frame_feedback_ids = []
    for i in range(scene_count):
        tasks.append({
            "id": task_id,
            "stage": "09-frames",
            "action": "await_feedback",
            "frame_num": i + 1,
            "status": "pending",
            "deps": [frame_batch_id],
            "message": f"Review frame {i+1}/{scene_count}"
        })
        frame_feedback_ids.append(task_id)
        task_id += 1

    # Stage 10: Video generation (batch, depends on all frame approvals)
    tasks.append({
        "id": task_id,
        "stage": "10-videos",
        "action": "generate_batch",
        "video_count": scene_count,
        "api": "kling-direct",  # or "kling-pollo", "veo3-laozhang", "veo3-pollo"
        "status": "pending",
        "deps": frame_feedback_ids
    })
    video_batch_id = task_id
    task_id += 1

    # Stage 10: Video feedback (9 tasks, one per video)
    video_feedback_ids = []
    for i in range(scene_count):
        tasks.append({
            "id": task_id,
            "stage": "10-videos",
            "action": "await_feedback",
            "scene_num": i + 1,
            "status": "pending",
            "deps": [video_batch_id],
            "message": f"Review video {i+1}/{scene_count}"
        })
        video_feedback_ids.append(task_id)
        task_id += 1

    # Stage 11: Captions
    tasks.append({
        "id": task_id,
        "stage": "11-captions",
        "action": "generate",
        "status": "pending",
        "deps": video_feedback_ids
    })
    captions_id = task_id
    task_id += 1

    # Stage 12: Assembly
    tasks.append({
        "id": task_id,
        "stage": "12-assembly",
        "action": "assemble",
        "status": "pending",
        "deps": [captions_id]
    })
    task_id += 1

    tasks.append({
        "id": task_id,
        "stage": "12-assembly",
        "action": "await_feedback",
        "status": "pending",
        "deps": [task_id - 1],
        "message": "Review final video"
    })

    return tasks
```

For 9 scenes: **~30 tasks total**

---

## Dependency Rebuilding

When user regenerates a stage:

```python
def mark_dependents_pending(queue: list, stage: str) -> None:
    """Mark all dependent stages as pending."""

    # Stage dependency map (V3 corrected)
    DEPENDENTS = {
        "01-story": ["02-understanding", "03-script", "04-assets", "05-references", "06-characteristic",
                     "07-grids", "08-script-adapted", "09-frames", "10-videos", "11-captions", "12-assembly"],
        "02-understanding": ["03-script", "04-assets", "05-references", "06-characteristic",
                            "07-grids", "08-script-adapted", "09-frames", "10-videos", "11-captions", "12-assembly"],
        "03-script": ["04-assets", "05-references", "06-characteristic", "07-grids",
                      "08-script-adapted", "09-frames", "10-videos", "11-captions", "12-assembly"],
        "04-assets": ["05-references", "06-characteristic", "07-grids",
                      "08-script-adapted", "09-frames", "10-videos", "11-captions", "12-assembly"],
        "05-references": ["06-characteristic", "07-grids", "08-script-adapted",
                          "09-frames", "10-videos", "11-captions", "12-assembly"],
        "06-characteristic": ["07-grids", "08-script-adapted", "09-frames", "10-videos", "11-captions", "12-assembly"],
        "07-grids": ["08-script-adapted", "09-frames", "10-videos", "11-captions", "12-assembly"],
        "08-script-adapted": ["09-frames", "10-videos", "11-captions", "12-assembly"],
        "09-frames": ["10-videos", "12-assembly"],  # Note: not captions (captions read script, not frames)
        "10-videos": ["11-captions", "12-assembly"],
        "11-captions": ["12-assembly"]
    }

    dependent_stages = DEPENDENTS.get(stage, [])

    for task in queue:
        if task['stage'] in dependent_stages and task['status'] == 'completed':
            task['status'] = 'pending'
            task['result'] = None
            # Clear parent_iteration on regenerated tasks
            if 'parent_iteration' in task:
                task['parent_iteration'] = None
```

**Example:** User regenerates frame 3:
- Frame 3 task marked pending
- Video 3 tasks (generate + await_feedback) marked pending
- Assembly tasks (generate + await_feedback) marked pending
- BUT: Frames 1-2, 4-9 stay completed
- BUT: Videos 1-2, 4-9 stay completed

---

## Critical Checkpoint: Grid

**Grid approval is the point of no return:**

- **Before grid:** Can iterate freely on script, assets, etc.
- **After grid:** Grid defines character appearance and is immutable for this take
  - Frame changes → edit with NanoBanana (new frame iteration)
  - Video changes → regenerate with Kling (new video iteration)
  - **Grid changes → NEW TAKE REQUIRED**

**Why:**
- Grid defines character consistency
- All frames are extracted from grid
- Changing grid invalidates ALL frames

**Implementation:**
```python
def handle_grid_regeneration_request(story_name: str, take: str) -> None:
    """User wants to regenerate grid after frames exist."""

    # Check if frames already generated
    frames_exist = Path(f"/mnt/video/microdrama-v3/{story_name}/{take}/09-frames").exists()

    if frames_exist:
        print("ERROR: Cannot regenerate grid after frames generated")
        print("Grid defines character appearance and is locked after frame extraction")
        print("Options:")
        print("  1. Edit individual frames with NanoBanana (--stage 09-frames --frame N --feedback '...')")
        print("  2. Start new take with grid feedback (--new-take --feedback 'grid changes...')")
        sys.exit(1)
```

---

## Telegram Bot Integration

Simple integration:

```python
# In telegram_bot.py

import subprocess
import sys

async def handle_story_generation(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """User starts video generation."""

    story_text = update.message.text
    story_name = generate_story_slug(story_text)
    chat_id = update.effective_chat.id

    # Save story text to temp file
    story_file = f"/tmp/story_{chat_id}.txt"
    with open(story_file, 'w') as f:
        f.write(story_text)

    # Start orchestrator in background
    process = subprocess.Popen([
        sys.executable,
        'scripts/orchestrator_v4.py',
        story_name,
        '--init',
        '--feedback', story_text,
        '--run',
        '--bot-mode',
        '--chat-id', str(chat_id)
    ])

    context.user_data['orchestrator_pid'] = process.pid
    context.user_data['story_name'] = story_name

    await update.message.reply_text(
        "🎬 Starting video generation...\n"
        "I'll show you outputs at each stage for approval."
    )

async def handle_feedback(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """User provides feedback."""

    feedback_text = update.message.text
    story_name = context.user_data.get('story_name')
    chat_id = update.effective_chat.id

    # Resume orchestrator with feedback
    process = subprocess.Popen([
        sys.executable,
        'scripts/orchestrator_v4.py',
        story_name,
        '--feedback', feedback_text,
        '--run',
        '--bot-mode',
        '--chat-id', str(chat_id)
    ])

    await update.message.reply_text("✅ Processing...")
```

**Orchestrator bot mode:**
```python
if args.bot_mode:
    # Send output to Telegram instead of console
    send_telegram_image(args.chat_id, output_path, caption="Review this grid")

    # Exit and wait for user to call again with feedback
    sys.exit(0)
```

---

## Simplified Workflow Example

```bash
# 1. User provides story
python orchestrator_v4.py "fire-truck" --init --feedback "A firefighter..." --run

# Output:
# Task 1: init_story → completed
# Task 2: generate script → completed
# Task 3: await_feedback → WAITING
#
# Review: /mnt/video/microdrama-v3/fire-truck/001/03-script/001/script.json
#
# Provide feedback:
#   - Approve: --feedback "approve"
#   - Regenerate: --feedback "make it darker"

# 2. User approves script
python orchestrator_v4.py "fire-truck" --feedback "approve" --run

# Output:
# Task 4: generate grid → completed
# Task 5: await_feedback → WAITING
#
# CRITICAL CHECKPOINT: Review grid before proceeding
# Grid defines all character appearances and cannot be changed after frames are extracted
#
# Review: /mnt/video/microdrama-v3/fire-truck/001/07-grids/001/grid.jpg

# 3. User approves grid
python orchestrator_v4.py "fire-truck" --feedback "approve" --run

# Output:
# Task 6: adapt script to grid → completed
# Task 7: extract 9 frames → completed
# Task 8: await_feedback (frame 1) → WAITING
#
# Review: /mnt/video/microdrama-v3/fire-truck/001/09-frames/scene_01/001/frame.jpg

# 4. User approves frame 1
python orchestrator_v4.py "fire-truck" --feedback "approve" --run

# Output:
# Task 9: await_feedback (frame 2) → WAITING
#
# Review: /mnt/video/microdrama-v3/fire-truck/001/09-frames/scene_02/001/frame.jpg

# ... continue approving all 9 frames ...

# 5. After all frames approved
python orchestrator_v4.py "fire-truck" --feedback "approve" --run

# Output:
# Task 17: generate 9 videos (batch) → running (may take 5-10 minutes)
# Task 17: generate 9 videos (batch) → completed
# Task 18: await_feedback (video 1) → WAITING
#
# Review: /mnt/video/microdrama-v3/fire-truck/001/10-videos/scene_01/001/video.mp4

# ... continue approving all 9 videos ...

# 6. Final assembly
python orchestrator_v4.py "fire-truck" --feedback "approve" --run

# Output:
# Task 27: generate captions → completed
# Task 28: assemble final video → completed
# Task 29: await_feedback (final) → WAITING
#
# Review: /mnt/video/microdrama-v3/fire-truck/001/12-assembly/001/final.mp4
# Duration: 52.3s
# Size: 38.5MB

# 7. Done!
python orchestrator_v4.py "fire-truck" --feedback "approve"

# Output:
# ✅ Generation complete!
# Final video: /mnt/video/microdrama-v3/fire-truck/001/12-assembly/001/final.mp4
```

---

## File Structure After V4

```
/home/videobot/dev/microdrama-v2/
├── scripts/
│   ├── orchestrator_v4.py       # NEW - Single entry point
│   ├── init_take_v4.py          # Refactored from v3
│   ├── stages/                  # NEW - Stage execution modules
│   │   ├── __init__.py
│   │   ├── stage_01_story.py
│   │   ├── stage_02_understanding.py
│   │   ├── stage_03_script.py
│   │   ├── stage_04_assets.py
│   │   ├── stage_05_references.py
│   │   ├── stage_06_characteristic.py
│   │   ├── stage_07_grids.py
│   │   ├── stage_08_script_adapt.py
│   │   ├── stage_09_frames.py
│   │   ├── stage_10_videos.py     # Support kling-direct, kling-pollo, veo3-laozhang, veo3-pollo
│   │   ├── stage_11_captions.py
│   │   └── stage_12_assembly.py
│   ├── lib/
│   │   ├── __init__.py
│   │   ├── paths.py             # Renamed from path_utils_v3
│   │   ├── logging.py           # Extended from logging_utils_v3
│   │   ├── metadata.py          # Renamed from metadata_utils_v3, add parent_iteration field
│   │   ├── queue.py             # NEW - Task queue operations
│   │   ├── feedback.py          # NEW - Feedback processing
│   │   ├── executor.py          # NEW - Task execution
│   │   └── kling_api.py         # Kept from v3
│   └── archive/                 # OLD scripts
│       ├── v3/
│       │   ├── assemble_video_v3.py       # Old Veo3 version
│       │   ├── generate_videos_v3.py      # Old Veo3 version
│       │   └── ...
│       └── unused/
│           └── generate_scene_frame_v3.py
└── docs/
    └── plans/
        └── v4-automated-workflow.md  # This plan

/mnt/video/microdrama-v3/{story-name}/{take}/
├── task_queue.json              # NEW - Task queue (editable JSON)
├── feedback.jsonl               # NEW - Feedback log (append-only)
├── generation.log               # NEW - Verbose text log
├── pipeline.log                 # Existing - JSONL structured log
├── session.txt                  # Existing - Claude session ID
├── 01-story/
├── 02-understanding/            # REQUIRED stage
├── 03-script/
├── 04-assets/                   # REQUIRED stage
├── 05-references/               # OPTIONAL - can be skipped with approval
├── 06-characteristic/           # REQUIRED stage
├── 07-grids/                    # CRITICAL CHECKPOINT
├── 08-script-adapted/
├── 09-frames/
├── 10-videos/
├── 11-captions/                 # NEW - separate stage before assembly
└── 12-assembly/
```

---

## Testing Checklist

After implementation:

**Stage Execution:**
- [ ] Can initialize new story (Stage 0-1)
- [ ] Can generate understanding [REQUIRED] (Stage 2)
- [ ] Can generate script [REQUIRED] (Stage 3)
- [ ] Can generate assets [REQUIRED] (Stage 4)
- [ ] Can skip reference search with Claude decision + user approval (Stage 5)
- [ ] Can generate characteristic shot [REQUIRED] (Stage 6)
- [ ] Can generate and approve grid [CRITICAL CHECKPOINT] (Stage 7)
- [ ] Can adapt script to grid (Stage 8)
- [ ] Can extract all 9 frames (batch) (Stage 9)
- [ ] Can approve each frame sequentially
- [ ] Can edit frame with NanoBanana (if rejected)
- [ ] Can generate all 9 videos with API choice (batch, waits for all frame approvals) (Stage 10)
- [ ] Can approve each video sequentially
- [ ] Can regenerate video with feedback
- [ ] Can choose API/model: kling-direct, kling-pollo, veo3-laozhang, veo3-pollo
- [ ] Can generate captions (Stage 11)
- [ ] Can assemble final video (Stage 12)

**Iteration & Dependencies:**
- [ ] All iterations preserved (never deleted)
- [ ] Parent iteration tracked in metadata.json
- [ ] Current symlink auto-updates to new iteration
- [ ] User can manually revert current symlink
- [ ] Dependency rebuilding works (regenerate frame → video regenerates)

**Logging & Queue:**
- [ ] Queue persists correctly (can resume after exit)
- [ ] Feedback log captures all interactions
- [ ] generation.log is verbose and helpful
- [ ] Can query queue with jq
- [ ] Can query feedback with jq

---

## Known Limitations

1. **No concurrent generations** - One story at a time (task_queue.json not locked)
2. **No undo** - Can only move forward or regenerate
3. **Grid checkpoint enforced** - Cannot change grid after frames extracted (must create new take)
4. **Manual queue editing** - If queue corrupts, must edit JSON by hand (but this is intentional for debuggability)

---

## Future Enhancements (After V4 Working)

1. **SQLite queue** - For concurrent access, better querying
2. **Web UI** - View queue, provide feedback via browser
3. **Multi-story parallel** - Generate multiple stories simultaneously
4. **Queue validation** - Detect corrupted queue and auto-fix
5. **Checkpoint snapshots** - Save complete state at each stage for rollback

---

## Success Criteria

**V4 is successful if:**

1. ✅ Can generate complete video from story text with automated workflow
2. ✅ Stops at each feedback point for human approval
3. ✅ Regenerates stages based on feedback
4. ✅ Rebuilds dependent stages automatically
5. ✅ **All iterations preserved permanently** (for future system improvement)
6. ✅ **Parent iteration tracked** in every metadata.json
7. ✅ **Current symlink auto-updates**, user can manually revert
8. ✅ **Required stages enforced** (understanding, assets, characteristic cannot skip)
9. ✅ **Reference search optional** with Claude decision + user approval
10. ✅ **API/model flexibility** (kling-direct, kling-pollo, veo3-laozhang, veo3-pollo)
11. ✅ Queue persists across runs
12. ✅ Feedback logged for Claude context
13. ✅ Verbose generation.log aids debugging
14. ✅ Can be called from Telegram bot
15. ✅ Scripts directory is clean and organized
16. ✅ **Implemented in one day**

---

## Next Steps

1. **Confirm approach** - Is this plan realistic for one-day implementation?
2. **Start Hour 1** - Build core queue infrastructure
3. **Test incrementally** - Get each hour's deliverable working before moving on
4. **Deploy to bot** - Once working, integrate into Telegram bot

**Timeline:** 8 hours focused work = one day implementation

**Priority:** Get basic automation working first, polish later

---

## Cascade Regeneration (`restart_from`)

### Problem

When reviewing stage X, the user may realize changes are needed at an earlier stage Y. For example, reviewing the characteristic shot (stage 06) and deciding the script understanding (stage 02) needs to change. Currently only refine/regenerate within the same stage is supported.

### Solution

New feedback action `restart_from` that:

1. Marks current feedback task as completed with restart context
2. Logs feedback for the target stage so Claude sees it in history
3. Creates new generate + await_feedback tasks for every stage from target through current
4. Rewires downstream pending tasks to depend on the new cascade's final task

### Implementation

**Files:**
- `scripts-v4/lib/cascade.py` — `STAGE_PIPELINE`, `create_cascade_tasks()`, `rewire_downstream_deps()`, `get_available_restart_stages()`
- `scripts-v4/orchestrator_v4.py` — `restart_from` branch in `process_feedback_response()`, restart option in `save_feedback_request()`

**Stage pipeline order:**
```
02-understanding → 03-script → 04-assets → 05-references → 06-characteristic → 07-grids
```

Each stage has task templates (generate + await_feedback, or check_skip + search_or_skip for references).

**Dep rewiring:** After creating cascade tasks, all pending tasks in stages AFTER the cascade range that depended on old cascade-stage tasks get rewired to depend on the new cascade's final task. This same rewiring fix was also applied to existing refine/regenerate to prevent downstream tasks from running prematurely.

### Feedback request format

When `save_feedback_request` creates the JSON, it includes a 4th option for stages that have earlier stages:

```json
{
    "action": "restart_from",
    "label": "⏪ Restart from...",
    "requires_text": true,
    "requires_target_stage": true,
    "available_stages": [
        {"stage": "02-understanding", "label": "Understanding"},
        {"stage": "03-script", "label": "Script"}
    ]
}
```

### Feedback response format

```json
{
    "action": "restart_from",
    "target_stage": "02-understanding",
    "feedback_text": "Add a second dog — Dave's black retriever that swims normally to contrast with Paul's water-walking golden retriever"
}
```

### Example: restart from 02-understanding while at 06-characteristic

User at task 44 (06-characteristic await_feedback), sends `restart_from: 02-understanding`:

```
New tasks created:
  45: 02-understanding / generate       deps=[44]
  46: 02-understanding / await_feedback  deps=[45]
  47: 03-script / generate              deps=[46]
  48: 03-script / await_feedback        deps=[47]
  49: 04-assets / generate              deps=[48]
  50: 04-assets / await_feedback        deps=[49]
  51: 05-references / check_skip        deps=[50]
  52: 05-references / search_or_skip    deps=[51]
  53: 06-characteristic / generate      deps=[52]
  54: 06-characteristic / await_feedback deps=[53]

Rewire: task 13 (07-grids/generate) deps [12] → [54]
```

Each stage reads from `current` symlink, so as each cascade stage completes and updates its symlink, the next stage automatically picks up the new output.
