Repository navigation
#602 stage A: fit_to_model and restore_to_source tasks #631
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 A of #602. Section Stages → Stage A: the two tasks (server) of plan v2; the plan comment stays authoritative, including The design for both tasks' behaviour, record shape and refusals.
Filed by the lead agent, model claude-opus-5-5 via anthropic.
What it builds
fit_to_model(video, width, height, num_frames, mode="letterbox")andrestore_to_source(video, fit)indw/tasks/(video_utils.pyor a newfit.py).- fit:
stretch/crop/letterboxintowidth×height(bicubic, antialiased); frame count set to exactlynum_frames(cut a long source, hold the last frame of a short one). Returns{video, fit}, the record per the plan. - restore: scale inferred from
videosize ÷ model size (must be uniform); letterbox crops the content box and resizes to source × scale; stretch resizes; crop returns the keptsource_boxat source density × scale; frames trimmed tomin(source_frames, len(video)); fps passes through. - Refusals before any work: non-positive/non-integer sizes or frame count, unknown
mode, empty video, malformedfitrecord, non-uniform scale. Each names the argument.
- fit:
register_commandentries with summaries and parameter descriptions.- The
modedomain indw/task_domains.py.
What it owes
- Unit tests: a round trip per mode on synthetic frames (size, frame count, content box, pixels within tolerance for letterbox and stretch at 1× and 2×); each refusal; discovery (
tests/test_task_discovery.py, presence tests). - Docs: a
docs/TASKS.mdVideo Processing row for each task, plus a short "round trip" paragraph.
Deploy
Server restart.
Passes when
Its own repro works (the plan's stage A acceptance intent: discovery, letterbox/stretch/crop round trips, inferred 2× scale, long source, refusals), and every pending: #<this issue> case the tester writes passes.
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
feat(fit): #631 - tests for fit_to_model and restore_to_source
docs(fit): #631 - fit_to_model and restore_to_source in TASKS and the…
Merge branch 'feat/602-A-fit-to-model' into develop (#631)
dkackman commentedon Oct 6, 2026
Stage A built, deployed:
deployed develop @ 95fc37f1(merge offeat/602-A-fit-to-model; commits 8ee0089 tasks + domain, e2d7b81 tests, docs commit after). Lem healthy after restart.Plan items covered
fit_to_model(video, width, height, num_frames, mode="letterbox")andrestore_to_source(video, fit)-dw/tasks/fit.py, registered indw/tasks/task.pywith summaries and per-parameter descriptions. Returns{video, fit}(read asprevious_result:<step>.video/.fit);fitis aJsonRecord, saved whole as one .json.letterbox(default),stretch,crop; exactnum_frames(long source cut to its first frames, short one holds its last). Restore infers a uniform scale from the output's size (2x restores to 2x source), crops letterbox bars, trims to the source frame count; crop returns the kept region (640x480 → 512x288 → 640x360 at 1x, 1280x720 at 2x).video; a malformed fit record (not a dict, missing/wrong-typed field, bad mode, box outside the frame); non-uniform scale (names both sizes). A fit given as a saved .json path goes throughvalidate_media_path+ JSON extension/size checks.modedomain indw/task_domains.py(TASK_ARGUMENT_CHOICES), plus POSITIVE domains for the three sizes;validate_workflowreports a literal unknown mode or a non-whole/non-positive literal size statically.docs/TASKS.mdVideo Processing rows + a "fit_to_model and restore_to_source" section with the round trip; adocs/ARCHITECTURE.mdrow.tests/test_fit_to_model.py(round trip per mode at 1x/2x, pixel tolerance, frame hold/cut, each refusal, a real dispatch +Resultsave), discovery presence intests/test_task_discovery.py,tests/test_shots.pysites. Full suite: 8754 passed.Deviations / additions
get_taskparameters now carry achoiceslist for any argument with aTASK_ARGUMENT_CHOICESentry (additive; this is howmode's three values show inget_task("fit_to_model")). Not a breaking change.fitrecord carriessource_boxin every mode (the whole source unlesscrop), alongsidecontent_box.pair_audio.— claude-opus-5-5 via anthropic (feature lead, build)
dkackman commentedon Oct 6, 2026
Architecture review queued by the loop driver: this hand-off changed
dw/ordw_mcp/ondevelop(a83ad1ccb4..95fc37f1cd: dw/introspection.py dw/task_domains.py dw/tasks/fit.py dw/tasks/task.py). The reviewer checks it againstdocs/ARCHITECTURE.mdbefore the tester verifies.5 remaining items
Merge branch 'feat/602-A-fit-to-model-bounce' into develop (#631)
dkackman commentedon Oct 6, 2026
Bounce fixed, redeployed:
deployed develop @ 955c5191(merge offeat/602-A-fit-to-model-bounce, fix commit 0d9b412). Lem healthy after restart.C-F241 crop arm (640x368 instead of 640x360). The restore itself was right: it returned 360 rows. The save added the extra 8.
Resultwrote frame-only video through diffusers'export_to_video, which defaults imageio'smacro_block_sizeto 16, so ffmpeg scaled any side that wasn't a multiple of 16 up to the next one (360 → 368). Bothexport_to_videocalls indw/result.pynow passmacro_block_size=2(VIDEO_MACRO_BLOCK), the even sides yuv420p needs. A saved video now keeps its own size, and only an odd side grows by one pixel.docs/TASKS.md's round-trip paragraph says so.TestRealPath::test_a_crop_restore_is_saved_at_its_own_sizesaves the crop restore throughResultand reads the written file's header: 640x360, 12 frames. Without the fix it fails with 640x368. Full suite: 8759 passed; ruff clean.Deviation, outside the stage's files: the writer change applies to every frame-only video save, not just these tasks. Any output whose sides aren't multiples of 16 (e.g. 640x360, 960x540) is now written at its true size instead of being upscaled at save. Sizes that are multiples of 16 are unchanged, and the muxed-audio path (
encode_video) was already exact. No MCP shape changes.— claude-opus-5-5 via anthropic (feature lead, build)
dkackman commentedon Oct 6, 2026
Architecture review queued by the loop driver: this hand-off changed
dw/ordw_mcp/ondevelop(6b6da603c1..955c519196: dw/result.py dw/tasks/fit.py). The reviewer checks it againstdocs/ARCHITECTURE.mdbefore the tester verifies.dkackman commentedon Oct 6, 2026
Architecture review: pass. Read
6b6da603..955c5191underdw/(commit 0d9b412:dw/result.py,dw/tasks/fit.py; also itsdocs/TASKS.mdline) againstdocs/ARCHITECTURE.mdand stage-c item 3.dw/writers.py's docstring says the video writes (export_to_video,encode_video) stay inResult, and both edited calls are those writes, inResult._write_video_file's frame-only branch andsave_audio_video's no-audio fallback. No other module indw/callsexport_to_videoor sets a macroblock, so no second copy of the even-sides rule exists. Thefit.pychange only rewords one error message.macro_block_sizeto diffusers'export_to_video, which hands it to imageio. Both are existing dependencies, and nothing is hand-rolled.VIDEO_MACRO_BLOCKindw/result.py. NoCLAUDE.mdchanged.Not an architecture finding, for the tester: as the hand-off says, this changes the saved size of every frame-only video whose sides aren't multiples of 16. That is a behaviour change outside the stage's two tasks.
— claude-opus-5-5 via anthropic (reviewer, architecture review)
dkackman commentedon Oct 6, 2026
Verified: stage A (#631) passes. Run by claude-opus-5-5 via anthropic, against lem on develop @ 955c519, workspace
regression-complete.The bounced case: C-F241
I rebuilt the RT workflow inline:
fit_to_model→get_dict_valuevideo / fit →restore_to_source. Sourceasset:qa-fit/src-640x480-50f.mp4, model size 512x288, n 57.Crop arm (job
428f22d2297e):source_box {x:0, y:60, w:640, h:360}, content_box full-frame.fittedis 512x288x57 at 24 fps.restoreis 640x360, 50 frames, 24 fps. It was 640x368 before the fix.restorematch the source's central band by eye: the top light and the bottom rows are cut.Stretch arm (job
c6e133b263ad):fittedis squashed with no bars.restoreis 640x480x50 at 24 fps.Adjacent case: a frame-only save whose height isn't a multiple of 16
This checks the writer change outside crop mode (job
8ba36a9b7566). Letterbox from 640x480 to 960x540, n 50:fittedis saved at 960x540, 50 frames. Before the fix it would have been rounded up to 960x544.{120, 0, 720, 540}.restoreis 640x480x50 at 24 fps.The other pending cases
The previous verify (comment above) passed C-F237, C-F238, C-F239, C-F240, C-F242, C-F243, C-F244 and SE-F043 against 95fc37f. I didn't re-run them, for two reasons:
Result's macroblock size and one error message.I've removed the
pending: #631lines from all nine cases (C-F237–C-F244 and SE-F043), so they're now ordinary regression cases. All three jobs above are deleted.feat(fit): dkackman#631 - fit_to_model and restore_to_source tasks, t…
fix(result): dkackman#631 - save a video at its own size, not rounded…