FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [View Raw Code]   [Original HTTPS Page]

diffusers-workflow/docs/ARCHITECTURE.md at master · dkackman/diffusers-workflow · GitHub

Repository navigation

Latest commit

 

History

History
193 lines (170 loc) · 61 KB

File metadata and controls

193 lines (170 loc) · 61 KB

Architecture: the seam map

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.

  • tests/test_architecture_map.py checks 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.

Engine core

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
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 A pipeline with no step callback reports its denoise steps through its progress bars. tests/test_modular_progress.py::test_every_advance_of_the_bar_is_reported
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, 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 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 A literal reference to a step that is not earlier in the workflow is a validation error. tests/test_previous_results.py::TestStaticReferenceChecking::test_a_step_cannot_reference_itself_or_a_later_one
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

References and libraries

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
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

Runs and outputs

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
Run versions dw/runs.py: open_run, run_versions, record_run_versions A run takes its number once, when it opens, 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
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
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 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. —

Validation, plan and cost

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, 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. 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.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
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 plan.estimate quotes the observed figure ahead of the curated one, and blends it toward the curated figure below three runs. 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
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. tests/test_vram_inheritance.py::test_every_template_declaring_one_identity_declares_the_same_numbers
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 and frame count set differently from the template. 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::TestThePlanReportsIt::test_the_estimate_is_inherited_and_the_validate_answer_warns
H3 adapter partition dw/adapter_compatibility.py An FL2VA LoRA on a reference step is refused, and a LoRA whose file 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
H3 audio hold, refine and guide layout Five 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 asked before validation and before the call; not_h3, the step rule; snap_guide_length, guide_frame_problem, guide_chain_problems, guide_end_problem; CHAIN_CONTINUITY_MODES; the chunk constants), dw/pipeline_processors/h3_hold.py puts the hold and refine blocks in (insert_audio_hold, core_denoise_sequences, refines), and dw/pipeline_processors/h3_guides.py the guide layout (LAYOUT_ANCHORS, insert_guides), 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 dw/pipeline_processors/h3_guide_steps.py (splice_guide_rows), which import diffusers when they load, so the two owners import them only inside blocks() and guide_blocks() (#790), and h3_guide_steps imports h3_hold_steps and never the reverse (_with_guides in dw/pipeline_processors/pipeline.py adds its blocks for a guides 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/hold_audio.py: hold_audio_errors, refine_strength_errors 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 they are no-ops without hold_audio / refine_strength. 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_refine.py::TestSigmaGridDrift::test_the_full_grid_is_stock_set_timesteps, 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
IC-LoRA reference scale workflows/templates/ltx2/ The three 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

Execution and caching

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. 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. tests/test_worker_messages.py::test_from_wire_inverts_to_wire, 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, 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_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

Media and DSP

Concept Owner Rule Enforced by
Opening media dw/media.py This is the only module that calls av.open. It imports 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: —)
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/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. 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). 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
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

Security and architecture guardrails

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
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

Server

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/jobs.py: JobManager._next_dispatch; dw/server/pool.py: WorkerSlot, check_fits, largest_ceiling_gb; 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, 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, 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_the_later_started_worker_is_ranked_first, tests/test_worker_pool.py::TestFitAtSubmit::test_a_job_only_the_larger_card_holds_is_admitted
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
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 Finished jobs persist in jobs.sqlite, in WAL mode, so the Jobs view survives a restart. tests/test_server.py::test_job_history_survives_restart_and_reruns, tests/test_job_history_wal.py::test_the_jobs_database_uses_wal_mode
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

MCP

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
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. 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

UI

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: ApiModel, send_rejected_responses; 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) The state colour marks machine state only, 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

Back | FazBrowse Home | New Git URL