Extending the workflow engine
docs/workflow_development.md is the authoritative guide. Read its
"Adding things to the workflow engine" section first (new workflow, media
runner, spec test, report kind) — this skill covers the v1-side wiring in
workflows/workflow_dispatch.py that the doc only mentions in passing, plus the
end-to-end verification people skip.
How the two halves connect
Repo-root run.py is the only user-facing entry point. It does NOT import the
engine modules — it bridges through workflows/workflow_dispatch.py:
can_dispatch_to_engine(model_spec, runtime_config) decides routing (run.py:961).
If it returns false for a supported workflow, run.py raises rather than falling
back to a legacy path.
build_engine_commands(...) builds VenvCommands that run run_workflows.py
inside the WorkflowVenvType.WORKFLOW_RUN_SCRIPT venv.
run_workflows.py re-imports v1's workflows.model_spec.MODEL_SPECS via a
sys.path insert — the model catalog (workflows/model_specs/prod/*.yaml) is
shared, not duplicated.
Recurring failure mode: code works when invoking run_workflows.py directly but is
unreachable from run.py because a bridge table or a forwarding helper below was
not updated.
v1-side wiring checklist (the part people forget)
All in workflows/workflow_dispatch.py unless noted.
- Onboarding a model → routing is now by model type, not per-name.
If the model's
ModelType is already in _ENGINE_ROUTED_MODEL_TYPES
(frozenset, checked by is_engine_routed_model), a new model of that type is
picked up automatically — just add it to the shared catalog
workflows/model_specs/prod/<type>.yaml (or dev/ for --dev-mode).
A brand-new model type must be added to _ENGINE_ROUTED_MODEL_TYPES.
(LLM/VLM route via the workflow-specific _is_*_run predicates in
can_dispatch_to_engine, not the frozenset.)
- New workflow → register it in the engine's
WORKFLOW_REGISTRY
(workflow_module/workflows.py), and map its WorkflowType to the engine
workflow name in _ENGINE_WORKFLOW_NAMES. If no fitting WorkflowType exists,
add one to the enum in workflows/workflow_types.py — otherwise it can never be
selected via run.py --workflow.
- New CLI flag → forwarding is explicit, not pass-through. Add the flag to
run.py argparse, then forward it in the relevant _forward_* /
_build_*_cmd helper (use _extend_if_set(cmd, "--flag", value)), and give
run_workflows.py a matching add_argument. If a v1 flag is deliberately
unsupported on the engine path, add it to _warn_on_unsupported_args so users
get a warning instead of silent ignoring.
- Runner needs an extra host venv → define a
WorkflowVenvType + VenvConfig
in workflows/workflow_venvs.py and map model_type → venv in
_ENGINE_EVAL_VENV_BY_MODEL_TYPE. _engine_dependency_venv_types only
provisions venvs listed there, and only for _ENGINE_EVAL_WORKFLOWS.
Engine-side registries (quick map)
Details in docs/workflow_development.md; the registries to touch:
- Workflows:
WORKFLOW_REGISTRY in workflow_module/workflows.py; optionally
ReleaseWorkflow.children and a new MediaTaskType wired in run_media_task.
- Media runners:
BENCHMARK_DISPATCH / EVAL_DISPATCH in
test_module/dispatch.py, keyed by model_spec.model_type.name. The value is
the runner's function name (string); _resolve_runner imports it lazily.
Export it from test_module/benchmark_tests/__init__.py (or eval_tests/).
- Spec tests: data-driven via
test_module/test_suites/<category>.json; guides
in test_module/test_categorization_system/TEST_SUITE_CONFIG_GUIDE.md and
TEST_MARKING_SYSTEM.md. New test classes subclass BaseTest from
_test_common/base_test.py.
- Report kinds: renderer in
report_module/renderers.py (schema rules in
tests/report_module/SCHEMA_GUIDE.md); acceptance in
report_module/acceptance_criteria.py (_check_<kind> + category list in
acceptance_criteria_check).
Verify the wiring end-to-end
Always finish by exercising run.py, not run_workflows.py directly:
python run.py --model <model> --workflow <workflow> --device <device> ...
- The run log must contain
Model <name> (model_type=<TYPE>) routes through the workflow engine. (run.py:967) followed by Delegating workflow '<name>' to workflow engine. (workflow_dispatch.py:297). If those are missing, the routing
in step 1/2 is not wired.
- For flag changes, confirm the value survives the hop: inspect the engine
subprocess command logged during the run, and
python run_workflows.py --help
for the receiving argparse entry.
1---2name: extend-workflow-engine3description: Checklist for adding or modifying code in the workflow engine (run_workflows.py plus the llm_module/report_module/test_module/workflow_module packages) — new models, workflows, media runners, spec tests, report kinds, or CLI flags — and wiring it back to the entry point run.py through workflows/workflow_dispatch.py. Use whenever code lands in those modules, when onboarding a model type to the engine, or when an engine feature "works standalone via run_workflows.py but not through run.py".4---56# Extending the workflow engine78`docs/workflow_development.md` is the authoritative guide. Read its9**"Adding things to the workflow engine"** section first (new workflow, media10runner, spec test, report kind) — this skill covers the v1-side wiring in11`workflows/workflow_dispatch.py` that the doc only mentions in passing, plus the12end-to-end verification people skip.1314## How the two halves connect1516Repo-root `run.py` is the only user-facing entry point. It does NOT import the17engine modules — it bridges through `workflows/workflow_dispatch.py`:1819- `can_dispatch_to_engine(model_spec, runtime_config)` decides routing (`run.py:961`).20 If it returns false for a supported workflow, `run.py` raises rather than falling21 back to a legacy path.22- `build_engine_commands(...)` builds `VenvCommand`s that run `run_workflows.py`23 inside the `WorkflowVenvType.WORKFLOW_RUN_SCRIPT` venv.24- `run_workflows.py` re-imports v1's `workflows.model_spec.MODEL_SPECS` via a25 `sys.path` insert — the model catalog (`workflows/model_specs/prod/*.yaml`) is26 **shared**, not duplicated.2728Recurring failure mode: code works when invoking `run_workflows.py` directly but is29unreachable from `run.py` because a bridge table or a forwarding helper below was30not updated.3132## v1-side wiring checklist (the part people forget)3334All in `workflows/workflow_dispatch.py` unless noted.35361. **Onboarding a model** → routing is now by **model type**, not per-name.37 If the model's `ModelType` is already in `_ENGINE_ROUTED_MODEL_TYPES`38 (frozenset, checked by `is_engine_routed_model`), a new model of that type is39 picked up automatically — just add it to the shared catalog40 `workflows/model_specs/prod/<type>.yaml` (or `dev/` for `--dev-mode`).41 A brand-new model *type* must be added to `_ENGINE_ROUTED_MODEL_TYPES`.42 (LLM/VLM route via the workflow-specific `_is_*_run` predicates in43 `can_dispatch_to_engine`, not the frozenset.)442. **New workflow** → register it in the engine's `WORKFLOW_REGISTRY`45 (`workflow_module/workflows.py`), **and** map its `WorkflowType` to the engine46 workflow name in `_ENGINE_WORKFLOW_NAMES`. If no fitting `WorkflowType` exists,47 add one to the enum in `workflows/workflow_types.py` — otherwise it can never be48 selected via `run.py --workflow`.493. **New CLI flag** → forwarding is explicit, not pass-through. Add the flag to50 `run.py` argparse, then forward it in the relevant `_forward_*` /51 `_build_*_cmd` helper (use `_extend_if_set(cmd, "--flag", value)`), and give52 `run_workflows.py` a matching `add_argument`. If a v1 flag is deliberately53 unsupported on the engine path, add it to `_warn_on_unsupported_args` so users54 get a warning instead of silent ignoring.554. **Runner needs an extra host venv** → define a `WorkflowVenvType` + `VenvConfig`56 in `workflows/workflow_venvs.py` and map `model_type → venv` in57 `_ENGINE_EVAL_VENV_BY_MODEL_TYPE`. `_engine_dependency_venv_types` only58 provisions venvs listed there, and only for `_ENGINE_EVAL_WORKFLOWS`.5960## Engine-side registries (quick map)6162Details in `docs/workflow_development.md`; the registries to touch:6364- **Workflows**: `WORKFLOW_REGISTRY` in `workflow_module/workflows.py`; optionally65 `ReleaseWorkflow.children` and a new `MediaTaskType` wired in `run_media_task`.66- **Media runners**: `BENCHMARK_DISPATCH` / `EVAL_DISPATCH` in67 `test_module/dispatch.py`, keyed by `model_spec.model_type.name`. The value is68 the runner's function *name* (string); `_resolve_runner` imports it lazily.69 Export it from `test_module/benchmark_tests/__init__.py` (or `eval_tests/`).70- **Spec tests**: data-driven via `test_module/test_suites/<category>.json`; guides71 in `test_module/test_categorization_system/TEST_SUITE_CONFIG_GUIDE.md` and72 `TEST_MARKING_SYSTEM.md`. New test classes subclass `BaseTest` from73 `_test_common/base_test.py`.74- **Report kinds**: renderer in `report_module/renderers.py` (schema rules in75 `tests/report_module/SCHEMA_GUIDE.md`); acceptance in76 `report_module/acceptance_criteria.py` (`_check_<kind>` + category list in77 `acceptance_criteria_check`).7879## Verify the wiring end-to-end8081Always finish by exercising `run.py`, not `run_workflows.py` directly:8283```bash84python run.py --model <model> --workflow <workflow> --device <device> ...85```8687- The run log must contain `Model <name> (model_type=<TYPE>) routes through the88 workflow engine.` (`run.py:967`) followed by `Delegating workflow '<name>' to89 workflow engine.` (`workflow_dispatch.py:297`). If those are missing, the routing90 in step 1/2 is not wired.91- For flag changes, confirm the value survives the hop: inspect the engine92 subprocess command logged during the run, and `python run_workflows.py --help`93 for the receiving argparse entry.