Skip to content

#1199 stage 10: export_timeline - an OpenTimelineIO bundle of a joined piece #1211

Open

Description

@dkackman

Stage of #1199, filed by model claude-opus-5-5 via provider anthropic. Already
built on feat/long-form-media (merged 3f8ed222). The authoritative spec is the
plan (docs/superpowers/plans/2026-11-03-long-form-media.md, task named below) and
the design record. If they differ, the design record says what shipped.

Built: the export_timeline task (dw/tasks/timeline_export.py; 1e77425,
75423d4, 224fb27). video (a stored file) plus optional sources and shots give
an .otioz (application/x-otioz) with one clip per shot, gaps filled, and an audio
track when the file has one. Paths are validated, and member names are
media/<basename>.

Observe over MCP:

  • run_workflow an inline workflow: templates/assemble-and-score (or any joined
    piece), then an export_timeline task step on its output.
  • download_output the .otioz and unzip it.

Acceptance intent:

  • The bundle's timeline has one clip per shot, in order, with durations that sum to
    the piece.
  • The media are inside the bundle.
  • An in-memory clip is refused.
  • A shot list that does not match the video is refused (224fb27).

Resolve 21 imports the bundle (Don, 2026-11-06). The Mac App Store build is sandboxed:
from ~/Movies/DaVinci Resolve/ it works, from ~/Desktop it silently does nothing.
That is not a tester case.

Owes: nothing. Deploy: done at 3f8ed222.

verify: run every pending: #1211 case.

Activity

  1. dkackman commented on Nov 7, 2026

    @dkackman
    OwnerAuthor

    Verify of #1211 by tester claude-opus-5-5 via anthropic, against lem develop @ 85bcd33. Bounced to the lead: one of the four acceptance points fails.

    The suites have no pending: #1211 cases (I grepped every regression-suite-*.md), so I checked the issue's acceptance intent directly. The input was the 4-shot joined H3 take output:templates/minimax/chained-segments/20261107-145804-eb6cf5f0/final/MiniMaxH3I2VChained-chained_keyframe_to_video_audio.0-0.0.mp4. It has 430 frames at 24 fps and stereo audio. get_gallery_metadata lists its media.shots as 124 + 102 + 102 + 102 frames. Every run below was an inline one-step export_timeline workflow with result.content_type: application/x-otioz, run in workspace="default".

    # export_timeline arguments Result Expected
    1 video only (shots from the manifest), name: "t1211" succeeded, .otioz 3.9 MB ok
    2 video: "previous_result:frames" (the output of a video_frames step) failed: the video must be a stored video (asset:, output: or a path) - the bundle carries the file itself, so a clip still in memory has to be saved first, not FittedVideo ok, refused
    3 shots: [{name:a,start_frame:0,num_frames:124},{name:b,start_frame:124,num_frames:400}]. The shots total 524 frames against a 430-frame video. succeeded, wrote a 3.9 MB .otioz, no warnings in job.warnings or get_job_events should be refused
    4 shots: [{name:a,start_frame:0,num_frames:124},{name:b,start_frame:600,num_frames:100}]. Shot b starts 170 frames past the end of the video. succeeded, no warnings should be refused
    5 sources: 2 segment files for the 4 manifest shots failed: sources names 2 files for 4 shots - give one per shot, in order ok, refused

    What fails: "A shot list that does not match the video is refused (224fb27)". The task checks the number of sources against the number of shots (run 5), but it doesn't check a caller's shots against the video's length. A shot can overrun the end (run 3) or start past it (run 4), and the task still writes a bundle without a word. I used the same record keys that media.shots reports (name, start_frame, num_frames). If shots expects a different key shape, the task should refuse unknown records rather than accept them.

    What I couldn't check over MCP: "one clip per shot, in order, with durations that sum to the piece" and "the media are inside the bundle". On this endpoint download_output saves on the server, get_output_text refuses the bundle (it reports it as application/octet-stream, not text), and get_gallery_metadata returns media: null for it. No consumer tool shows what is inside the .otioz. The 3.9 MB size is close to the 4.1 MB source, which suggests the media is embedded, but that is an inference, not a check. Two ways to make this verifiable: a timeline summary on the job or in get_gallery_metadata (clip names and durations, member list), or a regression case in a later stage that reads one.

    Cleanup: all five runs are deleted.

  2. added
    owner:leadFeature lead's turn (harnest R11)
    owner:donParked for human decision
    status:needs-approvalParked pending a written proposal + human approval
    and removed
    owner:leadFeature lead's turn (harnest R11)
    on Nov 7, 2026
  3. dkackman commented on Nov 7, 2026

    @dkackman
    OwnerAuthor

    unstrand: Don — this is a stage of #1199 (its body says "Stage of #1199"), but GitHub has no sub-issue parent link on it, so lib/classify.jq reads "stage with no parent" and no queue picks it up. Labels alone can't fix that: the link needs a sub-issue write, which this loop can't make. Separately, the tester bounced it to the lead with a failed acceptance point, and the parent #1199 sits with owner:tester + status:fixed-pending-verify, so even linked it would wait on #1199 rather than reach a lead build.

    Readings:

    Same call for #1205, #1206, #1207, #1208, #1209, #1211 and #1214.

    Unstranded by model claude-opus-5-5 via provider anthropic.

    Decision for Don: do the bounced #1199 stages go back to the lead (link them under #1199) or to the implementer loop as bugs?
    Kind: record Default: reading A: link each as a sub-issue of #1199, return #1199 and the stages to owner:lead
    If otherwise: reading B: relabel each bug + owner:implementer, no status, with a backend: label Reversible: yes

  4. added
    bugSomething isn't working
    and removed
    owner:donParked for human decision
    status:needs-approvalParked pending a written proposal + human approval
    stageA child issue: one stage of a feature
    on Nov 7, 2026
  5. dkackman commented on Nov 7, 2026

    @dkackman
    OwnerAuthor

    Don's ruling on the unstrand question: reading B. The bounce goes to the fix loop as a plain bug, not stage rework. Relabelled bug + owner:implementer + backend:shared, with no status and no stage. The target:lem claim stays. The tester's bounce comment above is the spec. If it hides a spec question (whether the threshold or the acceptance claim is wrong, or whether an effect is intended), settle it from the design record and the bench records, and escalate only if those don't answer it.

    — recorded from Don's Claude Code session (model claude-opus-5-5 via provider anthropic)

  6. dkackman commented on Nov 7, 2026

    @dkackman
    OwnerAuthor

    triage: work — export_timeline accepts a shot list totalling 524 frames on a 430-frame video; the 224fb27 mismatch check does not fire. Self-contained in dw/tasks/timeline_export.py. Triaged by model claude-opus-5-5 via provider anthropic.

  7. added a commit that references this issue on Nov 7, 2026
  8. dkackman commented on Nov 7, 2026

    @dkackman
    OwnerAuthor

    Fix by implementer claude-sonnet-5-5 via anthropic. export_timeline (video mode) now refuses a shot list that runs past the video. Your run 3 (shots 0-124 and 124-524 on a 430-frame video) and run 4 (shot starting at 600) now fail with shot 'b' runs to frame N (...) but the video has only 430 frames - the shots must fit inside the video, where before both wrote a bundle. Valid shots, the manifest default and the overlap and sources count checks are unchanged. The check applies to the video-mode layout only: in sources mode each clip uses its own file's length, so the shot spans there are names only. Commit 2fc7c80 on develop, deployed to lem 19:40 (healthy).

    Not changed: the tester's other note, that no consumer tool shows what is inside the .otioz. That is a separate visibility gap and I did not address it here.

  9. dkackman commented on Nov 7, 2026

    @dkackman
    OwnerAuthor

    Architecture review queued by the loop driver: this hand-off changed dw/ or dw_mcp/ on develop (1ee08ea07b..2fc7c8010a: dw/tasks/timeline_export.py). The reviewer checks it against docs/ARCHITECTURE.md before the tester verifies.

  10. dkackman commented on Nov 7, 2026

    @dkackman
    OwnerAuthor

    Architecture review of 2fc7c80 (1ee08ea..2fc7c80, dw/tasks/timeline_export.py, the only file touched) by reviewer claude-opus-5-5 via anthropic: pass, no findings. Read against docs/ARCHITECTURE.md and stage-c-guardrails.md item 3. Second owner: the new bounds check (shot start_frame + num_frames <= the video's frame_count) sits next to this module's own _refuse_overlaps. No map row assigns caller-shot-list validation to another module: the 'Shots in a joined video' row (dw/shots.py) covers AudioVideo constructors and measured sample spans, and dw/shots.py has no bounds helper. The frame count comes from the existing _facts(path) call, so the change adds no media decode. Build vs buy: it is integer comparison, so there is nothing a pyproject dependency would replace. Map: no new module or owner, so no row is needed. Context: no CLAUDE.md change. The issue has no arch-approved label, and nothing needed waiving. Left with owner:tester + status:fixed-pending-verify for the MCP verify.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions