| name | generate-edit-decision-list |
| description | turn a creator-grammar archetype + script/transcript + scene table into a validated edl.json — the machine-readable timeline that render-edl executes. supports generative mode (clips to be created) and conform mode (recut of existing footage). use whenever you need a concrete, validated cut plan before spending AI generation or editing credits. |
generate-edit-decision-list
Purpose
This is the editorial brain of the creator-grammar pipeline. It transforms:
- a chosen archetype (from
references/archetypes.json)
- a script or transcript with word-timestamps
- an optional storyboard scene table or shot-list JSON
- an optional beat-grid JSON
into a concrete, validated edl.json — the Edit Decision List defined in
references/edl-schema.md. The EDL encodes every cut point, role, transition,
caption cue, audio beat, and pacing-curve metric for the video. Downstream
steps (render-edl, add-zoom-cuts, synthesize-sfx-ffmpeg, add-captions-burn)
consume it without further planning decisions.
Two source modes:
- generative — timeline clips are scenes still to be created. The EDL records
a generation spec per clip that the orchestrator passes to image/video gen atoms.
- conform — timeline clips are time-ranges of existing video files. The EDL
records
source.file, source.in, source.out per clip. Used to recut an
existing video to creator grammar.
Both modes produce the same schema; render-edl executes either.
Inputs
| Input | Required | Notes |
|---|
archetype | yes | one of the 8 IDs in references/archetypes.json |
script_source | one of the two | path to a script-locked.md markdown file + word_timestamps_path JSON, OR |
words_json | one of the two | path to a [{text, start, end}] word-timestamps JSON (from transcribe-audio or similar) |
scene_table | no | path to a storyboard scene table JSON or shot-list JSON ([{scene_id, description, duration_s, ...}]). If omitted, clips are derived from VO segments. |
beat_grid | no | path to a beat-grid.json (from extract-beat-grid). Required only if archetype has beat_sync: true. |
target_duration | yes | target video runtime in seconds |
source_mode | yes | "generative" or "conform" |
source_files | conform mode only | list of source video file paths the conform clips are drawn from |
output_path | yes | where to write edl.json |
Workflow
This is an LLM-executed planning workflow. Follow these steps in order.
Step 1 — Load archetype parameters
Read references/archetypes.json and extract the record for archetype. Note
audio_mode, caption_family, caption_preset, cuts_per_10s, payoff_hold,
transition_default, and beat_sync.
Step 2 — Compute the pacing curve
Run scripts/edl_pacing.py to derive the segment boundaries and target cut counts:
python3 skills/atoms/planning/generate-edit-decision-list/scripts/edl_pacing.py \
--archetype <archetype> \
--target-duration <target_duration>
Record hook_end_s, payoff_start_s, payoff_end_s, tail_start_s,
target_cuts, mean_shot_len_s, and payoff_hold_dur_s — you will use these
in every subsequent step.
Step 3 — Lay out the timeline clips
Build the ordered timeline[] array honoring the pacing targets:
Generative mode:
- Use the scene table as the primary source of clips (one clip per scene or
sub-scene). If no scene table is provided, derive clips from VO phrase
boundaries in the word-timestamps.
- Assign
role to each clip:
- The first clip is always
"hook".
- Clips in the hook segment (before
hook_end_s) use "hook".
- Clips covering feature narration use
"feature".
- B-roll cuts away from main VO use
"b-roll".
- Mark exactly one clip
"payoff-hold" (see Step 4).
- The final clip(s) after
tail_start_s use "feature" or "establishing".
- Set
transition_in per clip using the archetype's transition_default as the
base, then upgrade specific boundaries per the creator grammar:
silence-masked-cut on the biggest edit (ideally the cut into the payoff).
jump-cut on breath/pause removal (gaps > 0.35s in the word-timestamps).
beat-synced-cut if beat_sync: true and a beat-grid is provided.
b-roll-insert when cutting away from VO.
- Fill in
source.spec.description per clip from the scene table or from the
VO phrase that clip covers. Write in camera-language format:
"<shot-size>, <subject>, <action>" (e.g. "ECU, spray nozzle, liquid emerging").
- Set
shot.size and shot.movement per clip following the WS→MS→CU/ECU
cycling pattern from CREATOR_GRAMMAR.md §5.
- Set
target_in and target_out so clips are contiguous (no gaps, no overlaps)
and the final clip's target_out is within ±0.5s of target_duration.
Conform mode:
- Load each source file's word-timestamps to map speech to time.
- Identify the strongest claim (the most direct, benefit-forward sentence) in the
transcript and move it to position 0.0–
hook_end_s as the hook clip, even if
it appeared later in the original recording.
- Remove inter-word and inter-sentence gaps > 0.35s by splitting at those
boundaries: each contiguous speech run becomes a clip with
transition_in: "jump-cut".
- Record
source.mode: "file", source.file, source.in, source.out for each clip.
- Keep the same role,
target_in/target_out, and pacing rules as generative mode.
Step 4 — Mark the payoff-hold clip
Identify the clip that represents the hero moment: the product working, the key
reveal, the strongest visual proof of the claim. This clip should fall in the
payoff window (payoff_start_s to payoff_end_s).
- Set its
role to "payoff-hold".
- Set its duration to approximately
payoff_hold_dur_s (from Step 2), which is
mean_shot_len_s × payoff_hold.multiplier from the archetype. Acceptable range
is 2.0×–4.0× the mean shot length.
- Record its
id in grammar.payoff_hold_id.
- Set
transition_in: "silence-masked-cut" on this clip (the biggest cut is
hidden in the music gap).
Step 5 — Place caption cues
Use the archetype's caption_family to decide caption style:
karaoke-pop family (speech-only or vo+music archetypes):
- Generate one caption cue per word or word-group from the word-timestamps.
- Each cue:
{ "text": "<word(s)>", "start": <master_s>, "end": <master_s>, "preset": "<caption_preset>" }.
- For conform mode, map source-file word timestamps to master-timeline positions.
- Suppress captions on clips that have their own on-screen text (e.g. hyperframes,
end cards, title cards). Add a
notes comment on those clips.
static-title-card family (music-only archetypes):
- Place one caption cue on the hook clip (or a dedicated title-card clip) that
holds for the clip's full duration. This is the single on-screen claim.
- Do not place per-word cues.
Set grammar.caption_family and grammar.caption_preset from the archetype.
Step 6 — Snap cut points to beat grid (when applicable)
If beat_sync: true for the archetype AND a beat-grid file is provided:
- For each clip boundary in the body and tail segments, find the nearest bar
boundary in the beat-grid.
- Snap
target_in to that bar boundary if within ±150ms (a deliberate half-beat
tolerance — do not force snapping that would break a narration phrase).
- Set
transition_in: "beat-synced-cut" on those clips.
- The hook clip's first cut should land on a downbeat.
Step 7 — Place silence beats and SFX
Silence beats (audio_beats[]):
- Place one silence beat immediately before the payoff-hold clip (the
at time
is payoff_hold.target_in - 0.10s, duration 0.25–0.6s from the archetype's
characteristic gap).
- For
broll-narrative-essay: place one additional silence beat at the
structural pivot between acts (~50% of the video).
SFX on cuts (clip.sfx[]):
- Add
{ "type": "whoosh", "at": <target_in> } on feature-flash cuts in the
hook and body segments.
- Add
{ "type": "tap", "at": <target_in> } on match-cuts or zoom-punch cuts.
- Do not add SFX to the payoff-hold clip or tail clips.
Step 8 — Assemble the EDL object
Write the complete edl.json per references/edl-schema.md:
meta — archetype, target_duration, aspect, fps, audio_mode, source_mode, project_dir
grammar — hook_type, caption_family, caption_preset, pacing_profile,
payoff_hold_id, loop
audio — music, beat_grid, vo, ducking, music_drop_s (set to payoff_hold.target_in)
timeline — all clips from Step 3 + Step 4
audio_beats — from Step 7
pacing_curve — fill from pacing script output and actual clip durations:
cuts_per_10s: array of cut density per 10s window
mean_shot_len: total_duration / number_of_clips
payoff_hold_ratio: payoff_hold_duration / mean_shot_len
segments: hook_end_s, payoff_start_s, tail_start_s
Step 9 — Validate
Run the validator before reporting done:
python3 skills/atoms/planning/generate-edit-decision-list/scripts/edl_validate.py <output_path>
If the validator reports errors, fix each one and re-run. Do not deliver an
invalid EDL. All nine rules in references/edl-schema.md §"Validation rules"
must pass.
Output
edl.json at output_path — validated, conforming to references/edl-schema.md.
- Optionally a brief
planning-notes.md in the same folder with the key editorial
decisions (archetype rationale, payoff clip choice, beat-sync adjustments).
The EDL schema is fully documented in references/edl-schema.md. The pacing_curve
section is derived from the pacing script and actual clip durations.
Quality Checks
scripts/edl_validate.py exits 0 (all nine rules pass).
- Exactly one clip has
role: "payoff-hold" and its duration is 2–4× mean_shot_len.
- All clips are contiguous:
timeline[N].target_out == timeline[N+1].target_in within
±1 frame (33ms at 30fps).
- Last clip
target_out is within ±0.5s of meta.target_duration.
- For conform mode: every
source.file path exists on disk.
- For beat-synced archetypes with a beat-grid: every
beat-synced-cut clip's
target_in is within ±50ms of a grid boundary.
- No karaoke-pop captions in a music-only EDL.
grammar.caption_preset is a known preset in caption-style-presets.json.
meta.archetype is one of the 8 IDs in archetypes.json.
Failure Modes
- No word-timestamps provided for a karaoke-pop archetype — cannot place
per-word caption cues. Request the word-timestamps JSON from
transcribe-audio
or similar before running.
- Scene table missing for generative mode — fall back to VO phrase boundaries
to derive clips, but flag in
planning-notes.md that visual descriptions are
placeholder-level and need review before generation.
- No strongest claim identifiable in conform mode — log a warning in
planning-notes.md and keep the original order; do not silently reorder.
- Beat-grid missing for a
beat_sync: true archetype — proceed without
beat snapping and add a warning. Record audio.beat_grid: null.
- Payoff window is too short for the archetype's payoff multiplier — log the
shortfall in
planning-notes.md and use the longest available shot in the
window.
- Validator exits non-zero — do not deliver the EDL. Fix each reported error
and re-validate. Common fixes: reorder clips by
target_in; adjust the last
clip's target_out to match meta.target_duration; confirm exactly one
payoff-hold clip.