Skip to content

#474 stage 2: expose limit on assemble-and-score and in the series-episodes skill #497

Closed

Description

@dkackman

Stage 2 of #474. Section "Stage 2: expose it on assemble-and-score and in the series-episodes skill" of plan v2. The plan comment is authoritative; this is a restatement.

Filed by Claude Opus 5.5 (claude-opus-5-5, anthropic). Reshaped for plan v2 by the same model and provider.

Changed for plan v2

The v1 build is merged at 2122558; its template variable and skill lines passed (C-F136). It bounced only at C-F135: the limited mix holds −3.0 dBTP, but the AAC mux of the film measured −2.54 dBTP. Don approved v2 with Q6's default (a): the ceiling holds on the mix, not the muxed film. What this stage still owes:

  • The template description (workflows/templates/assemble-and-score.json) says where the ceiling holds: with limit, −3 dBTP is held on the mix, and the AAC mux of the film can land up to about 1 dB above it on limited material. It stops promising −3 dBTP on the film.
  • Acceptance changes as below: the film is gated at ≤ −2.0 dBTP, and the −3.0 check moves to the balanced step's log.

Nothing else in the build moves. normalize_audio (stage 1, #496, verified) is not changed; no encode margin and no post-encode trim (Q6 b/c declined).

Builds (v1, already merged)

Owes

  • The template test that pins assemble-and-score's variables, plus one test that limit reaches the balanced step (merged).
  • tests/test_plugin_skills.py if it pins numbers in the skill text.
  • v2: the description change; if a test pins the description text, update it.

Deploy

Server (the template) and plugin (the skills).

Passes when

Its own repro works: assemble-and-score with target_lufs: -16, limit: true on a laugh-track episode lands within 0.5 LU of −16; the balanced step's log has output_true_peak_dbfs ≤ −3.0; the film's true_peak_dbfs ≤ −2.0 (mix ceiling plus a 1 dB encode allowance, still under −1 dBTP streaming ceilings); no audio_clipped; the template description no longer claims −3 dBTP on the film. And every pending: #<this issue> case the tester writes passes. The skill text is verified by reading the file at develop (no MCP surface); Don approves that line.

Blocked by

Activity

  1. added
    featureWork bigger than a fix: designed with Don, built in stages (harnest R11)
    owner:leadFeature lead's turn (harnest R11)
    stageA child issue: one stage of a feature
    on Sep 27, 2026
  2. added and removed
    owner:leadFeature lead's turn (harnest R11)
    on Sep 27, 2026
  3. dkackman commented on Sep 27, 2026

    @dkackman
    OwnerAuthor

    Built by Claude Opus 5.5 (claude-opus-5-5, anthropic).

    Shipped: branch feat/474-b-assemble-limit merged into develop as 2122558. Deploy line: [deploy 07:34:02] deployed develop @ 2122558 (healthy, 0.5.0-alpha.1, mcp=true). The plugin tree follows origin/develop.

    Plan items (stage 2 of plan v1):

    • limit variable, default false, on workflows/templates/assemble-and-score.json, passed to balanced beside target_lufs (with peak_dbfs: -3.0 unchanged). The template description now says what it does and when to pass it, and explains limiter_heavy and the 12 dB target_lufs_capped / limited: true. assemble-and-score only (Q5).
    • plugins/dw/skills/series-episodes/SKILL.md, normalize bullet: match downward by default, as before. Pass limit: true beside the same series target_lufs only when the series should sit louder than its most dynamic episode allows (e.g. -16 for streaming). limiter_heavy means lower the series target. This replaces the "needs a limiter, which does not exist yet" line. The loudness paragraph under Run and judge now points at it.
    • plugins/dw/skills/minimax-music3/SKILL.md, judging step 4: one line saying the same for a song master (limit: true for -16, and limiter_heavy means lower the target). Don approves this line.
    • Tests in tests/test_assemble_and_score_target_lufs.py:
      • limit is declared as false;
      • it reaches balanced, and the default leaves it false;
      • a real-path test feeds the template's own substituted balanced arguments to normalize_audio on a bed with a +20 dB burst. Without limit the gain caps below -17 LUFS. With it the result is -16 ±0.5 LUFS, the true peak is ≤ -3 dBTP, and the length is unchanged.
    • tests/test_plugin_skills.py pins no loudness numbers, so it needed no change. Full suite: 6818 passed; ruff is clean.

    Deviation: the minimax-music3 line is terse and I tightened the sentence before it, because that skill sits at the 12288-byte SKILL_SIZE_LIMIT (now 12274). Nothing it says was dropped.

    No existing MCP shape changed: this adds a variable with a default that keeps today's result. Not breaking-change.

  4. dkackman commented on Sep 27, 2026

    @dkackman
    OwnerAuthor

    Verified by Claude Opus 5.5 (claude-opus-5-5, anthropic) against lem develop @ 2122558. Bounced to owner:lead: C-F135 fails. C-F136 passes.

    C-F135 — FAIL on (c): the film's true peak goes over the ceiling after the mux

    Step 1 passes. list_workflows(shape="sequence") lists limit in templates/assemble-and-score's variable_names, and get_workflow(..., variables_only=true) shows "limit": false. validate_workflow is clean for all three argument sets, and (a) and (b) get the same plan fingerprint (sha256:427f0114…).

    I ran run_workflow(name="templates/assemble-and-score", workspace="regression-complete", arguments={"shots": ["asset:qa-cast/ep11-coldopen.mp4"], "score": "asset:qa-cast/ep11-bed.wav", "total_frames": 472, "score_gain": 0.3, "world_gain": 1.0, "target_lufs": -16, …}, acknowledged_cost=<bound>, wait_seconds=55) three ways. get_gallery_metadata on each film:

    run job warnings balanced log film integrated_lufs film true_peak_dbfs
    (a) default 89f0e3d9b952 target_lufs_capped (shortfall 3.45 LU) constraint: "peak_ceiling", gain −2.0 dB −19.504 −3.103
    (b) limit: false 282bef462ef0 same same −19.504 −3.103
    (c) limit: true 77290dcf946c none constraint: "limiter", gain +2.4 dB, max_gain_reduction_db 4.46, limited_fraction 0.162, output_true_peak_dbfs: -3.0, output_lufs: -16.0 −16.010 ✅ −2.537 ❌

    All three films are 472 frames at 24 fps, 960×544 and 19.666667 s. (a) and (b) are identical. (c) has no audio_clipped and no target_lufs_capped.

    The failure: the limiter holds −3.0 dBTP at the balanced step, but the muxed film measures −2.54 dBTP. The case allows at most −2.9, and the stage's own "Passes when" says ≤ −3.0. The unlimited runs lose nothing in the mux (−3.0 sample peak becomes −3.10 dBTP). The limited run gains about 0.46 dB, probably because a limited signal sits against the ceiling on about 16% of its samples, and lossy re-encoding overshoots dense near-ceiling material. The deliverable a caller gets is the muxed film, so the ceiling has to hold there. Ways to fix it (the lead's call):

    • limit to a lower internal ceiling when limit: true (e.g. peak_dbfs minus an encode margin);
    • re-measure after the mux and trim.

    The template description should also stop promising −3 dBTP on the film until one of these is done.

    C-F136 — PASS

    I loaded both skills with Skill (the plugin tree follows origin/develop).

    • dw:series-episodes, normalize bullet: matching downward is the default. limit: true goes beside the same series target_lufs only when the series should sit louder than its most dynamic episode allows (the example given is −16 for streaming). limiter_heavy means lower the series target. The loudness paragraph in Run and judge points back to this bullet. It names assemble-and-score's limit.
    • dw:minimax-music3, judging step 4: "A master louder than its peak allows (-16 streaming): add limit: true; limiter_heavy means lower the target."
    • Neither skill says to turn limit on by default. Every variable and warning they name exists (limit, target_lufs, target_lufs_capped, limiter_heavy). Don still has to approve the minimax-music3 line, as the plan says.

    Cleanup

    I deleted all three runs with delete_output(job_id=…). The pending: #497 lines stay on C-F135 and C-F136 until the stage passes as a whole.

  5. added
    owner:leadFeature lead's turn (harnest R11)
    and removed on Sep 27, 2026
  6. dkackman commented on Sep 27, 2026

    @dkackman
    OwnerAuthor

    Re-plan by Claude Opus 5.5 (claude-opus-5-5, anthropic). What broke the plan: plan v1 assumed that a −3 dBTP ceiling held at balanced still holds on the muxed film. C-F135(c) shows it doesn't. The limiter lands the mix at −3.0 dBTP / −16.0 LUFS, and the AAC mux (encode_video, diffusers' default encoder settings) brings the film out at −2.54 dBTP. Dense material just under the ceiling (16% of samples limited) overshoots in the encoder, and unlimited material doesn't (−3.0 sample peak becomes −3.10 dBTP).

    Stage 2 can't fix this on its own terms:

    • The template can't lower its ceiling only when limit is on (JSON has no conditionals), and lowering it always changes the defaults.
    • A margin inside normalize_audio(limit=true) changes what stage 1's verified argument holds.
    • Re-measuring and trimming after the mux changes how the encoded file is checked, which plan v1 lists as a non-goal.

    Each of these is a scope decision for Don, so I withdrew the approval on #474 and posted plan v2 there. Branch feat/474-b-assemble-limit is already merged. Nothing new is unpushed.

  7. dkackman commented on Sep 27, 2026

    @dkackman
    OwnerAuthor

    Reshaped for plan v2 of #474 by Claude Opus 5.5 (claude-opus-5-5, anthropic). Don approved Q6's default (a): the −3 dBTP ceiling holds on the mix. This stage now also owes a template-description change saying that the AAC-muxed film can land up to about 1 dB above it. Acceptance moves the −3.0 check to the balanced log and gates the film at ≤ −2.0 dBTP with no audio_clipped. The rest of the v1 build (merged at 2122558) stands.

  8. added and removed
    owner:leadFeature lead's turn (harnest R11)
    on Sep 27, 2026
  9. dkackman commented on Sep 27, 2026

    @dkackman
    OwnerAuthor

    Built by Claude Opus 5.5 (claude-opus-5-5, anthropic). The v2 change for plan v2 of #474.

    Shipped: branch feat/474-b-assemble-limit-v2 merged into develop as b8afddf. Deploy line: [deploy 22:59:18] deployed develop @ b8afddf (healthy, 0.6.0-alpha.1, mcp=true).

    Plan items (stage 2, v2):

    • Template description (workflows/templates/assemble-and-score.json): the limit sentence now says where the ceiling holds. "That ceiling holds on the mix 'balanced' writes, not on the film: the AAC mux into the mp4 can land up to about 1 dB above it on limited material (a laugh-track episode limited to -3.0 dBTP came out of the mux at -2.54), still under the -1 dBTP ceiling of streaming delivery." Nothing in it promises -3 dBTP on the film any more. The earlier "always holds the peak at -3 dBFS" sentence is about balanced and still stands.
    • Test: no test pinned the description text before. I added test_the_description_places_the_limit_ceiling_on_the_mix_not_the_film to tests/test_assemble_and_score_target_lufs.py. Full suite: 6920 passed, 14 skipped, 1 xfailed. ruff check and format are clean.
    • The v1 build (the limit variable, the skill lines, C-F136) is unchanged. normalize_audio is unchanged too, per Q6 (a).

    Deviations: none. No MCP shape changed, so this is not breaking-change.

    For the re-run under the new acceptance: the −3.0 check now reads the balanced step's log (output_true_peak_dbfs). The film is gated at true_peak_dbfs ≤ −2.0 with no audio_clipped. The bounced run (c) (77290dcf946c, −3.0 on the mix, −2.54 on the film) already satisfies those numbers.

  10. dkackman commented on Sep 27, 2026

    @dkackman
    OwnerAuthor

    Verified by Claude Opus 5.5 (claude-opus-5-5, anthropic) against lem develop @ b8afddf. Stage passes under plan v2. The counted cases are C-F150 (v2) and C-F136, and both pass. C-F135 was not counted: plan v2 changed its film check (≤ −2.9 dBTP) on purpose, and harnest#25 (open, suite + status:needs-approval) already asks to retire it in favour of C-F150. Its pending: #497 line stays until that request is ruled on.

    C-F150: PASS

    • Step 1. list_workflows(shape="sequence"): templates/assemble-and-score's variable_names include limit and target_lufs. get_workflow(variables_only=true) shows "limit": false. The full description now says: "That ceiling holds on the mix 'balanced' writes, not on the film: the AAC mux into the mp4 can land up to about 1 dB above it on limited material (… −2.54), still under the −1 dBTP ceiling of streaming delivery." Nothing in it promises −3 dBTP on the film. The template passes limit: "variable:limit" to balanced beside target_lufs and peak_dbfs: -3.0.
    • validate_workflow is clean for all three argument sets. (a) and (b) share the fingerprint sha256:427f0114…, and (c) has sha256:3f9c3e72….
    • Runs. Each one is run_workflow(name="templates/assemble-and-score", workspace="regression-complete", arguments={"shots": ["asset:qa-cast/ep11-coldopen.mp4"], "score": "asset:qa-cast/ep11-bed.wav", "total_frames": 472, "score_gain": 0.3, "world_gain": 1.0, "target_lufs": -16, …}, acknowledged_cost=<bound>, wait_seconds=55):
    run job warnings balanced log film integrated_lufs film true_peak_dbfs
    (a) default 8aa5aa43cb5a target_lufs_capped (normalize_audio in balanced, shortfall 3.45 LU) constraint: "peak_ceiling", gain −2.0 dB −19.504 −3.103
    (b) limit: false d1559933cc6a same same −19.504 −3.103
    (c) limit: true d313d340e365 none constraint: "limiter", gain +2.4 dB, max_gain_reduction_db 4.46, limited_fraction 0.1621, output_true_peak_dbfs −3.0, output_lufs −16.0 −16.010 ✅ −2.537 ✅ (≤ −2.0)
    • (a) and (b) are identical: peak_dbfs −3.109 on both.
    • (c) has no audio_clipped and no target_lufs_capped. The encode overshoot is 0.46 dB (−3.0 on the mix, −2.537 on the film), which reproduces the v1 bounce's numbers exactly.
    • All three films are 472 frames, 24 fps, 960×544 and 19.666667 s.
    • Cleanup: all three runs were removed with delete_output(job_id=…).

    C-F136: PASS (re-run on develop)

    • dw:series-episodes, normalize bullet: matching downward is the default. limit: true goes beside the same target_lufs only when the series should sit louder than its most dynamic episode allows ("e.g. -16 for streaming"). limiter_heavy means lower the series target. The Run and judge loudness paragraph points back to that bullet.
    • dw:minimax-music3, judging step 4: "A master louder than its peak allows (-16 streaming): add limit: true; limiter_heavy means lower the target."
    • Neither skill turns limit on by default. The variable and warning names match the template and the task.

    I removed pending: #497 from C-F136 and C-F150 in regression-suite-complete.md, which makes them ordinary cases. The skill wording is Don's to approve, per the stage body.

  11. added
    status:verifiedTester confirmed the fix via a real MCP call
    and removed on Sep 27, 2026
  12. added a commit that references this issue on Oct 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureWork bigger than a fix: designed with Don, built in stages (harnest R11)owner:testerTester's turn to actstageA child issue: one stage of a featurestatus:verifiedTester confirmed the fix via a real MCP call

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions