remotion-composer
Intent
Sixth stage of the AnimeLegends generative pipeline. Receives artifact paths (generated images, videos, voice audio) from gen-director, loads the appropriate Remotion v4 composition template based on channel (PowerScaling / Philosophy / LoreDrop / TrainingArc / LegendsLabs), composites all shots into a single timeline with timing from ScriptManifest and StoryboardManifest, applies the AnimeLegends brand LUT (color grading), adds gold particle intro animation (2s), appends 3s brand bumper outro with subscribe CTA, renders final MP4 at 1080×1920 vertical format (Day 1-30) or 2160×3840 4K (Day 31+), 30fps (Day 1-30) or 60fps (Day 31+), and outputs the rendered video path for publish-orchestrator to distribute.
Inputs
What this skill expects to receive:
interface RemotionComposerInput {
generate_manifest: GenerateManifest; // from gen-director (required)
script_manifest: ScriptManifest; // from script-forge (for dialogue timing)
storyboard_manifest: StoryboardManifest; // from storyboard-forge (for shot metadata)
render_quality?: "preview" | "production"; // default: production
output_resolution?: "1080p" | "4K"; // default: 1080p (Day 1-30), 4K (Day 31+)
}
Source of input: GenerateManifest from gen-director, with references to ScriptManifest and StoryboardManifest for timing and metadata.
Outputs
What this skill produces:
interface RemotionComposerOutput {
render_id: string; // UUID v4
generate_id: string; // parent generate UUID
topic: string; // clean topic text
channel: string; // PowerScaling | Philosophy | LoreDrop | TrainingArc | LegendsLabs
video_path: string; // final rendered MP4 file path
render_stats: {
resolution: string; // "1080x1920" | "2160x3840"
fps: number; // 30 | 60
duration_sec: number; // total video duration (original + intro + outro)
file_size_mb: number; // final MP4 file size
render_time_sec: number; // wall-clock render time
template_used: string; // Remotion template name
};
timestamp: string; // ISO 8601
}
Where the output goes: passed to publish-orchestrator as input (if publishing), logged to grimoire-keeper, saved to renders/ directory.
Core logic / rules
Template selection — Map script_manifest.channel to Remotion composition template:
PowerScaling → templates/PowerScaling.tsx (red/gold accent, dynamic energy, comparison overlays)
Philosophy → templates/Philosophy.tsx (blue/purple gradient, contemplative pacing, symbol overlays)
LoreDrop → templates/LoreDrop.tsx (green/teal accent, archive aesthetic, timeline graphics)
TrainingArc → templates/TrainingArc.tsx (orange/warm gradient, journey framing, progress bars)
LegendsLabs → templates/LegendsLabs.tsx (silver/tech accent, grid overlays, technical diagrams)
Resolution and FPS determination — Check render date to determine output specs:
Day 1-30 (before 2026-09-16): 1080×1920 vertical, 30fps
Day 31+ (after 2026-09-16): 2160×3840 vertical 4K, 60fps
Respect output_resolution override if provided (for testing).
Asset loading — For each shot in generate_manifest.artifacts:
Load image_path (PNG/JPG) as still frame or I-frame for video
Load video_path (MP4) if shot has motion
Load audio_path (WAV/MP3) voice audio
Verify file exists and is readable; if missing, skip shot and log warning to Grimoire.
Timeline composition — Arrange shots on Remotion timeline:
Intro sequence (0-2s): gold particle burst animation + AnimeLegends logo fade-in
Shot sequence (2s to 2s+duration_sec): each shot placed at shot.start_sec + 2s (offset by intro)
Layer video (or static image) on video track
Layer voice audio on audio track
Apply shot-specific transitions (crossfade between shots, 0.3s overlap)
Outro sequence (2s+duration_sec to 5s+duration_sec): 3s brand bumper with subscribe CTA
Gold particle outro animation
"Follow for more [channel] content" text overlay
AnimeLegends logo hold
Brand LUT application — Apply color grading via LUT (look-up table):
AnimeLegends LUT : contrast +15%, saturation +10%, shadows lifted +8%, gold particle hue preserved (±5° tolerance)
LUT applied globally to all shots (except intro/outro which have pre-graded assets)
Audio mixing — Mix voice audio tracks:
Normalize voice audio to -3dB peak (prevent clipping)
Apply per-mascot reverb/EQ (already in gen-director voice files, but re-verify)
Add background music bed (optional, low-level ambient drone at -18dB if available in assets/music/)
Master output to -1dB peak
Remotion render — Invoke Remotion CLI to render composition:
npx remotion render [template] [output_path] \
--props='{"artifacts": [...], "channel": "PowerScaling", ...}' \
--codec=h264 \
--crf=18 \
--preset=slow \
--audio-codec=aac \
--audio-bitrate=192k
CRF 18 for high quality (lower = higher quality, 18-23 range recommended)
Preset "slow" for better compression (acceptable for production)
Audio codec AAC at 192kbps (standard for social media)
Output verification — After render completes:
Verify video_path file exists and size >0 bytes
Verify duration matches expected: intro(2s) + duration_sec + outro(3s)
Compute file size in MB
If render failed (file missing or zero size), abort and log error to Grimoire.
Manifest generation — Populate RemotionComposerOutput with all fields, generate UUID for render_id, set timestamp to ISO 8601 UTC.
Grimoire logging — Log invocation to grimoire-keeper with stage="remotion-composer", input_id=generate_id, output_id=render_id, decision="accept" (if successful) or "error" (if render failed), rationale (one-line: template + resolution + render time), duration_ms.
Worked examples
Example 1: 30s PowerScaling video, 1080p render
Input:
{
"generate_manifest": {
"generate_id": "dddd4444-eeee-5555-ffff-666677778888",
"topic": "Why speed doesn't beat power",
"duration_sec": 30,
"artifacts": [
{ "shot_num": 1, "video_path": "/workspace/artifacts/gen/shot_001_video.mp4", "audio_path": "/workspace/artifacts/gen/shot_001_audio.wav" },
{ "shot_num": 2, "video_path": "/workspace/artifacts/gen/shot_002_video.mp4", "audio_path": "/workspace/artifacts/gen/shot_002_audio.wav" },
{ "shot_num": 3, "video_path": "/workspace/artifacts/gen/shot_003_video.mp4", "audio_path": "/workspace/artifacts/gen/shot_003_audio.wav" }
]
},
"script_manifest": { "channel": "PowerScaling" },
"storyboard_manifest": { /* shot metadata */ }
}
Output:
{
"render_id": "eeee5555-ffff-6666-aaaa-777788889999",
"generate_id": "dddd4444-eeee-5555-ffff-666677778888",
"topic": "Why speed doesn't beat power",
"channel": "PowerScaling",
"video_path": "/workspace/renders/powerscaling_20260816_103000.mp4",
"render_stats": {
"resolution": "1080x1920",
"fps": 30,
"duration_sec": 35,
"file_size_mb": 18.7,
"render_time_sec": 120,
"template_used": "PowerScaling"
},
"timestamp": "2026-08-16T10:30:00Z"
}
Why this output: PowerScaling template selected. 1080p@30fps (Day 1-30 default). 3 shots composited: intro (2s) + 30s content + outro (3s) = 35s total. Render time 120s (4× real-time, typical for Remotion h264 slow preset). File size 18.7 MB (~0.53 MB/s bitrate).
Example 2: 45s Philosophy video, 4K render (Day 31+)
Input:
{
"generate_manifest": {
"generate_id": "ffff6666-aaaa-7777-bbbb-888899990000",
"topic": "Why Domain is philosophically perfect",
"duration_sec": 45,
"artifacts": [ /* 5 shots */ ]
},
"script_manifest": { "channel": "Philosophy" },
"output_resolution": "4K"
}
Output:
{
"render_id": "aaaa7777-bbbb-8888-cccc-999900001111",
"generate_id": "ffff6666-aaaa-7777-bbbb-888899990000",
"topic": "Why Domain is philosophically perfect",
"channel": "Philosophy",
"video_path": "/workspace/renders/philosophy_20260916_140000_4k.mp4",
"render_stats": {
"resolution": "2160x3840",
"fps": 60,
"duration_sec": 50,
"file_size_mb": 67.3,
"render_time_sec": 310,
"template_used": "Philosophy"
},
"timestamp": "2026-09-16T14:00:00Z"
}
Why this output: Philosophy template selected. 4K@60fps (Day 31+ or explicit override). 5 shots composited: intro (2s) + 45s content + outro (3s) = 50s total. Render time 310s (6× real-time for 4K 60fps). File size 67.3 MB (1.35 MB/s bitrate, higher for 4K).
Example 3: Render failed (missing artifact)
Input:
{
"generate_manifest": {
"generate_id": "gggg8888-cccc-9999-dddd-000011112222",
"topic": "Test topic",
"duration_sec": 30,
"artifacts": [
{ "shot_num": 1, "video_path": "/workspace/artifacts/gen/shot_001_video.mp4", "audio_path": "/workspace/artifacts/gen/shot_001_audio.wav" },
{ "shot_num": 2, "video_path": "/workspace/artifacts/gen/shot_002_MISSING.mp4", "audio_path": "/workspace/artifacts/gen/shot_002_audio.wav" },
{ "shot_num": 3, "video_path": "/workspace/artifacts/gen/shot_003_video.mp4", "audio_path": "/workspace/artifacts/gen/shot_003_audio.wav" }
]
},
"script_manifest": { "channel": "PowerScaling" }
}
Output:
{
"error": "Render failed: missing artifact /workspace/artifacts/gen/shot_002_MISSING.mp4 for shot 2",
"stage": "remotion-composer",
"decision": "error",
"rationale": "missing_artifact_shot_2",
"remediation": "Re-run gen-director to regenerate missing shot 2 artifacts."
}
Why this output: Shot 2 video file missing. Asset loading step failed. Render aborted. Error logged to Grimoire with specific missing file path.
Failure modes
Missing artifact files — If any video_path or audio_path in generate_manifest.artifacts does not exist on disk, skip that shot and log warning to Grimoire with rationale="missing_artifact_shot_N". If >50% of shots are missing, abort render and return error: "Render failed: too many missing artifacts (X/Y shots)".
Remotion render timeout — If npx remotion render command times out (>600s for 1080p, >1200s for 4K), abort render and log error: "Render timeout exceeded". User may need to reduce quality preset (slow → medium) or resolution.
Remotion CLI not installed — If npx remotion command not found, abort with error: "Remotion v4 CLI not installed. Run: npm install -g remotion". Log to Grimoire with decision="error", rationale="remotion_not_installed".
FFmpeg not installed — If FFmpeg not available (required by Remotion for video encoding), abort with error: "FFmpeg not installed. Install via system package manager.". Log to Grimoire with decision="error", rationale="ffmpeg_not_installed".
Insufficient disk space — If render output directory has <1GB free space, abort with error: "Insufficient disk space for render. Require 1GB free, found X MB.". Log to Grimoire.
Invalid template — If script_manifest.channel does not match one of the five templates (PowerScaling / Philosophy / LoreDrop / TrainingArc / LegendsLabs), fall back to "PowerScaling" template (default) and log warning.
Audio sync issue — If voice audio duration does not match expected shot duration (±1s tolerance), stretch or compress audio to fit (via FFmpeg atempo filter), log warning to Grimoire with rationale="audio_resampled_shot_N".
Grimoire logging contract
Every invocation of this skill logs via grimoire-keeper. Fields logged:
stage: "remotion-composer"
input_id: generate_id from input
output_id: render_id from generated output (or null if aborted)
decision: "accept" (success) | "error" (render failed)
rationale: one-line explanation (e.g., "PowerScaling_1080p30_120s", "missing_artifact_shot_2", "remotion_not_installed")
cost_usd: 0.00 (no external API costs; compute is local or via internal render farm)
duration_ms: elapsed time from input receipt to render completion (or abort)
file_size_mb: final MP4 file size (or 0 if aborted)
resolution: "1080x1920" | "2160x3840"
fps: 30 | 60
render_time_sec: wall-clock render time (or 0 if aborted)
Legal firewall
This skill does not generate new content or invoke external APIs. It only composites assets generated upstream by gen-director, which already enforced legal filtering. Intro/outro brand assets (logo, particle animations) are AnimeLegends original IP. No copyrighted material is introduced at composition stage.
Quality gates
Output RemotionComposerOutput has all required fields (render_id, generate_id, topic, channel, video_path, render_stats, timestamp)
render_id is valid UUID v4
video_path file exists on disk and size >0 bytes
render_stats.duration_sec equals intro(2s) + generate_manifest.duration_sec + outro(3s) (±1s tolerance)
render_stats.resolution matches expected format: "1080x1920" or "2160x3840"
render_stats.fps is 30 or 60
render_stats.file_size_mb >0
render_stats.template_used is one of: PowerScaling, Philosophy, LoreDrop, TrainingArc, LegendsLabs
Grimoire log entry created with all required fields
Final MP4 plays back successfully in standard video player (manual verification recommended)
Audio tracks are in sync with video (±0.1s tolerance)
Brand LUT applied (verify color grading matches AnimeLegends aesthetic)
Intro and outro sequences present (intro 0-2s, outro last 3s)
Changelog
0.1.0 — 2026-08-16
Initial release
Five Remotion v4 templates (PowerScaling, Philosophy, LoreDrop, TrainingArc, LegendsLabs)
Resolution progression: 1080p@30fps (Day 1-30) → 4K@60fps (Day 31+)
Brand LUT color grading (+15% contrast, +10% saturation, +8% shadow lift)
Gold particle intro (2s) and brand bumper outro (3s)
Audio mixing with per-mascot reverb/EQ
H.264 codec, CRF 18, slow preset for high quality
Asset loading with missing file handling
Grimoire logging integration with render stats
1 --- 2 name: remotion-composer 3 description: Composites generated assets into final MP4 via Remotion v4. Five templates mapped to five sub-channels (PowerScaling, Philosophy, LoreDrop, TrainingArc, LegendsLabs). Applies brand LUT + gold particle intro + 3s brand bumper outro. 4 --- 5 6 # remotion-composer 7 8 ## Intent 9 10 Sixth stage of the AnimeLegends generative pipeline. Receives artifact paths (generated images, videos, voice audio) from `gen-director`, loads the appropriate Remotion v4 composition template based on channel (PowerScaling / Philosophy / LoreDrop / TrainingArc / LegendsLabs), composites all shots into a single timeline with timing from `ScriptManifest` and `StoryboardManifest`, applies the AnimeLegends brand LUT (color grading), adds gold particle intro animation (2s), appends 3s brand bumper outro with subscribe CTA, renders final MP4 at 1080×1920 vertical format (Day 1-30) or 2160×3840 4K (Day 31+), 30fps (Day 1-30) or 60fps (Day 31+), and outputs the rendered video path for `publish-orchestrator` to distribute. 11 12 ## Inputs 13 14 What this skill expects to receive: 15 16 ```typescript 17 interface RemotionComposerInput { 18 generate_manifest: GenerateManifest; // from gen-director (required) 19 script_manifest: ScriptManifest; // from script-forge (for dialogue timing) 20 storyboard_manifest: StoryboardManifest; // from storyboard-forge (for shot metadata) 21 render_quality?: "preview" | "production"; // default: production 22 output_resolution?: "1080p" | "4K"; // default: 1080p (Day 1-30), 4K (Day 31+) 23 } 24 ``` 25 26 Source of input: `GenerateManifest` from `gen-director`, with references to `ScriptManifest` and `StoryboardManifest` for timing and metadata. 27 28 ## Outputs 29 30 What this skill produces: 31 32 ```typescript 33 interface RemotionComposerOutput { 34 render_id: string; // UUID v4 35 generate_id: string; // parent generate UUID 36 topic: string; // clean topic text 37 channel: string; // PowerScaling | Philosophy | LoreDrop | TrainingArc | LegendsLabs 38 video_path: string; // final rendered MP4 file path 39 render_stats: { 40 resolution: string; // "1080x1920" | "2160x3840" 41 fps: number; // 30 | 60 42 duration_sec: number; // total video duration (original + intro + outro) 43 file_size_mb: number; // final MP4 file size 44 render_time_sec: number; // wall-clock render time 45 template_used: string; // Remotion template name 46 }; 47 timestamp: string; // ISO 8601 48 } 49 ``` 50 51 Where the output goes: passed to `publish-orchestrator` as input (if publishing), logged to `grimoire-keeper`, saved to `renders/` directory. 52 53 ## Core logic / rules 54 55 1. **Template selection** — Map `script_manifest.channel` to Remotion composition template: 56 - **PowerScaling** → `templates/PowerScaling.tsx` (red/gold accent, dynamic energy, comparison overlays) 57 - **Philosophy** → `templates/Philosophy.tsx` (blue/purple gradient, contemplative pacing, symbol overlays) 58 - **LoreDrop** → `templates/LoreDrop.tsx` (green/teal accent, archive aesthetic, timeline graphics) 59 - **TrainingArc** → `templates/TrainingArc.tsx` (orange/warm gradient, journey framing, progress bars) 60 - **LegendsLabs** → `templates/LegendsLabs.tsx` (silver/tech accent, grid overlays, technical diagrams) 61 62 2. **Resolution and FPS determination** — Check render date to determine output specs: 63 - **Day 1-30** (before 2026-09-16): 1080×1920 vertical, 30fps 64 - **Day 31+** (after 2026-09-16): 2160×3840 vertical 4K, 60fps 65 - Respect `output_resolution` override if provided (for testing). 66 67 3. **Asset loading** — For each shot in `generate_manifest.artifacts`: 68 - Load `image_path` (PNG/JPG) as still frame or I-frame for video 69 - Load `video_path` (MP4) if shot has motion 70 - Load `audio_path` (WAV/MP3) voice audio 71 - Verify file exists and is readable; if missing, **skip shot** and log warning to Grimoire. 72 73 4. **Timeline composition** — Arrange shots on Remotion timeline: 74 - **Intro sequence** (0-2s): gold particle burst animation + AnimeLegends logo fade-in 75 - **Shot sequence** (2s to 2s+duration_sec): each shot placed at `shot.start_sec + 2s` (offset by intro) 76 - Layer video (or static image) on video track 77 - Layer voice audio on audio track 78 - Apply shot-specific transitions (crossfade between shots, 0.3s overlap) 79 - **Outro sequence** (2s+duration_sec to 5s+duration_sec): 3s brand bumper with subscribe CTA 80 - Gold particle outro animation 81 - "Follow for more [channel] content" text overlay 82 - AnimeLegends logo hold 83 84 5. **Brand LUT application** — Apply color grading via LUT (look-up table): 85 - **AnimeLegends LUT**: contrast +15%, saturation +10%, shadows lifted +8%, gold particle hue preserved (±5° tolerance) 86 - LUT applied globally to all shots (except intro/outro which have pre-graded assets) 87 88 6. **Audio mixing** — Mix voice audio tracks: 89 - Normalize voice audio to -3dB peak (prevent clipping) 90 - Apply per-mascot reverb/EQ (already in `gen-director` voice files, but re-verify) 91 - Add background music bed (optional, low-level ambient drone at -18dB if available in `assets/music/`) 92 - Master output to -1dB peak 93 94 7. **Remotion render** — Invoke Remotion CLI to render composition: 95 96 ```bash 97 npx remotion render [template] [output_path] \ 98 --props='{"artifacts": [...], "channel": "PowerScaling", ...}' \ 99 --codec=h264 \ 100 --crf=18 \ 101 --preset=slow \ 102 --audio-codec=aac \ 103 --audio-bitrate=192k 104 ``` 105 106 - CRF 18 for high quality (lower = higher quality, 18-23 range recommended) 107 - Preset "slow" for better compression (acceptable for production) 108 - Audio codec AAC at 192kbps (standard for social media) 109 110 8. **Output verification** — After render completes: 111 - Verify `video_path` file exists and size >0 bytes 112 - Verify duration matches expected: `intro(2s) + duration_sec + outro(3s)` 113 - Compute file size in MB 114 - If render failed (file missing or zero size), **abort** and log error to Grimoire. 115 116 9. **Manifest generation** — Populate `RemotionComposerOutput` with all fields, generate UUID for `render_id`, set `timestamp` to ISO 8601 UTC. 117 118 10. **Grimoire logging** — Log invocation to `grimoire-keeper` with `stage="remotion-composer"`, `input_id=generate_id`, `output_id=render_id`, `decision="accept"` (if successful) or `"error"` (if render failed), `rationale` (one-line: template + resolution + render time), `duration_ms`. 119 120 ## Worked examples 121 122 ### Example 1: 30s PowerScaling video, 1080p render 123 124 **Input:** 125 126 ```json 127 { 128 "generate_manifest": { 129 "generate_id": "dddd4444-eeee-5555-ffff-666677778888", 130 "topic": "Why speed doesn't beat power", 131 "duration_sec": 30, 132 "artifacts": [ 133 { "shot_num": 1, "video_path": "/workspace/artifacts/gen/shot_001_video.mp4", "audio_path": "/workspace/artifacts/gen/shot_001_audio.wav" }, 134 { "shot_num": 2, "video_path": "/workspace/artifacts/gen/shot_002_video.mp4", "audio_path": "/workspace/artifacts/gen/shot_002_audio.wav" }, 135 { "shot_num": 3, "video_path": "/workspace/artifacts/gen/shot_003_video.mp4", "audio_path": "/workspace/artifacts/gen/shot_003_audio.wav" } 136 ] 137 }, 138 "script_manifest": { "channel": "PowerScaling" }, 139 "storyboard_manifest": { /* shot metadata */ } 140 } 141 ``` 142 143 **Output:** 144 145 ```json 146 { 147 "render_id": "eeee5555-ffff-6666-aaaa-777788889999", 148 "generate_id": "dddd4444-eeee-5555-ffff-666677778888", 149 "topic": "Why speed doesn't beat power", 150 "channel": "PowerScaling", 151 "video_path": "/workspace/renders/powerscaling_20260816_103000.mp4", 152 "render_stats": { 153 "resolution": "1080x1920", 154 "fps": 30, 155 "duration_sec": 35, 156 "file_size_mb": 18.7, 157 "render_time_sec": 120, 158 "template_used": "PowerScaling" 159 }, 160 "timestamp": "2026-08-16T10:30:00Z" 161 } 162 ``` 163 164 **Why this output:** PowerScaling template selected. 1080p@30fps (Day 1-30 default). 3 shots composited: intro (2s) + 30s content + outro (3s) = 35s total. Render time 120s (4× real-time, typical for Remotion h264 slow preset). File size 18.7 MB (~0.53 MB/s bitrate). 165 166 ### Example 2: 45s Philosophy video, 4K render (Day 31+) 167 168 **Input:** 169 170 ```json 171 { 172 "generate_manifest": { 173 "generate_id": "ffff6666-aaaa-7777-bbbb-888899990000", 174 "topic": "Why Domain is philosophically perfect", 175 "duration_sec": 45, 176 "artifacts": [ /* 5 shots */ ] 177 }, 178 "script_manifest": { "channel": "Philosophy" }, 179 "output_resolution": "4K" 180 } 181 ``` 182 183 **Output:** 184 185 ```json 186 { 187 "render_id": "aaaa7777-bbbb-8888-cccc-999900001111", 188 "generate_id": "ffff6666-aaaa-7777-bbbb-888899990000", 189 "topic": "Why Domain is philosophically perfect", 190 "channel": "Philosophy", 191 "video_path": "/workspace/renders/philosophy_20260916_140000_4k.mp4", 192 "render_stats": { 193 "resolution": "2160x3840", 194 "fps": 60, 195 "duration_sec": 50, 196 "file_size_mb": 67.3, 197 "render_time_sec": 310, 198 "template_used": "Philosophy" 199 }, 200 "timestamp": "2026-09-16T14:00:00Z" 201 } 202 ``` 203 204 **Why this output:** Philosophy template selected. 4K@60fps (Day 31+ or explicit override). 5 shots composited: intro (2s) + 45s content + outro (3s) = 50s total. Render time 310s (~6× real-time for 4K 60fps). File size 67.3 MB (~1.35 MB/s bitrate, higher for 4K). 205 206 ### Example 3: Render failed (missing artifact) 207 208 **Input:** 209 210 ```json 211 { 212 "generate_manifest": { 213 "generate_id": "gggg8888-cccc-9999-dddd-000011112222", 214 "topic": "Test topic", 215 "duration_sec": 30, 216 "artifacts": [ 217 { "shot_num": 1, "video_path": "/workspace/artifacts/gen/shot_001_video.mp4", "audio_path": "/workspace/artifacts/gen/shot_001_audio.wav" }, 218 { "shot_num": 2, "video_path": "/workspace/artifacts/gen/shot_002_MISSING.mp4", "audio_path": "/workspace/artifacts/gen/shot_002_audio.wav" }, 219 { "shot_num": 3, "video_path": "/workspace/artifacts/gen/shot_003_video.mp4", "audio_path": "/workspace/artifacts/gen/shot_003_audio.wav" } 220 ] 221 }, 222 "script_manifest": { "channel": "PowerScaling" } 223 } 224 ``` 225 226 **Output:** 227 228 ```json 229 { 230 "error": "Render failed: missing artifact /workspace/artifacts/gen/shot_002_MISSING.mp4 for shot 2", 231 "stage": "remotion-composer", 232 "decision": "error", 233 "rationale": "missing_artifact_shot_2", 234 "remediation": "Re-run gen-director to regenerate missing shot 2 artifacts." 235 } 236 ``` 237 238 **Why this output:** Shot 2 video file missing. Asset loading step failed. Render aborted. Error logged to Grimoire with specific missing file path. 239 240 ## Failure modes 241 242 - **Missing artifact files** — If any `video_path` or `audio_path` in `generate_manifest.artifacts` does not exist on disk, **skip that shot** and log warning to Grimoire with `rationale="missing_artifact_shot_N"`. If >50% of shots are missing, **abort render** and return error: `"Render failed: too many missing artifacts (X/Y shots)"`. 243 244 - **Remotion render timeout** — If `npx remotion render` command times out (>600s for 1080p, >1200s for 4K), **abort render** and log error: `"Render timeout exceeded"`. User may need to reduce quality preset (slow → medium) or resolution. 245 246 - **Remotion CLI not installed** — If `npx remotion` command not found, **abort** with error: `"Remotion v4 CLI not installed. Run: npm install -g remotion"`. Log to Grimoire with `decision="error"`, `rationale="remotion_not_installed"`. 247 248 - **FFmpeg not installed** — If FFmpeg not available (required by Remotion for video encoding), **abort** with error: `"FFmpeg not installed. Install via system package manager."`. Log to Grimoire with `decision="error"`, `rationale="ffmpeg_not_installed"`. 249 250 - **Insufficient disk space** — If render output directory has <1GB free space, **abort** with error: `"Insufficient disk space for render. Require 1GB free, found X MB."`. Log to Grimoire. 251 252 - **Invalid template** — If `script_manifest.channel` does not match one of the five templates (PowerScaling / Philosophy / LoreDrop / TrainingArc / LegendsLabs), **fall back to "PowerScaling"** template (default) and log warning. 253 254 - **Audio sync issue** — If voice audio duration does not match expected shot duration (±1s tolerance), **stretch or compress audio** to fit (via FFmpeg atempo filter), log warning to Grimoire with `rationale="audio_resampled_shot_N"`. 255 256 ## Grimoire logging contract 257 258 Every invocation of this skill logs via `grimoire-keeper`. Fields logged: 259 260 - `stage`: `"remotion-composer"` 261 - `input_id`: `generate_id` from input 262 - `output_id`: `render_id` from generated output (or `null` if aborted) 263 - `decision`: `"accept"` (success) | `"error"` (render failed) 264 - `rationale`: one-line explanation (e.g., `"PowerScaling_1080p30_120s"`, `"missing_artifact_shot_2"`, `"remotion_not_installed"`) 265 - `cost_usd`: `0.00` (no external API costs; compute is local or via internal render farm) 266 - `duration_ms`: elapsed time from input receipt to render completion (or abort) 267 - `file_size_mb`: final MP4 file size (or `0` if aborted) 268 - `resolution`: `"1080x1920"` | `"2160x3840"` 269 - `fps`: `30` | `60` 270 - `render_time_sec`: wall-clock render time (or `0` if aborted) 271 272 ## Legal firewall 273 274 This skill does not generate new content or invoke external APIs. It only composites assets generated upstream by `gen-director`, which already enforced legal filtering. Intro/outro brand assets (logo, particle animations) are AnimeLegends original IP. No copyrighted material is introduced at composition stage. 275 276 ## Quality gates 277 278 - [ ] Output `RemotionComposerOutput` has all required fields (render_id, generate_id, topic, channel, video_path, render_stats, timestamp) 279 - [ ] `render_id` is valid UUID v4 280 - [ ] `video_path` file exists on disk and size >0 bytes 281 - [ ] `render_stats.duration_sec` equals `intro(2s) + generate_manifest.duration_sec + outro(3s)` (±1s tolerance) 282 - [ ] `render_stats.resolution` matches expected format: "1080x1920" or "2160x3840" 283 - [ ] `render_stats.fps` is 30 or 60 284 - [ ] `render_stats.file_size_mb` >0 285 - [ ] `render_stats.template_used` is one of: PowerScaling, Philosophy, LoreDrop, TrainingArc, LegendsLabs 286 - [ ] Grimoire log entry created with all required fields 287 - [ ] Final MP4 plays back successfully in standard video player (manual verification recommended) 288 - [ ] Audio tracks are in sync with video (±0.1s tolerance) 289 - [ ] Brand LUT applied (verify color grading matches AnimeLegends aesthetic) 290 - [ ] Intro and outro sequences present (intro 0-2s, outro last 3s) 291 292 ## Changelog 293 294 ### 0.1.0 — 2026-08-16 295 296 - Initial release 297 - Five Remotion v4 templates (PowerScaling, Philosophy, LoreDrop, TrainingArc, LegendsLabs) 298 - Resolution progression: 1080p@30fps (Day 1-30) → 4K@60fps (Day 31+) 299 - Brand LUT color grading (+15% contrast, +10% saturation, +8% shadow lift) 300 - Gold particle intro (2s) and brand bumper outro (3s) 301 - Audio mixing with per-mascot reverb/EQ 302 - H.264 codec, CRF 18, slow preset for high quality 303 - Asset loading with missing file handling 304 - Grimoire logging integration with render stats