Repository navigation
#602 stage B: rewire upscale-clip and refine-clip through fit/restore #632
Copy link
Copy link
Closed
Labels
featureWork bigger than a fix: designed with Don, built in stages (harnest R11)Work bigger than a fix: designed with Don, built in stages (harnest R11)owner:testerTester's turn to actTester's turn to actstageA child issue: one stage of a featureA child issue: one stage of a featurestatus:verifiedTester confirmed the fix via a real MCP callTester confirmed the fix via a real MCP call
Description
Stage B of #602. Section Stages → Stage B: rewire upscale-clip and refine-clip of plan v4; the plan comment stays authoritative, including The design, Templates (stage B), Risks and Decisions. Needs stage A (#631, verified).
Filed by the lead agent, model claude-opus-5-5 via anthropic. Reshaped for plan v4 (reconciled 2026-10-06 by the same model): v2's ref_width/ref_height breaking change is gone (Q2′). upscale-clip keeps width/height as the output size, and the stage now also changes fit_to_model itself: a downscale argument, and video as an fps-carrying float32 ndarray. No breaking-change.
What it builds
- In
dw/tasks/fit.py:fit_to_modelgainsdownscale: an optional positive integer, default 1. It fits intowidth/downscale × height/downscaleand records that as the model size. It refuses, naming both values, whenwidthorheightisn't divisible by it. Itsget_taskdescription says so.fit_to_model'svideobecomes a float32numpy.ndarraysubclass carrying.fps(indw/media_types.py, alongsideAudioVideo), in place of anAudioVideo. Thenprevious_result:fit.videofeeds a pipeline argument or a condition'sframesdirectly.- Unit tests for both. One realizes
previous_result:fit.videointo a pipeline's arguments and a condition'sframesthrough the real argument path, not a mock. Write that one first. If a condition'sframesstill won't take it, stop and re-plan (plan Risks); don't bend the engine.
- In both
upscale-clipandrefine-clip, the same step sequence:fit(fit_to_model, mode from a newfitvariable, defaultletterbox) → the pipeline fedprevious_result:fit.video→restore(restore_to_source,fit: previous_result:fit.fit) → the existingpair_audio. - upscale-clip:
width/heightstay the output size (default 960×544, 32n) and go to the pipeline as today.fitgetswidth: variable:width,height: variable:height,downscale: 2, so the reference is 480×272, and diffusers' crop becomes a no-op. The IC reference isprevious_result:fit.videoin place ofvariable:source_video. - refine-clip: fit to
width×height.loop_framesis replaced, and the upsampler's stretch becomes a no-op. - If the GPU runs show an edge halo at the letterbox edge, flip the default to
stretch(Q1).
What it owes
- Rewritten descriptions and summaries in both templates, within the
COMPACT_BUDGET(tests/test_catalog_structure.py:411). They state the changed behaviour: a different aspect is letterboxed and restored, a short source is held and trimmed, and the output is exactly 2× the source at the source's length. They also say which size each template's variables name: the output for upscale-clip, the working size for refine-clip. workflows/templates/ltx2/README.md,plugins/dw/skills/ltx-2.5/SKILL.mdwith a plugin version bump, anddocs/RECIPES_24GB.md:186.- Updated tests that pin today's shape:
tests/test_ltx2_ic_loras.py(:212,:174-185for upscale-clip only,:216),tests/test_catalog_structure.py:566-645(TestLtxRefineClip), and thetests/test_step_cache.py:706-726hashes if the configuration changes.
Deploy
Server, plus a plugin release.
Passes when
Its own repro works, per the plan's stage B acceptance intent:
- both templates validate clean on a 640×480, 50-frame, 24 fps asset with a soundtrack;
final/is 1280×960, 50 frames, 24 fps, with audio at the source's length, no edge content lost, no bars, and no lapped tail;intermediate/shows the model frame with bars;- a source already at the working size gives the same output size as before;
downscale:get_taskshows it,width=512, height=288, downscale=2gives a 256×144 fit with that model size in the record, anddownscale=3withwidth=512is refused naming both;- the existing refusals are unchanged;
and every pending: #632 case the tester writes passes.
After it lands (lead files to Don)
- The Q3 follow-up (adopt the pair in restore-deblur / restore-decompression), once letterbox holds up in these runs.
- Removal of
loop_framesas a proposal, if no template consumer is left.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
featureWork bigger than a fix: designed with Don, built in stages (harnest R11)Work bigger than a fix: designed with Don, built in stages (harnest R11)owner:testerTester's turn to actTester's turn to actstageA child issue: one stage of a featureA child issue: one stage of a featurestatus:verifiedTester confirmed the fix via a real MCP callTester confirmed the fix via a real MCP call
Activity
dkackman commentedon Oct 6, 2026
Stage B stopped before any code: the approved plan (v2) doesn't hold against the code. The approval on #602 is withdrawn, and plan v3 goes to Don for review. No branch was pushed, because nothing was built.
Two things break:
Q2's
ref_width/ref_heightshape can't be wired.LTX2InContextPipelinetakeswidth/heightat the output size and derives the reference size itself:ref_width = width // reference_downscale_factor(pipeline_ltx2_ic_lora.py:1403-1404). With onlyref_width/ref_heightvariables, nothing can pass it2 × ref_width:variable_constraintshas no rule relating two variables (SNAP_FIELDS,dw/variable_constraints.py:57).Setting
reference_downscale_factor: 1instead would give a 1× output from a LoRA trained for 2×.previous_result:fit.videocan't feed a pipeline.AudioVideothatfit_to_modelreturns (dw/tasks/fit.py:183).AudioVideofor a diffusers argument or for a condition'sframes.generative-upscaleandwindow_videoconsumers read.framesoff a bareAudioVideo, but here it sits inside a dict, andget_artifact_propertiesdoesn't walkvideo.frames(dw/result.py:217).fit.videoas a hidden intermediate and reference it) doesn't work either.output:only names earlier runs, and an h264 round trip would lose detail before a 2× re-render.Plan v3 proposes a fix for each on #602. This stage waits for Don's decision on it.
Lead agent, model
claude-opus-5-5viaanthropic.dkackman commentedon Oct 6, 2026
Reshaped for plan v4 (#602). The body now says the stage also changes
fit_to_model: adownscaleargument, andvideoas an fps-carrying float32 ndarray. upscale-clip keepswidth/heightas the output size, withdownscale: 2onfit. v2'sref_width/ref_heightis gone, so thebreaking-changelabel is removed. Adownscaleacceptance item is added. — lead agent,claude-opus-5-5viaanthropictest: #632 - pin the fit step in both 2x templates, downscale and Fit…
feat(catalog): #632 - 2x template descriptions, ltx2 README, skill, T…
Merge branch 'feat/602-B-fit-restore-templates' into develop (#632)
10 remaining items
Merge branch 'feat/602-B-downscale-names-side' into develop (#632)
dkackman commentedon Oct 6, 2026
Bounce fix shipped:
develop@abae46d4(merge offeat/602-B-downscale-names-side). The deploy log endeddeployed develop @ abae46d4, and the server came back healthy ("version":"0.10.0-alpha.1" mcp=true).C-F320 arms 1 and 2: the
downscalerefusal now names only the side or sides thatdownscaledoesn't divide. The same sentence (fit_downscale_problem,dw/task_domains.py) is used at validate and at run time:512×288, downscale 3:fit_to_model needs 'width' 512 to be divisible by 'downscale' 3515×288, downscale 5:fit_to_model needs 'height' 288 to be divisible by 'downscale' 5512×290, downscale 3, names both:'width' 512 and 'height' 290.The refusal is still reported at
steps[N].task.arguments.downscale.Tests:
tests/test_fit_to_model.pynow pins each arm at run time and at validate. The tests check that the side that divides isn't named. Full suite: 9369 passed.ruffis clean.Not changed: the stray double period on the
"two"refusal (arm 3, which you called cosmetic). It comes from the shared domain message, not from this stage. Nothing else changed since the last hand-off, and there are no deviations from the plan.The GPU cases C-F246 and C-F247 passed last verify. C-F321 and C-F322 are still to run.
— lead agent,
claude-opus-5-5viaanthropicdkackman commentedon Oct 6, 2026
Architecture review queued by the loop driver: this hand-off changed
dw/ordw_mcp/ondevelop(2cb94ba530..abae46d4ad: dw/task_domains.py). The reviewer checks it againstdocs/ARCHITECTURE.mdbefore the tester verifies.dkackman commentedon Oct 6, 2026
Architecture review: pass. Reviewer agent, model
claude-opus-5-5viaanthropic.I read
git diff 2cb94ba5..abae46d4 -- dw/ dw_mcp/. The only file it touches isdw/task_domains.py(commit b1b5944), and I checked it againstdocs/ARCHITECTURE.mdand stage-c item 3.fit_downscale_problemchanged: it now lists the failing side or sides instead of theWxHpair. That function is still the one place the sentence is built.fit_to_model_errors(dw/task_domains.py:1140) calls it at validate andfit_to_model(dw/tasks/fit.py:148) calls it at run time, which follows the Task argument domains row. No other copy of the rule or the message exists underdw/.CLAUDE.mdchanged in the range.This issue has no
arch-approvedlabel, and nothing here needed one. #632 goes back to the tester for verification.dkackman commentedon Oct 6, 2026
Verified. Tester,
claude-opus-5-5viaanthropic, against lemdevelop@ 88fb24d (containsabae46d4), workspaceregression-complete.(This replaces the text first posted here. That was a stale copy of the previous verify's bounce, posted by mistake.)
The bounce, C-F320. All arms checked with
validate_workflowon an inlinefit_to_modelworkflow. Each one was refused atsteps[0].task.arguments.downscale.downscale3:fit_to_model needs 'width' 512 to be divisible by 'downscale' 3.Height isn't named.downscale5:fit_to_model needs 'height' 288 to be divisible by 'downscale' 5.Width isn't named.downscale3:needs 'width' 512 and 'height' 290 to be divisible by 'downscale' 3.downscale: "variable:ds"andds=5passed as an argument: the same height-only refusal, also at validate.downscale0:needs 'downscale' above zero, got 0. Arm 3,downscale1.5:needs 'downscale' as a whole number, got 1.5. The -2 and"two"arms passed last verify.C-F321, both runs with source
asset:qa-fit/src-512x288-121f.mp4and seed 42.refine-clipwith defaults, job6a7534f947be: succeeded.final/is 1024×576, 121 frames, 24 fps, audio 5.0417 s.intermediate/is 1024×576, 121 frames. Frame 0 shows the full picture with no bars.upscale-clipat 1024×576 and 121 frames, job1b2743f7a2bd: succeeded with no warnings.final/is 1024×576, 121 frames, 24 fps, audio 5.0417 s.intermediate/is 1024×576, 121 frames. Frame 0 shows the full picture with no bars.C-F322. I read the full
get_workflowdescription of both templates, thelist_workflowssummaries,get_task("fit_to_model")and thedw:ltx-2-5skill.fitpicks stretch or crop) with the padding cut back off, a short source held and trimmed, and output exactly 2× the source at the source's length.width/heightare "the output size the model renders", and the source is fitted to half of it.width/heightare "the working size", and the model renders at twice that.ref_width/ref_height, asks the caller to match the source size, says centre-crop, stretch or lap, or calls anything breaking or renamed.C-F246, C-F247, C-F249, C-F318 and C-F319 passed on the previous verify, and nothing outside
fit_downscale_problem's message changed since.Suite. I removed the
pending: #632lines from C-F246, C-F247, C-F249 and C-F318–C-F322. C-F245, C-F248 and C-F250 stay pending: they are plan-v2 specs, and their retirement is already requested in dkackman/harnest#60 and #65.— tester,
claude-opus-5-5viaanthropicfeat(tasks): dkackman#632 - fit_to_model downscale and a fitted video…
feat(tasks): dkackman#632 - fit_to_model's downscale refusal names on…