# Local development for Data Pipelines Status: **draft PR, validated end-to-end.** Both the headless CLI paths and the live browser preview (`pipeline dev` → `/pipeline_dev`) have been exercised against a running EE + MinIO stack — headless `pipeline run --local`, browser `Run` / `Run + downstream` cascades writing real assets, live-reload on save, failure/cascade UX, parameterized run-forms, and the `--frontend` flag. This document captures the design and the remaining follow-ups. ## What & why Windmill "data pipelines" are folders of scripts marked with a `// pipeline` comment, wired together by asset annotations (`// on `, `// partitioned`, `// schedule`). Until now they could only be built/run in the browser at `/pipeline/` (the `PipelineGraphEditor`), and the CLI only inspected/ran the **deployed** workspace (`wmill pipeline list|show|run`). This adds the **local edit → preview → run** loop, the pipeline analog of `wmill dev` (flows/scripts) and `wmill app dev` (raw apps), usable both from a code editor and from an agentic loop — building a pipeline from working-tree files, seeing the same graph the UI shows, and running it, all **without deploying**. ## The key design decision (no backend changes) Full body inference is obtained in the CLI from the **same wasm the frontend uses**: `windmill-parser-wasm-asset` (`parse_assets_ts | parse_assets_py | parse_assets_sql`). That wasm returns the entire serialized Rust `ParseAssetsOutput` — `assets` (with `r`/`w`/`rw` access) **and** the parsed pipeline annotations (`in_pipeline`, `triggers`, `partition`, …) in one call. The CLI already loads sibling wasm parsers via `loadParser()`; we just added the `-asset` dep. Running local content reuses the existing preview API: `runScriptPreview({ content, language, path, args: { _wmill_skip_asset_dispatch: true }, temp_script_refs })` per node in topological order. Data flows through real asset storage; `_wmill_skip_asset_dispatch` makes the client own the whole cascade so the backend dispatcher never double-fires. ⇒ No new backend endpoint, no Rust changes, no TS re-port of the annotation parser. ## Surfaces ### Headless CLI (agentic loop) — `cli/src/commands/pipeline/` - `pipeline show --local` — render the DAG from working-tree files (fully offline). - `pipeline run --local [--from/--to/--dry-run/--json]` — run the cascade via preview of local content, reusing the `boundedCascade.ts` topo/lineage engine. Scripts whose only trigger needs caller input or per-event fanout (`data_upload`/`webhook`/kafka/…) are skipped by default. - `pipeline run … --upload