Skip to content

Release 0.6.0 - #569

Merged
dkackman merged 296 commits into
masterfrom
develop
Oct 1, 2026
Merged

dkackman merged 296 commits into
masterfrom
develop

Conversation

@dkackman

@dkackman dkackman commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Release 0.6.0: everything on develop since v0.5.0, closing stabilization gate 3.

  • The release notes are in docs/RELEASING.md under ### 0.6.0. They get pasted into the GitHub release body once the tag publishes.
  • Breaking: the three library listings share one envelope; the teacache configuration key is removed; failures after admission answer 500, not 400; the REPL is removed, and dw.run is a client of dw.serve.
  • Checks: scripts/preflight.sh passed on the Mac (MPS):
    • ruff;
    • 7,628 unit tests;
    • 5 integration tests;
    • UI: 340 unit tests and 108 e2e tests.
  • lem (RTX 3090) runs ee5c4944:
    • ltx2/two-stage cold: 159 s;
    • cached rerun: 0.76 s.
  • The gate 3 report is in docs/stabilization/ROADMAP.md.

🤖 Generated with Claude Code

dkackman and others added 30 commits September 27, 2026 23:24
…launch sets DW_MCP_MAX_WAIT_SECONDS=1800

Docs and skills ask for plan.estimate plus a margin as timeout_seconds and
read timeout_applied_seconds/timeout_capped back; no doc quotes a deployment
cap. deploy.sh and dw-serve.service set the cap to 1800 on lem's launch; the
code default stays 55.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ale of the caller's clip

Pins the Pixel-Spatial-Upscaler card (factor 2, strength 1.0) for both
upscale-clip and generative-upscale; pair_audio reads an mp4's track.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and CLAUDE.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the caller's clip

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and RECIPES_24GB

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the RTX 3090

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LTX2LatentUpsamplePipeline overwrites num_frames with len(video), so a
source longer than num_frames was encoded whole: a 97-frame run came out
121 frames with a 4 s generated track (the tester's M-F065 short arm).
A loop_frames step now hands the upsampler the source's opening
num_frames frames.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… seams

Passing `names` or `boundaries` alongside `at` or `count` silently
dropped them with no warning. Refuse the combination up front, naming
the offending argument(s), and say in the tool description and docs
that they are modifiers of `seams` only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
find_loop_bed hands back a computed multiplier (e.g. 10.374) as
mix_audio's documented gains value, but the heuristic flagged any loud
gain regardless of shape, warning on the server's own recommendation. A
hand-typed dB figure is a round number, so only an integer above the
threshold is a likely unit typo now.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
get_gallery_metadata on a kept asset reported job: null even when the
file's job was still in history, because keep_output copied only bytes.
It now records {job, run_id, version, workspace, source} in the same
manifest.json sidecar #393 uses for shots, and gallery_metadata reads
it back for an asset the same way it already read shots.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
test_gallery_metadata_says_a_kept_asset_has_no_provenance asserted the
pre-fix docstring/hint wording ("no provenance"), which no longer holds
now that keep_output carries provenance when it can. Renamed and
updated to match the current hint ("no recorded job").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… halves

slice_audio computed start and length from separately rounded halves
(round(start_frame) + round(num_frames) samples), so a slice meant to
reach the source's exact end could land a sample short and fire a
'dropping its tail' warning whose own figures read 0.00 s. The end
sample is now round(end_frame * sr / fps), matching how the frame grid
is meant to align. Also floors the tail-trim warning at 10 ms, the same
rounding-noise threshold the pad warning already uses, so a remainder
too short to act on says nothing regardless of how it arose.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…scribe_audio call

Without return_timestamps, Whisper's decoder can emit an early
end-of-text after a pause between lines, so plain-text mode truncated
a multi-line clip well under the 30s long-form threshold while
segment/word mode (which already forced return_timestamps) returned
the full text. Force it on for every Whisper call regardless of
length; only the caller's requested mode still surfaces chunks.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nt_step

A for_each step whose members each compose the same catalog template,
with no result of its own, rolled the child's manifest entries into the
parent's manifest unattributed - two members produced two entries under
the identical, repeated child step name with nothing tying an entry back
to the for_each member that produced it. Tag each rolled-up entry with
the nearest composing step's name (setdefault, so a deeper nested rollup
keeps its own tag rather than being overwritten by an ancestor).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ent owns saving

A composed for_each member's own last step never wrote a file when its
parent step declared a result (parent_saves_this, #92): the fps stamp-back
and audio-to-frames fit that save_audio_video applied only ever ran as a
side effect of that write. With the write skipped, the artifact handed up
to the parent kept fps=None, and the parent's own result_definition - the
composing step's own result block, not the child's - declares no fps
either, so video_fps() fell back to DEFAULT_VIDEO_FPS (8) with no warning.
LTX-2.5 image-to-video members were written at 8fps instead of the child
template's declared 24.

Factor the stamp-back and fit out of save_audio_video into
Result.conform_artifact/conform_artifacts, and call conform_artifacts from
workflow.py's parent_saves_this branch in place of the skipped save(), so a
child's declared fps and audio fit always land on the artifact whether or
not that child ends up writing its own file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
dkackman and others added 26 commits October 1, 2026 08:05
…p branch and two redundant lazy imports

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…KEYS to type_helpers; names_no_media public

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (explicit reference, type reference, nested, list helpers)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lution; fetch_constant keeps removeprefix

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hecks to dw/type_references.py, inert-argument warnings to dw/argument_warnings.py; _parse_docstring_args cut

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the trust gate (TRUST_WORKFLOWS_ENV_VAR, TRUSTED_TOP_LEVEL_PACKAGES,
CONSTRUCTIBLE_BASE_CLASSES, _ATTENTION_PROCESSOR_*, is_constructible_class,
require_constructible_class, set_trust_workflows, workflows_are_trusted,
_top_level_package, require_trusted_dotted_name, require_trusted_pre_load_modules,
REMOTE_CODE_ARGUMENTS, require_trusted_from_pretrained_arguments, and their
private helpers) from dw/security.py into a new dw/trust.py. UntrustedWorkflowError
stays in security.py with its exception family. Repoint all importers (type_helpers,
pipeline_processors/pipeline, pipeline_processors/remote, locations, serve,
validate, server/routes/system, and tests) to dw.trust for the moved names.

security.py lands at 771 lines (from 1041). Baseline modules: 157 -> 158.

Tests pass: 7590 passed. Ruff checks pass. arch_metrics pass.
No CodeQL model changes: validators stay in security.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t, components, adapters and progress modules

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tep->pipeline tables; memory reclaim moves to dw/pipeline_ownership.py

The four per-run tables (_prior_step_keys, _running_pipeline_keys,
_pipeline_keys_by_step, _deferred_pipelines) become one PipelineOwnership
object on Workflow, created in __init__ and replaced per run, so the
hasattr/getattr guards go. finish_release, evict_superseded,
reclaim_after_step, _allocated_mb and _release_host_caches hold every
empty_device_cache and release_host_caches lookup; the 19 string patch
targets retarget to dw.pipeline_ownership. The worker reads
workflow.pipeline_ownership.keys_by_step through a getattr chain.
Baseline modules 162 -> 163.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…atcher (_pipeline_action, _reuse_resident, _reference_action, _sub_workflow_action)

create_step_action keeps its signature and stays a Workflow method; each
branch is its own method. The trust gate still precedes the loading
phase, the superseded release still precedes the fresh load, and the
sub-workflow branch keeps its own resolution (a missing builtin is still
a security error, not SubWorkflowNotFound). 279 lines / complexity 15
-> 40 lines / complexity 4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uments on _handed_arguments, off its definition

create_step_action wrote the parent step's realized arguments into the
child's workflow_definition as argument_template, so every validate() and
run() of the child deep-copied them again. The property now returns
_handed_arguments when set (an empty dict included, which still shadows a
file's own argument_template), else the definition's. The child's one
intended copy is still _owned_arguments. workflow_schema.json is
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…c: workflow.py reads it across modules)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion.py; step_value_checks.py; ConstantError to variables

Workflow's validation methods are one-line calls into dw.validation
(workflow_errors, workflow_context, run_warning_check,
undeclared_variable_errors, sub_workflow_errors/argument_warnings through
Workflow.open_sub_workflow). fps/null_media/select checkers move to
dw/step_value_checks.py. Device patches retargeted to dw.validation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es only open_sub_workflow)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ne is dead when its step's result is written

A weakref to the pipeline a release_pipeline step loaded is dead at that
step's Result.save, and the kept step's is alive at its own. Passes on the
base (18a84cd); fails when the run frame keeps step_action across
finish_release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ses in dw/workflow_run.py

dw/workflow_run.py holds RunRecord, PreparedRun, StepLoop and the phases
prepare_run, open_run, begin_steps, run_step (with wire_child,
release_step_pipeline, save_step, record_step), plus prepare_definition,
cache_lookup, the cache_hits loop, write_run_manifest (takes the record;
__version__ imported at the top), selected_field, _relative_shots and
release_unreferenced_results. It does not import dw.workflow; a composed
child is isinstance(step_action, type(workflow)).

Workflow.run is the orchestrator (96 lines, complexity 7); cache_hits is a
one-line delegator. RunRecord.arguments is rebound by _owned_arguments;
started_at/run_id are set at the top and again in open_run; _run_dir is
still not reset at the top of run (known quirk, preserved). run_step holds
step_action and the popped pipeline in its own frame and drops both after
measuring `before` and ahead of finish_release.

SEED_BITS moves with the seed draw; dw/server/jobs.py imports it from
dw.workflow_run. Tests retarget: prepare_definition (6 calls),
workflow_run.realize_args (test_workflow_step_cache), and
release_unreferenced_results / referenced_result_names importers.
Modules 164 -> 165.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t_subfolder and catalog_root_dir join dw/library.py

Beside catalog_root, the rule they share. workflow.py imports them from
there; tests/test_workflow.py and tests/test_workflow_snapshot.py repoint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and fresh-load wrappers join dw/pipeline_ownership.py

wrap_resident (republish shared components, new wrapper around the
resident model, generator, `cached` phase) and load_fresh (trust gate
before the `loading` marker, then load) take the step's output dir and
file prefix from Workflow._pipeline_action, which keeps the key, the
deferral, the superseded eviction and the store. workflow.py 1037 -> 993.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lishes before the step's output dir; weakref test covers the reuse wrapper

Fallback 2 had moved step_output_dir (validate_output_path + makedirs of a
result subfolder) ahead of publish_shared_components on the resident-reuse
path, so a publish that refuses left an empty subfolder the base never made.
_pipeline_action republishes first again; wrap_resident no longer takes
shared_components. New test: a resident pipeline that cannot share raises
its ValueError and leaves no subfolder (fails before this fix, passes on
the base).

The release characterization gains the resident-reuse case: the popped
resident pipeline and the step's distinct wrapper are both dead at the
step's Result.save. Both weakref tests pass on the base (18a84cd, checked
in a scratch worktree) and on head.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…aising AttributeError

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…are a walker

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…previous_results; for_each and previous_results spell prefixes through is_ref/ref_name

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…se notes

Re-baseline modules_over_1000_lines 0, functions_over_150_lines 0,
complex_functions 7; point docs and comments at the moved homes
(audio_qc, writers, argument_media, type_references, workflow_run, trust);
add the 3e release-note entry and carried lists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…se notes, hot zone released, roadmap

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… workflow.py, arguments.py, introspection.py and security.py under 1,000 lines; no function over 150; PipelineOwnership, workflow_run phases, validation block in dw/validation.py; one previous_result walker; modules 151 -> 165

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e notes moved into ROADMAP, 0.6.0, Phase 4 carries

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread dw/library.py Fixed
Comment thread dw/server/routes/jobs.py Dismissed
Comment thread dw/server/routes/jobs.py Dismissed
dkackman and others added 2 commits October 1, 2026 10:35
…ng the disk (CodeQL dw/path-injection #80)

LibraryPath.find(refuse=True) ran os.path.isfile on the unconfined join for
prompts. A name leaving the root is now refused before any probe; a dangling
link inside the root is still a miss.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
JobManager._finish sets the terminal status before history.record, so a
restore right after the status turned could find no row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dkackman
dkackman merged commit a29324a into master Oct 1, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants