Documentary Montage hardening plus governance fixes
This commit is contained in:
@@ -6,7 +6,7 @@ The shot list exists. You now have to actually go out and find the
|
||||
clips that fill each slot. This is a two-step operation:
|
||||
|
||||
1. **Build the corpus** — fan the scene director's queries out across
|
||||
Pexels / Archive.org / NASA and download/embed the candidates.
|
||||
Pexels / Archive.org / NASA / Wikimedia / Unsplash and download/embed the candidates.
|
||||
2. **Pick per slot** — run CLIP retrieval against the corpus with each
|
||||
slot description and choose one winner per slot.
|
||||
|
||||
@@ -71,7 +71,7 @@ corpus_builder.execute({
|
||||
{"query": "taxi heavy rain yellow", "kind": "video", "per_source": 6},
|
||||
# ... one entry per unique slot query
|
||||
],
|
||||
"sources": ["pexels", "archive_org"], # from preferred_sources union
|
||||
"sources": ["pexels", "archive_org", "wikimedia"], # from preferred_sources union
|
||||
"filters": {
|
||||
"min_duration": 3,
|
||||
"max_duration": 40,
|
||||
@@ -85,6 +85,9 @@ corpus_builder.execute({
|
||||
|
||||
**Rules for the fan-out:**
|
||||
|
||||
- If the brief pins a source and `corpus_builder.source_provider_menu`
|
||||
says that source is unavailable, STOP and surface it. Do not silently
|
||||
drop to the remaining sources.
|
||||
- Budget the corpus for 8-12x the slot count. A 15-slot montage wants
|
||||
~150 candidates so retrieval has real choices.
|
||||
- `per_source` of 4-8 per query is usually enough. Pushing to 20+
|
||||
@@ -94,6 +97,8 @@ corpus_builder.execute({
|
||||
is slow — don't interleave it with the modern Pexels batch.
|
||||
- If any slot has `nasa` in `preferred_sources`, run ONE small
|
||||
`nasa`-only batch. NASA is slow and its results are niche.
|
||||
- `unsplash` is image-only. Use it as a support source, not the
|
||||
backbone of a motion-led documentary cut.
|
||||
|
||||
### 3. Sanity-Check The Corpus Before Retrieval
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ The output is a single mp4 plus a `render_report` artifact.
|
||||
| Schema | `schemas/artifacts/render_report.schema.json` | Artifact validation |
|
||||
| Prior artifact | `state.artifacts["edit"]["edit_decisions"]` | Cuts, transitions, music, metadata hints |
|
||||
| Prior artifact | `state.artifacts["assets"]["asset_manifest"]` | File paths, durations, providers |
|
||||
| Tool | `video_compose` (FFmpeg + Remotion) | Primary render engine |
|
||||
| Tool | `video_compose` (Remotion-first + FFmpeg fallback) | Primary render engine |
|
||||
| Tool | `audio_mixer` | Music fade, silence window, L-cuts |
|
||||
| Tool (optional) | `color_grade` | Uniform LUT across mixed-era clips |
|
||||
| Tool (optional) | `video_trimmer`, `video_stitch` | Lower-level helpers if needed |
|
||||
@@ -47,11 +47,16 @@ If the brief said "no narration" and a narration track somehow
|
||||
appeared in the edit, STOP and ask. Do not render over a contract
|
||||
violation.
|
||||
|
||||
Also confirm the render engine you intend to use is actually
|
||||
available — `video_compose` in FFmpeg-only mode is fine for this
|
||||
pipeline (the whole piece is footage-led, no Remotion scenes needed
|
||||
unless the user asked for title cards). FFmpeg alone can render this
|
||||
pipeline end to end.
|
||||
Also confirm that `edit_decisions.renderer_family` is locked to
|
||||
`documentary-montage` and that the chosen render engine preserves that
|
||||
decision. For this repo's governance model, `video_compose` is
|
||||
Remotion-first on `operation="render"`, even for footage-led pieces.
|
||||
|
||||
- If Remotion is available, use the normal `render` path and keep the
|
||||
approved renderer family.
|
||||
- If Remotion is unavailable, do NOT quietly drop to FFmpeg. Surface
|
||||
the engine change and get approval before using a lower-level
|
||||
FFmpeg-only path.
|
||||
|
||||
### 1. Resolve The Canvas
|
||||
|
||||
@@ -83,32 +88,8 @@ For a pipeline this simple, the cleanest path is:
|
||||
video_compose.execute({
|
||||
"operation": "render",
|
||||
"output_path": "projects/<name>/renders/final.mp4",
|
||||
"canvas": { "width": 1920, "height": 1080, "fps": 24 },
|
||||
"cuts": [
|
||||
{
|
||||
"source": "<resolved file path>",
|
||||
"in": 1.2,
|
||||
"out": 5.2,
|
||||
"scale": "fit_canvas_center_crop",
|
||||
"transition_in": "fade",
|
||||
"transition_in_duration": 0.8,
|
||||
},
|
||||
# ... more cuts
|
||||
],
|
||||
"audio": {
|
||||
"music_path": "<resolved music path>",
|
||||
"music_volume": 0.7,
|
||||
"music_fade_in": 1.0,
|
||||
"music_fade_out": 4.0,
|
||||
"silence_windows": [{"start": 54.0, "end": 56.0}],
|
||||
"sfx_layers": [
|
||||
{"source": "<rain carry clip>", "start": 20.8, "duration": 1.2, "volume": 0.6}
|
||||
],
|
||||
},
|
||||
"frame_treatment": {
|
||||
"lut_path": "styles/luts/warm_film_100.cube",
|
||||
"letterbox": "2.35:1"
|
||||
}
|
||||
"edit_decisions": edit_decisions_with_renderer_family,
|
||||
"asset_manifest": asset_manifest,
|
||||
})
|
||||
```
|
||||
|
||||
@@ -116,6 +97,9 @@ The exact field names come from the live `video_compose` schema at
|
||||
render time — consult the tool's `agent_skills` if available before
|
||||
writing the call. Do not invent parameters.
|
||||
|
||||
`edit_decisions_with_renderer_family` means the normal edit artifact
|
||||
with `renderer_family = "documentary-montage"` preserved intact.
|
||||
|
||||
### 3. Apply Grade Via LUT, Not Per Clip
|
||||
|
||||
Read `edit_decisions.metadata.grade_profile`. Map it to a LUT file:
|
||||
@@ -163,8 +147,8 @@ Recommended encoder settings for doc montage:
|
||||
| Audio codec | `aac` | Universal |
|
||||
| Audio bitrate | `192k` | Music-bed friendly |
|
||||
|
||||
If the source clips are 30fps and the canvas is 24fps, let FFmpeg
|
||||
drop frames evenly — don't blend. Motion interpolation on
|
||||
If the source clips are 30fps and the canvas is 24fps, let the render
|
||||
pipeline drop frames evenly — don't blend. Motion interpolation on
|
||||
mixed-source footage looks awful.
|
||||
|
||||
### 6. Post-Render Verification
|
||||
@@ -210,7 +194,7 @@ Record verifications in `render_report.verification_notes`.
|
||||
"Silence window 54-56s confirmed (music -60dB)",
|
||||
"Last frame fades to black at 89.0s"
|
||||
],
|
||||
"render_grammar": "cinematic-trailer",
|
||||
"render_grammar": "documentary-montage",
|
||||
"metadata": {
|
||||
"pipeline": "documentary-montage",
|
||||
"canvas": { "width": 1920, "height": 1080 },
|
||||
@@ -247,10 +231,9 @@ Record verifications in `render_report.verification_notes`.
|
||||
- **Per-clip color grading.** One LUT across the whole piece. Do
|
||||
not try to balance each clip individually — it takes 10x the time
|
||||
and makes the register LESS consistent, not more.
|
||||
- **Quiet render engine swap.** If `video_compose` routes through
|
||||
Remotion for some reason and the aesthetic changes, stop and
|
||||
surface. This pipeline is FFmpeg-friendly and shouldn't need
|
||||
Remotion unless the user asked for title cards.
|
||||
- **Quiet FFmpeg downgrade.** If Remotion is blocked and you route to
|
||||
FFmpeg without surfacing it, you've changed the approved render path.
|
||||
Stop and surface that downgrade before rendering.
|
||||
- **Overriding edit decisions at render time.** If you find yourself
|
||||
adjusting volumes, fades, or trims in the render call, you're
|
||||
editing during compose. Go back to the edit stage, fix the
|
||||
|
||||
@@ -34,6 +34,10 @@ The enemy is "slideshow" — a sequence of clips played back-to-back
|
||||
with the same hold length and no sound design. If it feels like a
|
||||
slideshow, the edit has failed, regardless of how good the clips are.
|
||||
|
||||
This stage also locks the render grammar. For documentary montage,
|
||||
set `renderer_family` to `documentary-montage` so compose stays on the
|
||||
approved Remotion-first path.
|
||||
|
||||
## Process
|
||||
|
||||
### 0. Guardrails — No Silent Major Changes
|
||||
@@ -228,6 +232,7 @@ Canonical shape for this pipeline:
|
||||
```json
|
||||
{
|
||||
"version": "1.0",
|
||||
"renderer_family": "documentary-montage",
|
||||
"cuts": [
|
||||
{
|
||||
"id": "cut_01",
|
||||
@@ -284,6 +289,7 @@ Canonical shape for this pipeline:
|
||||
|
||||
- `sum(out - in for cut in cuts)` is within ±10% of
|
||||
`brief.duration_seconds`.
|
||||
- `renderer_family = "documentary-montage"` is present and unchanged.
|
||||
- Hero slots have the longest holds.
|
||||
- No two adjacent cuts share subject AND scale.
|
||||
- The transition vocabulary is at most 4 distinct values.
|
||||
|
||||
@@ -55,7 +55,7 @@ before starting the stage.
|
||||
|
||||
| Tool | Role |
|
||||
|------|------|
|
||||
| `corpus_builder` | Fans out across Pexels/Archive.org/NASA, downloads + embeds + indexes |
|
||||
| `corpus_builder` | Fans out across Pexels/Archive.org/NASA/Wikimedia/Unsplash, downloads + embeds + indexes |
|
||||
| `clip_search` | Ranks clips for a slot, finds similar sets, diversifies selections |
|
||||
| `video_compose` / Remotion | Renders the final timeline |
|
||||
|
||||
|
||||
@@ -113,8 +113,9 @@ open for the scene director to decide per slot.
|
||||
- Tone is ONE register from the fixed list.
|
||||
- Duration and shape are concrete numbers / enum values.
|
||||
- Music source is named OR the brief explicitly says "no music".
|
||||
- Sources list is non-empty and at least one is `available` per the
|
||||
tool registry.
|
||||
- Sources list is non-empty and at least one requested source is
|
||||
`available` per `corpus_builder.source_provider_menu` surfaced in
|
||||
preflight.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
The brief exists. You now have to turn a thematic question into a
|
||||
concrete list of SLOTS the retrieval layer can fill. Each slot is an
|
||||
intention ("a silhouette at a doorway at dusk") plus the queries that
|
||||
will find it in the real world (Pexels/Archive.org/NASA).
|
||||
will find it in the real world (Pexels/Archive.org/NASA/Wikimedia/Unsplash).
|
||||
|
||||
This is the most creative stage in the pipeline. Retrieval is only as
|
||||
good as the slot descriptions you write.
|
||||
@@ -147,6 +147,8 @@ based on what footage lives where:
|
||||
| `pexels` | Modern HD footage, clean shots, people, cities, nature | Default for modern/any era |
|
||||
| `archive_org` | Prelinger home movies, mid-century educational film, 1940s-1980s texture | Vintage, wry, dreamlike, anything nostalgic |
|
||||
| `nasa` | Earth-from-orbit, astronomy, flight, scale imagery | Reverent, anything about scale, space, planet, flight |
|
||||
| `wikimedia` | Commons photos and CC video, civic/documentary/public-event coverage | Public spaces, landmarks, protests, city texture, educational footage |
|
||||
| `unsplash` | Polished editorial stills, lifestyle, product-adjacent photography | Modern still-image support shots when motion footage is thin |
|
||||
|
||||
If `era_mix = "vintage"`, bias slots toward `archive_org` and write
|
||||
queries in period-appropriate vocabulary ("commuter", "housewife",
|
||||
|
||||
Reference in New Issue
Block a user