# KHẢO SÁT CHUYÊN SÂU PIPELINE VÀ THIẾT KẾ KIẾN TRÚC CỔNG KIỂM DUYỆT 2 TẦNG (R1)
## CHIẾN DỊCH TÁI SẢN XUẤT TẬP 01 (EP01) - PHIM ĐIỆN ẢNH "THẬP NGŨ NIÊN"

> **Báo cáo kỹ thuật của Explorer Subagent**  
> **Workspace**: `C:\Projects\KieuStory`  
> **Thư mục tác nghiệp**: `.agents/teamwork/explorer_survey_1/`  
> **Ngày lập**: 2026-10-09  
> **Tiêu chuẩn kỹ thuật**: AGENTS.md, GEMINI.md, ORIGINAL_REQUEST.md, Google Antigravity Python SDK, Pydantic v2  

---

## 1. TỔNG QUAN HỆ THỐNG VÀ BỐI CẢNH DỰ ÁN

Dự án điện ảnh AI **"Thập Ngũ Niên" (The Fifteen Springs)** quy mô 180 phút (6 Tập x 30m) đang bước vào chiến dịch dồn toàn lực tái sản xuất toàn bộ **10 Cảnh trọng điểm của Tập 01 (Ep01)**.

### Mục tiêu chiến dịch:
1. **Triệt tiêu dứt điểm lỗi "Nhiễm Thúy Kiều" (Character Cross-Contamination)**: Trước đây, do cơ chế nối tiếp mù quáng (*blind head-tail chaining*), frame đuôi của shot trước (`clean_frame_239.jpg`) bị ép làm Start Frame cho shot sau kể cả khi shot sau đã chuyển sang nhân vật khác (Vương Ông, Vương Bà, Vương Quan, Kim Trọng). Điều này khiến Muse.ai sinh ra các video biến dạng nhân vật (morphing) hoặc mang gương mặt thiếu nữ gán vào người cha già/thư sinh.
2. **Thiết lập Cổng Kiểm duyệt 2 Tầng Antigravity (2-Tier Quality Gate - R1)**: Đánh giá tự động chất lượng shot 10s (Tầng 1 - Shot Gate) và chất lượng toàn cảnh 30-40s (Tầng 2 - Scene Gate) bằng MLLM Vision thông qua Google Antigravity SDK (`google.antigravity`) / `google.genai`, chuẩn hóa đầu ra qua Pydantic `VideoCriticVerdict`, với ngưỡng chấp thuận khắt khe `score >= 0.8`.
3. **Phân định rạch ròi Cú máy**: Tách bạch `Cinematic Cut` (chuyển cảnh/đổi nhân vật: BẮT BUỘC nạp Start Frame độc lập) và `Continuous Take` (cùng nhân vật/góc máy liền mạch: mới được phép nạp Tail Frame của shot trước).
4. **Bảo tồn bằng chứng sở hữu trí tuệ**: Lưu trữ 100% video cũ vào `04_Assets/archive/ep01_legacy_v1/`, dọn sạch keyframe lỗi mà không làm tổn hại tài sản gốc (`04_Assets/characters/`).

---

## 2. KHẢO SÁT HỆ THỐNG MÃ NGUỒN PIPELINE HIỆN TẠI (`05_Production_Pipeline/`)

### 2.1. Sơ đồ các module sản xuất trung tâm

```
05_Production_Pipeline/
├── production_orchestrator.py     <-- [ĐIỀU PHỐI CHÍNH] CLI, resolve start frame, batch scene, concat, master audio
├── multi_worker_orchestrator.py   <-- [RENDER SONG SONG] Điều phối 6 Worker Muse.ai (muse, muse_w2..w6), Work-Stealing Queue
├── episode_manager.py             <-- [QUẢN LÝ TẬP PHIM] Tách/gộp episodes/ep01..ep06, manifest.json, sync-to-master
├── audio_continuity_engine.py     <-- [HẬU KỲ ÂM THANH] 4-Stem Concat, Crossfade, Ducking -14dB, EBU R128 (-14 LUFS)
├── pipeline_helper.py             <-- [CÔNG CỤ PHỤ TRỢ] Prompt exporter, Shorts 9:16 NVENC maker, reverse motion
├── run_shot.py                    <-- [RENDER ĐƠN SHOT] Browser automation qua agent-browser hoặc InvisiblePlaywright
└── continuity_checker.py          <-- [KIỂM TRA CŨ] OpenCV Histogram + Diff correlation (còn thô sơ, chưa có AI)
```

