Skip to content

Keep every chapter mark: fix dropped marks, strip pause markers, cut a segment at each heading - #210

Merged
davior merged 2 commits into
mainfrom
claude/admiring-tesla-breov5
Oct 15, 2026
Merged

davior merged 2 commits into
mainfrom
claude/admiring-tesla-breov5

Conversation

@davior

@davior davior commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Summary

This fixes the last two chapter-mark quirks listed in docs/gecko-video-creator.md §3.2, which completes Phase 0. It also settles open decision 15: a heading partway through a section now starts a segment of its own.

Commit 1: marks that were dropped, and pause markers

A chapter mark can vanish. When headings are not read aloud, a heading takes no time of its own. Its mark was dropped wherever there was no shot for it to start:

  • a quote or code block took over the picture's section (_take_over now keeps the mark);
  • the heading came right before a clip with sound (flush now returns the mark and the clip takes it);
  • it landed on a missing picture, or on an empty continuation after a quote, and that part was then left out (the cleanup now passes the mark on to the next part it keeps);
  • it ended a section that already had a mark. This is a heading above each picture, the most common note shape. The mark now starts the next part.

When two marks land on the same moment, the first is kept, as two headings in a row always have been.

Pause markers leaked into chapter marks. A section's chapter was set after its Shot was built, so __post_init__ never stripped it. The Road [pause:2s] reached the MP4 chapter list and the chapter overlay exactly as typed. Now:

  • the importer takes the mark without its markers;
  • the compiler builds the mark into the shot, so a hand-written or AI-written spec is stripped too;
  • a heading that is nothing but markers is treated as a pause and no longer makes a blank card or chapter entry.

Commit 2: a segment at each heading (open decision 15)

A section was one segment, so it held one mark. With chapter screens off, a second heading inside a section got no chapter entry, and a note of plain text with three headings had one chapter.

Now a heading that comes after something has been said in its section starts a new scene at that point, over the same picture. The new scene carries the heading's chapter and every beat from the heading on, plus transition_in: {style: "none"}.

A heading followed by nothing but other headings before the picture changes opens no segment. It is read (if it's read at all) at the end of the section above, and its mark starts the next segment. So a heading above an image now marks that image.

  • Compiler. It now accepts transition_in with style "none" and sets the new Shot.cut_in. Any other per-segment style is still refused.
  • Renderer. It joins a cut_in shot with a straight cut on every transition path:
    • no dip at either side of the join (build_shot_command(dip_in=, dip_out=));
    • a concat instead of an xfade in the blend stitch (build_xfade_command(cuts=));
    • no overlap eaten in build_timeline, crossfade_total or the estimate.
  • Costs. Each new segment is held for at least min_shot_seconds. A looping clip or a Ken Burns drift restarts at the cut.

Oracle. Both commits' changes were written independently into tests/segment_reference.py, each marked Fixed:. The parity tests therefore still compare two implementations. Two heading-pause tests now expect the heading to open its own shot.

Docs. In §3.2, both quirks are marked fixed, and the heading-above-image entry is updated: the mark now goes to the image, and only where the heading is read is left to decision 5. §7 has a mapping row for the cut, Phase 0 is marked done, and decision 15 is recorded as decided.

Test plan

  • Full backend suite: 1348 passed, 1 skipped.
  • New segmenter cases: dropped marks, markers, and heading cuts (read aloud and not, plain text only, over a silent clip, a heading right above a picture, back-to-back headings). New spec, compiler and route cases, a render-level check that the cut join has no dip, and ffmpeg cases for one-sided dips, the concat join in the xfade stitch, crossfade_total and the timeline.
  • Every note of up to five blocks (headings, marker-only headings, text, images, missing media, silent and sounded clips, quotes, code), under each chapter_screens, read_chapters and narrate_code setting: 374,576 notes, identical between the importer and the oracle.
  • Lost-mark scan over 115,867 notes: 7,203 marks were dropped before. No heading inside a section loses its mark now. The only drops left are a second mark at the same moment, back-to-back headings, and a heading with nothing after it at the end of a note.
  • Real ffmpeg smoke test: one-sided dips encode, and a three-shot dissolve stitch with one straight cut comes out at 8.52 s against the 8.5 s expected.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PwaujUokXQAxnebBArpZuj

With headings not read aloud a heading takes no time of its own, and its
mark was lost wherever it had no shot of its own to start: when a quote or
code block took over the picture's section, right before a clip with
sound, on a missing picture or an empty continuation that was then left
out, and at the end of a section that already had a mark (a heading above
each picture, the commonest shape of note). Each now starts whatever comes
where the heading stands; when two land on the same moment the first is
kept, as with two headings in a row.

A section's chapter was also set after its shot was built, so its
[pause:...] markers reached the MP4 chapter list and the chapter overlay.
The importer now takes the mark without them, the compiler builds it into
the shot so a hand-written spec is stripped too, and a heading that is
nothing but markers no longer makes a blank card or chapter entry.

The same fixes are written independently into tests/segment_reference.py,
so the parity tests still compare two implementations; every note of up to
five blocks under each chapter setting comes out identical both ways.
Docs: both quirks marked fixed, Phase 0 done, and open decision 15 on
marks inside a section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PwaujUokXQAxnebBArpZuj
Open decision 15: a section was one segment and so held one chapter mark,
so with chapter screens off a second heading inside it (## A, text, ## B,
more text, over one picture) got no entry, read aloud or not, and a note
of text alone with three headings had one chapter.

Now a heading after something has been said in its section starts a new
scene there, over the same picture, carrying its chapter and the beats
from the heading on, with transition_in {style: "none"}. A heading with
only headings after it before the picture changes opens nothing: it is
read (if read at all) at the end of the section above, and its mark
starts the next segment, so a heading above an image now marks the image.

The compiler accepts transition_in "none" (any other per-segment style is
still refused) and sets Shot.cut_in. The renderer joins such a shot by a
straight cut on every transition path: no dip at either side of the join,
a concat instead of an xfade in the blend stitch, and no overlap eaten in
the timeline, chapter marks or estimate.

The oracle in tests/segment_reference.py gets the same split, written
independently. Two heading-pause tests now expect the heading to open
its own shot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PwaujUokXQAxnebBArpZuj
@davior davior changed the title Keep chapter marks the video walk used to drop, and strip their pause markers Keep every chapter mark: fix dropped marks, strip pause markers, cut a segment at each heading Oct 15, 2026
@davior
davior marked this pull request as ready for review October 15, 2026 13:39
@davior
davior merged commit 186fd0a into main Oct 15, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants