This map says where each concept lives. Each row names a concept, the module that owns it, the rule that holds across the seam (in one sentence), and what enforces that rule. The detail is in the owning module's docstring. Read the docstring before editing; the map only tells you which one to open.
-
Owner: repo-relative paths. Function, class or constant names follow the path after a colon (
dw/runs.py:run_versions). -
Enforced by: one of four kinds.
- A test, written as a file, or as
file::Class::test_name. - A ratchet, named by its key in
docs/stabilization/baseline.json. - The CodeQL pack.
- The validation registry.
A
—means nothing mechanical holds the rule, so a change there is caught only in review. - A test, written as a file, or as
-
tests/test_architecture_map.pychecks three things: every path named here exists, every name after a path is defined in that file, and every test named here is defined in its class.
| Concept | Owner | Rule | Enforced by |
|---|---|---|---|
| Workflow | dw/workflow.py: Workflow, workflow_from_file |
A workflow is loaded and schema-checked here (the hand-off of each run phase to dw/workflow_run.py: —). |
tests/test_workflow.py::test_workflow_validation_invalid |
| One execution, in phases | dw/workflow_run.py: prepare_definition, cache_lookup, cache_hits |
The cache probe shares prepare_definition and cache_lookup with the run, so the probe answers for the run that would happen. |
tests/test_workflow_step_cache.py::TestCacheHits::test_after_a_run_the_probe_names_what_the_next_run_reuses |
| Step | dw/step.py: Step, dw/workflow.py: Workflow.create_step_action |
A step is a pipeline, a task or a sub-workflow, create_step_action builds its action, and dw/workflow_run.py runs it. |
— |
| Pipeline loading | dw/pipeline_processors/pipeline.py: Pipeline, dw/pipeline_processors/components.py: load_component |
A quantization config is built from a config_type that the _type key convention loads by name, so a new backend needs no code. |
tests/test_config_objects.py::TestQuantizationConfiguration::test_the_config_type_is_constructed_with_its_arguments |
| Device translation | dw/pipeline_processors/placement.py: place_component |
A device is translated by resolve_device() before anything reads its backend, so the MPS accommodations also fire for a translated device. |
tests/test_device_portability.py::TestPlacement::test_a_translated_device_gets_its_backends_offload_downgrade |
| On-demand wrappers | dw/pipeline_processors/placement.py: apply_on_demand_placement |
The on-demand wrappers keep the wrapped signature, because H3's denoiser reads signature(transformer.forward). |
tests/test_modular_pipeline.py::TestOnDemandResidency::test_the_wrapped_signature_survives |
| On-demand and offload | dw/pipeline_processors/placement.py: place_component, has_component_group_offload |
On-demand residency and group_offload cannot both be set on one component, and either one stops the whole pipeline from being moved to the device. |
tests/test_modular_pipeline.py::TestOnDemandResidency::test_group_offload_and_on_demand_together_are_rejected, tests/test_modular_pipeline.py::TestOffloadDevice::test_component_group_offload_is_not_moved_to_device |
| Quantized component cache | dw/quantized_cache.py: load_components, from_pretrained, cache_key |
A key that can't pin the weights isn't cached, the cache never fails a job, and the save comes before pruning, so an entry is the whole component. | tests/test_quantized_cache.py |
| LoRAs | dw/pipeline_processors/adapters.py: active_loras, load_loras |
A loras entry whose model_name is null is switched off. |
tests/test_lora_disable.py::TestLoadLoras::test_an_all_null_entry_loads_nothing |
| Step progress | dw/pipeline_processors/progress.py: reported_progress_bars, reported_blocks |
A pipeline with no step callback reports its denoise steps through its progress bars. reported_blocks names each top-level block of a sequential modular graph in a log event as it begins; a dw block that declares idle_without = <input> is left unnamed when that input is absent from the state, and is still run - whether a block acts is the block's own decision, never the narrator's. |
tests/test_modular_progress.py::test_every_advance_of_the_bar_is_reported, tests/test_modular_progress.py::test_an_idle_block_is_unnamed_but_still_run_without_its_input, tests/test_modular_progress.py::test_an_idle_block_is_named_and_run_when_its_input_is_given |
| Step timing | dw/pipeline_processors/timing.py: StepTimer, step_timer, timed_step, mark |
Only a pipeline step that ran is timed, on the worker's monotonic clock: step_timer opens a StepTimer for a step with a pipeline or pipeline_reference and None for any other, which also hides an outer step's timer from a composed child. Load, call, denoise start, each pass and save reach the open timer through mark, and nowhere else. Denoise start is never taken inside a compiled region: a whole-compiled denoiser (_compiled_call_impl set) has that callable wrapped for the call instead of a forward pre-hook, which fullgraph: true cannot trace. A span with a missing mark is null, never guessed. record_step puts the reading on the step's manifest entry and step_end, and a reused step gets no timing. A chain sums its pipeline calls into one timing with segments: N. ManifestEntry.timing declares the shape (docs/WORKSPACES.md Step timing). |
tests/test_step_timing.py::test_a_missing_denoise_start_nulls_encode_and_first_pass, tests/test_step_timing.py::test_a_reused_step_carries_no_timing_even_when_one_is_passed, tests/test_step_timing.py::test_a_chain_sums_its_segments_and_takes_one_median_over_both, tests/test_step_timing.py::test_a_task_step_run_through_the_step_loop_has_no_timing, tests/test_step_timing.py::test_the_denoise_hook_marks_the_first_forward_once_and_removes_itself, tests/test_step_timing.py::test_a_fullgraph_compiled_denoiser_runs_and_still_gets_the_start_mark |
| Step-cache skip counts | dw/pipeline_processors/timing.py: CacheCounter, counting_forwards, report_cache_stats, StepTimer.cache_counted; dw/pipeline_processors/components.py: stateful_cache_context; dw/pipeline_processors/mag_calibration.py: get_cache_transformer, captured_mag_calibration |
For first_block and mag, a forward hook on the cache transformer counts computed or skipped once per forward, reading the leader hook's should_compute inside the cond/uncond cache_context the pipeline opened, so classifier-free guidance counts both forwards. stateful_cache_context registers the hook for one pipeline call; its finally emits cache_stats before _reset_stateful_cache() and removes the hook after the event. Private diffusers state that has moved gives null counts and one cache_stats_unavailable warning, and never fails the run; taylorseer, faster and text_kv report type and config only, with no warning. The denoiser a cache attaches to (get_cache_transformer) and the MagCache calibration capture (captured_mag_calibration, the end-of-run log record turned into cache_stats-side job events, #891/#927) live in mag_calibration.py, which components.py imports and re-exports for pipeline.py, never the reverse. The counts reach the step's timing.cache only through StepTimer.cache_counted; a chain sums its calls' counts, and a null in any call nulls the sum. A step with no cache has no cache key and no event (docs/ACCELERATION.md Reading the skip counts). |
tests/test_cache_stats.py::test_cfg_counts_two_forwards_per_step_and_they_add_up, tests/test_cache_stats.py::test_the_event_names_the_step_and_segment_and_goes_out_before_the_reset, tests/test_cache_stats.py::test_moved_hook_state_nulls_the_counts_warns_once_and_the_run_finishes, tests/test_cache_stats.py::test_the_private_diffusers_names_are_where_the_counter_reads_them, tests/test_cache_stats.py::test_the_timer_sums_counts_and_nulls_on_any_null, tests/test_cache_stats.py::test_types_without_a_decision_report_type_only_and_no_warning |
| Vendored model ports | dw/vendored/ (audio8_tts_model.py, mmaudio_model.py, mmaudio_sync_model.py, mmaudio_clip.py) |
Upstream model code dw carries is one module per port, pinned to the commit or Hub revision its docstring names, so it can be diffed against its source and re-pinned as a unit; a port reaches dw only for the validated media loaders and is imported only by the task that runs it. Like dw/community_pipelines/, the package is outside the architecture ratchet (EXCLUDED in scripts/arch_metrics.py): a port keeps upstream's shape, which the module and function ceilings are not written for. |
tests/test_arch_metrics.py::test_vendored_model_ports_are_not_counted, tests/test_mmaudio_model.py::test_modules_are_imported_only_by_the_sound_effects_task, tests/test_mmaudio_sync_model.py::test_module_is_imported_only_by_the_sound_effects_task |
| Community pipelines | dw/community_pipelines/ (e.g. pipeline_ltx2_refine.py: LTX2RefinePipeline), dw/introspection.py: community_pipelines, load_allowed_class |
A pipeline dw ships is named by its dotted path (dw.community_pipelines.<module>.<Class>), loads untrusted because it is under dw, and is listed by list_pipelines and resolved by get_pipeline_signature - only the modules in that directory, never a caller-chosen import. LTX2RefinePipeline VAE-encodes video at width x height and passes it unnormalized as latents, because LTX2Pipeline.prepare_latents normalizes 5-D latents itself. |
tests/test_ltx2_refine_pipeline.py::TestIntrospection::test_only_the_shipped_modules_resolve, tests/test_ltx2_refine_pipeline.py::TestEncode::test_the_latents_are_left_unnormalized_for_the_parent |
| Adding a task | dw/tasks/registry.py: register_command, RegistryTable |
A task is a function registered with @register_command, and the signature of its implementation is its argument schema. Its validate-time rules are declared on the same decorator - domains, choices, static_check, whole_numbers and media_arguments - and TASK_ARGUMENT_DOMAINS, TASK_ARGUMENT_CHOICES, TASK_STATIC_CHECKS, TASK_WHOLE_NUMBER_ARGUMENTS and TASK_MEDIA_ARGUMENTS are read-only views derived from them, so there is no second table to forget (#692). The registry lives in dw/tasks/registry.py; dw/tasks/task.py re-exports it, and a task module may register its own handler, as dw/tasks/beats.py does, provided dw/tasks/task.py imports it (docs/TASKS.md Adding a task). The built-in handlers are grouped by family the same way - dw/tasks/audio_handlers.py, dw/tasks/video_handlers.py, dw/tasks/finish_handlers.py and dw/tasks/model_handlers.py (#790) - and dw/tasks/task.py keeps the general-purpose commands, the processor dispatch and Task. |
tests/test_task_discovery.py::TestDescribeTask::test_signature_becomes_the_schema, tests/test_task_registry_rules.py::test_every_derived_table_is_the_registrys_view, tests/test_task_registry_rules.py::test_every_static_check_is_registered_to_exactly_one_command, tests/test_task_registry_rules.py::test_every_media_argument_is_a_parameter_of_its_command |
| Task argument domains | dw/task_domains.py: check_arguments, task_argument_errors; dw/task_problems.py: face_track_errors, beats_errors, cuts_errors, lut_errors |
A numeric domain that a signature cannot express is declared on the command's @register_command(domains=...), and it is checked at validation and again at run time. A numeric argument is read by whole_number or real_number, the only numeric coercion in dw/tasks/, and validation applies the same rule (number_problem), so a value is refused at both or at neither: "3.0" is a whole number, "3.5" for one of whole_numbers is not, and true is no number (#774). Task.run coerces every declared argument through coerce_arguments before dispatch, so a handler sees a number, and an argument that has no range besides being a number declares finite. A command's cross-argument rules are its registered static_check, which task_argument_errors runs at validation (attribute_voices' voices_argument_errors, kept in dw/tasks/voice_attribution.py, among them). A literal enum choice (choice_errors) is refused at validation by the same module. The per-family rules past a single domain live in dw/task_problems.py, which reads task_domains' helpers and is never imported by it (task_argument_errors reaches them only through the registered static_check, so import_cycles stays 0; #790): the ingredients_grid image count against max_images (ingredients_grid_errors), and crop_face_track's cross-argument rules (face_track_problems, face_track_errors: crop size a multiple of the step's multiple (32 by default), remainder below modulus, gate_zero above gate_full, max_frames at or above min_frames, padding and confidence limits), which the task checks again at run time, as analyze_beats' are too (beats_problems, beats_errors: min_bpm below max_bpm, and anchors that are one kind per list, ascending, with whole beat indexes; tests/test_analyze_beats.py), and plan_cuts' (cuts_problems, cuts_errors: a bare-string transcript (transcript_problem, which plan_cuts also calls at run time), min_scene_s above max_scene_s, segment_by: "beat" without beats), and apply_lut's (lut_source_problem, palette_problem, lut_errors: exactly one of lut or palette, and a palette of 2..16 #rrggbb colours, re-checked at run time by check_lut_source), and check_face_detector_source vets its Hub repo and .onnx file before any download. select's index stays in task_domains with the other media-timing rules (select_index_problem, select_rule_problems: a whole number, at least 0, and below the candidate count when the count is known, i.e. at run time or when candidates is a written list). |
tests/test_task_domains.py::TestTheStaticPass::test_a_negative_frame_count_is_refused_at_its_path, tests/test_numeric_coercion.py::TestGradeExposure::test_a_string_number_grades_like_the_number, tests/test_task_domains.py::TestAtRunTime::test_slice_audio_refuses_a_negative_count, tests/test_ingredients_grid.py::TestStaticValidation::test_bad_layout_and_fit, tests/test_face_track.py::TestStaticValidation::test_bad_literal_is_an_error_at_its_argument, tests/test_face_track.py::TestCropFaceTrack::test_bad_arguments_are_refused_at_run_time, tests/test_face_track.py::TestStaticValidation::test_a_declared_grid_is_accepted, tests/test_face_track.py::TestDetectorSource::test_traversing_repo_is_refused, tests/test_select_validation.py::TestSelectErrors::test_an_out_of_range_literal_index_names_both_ends, tests/test_lut.py::TestPaletteAtValidate::test_both_refuse_naming_the_two, tests/test_lut.py::TestPalette::test_a_bad_palette_refuses_naming_the_entry |
| Variables | dw/variables.py: set_variables, argument_errors, resolve_variable_values |
argument_errors folds a caller's arguments in exactly as set_variables does when a run starts, so a bad name or value is refused before anything is queued. |
tests/test_variables.py::test_argument_errors_reports_a_dict_passed_for_a_string_variable |
| List variables from the CLI | dw/run.py, dw/variables.py: get_value |
A name=value string given for a list variable is split on commas, so a list of objects can only be passed as JSON over the API or MCP. |
tests/test_variables.py::test_set_variables_list_default_splits_on_comma |
for_each expansion |
dw/for_each.py: expand_for_each, group_of |
Each entry becomes an ordinary step named <step>@<entry>, after substitution and before the reference check. |
tests/test_for_each.py::TestNaming::test_member_name_joins_with_at |
| Optional entry field | dw/for_each.py: _optional_item, _item_fields |
{"item": "field", "default": X} reads an entry field with a fallback; without default it is the plain required read, and list_fields / entry_field_warnings count the field as one entries take. The editor's string scan needs no twin code, only the shared case file. |
tests/test_for_each.py::TestOptionalItem::test_a_present_field_wins_and_an_absent_one_takes_the_default, tests/test_ui_twins.py::test_the_engine_decides_each_shared_reference_case |
| Previous results | dw/previous_results.py: get_iterations, previous_result_reference_errors |
Several previous_result: references in one step multiply into a cartesian product. |
tests/test_previous_results.py::TestGetIterations::test_multiple_references_create_cartesian_product |
| Previous-result check | dw/previous_results.py: previous_result_reference_errors, cross_entry_reference_errors; the sibling rule is dw/for_each.py: cross_entry_key (beside group_of, which it calls), which the cross-entry check, the earlier-step check and dw/video_extensions.py: video_into_image_errors all ask |
A literal reference to a step that is not earlier in the workflow is a validation error. A for_each member reading another entry of its own list (shot@a inside shot@b, either spelling, any .property) is a validation error too, cross_entry_reference_errors (#1101); same-key pairing, other groups and gather: stay legal. |
tests/test_previous_results.py::TestStaticReferenceChecking::test_a_step_cannot_reference_itself_or_a_later_one, tests/test_previous_results.py::TestCrossEntryReferences::test_the_prefix_spelled_in_an_entry_is_refused |
| Type conversion | dw/arguments.py: realize_args, dw/type_helpers.py: get_type |
A key ending _type or _dtype loads a Python object at realize time. The other spellings, and {} escaping, are in docs/WORKFLOW_GUIDE.md. |
tests/test_type_helpers.py::TestGetType::test_get_type_from_diffusers, tests/test_arguments.py::TestRealizeArgs::test_realize_escaped_type_reference |
| Concept | Owner | Rule | Enforced by |
|---|---|---|---|
| Reference prefixes | dw/references.py |
Every reference prefix is spelled only in this module, and every other module goes through it. | docs/stabilization/baseline.json: prefix_literals, prefix_handling; tests/test_references.py::test_references_imports_nothing_from_dw |
| Explicit references resolve first | dw/arguments.py: _realize_explicit_reference |
asset:, output:, constant: and prompt: resolve in realize_args before any key-name convention. |
— |
| Reference confinement | dw/assets.py: resolve_asset_reference, dw/runs.py: resolve_output_reference, dw/library.py: LibraryPath, dw/security.py: validate_asset_reference, validate_output_reference |
dw/security.py validators confine an asset: path to its library and an output: path to the output root. |
tests/test_assets.py::TestReferences::test_a_symlink_out_of_the_library_is_refused, tests/test_security_symlinks.py::TestOutputs::test_an_output_reference_does_not_follow_a_linked_run_directory |
| Reference name shape | dw/reference_names.py: reference_name_errors |
The shape of every asset:, prompt: and output: name is checked before the queue, and existence is checked later. |
tests/test_reference_names.py::TestTheValidationPass::test_a_malformed_reference_is_refused_before_the_queue |
| Library reads | dw/library.py: LibraryPath |
Reads go through the roots front to back, so an earlier name shadows a later one. | tests/test_library_path.py::TestResolution::test_the_front_of_the_path_shadows_the_rest |
| Library writes | dw/library.py: LibraryPath |
Writes go only to the front root, so saving something opened from a read-only root writes a copy. | tests/test_library_path.py::TestConstruction::test_the_writable_root_comes_first, tests/test_library_sources.py::TestServer::test_saving_an_example_prompt_writes_a_copy |
| LoRA catalog matching | dw/lora_catalog.py: matches, workflow_bases |
An entry fits a base only by exact repo id (and H3 partition); no alias or family match. | tests/test_lora_catalog.py::TestMatching::test_the_base_must_match_exactly |
| Hub search | dw/lora_hub.py: search_hub |
The Hub is searched only by GET /api/loras/recommend, never raises, and offers no pickle-only repo. |
tests/test_lora_hub.py::TestCandidates::test_a_pickle_only_repo_is_dropped, tests/test_lora_hub.py::TestFailure::test_a_hub_error_is_returned_not_raised |
| Packaged builtins | dw/library.py: builtin_root, resolve_sub_workflow_reference |
builtin: names the packaged dw/workflows/, not the top-level workflows/ examples. |
tests/test_sub_workflow_resolver.py::TestTheResolver::test_a_builtin_resolves_in_the_packaged_root |
| Sub-workflow paths | dw/library.py: resolve_sub_workflow_reference, resolve_sub_workflow |
A sub-workflow path is confined to the root it resolves in. The search order is in the docstring of resolve_sub_workflow. |
tests/test_sub_workflow_resolver.py::TestTheResolver::test_a_path_escaping_its_root_is_refused |
| Workspace | dw/workspace.py: resolve_workspace, set_workspace |
--workspace beats DW_WORKSPACE, which beats the setting, which beats a working directory that looks like a workspace, which beats ~/diffusers-workspace. |
tests/test_workspace.py::TestResolution::test_a_flag_wins_over_everything, tests/test_workspace.py::TestResolution::test_a_bare_working_directory_falls_back_to_the_home_workspace |
| Workspace copy | dw/workspace_copy.py: copy_between |
Copies runs and asset-library entries between workspaces under one root: media hard-linked, sidecars always copied, never a symlink; an asset keeps its library-relative path and its note and kept-shots entry are merged into the destination folder's sidecars; all or nothing, a differing asset refusing runs too; vN renumbered by runs.copy_runs_in under the run lock; entries checked by security.validate_output_prefix. |
tests/test_workspace_copy.py |
| Realized workflow | dw/realize.py: realize_workflow, dw/runs.py: write_realized_workflow |
Each run directory holds workflow.json, with the run's arguments, seed and stored prompt text pinned. |
tests/test_realize.py::TestVariablesAndSeed::test_arguments_become_the_variable_defaults, tests/test_runs.py::TestRealizedWorkflow::test_the_run_directory_holds_a_realized_copy |
| Concept | Owner | Rule | Enforced by |
|---|---|---|---|
| Run directories | dw/runs.py: open_run, workflow_identity, new_run_id |
Each execution writes into <output_dir>/<identity>/<run id>/ with a manifest.json, unless the flat layout is chosen (the test pins the flat-layout exception). |
tests/test_runs.py::TestRunDirectories::test_the_flat_layout_writes_where_it_always_did |
| A file's role (final / intermediate / input) | dw/roles.py |
Decided per step: result.role, then a final/intermediate subfolder, then data flow (a step a media-writing or unsaved consumer reads is intermediate). Engine records it at run end, with role_declared where result.role stated it; a run that predates roles recorded none, so the listing falls back on the file's own final/intermediate subfolder, then the demotion below, and a file neither places has none. At read time the gallery (GET /api/gallery, its role= filter, the metadata route) also demotes a file a later run of the same workspace read - an output: input (an unpinned latest is the newest run started before the reader) or an asset: keep_output kept from it - unless its role was declared (consumed_outputs, listed_role, once per listing; no manifest is rewritten). |
tests/test_roles.py, tests/test_roles.py::test_a_file_a_later_run_read_is_consumed, tests/test_roles.py::test_listed_role, tests/test_roles.py::test_listed_role_of_a_run_that_recorded_none, tests/test_gallery_roles.py::test_older_run_reads_its_intermediate_subfolder_as_intermediate, tests/test_gallery_roles.py::test_a_pre_roles_file_a_later_run_read_lists_as_intermediate, tests/test_gallery_roles.py::test_metadata_agrees_with_the_listing_for_an_older_run, tests/test_gallery_roles.py::test_a_file_a_later_run_read_lists_as_intermediate, tests/test_gallery_roles.py::test_a_declared_final_stays_final |
| Notes on runs, files, assets, workspaces | dw/notes.py |
A note is notes.json beside what it describes (run dir or asset dir; a file's own note, else the run's), or workspace.json at a workspace root. Only the server writes them, never the engine; at most 4000 characters, plain text. |
tests/test_notes.py |
| Run versions | dw/runs.py: open_run, run_versions, record_run_versions, copy_runs_in |
A run takes its number once, when it opens (or, copied in from another workspace, when copy_runs_in places it), so deleting a middle run leaves a gap rather than renumbering. |
tests/test_runs.py::TestRunVersions::test_a_deleted_middle_run_leaves_a_gap_rather_than_renumbering, tests/test_runs.py::TestRunVersions::test_runs_started_in_the_same_second_still_number_upward |
| Run-version surfaces | dw/runs.py: run_versions, dw/server/routes/gallery.py, dw/server/outputs.py, dw_mcp/tools_catalog.py: list_gallery, ui/src/lib/pages/GalleryPage.svelte |
Only dw/runs.py computes the number, and every other module reads it back. |
tests/test_runs.py::TestRunVersions::test_an_output_reference_can_name_a_run_by_its_version |
| What the server says about an output file | dw/server/outputs.py: output_annotations |
The gallery listing (dw/server/routes/gallery.py) and the metadata route (dw/server/routes/media.py) describe an output file's role, note, note_source, run_note, starred and version through output_annotations, and neither builds those fields on its own. |
tests/test_gallery_roles.py::test_metadata_agrees_with_the_listing_on_a_starred_noted_demoted_file |
| Result subfolders | dw/subfolders.py: subfolder_errors, dw/security.py: SUBFOLDER_PATTERN |
A step's result.subfolder is checked for shape at validation. |
tests/test_subfolders.py::TestValidationErrorsIntegration::test_validation_errors_reports_a_bad_subfolder |
| Template subfolder roles | workflows/templates/, dw/workflows/ |
Every saving step of a template is marked final or intermediate, with at least one final, and packaged builtins stay unmarked. |
tests/test_template_subfolders.py::test_every_saving_step_of_a_template_names_its_role, tests/test_template_subfolders.py::test_the_packaged_builtins_stay_unmarked |
| Frames carrying their rate | dw/media_types.py: FrameList (re-exported by dw/tasks/video_utils.py), rated_videos |
The one list of frames (or of videos) that carries .fps and .shots: a video loaded from a file (dw/argument_media.py) and a step that knows its rate but would return bare frame lists both answer it - h3_tiles' tiled refine videos, and every pipeline step whose pipeline reports its own fps (MiniMax-H3's property; dw/output_extraction.py: attach_video_frame_rate, called by pipeline.py's _run_once beside attach_audio_sample_rate, #1056) - through rated_videos, the batch and each video in it, so pair_audio and the result writer read getattr(video, "fps", None) instead of fitting and playing at the default 8 fps. A pipeline that reports no rate leaves its lists bare. No second class of that name. |
tests/test_h3_tiles.py::TestTileLoopReporting::test_the_videos_are_dws_one_frame_list, tests/test_pipeline_components.py::TestVideoFrameRate::test_a_dict_outputs_videos_carry_the_pipelines_rate, tests/test_pipeline_components.py::TestVideoFrameRate::test_a_pipeline_with_no_rate_leaves_the_lists_bare, tests/test_modular_output_properties.py::TestTheBasesOwnTakeSavesBesideIt::test_the_base_take_pairs_at_its_own_rate_with_no_padding, tests/test_video_utils.py (the FrameList cases) |
| Writing a video at its rate | dw/writers.py: encode_video, exact_frame_rate |
A fractional rate is written as the exact rational (30000/1001, 24000/1001, 60000/1001), never truncated to a whole rate, and no dw/ or dw_mcp/ module imports encode_video from diffusers.utils - every audio-carrying video write goes through dw.writers.encode_video (dw/result.py, dw/pipeline_processors/chain_spill.py - a chain's segments on disk, SegmentSpill, SegmentedFrames and SavedFrames, re-exported by chain.py). A declared fractional result.fps is therefore accepted by fps_errors. |
tests/test_fractional_fps.py::test_encode_video_with_audio_keeps_fractional_rate, tests/test_fractional_fps.py::test_no_dw_module_calls_the_diffusers_encode_video_directly, tests/test_fractional_fps.py::test_the_diffusers_hook_the_wrapper_swaps_still_exists |
| JSON records beside media | dw/media_types.py: JsonRecord, dw/result.py: Result.save_artifact |
A dict marked JsonRecord is saved whole as one .json, whatever content type the step declares, and is never split into one file per key. |
tests/test_face_track.py::test_json_record_is_saved_whole_under_a_video_content_type, tests/test_face_track.py::test_the_task_result_saves_crops_as_video_and_track_as_one_json |
| Reading a JSON record back | dw/locations.py: load_json_record |
A task reads a saved JSON record (fit_to_model's, crop_face_track's) only through load_json_record: the path is confined like any media path, then the extension must be .json, then the size is checked against MAX_JSON_SIZE, and only then is it parsed. fit._read_fit and face_track._read_track call it, never open. |
tests/test_locations.py::TestLoadJsonRecord::test_traversal_out_of_the_roots_is_refused, tests/test_locations.py::TestLoadJsonRecord::test_a_non_json_extension_is_refused, tests/test_locations.py::TestLoadJsonRecord::test_an_oversized_file_is_refused_before_it_is_read, tests/test_fit_to_model.py::TestRestore::test_fit_path_with_traversal_is_refused |
| Shots in a joined video | dw/shots.py |
Every AudioVideo constructor decides what to do with shots, and the sample spans are measured from the joined waveform. |
tests/test_shots.py::test_every_audio_video_constructor_site_is_decided, tests/test_shots.py::TestConcatVideosShots::test_an_overrun_track_is_measured_not_derived |
| Overlap windows of a long video | dw/tasks/windows.py: window_video, window_span |
Window i covers source frames [i*stride - overlap, i*stride + stride), its synthetic frames repeat the first or last frame, and its audio is cut on the source's own frames_to_samples boundaries, so adjacent windows' strides tile the track with no sample lost or repeated. |
tests/test_window_video.py::TestAudio::test_strides_tile_the_source_track_exactly, tests/test_window_video.py::TestRealPath::test_a_path_argument_keeps_its_audio, tests/test_window_video.py::TestFileMatchesInMemory::test_every_window_equals_the_in_memory_window, tests/test_read_frame_range.py |
| Keep a span of a video's frames and audio | dw/tasks/trim.py: trim_video; dw/shots.py: trimmed_shots |
Frames [start_frame, start_frame + num_frames) are kept and the audio is cut on frames_to_samples of each end at the track's own rate, so consecutive trims tile the track. A span past the clip's end is refused, never shortened. Shots are clipped to the span, sample side cleared. |
tests/test_trim_video.py::TestFramesAndAudio::test_consecutive_trims_tile_the_track, tests/test_trim_video.py::TestShots::test_shots_are_clipped_across_the_boundary |
| Join processed windows back to the source's length | dw/tasks/windows.py: join_windows; dw/task_domains.py: window_count, window_count_problem, join_windows_errors |
Output is exactly the source's frames, each once: later windows' first overlap frames blend over the previous window's last on the open ramp dissolve_videos uses. The window count is window_count's ceil(source_frames / stride) and nothing else (it is also window_video's last index plus one). Audio is the source's own track; shots are one per window with overlap_frames on each seam, their sample side laid over that track by dw/shots.py's remeasured_shots (the same rounding as frames_to_samples, so the spans are cumulative). |
tests/test_join_windows.py::TestRoundTrip::test_unprocessed_windows_rejoin_to_the_source, tests/test_join_windows.py::TestShots::test_spans_partition_the_frames_and_tile_the_audio, tests/test_join_windows.py::TestUint8Output::test_matches_the_float32_accumulator_exactly |
A join_windows list of the wrong length, before the run |
dw/window_count_errors.py: window_count_errors, wired into validation_errors |
Owned by the task, never a template: any join_windows step whose source is probeable header-only (asset:/output: or a readable literal path), with literal num_frames/overlap and an expanded videos list, is checked against window_count_problem - the same rule the run refuses with. Anything not knowable stays silent for the run. |
tests/test_window_count_errors.py |
| Fit to a model and restore | dw/tasks/fit.py: fit_to_model, restore_to_source |
fit_to_model's record is the whole of what restore_to_source needs: the scale is inferred from the output's size and must be uniform, and restore returns the source's size times that scale and at most its frame count. | tests/test_fit_to_model.py |
| Shared image ops | dw/tasks/image_ops.py: LUMA_WEIGHTS, luma, split_alpha, join_alpha, per_frame |
grade, finish and lut take luma (float64 Rec. 709 weights, cast to the input's dtype) and alpha handling from image_ops, never from each other, so an RGBA input comes back RGBA with its alpha untouched; lut.LUMA_WEIGHTS is a re-export. An image command runs over a video or frame list only through per_frame. A media file is read by task._load_media with fetch_image(keep_alpha=True), so a file with transparency arrives RGBA; a pipeline input still flattens to RGB. |
tests/test_image_ops.py::TestLuma::test_lut_re_exports_the_one_float64_weights, tests/test_image_ops.py::TestAlpha::test_rgba_round_trip_keeps_the_alpha_bytes, tests/test_image_ops.py::TestOutputsAreByteIdentical::test_every_command_matches_the_pre_refactor_bytes, tests/test_image_ops.py::TestPerFrame::test_a_video_comes_back_as_a_video_with_the_same_frames_and_audio, tests/test_finish.py::TestRgbaFileKeepsAlpha::test_a_file_path_comes_back_rgba_with_its_alpha |
| Audio level of a deliverable | dw/audio_qc.py: warn_without_headroom, warn_if_written_above_full_scale, all_shots_silent_by_design |
A muxed video keeps the pre-encode headroom prediction and falls back to it only when the post-write probe cannot measure the file. A result a later step resets the level of (consumed_by_normalizer, dw/step_cache.py) draws neither the post-write warning nor the fallback. |
tests/test_result.py::TestNoHeadroom::test_a_clean_video_mux_drops_the_stale_prediction, tests/test_result.py::TestNoHeadroom::test_an_unprobeable_video_mux_falls_back_to_the_prediction, tests/test_result.py::TestNoHeadroom::test_a_video_a_join_level_matches_draws_no_headroom_warning, tests/test_result.py::TestTheWrittenLevel::test_a_file_that_decodes_above_full_scale_warns |
| Level-spread warning on a replaced track | dw/step_cache.py: audio_replaced_downstream, dw/tasks/joins.py: warn_on_level_spread |
A join whose track a later pair_audio takes as its video draws no level_spread warning; being that step's audio argument doesn't count. |
tests/test_step_cache.py::test_level_spread_warning_suppressed_when_audio_replaced, tests/test_step_cache.py::test_audio_replaced_downstream_false_when_it_is_the_audio_or_unread |
| Template audio-level placement | workflows/templates/minimax/music-video.json, workflows/templates/minimax/music.json |
normalize_audio (-3 dBFS) acts only on the track that goes into the final file, so the slices that condition the shots are untouched. music-video slices each shot lead_frames early and trim_video drops the run-up before the join. |
— |
| Concept | Owner | Rule | Enforced by |
|---|---|---|---|
| Validation registry | dw/validation.py: ERROR_CHECKS, WARNING_CHECKS, run_checks |
A check is one Check in a registry, and a check that raises becomes one internal finding while the rest still run. |
tests/test_validation.py::TestExceptionPolicy::test_a_raising_check_is_one_internal_error_and_the_rest_still_run |
| Step-value checks | dw/step_value_checks.py: fps_errors, cache_errors, null_media_errors, select_errors, chain_prompts_errors |
The only caller of these checks is the error registry. | dw/validation.py: ERROR_CHECKS |
| Admission | dw/server/admission.py: admit |
Validate, submit, rerun and enhance load and check a request once, and JobManager.submit does not check it again. |
tests/test_admission.py::test_a_submit_expands_once |
| Variable constraints | dw/variable_constraints.py: apply_constraints; the grid arithmetic aligned / aligned_down |
A model's rule about a value is declared in the workflow's variable_constraints, and it is checked at validation and again before anything loads. plan_cuts (and its static check) rounds render lengths through aligned / aligned_down, never its own copy, so a planned num_frames is one the template's constraint accepts; crop_face_track pads its crops to the step's modulus/remainder grid through aligned too (padding_to_grid). |
tests/test_variable_constraints.py::TestTheGrid::test_without_snap_an_off_grid_value_is_refused_not_rounded, tests/test_variable_constraints.py::TestTheGrid::test_plan_cuts_rounds_through_the_constraint_owner, tests/test_face_track.py::TestPadding::test_padding_to_grid_rounds_through_the_constraint_owner, tests/test_variable_constraints.py::TestTheRunTimePass::test_an_illegal_value_is_refused_before_anything_loads |
| Bound acknowledgement | dw/plan.py: fingerprint, dw/server/admission.py: check_bound_acknowledgement |
A bound acknowledged_cost is refused with 409 when the plan's fingerprint or its required downloads changed. The fingerprint is keyed only by what can change the output: DOCUMENTATION_KEYS (cost, cost_drivers, vram_estimate, descriptions, catalog shape/traits), a constraint's reason and a result's role, subfolder and file name are left out, of the workflow and of each composed child; every schema key is sorted into left out or kept. A number digests by value, not spelling (12.0 as 12), and the seed stays out (#85). |
tests/test_plan.py::TestFingerprintKeys::test_the_exclusion_lists_are_pinned, tests/test_plan.py::TestTheCatalogH3Template::test_a_client_round_trip_keeps_it, tests/test_plan.py::TestFingerprintKeys::test_every_top_level_schema_key_is_sorted, tests/test_server.py::TestBoundRerun::test_a_rerun_bound_to_a_stale_plan_is_refused, tests/test_server.py::TestBoundAcknowledgement::test_a_matching_fingerprint_queues |
| Per-repo gating | dw/plan_downloads.py: _model_info, _probe_gate_blocked, assembled by dw/plan.py: build_plan |
Each gated repo, including a loras entry's repo, is probed and reported on its own. |
tests/test_plan.py::TestDownloadsRequired::test_a_gated_repo_this_token_lacks_access_to_is_blocked |
| Download sources | dw/plan_downloads.py: downloads_required, _collect_sources, _collect_component_overrides, _sub_workflow_paths (which plan.estimate walks children by too; the module never imports dw/plan.py back, and plan.py re-exports downloads_required and gate_warnings) |
downloads_required lists every model_name, loras repo (a loras entry naming a resolved weight_name is listed instead as a {repo, filename, revision} file entry built by file_entries, sized as that file), single-file URL and load_components.pretrained_model_name_or_path override (string, per-component dict, default), once each. The base repo stays listed whole: an override's replaced subfolder is not subtracted, so the list is a floor. |
tests/test_plan.py::TestDownloadsRequired::test_a_load_components_repo_override_is_listed |
| Task weights | each fetching task's *_source(arguments) (e.g. dw/tasks/remove_object.py: remover_source); dw/tasks/weights.py: argument_or_default, file_entries, package_dir; dw/tasks/registry.py: required_weights_hook, register_weights_fallback; dw/tasks/image_utils.py: processor_weights_hook; dw/security.py: validate_weight_name; dw/plan_downloads.py: _collect_task_weights, _file_info |
A model-backed task declares required_weights(arguments) on its register_command, and downloads_required lists those repos beside the pipeline model_names (a shared repo once; a reference or local-path argument none). A single-file task lists {repo, filename, revision} entries, checked per file against the cache; restore_faces also lists facexlib's weights as URL entries. A task's declaration and its loader read the same resolution of which argument wins (a single-file task's *_source(arguments), an overridable whole-repo task's argument_or_default call, a fixed repo's module constant), so they cannot differ; argument_or_default is the shared helper only where None means the default (remover_source and upscaler_source treat any falsy value as absent; spandrel_source has no default). file_entries builds a file entry (its name checked by validate_weight_name), package_dir finds facexlib's weights dir without importing it, and _file_info sizes one file. The lookup path is acyclic: dw/locations.py reads the registry's tables and the task modules import the location policy and weights.py, so the registry imports no task module (an image processor's hook reaches it through register_weights_fallback, which image_utils calls at import) and weights.py imports only dw/security.py, where validate_weight_name lives for that reason. A task module the hook lookup imports keeps transformers out of its module top (hf_pipeline is a lazy wrapper, patched by name in the tests), so a validate never pays for it. |
tests/test_task_weights.py, tests/test_task_weights.py::TestTheHookPathStaysAcyclic::test_weights_imports_only_the_security_leaf_from_dw, tests/test_task_weights.py::TestTheHookPathStaysAcyclic::test_the_registry_imports_nothing_from_dw |
| Observed cost | dw/server/observed_cost.py: declared_drivers, ObservedCosts._on_this_card, ObservedCosts.card |
observed comes only from this box's finished jobs on the named card (the one route names, for plan.estimate), else the server's own card; ObservedCosts.card owns a card's (kind, name, default). Rows are matched by card name whatever index they ran on (a job from before jobs carried a device counts only while the server is on the default card), and never writes cost. Cold and warm runs are reported separately. |
tests/test_observed_cost.py::TestColdIsNotWarm::test_the_two_are_reported_separately_each_with_its_runs, tests/test_observed_cost.py::TestByCard::test_only_rows_from_this_card_count_whichever_index_they_had, tests/test_observed_cost.py::TestByCard::test_rows_from_before_jobs_carried_a_device_do_not_count_off_the_default_card |
| The estimate | dw/plan.py: estimate, _tempered; the driver scaling is dw/cost_scaling.py: scale_curated, _driver_scaling |
plan.estimate quotes the observed figure ahead of the curated one, and blends it toward the curated figure below three runs. A cost entry is measured at the workflow's variable defaults; a run moving num_inference_steps, width/height or num_frames (per list entry, summed) scales it in proportion (scale_curated, which dw/server/deps.py reuses for an inherited figure), basis derived with scaled naming each axis, times the list-length re-price, and that scaled figure is what a thin observed one blends toward (#866). |
tests/test_plan.py::TestObservedEstimate::test_history_beats_a_curated_figure, tests/test_plan.py::TestLowConfidenceObservedEstimate::test_a_single_run_blends_toward_the_curated_figure, tests/test_plan.py::TestDriverScaling::test_steps_canvas_and_frames_multiply, tests/test_plan.py::TestScalingAccuracy::test_within_thirty_percent |
| Cost drivers | dw/server/observed_cost.py: declared_drivers |
A cost driver that names no declared variable is dropped. | tests/test_observed_cost.py::TestComparability::test_a_driver_naming_no_variable_is_dropped, tests/test_observed_cost.py::TestTheCatalogsDriversAreReal::test_every_declared_driver_is_a_variable_of_its_workflow |
| Raw workflow GET | dw/server/routes/library.py: get_workflow |
The raw workflow GET is served verbatim, with no observed, because the editor saves what it reads. |
— |
| VRAM projection | dw/vram_estimate.py, dw/guides.py: guide_lengths |
A template's vram_estimate is projected per step after for_each expansion, and only the largest step over the ceiling is reported. With bytes_per_guide_voxel declared, each guide the step lays in (its own non-null guides plus a guide chain's) adds its snapped frames times the canvas; an unprobeable clip is charged at num_frames, and validate, admission and run project the same number. |
tests/test_vram_estimate.py::test_only_the_largest_over_ceiling_step_is_reported_once_per_cost_entry, tests/test_vram_guides.py::test_validate_apply_and_required_agree_on_a_guided_definition |
| H3 Ref2VA VRAM numbers | workflows/templates/minimax/ |
Every Ref2VA template declares base_gb 16.0, bytes_per_voxel 28.71 and gb_per_reference 1.0. |
tests/test_h3_vram_ceiling.py::test_every_ref2va_minimax_template_declares_gb_per_reference |
| Inherited VRAM ceiling | dw/vram_inheritance.py, dw/server/deps.py: ceiling_index (the catalog parse is _catalog_listing, shared with the cost lookup below) |
A workflow with no vram_estimate is matched against the catalog by pipeline identity, and is warned, never refused. Under the ceiling it still gets a vram_config_differs warning when its offload or quantization differs from the template's, which the estimate was calibrated on (#954). |
tests/test_vram_inheritance.py::test_every_template_declaring_one_identity_declares_the_same_numbers, tests/test_vram_inheritance.py::TestConfigDiffers::test_missing_offload_under_the_ceiling_notes_it |
| Inherited cost | dw/server/deps.py: inherited_observed, dw/vram_inheritance.py: inherited_differences, dw/plan.py: _observed (basis inherited), dw/server/routes/jobs.py: _inherited_cost_warnings |
An inline workflow (no catalog name) is priced from the observed runs of the catalog template with the same component_type + model_name + workflow key as vram_inheritance; the template with the most cold runs wins, then name. differs lists offload, quantization, frame count, attention backend, cache, LoRA and reference count (the references list lengths, per-entry item: ones read off the for_each list; named, not scaled, #915) set differently from the template, and sets low_confidence when it lists any (#866). More pipeline steps than the template run (a for_each counted per member, against the list the bucket ran, so a fallback to the default bucket re-prices a longer list) re-price its minutes linearly, and the mean frames, inference steps, width and height per pipeline step are scaled by scale_curated itself - the one rule the catalog figure uses, so an inline copy and its template quote one request alike - with scaled naming why and low_confidence set (#851, #866). It is warned, never refused. |
tests/test_inherited_cost.py::TestPricedByPipelineIdentity::test_an_inline_copy_inherits_the_templates_runs, tests/test_inherited_cost.py::TestDifferences::test_offload_quantization_and_frames_are_named, tests/test_inherited_cost.py::TestDifferences::test_a_reference_count_other_than_the_templates_is_named, tests/test_inherited_cost.py::TestThePlanReportsIt::test_the_estimate_is_inherited_and_the_validate_answer_warns, tests/test_inherited_cost.py::TestTheTemplateAndAnInlineCopyAgree::test_the_same_minutes_within_rounding, tests/test_inherited_cost.py::TestAListLongerThanTheFallbackBucket::test_an_inline_six_shot_copy_scales_by_members |
| Projected host-RAM peak | dw/server/host_ram_need.py: memory_key, projected_peak, host_memory_block; the fit is dw/host_memory_projection.py: projected_mb; history by key is dw/server/job_history.py: peaks_for_key |
A run's memory key is the sorted pipeline identity of its pipeline steps plus every quantization_config / offload / group_offload value, with arguments substituted through replace_variables; never the workflow's name, so a renamed or inline copy shares the template's history. observed is the max peak over this machine's successful same-key runs and comes ahead of the list-run fit, unless the shape holds its pipeline across the list and the fit is the larger (the key leaves list length out, #910); with neither there is no figure. It never refuses; dispatch may delay a start on it (dw/server/dispatch.py: Dispatcher._next_dispatch, #869) (Admission.host_ram_need, validate's plan.host_memory). |
tests/test_host_ram_need.py::TestMemoryKey::test_an_inline_copy_without_its_metadata_has_the_same_key, tests/test_host_ram_need.py::TestMemoryKey::test_a_different_weights_dtype_is_a_different_key, tests/test_host_ram_need.py::TestProjectedPeak::test_an_observed_peak_beats_the_fit, tests/test_host_ram_need.py::TestALongerListThanObserved::test_an_accumulating_list_takes_the_larger_fit, tests/test_host_ram_need.py::TestProjectedPeak::test_a_failed_run_is_not_observed, tests/test_host_ram_need.py::TestValidateRoute::test_an_observed_run_is_the_plans_host_memory |
| Acceleration warnings | dw/acceleration_warnings.py: acceleration_warnings, CACHE_MIN_USEFUL_STEPS, CUDA_ONLY_ATTENTION_BACKENDS (the last entry of WARNING_CHECKS) |
An acceleration setting that can't pay off (a first_block/mag cache under CACHE_MIN_USEFUL_STEPS, any taylorseer cache, compile.fullgraph with a cache, a cache on a pipeline class with no transformer or whose transformer lacks enable_cache(), a CUDA-only attention_backend on a non-CUDA device) is a warning and never a refusal, and an unresolvable value (steps, backend, device) leaves it silent. |
tests/test_validation.py::TestAccelerationWarnings::test_warnings_never_refuse, tests/test_validation.py::TestAccelerationWarnings::test_steps_that_resolve_to_nothing_are_silent, tests/test_validation.py::TestAccelerationWarnings::test_an_unknown_device_is_silent, tests/test_validation.py::TestAccelerationWarnings::test_an_unresolvable_backend_is_silent_on_mps |
| H3 sparse attention | dw/sparse_attention.py: sparse_call, install_sparse, install_sparse_for_call, apply_sparse_settings, SparseH3AttnProcessor; wired from dw/pipeline_processors/components.py: configure_components and dw/pipeline_processors/pipeline.py: apply_runtime_settings |
Whether a pipeline call runs sparse is decided once per call, all or nothing (the call's first kernel invocation included); it never changes diffusers' process-wide backend and never reaches AttentionBackendName; it installs by a processor swap plus forward hooks, never a forward wrapper; and sparse_tau is a runtime setting, not part of the weights identity (#931). |
tests/test_sparse_attention_processor.py, tests/test_sparse_attention_wiring.py, tests/test_sparse_attention_tiles.py, tests/test_sparse_attention_backends.py |
| H3 adapter partition | dw/adapter_compatibility.py; the server's index is dw/server/deps.py lora_index |
The LoRA catalog decides first, on the server only: a proven entry's workflow refuses a step in the other partition, a trial/rejected entry's only warns. A file no entry places falls to its name: an FL2VA LoRA on a reference step is refused, and one whose name says neither ref2v nor fl2v is warned. |
tests/test_h3_adapters.py::TestWhatIsRefused::test_a_keyframe_adapter_on_the_reference_path, tests/test_h3_adapters.py::TestWhatIsRefused::test_an_unrecognised_name_is_a_warning_not_a_refusal, tests/test_h3_adapters.py::TestTheCatalogDecidesFirst::test_a_proven_entry_refuses_the_other_partition, tests/test_h3_adapters.py::TestTheCatalogDecidesFirst::test_a_weaker_entry_only_warns, tests/test_server_loras.py::TestTheCatalogPlacesAnAdapter::test_a_trial_entry_warns_and_promoting_it_refuses |
| H3 audio hold, refine, tiled refine, guide layout, guide noise, latent anchor and regional edit | Fourteen modules, imported one way: dw/pipeline_processors/h3_rules.py holds the names and rules, torch- and diffusers-free (refine_problems, the per-argument refine rules, refine_tiles and the explicit refine_sigmas schedule among them, asked before validation and before the call; tile_plan, the tile layout on the 32 px grid, shared by validate and the run; not_h3, the step rule; snap_guide_length, guide_frame_problem, guide_chain_problems, guide_end_problem; CHAIN_CONTINUITY_MODES; the chunk constants; and the regional-edit rules mask_problems, media_problems and edits_region, the argument rules shared by validate and _with_mask; media_problems is asked by validate too, on the files that resolve then, and refuses a source_video off the 17n + 5 grid, which the pipeline would otherwise snap up over a padded source), dw/pipeline_processors/h3_mask_cells.py holds the mask-to-rows arithmetic (binarized, regenerate_cells, keep_rows, on Pillow's L conversion, torch's adaptive max pool and scipy's dilation, each imported where it runs), which h3_mask.py re-exports and h3_mask_steps.py reads from there so the blocks never import their importer, dw/pipeline_processors/h3_hold.py puts the hold and refine blocks in (insert_audio_hold, core_denoise_sequences, refines; HELD_LATENTS_OUTPUT and encoded_hold_audio, the hold block's encode handed back to it), and dw/pipeline_processors/h3_guides.py the guide layout (LAYOUT_ANCHORS, insert_guides), which imports h3_hold and never the reverse, dw/pipeline_processors/h3_anchor.py the latent statistics anchor (insert_latent_anchor, anchor_refusal; its block in h3_anchor_steps.py, imported only by anchor_block(); the arithmetic, channel_statistics and match_channel_statistics, in h3_anchor_stats.py, torch only, which h3_anchor re-exports and h3_anchor_steps reads from there, as h3_mask_cells is read, so the block never imports its importer), which imports h3_hold and h3_guides and never the reverse, and runs right after after_denoise in t2va/fl2va only (a no-op without latent_anchor, decided by the block itself; it declares idle_without = "latent_anchor" so the step-progress narrator leaves it unnamed then; chain.anchor: "segment_1" hands segments 2+ segment 1's frames in chain.py's _segment_arguments, and run_chain sets pipeline.latent_anchor_source to segment 1 for those segments only; Pipeline._with_latent_anchor emits one log line per anchored call naming that source, the quoted reference, or the step's frames; dw/guides.py's anchor_errors is the latent_anchor validation check, on h3_rules.chain_anchor_problem and the guides step rule _takes_guides_problem, asked for the anchor's name, so a step passing references (ref2va) is refused as guides is; Pipeline._with_latent_anchor refuses references at run time as _with_guides does), and dw/pipeline_processors/h3_mask.py the regional edit (insert_mask_blocks, edits_regions), which imports h3_hold and never the reverse; the blocks themselves, and the arithmetic they run, are dw/pipeline_processors/h3_hold_steps.py (encode_audio_span, refine_sigmas, and explicit_sigmas, a step's refine_sigmas list run verbatim in place of the shift grid) dw/pipeline_processors/h3_guide_steps.py (splice_guide_rows, and noised_guide_pixels, the guide_noise pixels: the layout block's _encode_guides encodes each video guide's noised pixels in place of the clean ones when guide_noise is set, on h3_rules.guide_noise_taper, one encode per guide either way (PrefixStream's paired encode under one seed is that encode, so the clean one is not run); guide_noise_errors/guide_noise_warnings in dw/guides.py validate it on h3_rules.guide_noise_problem and _takes_guides_problem, and Pipeline._with_guide_noise drops a 0 and refuses at run time) and dw/pipeline_processors/h3_mask_steps.py (the source-latents and mask-blend blocks), which import diffusers when they load, so the three owners import them only inside blocks(), guide_blocks() and h3_mask.blocks() (#790), and h3_guide_steps imports h3_hold_steps and never the reverse (_with_guides in dw/pipeline_processors/pipeline_h3.py - the H3Arguments mixin that gives Pipeline its per-call H3 argument wrappers _with_held_audio, _with_guides, _with_guide_noise, _with_latent_anchor, _with_mask and _check_refine, still named Pipeline._with_* - adds its blocks for a guides call, and _with_mask checks a regional edit's arguments and media and builds the regenerate mask for a source_video call), dw/guides.py: the guides validation check and the chain continuity: "guide" check, guide_chain_errors, which asks the guides check's own step rule (_takes_guides_problem, built on h3_rules.not_h3 and REFERENCE_WORKFLOWS) and reads the mode names from h3_rules.CHAIN_CONTINUITY_MODES, the names chain.py's CONTINUITY_MODES is zipped against (both registered in dw/validation.py; a previous_result: guide is checked at run time), dw/pipeline_processors/h3_tiles.py: the tiled refine (the tile loop, the latent-space feather blend, the per-tile crop of one whole-canvas noise draw, the decode-free graph variant, and the one spatially tiled decode), which imports h3_rules, h3_hold and media_types (for FrameList; and events, progress) and is imported by pipeline.py and, lazily, components.py (which attaches the variant at load, behind h3_hold.core_denoise_sequences, and calls insert_mask_blocks at load), never by h3_rules or h3_hold, dw/hold_audio.py: hold_audio_errors (a video is held by its soundtrack), refine_strength_errors, mask_errors (Check regional_edit, which probes the source and mask through ValidationContext.probe the way dw/guides.py probes guide videos) |
The one place dw modifies a diffusers modular pipeline's block graph: three blocks go in, before set_timesteps, denoise and after_denoise, in every H3 core-denoise sequence (the whole graph and the workflow=-pruned flat graph), and two more for a regional edit: the source block before set_timesteps and the blend block inside the denoise loop after update. All are no-ops without their arguments (hold_audio / refine_strength, source_video). The tiled refine adds a second graph built at load beside them (h3_tiles.decode_free_blocks: the decode and text-encoder blocks dropped, every kept block the graph's own instance, so hold and refine stay in) and swaps it into pipeline._blocks only for the duration of the one text-encode call and the tile calls (swapped_blocks, restored on failure). Every tile runs the hold block, but the track is encoded once per refine: the first tile is asked for the block's encode (HELD_LATENTS_OUTPUT) and every later tile's hold_audio carries it (encoded_hold_audio, an EncodedAudioReference), which the block writes in place of running the audio VAE. A hold_audio path is opened only after locations.validate_media_path against the step's workflow directory. dw/introspection.py builds the same graph without weights, hold included, so get_pipeline_signature lists a modular pipeline's block inputs. The copies of diffusers behaviour are pinned to the installed diffusers: shifted_sigma_grid (the spacing refine_sigmas rests on) to stock set_timesteps, GUIDE_FRAMES_PER_CHUNK/GUIDE_LATENTS_PER_CHUNK to the H3 video VAE's clip_length and latents per clip, and every name in LAYOUT_ANCHORS (_fill_audio_positions among them) to before_denoise. |
tests/test_h3_hold_audio.py::TestAnchors::test_inserted_next_to_the_anchors_in_every_shape, tests/test_h3_hold_audio.py::TestHoldAudioLocation::test_a_path_outside_the_roots_is_refused_at_the_call, tests/test_h3_hold_audio.py::TestRefusals::test_a_non_h3_step_is_refused, tests/test_h3_hold_audio.py::TestSignature::test_the_h3_signature_shows_hold_audio, tests/test_h3_refine.py::TestAnchors::test_inserted_before_denoise_in_every_shape, tests/test_h3_tiles.py::TestDecodeFree::test_variant_has_no_decode_and_shares_blocks, tests/test_h3_tiles.py::TestTileLoop::test_blocks_are_restored_when_a_tile_fails, tests/test_h3_tiles.py::TestTileLoop::test_the_track_is_encoded_by_one_tile_and_handed_to_the_rest, tests/test_h3_hold_audio.py::TestHoldBlock::test_a_carried_encode_is_written_without_encoding_again, tests/test_h3_tiles.py, tests/test_h3_rules.py (tile_plan and the refine_tiles rules), tests/test_h3_mask.py, tests/test_h3_refine.py::TestSigmaGridDrift::test_the_full_grid_is_stock_set_timesteps, tests/test_h3_refine.py::TestSigmaListOneOwner::test_the_runtime_message_is_the_owners, tests/test_h3_refine.py::TestSigmaListSchedule::test_the_block_runs_the_list_unshifted, tests/test_h3_guides.py::TestChunkDrift::test_frames_per_chunk_is_the_vae_clip_length, tests/test_h3_guides.py::TestChunkDrift::test_latents_per_chunk_is_the_vae_tokens_per_clip, tests/test_h3_guides.py::TestAnchors::test_installed_diffusers_has_every_anchor, tests/test_guide_chain_validation.py::TestRefused::test_the_step_rule_is_the_guides_rule, tests/test_guide_chain_validation.py::TestRefused::test_every_run_mode_is_known_to_validate, tests/test_h3_rules.py::test_h3_rules_imports_neither_torch_nor_diffusers, tests/test_h3_rules.py::test_the_block_owners_import_no_diffusers, tests/test_h3_anchor.py::TestInsert::test_follows_after_denoise, tests/test_h3_anchor.py::TestStep::test_without_an_anchor_the_latents_are_untouched, tests/test_h3_anchor.py::TestWithLatentAnchor::test_no_anchor_logs_nothing, tests/test_h3_anchor.py::TestWithLatentAnchor::test_a_chain_segment_logs_its_label_and_segment_1, tests/test_modular_progress.py::test_the_anchor_step_is_idle_without_latent_anchor, tests/test_h3_anchor.py::TestAnchorErrors::test_an_unknown_anchor_names_the_values, tests/test_h3_guide_noise.py |
| IC-LoRA reference scale | workflows/templates/ltx2/ |
The five conditioning templates run at reference_downscale_factor: 1, and the two upscalers at 2. |
tests/test_ltx2_ic_loras.py::TestEachTemplateMatchesItsCard::test_the_reference_is_encoded_at_the_output_resolution, tests/test_ltx2_ic_loras.py::TestTheGenerativeUpscaleMatchesTheSameCard::test_it_loads_the_upscaler_at_factor_two |
| IC-LoRA numbers | workflows/templates/ltx2/ |
Every number in the IC-LoRA templates comes from the vendor's model card. | tests/test_ltx2_ic_loras.py::TestEachTemplateMatchesItsCard::test_the_strength_is_the_cards_default, tests/test_ltx2_ic_loras.py::TestEachTemplateMatchesItsCard::test_the_defaults_are_the_trained_bucket |
| IC-LoRA prompt genre | prompts/ltx2/ |
A stored prompt tagged ic-lora is checked against its trained caption form, not against the T2V paragraph rule. |
tests/test_ltx_prompt_library.py::test_an_ic_lora_prompt_is_in_its_trained_form |
| Concept | Owner | Rule | Enforced by |
|---|---|---|---|
| Step cache | dw/step_cache.py, dw/workflow_run.py: cache_lookup |
A seeded rerun with unchanged inputs is served from the cache, marked reused: true, and writes nothing; POST /api/memory/clear (dw/server/routes/system.py: clear_memory) drops it. A step's key is only what can change its output: workflow metadata (cost_drivers, vram_estimate, descriptions) never reaches it and a result's role is dropped (NON_OUTPUT_RESULT_KEYS); attention backend, cache config, dtype and LoRA stay, and so do subfolder and file_base_name, because a hit republishes the cached paths. |
tests/test_workflow_step_cache.py::TestOutputKeys::test_the_exclusion_list_is_pinned, tests/test_workflow_step_cache.py::TestOutputKeys::test_metadata_alone_is_a_hit, tests/test_workflow_step_cache.py::test_cache_hit_marks_its_manifest_entry_and_event_reused, tests/test_workflow_step_cache.py::test_second_run_with_unchanged_step_reuses_cached_result |
| No seed, no cache | dw/workflow_run.py: prepare_run |
A workflow that sets no seed skips the cache. |
tests/test_workflow_step_cache.py::TestCacheHits::test_an_unseeded_workflow_has_no_hits |
| A different result | dw/server/routes/jobs.py, dw_mcp/tools_jobs.py: rerun_job |
rerun with new_seed draws a fresh seed into the workflow's seed variable. |
tests/test_rerun_new_seed.py::test_a_rerun_with_a_new_seed_draws_one_into_that_variable |
| Worker protocol | dw/worker_protocol.py: parse_reply |
Every command and reply is a frozen dataclass that travels as a wire dict. A run event's keys sit beside the wire's own type, so an event's type key (cache_stats' cache type) crosses as event_type and comes back as type. |
tests/test_worker_messages.py::test_from_wire_inverts_to_wire, tests/test_cache_stats.py::test_the_event_crosses_the_worker_wire_with_its_cache_type, tests/test_worker_messages.py::test_an_unknown_reply_type_is_kept_whole_rather_than_raised |
| Persistent worker | dw/worker.py, dw/worker_manager.py, dw/serve.py |
Jobs run in spawned workers (one per card) that keep models loaded, so a change to engine code needs a server restart. | — |
| Worker card pinning | dw/devices.py: worker_environment, pinned_environment, resolve_serve_devices, check_device_present, label_of |
A worker named with a CUDA index is spawned with CUDA_VISIBLE_DEVICES set to that card and DW_DEVICE=cuda (a bare cuda pins nothing), so index-0 reads inside it measure its own card; --devices/devices takes one entry per worker (a card named twice, or a bare cuda in a list of several, is refused), a card the box lacks is refused at startup naming the cards present (in a list of several it is dropped with a warning and the server starts on the rest; only a list with no card present refuses), and a job stores the card it ran on as device_ordinal and device_card (#693), job and history alike: rerun affinity reads the ordinal, observed cost buckets by the card, and the device label "cuda:N <card name>" is only label_of joining the two - nothing splits a label back apart. |
tests/test_worker_manager.py::TestWorkerPinning::test_a_cuda_index_pins_the_worker_through_the_environment, tests/test_worker_manager.py::TestWorkerPinning::test_a_bare_cuda_sets_no_pin, tests/test_serve_main.py::TestConfigureDevices::test_a_refusal_exits_with_code_2, tests/test_serve_main.py::TestResolveServeDevices::test_a_card_the_machine_lacks_is_refused_naming_the_cards_present, tests/test_serve_main.py::TestResolveServeDevices::test_two_entries_are_both_kept, tests/test_serve_main.py::TestResolveServeDevices::test_a_card_gone_from_a_list_is_dropped_and_reported, tests/test_serve_main.py::TestConfigureDevices::test_a_pulled_card_in_the_setting_starts_on_the_rest_and_warns, tests/test_serve_main.py::TestResolveServeDevices::test_a_card_named_twice_is_refused |
| Failed-run reporting | dw/worker.py, dw/worker_protocol.py: Failed, Cancelled |
A failed or cancelled run's reply still carries the manifest of the steps that ran. | tests/test_worker_execute.py::test_failure_carries_the_manifest_of_the_steps_that_ran, tests/test_worker_execute.py::test_cancellation_carries_the_manifest_too |
| Failure path redaction | dw/path_redaction.py: redact_paths, called by dw/worker.py |
A failed run's message and traceback name a file under the job's asset roots or output directory by its asset:/output: reference, never its absolute server path. |
tests/test_worker_execute.py::test_a_failure_names_an_asset_by_reference_not_by_server_path, tests/test_path_redaction.py |
| Run-time warnings | dw/events.py: emit_warning |
A warning found at run time is emitted as an event, so it reaches the caller and not just the log. | tests/test_concat_videos.py::TestWarningsReachTheCaller::test_the_level_spread_warning_is_emitted_as_an_event |
| Concept | Owner | Rule | Enforced by |
|---|---|---|---|
| Opening media | dw/media.py, dw/media_readers.py (the frame readers, re-exported by dw/media.py, which imports from it and never the reverse) |
These are the only modules that call av.open. They import no torch and no step types. |
tests/test_media_layering.py::test_av_open_appears_only_in_media, tests/test_media_layering.py::test_media_imports_no_step_types (no torch: —) |
| Frame moments and crops | dw/media_frames.py: moment_index, parse_crop |
One at grammar ("frame:N", or seconds at the clip's fps; negative counts back from the end for get_output_frames and is refused by compare_outputs) and one [x, y, width, height] crop parse serve get_output_frames and compare_outputs. Only the bounds policy is the caller's: get_output_frames refuses a moment past the end and clamps a crop (_moment_to_index, resolve_crop_box); compare_outputs blanks a past-end cell and refuses a crop that doesn't fit an output. |
tests/test_media_compare.py::test_resolve_moments_refuses_what_get_output_frames_refuses, tests/test_media_compare.py::test_crop_box_refuses_a_malformed_box_as_resolve_crop_box_does |
| Output comparison | dw/media_compare.py (texture bands from dw/texture_bands.py): compare, psnr, ssim, mean_abs_diff, check_request, crop_box |
Metrics always compare the same pixels: one shared crop is the measured region, and per-output crops are display only, leaving metrics on the full frame. An output whose size differs from the baseline's, or a moment past its end, gets null metrics with a reason; nothing is resized to force a number. SSIM is on Rec. 601 luma (SSIM's convention, not image_ops.luma). It runs in the server process and decodes through dw/media.py and open_bounded. |
tests/test_compare_route.py::test_a_shared_crop_is_the_region_measured, tests/test_compare_route.py::test_per_output_crops_are_display_only, tests/test_compare_route.py::test_a_size_mismatch_gets_no_numbers_and_says_why, tests/test_compare_route.py::test_a_moment_past_the_shorter_clip_is_a_blank_cell |
| Signal processing | dw/dsp.py |
It measures and transforms waveforms, decides nothing, and imports nothing from dw. |
tests/test_media_layering.py::test_dsp_imports_nothing_from_dw |
| Assessment probes | dw/tasks/assess.py, dw/assessment_rules.py |
Exactly three registered commands are assessment probes. Each measures a finished file, and every rule names a real probe field. | tests/test_assessment_rules.py::TestRuleProbesAreRealCommands::test_assessment_is_exactly_the_three_probes, tests/test_assessment_rules.py::TestRuleProbesAreRealCommands::test_every_rule_names_a_registered_json_command |
| Script check | dw/tasks/script_check.py: check; dw/tasks/script_findings.py: the rule constants and the finding builders check calls (imported one way, re-exported by script_check); dw/tasks/script_take.py: guard_words, word_level, shot_spans; dw/script_lines.py: parse_lines, parse_shots, shot_names_error |
check_script aligns a take's heard words to its expected lines and guards out words heard over silence and repetition loops (Whisper's hallucination over music). It builds its findings with assessment_rules.finding() and reads the guard's floor and window (DEAD_AIR_FLOOR_DBFS, DEAD_AIR_WINDOW) rather than copying them; the loudest-window measure is dsp.loudest_window_dbfs and the looped-bed self-correlation dsp.repeat_correlation (script_findings.is_looped_bed only decides on it) (script_take.word_level wraps it; the guard, the transcription levelling, the floor-gated audible_runs and the shot placement (with clamp_to_shot, a line's timing kept inside its shot) are dw/tasks/script_take.py, which script_check reads one way - nothing there reads the rule tables), which transcribe_audio's suspect: "silent" mark reads with the same floor and window; the repetition-loop detector (script_lines.looped_texts, REPEAT_RUN_MIN, REPEAT_MAX_PERIOD) lives in dw/script_lines.py and backs both check_script's repetition discard and transcribe_audio's suspect: "repetition", and script_lines.suspect_runs/SUSPECT_LEFT_OUT are what plan_cuts groups and leaves out suspect chunks by - so neither task imports the other. It is not an assessment probe and adds nothing to RULES. Its literal lines is refused at validation through task_problems (script_lines_errors), which calls the same parse_lines the task runs, and a literal shots through the same parse_shots and shot_names_error; the parsers live in dw/script_lines.py, not the task, so that task_problems reaches them without importing script_check (the import_cycles ratchet in scripts/arch_metrics.py). A line's shot may be the short @name of the one recorded shot that carries it (script_lines.matching_shot_names, with_full_shot_names, shared by the task and the literal-shots check); which member key a recorded name carries comes from references.member_keys, beside MEMBER_SEPARATOR and for_each.member_name, never a re-derived pattern: a key ends at the first character outside references.VARIABLE_NAME_CHARACTERS, the set security.validate_variable_name (which checks a for_each entry's name) builds its pattern from. With audio an output:/asset:/path whose run manifest records shots, dw/shot_span_preflight.py: script_shot_name_errors (the script_shot_names error check) runs shot_names_error at validation, reading the map through runs.shots_beside. Its shot map comes from assess.resolve_shots, the one the probes use, never a copy; it places each shot on the soundtrack with assess.sample_span, decides repeated shot names with shots.duplicate_shot_names, and asks locations.is_http_url whether the take is a URL. |
tests/test_script_check.py::TestRegistration::test_registered_as_json_command_without_assessment_flag, tests/test_script_check.py::TestEmptyLines::test_words_only_over_silence_are_discarded_not_found, tests/test_script_check.py::TestLinesErrors::test_literal_with_a_colon_is_not_a_reference, tests/test_script_check.py::TestShotValidation::test_a_short_member_name_matches_the_one_shot_carrying_it, tests/test_script_check.py::TestShotValidation::test_a_valid_variable_name_comes_back_whole_as_a_member_key, tests/test_shot_span_preflight.py::TestScriptShotNames::test_a_wrong_shot_name_is_an_error_at_validate_time |
| Audio sync against a source slice | dw/tasks/measure_sync.py: measure_sync; dw/dsp.py: envelope_lag |
measure_sync cross-correlates the onset envelopes of a shot's audio and its source slice and answers the lag and a 0-1 confidence. A positive lag means the audio is LATE (its events fall after the reference's), negative early; lag_seconds is None for a silent or flat track. It builds its findings (sync_unmeasurable, sync_low_confidence, audio_out_of_sync) with assessment_rules.finding() over module-local rule dicts and adds nothing to RULES; it reads the silence floor from beats.SILENT_DBFS. envelope_lag imports nothing from dw. |
tests/test_measure_sync.py::TestLag::test_late_audio_is_a_positive_lag, tests/test_measure_sync.py::TestLag::test_early_audio_is_a_negative_lag, tests/test_measure_sync.py::TestUnmeasurable::test_a_silent_reference_has_no_lag |
| Sung phrases from voiced energy | dw/tasks/phrases.py: analyze_phrases, voiced_power, voiced_runs; dw/task_problems.py: phrases_problems, phrases_errors |
analyze_phrases times a song's sung phrases with no model: a stereo file's centre-weighted band power (max(0, P_mid - P_side), 250-3500 Hz, 20 ms frames) or, for mono, centre: false or identical channels, the downmix's band power (method band). The voiced floor is voice_attribution.voiced_floor_dbfs, the rule attribute_voices uses, unless floor_db is given. It answers a dict - {phrases, duration_seconds, method, warnings} - so a step saves one JSON artifact. Its ranges are the registration's domains; phrases_problems (a real boolean centre) runs at validate and at run time. |
tests/test_analyze_phrases.py::TestCentre::test_centred_bursts_under_hard_panned_noise_are_found, tests/test_analyze_phrases.py::TestCentre::test_bursts_panned_hard_to_one_side_are_not_centre, tests/test_analyze_phrases.py::TestRefusals::test_validate_refuses_a_missing_audio_and_a_negative_gap |
| Concept | Owner | Rule | Enforced by |
|---|---|---|---|
| Path validators | dw/security.py: validate_path, validate_workflow_path, validate_output_path |
Filesystem access goes through a path validator that is given a base directory. | tests/test_security.py::test_path_validation, .github/codeql/dw-security/DwPathInjection.ql |
| URL and argument validators | dw/security.py: validate_url, sanitize_command_args |
A URL must be http or https, and a subprocess argument must not carry a shell metacharacter. | tests/test_security.py::TestValidateUrl::test_dangerous_schemes_are_rejected, tests/test_security.py::TestSanitizeCommandArgs::test_every_shell_metacharacter_is_rejected |
| Locations from a workflow | dw/locations.py |
A local path in a workflow's media arguments (named like their media, or declared by the task's @register_command(media_arguments=...), read through TASK_MEDIA_ARGUMENTS) is confined to a known root, a location written as a URL is one security.validate_url accepts, and a URL must not name a host inside the deployment. A host that does not resolve is refused. The requests themselves are dw/outbound.py's. |
tests/test_locations.py::TestMediaPathContainment::test_absolute_path_outside_every_root_is_refused, tests/test_locations.py::TestMediaPathContainment::test_a_url_that_is_not_http_is_refused, tests/test_locations.py::TestMediaPathContainment::test_an_upper_case_https_url_is_not_refused_as_another_scheme, tests/test_security_ssrf.py::TestInternalAddressSpellings::test_a_name_that_resolves_inside, tests/test_locations.py::TestTaskMediaArguments::test_an_unreadable_source_is_an_error_at_its_path |
| Outbound requests | dw/outbound.py: safe_get, safe_post |
Every outbound request is validated per hop against dw/locations.py's policy, resolved once and dialed at the address the policy checked, capped and timed. No other module under dw/ makes an HTTP request or builds a session or adapter. |
tests/test_outbound.py::TestSafeRequest::test_one_lookup_per_hop_and_the_dial_is_the_checked_address |
| Trust gate | dw/trust.py: require_trusted_dotted_name |
In an untrusted workflow (the default), a dotted type outside the allowlist is a validation error before anything is imported. | tests/test_security_trust_gate.py::TestValidationRefusesBeforeImport::test_a_dotted_type_is_a_validation_error |
| Remote generate code | dw/trust.py: require_trusted_generate_arguments; dw/tasks/text_generation.py: _validated_generate_kwargs, _validated_chat_template_kwargs |
Untrusted, generate_kwargs may not set custom_generate or trust_remote_code (transformers would download and run a repo's custom_generate/generate.py); both it and chat_template_kwargs hold only scalars (or, for generate_kwargs, lists of them) and none of the pipeline call's own keys. All checked before the model loads. |
tests/test_security_trust_gate.py::TestRemoteGenerateCodeUntrusted::test_refused_before_the_model_loads, tests/test_text_generation.py::TestGenerateKwargsAreFiltered::test_a_structural_key_is_refused_before_any_load, tests/test_text_generation.py::TestGenerateKwargsAreFiltered::test_a_nested_value_is_refused |
| Hub-file system prompts | dw/tasks/text_generation.py: resolve_system_prompt |
A system_prompt.from_hub file is pinned to a full commit, asked of the Hub anonymously before every use (failing closed on any error, offline or under HF_HUB_OFFLINE), downloaded with token=False, and refused over 1 MiB or when the Hub reports no size. |
tests/test_text_generation.py::TestSystemPromptFromHub::test_a_file_the_hub_will_not_serve_publicly_is_refused, tests/test_text_generation.py::TestSystemPromptFromHub::test_the_download_never_carries_the_server_token, tests/test_text_generation.py::TestSystemPromptFromHub::test_a_file_of_unknown_size_is_refused_before_downloading |
| CodeQL path-injection model | .github/codeql/dw-security/DwPathSanitizers.qll, .github/workflows/codeql.yml |
The local pack models the dw/security.py validators (and dw/locations.py's validate_media_path) as sanitizers, so a validator that moves is re-modelled in the same commit. |
.github/codeql/dw-security/DwPathInjection.ql |
| Archives never follow links | dw/server/outputs.py: zip_download |
A gallery listing drops a symlink that leaves its root, and an archive skips one. | tests/test_security_symlinks.py::TestOutputs::test_the_archive_route_does_not_follow_the_link, tests/test_security_symlinks.py::TestOutputs::test_the_gallery_listing_does_not_enumerate_the_link |
| Engine/server import direction | dw/workspace.py: EXPORTS_SUBDIR |
No engine module imports dw.server except the entry point dw/serve.py, and a constant both sides need lives engine-side. |
docs/stabilization/baseline.json: import_cycles ratchet (indirect: it fails only on an import that closes a cycle) |
| Module and function size | scripts/arch_metrics.py: SIZE_WARNING, SIZE_CEILING |
A module stays at or under 1,100 lines, and it warns above 1,000. A function stays at or under 150 lines. | docs/stabilization/baseline.json: modules_over_size_ceiling, functions_over_150_lines |
| Concept | Owner | Rule | Enforced by |
|---|---|---|---|
| App factory | dw/server/app.py: create_app |
create_app builds the JobManager and app.state, installs the middleware and registers the routes, and all state lives in the JobManager. |
— |
| Routers | dw/server/routes/*.py, dw/server/routes/__init__.py: ROUTERS, include_routers, include_file_routes |
Routers register in ROUTERS order, because a greedy {name:path} route must come after its specific siblings. The files router comes later, after /mcp. |
tests/test_server_downloads.py::test_download_workflow_sets_content_disposition_attachment |
| Job queue | dw/server/jobs.py: JobManager; dw/server/job_results.py: consume_results |
A dispatcher thread hands queued jobs, in queue order, to the pool's workers (Worker pool dispatch); each worker runs one job at a time, and consume_results folds that worker's replies into the job, naming files relative to the job's own output_dir. A job carries its own output_dir, asset_dir and workflow_dir, so it stays in its workspace. |
tests/test_server_workspaces.py::TestRunning::test_rerun_stays_in_the_workspace_it_ran_in (FIFO: —) |
| Worker pool dispatch | dw/server/dispatch.py: Dispatcher._next_dispatch, Dispatcher._host_ram_wait (mixed into JobManager); dw/server/pool.py: WorkerSlot, check_fits, largest_ceiling_gb, host_ram_wait; dw/vram_estimate.py: required_vram_gb; dw/server/admission.py: pool_capacity |
--devices runs one worker per card; a queued job goes to a free card that fits what it needs (none starts while a cache clear holds dispatch, dw disk caches), a job that fits no free card keeps its place while a smaller one behind it takes the card, a job bigger than every card is refused at submit, a job that fits a free card is also held while its projected host-RAM peak exceeds MemAvailable less what running jobs have yet to add plus the idle worker's own RSS (a job alone is never held, no figure or no RAM reading means no gate, a smaller job behind a held one starts, and the dispatcher re-checks every 10 s; #869), and cancel or a crash touches only the worker running that job. On a backend no cost entry describes, a declared vram_estimate is held at admission to the pool's largest card, so a job the first card cannot hold is not refused when a larger card can hold it. |
tests/test_worker_pool.py::TestConcurrency::test_two_workers_run_two_jobs_at_once, tests/test_worker_pool.py::TestBackfill::test_a_job_too_big_for_the_free_card_waits_while_a_smaller_one_takes_it, tests/test_worker_pool.py::TestFitAtSubmit::test_a_job_too_big_for_every_card_is_refused_naming_the_largest, tests/test_worker_pool.py::TestCancel::test_cancel_reaches_the_worker_running_the_job, tests/test_worker_pool.py::TestCrashIsolation::test_a_crashed_worker_fails_only_its_own_job, tests/test_worker_pool.py::TestOomRanking::test_each_dispatch_scores_its_worker_highest, tests/test_worker_pool.py::TestFitAtSubmit::test_a_job_only_the_larger_card_holds_is_admitted, tests/test_worker_pool.py::TestHostRamGate::test_a_job_the_host_cannot_hold_beside_the_running_one_is_held, tests/test_worker_pool.py::TestHostRamGate::test_a_job_alone_is_never_held, tests/test_worker_pool.py::TestHostRamGate::test_a_smaller_job_starts_past_a_held_one |
| Card affinity | dw/server/pool.py: choose_slot; dw/server/jobs.py: JobManager.route; dw/worker_protocol.py: workflow_identity |
Among the free cards a job fits it goes to the rerun's original card, else the card whose worker last ran its workflow identity (workflow_identity, the one rule the worker caches by too), else one with no worker running, else the first in --devices order; a cache probe and plan.estimate (priced_for) ask about the card route names, and memory is read and cleared per card. |
tests/test_worker_pool.py::TestIdentityAffinity::test_a_second_run_lands_on_the_card_that_ran_the_workflow_first, tests/test_worker_pool.py::TestRerunAffinity::test_a_rerun_lands_on_the_original_card_when_it_is_free, tests/test_worker_pool.py::TestProbeRouting::test_the_probe_goes_to_the_worker_the_job_would_be_routed_to, tests/test_worker_pool.py::TestMemoryPerCard::test_a_named_card_running_a_job_is_refused_though_the_other_is_idle |
| Per-card memory and cache | dw/server/worker_memory.py: memory_status, clear_memory, probe_cache |
Each card keeps its own last memory reading; an idle card's worker is asked live, a busy one answers with the run's last report and says why it is not live, and a clear leaves a card running a job alone. Every request takes the card's lock with a bounded wait, and a card's lock is never taken while the JobManager's lock is held. | tests/test_worker_pool.py::TestMemoryPerCard::test_a_named_card_running_a_job_is_refused_though_the_other_is_idle, tests/test_worker_pool.py::TestProbeRouting::test_the_probe_goes_to_the_worker_the_job_would_be_routed_to, tests/test_server.py::test_memory_says_why_a_reading_is_not_the_worker_s |
| dw disk caches | dw/disk_caches.py; dw/server/jobs.py: JobManager.hold_dispatch; dw/server/routes/system.py: clear_cache, delete_cache_entry |
Members inductor, triton, quantized. Clear only a root carrying the .dw-cache marker, never while a job is running or queued (quantized also not during a model download); an entry delete takes only a 64-hex id, checked before any path is built; clear renames into .clearing-* then deletes. The busy check and the dispatch hold are one step in JobManager.hold_dispatch, kept for the whole clear or entry delete: a job submitted meanwhile is accepted but stays queued until it ends. |
tests/test_disk_caches.py, tests/test_disk_caches.py::test_job_submitted_during_a_clear_waits_for_it |
| Job-to-run link | dw/server/job_record.py, dw/server/jobs.py: JobManager.realized |
A job records run_id, run_dir and run_version, and JobManager.realized reads that run's workflow.json, confined to the output root. |
tests/test_server_jobs.py::test_realized_reads_the_file_the_run_wrote, tests/test_server_jobs.py::test_realized_refuses_a_run_dir_that_escapes_the_output_root |
| Job history | dw/server/job_history.py: JobHistory, _ADDED_COLUMNS |
Finished jobs persist in jobs.sqlite, in WAL mode, so the Jobs view survives a restart. A database from an older release gains each column it lacks from the _ADDED_COLUMNS table when it is opened. |
tests/test_server.py::test_job_history_survives_restart_and_reruns, tests/test_job_history_wal.py::test_the_jobs_database_uses_wal_mode, tests/test_server_jobs.py::test_a_v0_13_0_database_gains_the_columns_added_since |
| Job fields, live and persisted | dw/server/job_record.py: Job.summary, Job.detail; dw/server/job_history.py: _RECORDED, _ADDED_COLUMNS, recent_summaries, _to_detail |
A job field is declared once per side: on Job (and its summary()/detail()), and in history as an _ADDED_COLUMNS entry plus one (column, getter) in _RECORDED. History readers take columns by name, never by position, and a historical job serves the same summary and detail keys a live one does, apart from the live-only poll state and the history-only markers the parity test declares. |
tests/test_job_history_parity.py::test_history_serves_the_keys_a_live_job_does |
| HTTP security | dw/server/http_security.py: install_middleware, query_token_ok |
Every API route needs the token, and only a route marked query_token_ok takes it as ?token=. |
tests/test_security_auth.py::TestTheTokenGate::test_every_api_spelling_needs_the_token, tests/test_security_auth.py::TestTheTokenGate::test_the_query_token_is_refused_where_it_is_not_allowed |
| Prompt enhancement | dw/server/enhancers.py: PRESETS, _T2I_SYSTEM_PROMPT |
PRESETS is the Enhance panel's menu (label, curated LLMs, preselect hints); a preset's family-specific knowledge, its spec, lives in a builtin under dw/workflows/, never in Python. t2i's generic, family-agnostic system prompt stays in Python. A third preset moves the menu into builtin metadata (#696). |
tests/test_prompt_library.py::test_every_enhancer_preset_targets_a_known_family, tests/test_server.py::TestEnhance::test_presets_are_listed, tests/test_server.py::TestEnhance::test_enhance_workflows_are_schema_valid, tests/test_server.py::TestEnhance::test_an_unknown_preset_is_a_client_error |
| Concept | Owner | Rule | Enforced by |
|---|---|---|---|
dw_mcp stays torch-free |
dw_mcp/ |
dw_mcp reaches dw.serve over HTTP and imports no dw module, because dw/__init__.py pulls in torch. |
tests/test_mcp_server.py::TestStartupWeight::test_the_server_starts_without_importing_the_engine |
| Tool surface | dw_mcp/server.py, dw_mcp/tools_*.py |
Only these modules import the MCP SDK, and each tool body is a one-line call into a handler. | tests/test_mcp_server.py::test_the_wiring_table_covers_every_registered_tool, tests/test_mcp_server.py::test_the_stated_tool_count_is_the_registered_one |
| Surface text budget | the tool docstrings in dw_mcp/tools_*.py, the instructions in dw_mcp/server.py |
The instructions and each tool description stay at or under 2,048 characters, and the whole surface stays within its token budget. | tests/test_mcp_server.py: SURFACE_BUDGET, CLIENT_TEXT_LIMIT; tests/test_mcp_server.py::test_no_text_the_agent_reads_is_cut_off_by_the_client, tests/test_mcp_server.py::test_the_tool_surface_fits_the_budget |
| Guide pointers in resident text | the tool docstrings in dw_mcp/tools_*.py, the instructions in dw_mcp/server.py, each plugin SKILL.md and its references/ |
Every get_guide(...) / guide-section pointer opens a real section through dw/server/guides.py's get_guide, and the tools that point at a guide keep their pointer when text is trimmed to fit the budget. |
tests/test_guide_pointers.py (TOOLS_THAT_POINT) |
| Spending needs consent | each handler behind a tool that takes acknowledged_cost (dw_mcp/diagnose.py, models.py, prompts.py, workspaces.py) |
Every tool that takes acknowledged_cost refuses until it is set; delete_workspace's refusal is the server's 409. |
tests/test_mcp_twins.py::test_every_tool_that_takes_acknowledged_cost_refuses_without_it |
| Copies of engine rules | the constants and word lists in dw_mcp/ and dw/run.py |
dw_mcp cannot import its owners, so each copy equals its owner; a copy that needs no twin is deleted, not pinned. |
tests/test_mcp_twins.py |
| The CLI is a client of the server | dw/run.py |
dw.run queues on dw.serve through dw_mcp.client, so dw imports dw_mcp and never the reverse. |
tests/test_mcp_server.py::TestStartupWeight::test_the_server_starts_without_importing_the_engine, tests/test_mcp_twins.py |
| Inline images | dw/server/inline_media.py |
Fitting an image to a longest side and halving it under a base64 budget happens on the server, for /image and /frames alike; dw_mcp forwards the size and budget and imports no Pillow. |
tests/test_server_gallery_image.py, tests/test_mcp_server.py::TestStartupWeight::test_the_server_starts_without_importing_the_engine |
| Workflow patch | dw/library.py: merge_patch; dw/server/routes/library.py: patch_workflow |
A merge patch is applied on the server under the lock PUT takes; no client reads, merges and writes back. |
tests/test_server_library_path.py::TestPatchWorkflow::test_patch_merges_onto_the_stored_definition |
| Deleting a job's run | dw/server/jobs.py: JobManager.run_location; dw/server/outputs.py: delete_run_directory |
The server resolves a job's run directory against the root the job ran in, and refuses a job still queued or running. | tests/test_server_jobs.py::TestDeleteAJobsRun::test_a_running_job_is_409 |
| Level findings | dw/server/assess.py: level_findings |
Gallery metadata reports level problems from dw/audio_qc.py's thresholds; no client restates a threshold. The silent-by-design exemption to the near-silent rule is dw/audio_qc.py: all_shots_silent_by_design, used by this row and by the write-time warning. |
tests/test_server_assess.py::TestMetadataLevelFindings::test_level_findings_read_audio_qc_thresholds |
| API errors | dw_mcp/client.py |
An API failure becomes a message a person can act on, here and nowhere else. | tests/test_mcp_client.py::test_a_400_surfaces_the_servers_detail_verbatim |
| Concept | Owner | Rule | Enforced by |
|---|---|---|---|
| The UI reads engine fields | ui/src/lib/plan.ts: describePlan, ui/src/lib/results.ts: sectionBySubfolder |
The UI reads plan, version and subfolder as fields the server sends and derives nothing of its own, and it never sends acknowledged_cost. |
ui/src/lib/plan.test.ts, ui/src/lib/results.test.ts |
| Reference prefixes in the UI | ui/src/lib/references.ts |
The only module in ui/src that spells a reference prefix; each equals dw/references.py's. |
tests/test_ui_twins.py::test_the_ui_spells_every_reference_prefix_the_engine_does; prefix_literals in ui/scripts/arch-metrics.mjs |
| Output kinds on the job page | dw/server/outputs.py: MEDIA_KINDS, output_kinds |
The page renders an output by the output_kinds the job detail carries, never by its extension. |
tests/test_server.py::test_a_job_reports_each_output_files_media_kind, ui/src/lib/pages/JobPage.test.ts |
| The editor's live reference check | ui/src/lib/flow.ts: danglingReferenceDetails, owned by dw/previous_results.py and dw/for_each.py |
The editor warns as the author types, so it keeps a copy of the reference rules; one case file is run through both, the engine deciding each case. | tests/test_ui_twins.py::test_the_engine_decides_each_shared_reference_case, ui/src/lib/flow.test.ts |
| Name segments in the UI | ui/src/lib/names.ts: isNameSegment |
Workspaces, folders and stored names follow dw/security.py's WORKSPACE_NAME_PATTERN and length cap, counted in code points. |
tests/test_ui_twins.py::test_the_engine_decides_each_shared_workspace_name_case, ui/src/lib/names.test.ts |
| UI copies of engine rules | the constants tests/test_ui_twins.py reads |
A list the UI must hold equals its owner, read from the TS source; a rule tested on both sides reads one case file in tests/fixtures/. |
tests/test_ui_twins.py |
| UI overlays and suggestions | ui/src/lib/ui/: ConfirmDialog, Modal, Popover, Suggest |
Bits UI is imported only here; pages and components use the wrappers, which own focus, Escape, outside presses and scroll lock. Overlay styles and the stacking scale (--layer-*) live in ui/src/app.css. |
ui/eslint.config.js (no-restricted-imports), ui/src/lib/ui/*.test.ts, ui/e2e/chrome.spec.ts |
| Escape on a page under an overlay | ui/src/lib/ui/layers.svelte.ts: holdLayer, overlayOpen |
Every open overlay holds a layer; a page's own Escape handling acts only when none is open. | ui/src/lib/pages/GalleryPage.test.ts (an Escape that closes a confirm leaves the selection) |
| The editors' document | ui/src/lib/editorShell.svelte.ts: DocumentEditor |
The workflow and prompt editors keep one rule each for the view (remembered per editor), the JSON draft (a failed parse pins it), dirty against the last load or save, the save path, and the tab-close guard. | ui/src/lib/editorShell.test.ts, ui/src/lib/pages/PromptEditorPage.test.ts, ui/src/lib/pages/EditorPage.test.ts |
| UI polling | ui/src/lib/poll.ts: poll, sleep |
A page that refreshes on a timer starts it through poll and stops it with the function poll returns. |
ui/src/lib/poll.test.ts |
| Workspace state in the UI | ui/src/lib/workspaceState.svelte.ts: workspace |
The current workspace is one $state; api.ts reads it from here, so the request layer imports no page state. |
import_cycles in ui/scripts/arch-metrics.mjs |
| The UI's response contract | dw/server/api_models.py (the models; it re-exports dw/server/api_models_base.py: ApiModel, send_rejected_responses, and the library routes' models in dw/server/api_models_library.py); scripts/dump_openapi.py |
Every JSON route api.ts calls declares a response model, and types.ts re-exports the types generated from it (ui/src/lib/generated/). The payload does not change: an absent key stays absent (response_model_exclude_unset), a sometimes-sent key is sometimes(). Runtime is lenient - an undeclared key passes and a rejected response is logged and sent as built; tests, the e2e server and the dump run strict (DW_STRICT_RESPONSES=1). The document is generated under the FastAPI and Pydantic constraints-openapi.txt pins: CI installs them, and the dump refuses to write under others. |
tests/test_api_contract.py, tests/test_ui_twins.py::test_every_json_route_the_ui_calls_declares_its_response, CI's "Response contract" step |
Where --live appears |
ui/src/app.css (its header) |
--live marks machine state only and --select the user's place only (focus ring, sidebar), each in the files ui/scripts/design-rules.test.ts lists. |
ui/scripts/design-rules.test.ts |
| UI architecture ratchet | ui/scripts/arch-metrics.mjs |
No metric rises above docs/stabilization/ui/baseline.json. |
the ui job in .github/workflows/ci.yml; npm run preflight |