### 2.2. Chi tiết các kịch bản và luồng thực thi

#### A. `production_orchestrator.py`
- **Vai trò**: Cỗ máy điều phối hạt nhân.
- **Entry points (CLI)**:
  - `--status`: Quét toàn bộ thư mục `04_Assets/videos/`, `keyframes/` và `02_AI_Prompts/muse_ai_video_prompts.json` để in bảng tiến độ theo từng cảnh.
  - `--render-shot <shot_id>`: Render 1 shot đơn lẻ qua Muse.ai.
  - `--batch-scene <scene_id>`: Duyệt tuần tự các shot trong cảnh, render nếu chưa có, và tự động gọi concat.
  - `--concat-scene <scene_id>`: Gọi `AudioContinuityEngine.stitch_with_audio_crossfade` hoặc fallback FFmpeg `filter_complex concat`.
  - `--master-audio <scene_id>`: Hòa trộn 4-Stem và chuẩn hóa âm lượng EBU R128 (-14 LUFS).
- **Cơ chế Versioning**: Sử dụng hàm `resolve_versioned_path` để tạo file có hậu tố `_v<N>` (`_v1.mp4`, `_v2.mp4`...).

#### B. `multi_worker_orchestrator.py`
- **Vai trò**: Vận hành song song 5-6 phiên trình duyệt Muse.ai riêng biệt.
- **Phiên làm việc độc lập**:
  - Worker 1: Session `muse`
  - Worker 2 - 6: Session `muse_w2` đến `muse_w6`
  - Thư mục tải về cô lập: `04_Assets/temp_downloads/w<id>` qua biến môi trường `AGENT_BROWSER_DOWNLOAD_PATH`.
- **Nguyên tắc bất biến về mạch phim (Continuity Invariant)**:
  - **Đơn vị phân chia song song là CẢNH (SCENE) hoặc TẬP (EPISODE)**.
  - Tuyệt đối không xé lẻ shot trong cùng một cảnh cho các worker khác nhau.

#### C. `episode_manager.py`
- **Vai trò**: Hiện thực hóa chỉ thị **Anti-Dilution & Zero Overlap Directive**.
- **Cấu trúc dữ liệu theo tập**: Thư mục `episodes/ep01/` đến `episodes/ep06/` chứa riêng `screenplay.md`, `prose.md`, `prompts/muse_prompts.json`, `prompts/banana_prompts.json`, `manifest.json`.
- **Cơ chế Two-Way Sync**: Hàm `sync_to_master()` gộp các chỉnh sửa từ từng tập về file master `02_AI_Prompts/muse_ai_video_prompts.json`.

#### D. `audio_continuity_engine.py`
- **Vai trò**: Đảm bảo chất lượng âm thanh điện ảnh chuẩn phát sóng YouTube Green Dollar.
- **Tính năng vượt trội**:
  - Kiến trúc 4-Stem (BGM, Ambience, Foley, Dialogue).
  - Equal-power crossfade (qsin curve) triệt tiêu tiếng click/pop.
  - Dynamic sidechain ducking (hạ BGM -14dB khi có thoại/ngâm thơ).
  - Two-pass linear normalization (-14 LUFS, True Peak -1.0 dBTP).

---

### 2.3. Chẩn đoán nguyên nhân gốc rễ (Root-Cause Analysis) lỗi "Nhiễm Thúy Kiều"

Khi soi chiếu mã nguồn hàm `resolve_start_frame` trong `production_orchestrator.py` (dòng 127 - 187):

```python
# MÃ NGUỒN CŨ GÂY LỖI TRONG production_orchestrator.py:
def resolve_start_frame(shot_id: str, shot_data: Dict, explicit_frame: Optional[str] = None) -> Optional[str]:
    if explicit_frame and os.path.exists(explicit_frame):
        return str(Path(explicit_frame).resolve())

    # TỰ ĐỘNG TÌM SHOT TRƯỚC NẾU LÀ SHOT ĐÁNH SỐ:
    m = re.search(r"^(ep\d+_scene\d+)_shot(\d+)$", shot_id, re.IGNORECASE)
    if m:
        scene_prefix = m.group(1)
        shot_num = int(m.group(2))
        if shot_num > 1:
            prev_shot_id = f"{scene_prefix}_shot{shot_num - 1:02d}"
            prev_tail = find_tail_frame(prev_shot_id)
            if prev_tail:
                return str(prev_tail.resolve())  # <--- BẪY CHẾT NGƯỜI TẠI ĐÂY!
...
```

#### Hệ quả tai hại:
1. Trong kịch bản phân cảnh (`FilmMaker/TAP_01_*.md`), trong một cảnh thường xuyên có nhiều cú cắt máy nghệ thuật (*Cinematic Cuts*):
   - Ví dụ tại Cảnh 01: Shot 01 (Cụ ông & đứa cháu) -> Shot 02 (Trung cận cụ ông) -> Shot 03 (Rặng cau & mái ngói) -> Shot 04 (Bàn tay quạt mo cau) -> Shot 05 (Chén nước chè).
   - Ví dụ tại Cảnh 02 (Phòng khách họ Vương): Shot 01 (Toàn cảnh gia đình) -> Shot 02 (Trung cận Vương Ông 55 tuổi) -> Shot 03 (Vương Bà) -> Shot 04 (Vương Quan 14 tuổi).
   - Ví dụ tại Cảnh 05 (Mộ Đạm Tiên): Shot chứa Thúy Kiều chuyển sang shot Vương Quan hoặc nấm mồ vô chủ.
2. Với logic cũ: Cứ `shot_num > 1` là script tự động bốc `clean_frame_239.jpg` của shot trước làm ảnh mồi I2V cho shot sau!
3. Kết quả: Khi shot trước là Thúy Kiều và shot sau là Vương Ông 55 tuổi, Muse.ai bị nạp Start Frame là mặt Thúy Kiều kèm prompt tả ông già tóc bạc. Muse.ai cố gắng nội suy từ gương mặt thiếu nữ sang ông già, dẫn đến hiện tượng khuôn mặt bị méo mó kinh dị, râu mọc trên mặt Thúy Kiều, hoặc nhân vật bị biến dạng hoàn toàn (*morphing disaster*).
4. **Kết luận khảo sát**: Cần phải thay thế hoàn toàn logic `resolve_start_frame` bằng bộ phân loại thông minh **Cinematic Cut vs Continuous Take**, đối chiếu trực tiếp với `character_anchor` trong `02_AI_Prompts/gemini_banana_prompts.json` và kho ảnh chân dung Master trong `04_Assets/characters/`.

---

## 3. THIẾT KẾ KIẾN TRÚC CỔNG KIỂM DUYỆT 2 TẦNG (R1 - ANTIGRAVITY QUALITY GATE)

### 3.1. Vị trí module và chiến lược đa động cơ (Multi-Engine Architecture)

- **Vị trí file**: `05_Production_Pipeline/antigravity_critic_gate.py`
- **Mô hình Hybrid 3 Lớp Dự Phòng (Triple-Tier Fallback Strategy)**:
  1. **Động cơ 1 (Primary - Google Antigravity SDK)**:
     Sử dụng `from google.antigravity import Agent, LocalAgentConfig, Video, Image` kết hợp `LocalAgentConfig(response_schema=VideoCriticVerdict)` để nạp trực tiếp file video/ảnh và yêu cầu MLLM sinh kết quả có cấu trúc Pydantic nghiêm ngặt.
  2. **Động cơ 2 (Secondary - Google GenAI SDK)**:
     Sử dụng `google.genai.Client` với model `gemini-2.5-flash` (hoặc `gemini-1.5-pro`) với `response_mime_type="application/json"` và `response_schema=VideoCriticVerdict`. Tự động kích hoạt khi môi trường cần gọi direct API client.
  3. **Động cơ 3 (Offline / Deterministic Rule-Engine)**:
     Sử dụng OpenCV (`cv2`) và FFprobe để kiểm tra độ phân giải, độ dài video (đủ 10s ± 0.5s), luồng âm thanh AAC (không câm), và tỷ lệ biến đổi màu sắc. Đảm bảo quy trình sản xuất không bao giờ bị dừng đột ngột (*pipeline resilience*).

---

### 3.2. Cấu trúc dữ liệu Pydantic Model (`VideoCriticVerdict`)

Toàn bộ kết quả kiểm duyệt được đóng gói chuẩn hóa theo schema Pydantic v2:

