Sequences (ordered shots = scene + in/out + transition) with full validation,
CRUD, and a client-driven film render the server orchestrates: browser renders
each shot through the existing /render/* endpoints, server ffmpeg-concatenates
in order with cuts or xfade dissolves, muxes a film-level audio bed, reaps
after 48h. render.js refactored so there is one frame-post path in the repo
(captureFrames/withRenderSize/begin/end/poll); new web/film.js drives the loop.
Also fixes a pre-existing mux bug: no apad meant short audio truncated PICTURE.
Orchestrator: deploy compose/script now carry SCENEGOD_SEQUENCES + SCENEGOD_FILMS
volumes so shot lists survive a rebuild.
Verified live: demo-two-shot renders one 11.500s mp4 with a genuinely
compositing dissolve; a film built from the M7 templates (street wide ->
dissolve -> closer -> cut -> record store) renders 11.800s at 960x540.
15 test groups green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
templates/ ships with the repo (two-hander-golden-hour, record-store,
empty-stage), GET /templates + /templates/{name} behind the same slug and
path-traversal guards as /scenes; presentation metadata rides in a template
key that validate_scene ignores and toJSON drops, so loading then saving
yields plain scene JSON.
Test group asserts the three list in order, round-trip, traversal/unknown
rejected, write verbs refused, and every template passes validate_scene with
every referenced asset present on disk - the check that stops a rotten
template shipping. 10 groups green.
Verified live: two-hander loads 7 entities, 8s, both performers animating,
cut fires cam1 to cam2 at 4.5s; other two load clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GET /rhubarb?path= -> rhubarb -f json -> mouthCues for Lane A's viseme lane;
503 when the binary is absent (not installed on ultra yet). Path-guarded.
test_server.py: +503 gating assert (9 groups green).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>