```python
from typing import List, Optional, Literal
from pydantic import BaseModel, Field

# ==========================================
# CÁC SUB-MODEL THẨM ĐỊNH THÀNH PHẦN
# ==========================================

class CharacterValidation(BaseModel):
    expected_characters: List[str] = Field(
        default_factory=list, 
        description="Danh sách nhân vật theo kịch bản và Character Bible (vd: ['vuong_ong', 'thuy_kieu'])"
    )
    detected_characters: List[str] = Field(
        default_factory=list, 
        description="Nhân vật nhận diện được trong khung hình"
    )
    character_match_score: float = Field(
        ..., ge=0.0, le=1.0, 
        description="Độ tương đồng tạo hình, trang phục, độ tuổi so với Character DNA (0.0 - 1.0)"
    )
    character_mixup_detected: bool = Field(
        default=False, 
        description="Cờ cảnh báo nhầm vai nghiêm trọng (vd: Thúy Kiều xuất hiện trong shot của Vương Ông/Kim Trọng)"
    )
    notes: str = Field(default="", description="Nhận xét chi tiết về tạo hình nhân vật")


class VisualQuality(BaseModel):
    artifact_detected: bool = Field(
        default=False, 
        description="Phát hiện dị tật AI (mắt lé, biến dạng ngón tay, mặt nhòe sáp, morphing méo mó)"
    )
    artifact_details: List[str] = Field(
        default_factory=list, 
        description="Chi tiết các dị tật thị giác phát hiện được"
    )
    motion_smoothness_score: float = Field(
        ..., ge=0.0, le=1.0, 
        description="Độ mượt mà của chuyển động máy quay và nhân vật"
    )
    reverse_motion_detected: bool = Field(
        default=False, 
        description="Phát hiện chuyển động ngược chiều vật lý (đi lùi, khói bay ngược)"
    )
    headroom_safe: bool = Field(
        default=True, 
        description="Bố cục an toàn, không bị cắt lẹm trâm cài, đỉnh đầu hoặc vai"
    )


class AudioCompliance(BaseModel):
    has_unauthorized_bgm: bool = Field(
        default=False, 
        description="Phát hiện nhạc nền rác do Muse AI tự sinh (vi phạm Audio Guard)"
    )
    speech_clarity_score: float = Field(
        default=1.0, ge=0.0, le=1.0, 
        description="Độ rõ ràng của âm thanh thoại/foley/ambience"
    )
    audio_notes: str = Field(default="", description="Ghi chú về âm thanh")


class JunctionEvaluation(BaseModel):
    shot_a: str = Field(..., description="Mã Shot trước")
    shot_b: str = Field(..., description="Mã Shot sau")
    axis_180_degree_violation: bool = Field(
        default=False, 
        description="Vi phạm quy tắc trục 180 độ (nhảy trục qua đường hành động)"
    )
    eyeline_match_score: float = Field(
        default=1.0, ge=0.0, le=1.0, 
        description="Độ khớp hướng mắt giữa các nhân vật đối thoại (0.0 - 1.0)"
    )
    color_continuity_score: float = Field(
        default=1.0, ge=0.0, le=1.0, 
        description="Độ đồng nhất về tone màu và ánh sáng giữa điểm giao cắt"
    )
    jump_cut_detected: bool = Field(
        default=False, 
        description="Phát hiện cú giật hình bất thường không chủ ý"
    )


class PacingTempo(BaseModel):
    tempo_score: float = Field(
        ..., ge=0.0, le=1.0, 
        description="Điểm nhịp điệu cắt dựng điện ảnh (0.0 - 1.0)"
    )
    static_freeze_detected: bool = Field(
        default=False, 
        description="Phát hiện đoạn tĩnh vô hồn kéo dài > 2 giây"
    )
    recommended_trim_ranges: List[str] = Field(
        default_factory=list, 
        description="Chỉ định đoạn cần cắt tỉa (vd: ['shot02: 8.5s - 10.0s'])"
    )


class QualityActions(BaseModel):
    needs_retake: bool = Field(
        default=False, 
        description="Yêu cầu render lại (re-take) do không đạt chuẩn"
    )
    retake_reason: Optional[str] = Field(
        default=None, 
        description="Lý do bắt buộc re-take"
    )
    needs_color_match: bool = Field(
        default=False, 
        description="Yêu cầu kích hoạt bộ lọc cân bằng màu sắc (Color Match)"
    )
    needs_trim: bool = Field(
        default=False, 
        description="Yêu cầu cắt bớt frame tĩnh trước khi xuất master"
    )
    retake_shot_ids: List[str] = Field(
        default_factory=list, 
        description="Danh sách shot cụ thể cần render lại"
    )


# ==========================================
# MODEL PHÊ DUYỆT TỔNG THỂ (VERDICT MODEL)
# ==========================================

class VideoCriticVerdict(BaseModel):
    target_id: str = Field(..., description="Mã định danh shot (vd: ep01_scene02_shot03) hoặc scene (vd: ep01_scene02)")
    tier: Literal["shot_gate", "scene_gate"] = Field(..., description="Tầng kiểm duyệt: shot_gate (Tầng 1) hoặc scene_gate (Tầng 2)")
    score: float = Field(..., ge=0.0, le=1.0, description="Điểm đánh giá tổng hợp (0.0 - 1.0). Ngưỡng đạt: >= 0.8")
    passed: bool = Field(..., description="Trạng thái phê duyệt (True nếu score >= 0.8 và không có lỗi nghiêm trọng)")
    
    # Đánh giá chi tiết Tầng 1
    character_validation: Optional[CharacterValidation] = None
    visual_quality: Optional[VisualQuality] = None
    audio_compliance: Optional[AudioCompliance] = None
    
    # Đánh giá chi tiết Tầng 2
    junctions: Optional[List[JunctionEvaluation]] = None
    pacing: Optional[PacingTempo] = None
    
    # Hành động điều chỉnh tự động
    actions: QualityActions = Field(default_factory=QualityActions)
    
    critique_summary: str = Field(..., description="Tóm tắt nhận xét của Hội đồng Phê bình MLLM")
    director_guidance: str = Field(..., description="Chỉ thị cụ thể cho Đạo diễn/Pipeline để khắc phục nếu không đạt")
```

---

### 3.3. Tầng 1: Shot Gate (Kiểm duyệt độc lập Shot 10s)

- **Mục tiêu**: Thẩm định từng file video 10s ngay khi vừa tải về từ Muse.ai, trước khi lưu chính thức vào `04_Assets/videos/` và trước khi trích xuất Tail Frame.
- **Tiêu chuẩn kiểm duyệt**:
  1. **Nhân vật & Tuân thủ Character Bible**:
     - Đối chiếu với chân dung chuẩn trong `04_Assets/characters/` (ví dụ: `vuong_ong_55yo_720p.png`, `kim_trong_18yo_720p.png`, `thuy_kieu_maiden_720p.png`).
     - Bắt lỗi nhầm lẫn nghiêm trọng: Nếu kịch bản yêu cầu Vương Ông (người cha 55 tuổi) nhưng trên video lại xuất hiện Thúy Kiều -> `character_mixup_detected = True`, `score = 0.0`, **FAIL ngay lập tức**.
     - Tuân thủ quy tắc **Anti-Male-Tears Directive**: Nam nhân (Kim Trọng, Vương Ông, Vương Quan) tuyệt đối không rơi lệ mềm yếu; biểu cảm khắc kỷ kiên định.
  2. **Dị tật thị giác (Visual Artifacts)**:
     - Kiểm tra mắt lé, tay thừa ngón, khuôn mặt biến dạng dạng sáp nhựa bóng bẩy (*plastic mannequin skin*).
     - Headroom: Không cắt cụt trâm cài tóc, mũ mãng, đỉnh đầu của nhân vật.
  3. **Vật lý chuyển động (Motion Physics)**:
     - Phát hiện chuyển động ngược: nhân vật đi lùi, khói chìm ngược xuống tẩu.
  4. **Kiểm chuẩn Audio Guard**:
     - Kiểm tra âm thanh Muse.ai sinh ra: nếu có nhạc nền điện tử hoặc beat ngẫu nhiên vi phạm chỉ thị Audio Guard -> đánh dấu cảnh báo để hậu kỳ lọc bỏ.
- **Quy tắc quyết định**:
  - `score >= 0.8`: Chấp nhận shot, lưu thành `ep01_sceneXX_shotYY_10s_v<N>.mp4`, trích xuất `clean_frame_239.jpg`.
  - `score < 0.8`: Đánh dấu thất bại. Tự động kích hoạt cơ chế **Re-take** (tối đa 3 lần), điều chỉnh prompt/Start Frame theo `director_guidance`.

---

### 3.4. Tầng 2: Scene Gate (Kiểm duyệt dòng chảy điện ảnh toàn cảnh 30-40s)

- **Mục tiêu**: Thẩm định video Master của cả cảnh (sau khi đã nối tạm các shot) để đánh giá tính liền mạch, nhịp điệu và tiếp biên giữa các shot.
- **Tiêu chuẩn kiểm duyệt**:
  1. **Điểm giao cắt tiếp biên (Junction Continuity)**:
     - Thẩm định cặp khung hình: Frame cuối của Shot N (`clean_frame_239.jpg`) và Frame đầu của Shot N+1 (`frame_000.jpg`).
     - Phát hiện hiện tượng giật cục bất thường (*jarring jump-cut*).
  2. **Quy tắc trục 180 độ (180-Degree Rule & Line of Action)**:
     - Trong các cảnh đối thoại (Kim - Kiều thề nguyền tại Scene 10, Vương Ông trò chuyện tại Scene 02): kiểm tra hướng nhìn của hai nhân vật có đảo ngược hợp lý hay bị "nhảy trục" làm khán giả mất phương hướng không gian.
  3. **Độ khớp hướng nhìn (Eye-Line Continuity)**:
     - Khi Shot A nhân vật nhìn chếch sang phải (screen-right), Shot B nhân vật đối diện phải nhìn chếch sang trái (screen-left).
  4. **Đồng nhất màu sắc & Ánh sáng (Color Continuity)**:
     - Đo lường độ chênh lệch nhiệt độ màu và độ tương phản giữa các shot.
     - **Tự động kích hoạt Color Match**: Nếu phát hiện chênh lệch màu giữa các shot vượt ngưỡng 15%, tự động gọi script FFmpeg/OpenCV cân bằng màu (Color Transfer/LUT matching) trước khi xuất master.
  5. **Nhịp điệu cắt dựng & Đoạn tĩnh (Editing Tempo & Static Freeze)**:
     - Phát hiện đoạn video bị "đơ" (static freeze) quá 2.0 giây mà không có chuyển động vi mô.
     - **Tự động kích hoạt Trim**: Đề xuất khoảng thời gian cắt tỉa chính xác (ví dụ: `trim shot02 from 8.2s to 10.0s`) để nhịp phim dồn dập, cuốn hút, bảo đảm tỷ lệ giữ chân khán giả (*Audience Retention*).
- **Quy tắc quyết định**:
  - `score >= 0.8`: Phê duyệt xuất bản Master cảnh vào `04_Assets/videos/<scene_id>_master_v<N>.mp4` và `06_Exports/`.
  - `score < 0.8`: Kích hoạt bộ xử lý hậu kỳ (Color Match / Trim) hoặc chỉ định đích danh shot cần re-take.

---

### 3.5. Tái cấu trúc phân loại cú máy: Cinematic Cut vs Continuous Take

Đây là cải tiến kiến trúc mang tính sống còn để triệt tiêu vĩnh viễn lỗi "Nhiễm Thúy Kiều".

| Tiêu Chí Phân Định | Continuous Take (Cú máy Nối tiếp Liền mạch) | Cinematic Cut (Cú cắt Chuyển cảnh / Đổi góc) |
| :--- | :--- | :--- |
| **Bản chất điện ảnh** | Cùng một nhân vật, cùng góc máy hoặc camera pan/tilt theo một hành động liên tục không ngắt quãng. | Chuyển sang nhân vật khác; đổi từ toàn cảnh sang cận cảnh; đổi sang góc quay đối diện (reverse-shot); hoặc cắt sang cảnh vật/đạo cụ. |
| **Quy tắc Start Frame** | **ĐƯỢC PHÉP** sử dụng Tail Frame (`clean_frame_239.jpg`) của shot trước làm Start Frame. | **TUYỆT ĐỐI CẤM** dùng Tail Frame của shot trước! **BẮT BUỘC** nạp Start Frame độc lập. |
| **Nguồn nạp Start Frame** | `04_Assets/keyframes/<prev_shot>/clean_frame_239.jpg` | 1. Ảnh chân dung chuẩn trong `04_Assets/characters/`<br>2. Prompt chuyên biệt từ `02_AI_Prompts/gemini_banana_prompts.json`<br>3. Khung hình bối cảnh từ `04_Assets/backgrounds/` |
| **Cơ chế nhận diện tự động** | `character_anchor` của Shot N **trùng khớp 100%** với Shot N-1, và kịch bản không ghi `Cut to:` | `character_anchor` khác nhau, hoặc `character_anchor == "none"` (cảnh vật, chén trà, mái ngói), hoặc đổi loại cỡ cảnh. |

#### Thuật toán Resolve Start Frame mới:

```python
def resolve_start_frame_v2(shot_id: str, shot_data: Dict, banana_prompts: Dict) -> Tuple[str, str]:
    """
    Phân loại cú máy và giải quyết Start Frame an toàn:
    Trả về: (start_frame_path, take_type: "CONTINUOUS_TAKE" | "CINEMATIC_CUT")
    """
    # 1. Nếu người dùng chỉ định rõ ràng
    if shot_data.get("explicit_frame"):
        return shot_data["explicit_frame"], "CINEMATIC_CUT"

    # 2. Tra cứu metadata từ gemini_banana_prompts.json
    curr_banana = banana_prompts.get(shot_id, {})
    curr_char = curr_banana.get("character_anchor", "").strip()
    
    # Tìm shot trước trong cùng scene
    m = re.search(r"^(ep\d+_scene\d+)_shot(\d+)$", shot_id, re.IGNORECASE)
    if m:
        scene_prefix, shot_num = m.group(1), int(m.group(2))
        if shot_num > 1:
            prev_shot_id = f"{scene_prefix}_shot{shot_num - 1:02d}"
            prev_banana = banana_prompts.get(prev_shot_id, {})
            prev_char = prev_banana.get("character_anchor", "").strip()
            
            # ĐIỀU KIỆN ĐỂ ĐƯỢC COI LÀ CONTINUOUS TAKE:
            # - Cùng nhân vật
            # - Nhân vật không phải là 'none' (cảnh vật)
            # - Prompt không yêu cầu góc máy ngược hoặc chuyển cảnh
            if curr_char and curr_char == prev_char and curr_char != "none":
                prev_tail = find_tail_frame(prev_shot_id)
                if prev_tail:
                    return str(prev_tail), "CONTINUOUS_TAKE"

    # 3. KHI LÀ CINEMATIC CUT: BẮT BUỘC NẠP ẢNH ĐỘC LẬP
    # Map character_anchor sang file ảnh trong 04_Assets/characters/
    char_asset_path = resolve_character_asset_from_anchor(curr_char)
    if char_asset_path and os.path.exists(char_asset_path):
        return str(char_asset_path), "CINEMATIC_CUT"
        
    # Hoặc lấy Start Frame đã được sinh sẵn cho shot này
    pre_gen_sf = find_pregenerated_start_frame(shot_id)
    if pre_gen_sf:
        return str(pre_gen_sf), "CINEMATIC_CUT"

    # Fallback an toàn: Không lấy tail frame bừa bãi
    return None, "CINEMATIC_CUT"
```

---

### 3.6. Tích hợp Cổng Kiểm duyệt vào quy trình sản xuất (Workflow Integration)

```
[ BẮT ĐẦU RENDER SHOT ]
         │
         ▼
[ Phân loại Cú máy (resolve_start_frame_v2) ]
   ├── CONTINUOUS_TAKE ➔ Nạp Tail Frame shot trước
   └── CINEMATIC_CUT   ➔ Nạp Start Frame độc lập (Character Bible)
         │
         ▼
[ Render video qua Muse.ai (agent-browser / invpw) ]
         │
         ▼
[ TẦNG 1: SHOT GATE (antigravity_critic_gate) ]
   ├── Score < 0.8 ➔ Gửi vào archive, tự động Re-take (tối đa 3 lần)
   └── Score >= 0.8 ➔ Chấp nhận! Trích xuất clean_frame_239.jpg
         │
         ▼
[ LẶP CHO TOÀN BỘ CÁC SHOT TRONG SCENE ]
         │
         ▼
[ Ghép nối tạm thời các shot (AudioContinuityEngine) ]
         │
         ▼
[ TẦNG 2: SCENE GATE (antigravity_critic_gate) ]
   ├── Phát hiện lệch màu ➔ Kích hoạt Auto Color Match
   ├── Phát hiện đơ hình > 2s ➔ Kích hoạt Auto Trim
   ├── Phát hiện lỗi trục 180° / hỏng junction ➔ Re-take shot bị lỗi
   └── Score >= 0.8 ➔ Phê duyệt!
         │
         ▼
[ Master Âm thanh 4-Stem chuẩn -14 LUFS ] ➔ [ XUẤT MASTER CẢNH ]
```

---

## 4. CHI TIẾT TÀI NGUYÊN VÀ DỮ LIỆU ĐÃ KIỂM KÊ CỦA TẬP 01 (EP01)

### 4.1. Danh mục 15 Cảnh và 188 Shots của Tập 01
Qua đối soát giữa `FilmMaker/TAP_01_*.md` và `02_AI_Prompts/gemini_banana_prompts.json`:
- **10 Cảnh Trọng Điểm Cần Tái Sản Xuất Ưu Tiên (Scenes 01 -> 10)**:
  1. `ep01_scene01` (21 shots): Khung truyện Bắc Bộ 198x & Kinh thành Gia Tĩnh.
  2. `ep01_scene02` (15 shots): Gia trang họ Vương - Phòng khách (Vương Ông, Vương Bà, Vương Quan).
  3. `ep01_scene03` (15 shots): Khuê phòng Thúy Kiều & Thúy Vân (Tài sắc song toàn).
  4. `ep01_scene04` (12 shots): Hội Đạp Thanh rộn ràng (Dòng người trẩy hội).
  5. `ep01_scene05` (27 shots): Bờ suối Tiêu Khê & Nấm mồ Đạm Tiên.
  6. `ep01_scene06` (14 shots): Tương ngộ Kim Trọng dưới rặng liễu hoàng hôn.
  7. `ep01_scene07` (8 shots): Thư phòng Kim Trọng (Tương tư đêm trăng).
  8. `ep01_scene08` (8 shots): Bức tường hoa ngăn cách hai nhà.
  9. `ep01_scene09` (6 shots): Kim - Kiều tương kiến & Trao cành kim thoa.
  10. `ep01_scene10` (14 shots): Vườn Thúy đêm rằm tháng Tư - Thề nguyền đồng tâm.
  *(Tổng cộng 10 cảnh đầu: 140 shots)*.
- **5 Cảnh Cao Trào Gia Biến (Scenes 11 -> 15)**:
  11. `ep01_scene11` (10 shots): Biệt ly Liêu Dương.
  12. `ep01_scene12` (8 shots): Giông bão cổng chính họ Vương.
  13. `ep01_scene13` (12 shots): Đại biến gia tộc (Bóng đổ Chiaroscuro nha môn).
  14. `ep01_scene14` (12 shots): Tan hoang đêm mưa.
  15. `ep01_scene15` (6 shots): Gian thờ tổ tiên - Quyết định bán mình (Cliffhanger).
  *(Tổng cộng toàn bộ Tập 01: 188 shots bám sát 100% prompt banana)*.

### 4.2. Kho ảnh nhân vật gốc (`04_Assets/characters/`)
Đã kiểm kê và xác nhận đầy đủ 100% các file chân dung 720p không nén sẵn sàng làm Start Frame:
- `01_Main_Protagonists/`: `thuy_kieu_maiden_720p.png`, `thuy_van_maiden_16yo_720p.png`, `kim_trong_18yo_720p.png`.
- `02_Vuong_Family_And_Fate/`: `vuong_ong_55yo_720p.png`, `vuong_ba_50yo_720p.png`, `vuong_quan_16yo_720p.png`, `dam_tien_720p.png`.
- `04_Brokers_And_Brothels/`: `ma_giam_sinh_720p.png`, `tu_ba_720p.png`, `so_khanh_720p.png`.
- `05_Imperial_Court_And_Officials/`: `sai_nha_720p.png`, `thang_ban_to_720p.png`.

---

## 5. KẾ HOẠCH HÀNH ĐỘNG TRIỂN KHAI TIẾP THEO

1. **Triển khai Module `antigravity_critic_gate.py`**:
   - Viết trọn vẹn mã nguồn module theo thiết kế Pydantic v2 ở Mục 3.2.
   - Hiện thực hóa lớp `AntigravityCriticGate` hỗ trợ kiểm duyệt cả file video MP4 và cặp ảnh junction.
2. **Triển khai Kịch bản Lưu trữ & Dọn dẹp (`archive_and_clean_ep01.py`)**:
   - Chuyển toàn bộ video raw/master cũ của Ep01 vào `04_Assets/archive/ep01_legacy_v1/` kèm file thuyết minh `README.md`.
   - Dọn sạch các file frame lỗi cũ trong `04_Assets/keyframes/ep01_*/` để triệt tiêu nguồn lây nhiễm hình ảnh.
3. **Cập nhật `production_orchestrator.py`**:
   - Tích hợp hàm `resolve_start_frame_v2` phân loại `Cinematic Cut` vs `Continuous Take`.
   - Tích hợp gọi `AntigravityCriticGate` trong `render_single_shot` và `batch_render_scene`.
4. **Vận hành chiến dịch Render Ep01**:
   - Chạy render tuần tự hoặc song song đa worker từ Scene 01 đến Scene 10.
   - Thẩm định tự động 100% qua Cổng Antigravity với điểm số >= 0.8.
   - Ghép Master hoàn thiện đạt chuẩn YouTube Green Dollar.

---
*Tài liệu khảo sát và thiết kế kỹ thuật - Bản quyền sản xuất © 2026 THẬP NGŨ NIÊN - Tác giả & Chủ sở hữu IP: Nguyễn Sĩ Sơn.*
