[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-astronomer-blueprint":3,"mdc-u363n3-key":55,"related-org-astronomer-blueprint":6305,"related-repo-astronomer-blueprint":6464},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":25,"repoUrl":26,"updatedAt":27,"license":28,"forks":29,"topics":30,"repo":50,"sourceUrl":53,"mdContent":54},"blueprint","build reusable Airflow task group templates","Define reusable Airflow task group templates with Pydantic validation and compose DAGs from YAML. Use when creating blueprint templates, composing DAGs from YAML, validating configurations, or enabling no-code DAG authoring for non-engineers.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"astronomer","Astronomer","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fastronomer.png",[12,16,19,22],{"name":13,"slug":14,"type":15},"Airflow","airflow","tag",{"name":17,"slug":18,"type":15},"ETL","etl",{"name":20,"slug":21,"type":15},"Data Pipeline","data-pipeline",{"name":23,"slug":24,"type":15},"Templates","templates",412,"https:\u002F\u002Fgithub.com\u002Fastronomer\u002Fagents","2026-04-06T18:01:45.361425",null,55,[31,32,33,34,14,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49],"agentic-workflow","agents","ai","ai-agents","apache-airflow","claude","cursor","dag","data-engineering","data-pipelines","dbt","llm","mcp","orchestrator","skills","workflow-automation","workflow-management","workflow-orchestration","workflows",{"repoUrl":26,"stars":25,"forks":29,"topics":51,"description":52},[31,32,33,34,14,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49],"AI agent tooling for data engineering workflows.","https:\u002F\u002Fgithub.com\u002Fastronomer\u002Fagents\u002Ftree\u002FHEAD\u002Fskills\u002Fblueprint","---\nname: blueprint\ndescription: Define reusable Airflow task group templates with Pydantic validation and compose DAGs from YAML. Use when creating blueprint templates, composing DAGs from YAML, validating configurations, or enabling no-code DAG authoring for non-engineers.\n---\n\n# Blueprint Implementation\n\nYou are helping a user work with Blueprint, a system for composing Airflow DAGs from YAML using reusable Python templates. Execute steps in order and prefer the simplest configuration that meets the user's needs.\n\n> **Package**: `airflow-blueprint` on PyPI\n> **Repo**: https:\u002F\u002Fgithub.com\u002Fastronomer\u002Fblueprint\n> **Requires**: Python 3.10+, Airflow 2.5+, Blueprint 0.3.0+\n\n## Before Starting\n\nConfirm with the user:\n1. **Airflow version** ≥2.5\n2. **Python version** ≥3.10\n3. **Use case**: Blueprint is for standardized, validated templates. If user needs full Airflow flexibility, suggest writing DAGs directly or using DAG Factory instead.\n\n---\n\n## Determine What the User Needs\n\n| User Request | Action |\n|--------------|--------|\n| \"Create a blueprint\" \u002F \"Define a template\" | Go to **Creating Blueprints** |\n| \"Build a template from other templates\" | Go to **Composing Templates** |\n| \"Create a DAG from YAML\" \u002F \"Compose steps\" | Go to **Composing DAGs in YAML** |\n| \"Use a blueprint in an existing Python DAG\" \u002F \"Generate DAGs in a loop\" | Go to **Blueprints in Python DAGs** |\n| \"Customize DAG args\" \u002F \"Add tags to DAG\" | Go to **Customizing DAG-Level Configuration** |\n| \"Override config at runtime\" \u002F \"Trigger with params\" | Go to **Runtime Parameter Overrides** |\n| \"Post-process DAGs\" \u002F \"Add callback\" | Go to **Post-Build Callbacks** |\n| \"Validate my YAML\" \u002F \"Lint blueprint\" | Go to **Validation Commands** |\n| \"Set up blueprint in my project\" | Go to **Project Setup** |\n| \"Version my blueprint\" | Go to **Versioning** |\n| \"Generate schema\" \u002F \"Astro IDE setup\" | Go to **Schema Generation** |\n| Blueprint errors \u002F troubleshooting | Go to **Troubleshooting** |\n\n---\n\n## Project Setup\n\nIf the user is starting fresh, guide them through setup:\n\n### 1. Install the Package\n\n```bash\n# Add to requirements.txt\nairflow-blueprint>=0.3.0\n\n# Or install directly\npip install airflow-blueprint\n```\n\n### 2. Create the Loader\n\nCreate `dags\u002Floader.py`:\n\n```python\nfrom blueprint import build_all_dags\n\nbuild_all_dags()\n```\n\n> **Use `build_all_dags`, not `build_all`.** The function was renamed in 0.3.0 so the loader's import line contains the substring `dag`, which Airflow's safe-mode DAG file processor requires — otherwise the file is silently skipped and no DAGs appear. `build_all` still works as a deprecated alias (emits `DeprecationWarning`); migrate existing loaders.\n\nDAG-level configuration (schedule, description, tags, default_args, etc.) is handled via YAML fields and `BlueprintDagArgs` templates — see **Customizing DAG-Level Configuration**.\n\n### 3. Verify Installation\n\n```bash\nuvx --from airflow-blueprint blueprint list\n```\n\nIf no blueprints found, user needs to create blueprint classes first.\n\n> **Provider operators in the CLI.** The `uvx --from airflow-blueprint` environment is isolated and does **not** include the Airflow provider packages your Astro Runtime project has. If your templates import provider operators (BigQuery, Snowflake, etc.), add `--with` so the CLI can import them — otherwise `list`\u002F`lint`\u002F`schema` fail with `ModuleNotFoundError: No module named 'airflow.providers.X'`:\n>\n> ```bash\n> uvx --from airflow-blueprint --with apache-airflow-providers-google blueprint list --template-dir dags\u002Ftemplates\n> ```\n\n---\n\n## Creating Blueprints\n\nWhen user wants to create a new blueprint template:\n\n### Blueprint Structure\n\n```python\n# dags\u002Ftemplates\u002Fmy_blueprints.py\nfrom airflow.operators.bash import BashOperator\nfrom airflow.utils.task_group import TaskGroup\nfrom blueprint import Blueprint, BaseModel, Field\n\nclass MyConfig(BaseModel):\n    # Required field with description (used in CLI output and JSON schema)\n    source_table: str = Field(description=\"Source table name\")\n    # Optional field with default and validation\n    batch_size: int = Field(default=1000, ge=1)\n\nclass MyBlueprint(Blueprint[MyConfig]):\n    \"\"\"Docstring becomes blueprint description.\"\"\"\n\n    def render(self, config: MyConfig) -> TaskGroup:\n        with TaskGroup(group_id=self.step_id) as group:\n            BashOperator(\n                task_id=\"my_task\",\n                bash_command=f\"echo '{config.source_table}'\"\n            )\n        return group\n```\n\n### Key Rules\n\n| Element | Requirement |\n|---------|-------------|\n| Config class | Must inherit from `BaseModel` |\n| Blueprint class | Must inherit from `Blueprint[ConfigClass]` |\n| `render()` method | Must return `TaskGroup` or `BaseOperator` |\n| Task IDs | Use `self.step_id` for the group\u002Ftask ID |\n| Field types | Must be single-typed and YAML-compatible (see below) |\n\n### Config Field Types Must Be YAML-Compatible\n\nAs of 0.3.0, config fields must be single-typed. Multi-type unions like `str | int` or `Union[A, B]` are **rejected at class-definition time** (raises `TypeError`) because they produce ambiguous YAML parsing and `anyOf` schemas. The check recurses through nested models, list items, and dict values.\n\n- **Allowed**: scalars (`str`, `int`, `float`, `bool`), `Literal[...]`, `list[X]`, `dict[str, V]`, nested `BaseModel`, and `Optional[X]` \u002F `X | None` (the nullable pattern).\n- **Rejected**: `str | int`, `Union[A, B]`, or any union with more than one non-`None` arm. Bare `Any` and `dict[str, Any]` are rejected for the same reason — use an explicit single type for the value.\n\n### Internal Fields Not Settable from YAML\n\nUse `Field(default=..., init=False)` for fields used inside `render()` that should not be overridable from YAML. They are excluded from the constructor (always use their default) and omitted from JSON Schema output:\n\n```python\nclass ExtractConfig(BaseModel):\n    source_table: str\n    _internal_batch_multiplier: int = Field(default=4, init=False)\n```\n\n### Recommend Strict Validation\n\nSuggest adding `extra=\"forbid\"` to catch YAML typos:\n\n```python\nfrom pydantic import ConfigDict\n\nclass MyConfig(BaseModel):\n    model_config = ConfigDict(extra=\"forbid\")\n    # fields...\n```\n\n---\n\n## Composing Templates\n\nA blueprint can instantiate and render **other blueprints** inside its `render()` method, letting you build higher-level templates from lower-level building blocks while exposing a single, flat config to YAML authors.\n\nInside `render()`, instantiate each child blueprint, set its `step_id`, call `render(...)` with a config you construct, and wire the results together inside a parent `TaskGroup`:\n\n```python\nclass QualityGateConfig(BaseModel):\n    checks: list[str] = Field(default=[\"nulls\", \"duplicates\"])\n    report_channel: str = Field(default=\"data-alerts\")\n\nclass QualityGate(Blueprint[QualityGateConfig]):\n    \"\"\"Run checks then send a report — composed from Validate and Report.\"\"\"\n\n    def render(self, config: QualityGateConfig) -> TaskGroup:\n        with TaskGroup(group_id=self.step_id) as group:\n            validate = Validate()\n            validate.step_id = \"validate\"\n            validate_group = validate.render(ValidateConfig(checks=config.checks))\n\n            report = Report()\n            report.step_id = \"report\"\n            report_task = report.render(ReportConfig(channel=config.report_channel))\n\n            validate_group >> report_task\n        return group\n```\n\nYAML authors then see a single step with a flat config:\n\n```yaml\nsteps:\n  quality:\n    blueprint: quality_gate\n    checks: [nulls, duplicates, freshness]\n    report_channel: \"#data-alerts\"\n```\n\n---\n\n## Composing DAGs in YAML\n\nWhen user wants to create a DAG from blueprints:\n\n### YAML Structure\n\n```yaml\n# dags\u002Fmy_pipeline.dag.yaml\ndag_id: my_pipeline\nschedule: \"@daily\"\ndescription: \"My data pipeline\"\n\nsteps:\n  step_one:\n    blueprint: my_blueprint\n    source_table: raw.customers\n    batch_size: 500\n\n  step_two:\n    blueprint: another_blueprint\n    depends_on: [step_one]\n    target: analytics.output\n```\n\nBy default, only `schedule` and `description` are supported as DAG-level fields (via the built-in `DefaultDagArgs`). For other fields like `tags`, `default_args`, `catchup`, etc., see **Customizing DAG-Level Configuration**.\n\n### Reserved Keys in Steps\n\n| Key | Purpose |\n|-----|---------|\n| `blueprint` | Template name (required) |\n| `depends_on` | List of upstream step names |\n| `version` | Pin to specific blueprint version |\n| `trigger_rule` | Airflow trigger rule for the step (e.g. `all_done`, `one_success`); validated against the installed Airflow version |\n\nEverything else passes to the blueprint's config.\n\n### Trigger Rules (0.3.0)\n\nUse `trigger_rule` to control when a step runs relative to its upstream dependencies — for example, to run a notification step even if an upstream step failed:\n\n```yaml\nsteps:\n  analyze:\n    blueprint: analyze\n    depends_on: [extract]\n\n  notify:\n    blueprint: notify\n    depends_on: [analyze]\n    trigger_rule: all_done   # run regardless of whether analyze succeeded\n```\n\nValid values are validated dynamically against the installed Airflow's `TriggerRule` enum (`all_success`, `all_done`, `one_success`, `none_failed`, etc.). When the step's blueprint renders a `TaskGroup`, the rule is applied only to the group's **root** tasks (those with no internal upstream), preserving the blueprint author's internal wiring.\n\n### Jinja2 Support\n\nYAML supports Jinja2 templating with access to environment variables, Airflow variables\u002Fconnections, and runtime context:\n\n```yaml\ndag_id: \"{{ env.get('ENV', 'dev') }}_pipeline\"\nschedule: \"{{ var.value.schedule | default('@daily') }}\"\n\nsteps:\n  extract:\n    blueprint: extract\n    output_path: \"\u002Fdata\u002F{{ context.ds_nodash }}\u002Foutput.csv\"\n    run_id: \"{{ context.dag_run.run_id }}\"\n```\n\nAvailable template variables:\n- `env` — environment variables\n- `var` — Airflow Variables\n- `conn` — Airflow Connections\n- `context` — proxy that generates Airflow template expressions for runtime macros (e.g. `context.ds_nodash`, `context.dag_run.conf`, `context.task_instance.xcom_pull(...)`)\n\n---\n\n## Blueprints in Python DAGs\n\nBlueprints aren't tied to the YAML composition flow. Two patterns (both 0.3.0) let you use them from Python — useful for incremental adoption or data-driven DAG generation.\n\n### Inside a Hand-Written DAG\n\nTo drop a blueprint-rendered step into an existing Python DAG, instantiate the Blueprint class, set its `step_id`, call `render()`, and wire it in with `>>`:\n\n```python\n# dags\u002Fhybrid_dag.py\nfrom datetime import datetime\n\nfrom airflow import DAG\nfrom airflow.operators.bash import BashOperator\n\nfrom dags.etl_blueprints import Extract, ExtractConfig, Load, LoadConfig\n\nwith DAG(dag_id=\"hybrid_python_dag\", start_date=datetime(2024, 1, 1), schedule=None, catchup=False) as dag:\n    setup = BashOperator(task_id=\"setup\", bash_command=\"echo 'setup'\")\n\n    extract = Extract()\n    extract.step_id = \"extract\"\n    extract_group = extract.render(ExtractConfig(source_table=\"raw.events\", batch_size=100))\n\n    load = Load()\n    load.step_id = \"load\"\n    load_task = load.render(LoadConfig(target_table=\"warehouse.events\", mode=\"append\"))\n\n    finalize = BashOperator(task_id=\"finalize\", bash_command=\"echo 'done'\")\n\n    setup >> extract_group >> load_task >> finalize\n```\n\nThe `step_id` you set determines the `task_id` \u002F `group_id` the blueprint renders under.\n\n### Programmatic Building with `Builder` \u002F `DAGConfig`\n\nFor data-driven DAG generation (one DAG per region, tenant, etc.), build DAGs in a loop with `Builder` and `DAGConfig`, then register each in `globals()` so Airflow discovers them:\n\n```python\nfrom blueprint import Builder, DAGConfig\n\nbuilder = Builder()\n\nfor region in [\"us\", \"eu\", \"apac\"]:\n    config = DAGConfig(\n        dag_id=f\"pipeline_{region}\",\n        schedule=\"@hourly\",\n        steps={\n            \"extract\": {\"blueprint\": \"extract\", \"source_table\": f\"raw.{region}\"},\n            \"load\": {\"blueprint\": \"load\", \"depends_on\": [\"extract\"], \"target_table\": f\"out.{region}\"},\n        },\n    )\n    dag = builder.build(config)\n    globals()[dag.dag_id] = dag\n```\n\n`DAGConfig` accepts the same fields you would write in YAML (`dag_id`, `steps`, plus any fields your `BlueprintDagArgs` consumes). `Builder`, `DAGConfig`, and `StepConfig` are all exported from `blueprint`. See `examples\u002Fadvanced\u002Fdags\u002Fprogrammatic_dags.py` in the repo.\n\n---\n\n## Customizing DAG-Level Configuration\n\nBy default, Blueprint supports `schedule` and `description` as DAG-level YAML fields. To use other DAG constructor arguments (tags, default_args, catchup, etc.), define a `BlueprintDagArgs` subclass.\n\n### When to Use\n\n- User wants `tags`, `default_args`, `catchup`, `start_date`, or any other DAG kwargs in YAML\n- User wants to derive DAG properties from config (e.g. team name → owner, tier → retries)\n\n### Defining a BlueprintDagArgs Subclass\n\n```python\n# dags\u002Ftemplates\u002Fmy_dag_args.py\nfrom pydantic import BaseModel\nfrom blueprint import BlueprintDagArgs\n\nclass MyDagArgsConfig(BaseModel):\n    schedule: str | None = None\n    description: str | None = None\n    tags: list[str] = []\n    owner: str = \"data-team\"\n    retries: int = 2\n\nclass MyDagArgs(BlueprintDagArgs[MyDagArgsConfig]):\n    def render(self, config: MyDagArgsConfig) -> dict[str, Any]:\n        return {\n            \"schedule\": config.schedule,\n            \"description\": config.description,\n            \"tags\": config.tags,\n            \"default_args\": {\n                \"owner\": config.owner,\n                \"retries\": config.retries,\n            },\n        }\n```\n\nThen in YAML, the extra fields are validated by the config model:\n\n```yaml\ndag_id: my_pipeline\nschedule: \"@daily\"\ntags: [etl, production]\nowner: data-team\nretries: 3\n\nsteps:\n  extract:\n    blueprint: extract\n    source_table: raw.data\n```\n\n### Rules\n\n- Only **one** `BlueprintDagArgs` subclass per project (raises `MultipleDagArgsError` if more than one exists)\n- The `render()` method returns a dict of kwargs passed to the Airflow `DAG()` constructor\n- If no custom subclass exists, the built-in `DefaultDagArgs` is used (supports only `schedule` and `description`)\n\n---\n\n## Runtime Parameter Overrides\n\nBlueprint config fields can be overridden at DAG trigger time using Airflow params. This enables users to customize behavior when manually triggering DAGs from the Airflow UI.\n\n### Opt In with `supports_params = True`\n\nA blueprint must set the class attribute `supports_params = True` for its config fields to register as Airflow params (namespaced as `{step}__{field}`). **Without it, `self.param()` \u002F `self.resolve_config()` do nothing and no fields appear in the trigger form.** Only opt in for blueprints that actually use those methods — otherwise dead params clutter the form with no effect.\n\n### Using `self.param()` in Template Fields\n\nUse `self.param(\"field\")` in operator template fields to make a config field overridable at runtime. Airflow renders the actual value at execution time:\n\n```python\nclass ExtractConfig(BaseModel):\n    query: str = Field(description=\"SQL query to run\")\n    batch_size: int = Field(default=1000, ge=1)\n\nclass Extract(Blueprint[ExtractConfig]):\n    supports_params = True\n\n    def render(self, config: ExtractConfig) -> TaskGroup:\n        with TaskGroup(group_id=self.step_id) as group:\n            BashOperator(\n                task_id=\"run_query\",\n                bash_command=f\"run-etl --query {self.param('query')} --batch {self.param('batch_size')}\"\n            )\n        return group\n```\n\n### Using `self.resolve_config()` in Python Callables\n\nFor `@task` or `PythonOperator` callables, use `self.resolve_config()` to merge runtime params into config. It returns a new validated config instance:\n\n```python\nclass Extract(Blueprint[ExtractConfig]):\n    supports_params = True\n\n    def render(self, config: ExtractConfig) -> TaskGroup:\n        bp = self  # capture reference for closure\n\n        @task(task_id=\"run_query\")\n        def run_query(**context):\n            resolved = bp.resolve_config(config, context)\n            # resolved.query has the runtime override if one was provided\n            execute(resolved.query, resolved.batch_size)\n\n        with TaskGroup(group_id=self.step_id) as group:\n            run_query()\n        return group\n```\n\nUse `self.param()` for operators with template fields and `self.resolve_config()` for Python logic in `@task` functions; both can be combined in one blueprint.\n\n### How It Works\n\n- Params are **auto-generated** from Pydantic config models and namespaced per step (e.g. `step_name__field`)\n- YAML values become param defaults; Pydantic metadata (description, constraints, enum values) flows through to the Airflow trigger form\n- Invalid overrides raise `ValidationError` at execution time\n\n### Trigger Form Customization\n\nPydantic field schema flows through to Airflow's trigger form. Control how each field renders with `json_schema_extra`:\n\n```python\nclass LoadConfig(BaseModel):\n    query: str = Field(description=\"SQL to execute\", json_schema_extra={\"format\": \"multiline\"})\n    schedule_date: str = Field(default=\"2024-01-01\", json_schema_extra={\"format\": \"date\"})\n```\n\nSupported `format` values include `\"multiline\"` (textarea), `\"date\"`, `\"date-time\"`, and `\"time\"` (pickers). Also usable: `examples` (dropdown with free text), `values_display` (human-readable labels for enum\u002Fexample values), and `description_md` (Markdown descriptions).\n\n**Validation nuance:** only `Field` constraints that map to JSON Schema (`ge`, `le`, `pattern`, `min_length`, `max_length`, `Literal` enums) are enforced in the trigger form. Custom `@field_validator` \u002F `@model_validator` logic does **not** map to JSON Schema, so it runs only at build time and inside `resolve_config()` — not in the form. If custom validators enforce important constraints, call `self.resolve_config()` in your `@task` function so they run on overridden values.\n\n### Triggering with Overrides\n\nOverride params via the Airflow UI trigger form, or via the API using `conf` with the namespaced names:\n\n```bash\ncurl -X POST \u002Fapi\u002Fv2\u002Fdags\u002Fcustomer_pipeline\u002FdagRuns \\\n  -d '{\"conf\": {\"load__target_table\": \"staging.customers\", \"load__mode\": \"append\"}}'\n```\n\n---\n\n## Post-Build Callbacks\n\nUse `on_dag_built` to post-process DAGs after they are constructed. This is useful for adding tags, access controls, audit metadata, or any cross-cutting concern.\n\n```python\nfrom pathlib import Path\nfrom blueprint import build_all_dags\n\ndef add_audit_tags(dag, yaml_path: Path) -> None:\n    dag.tags.append(\"managed-by-blueprint\")\n    dag.tags.append(f\"source:{yaml_path.name}\")\n\nbuild_all_dags(on_dag_built=add_audit_tags)\n```\n\nThe callback receives:\n- `dag` — the constructed Airflow `DAG` object (mutable)\n- `yaml_path` — the `Path` to the YAML file that defined the DAG\n\n---\n\n## Validation Commands\n\nRun CLI commands with uvx:\n\n```bash\nuvx --from airflow-blueprint blueprint \u003Ccommand>\n```\n\n| Command | When to Use |\n|---------|-------------|\n| `blueprint list` | Show available blueprints |\n| `blueprint describe \u003Cname>` | Show config schema for a blueprint |\n| `blueprint describe \u003Cname> -v N` | Show schema for specific version |\n| `blueprint lint` | Validate all `*.dag.yaml` files |\n| `blueprint lint \u003Cpath>` | Validate specific file |\n| `blueprint schema \u003Cname>` | Generate JSON schema for a blueprint (step template) |\n| `blueprint schema --dag-args` | Generate JSON schema for DAG-level YAML fields |\n| `blueprint new` | Interactive DAG YAML creation |\n\n### Validation Workflow\n\n```bash\n# Check all YAML files\nuvx --from airflow-blueprint blueprint lint\n\n# Expected output for valid files:\n# PASS customer_pipeline.dag.yaml (dag_id=customer_pipeline)\n```\n\n---\n\n## Versioning\n\nWhen user needs to version blueprints for backwards compatibility:\n\n### Version Naming Convention\n\n- v1: `MyBlueprint` (no suffix)\n- v2: `MyBlueprintV2`\n- v3: `MyBlueprintV3`\n\n```python\n# v1 - original\nclass ExtractConfig(BaseModel):\n    source_table: str\n\nclass Extract(Blueprint[ExtractConfig]):\n    def render(self, config): ...\n\n# v2 - breaking changes, new class\nclass ExtractV2Config(BaseModel):\n    sources: list[dict]  # Different schema\n\nclass ExtractV2(Blueprint[ExtractV2Config]):\n    def render(self, config): ...\n```\n\n### Explicit Name and Version\n\nAs an alternative to the class name convention, blueprints can set `name` and `version` directly:\n\n```python\nclass MyCustomExtractor(Blueprint[ExtractV3Config]):\n    name = \"extract\"\n    version = 3\n\n    def render(self, config): ...\n```\n\nThis is useful when the class name doesn't follow the `NameV{N}` convention or when you want clearer control.\n\n### Using Versions in YAML\n\n```yaml\nsteps:\n  # Pin to v1\n  legacy_extract:\n    blueprint: extract\n    version: 1\n    source_table: raw.data\n\n  # Use latest (v2)\n  new_extract:\n    blueprint: extract\n    sources: [{table: orders}]\n```\n\n### Version Rules\n\n- A blueprint's discovered versions must form a contiguous `1..N` sequence. A gap (e.g. v1 and v3 with no v2) raises `NonContiguousVersionError` during discovery.\n- Pinning a version that doesn't exist in YAML raises `InvalidVersionError`.\n\n---\n\n## Schema Generation\n\nGenerate JSON schemas for editor autocompletion or external tooling:\n\n```bash\n# Generate schema for a blueprint (step template)\nuvx --from airflow-blueprint blueprint schema extract -o extract.schema.json\n\n# Generate schema for DAG-level YAML fields (dag_id, steps, + custom BlueprintDagArgs fields)\nuvx --from airflow-blueprint blueprint schema --dag-args -o dag-args.schema.json\n```\n\nUse `--dag-args` (with no blueprint name) to generate the schema for **DAG-level** YAML fields — `dag_id`, `steps`, and any fields your custom `BlueprintDagArgs` exposes — rather than a single step template's config.\n\nAs of 0.3.0, each emitted schema includes a top-level `templateType` field — `\"blueprint\"` for a step template, `\"dag_args\"` for DAG-level fields — so consumers can tell them apart. The command emits raw JSON when piped or written via `-o\u002F--output` (and pretty, syntax-highlighted JSON when run interactively), so `>` redirection produces valid JSON.\n\n### Astro Project Auto-Detection\n\nAfter creating or modifying a blueprint, **automatically check** if the project is an Astro project by looking for a `.astro\u002F` directory (created by `astro dev init`).\n\nIf the project is an Astro project, **automatically regenerate schemas** without prompting:\n\n```bash\nmkdir -p blueprint\u002Fgenerated-schemas\n# For each name from `blueprint list`:\n#   uvx --from airflow-blueprint blueprint schema NAME -o blueprint\u002Fgenerated-schemas\u002FNAME.schema.json\n# Also emit the DAG-level args schema:\n#   uvx --from airflow-blueprint blueprint schema --dag-args -o blueprint\u002Fgenerated-schemas\u002Fdag-args.schema.json\n```\n\nThe Astro IDE reads `blueprint\u002Fgenerated-schemas\u002F` to render configuration forms. Keeping schemas in sync ensures the visual builder always reflects the latest blueprint configs.\n\nIf you cannot determine whether the project is an Astro project, ask the user once and remember for the rest of the session.\n\n---\n\n## Troubleshooting\n\n### \"Blueprint not found\"\n\n**Cause**: Blueprint class not in Python path.\n\n**Fix**: Check template directory or use `--template-dir`:\n```bash\nuvx --from airflow-blueprint blueprint list --template-dir dags\u002Ftemplates\u002F\n```\n\n### \"Extra inputs are not permitted\"\n\n**Cause**: YAML field name typo with `extra=\"forbid\"` enabled.\n\n**Fix**: Run `uvx --from airflow-blueprint blueprint describe \u003Cname>` to see valid field names.\n\n### DAG not appearing in Airflow\n\n**Cause**: Missing or broken loader — including a loader that imports the deprecated `build_all`, which Airflow safe-mode may skip.\n\n**Fix**: Ensure `dags\u002Floader.py` exists and calls `build_all_dags()`:\n```python\nfrom blueprint import build_all_dags\nbuild_all_dags()\n```\n\n### \"ModuleNotFoundError: No module named 'airflow.providers.X'\" from `blueprint list`\u002F`lint`\u002F`schema`\n\n**Cause**: The standalone `uvx --from airflow-blueprint` CLI environment doesn't include Airflow provider packages that your Astro Runtime project has. A template importing provider operators can't be imported by the CLI. This is the CLI's isolated environment, not your project.\n\n**Fix**: Add `--with apache-airflow-providers-X` to the uvx invocation (common: `apache-airflow-providers-standard`, `-google`, `-snowflake`):\n```bash\nuvx --from airflow-blueprint --with apache-airflow-providers-snowflake blueprint lint\n```\n\n### Validation errors shown as Airflow import errors\n\nAs of v0.2.0, Pydantic validation errors are surfaced as Airflow import errors with actionable messages instead of being silently swallowed. The error message includes details on missing fields, unexpected fields, and type mismatches, along with guidance to run `blueprint lint` or `blueprint describe`.\n\n### \"Cyclic dependency detected\"\n\n**Cause**: Circular `depends_on` references.\n\n**Fix**: Review step dependencies and remove cycles.\n\n### \"MultipleDagArgsError\"\n\n**Cause**: More than one `BlueprintDagArgs` subclass discovered in the project.\n\n**Fix**: Only one `BlueprintDagArgs` subclass is allowed. Remove or merge duplicates.\n\n### \"NonContiguousVersionError\" \u002F \"InvalidVersionError\"\n\n**Cause**: A blueprint's versions don't form a contiguous `1..N` sequence (`NonContiguousVersionError`), or YAML pins a version that doesn't exist (`InvalidVersionError`).\n\n**Fix**: Ensure versions increment by one with no gaps; run `uvx --from airflow-blueprint blueprint list` to see available versions.\n\n### \"non-YAML-compatible fields\" (TypeError at import)\n\n**Cause**: A config field uses a type Blueprint rejects since 0.3.0 — a multi-type union (e.g. `str | int`), bare `Any`, or `dict[str, Any]`.\n\n**Fix**: Use a single, explicit type. `Optional[X]` \u002F `X | None` is still allowed. See **Creating Blueprints → Config Field Types Must Be YAML-Compatible**.\n\n### Debugging in Airflow UI\n\nEvery Blueprint task has extra fields in **Rendered Template**:\n- `blueprint_step_config` - resolved YAML config\n- `blueprint_step_code` - Python source of blueprint\n\n---\n\n## Verification Checklist\n\nBefore finishing, verify with user:\n\n- [ ] `blueprint list` shows their templates\n- [ ] `blueprint lint` passes (run it bare to scan all `*.dag.yaml` recursively, or pass a specific file — passing a directory path fails with `Is a directory`)\n- [ ] `dags\u002Floader.py` exists with `build_all_dags()`\n- [ ] DAG appears in Airflow UI without parse errors\n\n---\n\n## Reference\n\n- GitHub: https:\u002F\u002Fgithub.com\u002Fastronomer\u002Fblueprint\n- PyPI: https:\u002F\u002Fpypi.org\u002Fproject\u002Fairflow-blueprint\u002F\n\n### Astro IDE\n\n- Astro IDE Blueprint docs: https:\u002F\u002Fdocs.astronomer.io\u002Fastro\u002Fide-blueprint\n",{"data":56,"body":57},{"name":4,"description":6},{"type":58,"children":59},"root",[60,69,75,120,127,132,167,171,177,410,413,418,423,430,512,518,531,563,614,632,638,673,678,795,798,803,808,814,1002,1008,1133,1139,1182,1317,1323,1342,1373,1379,1392,1437,1440,1445,1464,1498,1651,1656,1782,1785,1790,1795,1801,2041,2094,2100,2204,2209,2215,2226,2377,2430,2436,2441,2592,2597,2666,2669,2674,2679,2685,2710,2886,2913,2932,2958,3083,3148,3151,3156,3181,3187,3226,3232,3413,3418,3589,3595,3668,3671,3676,3681,3693,3735,3748,3760,3871,3883,3910,4029,4054,4060,4100,4106,4118,4149,4216,4320,4326,4339,4397,4400,4405,4417,4485,4490,4530,4533,4538,4543,4589,4754,4760,4822,4825,4830,4835,4841,4879,4983,4989,5008,5053,5066,5072,5234,5240,5276,5279,5284,5289,5395,5434,5478,5484,5512,5524,5581,5594,5599,5602,5607,5613,5623,5640,5679,5685,5701,5718,5724,5740,5763,5784,5807,5823,5862,5901,5907,5925,5931,5947,5956,5962,5978,5994,6000,6029,6046,6052,6081,6109,6115,6126,6151,6154,6160,6165,6246,6249,6255,6279,6285,6299],{"type":61,"tag":62,"props":63,"children":65},"element","h1",{"id":64},"blueprint-implementation",[66],{"type":67,"value":68},"text","Blueprint Implementation",{"type":61,"tag":70,"props":71,"children":72},"p",{},[73],{"type":67,"value":74},"You are helping a user work with Blueprint, a system for composing Airflow DAGs from YAML using reusable Python templates. Execute steps in order and prefer the simplest configuration that meets the user's needs.",{"type":61,"tag":76,"props":77,"children":78},"blockquote",{},[79],{"type":61,"tag":70,"props":80,"children":81},{},[82,88,90,97,99,104,105,113,118],{"type":61,"tag":83,"props":84,"children":85},"strong",{},[86],{"type":67,"value":87},"Package",{"type":67,"value":89},": ",{"type":61,"tag":91,"props":92,"children":94},"code",{"className":93},[],[95],{"type":67,"value":96},"airflow-blueprint",{"type":67,"value":98}," on PyPI\n",{"type":61,"tag":83,"props":100,"children":101},{},[102],{"type":67,"value":103},"Repo",{"type":67,"value":89},{"type":61,"tag":106,"props":107,"children":111},"a",{"href":108,"rel":109},"https:\u002F\u002Fgithub.com\u002Fastronomer\u002Fblueprint",[110],"nofollow",[112],{"type":67,"value":108},{"type":61,"tag":83,"props":114,"children":115},{},[116],{"type":67,"value":117},"Requires",{"type":67,"value":119},": Python 3.10+, Airflow 2.5+, Blueprint 0.3.0+",{"type":61,"tag":121,"props":122,"children":124},"h2",{"id":123},"before-starting",[125],{"type":67,"value":126},"Before Starting",{"type":61,"tag":70,"props":128,"children":129},{},[130],{"type":67,"value":131},"Confirm with the user:",{"type":61,"tag":133,"props":134,"children":135},"ol",{},[136,147,157],{"type":61,"tag":137,"props":138,"children":139},"li",{},[140,145],{"type":61,"tag":83,"props":141,"children":142},{},[143],{"type":67,"value":144},"Airflow version",{"type":67,"value":146}," ≥2.5",{"type":61,"tag":137,"props":148,"children":149},{},[150,155],{"type":61,"tag":83,"props":151,"children":152},{},[153],{"type":67,"value":154},"Python version",{"type":67,"value":156}," ≥3.10",{"type":61,"tag":137,"props":158,"children":159},{},[160,165],{"type":61,"tag":83,"props":161,"children":162},{},[163],{"type":67,"value":164},"Use case",{"type":67,"value":166},": Blueprint is for standardized, validated templates. If user needs full Airflow flexibility, suggest writing DAGs directly or using DAG Factory instead.",{"type":61,"tag":168,"props":169,"children":170},"hr",{},[],{"type":61,"tag":121,"props":172,"children":174},{"id":173},"determine-what-the-user-needs",[175],{"type":67,"value":176},"Determine What the User Needs",{"type":61,"tag":178,"props":179,"children":180},"table",{},[181,200],{"type":61,"tag":182,"props":183,"children":184},"thead",{},[185],{"type":61,"tag":186,"props":187,"children":188},"tr",{},[189,195],{"type":61,"tag":190,"props":191,"children":192},"th",{},[193],{"type":67,"value":194},"User Request",{"type":61,"tag":190,"props":196,"children":197},{},[198],{"type":67,"value":199},"Action",{"type":61,"tag":201,"props":202,"children":203},"tbody",{},[204,223,240,257,274,291,308,325,342,359,376,393],{"type":61,"tag":186,"props":205,"children":206},{},[207,213],{"type":61,"tag":208,"props":209,"children":210},"td",{},[211],{"type":67,"value":212},"\"Create a blueprint\" \u002F \"Define a template\"",{"type":61,"tag":208,"props":214,"children":215},{},[216,218],{"type":67,"value":217},"Go to ",{"type":61,"tag":83,"props":219,"children":220},{},[221],{"type":67,"value":222},"Creating Blueprints",{"type":61,"tag":186,"props":224,"children":225},{},[226,231],{"type":61,"tag":208,"props":227,"children":228},{},[229],{"type":67,"value":230},"\"Build a template from other templates\"",{"type":61,"tag":208,"props":232,"children":233},{},[234,235],{"type":67,"value":217},{"type":61,"tag":83,"props":236,"children":237},{},[238],{"type":67,"value":239},"Composing Templates",{"type":61,"tag":186,"props":241,"children":242},{},[243,248],{"type":61,"tag":208,"props":244,"children":245},{},[246],{"type":67,"value":247},"\"Create a DAG from YAML\" \u002F \"Compose steps\"",{"type":61,"tag":208,"props":249,"children":250},{},[251,252],{"type":67,"value":217},{"type":61,"tag":83,"props":253,"children":254},{},[255],{"type":67,"value":256},"Composing DAGs in YAML",{"type":61,"tag":186,"props":258,"children":259},{},[260,265],{"type":61,"tag":208,"props":261,"children":262},{},[263],{"type":67,"value":264},"\"Use a blueprint in an existing Python DAG\" \u002F \"Generate DAGs in a loop\"",{"type":61,"tag":208,"props":266,"children":267},{},[268,269],{"type":67,"value":217},{"type":61,"tag":83,"props":270,"children":271},{},[272],{"type":67,"value":273},"Blueprints in Python DAGs",{"type":61,"tag":186,"props":275,"children":276},{},[277,282],{"type":61,"tag":208,"props":278,"children":279},{},[280],{"type":67,"value":281},"\"Customize DAG args\" \u002F \"Add tags to DAG\"",{"type":61,"tag":208,"props":283,"children":284},{},[285,286],{"type":67,"value":217},{"type":61,"tag":83,"props":287,"children":288},{},[289],{"type":67,"value":290},"Customizing DAG-Level Configuration",{"type":61,"tag":186,"props":292,"children":293},{},[294,299],{"type":61,"tag":208,"props":295,"children":296},{},[297],{"type":67,"value":298},"\"Override config at runtime\" \u002F \"Trigger with params\"",{"type":61,"tag":208,"props":300,"children":301},{},[302,303],{"type":67,"value":217},{"type":61,"tag":83,"props":304,"children":305},{},[306],{"type":67,"value":307},"Runtime Parameter Overrides",{"type":61,"tag":186,"props":309,"children":310},{},[311,316],{"type":61,"tag":208,"props":312,"children":313},{},[314],{"type":67,"value":315},"\"Post-process DAGs\" \u002F \"Add callback\"",{"type":61,"tag":208,"props":317,"children":318},{},[319,320],{"type":67,"value":217},{"type":61,"tag":83,"props":321,"children":322},{},[323],{"type":67,"value":324},"Post-Build Callbacks",{"type":61,"tag":186,"props":326,"children":327},{},[328,333],{"type":61,"tag":208,"props":329,"children":330},{},[331],{"type":67,"value":332},"\"Validate my YAML\" \u002F \"Lint blueprint\"",{"type":61,"tag":208,"props":334,"children":335},{},[336,337],{"type":67,"value":217},{"type":61,"tag":83,"props":338,"children":339},{},[340],{"type":67,"value":341},"Validation Commands",{"type":61,"tag":186,"props":343,"children":344},{},[345,350],{"type":61,"tag":208,"props":346,"children":347},{},[348],{"type":67,"value":349},"\"Set up blueprint in my project\"",{"type":61,"tag":208,"props":351,"children":352},{},[353,354],{"type":67,"value":217},{"type":61,"tag":83,"props":355,"children":356},{},[357],{"type":67,"value":358},"Project Setup",{"type":61,"tag":186,"props":360,"children":361},{},[362,367],{"type":61,"tag":208,"props":363,"children":364},{},[365],{"type":67,"value":366},"\"Version my blueprint\"",{"type":61,"tag":208,"props":368,"children":369},{},[370,371],{"type":67,"value":217},{"type":61,"tag":83,"props":372,"children":373},{},[374],{"type":67,"value":375},"Versioning",{"type":61,"tag":186,"props":377,"children":378},{},[379,384],{"type":61,"tag":208,"props":380,"children":381},{},[382],{"type":67,"value":383},"\"Generate schema\" \u002F \"Astro IDE setup\"",{"type":61,"tag":208,"props":385,"children":386},{},[387,388],{"type":67,"value":217},{"type":61,"tag":83,"props":389,"children":390},{},[391],{"type":67,"value":392},"Schema Generation",{"type":61,"tag":186,"props":394,"children":395},{},[396,401],{"type":61,"tag":208,"props":397,"children":398},{},[399],{"type":67,"value":400},"Blueprint errors \u002F troubleshooting",{"type":61,"tag":208,"props":402,"children":403},{},[404,405],{"type":67,"value":217},{"type":61,"tag":83,"props":406,"children":407},{},[408],{"type":67,"value":409},"Troubleshooting",{"type":61,"tag":168,"props":411,"children":412},{},[],{"type":61,"tag":121,"props":414,"children":416},{"id":415},"project-setup",[417],{"type":67,"value":358},{"type":61,"tag":70,"props":419,"children":420},{},[421],{"type":67,"value":422},"If the user is starting fresh, guide them through setup:",{"type":61,"tag":424,"props":425,"children":427},"h3",{"id":426},"_1-install-the-package",[428],{"type":67,"value":429},"1. Install the Package",{"type":61,"tag":431,"props":432,"children":437},"pre",{"className":433,"code":434,"language":435,"meta":436,"style":436},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Add to requirements.txt\nairflow-blueprint>=0.3.0\n\n# Or install directly\npip install airflow-blueprint\n","bash","",[438],{"type":61,"tag":91,"props":439,"children":440},{"__ignoreMap":436},[441,453,474,484,493],{"type":61,"tag":442,"props":443,"children":446},"span",{"class":444,"line":445},"line",1,[447],{"type":61,"tag":442,"props":448,"children":450},{"style":449},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[451],{"type":67,"value":452},"# Add to requirements.txt\n",{"type":61,"tag":442,"props":454,"children":456},{"class":444,"line":455},2,[457,462,468],{"type":61,"tag":442,"props":458,"children":460},{"style":459},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[461],{"type":67,"value":96},{"type":61,"tag":442,"props":463,"children":465},{"style":464},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[466],{"type":67,"value":467},">",{"type":61,"tag":442,"props":469,"children":471},{"style":470},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[472],{"type":67,"value":473},"=0.3.0\n",{"type":61,"tag":442,"props":475,"children":477},{"class":444,"line":476},3,[478],{"type":61,"tag":442,"props":479,"children":481},{"emptyLinePlaceholder":480},true,[482],{"type":67,"value":483},"\n",{"type":61,"tag":442,"props":485,"children":487},{"class":444,"line":486},4,[488],{"type":61,"tag":442,"props":489,"children":490},{"style":449},[491],{"type":67,"value":492},"# Or install directly\n",{"type":61,"tag":442,"props":494,"children":496},{"class":444,"line":495},5,[497,502,507],{"type":61,"tag":442,"props":498,"children":499},{"style":459},[500],{"type":67,"value":501},"pip",{"type":61,"tag":442,"props":503,"children":504},{"style":470},[505],{"type":67,"value":506}," install",{"type":61,"tag":442,"props":508,"children":509},{"style":470},[510],{"type":67,"value":511}," airflow-blueprint\n",{"type":61,"tag":424,"props":513,"children":515},{"id":514},"_2-create-the-loader",[516],{"type":67,"value":517},"2. Create the Loader",{"type":61,"tag":70,"props":519,"children":520},{},[521,523,529],{"type":67,"value":522},"Create ",{"type":61,"tag":91,"props":524,"children":526},{"className":525},[],[527],{"type":67,"value":528},"dags\u002Floader.py",{"type":67,"value":530},":",{"type":61,"tag":431,"props":532,"children":536},{"className":533,"code":534,"language":535,"meta":436,"style":436},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","from blueprint import build_all_dags\n\nbuild_all_dags()\n","python",[537],{"type":61,"tag":91,"props":538,"children":539},{"__ignoreMap":436},[540,548,555],{"type":61,"tag":442,"props":541,"children":542},{"class":444,"line":445},[543],{"type":61,"tag":442,"props":544,"children":545},{},[546],{"type":67,"value":547},"from blueprint import build_all_dags\n",{"type":61,"tag":442,"props":549,"children":550},{"class":444,"line":455},[551],{"type":61,"tag":442,"props":552,"children":553},{"emptyLinePlaceholder":480},[554],{"type":67,"value":483},{"type":61,"tag":442,"props":556,"children":557},{"class":444,"line":476},[558],{"type":61,"tag":442,"props":559,"children":560},{},[561],{"type":67,"value":562},"build_all_dags()\n",{"type":61,"tag":76,"props":564,"children":565},{},[566],{"type":61,"tag":70,"props":567,"children":568},{},[569,590,592,597,599,604,606,612],{"type":61,"tag":83,"props":570,"children":571},{},[572,574,580,582,588],{"type":67,"value":573},"Use ",{"type":61,"tag":91,"props":575,"children":577},{"className":576},[],[578],{"type":67,"value":579},"build_all_dags",{"type":67,"value":581},", not ",{"type":61,"tag":91,"props":583,"children":585},{"className":584},[],[586],{"type":67,"value":587},"build_all",{"type":67,"value":589},".",{"type":67,"value":591}," The function was renamed in 0.3.0 so the loader's import line contains the substring ",{"type":61,"tag":91,"props":593,"children":595},{"className":594},[],[596],{"type":67,"value":38},{"type":67,"value":598},", which Airflow's safe-mode DAG file processor requires — otherwise the file is silently skipped and no DAGs appear. ",{"type":61,"tag":91,"props":600,"children":602},{"className":601},[],[603],{"type":67,"value":587},{"type":67,"value":605}," still works as a deprecated alias (emits ",{"type":61,"tag":91,"props":607,"children":609},{"className":608},[],[610],{"type":67,"value":611},"DeprecationWarning",{"type":67,"value":613},"); migrate existing loaders.",{"type":61,"tag":70,"props":615,"children":616},{},[617,619,625,627,631],{"type":67,"value":618},"DAG-level configuration (schedule, description, tags, default_args, etc.) is handled via YAML fields and ",{"type":61,"tag":91,"props":620,"children":622},{"className":621},[],[623],{"type":67,"value":624},"BlueprintDagArgs",{"type":67,"value":626}," templates — see ",{"type":61,"tag":83,"props":628,"children":629},{},[630],{"type":67,"value":290},{"type":67,"value":589},{"type":61,"tag":424,"props":633,"children":635},{"id":634},"_3-verify-installation",[636],{"type":67,"value":637},"3. Verify Installation",{"type":61,"tag":431,"props":639,"children":641},{"className":433,"code":640,"language":435,"meta":436,"style":436},"uvx --from airflow-blueprint blueprint list\n",[642],{"type":61,"tag":91,"props":643,"children":644},{"__ignoreMap":436},[645],{"type":61,"tag":442,"props":646,"children":647},{"class":444,"line":445},[648,653,658,663,668],{"type":61,"tag":442,"props":649,"children":650},{"style":459},[651],{"type":67,"value":652},"uvx",{"type":61,"tag":442,"props":654,"children":655},{"style":470},[656],{"type":67,"value":657}," --from",{"type":61,"tag":442,"props":659,"children":660},{"style":470},[661],{"type":67,"value":662}," airflow-blueprint",{"type":61,"tag":442,"props":664,"children":665},{"style":470},[666],{"type":67,"value":667}," blueprint",{"type":61,"tag":442,"props":669,"children":670},{"style":470},[671],{"type":67,"value":672}," list\n",{"type":61,"tag":70,"props":674,"children":675},{},[676],{"type":67,"value":677},"If no blueprints found, user needs to create blueprint classes first.",{"type":61,"tag":76,"props":679,"children":680},{},[681,744],{"type":61,"tag":70,"props":682,"children":683},{},[684,689,691,697,699,704,706,712,714,720,722,728,729,735,737,743],{"type":61,"tag":83,"props":685,"children":686},{},[687],{"type":67,"value":688},"Provider operators in the CLI.",{"type":67,"value":690}," The ",{"type":61,"tag":91,"props":692,"children":694},{"className":693},[],[695],{"type":67,"value":696},"uvx --from airflow-blueprint",{"type":67,"value":698}," environment is isolated and does ",{"type":61,"tag":83,"props":700,"children":701},{},[702],{"type":67,"value":703},"not",{"type":67,"value":705}," include the Airflow provider packages your Astro Runtime project has. If your templates import provider operators (BigQuery, Snowflake, etc.), add ",{"type":61,"tag":91,"props":707,"children":709},{"className":708},[],[710],{"type":67,"value":711},"--with",{"type":67,"value":713}," so the CLI can import them — otherwise ",{"type":61,"tag":91,"props":715,"children":717},{"className":716},[],[718],{"type":67,"value":719},"list",{"type":67,"value":721},"\u002F",{"type":61,"tag":91,"props":723,"children":725},{"className":724},[],[726],{"type":67,"value":727},"lint",{"type":67,"value":721},{"type":61,"tag":91,"props":730,"children":732},{"className":731},[],[733],{"type":67,"value":734},"schema",{"type":67,"value":736}," fail with ",{"type":61,"tag":91,"props":738,"children":740},{"className":739},[],[741],{"type":67,"value":742},"ModuleNotFoundError: No module named 'airflow.providers.X'",{"type":67,"value":530},{"type":61,"tag":431,"props":745,"children":747},{"className":433,"code":746,"language":435,"meta":436,"style":436},"uvx --from airflow-blueprint --with apache-airflow-providers-google blueprint list --template-dir dags\u002Ftemplates\n",[748],{"type":61,"tag":91,"props":749,"children":750},{"__ignoreMap":436},[751],{"type":61,"tag":442,"props":752,"children":753},{"class":444,"line":445},[754,758,762,766,771,776,780,785,790],{"type":61,"tag":442,"props":755,"children":756},{"style":459},[757],{"type":67,"value":652},{"type":61,"tag":442,"props":759,"children":760},{"style":470},[761],{"type":67,"value":657},{"type":61,"tag":442,"props":763,"children":764},{"style":470},[765],{"type":67,"value":662},{"type":61,"tag":442,"props":767,"children":768},{"style":470},[769],{"type":67,"value":770}," --with",{"type":61,"tag":442,"props":772,"children":773},{"style":470},[774],{"type":67,"value":775}," apache-airflow-providers-google",{"type":61,"tag":442,"props":777,"children":778},{"style":470},[779],{"type":67,"value":667},{"type":61,"tag":442,"props":781,"children":782},{"style":470},[783],{"type":67,"value":784}," list",{"type":61,"tag":442,"props":786,"children":787},{"style":470},[788],{"type":67,"value":789}," --template-dir",{"type":61,"tag":442,"props":791,"children":792},{"style":470},[793],{"type":67,"value":794}," dags\u002Ftemplates\n",{"type":61,"tag":168,"props":796,"children":797},{},[],{"type":61,"tag":121,"props":799,"children":801},{"id":800},"creating-blueprints",[802],{"type":67,"value":222},{"type":61,"tag":70,"props":804,"children":805},{},[806],{"type":67,"value":807},"When user wants to create a new blueprint template:",{"type":61,"tag":424,"props":809,"children":811},{"id":810},"blueprint-structure",[812],{"type":67,"value":813},"Blueprint Structure",{"type":61,"tag":431,"props":815,"children":817},{"className":533,"code":816,"language":535,"meta":436,"style":436},"# dags\u002Ftemplates\u002Fmy_blueprints.py\nfrom airflow.operators.bash import BashOperator\nfrom airflow.utils.task_group import TaskGroup\nfrom blueprint import Blueprint, BaseModel, Field\n\nclass MyConfig(BaseModel):\n    # Required field with description (used in CLI output and JSON schema)\n    source_table: str = Field(description=\"Source table name\")\n    # Optional field with default and validation\n    batch_size: int = Field(default=1000, ge=1)\n\nclass MyBlueprint(Blueprint[MyConfig]):\n    \"\"\"Docstring becomes blueprint description.\"\"\"\n\n    def render(self, config: MyConfig) -> TaskGroup:\n        with TaskGroup(group_id=self.step_id) as group:\n            BashOperator(\n                task_id=\"my_task\",\n                bash_command=f\"echo '{config.source_table}'\"\n            )\n        return group\n",[818],{"type":61,"tag":91,"props":819,"children":820},{"__ignoreMap":436},[821,829,837,845,853,860,869,878,887,896,905,913,922,931,939,948,957,966,975,984,993],{"type":61,"tag":442,"props":822,"children":823},{"class":444,"line":445},[824],{"type":61,"tag":442,"props":825,"children":826},{},[827],{"type":67,"value":828},"# dags\u002Ftemplates\u002Fmy_blueprints.py\n",{"type":61,"tag":442,"props":830,"children":831},{"class":444,"line":455},[832],{"type":61,"tag":442,"props":833,"children":834},{},[835],{"type":67,"value":836},"from airflow.operators.bash import BashOperator\n",{"type":61,"tag":442,"props":838,"children":839},{"class":444,"line":476},[840],{"type":61,"tag":442,"props":841,"children":842},{},[843],{"type":67,"value":844},"from airflow.utils.task_group import TaskGroup\n",{"type":61,"tag":442,"props":846,"children":847},{"class":444,"line":486},[848],{"type":61,"tag":442,"props":849,"children":850},{},[851],{"type":67,"value":852},"from blueprint import Blueprint, BaseModel, Field\n",{"type":61,"tag":442,"props":854,"children":855},{"class":444,"line":495},[856],{"type":61,"tag":442,"props":857,"children":858},{"emptyLinePlaceholder":480},[859],{"type":67,"value":483},{"type":61,"tag":442,"props":861,"children":863},{"class":444,"line":862},6,[864],{"type":61,"tag":442,"props":865,"children":866},{},[867],{"type":67,"value":868},"class MyConfig(BaseModel):\n",{"type":61,"tag":442,"props":870,"children":872},{"class":444,"line":871},7,[873],{"type":61,"tag":442,"props":874,"children":875},{},[876],{"type":67,"value":877},"    # Required field with description (used in CLI output and JSON schema)\n",{"type":61,"tag":442,"props":879,"children":881},{"class":444,"line":880},8,[882],{"type":61,"tag":442,"props":883,"children":884},{},[885],{"type":67,"value":886},"    source_table: str = Field(description=\"Source table name\")\n",{"type":61,"tag":442,"props":888,"children":890},{"class":444,"line":889},9,[891],{"type":61,"tag":442,"props":892,"children":893},{},[894],{"type":67,"value":895},"    # Optional field with default and validation\n",{"type":61,"tag":442,"props":897,"children":899},{"class":444,"line":898},10,[900],{"type":61,"tag":442,"props":901,"children":902},{},[903],{"type":67,"value":904},"    batch_size: int = Field(default=1000, ge=1)\n",{"type":61,"tag":442,"props":906,"children":908},{"class":444,"line":907},11,[909],{"type":61,"tag":442,"props":910,"children":911},{"emptyLinePlaceholder":480},[912],{"type":67,"value":483},{"type":61,"tag":442,"props":914,"children":916},{"class":444,"line":915},12,[917],{"type":61,"tag":442,"props":918,"children":919},{},[920],{"type":67,"value":921},"class MyBlueprint(Blueprint[MyConfig]):\n",{"type":61,"tag":442,"props":923,"children":925},{"class":444,"line":924},13,[926],{"type":61,"tag":442,"props":927,"children":928},{},[929],{"type":67,"value":930},"    \"\"\"Docstring becomes blueprint description.\"\"\"\n",{"type":61,"tag":442,"props":932,"children":934},{"class":444,"line":933},14,[935],{"type":61,"tag":442,"props":936,"children":937},{"emptyLinePlaceholder":480},[938],{"type":67,"value":483},{"type":61,"tag":442,"props":940,"children":942},{"class":444,"line":941},15,[943],{"type":61,"tag":442,"props":944,"children":945},{},[946],{"type":67,"value":947},"    def render(self, config: MyConfig) -> TaskGroup:\n",{"type":61,"tag":442,"props":949,"children":951},{"class":444,"line":950},16,[952],{"type":61,"tag":442,"props":953,"children":954},{},[955],{"type":67,"value":956},"        with TaskGroup(group_id=self.step_id) as group:\n",{"type":61,"tag":442,"props":958,"children":960},{"class":444,"line":959},17,[961],{"type":61,"tag":442,"props":962,"children":963},{},[964],{"type":67,"value":965},"            BashOperator(\n",{"type":61,"tag":442,"props":967,"children":969},{"class":444,"line":968},18,[970],{"type":61,"tag":442,"props":971,"children":972},{},[973],{"type":67,"value":974},"                task_id=\"my_task\",\n",{"type":61,"tag":442,"props":976,"children":978},{"class":444,"line":977},19,[979],{"type":61,"tag":442,"props":980,"children":981},{},[982],{"type":67,"value":983},"                bash_command=f\"echo '{config.source_table}'\"\n",{"type":61,"tag":442,"props":985,"children":987},{"class":444,"line":986},20,[988],{"type":61,"tag":442,"props":989,"children":990},{},[991],{"type":67,"value":992},"            )\n",{"type":61,"tag":442,"props":994,"children":996},{"class":444,"line":995},21,[997],{"type":61,"tag":442,"props":998,"children":999},{},[1000],{"type":67,"value":1001},"        return group\n",{"type":61,"tag":424,"props":1003,"children":1005},{"id":1004},"key-rules",[1006],{"type":67,"value":1007},"Key Rules",{"type":61,"tag":178,"props":1009,"children":1010},{},[1011,1027],{"type":61,"tag":182,"props":1012,"children":1013},{},[1014],{"type":61,"tag":186,"props":1015,"children":1016},{},[1017,1022],{"type":61,"tag":190,"props":1018,"children":1019},{},[1020],{"type":67,"value":1021},"Element",{"type":61,"tag":190,"props":1023,"children":1024},{},[1025],{"type":67,"value":1026},"Requirement",{"type":61,"tag":201,"props":1028,"children":1029},{},[1030,1049,1067,1100,1120],{"type":61,"tag":186,"props":1031,"children":1032},{},[1033,1038],{"type":61,"tag":208,"props":1034,"children":1035},{},[1036],{"type":67,"value":1037},"Config class",{"type":61,"tag":208,"props":1039,"children":1040},{},[1041,1043],{"type":67,"value":1042},"Must inherit from ",{"type":61,"tag":91,"props":1044,"children":1046},{"className":1045},[],[1047],{"type":67,"value":1048},"BaseModel",{"type":61,"tag":186,"props":1050,"children":1051},{},[1052,1057],{"type":61,"tag":208,"props":1053,"children":1054},{},[1055],{"type":67,"value":1056},"Blueprint class",{"type":61,"tag":208,"props":1058,"children":1059},{},[1060,1061],{"type":67,"value":1042},{"type":61,"tag":91,"props":1062,"children":1064},{"className":1063},[],[1065],{"type":67,"value":1066},"Blueprint[ConfigClass]",{"type":61,"tag":186,"props":1068,"children":1069},{},[1070,1081],{"type":61,"tag":208,"props":1071,"children":1072},{},[1073,1079],{"type":61,"tag":91,"props":1074,"children":1076},{"className":1075},[],[1077],{"type":67,"value":1078},"render()",{"type":67,"value":1080}," method",{"type":61,"tag":208,"props":1082,"children":1083},{},[1084,1086,1092,1094],{"type":67,"value":1085},"Must return ",{"type":61,"tag":91,"props":1087,"children":1089},{"className":1088},[],[1090],{"type":67,"value":1091},"TaskGroup",{"type":67,"value":1093}," or ",{"type":61,"tag":91,"props":1095,"children":1097},{"className":1096},[],[1098],{"type":67,"value":1099},"BaseOperator",{"type":61,"tag":186,"props":1101,"children":1102},{},[1103,1108],{"type":61,"tag":208,"props":1104,"children":1105},{},[1106],{"type":67,"value":1107},"Task IDs",{"type":61,"tag":208,"props":1109,"children":1110},{},[1111,1112,1118],{"type":67,"value":573},{"type":61,"tag":91,"props":1113,"children":1115},{"className":1114},[],[1116],{"type":67,"value":1117},"self.step_id",{"type":67,"value":1119}," for the group\u002Ftask ID",{"type":61,"tag":186,"props":1121,"children":1122},{},[1123,1128],{"type":61,"tag":208,"props":1124,"children":1125},{},[1126],{"type":67,"value":1127},"Field types",{"type":61,"tag":208,"props":1129,"children":1130},{},[1131],{"type":67,"value":1132},"Must be single-typed and YAML-compatible (see below)",{"type":61,"tag":424,"props":1134,"children":1136},{"id":1135},"config-field-types-must-be-yaml-compatible",[1137],{"type":67,"value":1138},"Config Field Types Must Be YAML-Compatible",{"type":61,"tag":70,"props":1140,"children":1141},{},[1142,1144,1150,1151,1157,1159,1164,1166,1172,1174,1180],{"type":67,"value":1143},"As of 0.3.0, config fields must be single-typed. Multi-type unions like ",{"type":61,"tag":91,"props":1145,"children":1147},{"className":1146},[],[1148],{"type":67,"value":1149},"str | int",{"type":67,"value":1093},{"type":61,"tag":91,"props":1152,"children":1154},{"className":1153},[],[1155],{"type":67,"value":1156},"Union[A, B]",{"type":67,"value":1158}," are ",{"type":61,"tag":83,"props":1160,"children":1161},{},[1162],{"type":67,"value":1163},"rejected at class-definition time",{"type":67,"value":1165}," (raises ",{"type":61,"tag":91,"props":1167,"children":1169},{"className":1168},[],[1170],{"type":67,"value":1171},"TypeError",{"type":67,"value":1173},") because they produce ambiguous YAML parsing and ",{"type":61,"tag":91,"props":1175,"children":1177},{"className":1176},[],[1178],{"type":67,"value":1179},"anyOf",{"type":67,"value":1181}," schemas. The check recurses through nested models, list items, and dict values.",{"type":61,"tag":1183,"props":1184,"children":1185},"ul",{},[1186,1271],{"type":61,"tag":137,"props":1187,"children":1188},{},[1189,1194,1196,1202,1204,1210,1211,1217,1218,1224,1226,1232,1233,1239,1240,1246,1248,1253,1255,1261,1263,1269],{"type":61,"tag":83,"props":1190,"children":1191},{},[1192],{"type":67,"value":1193},"Allowed",{"type":67,"value":1195},": scalars (",{"type":61,"tag":91,"props":1197,"children":1199},{"className":1198},[],[1200],{"type":67,"value":1201},"str",{"type":67,"value":1203},", ",{"type":61,"tag":91,"props":1205,"children":1207},{"className":1206},[],[1208],{"type":67,"value":1209},"int",{"type":67,"value":1203},{"type":61,"tag":91,"props":1212,"children":1214},{"className":1213},[],[1215],{"type":67,"value":1216},"float",{"type":67,"value":1203},{"type":61,"tag":91,"props":1219,"children":1221},{"className":1220},[],[1222],{"type":67,"value":1223},"bool",{"type":67,"value":1225},"), ",{"type":61,"tag":91,"props":1227,"children":1229},{"className":1228},[],[1230],{"type":67,"value":1231},"Literal[...]",{"type":67,"value":1203},{"type":61,"tag":91,"props":1234,"children":1236},{"className":1235},[],[1237],{"type":67,"value":1238},"list[X]",{"type":67,"value":1203},{"type":61,"tag":91,"props":1241,"children":1243},{"className":1242},[],[1244],{"type":67,"value":1245},"dict[str, V]",{"type":67,"value":1247},", nested ",{"type":61,"tag":91,"props":1249,"children":1251},{"className":1250},[],[1252],{"type":67,"value":1048},{"type":67,"value":1254},", and ",{"type":61,"tag":91,"props":1256,"children":1258},{"className":1257},[],[1259],{"type":67,"value":1260},"Optional[X]",{"type":67,"value":1262}," \u002F ",{"type":61,"tag":91,"props":1264,"children":1266},{"className":1265},[],[1267],{"type":67,"value":1268},"X | None",{"type":67,"value":1270}," (the nullable pattern).",{"type":61,"tag":137,"props":1272,"children":1273},{},[1274,1279,1280,1285,1286,1291,1293,1299,1301,1307,1309,1315],{"type":61,"tag":83,"props":1275,"children":1276},{},[1277],{"type":67,"value":1278},"Rejected",{"type":67,"value":89},{"type":61,"tag":91,"props":1281,"children":1283},{"className":1282},[],[1284],{"type":67,"value":1149},{"type":67,"value":1203},{"type":61,"tag":91,"props":1287,"children":1289},{"className":1288},[],[1290],{"type":67,"value":1156},{"type":67,"value":1292},", or any union with more than one non-",{"type":61,"tag":91,"props":1294,"children":1296},{"className":1295},[],[1297],{"type":67,"value":1298},"None",{"type":67,"value":1300}," arm. Bare ",{"type":61,"tag":91,"props":1302,"children":1304},{"className":1303},[],[1305],{"type":67,"value":1306},"Any",{"type":67,"value":1308}," and ",{"type":61,"tag":91,"props":1310,"children":1312},{"className":1311},[],[1313],{"type":67,"value":1314},"dict[str, Any]",{"type":67,"value":1316}," are rejected for the same reason — use an explicit single type for the value.",{"type":61,"tag":424,"props":1318,"children":1320},{"id":1319},"internal-fields-not-settable-from-yaml",[1321],{"type":67,"value":1322},"Internal Fields Not Settable from YAML",{"type":61,"tag":70,"props":1324,"children":1325},{},[1326,1327,1333,1335,1340],{"type":67,"value":573},{"type":61,"tag":91,"props":1328,"children":1330},{"className":1329},[],[1331],{"type":67,"value":1332},"Field(default=..., init=False)",{"type":67,"value":1334}," for fields used inside ",{"type":61,"tag":91,"props":1336,"children":1338},{"className":1337},[],[1339],{"type":67,"value":1078},{"type":67,"value":1341}," that should not be overridable from YAML. They are excluded from the constructor (always use their default) and omitted from JSON Schema output:",{"type":61,"tag":431,"props":1343,"children":1345},{"className":533,"code":1344,"language":535,"meta":436,"style":436},"class ExtractConfig(BaseModel):\n    source_table: str\n    _internal_batch_multiplier: int = Field(default=4, init=False)\n",[1346],{"type":61,"tag":91,"props":1347,"children":1348},{"__ignoreMap":436},[1349,1357,1365],{"type":61,"tag":442,"props":1350,"children":1351},{"class":444,"line":445},[1352],{"type":61,"tag":442,"props":1353,"children":1354},{},[1355],{"type":67,"value":1356},"class ExtractConfig(BaseModel):\n",{"type":61,"tag":442,"props":1358,"children":1359},{"class":444,"line":455},[1360],{"type":61,"tag":442,"props":1361,"children":1362},{},[1363],{"type":67,"value":1364},"    source_table: str\n",{"type":61,"tag":442,"props":1366,"children":1367},{"class":444,"line":476},[1368],{"type":61,"tag":442,"props":1369,"children":1370},{},[1371],{"type":67,"value":1372},"    _internal_batch_multiplier: int = Field(default=4, init=False)\n",{"type":61,"tag":424,"props":1374,"children":1376},{"id":1375},"recommend-strict-validation",[1377],{"type":67,"value":1378},"Recommend Strict Validation",{"type":61,"tag":70,"props":1380,"children":1381},{},[1382,1384,1390],{"type":67,"value":1383},"Suggest adding ",{"type":61,"tag":91,"props":1385,"children":1387},{"className":1386},[],[1388],{"type":67,"value":1389},"extra=\"forbid\"",{"type":67,"value":1391}," to catch YAML typos:",{"type":61,"tag":431,"props":1393,"children":1395},{"className":533,"code":1394,"language":535,"meta":436,"style":436},"from pydantic import ConfigDict\n\nclass MyConfig(BaseModel):\n    model_config = ConfigDict(extra=\"forbid\")\n    # fields...\n",[1396],{"type":61,"tag":91,"props":1397,"children":1398},{"__ignoreMap":436},[1399,1407,1414,1421,1429],{"type":61,"tag":442,"props":1400,"children":1401},{"class":444,"line":445},[1402],{"type":61,"tag":442,"props":1403,"children":1404},{},[1405],{"type":67,"value":1406},"from pydantic import ConfigDict\n",{"type":61,"tag":442,"props":1408,"children":1409},{"class":444,"line":455},[1410],{"type":61,"tag":442,"props":1411,"children":1412},{"emptyLinePlaceholder":480},[1413],{"type":67,"value":483},{"type":61,"tag":442,"props":1415,"children":1416},{"class":444,"line":476},[1417],{"type":61,"tag":442,"props":1418,"children":1419},{},[1420],{"type":67,"value":868},{"type":61,"tag":442,"props":1422,"children":1423},{"class":444,"line":486},[1424],{"type":61,"tag":442,"props":1425,"children":1426},{},[1427],{"type":67,"value":1428},"    model_config = ConfigDict(extra=\"forbid\")\n",{"type":61,"tag":442,"props":1430,"children":1431},{"class":444,"line":495},[1432],{"type":61,"tag":442,"props":1433,"children":1434},{},[1435],{"type":67,"value":1436},"    # fields...\n",{"type":61,"tag":168,"props":1438,"children":1439},{},[],{"type":61,"tag":121,"props":1441,"children":1443},{"id":1442},"composing-templates",[1444],{"type":67,"value":239},{"type":61,"tag":70,"props":1446,"children":1447},{},[1448,1450,1455,1457,1462],{"type":67,"value":1449},"A blueprint can instantiate and render ",{"type":61,"tag":83,"props":1451,"children":1452},{},[1453],{"type":67,"value":1454},"other blueprints",{"type":67,"value":1456}," inside its ",{"type":61,"tag":91,"props":1458,"children":1460},{"className":1459},[],[1461],{"type":67,"value":1078},{"type":67,"value":1463}," method, letting you build higher-level templates from lower-level building blocks while exposing a single, flat config to YAML authors.",{"type":61,"tag":70,"props":1465,"children":1466},{},[1467,1469,1474,1476,1482,1484,1490,1492,1497],{"type":67,"value":1468},"Inside ",{"type":61,"tag":91,"props":1470,"children":1472},{"className":1471},[],[1473],{"type":67,"value":1078},{"type":67,"value":1475},", instantiate each child blueprint, set its ",{"type":61,"tag":91,"props":1477,"children":1479},{"className":1478},[],[1480],{"type":67,"value":1481},"step_id",{"type":67,"value":1483},", call ",{"type":61,"tag":91,"props":1485,"children":1487},{"className":1486},[],[1488],{"type":67,"value":1489},"render(...)",{"type":67,"value":1491}," with a config you construct, and wire the results together inside a parent ",{"type":61,"tag":91,"props":1493,"children":1495},{"className":1494},[],[1496],{"type":67,"value":1091},{"type":67,"value":530},{"type":61,"tag":431,"props":1499,"children":1501},{"className":533,"code":1500,"language":535,"meta":436,"style":436},"class QualityGateConfig(BaseModel):\n    checks: list[str] = Field(default=[\"nulls\", \"duplicates\"])\n    report_channel: str = Field(default=\"data-alerts\")\n\nclass QualityGate(Blueprint[QualityGateConfig]):\n    \"\"\"Run checks then send a report — composed from Validate and Report.\"\"\"\n\n    def render(self, config: QualityGateConfig) -> TaskGroup:\n        with TaskGroup(group_id=self.step_id) as group:\n            validate = Validate()\n            validate.step_id = \"validate\"\n            validate_group = validate.render(ValidateConfig(checks=config.checks))\n\n            report = Report()\n            report.step_id = \"report\"\n            report_task = report.render(ReportConfig(channel=config.report_channel))\n\n            validate_group >> report_task\n        return group\n",[1502],{"type":61,"tag":91,"props":1503,"children":1504},{"__ignoreMap":436},[1505,1513,1521,1529,1536,1544,1552,1559,1567,1574,1582,1590,1598,1605,1613,1621,1629,1636,1644],{"type":61,"tag":442,"props":1506,"children":1507},{"class":444,"line":445},[1508],{"type":61,"tag":442,"props":1509,"children":1510},{},[1511],{"type":67,"value":1512},"class QualityGateConfig(BaseModel):\n",{"type":61,"tag":442,"props":1514,"children":1515},{"class":444,"line":455},[1516],{"type":61,"tag":442,"props":1517,"children":1518},{},[1519],{"type":67,"value":1520},"    checks: list[str] = Field(default=[\"nulls\", \"duplicates\"])\n",{"type":61,"tag":442,"props":1522,"children":1523},{"class":444,"line":476},[1524],{"type":61,"tag":442,"props":1525,"children":1526},{},[1527],{"type":67,"value":1528},"    report_channel: str = Field(default=\"data-alerts\")\n",{"type":61,"tag":442,"props":1530,"children":1531},{"class":444,"line":486},[1532],{"type":61,"tag":442,"props":1533,"children":1534},{"emptyLinePlaceholder":480},[1535],{"type":67,"value":483},{"type":61,"tag":442,"props":1537,"children":1538},{"class":444,"line":495},[1539],{"type":61,"tag":442,"props":1540,"children":1541},{},[1542],{"type":67,"value":1543},"class QualityGate(Blueprint[QualityGateConfig]):\n",{"type":61,"tag":442,"props":1545,"children":1546},{"class":444,"line":862},[1547],{"type":61,"tag":442,"props":1548,"children":1549},{},[1550],{"type":67,"value":1551},"    \"\"\"Run checks then send a report — composed from Validate and Report.\"\"\"\n",{"type":61,"tag":442,"props":1553,"children":1554},{"class":444,"line":871},[1555],{"type":61,"tag":442,"props":1556,"children":1557},{"emptyLinePlaceholder":480},[1558],{"type":67,"value":483},{"type":61,"tag":442,"props":1560,"children":1561},{"class":444,"line":880},[1562],{"type":61,"tag":442,"props":1563,"children":1564},{},[1565],{"type":67,"value":1566},"    def render(self, config: QualityGateConfig) -> TaskGroup:\n",{"type":61,"tag":442,"props":1568,"children":1569},{"class":444,"line":889},[1570],{"type":61,"tag":442,"props":1571,"children":1572},{},[1573],{"type":67,"value":956},{"type":61,"tag":442,"props":1575,"children":1576},{"class":444,"line":898},[1577],{"type":61,"tag":442,"props":1578,"children":1579},{},[1580],{"type":67,"value":1581},"            validate = Validate()\n",{"type":61,"tag":442,"props":1583,"children":1584},{"class":444,"line":907},[1585],{"type":61,"tag":442,"props":1586,"children":1587},{},[1588],{"type":67,"value":1589},"            validate.step_id = \"validate\"\n",{"type":61,"tag":442,"props":1591,"children":1592},{"class":444,"line":915},[1593],{"type":61,"tag":442,"props":1594,"children":1595},{},[1596],{"type":67,"value":1597},"            validate_group = validate.render(ValidateConfig(checks=config.checks))\n",{"type":61,"tag":442,"props":1599,"children":1600},{"class":444,"line":924},[1601],{"type":61,"tag":442,"props":1602,"children":1603},{"emptyLinePlaceholder":480},[1604],{"type":67,"value":483},{"type":61,"tag":442,"props":1606,"children":1607},{"class":444,"line":933},[1608],{"type":61,"tag":442,"props":1609,"children":1610},{},[1611],{"type":67,"value":1612},"            report = Report()\n",{"type":61,"tag":442,"props":1614,"children":1615},{"class":444,"line":941},[1616],{"type":61,"tag":442,"props":1617,"children":1618},{},[1619],{"type":67,"value":1620},"            report.step_id = \"report\"\n",{"type":61,"tag":442,"props":1622,"children":1623},{"class":444,"line":950},[1624],{"type":61,"tag":442,"props":1625,"children":1626},{},[1627],{"type":67,"value":1628},"            report_task = report.render(ReportConfig(channel=config.report_channel))\n",{"type":61,"tag":442,"props":1630,"children":1631},{"class":444,"line":959},[1632],{"type":61,"tag":442,"props":1633,"children":1634},{"emptyLinePlaceholder":480},[1635],{"type":67,"value":483},{"type":61,"tag":442,"props":1637,"children":1638},{"class":444,"line":968},[1639],{"type":61,"tag":442,"props":1640,"children":1641},{},[1642],{"type":67,"value":1643},"            validate_group >> report_task\n",{"type":61,"tag":442,"props":1645,"children":1646},{"class":444,"line":977},[1647],{"type":61,"tag":442,"props":1648,"children":1649},{},[1650],{"type":67,"value":1001},{"type":61,"tag":70,"props":1652,"children":1653},{},[1654],{"type":67,"value":1655},"YAML authors then see a single step with a flat config:",{"type":61,"tag":431,"props":1657,"children":1661},{"className":1658,"code":1659,"language":1660,"meta":436,"style":436},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","steps:\n  quality:\n    blueprint: quality_gate\n    checks: [nulls, duplicates, freshness]\n    report_channel: \"#data-alerts\"\n","yaml",[1662],{"type":61,"tag":91,"props":1663,"children":1664},{"__ignoreMap":436},[1665,1680,1692,1709,1755],{"type":61,"tag":442,"props":1666,"children":1667},{"class":444,"line":445},[1668,1674],{"type":61,"tag":442,"props":1669,"children":1671},{"style":1670},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[1672],{"type":67,"value":1673},"steps",{"type":61,"tag":442,"props":1675,"children":1677},{"style":1676},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[1678],{"type":67,"value":1679},":\n",{"type":61,"tag":442,"props":1681,"children":1682},{"class":444,"line":455},[1683,1688],{"type":61,"tag":442,"props":1684,"children":1685},{"style":1670},[1686],{"type":67,"value":1687},"  quality",{"type":61,"tag":442,"props":1689,"children":1690},{"style":1676},[1691],{"type":67,"value":1679},{"type":61,"tag":442,"props":1693,"children":1694},{"class":444,"line":476},[1695,1700,1704],{"type":61,"tag":442,"props":1696,"children":1697},{"style":1670},[1698],{"type":67,"value":1699},"    blueprint",{"type":61,"tag":442,"props":1701,"children":1702},{"style":1676},[1703],{"type":67,"value":530},{"type":61,"tag":442,"props":1705,"children":1706},{"style":470},[1707],{"type":67,"value":1708}," quality_gate\n",{"type":61,"tag":442,"props":1710,"children":1711},{"class":444,"line":486},[1712,1717,1721,1726,1731,1736,1741,1745,1750],{"type":61,"tag":442,"props":1713,"children":1714},{"style":1670},[1715],{"type":67,"value":1716},"    checks",{"type":61,"tag":442,"props":1718,"children":1719},{"style":1676},[1720],{"type":67,"value":530},{"type":61,"tag":442,"props":1722,"children":1723},{"style":1676},[1724],{"type":67,"value":1725}," [",{"type":61,"tag":442,"props":1727,"children":1728},{"style":470},[1729],{"type":67,"value":1730},"nulls",{"type":61,"tag":442,"props":1732,"children":1733},{"style":1676},[1734],{"type":67,"value":1735},",",{"type":61,"tag":442,"props":1737,"children":1738},{"style":470},[1739],{"type":67,"value":1740}," duplicates",{"type":61,"tag":442,"props":1742,"children":1743},{"style":1676},[1744],{"type":67,"value":1735},{"type":61,"tag":442,"props":1746,"children":1747},{"style":470},[1748],{"type":67,"value":1749}," freshness",{"type":61,"tag":442,"props":1751,"children":1752},{"style":1676},[1753],{"type":67,"value":1754},"]\n",{"type":61,"tag":442,"props":1756,"children":1757},{"class":444,"line":495},[1758,1763,1767,1772,1777],{"type":61,"tag":442,"props":1759,"children":1760},{"style":1670},[1761],{"type":67,"value":1762},"    report_channel",{"type":61,"tag":442,"props":1764,"children":1765},{"style":1676},[1766],{"type":67,"value":530},{"type":61,"tag":442,"props":1768,"children":1769},{"style":1676},[1770],{"type":67,"value":1771}," \"",{"type":61,"tag":442,"props":1773,"children":1774},{"style":470},[1775],{"type":67,"value":1776},"#data-alerts",{"type":61,"tag":442,"props":1778,"children":1779},{"style":1676},[1780],{"type":67,"value":1781},"\"\n",{"type":61,"tag":168,"props":1783,"children":1784},{},[],{"type":61,"tag":121,"props":1786,"children":1788},{"id":1787},"composing-dags-in-yaml",[1789],{"type":67,"value":256},{"type":61,"tag":70,"props":1791,"children":1792},{},[1793],{"type":67,"value":1794},"When user wants to create a DAG from blueprints:",{"type":61,"tag":424,"props":1796,"children":1798},{"id":1797},"yaml-structure",[1799],{"type":67,"value":1800},"YAML Structure",{"type":61,"tag":431,"props":1802,"children":1804},{"className":1658,"code":1803,"language":1660,"meta":436,"style":436},"# dags\u002Fmy_pipeline.dag.yaml\ndag_id: my_pipeline\nschedule: \"@daily\"\ndescription: \"My data pipeline\"\n\nsteps:\n  step_one:\n    blueprint: my_blueprint\n    source_table: raw.customers\n    batch_size: 500\n\n  step_two:\n    blueprint: another_blueprint\n    depends_on: [step_one]\n    target: analytics.output\n",[1805],{"type":61,"tag":91,"props":1806,"children":1807},{"__ignoreMap":436},[1808,1816,1833,1858,1883,1890,1901,1913,1929,1946,1964,1971,1983,1999,2024],{"type":61,"tag":442,"props":1809,"children":1810},{"class":444,"line":445},[1811],{"type":61,"tag":442,"props":1812,"children":1813},{"style":449},[1814],{"type":67,"value":1815},"# dags\u002Fmy_pipeline.dag.yaml\n",{"type":61,"tag":442,"props":1817,"children":1818},{"class":444,"line":455},[1819,1824,1828],{"type":61,"tag":442,"props":1820,"children":1821},{"style":1670},[1822],{"type":67,"value":1823},"dag_id",{"type":61,"tag":442,"props":1825,"children":1826},{"style":1676},[1827],{"type":67,"value":530},{"type":61,"tag":442,"props":1829,"children":1830},{"style":470},[1831],{"type":67,"value":1832}," my_pipeline\n",{"type":61,"tag":442,"props":1834,"children":1835},{"class":444,"line":476},[1836,1841,1845,1849,1854],{"type":61,"tag":442,"props":1837,"children":1838},{"style":1670},[1839],{"type":67,"value":1840},"schedule",{"type":61,"tag":442,"props":1842,"children":1843},{"style":1676},[1844],{"type":67,"value":530},{"type":61,"tag":442,"props":1846,"children":1847},{"style":1676},[1848],{"type":67,"value":1771},{"type":61,"tag":442,"props":1850,"children":1851},{"style":470},[1852],{"type":67,"value":1853},"@daily",{"type":61,"tag":442,"props":1855,"children":1856},{"style":1676},[1857],{"type":67,"value":1781},{"type":61,"tag":442,"props":1859,"children":1860},{"class":444,"line":486},[1861,1866,1870,1874,1879],{"type":61,"tag":442,"props":1862,"children":1863},{"style":1670},[1864],{"type":67,"value":1865},"description",{"type":61,"tag":442,"props":1867,"children":1868},{"style":1676},[1869],{"type":67,"value":530},{"type":61,"tag":442,"props":1871,"children":1872},{"style":1676},[1873],{"type":67,"value":1771},{"type":61,"tag":442,"props":1875,"children":1876},{"style":470},[1877],{"type":67,"value":1878},"My data pipeline",{"type":61,"tag":442,"props":1880,"children":1881},{"style":1676},[1882],{"type":67,"value":1781},{"type":61,"tag":442,"props":1884,"children":1885},{"class":444,"line":495},[1886],{"type":61,"tag":442,"props":1887,"children":1888},{"emptyLinePlaceholder":480},[1889],{"type":67,"value":483},{"type":61,"tag":442,"props":1891,"children":1892},{"class":444,"line":862},[1893,1897],{"type":61,"tag":442,"props":1894,"children":1895},{"style":1670},[1896],{"type":67,"value":1673},{"type":61,"tag":442,"props":1898,"children":1899},{"style":1676},[1900],{"type":67,"value":1679},{"type":61,"tag":442,"props":1902,"children":1903},{"class":444,"line":871},[1904,1909],{"type":61,"tag":442,"props":1905,"children":1906},{"style":1670},[1907],{"type":67,"value":1908},"  step_one",{"type":61,"tag":442,"props":1910,"children":1911},{"style":1676},[1912],{"type":67,"value":1679},{"type":61,"tag":442,"props":1914,"children":1915},{"class":444,"line":880},[1916,1920,1924],{"type":61,"tag":442,"props":1917,"children":1918},{"style":1670},[1919],{"type":67,"value":1699},{"type":61,"tag":442,"props":1921,"children":1922},{"style":1676},[1923],{"type":67,"value":530},{"type":61,"tag":442,"props":1925,"children":1926},{"style":470},[1927],{"type":67,"value":1928}," my_blueprint\n",{"type":61,"tag":442,"props":1930,"children":1931},{"class":444,"line":889},[1932,1937,1941],{"type":61,"tag":442,"props":1933,"children":1934},{"style":1670},[1935],{"type":67,"value":1936},"    source_table",{"type":61,"tag":442,"props":1938,"children":1939},{"style":1676},[1940],{"type":67,"value":530},{"type":61,"tag":442,"props":1942,"children":1943},{"style":470},[1944],{"type":67,"value":1945}," raw.customers\n",{"type":61,"tag":442,"props":1947,"children":1948},{"class":444,"line":898},[1949,1954,1958],{"type":61,"tag":442,"props":1950,"children":1951},{"style":1670},[1952],{"type":67,"value":1953},"    batch_size",{"type":61,"tag":442,"props":1955,"children":1956},{"style":1676},[1957],{"type":67,"value":530},{"type":61,"tag":442,"props":1959,"children":1961},{"style":1960},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[1962],{"type":67,"value":1963}," 500\n",{"type":61,"tag":442,"props":1965,"children":1966},{"class":444,"line":907},[1967],{"type":61,"tag":442,"props":1968,"children":1969},{"emptyLinePlaceholder":480},[1970],{"type":67,"value":483},{"type":61,"tag":442,"props":1972,"children":1973},{"class":444,"line":915},[1974,1979],{"type":61,"tag":442,"props":1975,"children":1976},{"style":1670},[1977],{"type":67,"value":1978},"  step_two",{"type":61,"tag":442,"props":1980,"children":1981},{"style":1676},[1982],{"type":67,"value":1679},{"type":61,"tag":442,"props":1984,"children":1985},{"class":444,"line":924},[1986,1990,1994],{"type":61,"tag":442,"props":1987,"children":1988},{"style":1670},[1989],{"type":67,"value":1699},{"type":61,"tag":442,"props":1991,"children":1992},{"style":1676},[1993],{"type":67,"value":530},{"type":61,"tag":442,"props":1995,"children":1996},{"style":470},[1997],{"type":67,"value":1998}," another_blueprint\n",{"type":61,"tag":442,"props":2000,"children":2001},{"class":444,"line":933},[2002,2007,2011,2015,2020],{"type":61,"tag":442,"props":2003,"children":2004},{"style":1670},[2005],{"type":67,"value":2006},"    depends_on",{"type":61,"tag":442,"props":2008,"children":2009},{"style":1676},[2010],{"type":67,"value":530},{"type":61,"tag":442,"props":2012,"children":2013},{"style":1676},[2014],{"type":67,"value":1725},{"type":61,"tag":442,"props":2016,"children":2017},{"style":470},[2018],{"type":67,"value":2019},"step_one",{"type":61,"tag":442,"props":2021,"children":2022},{"style":1676},[2023],{"type":67,"value":1754},{"type":61,"tag":442,"props":2025,"children":2026},{"class":444,"line":941},[2027,2032,2036],{"type":61,"tag":442,"props":2028,"children":2029},{"style":1670},[2030],{"type":67,"value":2031},"    target",{"type":61,"tag":442,"props":2033,"children":2034},{"style":1676},[2035],{"type":67,"value":530},{"type":61,"tag":442,"props":2037,"children":2038},{"style":470},[2039],{"type":67,"value":2040}," analytics.output\n",{"type":61,"tag":70,"props":2042,"children":2043},{},[2044,2046,2051,2052,2057,2059,2065,2067,2073,2074,2080,2081,2087,2089,2093],{"type":67,"value":2045},"By default, only ",{"type":61,"tag":91,"props":2047,"children":2049},{"className":2048},[],[2050],{"type":67,"value":1840},{"type":67,"value":1308},{"type":61,"tag":91,"props":2053,"children":2055},{"className":2054},[],[2056],{"type":67,"value":1865},{"type":67,"value":2058}," are supported as DAG-level fields (via the built-in ",{"type":61,"tag":91,"props":2060,"children":2062},{"className":2061},[],[2063],{"type":67,"value":2064},"DefaultDagArgs",{"type":67,"value":2066},"). For other fields like ",{"type":61,"tag":91,"props":2068,"children":2070},{"className":2069},[],[2071],{"type":67,"value":2072},"tags",{"type":67,"value":1203},{"type":61,"tag":91,"props":2075,"children":2077},{"className":2076},[],[2078],{"type":67,"value":2079},"default_args",{"type":67,"value":1203},{"type":61,"tag":91,"props":2082,"children":2084},{"className":2083},[],[2085],{"type":67,"value":2086},"catchup",{"type":67,"value":2088},", etc., see ",{"type":61,"tag":83,"props":2090,"children":2091},{},[2092],{"type":67,"value":290},{"type":67,"value":589},{"type":61,"tag":424,"props":2095,"children":2097},{"id":2096},"reserved-keys-in-steps",[2098],{"type":67,"value":2099},"Reserved Keys in Steps",{"type":61,"tag":178,"props":2101,"children":2102},{},[2103,2119],{"type":61,"tag":182,"props":2104,"children":2105},{},[2106],{"type":61,"tag":186,"props":2107,"children":2108},{},[2109,2114],{"type":61,"tag":190,"props":2110,"children":2111},{},[2112],{"type":67,"value":2113},"Key",{"type":61,"tag":190,"props":2115,"children":2116},{},[2117],{"type":67,"value":2118},"Purpose",{"type":61,"tag":201,"props":2120,"children":2121},{},[2122,2138,2155,2172],{"type":61,"tag":186,"props":2123,"children":2124},{},[2125,2133],{"type":61,"tag":208,"props":2126,"children":2127},{},[2128],{"type":61,"tag":91,"props":2129,"children":2131},{"className":2130},[],[2132],{"type":67,"value":4},{"type":61,"tag":208,"props":2134,"children":2135},{},[2136],{"type":67,"value":2137},"Template name (required)",{"type":61,"tag":186,"props":2139,"children":2140},{},[2141,2150],{"type":61,"tag":208,"props":2142,"children":2143},{},[2144],{"type":61,"tag":91,"props":2145,"children":2147},{"className":2146},[],[2148],{"type":67,"value":2149},"depends_on",{"type":61,"tag":208,"props":2151,"children":2152},{},[2153],{"type":67,"value":2154},"List of upstream step names",{"type":61,"tag":186,"props":2156,"children":2157},{},[2158,2167],{"type":61,"tag":208,"props":2159,"children":2160},{},[2161],{"type":61,"tag":91,"props":2162,"children":2164},{"className":2163},[],[2165],{"type":67,"value":2166},"version",{"type":61,"tag":208,"props":2168,"children":2169},{},[2170],{"type":67,"value":2171},"Pin to specific blueprint version",{"type":61,"tag":186,"props":2173,"children":2174},{},[2175,2184],{"type":61,"tag":208,"props":2176,"children":2177},{},[2178],{"type":61,"tag":91,"props":2179,"children":2181},{"className":2180},[],[2182],{"type":67,"value":2183},"trigger_rule",{"type":61,"tag":208,"props":2185,"children":2186},{},[2187,2189,2195,2196,2202],{"type":67,"value":2188},"Airflow trigger rule for the step (e.g. ",{"type":61,"tag":91,"props":2190,"children":2192},{"className":2191},[],[2193],{"type":67,"value":2194},"all_done",{"type":67,"value":1203},{"type":61,"tag":91,"props":2197,"children":2199},{"className":2198},[],[2200],{"type":67,"value":2201},"one_success",{"type":67,"value":2203},"); validated against the installed Airflow version",{"type":61,"tag":70,"props":2205,"children":2206},{},[2207],{"type":67,"value":2208},"Everything else passes to the blueprint's config.",{"type":61,"tag":424,"props":2210,"children":2212},{"id":2211},"trigger-rules-030",[2213],{"type":67,"value":2214},"Trigger Rules (0.3.0)",{"type":61,"tag":70,"props":2216,"children":2217},{},[2218,2219,2224],{"type":67,"value":573},{"type":61,"tag":91,"props":2220,"children":2222},{"className":2221},[],[2223],{"type":67,"value":2183},{"type":67,"value":2225}," to control when a step runs relative to its upstream dependencies — for example, to run a notification step even if an upstream step failed:",{"type":61,"tag":431,"props":2227,"children":2229},{"className":1658,"code":2228,"language":1660,"meta":436,"style":436},"steps:\n  analyze:\n    blueprint: analyze\n    depends_on: [extract]\n\n  notify:\n    blueprint: notify\n    depends_on: [analyze]\n    trigger_rule: all_done   # run regardless of whether analyze succeeded\n",[2230],{"type":61,"tag":91,"props":2231,"children":2232},{"__ignoreMap":436},[2233,2244,2256,2272,2296,2303,2315,2331,2355],{"type":61,"tag":442,"props":2234,"children":2235},{"class":444,"line":445},[2236,2240],{"type":61,"tag":442,"props":2237,"children":2238},{"style":1670},[2239],{"type":67,"value":1673},{"type":61,"tag":442,"props":2241,"children":2242},{"style":1676},[2243],{"type":67,"value":1679},{"type":61,"tag":442,"props":2245,"children":2246},{"class":444,"line":455},[2247,2252],{"type":61,"tag":442,"props":2248,"children":2249},{"style":1670},[2250],{"type":67,"value":2251},"  analyze",{"type":61,"tag":442,"props":2253,"children":2254},{"style":1676},[2255],{"type":67,"value":1679},{"type":61,"tag":442,"props":2257,"children":2258},{"class":444,"line":476},[2259,2263,2267],{"type":61,"tag":442,"props":2260,"children":2261},{"style":1670},[2262],{"type":67,"value":1699},{"type":61,"tag":442,"props":2264,"children":2265},{"style":1676},[2266],{"type":67,"value":530},{"type":61,"tag":442,"props":2268,"children":2269},{"style":470},[2270],{"type":67,"value":2271}," analyze\n",{"type":61,"tag":442,"props":2273,"children":2274},{"class":444,"line":486},[2275,2279,2283,2287,2292],{"type":61,"tag":442,"props":2276,"children":2277},{"style":1670},[2278],{"type":67,"value":2006},{"type":61,"tag":442,"props":2280,"children":2281},{"style":1676},[2282],{"type":67,"value":530},{"type":61,"tag":442,"props":2284,"children":2285},{"style":1676},[2286],{"type":67,"value":1725},{"type":61,"tag":442,"props":2288,"children":2289},{"style":470},[2290],{"type":67,"value":2291},"extract",{"type":61,"tag":442,"props":2293,"children":2294},{"style":1676},[2295],{"type":67,"value":1754},{"type":61,"tag":442,"props":2297,"children":2298},{"class":444,"line":495},[2299],{"type":61,"tag":442,"props":2300,"children":2301},{"emptyLinePlaceholder":480},[2302],{"type":67,"value":483},{"type":61,"tag":442,"props":2304,"children":2305},{"class":444,"line":862},[2306,2311],{"type":61,"tag":442,"props":2307,"children":2308},{"style":1670},[2309],{"type":67,"value":2310},"  notify",{"type":61,"tag":442,"props":2312,"children":2313},{"style":1676},[2314],{"type":67,"value":1679},{"type":61,"tag":442,"props":2316,"children":2317},{"class":444,"line":871},[2318,2322,2326],{"type":61,"tag":442,"props":2319,"children":2320},{"style":1670},[2321],{"type":67,"value":1699},{"type":61,"tag":442,"props":2323,"children":2324},{"style":1676},[2325],{"type":67,"value":530},{"type":61,"tag":442,"props":2327,"children":2328},{"style":470},[2329],{"type":67,"value":2330}," notify\n",{"type":61,"tag":442,"props":2332,"children":2333},{"class":444,"line":880},[2334,2338,2342,2346,2351],{"type":61,"tag":442,"props":2335,"children":2336},{"style":1670},[2337],{"type":67,"value":2006},{"type":61,"tag":442,"props":2339,"children":2340},{"style":1676},[2341],{"type":67,"value":530},{"type":61,"tag":442,"props":2343,"children":2344},{"style":1676},[2345],{"type":67,"value":1725},{"type":61,"tag":442,"props":2347,"children":2348},{"style":470},[2349],{"type":67,"value":2350},"analyze",{"type":61,"tag":442,"props":2352,"children":2353},{"style":1676},[2354],{"type":67,"value":1754},{"type":61,"tag":442,"props":2356,"children":2357},{"class":444,"line":889},[2358,2363,2367,2372],{"type":61,"tag":442,"props":2359,"children":2360},{"style":1670},[2361],{"type":67,"value":2362},"    trigger_rule",{"type":61,"tag":442,"props":2364,"children":2365},{"style":1676},[2366],{"type":67,"value":530},{"type":61,"tag":442,"props":2368,"children":2369},{"style":470},[2370],{"type":67,"value":2371}," all_done",{"type":61,"tag":442,"props":2373,"children":2374},{"style":449},[2375],{"type":67,"value":2376},"   # run regardless of whether analyze succeeded\n",{"type":61,"tag":70,"props":2378,"children":2379},{},[2380,2382,2388,2390,2396,2397,2402,2403,2408,2409,2415,2417,2422,2424,2428],{"type":67,"value":2381},"Valid values are validated dynamically against the installed Airflow's ",{"type":61,"tag":91,"props":2383,"children":2385},{"className":2384},[],[2386],{"type":67,"value":2387},"TriggerRule",{"type":67,"value":2389}," enum (",{"type":61,"tag":91,"props":2391,"children":2393},{"className":2392},[],[2394],{"type":67,"value":2395},"all_success",{"type":67,"value":1203},{"type":61,"tag":91,"props":2398,"children":2400},{"className":2399},[],[2401],{"type":67,"value":2194},{"type":67,"value":1203},{"type":61,"tag":91,"props":2404,"children":2406},{"className":2405},[],[2407],{"type":67,"value":2201},{"type":67,"value":1203},{"type":61,"tag":91,"props":2410,"children":2412},{"className":2411},[],[2413],{"type":67,"value":2414},"none_failed",{"type":67,"value":2416},", etc.). When the step's blueprint renders a ",{"type":61,"tag":91,"props":2418,"children":2420},{"className":2419},[],[2421],{"type":67,"value":1091},{"type":67,"value":2423},", the rule is applied only to the group's ",{"type":61,"tag":83,"props":2425,"children":2426},{},[2427],{"type":67,"value":58},{"type":67,"value":2429}," tasks (those with no internal upstream), preserving the blueprint author's internal wiring.",{"type":61,"tag":424,"props":2431,"children":2433},{"id":2432},"jinja2-support",[2434],{"type":67,"value":2435},"Jinja2 Support",{"type":61,"tag":70,"props":2437,"children":2438},{},[2439],{"type":67,"value":2440},"YAML supports Jinja2 templating with access to environment variables, Airflow variables\u002Fconnections, and runtime context:",{"type":61,"tag":431,"props":2442,"children":2444},{"className":1658,"code":2443,"language":1660,"meta":436,"style":436},"dag_id: \"{{ env.get('ENV', 'dev') }}_pipeline\"\nschedule: \"{{ var.value.schedule | default('@daily') }}\"\n\nsteps:\n  extract:\n    blueprint: extract\n    output_path: \"\u002Fdata\u002F{{ context.ds_nodash }}\u002Foutput.csv\"\n    run_id: \"{{ context.dag_run.run_id }}\"\n",[2445],{"type":61,"tag":91,"props":2446,"children":2447},{"__ignoreMap":436},[2448,2472,2496,2503,2514,2526,2542,2567],{"type":61,"tag":442,"props":2449,"children":2450},{"class":444,"line":445},[2451,2455,2459,2463,2468],{"type":61,"tag":442,"props":2452,"children":2453},{"style":1670},[2454],{"type":67,"value":1823},{"type":61,"tag":442,"props":2456,"children":2457},{"style":1676},[2458],{"type":67,"value":530},{"type":61,"tag":442,"props":2460,"children":2461},{"style":1676},[2462],{"type":67,"value":1771},{"type":61,"tag":442,"props":2464,"children":2465},{"style":470},[2466],{"type":67,"value":2467},"{{ env.get('ENV', 'dev') }}_pipeline",{"type":61,"tag":442,"props":2469,"children":2470},{"style":1676},[2471],{"type":67,"value":1781},{"type":61,"tag":442,"props":2473,"children":2474},{"class":444,"line":455},[2475,2479,2483,2487,2492],{"type":61,"tag":442,"props":2476,"children":2477},{"style":1670},[2478],{"type":67,"value":1840},{"type":61,"tag":442,"props":2480,"children":2481},{"style":1676},[2482],{"type":67,"value":530},{"type":61,"tag":442,"props":2484,"children":2485},{"style":1676},[2486],{"type":67,"value":1771},{"type":61,"tag":442,"props":2488,"children":2489},{"style":470},[2490],{"type":67,"value":2491},"{{ var.value.schedule | default('@daily') }}",{"type":61,"tag":442,"props":2493,"children":2494},{"style":1676},[2495],{"type":67,"value":1781},{"type":61,"tag":442,"props":2497,"children":2498},{"class":444,"line":476},[2499],{"type":61,"tag":442,"props":2500,"children":2501},{"emptyLinePlaceholder":480},[2502],{"type":67,"value":483},{"type":61,"tag":442,"props":2504,"children":2505},{"class":444,"line":486},[2506,2510],{"type":61,"tag":442,"props":2507,"children":2508},{"style":1670},[2509],{"type":67,"value":1673},{"type":61,"tag":442,"props":2511,"children":2512},{"style":1676},[2513],{"type":67,"value":1679},{"type":61,"tag":442,"props":2515,"children":2516},{"class":444,"line":495},[2517,2522],{"type":61,"tag":442,"props":2518,"children":2519},{"style":1670},[2520],{"type":67,"value":2521},"  extract",{"type":61,"tag":442,"props":2523,"children":2524},{"style":1676},[2525],{"type":67,"value":1679},{"type":61,"tag":442,"props":2527,"children":2528},{"class":444,"line":862},[2529,2533,2537],{"type":61,"tag":442,"props":2530,"children":2531},{"style":1670},[2532],{"type":67,"value":1699},{"type":61,"tag":442,"props":2534,"children":2535},{"style":1676},[2536],{"type":67,"value":530},{"type":61,"tag":442,"props":2538,"children":2539},{"style":470},[2540],{"type":67,"value":2541}," extract\n",{"type":61,"tag":442,"props":2543,"children":2544},{"class":444,"line":871},[2545,2550,2554,2558,2563],{"type":61,"tag":442,"props":2546,"children":2547},{"style":1670},[2548],{"type":67,"value":2549},"    output_path",{"type":61,"tag":442,"props":2551,"children":2552},{"style":1676},[2553],{"type":67,"value":530},{"type":61,"tag":442,"props":2555,"children":2556},{"style":1676},[2557],{"type":67,"value":1771},{"type":61,"tag":442,"props":2559,"children":2560},{"style":470},[2561],{"type":67,"value":2562},"\u002Fdata\u002F{{ context.ds_nodash }}\u002Foutput.csv",{"type":61,"tag":442,"props":2564,"children":2565},{"style":1676},[2566],{"type":67,"value":1781},{"type":61,"tag":442,"props":2568,"children":2569},{"class":444,"line":880},[2570,2575,2579,2583,2588],{"type":61,"tag":442,"props":2571,"children":2572},{"style":1670},[2573],{"type":67,"value":2574},"    run_id",{"type":61,"tag":442,"props":2576,"children":2577},{"style":1676},[2578],{"type":67,"value":530},{"type":61,"tag":442,"props":2580,"children":2581},{"style":1676},[2582],{"type":67,"value":1771},{"type":61,"tag":442,"props":2584,"children":2585},{"style":470},[2586],{"type":67,"value":2587},"{{ context.dag_run.run_id }}",{"type":61,"tag":442,"props":2589,"children":2590},{"style":1676},[2591],{"type":67,"value":1781},{"type":61,"tag":70,"props":2593,"children":2594},{},[2595],{"type":67,"value":2596},"Available template variables:",{"type":61,"tag":1183,"props":2598,"children":2599},{},[2600,2611,2622,2633],{"type":61,"tag":137,"props":2601,"children":2602},{},[2603,2609],{"type":61,"tag":91,"props":2604,"children":2606},{"className":2605},[],[2607],{"type":67,"value":2608},"env",{"type":67,"value":2610}," — environment variables",{"type":61,"tag":137,"props":2612,"children":2613},{},[2614,2620],{"type":61,"tag":91,"props":2615,"children":2617},{"className":2616},[],[2618],{"type":67,"value":2619},"var",{"type":67,"value":2621}," — Airflow Variables",{"type":61,"tag":137,"props":2623,"children":2624},{},[2625,2631],{"type":61,"tag":91,"props":2626,"children":2628},{"className":2627},[],[2629],{"type":67,"value":2630},"conn",{"type":67,"value":2632}," — Airflow Connections",{"type":61,"tag":137,"props":2634,"children":2635},{},[2636,2642,2644,2650,2651,2657,2658,2664],{"type":61,"tag":91,"props":2637,"children":2639},{"className":2638},[],[2640],{"type":67,"value":2641},"context",{"type":67,"value":2643}," — proxy that generates Airflow template expressions for runtime macros (e.g. ",{"type":61,"tag":91,"props":2645,"children":2647},{"className":2646},[],[2648],{"type":67,"value":2649},"context.ds_nodash",{"type":67,"value":1203},{"type":61,"tag":91,"props":2652,"children":2654},{"className":2653},[],[2655],{"type":67,"value":2656},"context.dag_run.conf",{"type":67,"value":1203},{"type":61,"tag":91,"props":2659,"children":2661},{"className":2660},[],[2662],{"type":67,"value":2663},"context.task_instance.xcom_pull(...)",{"type":67,"value":2665},")",{"type":61,"tag":168,"props":2667,"children":2668},{},[],{"type":61,"tag":121,"props":2670,"children":2672},{"id":2671},"blueprints-in-python-dags",[2673],{"type":67,"value":273},{"type":61,"tag":70,"props":2675,"children":2676},{},[2677],{"type":67,"value":2678},"Blueprints aren't tied to the YAML composition flow. Two patterns (both 0.3.0) let you use them from Python — useful for incremental adoption or data-driven DAG generation.",{"type":61,"tag":424,"props":2680,"children":2682},{"id":2681},"inside-a-hand-written-dag",[2683],{"type":67,"value":2684},"Inside a Hand-Written DAG",{"type":61,"tag":70,"props":2686,"children":2687},{},[2688,2690,2695,2696,2701,2703,2709],{"type":67,"value":2689},"To drop a blueprint-rendered step into an existing Python DAG, instantiate the Blueprint class, set its ",{"type":61,"tag":91,"props":2691,"children":2693},{"className":2692},[],[2694],{"type":67,"value":1481},{"type":67,"value":1483},{"type":61,"tag":91,"props":2697,"children":2699},{"className":2698},[],[2700],{"type":67,"value":1078},{"type":67,"value":2702},", and wire it in with ",{"type":61,"tag":91,"props":2704,"children":2706},{"className":2705},[],[2707],{"type":67,"value":2708},">>",{"type":67,"value":530},{"type":61,"tag":431,"props":2711,"children":2713},{"className":533,"code":2712,"language":535,"meta":436,"style":436},"# dags\u002Fhybrid_dag.py\nfrom datetime import datetime\n\nfrom airflow import DAG\nfrom airflow.operators.bash import BashOperator\n\nfrom dags.etl_blueprints import Extract, ExtractConfig, Load, LoadConfig\n\nwith DAG(dag_id=\"hybrid_python_dag\", start_date=datetime(2024, 1, 1), schedule=None, catchup=False) as dag:\n    setup = BashOperator(task_id=\"setup\", bash_command=\"echo 'setup'\")\n\n    extract = Extract()\n    extract.step_id = \"extract\"\n    extract_group = extract.render(ExtractConfig(source_table=\"raw.events\", batch_size=100))\n\n    load = Load()\n    load.step_id = \"load\"\n    load_task = load.render(LoadConfig(target_table=\"warehouse.events\", mode=\"append\"))\n\n    finalize = BashOperator(task_id=\"finalize\", bash_command=\"echo 'done'\")\n\n    setup >> extract_group >> load_task >> finalize\n",[2714],{"type":61,"tag":91,"props":2715,"children":2716},{"__ignoreMap":436},[2717,2725,2733,2740,2748,2755,2762,2770,2777,2785,2793,2800,2808,2816,2824,2831,2839,2847,2855,2862,2870,2877],{"type":61,"tag":442,"props":2718,"children":2719},{"class":444,"line":445},[2720],{"type":61,"tag":442,"props":2721,"children":2722},{},[2723],{"type":67,"value":2724},"# dags\u002Fhybrid_dag.py\n",{"type":61,"tag":442,"props":2726,"children":2727},{"class":444,"line":455},[2728],{"type":61,"tag":442,"props":2729,"children":2730},{},[2731],{"type":67,"value":2732},"from datetime import datetime\n",{"type":61,"tag":442,"props":2734,"children":2735},{"class":444,"line":476},[2736],{"type":61,"tag":442,"props":2737,"children":2738},{"emptyLinePlaceholder":480},[2739],{"type":67,"value":483},{"type":61,"tag":442,"props":2741,"children":2742},{"class":444,"line":486},[2743],{"type":61,"tag":442,"props":2744,"children":2745},{},[2746],{"type":67,"value":2747},"from airflow import DAG\n",{"type":61,"tag":442,"props":2749,"children":2750},{"class":444,"line":495},[2751],{"type":61,"tag":442,"props":2752,"children":2753},{},[2754],{"type":67,"value":836},{"type":61,"tag":442,"props":2756,"children":2757},{"class":444,"line":862},[2758],{"type":61,"tag":442,"props":2759,"children":2760},{"emptyLinePlaceholder":480},[2761],{"type":67,"value":483},{"type":61,"tag":442,"props":2763,"children":2764},{"class":444,"line":871},[2765],{"type":61,"tag":442,"props":2766,"children":2767},{},[2768],{"type":67,"value":2769},"from dags.etl_blueprints import Extract, ExtractConfig, Load, LoadConfig\n",{"type":61,"tag":442,"props":2771,"children":2772},{"class":444,"line":880},[2773],{"type":61,"tag":442,"props":2774,"children":2775},{"emptyLinePlaceholder":480},[2776],{"type":67,"value":483},{"type":61,"tag":442,"props":2778,"children":2779},{"class":444,"line":889},[2780],{"type":61,"tag":442,"props":2781,"children":2782},{},[2783],{"type":67,"value":2784},"with DAG(dag_id=\"hybrid_python_dag\", start_date=datetime(2024, 1, 1), schedule=None, catchup=False) as dag:\n",{"type":61,"tag":442,"props":2786,"children":2787},{"class":444,"line":898},[2788],{"type":61,"tag":442,"props":2789,"children":2790},{},[2791],{"type":67,"value":2792},"    setup = BashOperator(task_id=\"setup\", bash_command=\"echo 'setup'\")\n",{"type":61,"tag":442,"props":2794,"children":2795},{"class":444,"line":907},[2796],{"type":61,"tag":442,"props":2797,"children":2798},{"emptyLinePlaceholder":480},[2799],{"type":67,"value":483},{"type":61,"tag":442,"props":2801,"children":2802},{"class":444,"line":915},[2803],{"type":61,"tag":442,"props":2804,"children":2805},{},[2806],{"type":67,"value":2807},"    extract = Extract()\n",{"type":61,"tag":442,"props":2809,"children":2810},{"class":444,"line":924},[2811],{"type":61,"tag":442,"props":2812,"children":2813},{},[2814],{"type":67,"value":2815},"    extract.step_id = \"extract\"\n",{"type":61,"tag":442,"props":2817,"children":2818},{"class":444,"line":933},[2819],{"type":61,"tag":442,"props":2820,"children":2821},{},[2822],{"type":67,"value":2823},"    extract_group = extract.render(ExtractConfig(source_table=\"raw.events\", batch_size=100))\n",{"type":61,"tag":442,"props":2825,"children":2826},{"class":444,"line":941},[2827],{"type":61,"tag":442,"props":2828,"children":2829},{"emptyLinePlaceholder":480},[2830],{"type":67,"value":483},{"type":61,"tag":442,"props":2832,"children":2833},{"class":444,"line":950},[2834],{"type":61,"tag":442,"props":2835,"children":2836},{},[2837],{"type":67,"value":2838},"    load = Load()\n",{"type":61,"tag":442,"props":2840,"children":2841},{"class":444,"line":959},[2842],{"type":61,"tag":442,"props":2843,"children":2844},{},[2845],{"type":67,"value":2846},"    load.step_id = \"load\"\n",{"type":61,"tag":442,"props":2848,"children":2849},{"class":444,"line":968},[2850],{"type":61,"tag":442,"props":2851,"children":2852},{},[2853],{"type":67,"value":2854},"    load_task = load.render(LoadConfig(target_table=\"warehouse.events\", mode=\"append\"))\n",{"type":61,"tag":442,"props":2856,"children":2857},{"class":444,"line":977},[2858],{"type":61,"tag":442,"props":2859,"children":2860},{"emptyLinePlaceholder":480},[2861],{"type":67,"value":483},{"type":61,"tag":442,"props":2863,"children":2864},{"class":444,"line":986},[2865],{"type":61,"tag":442,"props":2866,"children":2867},{},[2868],{"type":67,"value":2869},"    finalize = BashOperator(task_id=\"finalize\", bash_command=\"echo 'done'\")\n",{"type":61,"tag":442,"props":2871,"children":2872},{"class":444,"line":995},[2873],{"type":61,"tag":442,"props":2874,"children":2875},{"emptyLinePlaceholder":480},[2876],{"type":67,"value":483},{"type":61,"tag":442,"props":2878,"children":2880},{"class":444,"line":2879},22,[2881],{"type":61,"tag":442,"props":2882,"children":2883},{},[2884],{"type":67,"value":2885},"    setup >> extract_group >> load_task >> finalize\n",{"type":61,"tag":70,"props":2887,"children":2888},{},[2889,2891,2896,2898,2904,2905,2911],{"type":67,"value":2890},"The ",{"type":61,"tag":91,"props":2892,"children":2894},{"className":2893},[],[2895],{"type":67,"value":1481},{"type":67,"value":2897}," you set determines the ",{"type":61,"tag":91,"props":2899,"children":2901},{"className":2900},[],[2902],{"type":67,"value":2903},"task_id",{"type":67,"value":1262},{"type":61,"tag":91,"props":2906,"children":2908},{"className":2907},[],[2909],{"type":67,"value":2910},"group_id",{"type":67,"value":2912}," the blueprint renders under.",{"type":61,"tag":424,"props":2914,"children":2916},{"id":2915},"programmatic-building-with-builder-dagconfig",[2917,2919,2925,2926],{"type":67,"value":2918},"Programmatic Building with ",{"type":61,"tag":91,"props":2920,"children":2922},{"className":2921},[],[2923],{"type":67,"value":2924},"Builder",{"type":67,"value":1262},{"type":61,"tag":91,"props":2927,"children":2929},{"className":2928},[],[2930],{"type":67,"value":2931},"DAGConfig",{"type":61,"tag":70,"props":2933,"children":2934},{},[2935,2937,2942,2943,2948,2950,2956],{"type":67,"value":2936},"For data-driven DAG generation (one DAG per region, tenant, etc.), build DAGs in a loop with ",{"type":61,"tag":91,"props":2938,"children":2940},{"className":2939},[],[2941],{"type":67,"value":2924},{"type":67,"value":1308},{"type":61,"tag":91,"props":2944,"children":2946},{"className":2945},[],[2947],{"type":67,"value":2931},{"type":67,"value":2949},", then register each in ",{"type":61,"tag":91,"props":2951,"children":2953},{"className":2952},[],[2954],{"type":67,"value":2955},"globals()",{"type":67,"value":2957}," so Airflow discovers them:",{"type":61,"tag":431,"props":2959,"children":2961},{"className":533,"code":2960,"language":535,"meta":436,"style":436},"from blueprint import Builder, DAGConfig\n\nbuilder = Builder()\n\nfor region in [\"us\", \"eu\", \"apac\"]:\n    config = DAGConfig(\n        dag_id=f\"pipeline_{region}\",\n        schedule=\"@hourly\",\n        steps={\n            \"extract\": {\"blueprint\": \"extract\", \"source_table\": f\"raw.{region}\"},\n            \"load\": {\"blueprint\": \"load\", \"depends_on\": [\"extract\"], \"target_table\": f\"out.{region}\"},\n        },\n    )\n    dag = builder.build(config)\n    globals()[dag.dag_id] = dag\n",[2962],{"type":61,"tag":91,"props":2963,"children":2964},{"__ignoreMap":436},[2965,2973,2980,2988,2995,3003,3011,3019,3027,3035,3043,3051,3059,3067,3075],{"type":61,"tag":442,"props":2966,"children":2967},{"class":444,"line":445},[2968],{"type":61,"tag":442,"props":2969,"children":2970},{},[2971],{"type":67,"value":2972},"from blueprint import Builder, DAGConfig\n",{"type":61,"tag":442,"props":2974,"children":2975},{"class":444,"line":455},[2976],{"type":61,"tag":442,"props":2977,"children":2978},{"emptyLinePlaceholder":480},[2979],{"type":67,"value":483},{"type":61,"tag":442,"props":2981,"children":2982},{"class":444,"line":476},[2983],{"type":61,"tag":442,"props":2984,"children":2985},{},[2986],{"type":67,"value":2987},"builder = Builder()\n",{"type":61,"tag":442,"props":2989,"children":2990},{"class":444,"line":486},[2991],{"type":61,"tag":442,"props":2992,"children":2993},{"emptyLinePlaceholder":480},[2994],{"type":67,"value":483},{"type":61,"tag":442,"props":2996,"children":2997},{"class":444,"line":495},[2998],{"type":61,"tag":442,"props":2999,"children":3000},{},[3001],{"type":67,"value":3002},"for region in [\"us\", \"eu\", \"apac\"]:\n",{"type":61,"tag":442,"props":3004,"children":3005},{"class":444,"line":862},[3006],{"type":61,"tag":442,"props":3007,"children":3008},{},[3009],{"type":67,"value":3010},"    config = DAGConfig(\n",{"type":61,"tag":442,"props":3012,"children":3013},{"class":444,"line":871},[3014],{"type":61,"tag":442,"props":3015,"children":3016},{},[3017],{"type":67,"value":3018},"        dag_id=f\"pipeline_{region}\",\n",{"type":61,"tag":442,"props":3020,"children":3021},{"class":444,"line":880},[3022],{"type":61,"tag":442,"props":3023,"children":3024},{},[3025],{"type":67,"value":3026},"        schedule=\"@hourly\",\n",{"type":61,"tag":442,"props":3028,"children":3029},{"class":444,"line":889},[3030],{"type":61,"tag":442,"props":3031,"children":3032},{},[3033],{"type":67,"value":3034},"        steps={\n",{"type":61,"tag":442,"props":3036,"children":3037},{"class":444,"line":898},[3038],{"type":61,"tag":442,"props":3039,"children":3040},{},[3041],{"type":67,"value":3042},"            \"extract\": {\"blueprint\": \"extract\", \"source_table\": f\"raw.{region}\"},\n",{"type":61,"tag":442,"props":3044,"children":3045},{"class":444,"line":907},[3046],{"type":61,"tag":442,"props":3047,"children":3048},{},[3049],{"type":67,"value":3050},"            \"load\": {\"blueprint\": \"load\", \"depends_on\": [\"extract\"], \"target_table\": f\"out.{region}\"},\n",{"type":61,"tag":442,"props":3052,"children":3053},{"class":444,"line":915},[3054],{"type":61,"tag":442,"props":3055,"children":3056},{},[3057],{"type":67,"value":3058},"        },\n",{"type":61,"tag":442,"props":3060,"children":3061},{"class":444,"line":924},[3062],{"type":61,"tag":442,"props":3063,"children":3064},{},[3065],{"type":67,"value":3066},"    )\n",{"type":61,"tag":442,"props":3068,"children":3069},{"class":444,"line":933},[3070],{"type":61,"tag":442,"props":3071,"children":3072},{},[3073],{"type":67,"value":3074},"    dag = builder.build(config)\n",{"type":61,"tag":442,"props":3076,"children":3077},{"class":444,"line":941},[3078],{"type":61,"tag":442,"props":3079,"children":3080},{},[3081],{"type":67,"value":3082},"    globals()[dag.dag_id] = dag\n",{"type":61,"tag":70,"props":3084,"children":3085},{},[3086,3091,3093,3098,3099,3104,3106,3111,3113,3118,3119,3124,3125,3131,3133,3138,3140,3146],{"type":61,"tag":91,"props":3087,"children":3089},{"className":3088},[],[3090],{"type":67,"value":2931},{"type":67,"value":3092}," accepts the same fields you would write in YAML (",{"type":61,"tag":91,"props":3094,"children":3096},{"className":3095},[],[3097],{"type":67,"value":1823},{"type":67,"value":1203},{"type":61,"tag":91,"props":3100,"children":3102},{"className":3101},[],[3103],{"type":67,"value":1673},{"type":67,"value":3105},", plus any fields your ",{"type":61,"tag":91,"props":3107,"children":3109},{"className":3108},[],[3110],{"type":67,"value":624},{"type":67,"value":3112}," consumes). ",{"type":61,"tag":91,"props":3114,"children":3116},{"className":3115},[],[3117],{"type":67,"value":2924},{"type":67,"value":1203},{"type":61,"tag":91,"props":3120,"children":3122},{"className":3121},[],[3123],{"type":67,"value":2931},{"type":67,"value":1254},{"type":61,"tag":91,"props":3126,"children":3128},{"className":3127},[],[3129],{"type":67,"value":3130},"StepConfig",{"type":67,"value":3132}," are all exported from ",{"type":61,"tag":91,"props":3134,"children":3136},{"className":3135},[],[3137],{"type":67,"value":4},{"type":67,"value":3139},". See ",{"type":61,"tag":91,"props":3141,"children":3143},{"className":3142},[],[3144],{"type":67,"value":3145},"examples\u002Fadvanced\u002Fdags\u002Fprogrammatic_dags.py",{"type":67,"value":3147}," in the repo.",{"type":61,"tag":168,"props":3149,"children":3150},{},[],{"type":61,"tag":121,"props":3152,"children":3154},{"id":3153},"customizing-dag-level-configuration",[3155],{"type":67,"value":290},{"type":61,"tag":70,"props":3157,"children":3158},{},[3159,3161,3166,3167,3172,3174,3179],{"type":67,"value":3160},"By default, Blueprint supports ",{"type":61,"tag":91,"props":3162,"children":3164},{"className":3163},[],[3165],{"type":67,"value":1840},{"type":67,"value":1308},{"type":61,"tag":91,"props":3168,"children":3170},{"className":3169},[],[3171],{"type":67,"value":1865},{"type":67,"value":3173}," as DAG-level YAML fields. To use other DAG constructor arguments (tags, default_args, catchup, etc.), define a ",{"type":61,"tag":91,"props":3175,"children":3177},{"className":3176},[],[3178],{"type":67,"value":624},{"type":67,"value":3180}," subclass.",{"type":61,"tag":424,"props":3182,"children":3184},{"id":3183},"when-to-use",[3185],{"type":67,"value":3186},"When to Use",{"type":61,"tag":1183,"props":3188,"children":3189},{},[3190,3221],{"type":61,"tag":137,"props":3191,"children":3192},{},[3193,3195,3200,3201,3206,3207,3212,3213,3219],{"type":67,"value":3194},"User wants ",{"type":61,"tag":91,"props":3196,"children":3198},{"className":3197},[],[3199],{"type":67,"value":2072},{"type":67,"value":1203},{"type":61,"tag":91,"props":3202,"children":3204},{"className":3203},[],[3205],{"type":67,"value":2079},{"type":67,"value":1203},{"type":61,"tag":91,"props":3208,"children":3210},{"className":3209},[],[3211],{"type":67,"value":2086},{"type":67,"value":1203},{"type":61,"tag":91,"props":3214,"children":3216},{"className":3215},[],[3217],{"type":67,"value":3218},"start_date",{"type":67,"value":3220},", or any other DAG kwargs in YAML",{"type":61,"tag":137,"props":3222,"children":3223},{},[3224],{"type":67,"value":3225},"User wants to derive DAG properties from config (e.g. team name → owner, tier → retries)",{"type":61,"tag":424,"props":3227,"children":3229},{"id":3228},"defining-a-blueprintdagargs-subclass",[3230],{"type":67,"value":3231},"Defining a BlueprintDagArgs Subclass",{"type":61,"tag":431,"props":3233,"children":3235},{"className":533,"code":3234,"language":535,"meta":436,"style":436},"# dags\u002Ftemplates\u002Fmy_dag_args.py\nfrom pydantic import BaseModel\nfrom blueprint import BlueprintDagArgs\n\nclass MyDagArgsConfig(BaseModel):\n    schedule: str | None = None\n    description: str | None = None\n    tags: list[str] = []\n    owner: str = \"data-team\"\n    retries: int = 2\n\nclass MyDagArgs(BlueprintDagArgs[MyDagArgsConfig]):\n    def render(self, config: MyDagArgsConfig) -> dict[str, Any]:\n        return {\n            \"schedule\": config.schedule,\n            \"description\": config.description,\n            \"tags\": config.tags,\n            \"default_args\": {\n                \"owner\": config.owner,\n                \"retries\": config.retries,\n            },\n        }\n",[3236],{"type":61,"tag":91,"props":3237,"children":3238},{"__ignoreMap":436},[3239,3247,3255,3263,3270,3278,3286,3294,3302,3310,3318,3325,3333,3341,3349,3357,3365,3373,3381,3389,3397,3405],{"type":61,"tag":442,"props":3240,"children":3241},{"class":444,"line":445},[3242],{"type":61,"tag":442,"props":3243,"children":3244},{},[3245],{"type":67,"value":3246},"# dags\u002Ftemplates\u002Fmy_dag_args.py\n",{"type":61,"tag":442,"props":3248,"children":3249},{"class":444,"line":455},[3250],{"type":61,"tag":442,"props":3251,"children":3252},{},[3253],{"type":67,"value":3254},"from pydantic import BaseModel\n",{"type":61,"tag":442,"props":3256,"children":3257},{"class":444,"line":476},[3258],{"type":61,"tag":442,"props":3259,"children":3260},{},[3261],{"type":67,"value":3262},"from blueprint import BlueprintDagArgs\n",{"type":61,"tag":442,"props":3264,"children":3265},{"class":444,"line":486},[3266],{"type":61,"tag":442,"props":3267,"children":3268},{"emptyLinePlaceholder":480},[3269],{"type":67,"value":483},{"type":61,"tag":442,"props":3271,"children":3272},{"class":444,"line":495},[3273],{"type":61,"tag":442,"props":3274,"children":3275},{},[3276],{"type":67,"value":3277},"class MyDagArgsConfig(BaseModel):\n",{"type":61,"tag":442,"props":3279,"children":3280},{"class":444,"line":862},[3281],{"type":61,"tag":442,"props":3282,"children":3283},{},[3284],{"type":67,"value":3285},"    schedule: str | None = None\n",{"type":61,"tag":442,"props":3287,"children":3288},{"class":444,"line":871},[3289],{"type":61,"tag":442,"props":3290,"children":3291},{},[3292],{"type":67,"value":3293},"    description: str | None = None\n",{"type":61,"tag":442,"props":3295,"children":3296},{"class":444,"line":880},[3297],{"type":61,"tag":442,"props":3298,"children":3299},{},[3300],{"type":67,"value":3301},"    tags: list[str] = []\n",{"type":61,"tag":442,"props":3303,"children":3304},{"class":444,"line":889},[3305],{"type":61,"tag":442,"props":3306,"children":3307},{},[3308],{"type":67,"value":3309},"    owner: str = \"data-team\"\n",{"type":61,"tag":442,"props":3311,"children":3312},{"class":444,"line":898},[3313],{"type":61,"tag":442,"props":3314,"children":3315},{},[3316],{"type":67,"value":3317},"    retries: int = 2\n",{"type":61,"tag":442,"props":3319,"children":3320},{"class":444,"line":907},[3321],{"type":61,"tag":442,"props":3322,"children":3323},{"emptyLinePlaceholder":480},[3324],{"type":67,"value":483},{"type":61,"tag":442,"props":3326,"children":3327},{"class":444,"line":915},[3328],{"type":61,"tag":442,"props":3329,"children":3330},{},[3331],{"type":67,"value":3332},"class MyDagArgs(BlueprintDagArgs[MyDagArgsConfig]):\n",{"type":61,"tag":442,"props":3334,"children":3335},{"class":444,"line":924},[3336],{"type":61,"tag":442,"props":3337,"children":3338},{},[3339],{"type":67,"value":3340},"    def render(self, config: MyDagArgsConfig) -> dict[str, Any]:\n",{"type":61,"tag":442,"props":3342,"children":3343},{"class":444,"line":933},[3344],{"type":61,"tag":442,"props":3345,"children":3346},{},[3347],{"type":67,"value":3348},"        return {\n",{"type":61,"tag":442,"props":3350,"children":3351},{"class":444,"line":941},[3352],{"type":61,"tag":442,"props":3353,"children":3354},{},[3355],{"type":67,"value":3356},"            \"schedule\": config.schedule,\n",{"type":61,"tag":442,"props":3358,"children":3359},{"class":444,"line":950},[3360],{"type":61,"tag":442,"props":3361,"children":3362},{},[3363],{"type":67,"value":3364},"            \"description\": config.description,\n",{"type":61,"tag":442,"props":3366,"children":3367},{"class":444,"line":959},[3368],{"type":61,"tag":442,"props":3369,"children":3370},{},[3371],{"type":67,"value":3372},"            \"tags\": config.tags,\n",{"type":61,"tag":442,"props":3374,"children":3375},{"class":444,"line":968},[3376],{"type":61,"tag":442,"props":3377,"children":3378},{},[3379],{"type":67,"value":3380},"            \"default_args\": {\n",{"type":61,"tag":442,"props":3382,"children":3383},{"class":444,"line":977},[3384],{"type":61,"tag":442,"props":3385,"children":3386},{},[3387],{"type":67,"value":3388},"                \"owner\": config.owner,\n",{"type":61,"tag":442,"props":3390,"children":3391},{"class":444,"line":986},[3392],{"type":61,"tag":442,"props":3393,"children":3394},{},[3395],{"type":67,"value":3396},"                \"retries\": config.retries,\n",{"type":61,"tag":442,"props":3398,"children":3399},{"class":444,"line":995},[3400],{"type":61,"tag":442,"props":3401,"children":3402},{},[3403],{"type":67,"value":3404},"            },\n",{"type":61,"tag":442,"props":3406,"children":3407},{"class":444,"line":2879},[3408],{"type":61,"tag":442,"props":3409,"children":3410},{},[3411],{"type":67,"value":3412},"        }\n",{"type":61,"tag":70,"props":3414,"children":3415},{},[3416],{"type":67,"value":3417},"Then in YAML, the extra fields are validated by the config model:",{"type":61,"tag":431,"props":3419,"children":3421},{"className":1658,"code":3420,"language":1660,"meta":436,"style":436},"dag_id: my_pipeline\nschedule: \"@daily\"\ntags: [etl, production]\nowner: data-team\nretries: 3\n\nsteps:\n  extract:\n    blueprint: extract\n    source_table: raw.data\n",[3422],{"type":61,"tag":91,"props":3423,"children":3424},{"__ignoreMap":436},[3425,3440,3463,3495,3512,3529,3536,3547,3558,3573],{"type":61,"tag":442,"props":3426,"children":3427},{"class":444,"line":445},[3428,3432,3436],{"type":61,"tag":442,"props":3429,"children":3430},{"style":1670},[3431],{"type":67,"value":1823},{"type":61,"tag":442,"props":3433,"children":3434},{"style":1676},[3435],{"type":67,"value":530},{"type":61,"tag":442,"props":3437,"children":3438},{"style":470},[3439],{"type":67,"value":1832},{"type":61,"tag":442,"props":3441,"children":3442},{"class":444,"line":455},[3443,3447,3451,3455,3459],{"type":61,"tag":442,"props":3444,"children":3445},{"style":1670},[3446],{"type":67,"value":1840},{"type":61,"tag":442,"props":3448,"children":3449},{"style":1676},[3450],{"type":67,"value":530},{"type":61,"tag":442,"props":3452,"children":3453},{"style":1676},[3454],{"type":67,"value":1771},{"type":61,"tag":442,"props":3456,"children":3457},{"style":470},[3458],{"type":67,"value":1853},{"type":61,"tag":442,"props":3460,"children":3461},{"style":1676},[3462],{"type":67,"value":1781},{"type":61,"tag":442,"props":3464,"children":3465},{"class":444,"line":476},[3466,3470,3474,3478,3482,3486,3491],{"type":61,"tag":442,"props":3467,"children":3468},{"style":1670},[3469],{"type":67,"value":2072},{"type":61,"tag":442,"props":3471,"children":3472},{"style":1676},[3473],{"type":67,"value":530},{"type":61,"tag":442,"props":3475,"children":3476},{"style":1676},[3477],{"type":67,"value":1725},{"type":61,"tag":442,"props":3479,"children":3480},{"style":470},[3481],{"type":67,"value":18},{"type":61,"tag":442,"props":3483,"children":3484},{"style":1676},[3485],{"type":67,"value":1735},{"type":61,"tag":442,"props":3487,"children":3488},{"style":470},[3489],{"type":67,"value":3490}," production",{"type":61,"tag":442,"props":3492,"children":3493},{"style":1676},[3494],{"type":67,"value":1754},{"type":61,"tag":442,"props":3496,"children":3497},{"class":444,"line":486},[3498,3503,3507],{"type":61,"tag":442,"props":3499,"children":3500},{"style":1670},[3501],{"type":67,"value":3502},"owner",{"type":61,"tag":442,"props":3504,"children":3505},{"style":1676},[3506],{"type":67,"value":530},{"type":61,"tag":442,"props":3508,"children":3509},{"style":470},[3510],{"type":67,"value":3511}," data-team\n",{"type":61,"tag":442,"props":3513,"children":3514},{"class":444,"line":495},[3515,3520,3524],{"type":61,"tag":442,"props":3516,"children":3517},{"style":1670},[3518],{"type":67,"value":3519},"retries",{"type":61,"tag":442,"props":3521,"children":3522},{"style":1676},[3523],{"type":67,"value":530},{"type":61,"tag":442,"props":3525,"children":3526},{"style":1960},[3527],{"type":67,"value":3528}," 3\n",{"type":61,"tag":442,"props":3530,"children":3531},{"class":444,"line":862},[3532],{"type":61,"tag":442,"props":3533,"children":3534},{"emptyLinePlaceholder":480},[3535],{"type":67,"value":483},{"type":61,"tag":442,"props":3537,"children":3538},{"class":444,"line":871},[3539,3543],{"type":61,"tag":442,"props":3540,"children":3541},{"style":1670},[3542],{"type":67,"value":1673},{"type":61,"tag":442,"props":3544,"children":3545},{"style":1676},[3546],{"type":67,"value":1679},{"type":61,"tag":442,"props":3548,"children":3549},{"class":444,"line":880},[3550,3554],{"type":61,"tag":442,"props":3551,"children":3552},{"style":1670},[3553],{"type":67,"value":2521},{"type":61,"tag":442,"props":3555,"children":3556},{"style":1676},[3557],{"type":67,"value":1679},{"type":61,"tag":442,"props":3559,"children":3560},{"class":444,"line":889},[3561,3565,3569],{"type":61,"tag":442,"props":3562,"children":3563},{"style":1670},[3564],{"type":67,"value":1699},{"type":61,"tag":442,"props":3566,"children":3567},{"style":1676},[3568],{"type":67,"value":530},{"type":61,"tag":442,"props":3570,"children":3571},{"style":470},[3572],{"type":67,"value":2541},{"type":61,"tag":442,"props":3574,"children":3575},{"class":444,"line":898},[3576,3580,3584],{"type":61,"tag":442,"props":3577,"children":3578},{"style":1670},[3579],{"type":67,"value":1936},{"type":61,"tag":442,"props":3581,"children":3582},{"style":1676},[3583],{"type":67,"value":530},{"type":61,"tag":442,"props":3585,"children":3586},{"style":470},[3587],{"type":67,"value":3588}," raw.data\n",{"type":61,"tag":424,"props":3590,"children":3592},{"id":3591},"rules",[3593],{"type":67,"value":3594},"Rules",{"type":61,"tag":1183,"props":3596,"children":3597},{},[3598,3625,3644],{"type":61,"tag":137,"props":3599,"children":3600},{},[3601,3603,3608,3610,3615,3617,3623],{"type":67,"value":3602},"Only ",{"type":61,"tag":83,"props":3604,"children":3605},{},[3606],{"type":67,"value":3607},"one",{"type":67,"value":3609}," ",{"type":61,"tag":91,"props":3611,"children":3613},{"className":3612},[],[3614],{"type":67,"value":624},{"type":67,"value":3616}," subclass per project (raises ",{"type":61,"tag":91,"props":3618,"children":3620},{"className":3619},[],[3621],{"type":67,"value":3622},"MultipleDagArgsError",{"type":67,"value":3624}," if more than one exists)",{"type":61,"tag":137,"props":3626,"children":3627},{},[3628,3629,3634,3636,3642],{"type":67,"value":2890},{"type":61,"tag":91,"props":3630,"children":3632},{"className":3631},[],[3633],{"type":67,"value":1078},{"type":67,"value":3635}," method returns a dict of kwargs passed to the Airflow ",{"type":61,"tag":91,"props":3637,"children":3639},{"className":3638},[],[3640],{"type":67,"value":3641},"DAG()",{"type":67,"value":3643}," constructor",{"type":61,"tag":137,"props":3645,"children":3646},{},[3647,3649,3654,3656,3661,3662,3667],{"type":67,"value":3648},"If no custom subclass exists, the built-in ",{"type":61,"tag":91,"props":3650,"children":3652},{"className":3651},[],[3653],{"type":67,"value":2064},{"type":67,"value":3655}," is used (supports only ",{"type":61,"tag":91,"props":3657,"children":3659},{"className":3658},[],[3660],{"type":67,"value":1840},{"type":67,"value":1308},{"type":61,"tag":91,"props":3663,"children":3665},{"className":3664},[],[3666],{"type":67,"value":1865},{"type":67,"value":2665},{"type":61,"tag":168,"props":3669,"children":3670},{},[],{"type":61,"tag":121,"props":3672,"children":3674},{"id":3673},"runtime-parameter-overrides",[3675],{"type":67,"value":307},{"type":61,"tag":70,"props":3677,"children":3678},{},[3679],{"type":67,"value":3680},"Blueprint config fields can be overridden at DAG trigger time using Airflow params. This enables users to customize behavior when manually triggering DAGs from the Airflow UI.",{"type":61,"tag":424,"props":3682,"children":3684},{"id":3683},"opt-in-with-supports_params-true",[3685,3687],{"type":67,"value":3686},"Opt In with ",{"type":61,"tag":91,"props":3688,"children":3690},{"className":3689},[],[3691],{"type":67,"value":3692},"supports_params = True",{"type":61,"tag":70,"props":3694,"children":3695},{},[3696,3698,3703,3705,3711,3713,3733],{"type":67,"value":3697},"A blueprint must set the class attribute ",{"type":61,"tag":91,"props":3699,"children":3701},{"className":3700},[],[3702],{"type":67,"value":3692},{"type":67,"value":3704}," for its config fields to register as Airflow params (namespaced as ",{"type":61,"tag":91,"props":3706,"children":3708},{"className":3707},[],[3709],{"type":67,"value":3710},"{step}__{field}",{"type":67,"value":3712},"). ",{"type":61,"tag":83,"props":3714,"children":3715},{},[3716,3718,3724,3725,3731],{"type":67,"value":3717},"Without it, ",{"type":61,"tag":91,"props":3719,"children":3721},{"className":3720},[],[3722],{"type":67,"value":3723},"self.param()",{"type":67,"value":1262},{"type":61,"tag":91,"props":3726,"children":3728},{"className":3727},[],[3729],{"type":67,"value":3730},"self.resolve_config()",{"type":67,"value":3732}," do nothing and no fields appear in the trigger form.",{"type":67,"value":3734}," Only opt in for blueprints that actually use those methods — otherwise dead params clutter the form with no effect.",{"type":61,"tag":424,"props":3736,"children":3738},{"id":3737},"using-selfparam-in-template-fields",[3739,3741,3746],{"type":67,"value":3740},"Using ",{"type":61,"tag":91,"props":3742,"children":3744},{"className":3743},[],[3745],{"type":67,"value":3723},{"type":67,"value":3747}," in Template Fields",{"type":61,"tag":70,"props":3749,"children":3750},{},[3751,3752,3758],{"type":67,"value":573},{"type":61,"tag":91,"props":3753,"children":3755},{"className":3754},[],[3756],{"type":67,"value":3757},"self.param(\"field\")",{"type":67,"value":3759}," in operator template fields to make a config field overridable at runtime. Airflow renders the actual value at execution time:",{"type":61,"tag":431,"props":3761,"children":3763},{"className":533,"code":3762,"language":535,"meta":436,"style":436},"class ExtractConfig(BaseModel):\n    query: str = Field(description=\"SQL query to run\")\n    batch_size: int = Field(default=1000, ge=1)\n\nclass Extract(Blueprint[ExtractConfig]):\n    supports_params = True\n\n    def render(self, config: ExtractConfig) -> TaskGroup:\n        with TaskGroup(group_id=self.step_id) as group:\n            BashOperator(\n                task_id=\"run_query\",\n                bash_command=f\"run-etl --query {self.param('query')} --batch {self.param('batch_size')}\"\n            )\n        return group\n",[3764],{"type":61,"tag":91,"props":3765,"children":3766},{"__ignoreMap":436},[3767,3774,3782,3789,3796,3804,3812,3819,3827,3834,3841,3849,3857,3864],{"type":61,"tag":442,"props":3768,"children":3769},{"class":444,"line":445},[3770],{"type":61,"tag":442,"props":3771,"children":3772},{},[3773],{"type":67,"value":1356},{"type":61,"tag":442,"props":3775,"children":3776},{"class":444,"line":455},[3777],{"type":61,"tag":442,"props":3778,"children":3779},{},[3780],{"type":67,"value":3781},"    query: str = Field(description=\"SQL query to run\")\n",{"type":61,"tag":442,"props":3783,"children":3784},{"class":444,"line":476},[3785],{"type":61,"tag":442,"props":3786,"children":3787},{},[3788],{"type":67,"value":904},{"type":61,"tag":442,"props":3790,"children":3791},{"class":444,"line":486},[3792],{"type":61,"tag":442,"props":3793,"children":3794},{"emptyLinePlaceholder":480},[3795],{"type":67,"value":483},{"type":61,"tag":442,"props":3797,"children":3798},{"class":444,"line":495},[3799],{"type":61,"tag":442,"props":3800,"children":3801},{},[3802],{"type":67,"value":3803},"class Extract(Blueprint[ExtractConfig]):\n",{"type":61,"tag":442,"props":3805,"children":3806},{"class":444,"line":862},[3807],{"type":61,"tag":442,"props":3808,"children":3809},{},[3810],{"type":67,"value":3811},"    supports_params = True\n",{"type":61,"tag":442,"props":3813,"children":3814},{"class":444,"line":871},[3815],{"type":61,"tag":442,"props":3816,"children":3817},{"emptyLinePlaceholder":480},[3818],{"type":67,"value":483},{"type":61,"tag":442,"props":3820,"children":3821},{"class":444,"line":880},[3822],{"type":61,"tag":442,"props":3823,"children":3824},{},[3825],{"type":67,"value":3826},"    def render(self, config: ExtractConfig) -> TaskGroup:\n",{"type":61,"tag":442,"props":3828,"children":3829},{"class":444,"line":889},[3830],{"type":61,"tag":442,"props":3831,"children":3832},{},[3833],{"type":67,"value":956},{"type":61,"tag":442,"props":3835,"children":3836},{"class":444,"line":898},[3837],{"type":61,"tag":442,"props":3838,"children":3839},{},[3840],{"type":67,"value":965},{"type":61,"tag":442,"props":3842,"children":3843},{"class":444,"line":907},[3844],{"type":61,"tag":442,"props":3845,"children":3846},{},[3847],{"type":67,"value":3848},"                task_id=\"run_query\",\n",{"type":61,"tag":442,"props":3850,"children":3851},{"class":444,"line":915},[3852],{"type":61,"tag":442,"props":3853,"children":3854},{},[3855],{"type":67,"value":3856},"                bash_command=f\"run-etl --query {self.param('query')} --batch {self.param('batch_size')}\"\n",{"type":61,"tag":442,"props":3858,"children":3859},{"class":444,"line":924},[3860],{"type":61,"tag":442,"props":3861,"children":3862},{},[3863],{"type":67,"value":992},{"type":61,"tag":442,"props":3865,"children":3866},{"class":444,"line":933},[3867],{"type":61,"tag":442,"props":3868,"children":3869},{},[3870],{"type":67,"value":1001},{"type":61,"tag":424,"props":3872,"children":3874},{"id":3873},"using-selfresolve_config-in-python-callables",[3875,3876,3881],{"type":67,"value":3740},{"type":61,"tag":91,"props":3877,"children":3879},{"className":3878},[],[3880],{"type":67,"value":3730},{"type":67,"value":3882}," in Python Callables",{"type":61,"tag":70,"props":3884,"children":3885},{},[3886,3888,3894,3895,3901,3903,3908],{"type":67,"value":3887},"For ",{"type":61,"tag":91,"props":3889,"children":3891},{"className":3890},[],[3892],{"type":67,"value":3893},"@task",{"type":67,"value":1093},{"type":61,"tag":91,"props":3896,"children":3898},{"className":3897},[],[3899],{"type":67,"value":3900},"PythonOperator",{"type":67,"value":3902}," callables, use ",{"type":61,"tag":91,"props":3904,"children":3906},{"className":3905},[],[3907],{"type":67,"value":3730},{"type":67,"value":3909}," to merge runtime params into config. It returns a new validated config instance:",{"type":61,"tag":431,"props":3911,"children":3913},{"className":533,"code":3912,"language":535,"meta":436,"style":436},"class Extract(Blueprint[ExtractConfig]):\n    supports_params = True\n\n    def render(self, config: ExtractConfig) -> TaskGroup:\n        bp = self  # capture reference for closure\n\n        @task(task_id=\"run_query\")\n        def run_query(**context):\n            resolved = bp.resolve_config(config, context)\n            # resolved.query has the runtime override if one was provided\n            execute(resolved.query, resolved.batch_size)\n\n        with TaskGroup(group_id=self.step_id) as group:\n            run_query()\n        return group\n",[3914],{"type":61,"tag":91,"props":3915,"children":3916},{"__ignoreMap":436},[3917,3924,3931,3938,3945,3953,3960,3968,3976,3984,3992,4000,4007,4014,4022],{"type":61,"tag":442,"props":3918,"children":3919},{"class":444,"line":445},[3920],{"type":61,"tag":442,"props":3921,"children":3922},{},[3923],{"type":67,"value":3803},{"type":61,"tag":442,"props":3925,"children":3926},{"class":444,"line":455},[3927],{"type":61,"tag":442,"props":3928,"children":3929},{},[3930],{"type":67,"value":3811},{"type":61,"tag":442,"props":3932,"children":3933},{"class":444,"line":476},[3934],{"type":61,"tag":442,"props":3935,"children":3936},{"emptyLinePlaceholder":480},[3937],{"type":67,"value":483},{"type":61,"tag":442,"props":3939,"children":3940},{"class":444,"line":486},[3941],{"type":61,"tag":442,"props":3942,"children":3943},{},[3944],{"type":67,"value":3826},{"type":61,"tag":442,"props":3946,"children":3947},{"class":444,"line":495},[3948],{"type":61,"tag":442,"props":3949,"children":3950},{},[3951],{"type":67,"value":3952},"        bp = self  # capture reference for closure\n",{"type":61,"tag":442,"props":3954,"children":3955},{"class":444,"line":862},[3956],{"type":61,"tag":442,"props":3957,"children":3958},{"emptyLinePlaceholder":480},[3959],{"type":67,"value":483},{"type":61,"tag":442,"props":3961,"children":3962},{"class":444,"line":871},[3963],{"type":61,"tag":442,"props":3964,"children":3965},{},[3966],{"type":67,"value":3967},"        @task(task_id=\"run_query\")\n",{"type":61,"tag":442,"props":3969,"children":3970},{"class":444,"line":880},[3971],{"type":61,"tag":442,"props":3972,"children":3973},{},[3974],{"type":67,"value":3975},"        def run_query(**context):\n",{"type":61,"tag":442,"props":3977,"children":3978},{"class":444,"line":889},[3979],{"type":61,"tag":442,"props":3980,"children":3981},{},[3982],{"type":67,"value":3983},"            resolved = bp.resolve_config(config, context)\n",{"type":61,"tag":442,"props":3985,"children":3986},{"class":444,"line":898},[3987],{"type":61,"tag":442,"props":3988,"children":3989},{},[3990],{"type":67,"value":3991},"            # resolved.query has the runtime override if one was provided\n",{"type":61,"tag":442,"props":3993,"children":3994},{"class":444,"line":907},[3995],{"type":61,"tag":442,"props":3996,"children":3997},{},[3998],{"type":67,"value":3999},"            execute(resolved.query, resolved.batch_size)\n",{"type":61,"tag":442,"props":4001,"children":4002},{"class":444,"line":915},[4003],{"type":61,"tag":442,"props":4004,"children":4005},{"emptyLinePlaceholder":480},[4006],{"type":67,"value":483},{"type":61,"tag":442,"props":4008,"children":4009},{"class":444,"line":924},[4010],{"type":61,"tag":442,"props":4011,"children":4012},{},[4013],{"type":67,"value":956},{"type":61,"tag":442,"props":4015,"children":4016},{"class":444,"line":933},[4017],{"type":61,"tag":442,"props":4018,"children":4019},{},[4020],{"type":67,"value":4021},"            run_query()\n",{"type":61,"tag":442,"props":4023,"children":4024},{"class":444,"line":941},[4025],{"type":61,"tag":442,"props":4026,"children":4027},{},[4028],{"type":67,"value":1001},{"type":61,"tag":70,"props":4030,"children":4031},{},[4032,4033,4038,4040,4045,4047,4052],{"type":67,"value":573},{"type":61,"tag":91,"props":4034,"children":4036},{"className":4035},[],[4037],{"type":67,"value":3723},{"type":67,"value":4039}," for operators with template fields and ",{"type":61,"tag":91,"props":4041,"children":4043},{"className":4042},[],[4044],{"type":67,"value":3730},{"type":67,"value":4046}," for Python logic in ",{"type":61,"tag":91,"props":4048,"children":4050},{"className":4049},[],[4051],{"type":67,"value":3893},{"type":67,"value":4053}," functions; both can be combined in one blueprint.",{"type":61,"tag":424,"props":4055,"children":4057},{"id":4056},"how-it-works",[4058],{"type":67,"value":4059},"How It Works",{"type":61,"tag":1183,"props":4061,"children":4062},{},[4063,4082,4087],{"type":61,"tag":137,"props":4064,"children":4065},{},[4066,4068,4073,4075,4081],{"type":67,"value":4067},"Params are ",{"type":61,"tag":83,"props":4069,"children":4070},{},[4071],{"type":67,"value":4072},"auto-generated",{"type":67,"value":4074}," from Pydantic config models and namespaced per step (e.g. ",{"type":61,"tag":91,"props":4076,"children":4078},{"className":4077},[],[4079],{"type":67,"value":4080},"step_name__field",{"type":67,"value":2665},{"type":61,"tag":137,"props":4083,"children":4084},{},[4085],{"type":67,"value":4086},"YAML values become param defaults; Pydantic metadata (description, constraints, enum values) flows through to the Airflow trigger form",{"type":61,"tag":137,"props":4088,"children":4089},{},[4090,4092,4098],{"type":67,"value":4091},"Invalid overrides raise ",{"type":61,"tag":91,"props":4093,"children":4095},{"className":4094},[],[4096],{"type":67,"value":4097},"ValidationError",{"type":67,"value":4099}," at execution time",{"type":61,"tag":424,"props":4101,"children":4103},{"id":4102},"trigger-form-customization",[4104],{"type":67,"value":4105},"Trigger Form Customization",{"type":61,"tag":70,"props":4107,"children":4108},{},[4109,4111,4117],{"type":67,"value":4110},"Pydantic field schema flows through to Airflow's trigger form. Control how each field renders with ",{"type":61,"tag":91,"props":4112,"children":4114},{"className":4113},[],[4115],{"type":67,"value":4116},"json_schema_extra",{"type":67,"value":530},{"type":61,"tag":431,"props":4119,"children":4121},{"className":533,"code":4120,"language":535,"meta":436,"style":436},"class LoadConfig(BaseModel):\n    query: str = Field(description=\"SQL to execute\", json_schema_extra={\"format\": \"multiline\"})\n    schedule_date: str = Field(default=\"2024-01-01\", json_schema_extra={\"format\": \"date\"})\n",[4122],{"type":61,"tag":91,"props":4123,"children":4124},{"__ignoreMap":436},[4125,4133,4141],{"type":61,"tag":442,"props":4126,"children":4127},{"class":444,"line":445},[4128],{"type":61,"tag":442,"props":4129,"children":4130},{},[4131],{"type":67,"value":4132},"class LoadConfig(BaseModel):\n",{"type":61,"tag":442,"props":4134,"children":4135},{"class":444,"line":455},[4136],{"type":61,"tag":442,"props":4137,"children":4138},{},[4139],{"type":67,"value":4140},"    query: str = Field(description=\"SQL to execute\", json_schema_extra={\"format\": \"multiline\"})\n",{"type":61,"tag":442,"props":4142,"children":4143},{"class":444,"line":476},[4144],{"type":61,"tag":442,"props":4145,"children":4146},{},[4147],{"type":67,"value":4148},"    schedule_date: str = Field(default=\"2024-01-01\", json_schema_extra={\"format\": \"date\"})\n",{"type":61,"tag":70,"props":4150,"children":4151},{},[4152,4154,4160,4162,4168,4170,4176,4177,4183,4184,4190,4192,4198,4200,4206,4208,4214],{"type":67,"value":4153},"Supported ",{"type":61,"tag":91,"props":4155,"children":4157},{"className":4156},[],[4158],{"type":67,"value":4159},"format",{"type":67,"value":4161}," values include ",{"type":61,"tag":91,"props":4163,"children":4165},{"className":4164},[],[4166],{"type":67,"value":4167},"\"multiline\"",{"type":67,"value":4169}," (textarea), ",{"type":61,"tag":91,"props":4171,"children":4173},{"className":4172},[],[4174],{"type":67,"value":4175},"\"date\"",{"type":67,"value":1203},{"type":61,"tag":91,"props":4178,"children":4180},{"className":4179},[],[4181],{"type":67,"value":4182},"\"date-time\"",{"type":67,"value":1254},{"type":61,"tag":91,"props":4185,"children":4187},{"className":4186},[],[4188],{"type":67,"value":4189},"\"time\"",{"type":67,"value":4191}," (pickers). Also usable: ",{"type":61,"tag":91,"props":4193,"children":4195},{"className":4194},[],[4196],{"type":67,"value":4197},"examples",{"type":67,"value":4199}," (dropdown with free text), ",{"type":61,"tag":91,"props":4201,"children":4203},{"className":4202},[],[4204],{"type":67,"value":4205},"values_display",{"type":67,"value":4207}," (human-readable labels for enum\u002Fexample values), and ",{"type":61,"tag":91,"props":4209,"children":4211},{"className":4210},[],[4212],{"type":67,"value":4213},"description_md",{"type":67,"value":4215}," (Markdown descriptions).",{"type":61,"tag":70,"props":4217,"children":4218},{},[4219,4224,4226,4232,4234,4240,4241,4247,4248,4254,4255,4261,4262,4268,4269,4275,4277,4283,4284,4290,4292,4296,4298,4304,4306,4311,4313,4318],{"type":61,"tag":83,"props":4220,"children":4221},{},[4222],{"type":67,"value":4223},"Validation nuance:",{"type":67,"value":4225}," only ",{"type":61,"tag":91,"props":4227,"children":4229},{"className":4228},[],[4230],{"type":67,"value":4231},"Field",{"type":67,"value":4233}," constraints that map to JSON Schema (",{"type":61,"tag":91,"props":4235,"children":4237},{"className":4236},[],[4238],{"type":67,"value":4239},"ge",{"type":67,"value":1203},{"type":61,"tag":91,"props":4242,"children":4244},{"className":4243},[],[4245],{"type":67,"value":4246},"le",{"type":67,"value":1203},{"type":61,"tag":91,"props":4249,"children":4251},{"className":4250},[],[4252],{"type":67,"value":4253},"pattern",{"type":67,"value":1203},{"type":61,"tag":91,"props":4256,"children":4258},{"className":4257},[],[4259],{"type":67,"value":4260},"min_length",{"type":67,"value":1203},{"type":61,"tag":91,"props":4263,"children":4265},{"className":4264},[],[4266],{"type":67,"value":4267},"max_length",{"type":67,"value":1203},{"type":61,"tag":91,"props":4270,"children":4272},{"className":4271},[],[4273],{"type":67,"value":4274},"Literal",{"type":67,"value":4276}," enums) are enforced in the trigger form. Custom ",{"type":61,"tag":91,"props":4278,"children":4280},{"className":4279},[],[4281],{"type":67,"value":4282},"@field_validator",{"type":67,"value":1262},{"type":61,"tag":91,"props":4285,"children":4287},{"className":4286},[],[4288],{"type":67,"value":4289},"@model_validator",{"type":67,"value":4291}," logic does ",{"type":61,"tag":83,"props":4293,"children":4294},{},[4295],{"type":67,"value":703},{"type":67,"value":4297}," map to JSON Schema, so it runs only at build time and inside ",{"type":61,"tag":91,"props":4299,"children":4301},{"className":4300},[],[4302],{"type":67,"value":4303},"resolve_config()",{"type":67,"value":4305}," — not in the form. If custom validators enforce important constraints, call ",{"type":61,"tag":91,"props":4307,"children":4309},{"className":4308},[],[4310],{"type":67,"value":3730},{"type":67,"value":4312}," in your ",{"type":61,"tag":91,"props":4314,"children":4316},{"className":4315},[],[4317],{"type":67,"value":3893},{"type":67,"value":4319}," function so they run on overridden values.",{"type":61,"tag":424,"props":4321,"children":4323},{"id":4322},"triggering-with-overrides",[4324],{"type":67,"value":4325},"Triggering with Overrides",{"type":61,"tag":70,"props":4327,"children":4328},{},[4329,4331,4337],{"type":67,"value":4330},"Override params via the Airflow UI trigger form, or via the API using ",{"type":61,"tag":91,"props":4332,"children":4334},{"className":4333},[],[4335],{"type":67,"value":4336},"conf",{"type":67,"value":4338}," with the namespaced names:",{"type":61,"tag":431,"props":4340,"children":4342},{"className":433,"code":4341,"language":435,"meta":436,"style":436},"curl -X POST \u002Fapi\u002Fv2\u002Fdags\u002Fcustomer_pipeline\u002FdagRuns \\\n  -d '{\"conf\": {\"load__target_table\": \"staging.customers\", \"load__mode\": \"append\"}}'\n",[4343],{"type":61,"tag":91,"props":4344,"children":4345},{"__ignoreMap":436},[4346,4374],{"type":61,"tag":442,"props":4347,"children":4348},{"class":444,"line":445},[4349,4354,4359,4364,4369],{"type":61,"tag":442,"props":4350,"children":4351},{"style":459},[4352],{"type":67,"value":4353},"curl",{"type":61,"tag":442,"props":4355,"children":4356},{"style":470},[4357],{"type":67,"value":4358}," -X",{"type":61,"tag":442,"props":4360,"children":4361},{"style":470},[4362],{"type":67,"value":4363}," POST",{"type":61,"tag":442,"props":4365,"children":4366},{"style":470},[4367],{"type":67,"value":4368}," \u002Fapi\u002Fv2\u002Fdags\u002Fcustomer_pipeline\u002FdagRuns",{"type":61,"tag":442,"props":4370,"children":4371},{"style":464},[4372],{"type":67,"value":4373}," \\\n",{"type":61,"tag":442,"props":4375,"children":4376},{"class":444,"line":455},[4377,4382,4387,4392],{"type":61,"tag":442,"props":4378,"children":4379},{"style":470},[4380],{"type":67,"value":4381},"  -d",{"type":61,"tag":442,"props":4383,"children":4384},{"style":1676},[4385],{"type":67,"value":4386}," '",{"type":61,"tag":442,"props":4388,"children":4389},{"style":470},[4390],{"type":67,"value":4391},"{\"conf\": {\"load__target_table\": \"staging.customers\", \"load__mode\": \"append\"}}",{"type":61,"tag":442,"props":4393,"children":4394},{"style":1676},[4395],{"type":67,"value":4396},"'\n",{"type":61,"tag":168,"props":4398,"children":4399},{},[],{"type":61,"tag":121,"props":4401,"children":4403},{"id":4402},"post-build-callbacks",[4404],{"type":67,"value":324},{"type":61,"tag":70,"props":4406,"children":4407},{},[4408,4409,4415],{"type":67,"value":573},{"type":61,"tag":91,"props":4410,"children":4412},{"className":4411},[],[4413],{"type":67,"value":4414},"on_dag_built",{"type":67,"value":4416}," to post-process DAGs after they are constructed. This is useful for adding tags, access controls, audit metadata, or any cross-cutting concern.",{"type":61,"tag":431,"props":4418,"children":4420},{"className":533,"code":4419,"language":535,"meta":436,"style":436},"from pathlib import Path\nfrom blueprint import build_all_dags\n\ndef add_audit_tags(dag, yaml_path: Path) -> None:\n    dag.tags.append(\"managed-by-blueprint\")\n    dag.tags.append(f\"source:{yaml_path.name}\")\n\nbuild_all_dags(on_dag_built=add_audit_tags)\n",[4421],{"type":61,"tag":91,"props":4422,"children":4423},{"__ignoreMap":436},[4424,4432,4439,4446,4454,4462,4470,4477],{"type":61,"tag":442,"props":4425,"children":4426},{"class":444,"line":445},[4427],{"type":61,"tag":442,"props":4428,"children":4429},{},[4430],{"type":67,"value":4431},"from pathlib import Path\n",{"type":61,"tag":442,"props":4433,"children":4434},{"class":444,"line":455},[4435],{"type":61,"tag":442,"props":4436,"children":4437},{},[4438],{"type":67,"value":547},{"type":61,"tag":442,"props":4440,"children":4441},{"class":444,"line":476},[4442],{"type":61,"tag":442,"props":4443,"children":4444},{"emptyLinePlaceholder":480},[4445],{"type":67,"value":483},{"type":61,"tag":442,"props":4447,"children":4448},{"class":444,"line":486},[4449],{"type":61,"tag":442,"props":4450,"children":4451},{},[4452],{"type":67,"value":4453},"def add_audit_tags(dag, yaml_path: Path) -> None:\n",{"type":61,"tag":442,"props":4455,"children":4456},{"class":444,"line":495},[4457],{"type":61,"tag":442,"props":4458,"children":4459},{},[4460],{"type":67,"value":4461},"    dag.tags.append(\"managed-by-blueprint\")\n",{"type":61,"tag":442,"props":4463,"children":4464},{"class":444,"line":862},[4465],{"type":61,"tag":442,"props":4466,"children":4467},{},[4468],{"type":67,"value":4469},"    dag.tags.append(f\"source:{yaml_path.name}\")\n",{"type":61,"tag":442,"props":4471,"children":4472},{"class":444,"line":871},[4473],{"type":61,"tag":442,"props":4474,"children":4475},{"emptyLinePlaceholder":480},[4476],{"type":67,"value":483},{"type":61,"tag":442,"props":4478,"children":4479},{"class":444,"line":880},[4480],{"type":61,"tag":442,"props":4481,"children":4482},{},[4483],{"type":67,"value":4484},"build_all_dags(on_dag_built=add_audit_tags)\n",{"type":61,"tag":70,"props":4486,"children":4487},{},[4488],{"type":67,"value":4489},"The callback receives:",{"type":61,"tag":1183,"props":4491,"children":4492},{},[4493,4511],{"type":61,"tag":137,"props":4494,"children":4495},{},[4496,4501,4503,4509],{"type":61,"tag":91,"props":4497,"children":4499},{"className":4498},[],[4500],{"type":67,"value":38},{"type":67,"value":4502}," — the constructed Airflow ",{"type":61,"tag":91,"props":4504,"children":4506},{"className":4505},[],[4507],{"type":67,"value":4508},"DAG",{"type":67,"value":4510}," object (mutable)",{"type":61,"tag":137,"props":4512,"children":4513},{},[4514,4520,4522,4528],{"type":61,"tag":91,"props":4515,"children":4517},{"className":4516},[],[4518],{"type":67,"value":4519},"yaml_path",{"type":67,"value":4521}," — the ",{"type":61,"tag":91,"props":4523,"children":4525},{"className":4524},[],[4526],{"type":67,"value":4527},"Path",{"type":67,"value":4529}," to the YAML file that defined the DAG",{"type":61,"tag":168,"props":4531,"children":4532},{},[],{"type":61,"tag":121,"props":4534,"children":4536},{"id":4535},"validation-commands",[4537],{"type":67,"value":341},{"type":61,"tag":70,"props":4539,"children":4540},{},[4541],{"type":67,"value":4542},"Run CLI commands with uvx:",{"type":61,"tag":431,"props":4544,"children":4546},{"className":433,"code":4545,"language":435,"meta":436,"style":436},"uvx --from airflow-blueprint blueprint \u003Ccommand>\n",[4547],{"type":61,"tag":91,"props":4548,"children":4549},{"__ignoreMap":436},[4550],{"type":61,"tag":442,"props":4551,"children":4552},{"class":444,"line":445},[4553,4557,4561,4565,4569,4574,4579,4584],{"type":61,"tag":442,"props":4554,"children":4555},{"style":459},[4556],{"type":67,"value":652},{"type":61,"tag":442,"props":4558,"children":4559},{"style":470},[4560],{"type":67,"value":657},{"type":61,"tag":442,"props":4562,"children":4563},{"style":470},[4564],{"type":67,"value":662},{"type":61,"tag":442,"props":4566,"children":4567},{"style":470},[4568],{"type":67,"value":667},{"type":61,"tag":442,"props":4570,"children":4571},{"style":1676},[4572],{"type":67,"value":4573}," \u003C",{"type":61,"tag":442,"props":4575,"children":4576},{"style":470},[4577],{"type":67,"value":4578},"comman",{"type":61,"tag":442,"props":4580,"children":4581},{"style":464},[4582],{"type":67,"value":4583},"d",{"type":61,"tag":442,"props":4585,"children":4586},{"style":1676},[4587],{"type":67,"value":4588},">\n",{"type":61,"tag":178,"props":4590,"children":4591},{},[4592,4607],{"type":61,"tag":182,"props":4593,"children":4594},{},[4595],{"type":61,"tag":186,"props":4596,"children":4597},{},[4598,4603],{"type":61,"tag":190,"props":4599,"children":4600},{},[4601],{"type":67,"value":4602},"Command",{"type":61,"tag":190,"props":4604,"children":4605},{},[4606],{"type":67,"value":3186},{"type":61,"tag":201,"props":4608,"children":4609},{},[4610,4627,4644,4661,4686,4703,4720,4737],{"type":61,"tag":186,"props":4611,"children":4612},{},[4613,4622],{"type":61,"tag":208,"props":4614,"children":4615},{},[4616],{"type":61,"tag":91,"props":4617,"children":4619},{"className":4618},[],[4620],{"type":67,"value":4621},"blueprint list",{"type":61,"tag":208,"props":4623,"children":4624},{},[4625],{"type":67,"value":4626},"Show available blueprints",{"type":61,"tag":186,"props":4628,"children":4629},{},[4630,4639],{"type":61,"tag":208,"props":4631,"children":4632},{},[4633],{"type":61,"tag":91,"props":4634,"children":4636},{"className":4635},[],[4637],{"type":67,"value":4638},"blueprint describe \u003Cname>",{"type":61,"tag":208,"props":4640,"children":4641},{},[4642],{"type":67,"value":4643},"Show config schema for a blueprint",{"type":61,"tag":186,"props":4645,"children":4646},{},[4647,4656],{"type":61,"tag":208,"props":4648,"children":4649},{},[4650],{"type":61,"tag":91,"props":4651,"children":4653},{"className":4652},[],[4654],{"type":67,"value":4655},"blueprint describe \u003Cname> -v N",{"type":61,"tag":208,"props":4657,"children":4658},{},[4659],{"type":67,"value":4660},"Show schema for specific version",{"type":61,"tag":186,"props":4662,"children":4663},{},[4664,4673],{"type":61,"tag":208,"props":4665,"children":4666},{},[4667],{"type":61,"tag":91,"props":4668,"children":4670},{"className":4669},[],[4671],{"type":67,"value":4672},"blueprint lint",{"type":61,"tag":208,"props":4674,"children":4675},{},[4676,4678,4684],{"type":67,"value":4677},"Validate all ",{"type":61,"tag":91,"props":4679,"children":4681},{"className":4680},[],[4682],{"type":67,"value":4683},"*.dag.yaml",{"type":67,"value":4685}," files",{"type":61,"tag":186,"props":4687,"children":4688},{},[4689,4698],{"type":61,"tag":208,"props":4690,"children":4691},{},[4692],{"type":61,"tag":91,"props":4693,"children":4695},{"className":4694},[],[4696],{"type":67,"value":4697},"blueprint lint \u003Cpath>",{"type":61,"tag":208,"props":4699,"children":4700},{},[4701],{"type":67,"value":4702},"Validate specific file",{"type":61,"tag":186,"props":4704,"children":4705},{},[4706,4715],{"type":61,"tag":208,"props":4707,"children":4708},{},[4709],{"type":61,"tag":91,"props":4710,"children":4712},{"className":4711},[],[4713],{"type":67,"value":4714},"blueprint schema \u003Cname>",{"type":61,"tag":208,"props":4716,"children":4717},{},[4718],{"type":67,"value":4719},"Generate JSON schema for a blueprint (step template)",{"type":61,"tag":186,"props":4721,"children":4722},{},[4723,4732],{"type":61,"tag":208,"props":4724,"children":4725},{},[4726],{"type":61,"tag":91,"props":4727,"children":4729},{"className":4728},[],[4730],{"type":67,"value":4731},"blueprint schema --dag-args",{"type":61,"tag":208,"props":4733,"children":4734},{},[4735],{"type":67,"value":4736},"Generate JSON schema for DAG-level YAML fields",{"type":61,"tag":186,"props":4738,"children":4739},{},[4740,4749],{"type":61,"tag":208,"props":4741,"children":4742},{},[4743],{"type":61,"tag":91,"props":4744,"children":4746},{"className":4745},[],[4747],{"type":67,"value":4748},"blueprint new",{"type":61,"tag":208,"props":4750,"children":4751},{},[4752],{"type":67,"value":4753},"Interactive DAG YAML creation",{"type":61,"tag":424,"props":4755,"children":4757},{"id":4756},"validation-workflow",[4758],{"type":67,"value":4759},"Validation Workflow",{"type":61,"tag":431,"props":4761,"children":4763},{"className":433,"code":4762,"language":435,"meta":436,"style":436},"# Check all YAML files\nuvx --from airflow-blueprint blueprint lint\n\n# Expected output for valid files:\n# PASS customer_pipeline.dag.yaml (dag_id=customer_pipeline)\n",[4764],{"type":61,"tag":91,"props":4765,"children":4766},{"__ignoreMap":436},[4767,4775,4799,4806,4814],{"type":61,"tag":442,"props":4768,"children":4769},{"class":444,"line":445},[4770],{"type":61,"tag":442,"props":4771,"children":4772},{"style":449},[4773],{"type":67,"value":4774},"# Check all YAML files\n",{"type":61,"tag":442,"props":4776,"children":4777},{"class":444,"line":455},[4778,4782,4786,4790,4794],{"type":61,"tag":442,"props":4779,"children":4780},{"style":459},[4781],{"type":67,"value":652},{"type":61,"tag":442,"props":4783,"children":4784},{"style":470},[4785],{"type":67,"value":657},{"type":61,"tag":442,"props":4787,"children":4788},{"style":470},[4789],{"type":67,"value":662},{"type":61,"tag":442,"props":4791,"children":4792},{"style":470},[4793],{"type":67,"value":667},{"type":61,"tag":442,"props":4795,"children":4796},{"style":470},[4797],{"type":67,"value":4798}," lint\n",{"type":61,"tag":442,"props":4800,"children":4801},{"class":444,"line":476},[4802],{"type":61,"tag":442,"props":4803,"children":4804},{"emptyLinePlaceholder":480},[4805],{"type":67,"value":483},{"type":61,"tag":442,"props":4807,"children":4808},{"class":444,"line":486},[4809],{"type":61,"tag":442,"props":4810,"children":4811},{"style":449},[4812],{"type":67,"value":4813},"# Expected output for valid files:\n",{"type":61,"tag":442,"props":4815,"children":4816},{"class":444,"line":495},[4817],{"type":61,"tag":442,"props":4818,"children":4819},{"style":449},[4820],{"type":67,"value":4821},"# PASS customer_pipeline.dag.yaml (dag_id=customer_pipeline)\n",{"type":61,"tag":168,"props":4823,"children":4824},{},[],{"type":61,"tag":121,"props":4826,"children":4828},{"id":4827},"versioning",[4829],{"type":67,"value":375},{"type":61,"tag":70,"props":4831,"children":4832},{},[4833],{"type":67,"value":4834},"When user needs to version blueprints for backwards compatibility:",{"type":61,"tag":424,"props":4836,"children":4838},{"id":4837},"version-naming-convention",[4839],{"type":67,"value":4840},"Version Naming Convention",{"type":61,"tag":1183,"props":4842,"children":4843},{},[4844,4857,4868],{"type":61,"tag":137,"props":4845,"children":4846},{},[4847,4849,4855],{"type":67,"value":4848},"v1: ",{"type":61,"tag":91,"props":4850,"children":4852},{"className":4851},[],[4853],{"type":67,"value":4854},"MyBlueprint",{"type":67,"value":4856}," (no suffix)",{"type":61,"tag":137,"props":4858,"children":4859},{},[4860,4862],{"type":67,"value":4861},"v2: ",{"type":61,"tag":91,"props":4863,"children":4865},{"className":4864},[],[4866],{"type":67,"value":4867},"MyBlueprintV2",{"type":61,"tag":137,"props":4869,"children":4870},{},[4871,4873],{"type":67,"value":4872},"v3: ",{"type":61,"tag":91,"props":4874,"children":4876},{"className":4875},[],[4877],{"type":67,"value":4878},"MyBlueprintV3",{"type":61,"tag":431,"props":4880,"children":4882},{"className":533,"code":4881,"language":535,"meta":436,"style":436},"# v1 - original\nclass ExtractConfig(BaseModel):\n    source_table: str\n\nclass Extract(Blueprint[ExtractConfig]):\n    def render(self, config): ...\n\n# v2 - breaking changes, new class\nclass ExtractV2Config(BaseModel):\n    sources: list[dict]  # Different schema\n\nclass ExtractV2(Blueprint[ExtractV2Config]):\n    def render(self, config): ...\n",[4883],{"type":61,"tag":91,"props":4884,"children":4885},{"__ignoreMap":436},[4886,4894,4901,4908,4915,4922,4930,4937,4945,4953,4961,4968,4976],{"type":61,"tag":442,"props":4887,"children":4888},{"class":444,"line":445},[4889],{"type":61,"tag":442,"props":4890,"children":4891},{},[4892],{"type":67,"value":4893},"# v1 - original\n",{"type":61,"tag":442,"props":4895,"children":4896},{"class":444,"line":455},[4897],{"type":61,"tag":442,"props":4898,"children":4899},{},[4900],{"type":67,"value":1356},{"type":61,"tag":442,"props":4902,"children":4903},{"class":444,"line":476},[4904],{"type":61,"tag":442,"props":4905,"children":4906},{},[4907],{"type":67,"value":1364},{"type":61,"tag":442,"props":4909,"children":4910},{"class":444,"line":486},[4911],{"type":61,"tag":442,"props":4912,"children":4913},{"emptyLinePlaceholder":480},[4914],{"type":67,"value":483},{"type":61,"tag":442,"props":4916,"children":4917},{"class":444,"line":495},[4918],{"type":61,"tag":442,"props":4919,"children":4920},{},[4921],{"type":67,"value":3803},{"type":61,"tag":442,"props":4923,"children":4924},{"class":444,"line":862},[4925],{"type":61,"tag":442,"props":4926,"children":4927},{},[4928],{"type":67,"value":4929},"    def render(self, config): ...\n",{"type":61,"tag":442,"props":4931,"children":4932},{"class":444,"line":871},[4933],{"type":61,"tag":442,"props":4934,"children":4935},{"emptyLinePlaceholder":480},[4936],{"type":67,"value":483},{"type":61,"tag":442,"props":4938,"children":4939},{"class":444,"line":880},[4940],{"type":61,"tag":442,"props":4941,"children":4942},{},[4943],{"type":67,"value":4944},"# v2 - breaking changes, new class\n",{"type":61,"tag":442,"props":4946,"children":4947},{"class":444,"line":889},[4948],{"type":61,"tag":442,"props":4949,"children":4950},{},[4951],{"type":67,"value":4952},"class ExtractV2Config(BaseModel):\n",{"type":61,"tag":442,"props":4954,"children":4955},{"class":444,"line":898},[4956],{"type":61,"tag":442,"props":4957,"children":4958},{},[4959],{"type":67,"value":4960},"    sources: list[dict]  # Different schema\n",{"type":61,"tag":442,"props":4962,"children":4963},{"class":444,"line":907},[4964],{"type":61,"tag":442,"props":4965,"children":4966},{"emptyLinePlaceholder":480},[4967],{"type":67,"value":483},{"type":61,"tag":442,"props":4969,"children":4970},{"class":444,"line":915},[4971],{"type":61,"tag":442,"props":4972,"children":4973},{},[4974],{"type":67,"value":4975},"class ExtractV2(Blueprint[ExtractV2Config]):\n",{"type":61,"tag":442,"props":4977,"children":4978},{"class":444,"line":924},[4979],{"type":61,"tag":442,"props":4980,"children":4981},{},[4982],{"type":67,"value":4929},{"type":61,"tag":424,"props":4984,"children":4986},{"id":4985},"explicit-name-and-version",[4987],{"type":67,"value":4988},"Explicit Name and Version",{"type":61,"tag":70,"props":4990,"children":4991},{},[4992,4994,5000,5001,5006],{"type":67,"value":4993},"As an alternative to the class name convention, blueprints can set ",{"type":61,"tag":91,"props":4995,"children":4997},{"className":4996},[],[4998],{"type":67,"value":4999},"name",{"type":67,"value":1308},{"type":61,"tag":91,"props":5002,"children":5004},{"className":5003},[],[5005],{"type":67,"value":2166},{"type":67,"value":5007}," directly:",{"type":61,"tag":431,"props":5009,"children":5011},{"className":533,"code":5010,"language":535,"meta":436,"style":436},"class MyCustomExtractor(Blueprint[ExtractV3Config]):\n    name = \"extract\"\n    version = 3\n\n    def render(self, config): ...\n",[5012],{"type":61,"tag":91,"props":5013,"children":5014},{"__ignoreMap":436},[5015,5023,5031,5039,5046],{"type":61,"tag":442,"props":5016,"children":5017},{"class":444,"line":445},[5018],{"type":61,"tag":442,"props":5019,"children":5020},{},[5021],{"type":67,"value":5022},"class MyCustomExtractor(Blueprint[ExtractV3Config]):\n",{"type":61,"tag":442,"props":5024,"children":5025},{"class":444,"line":455},[5026],{"type":61,"tag":442,"props":5027,"children":5028},{},[5029],{"type":67,"value":5030},"    name = \"extract\"\n",{"type":61,"tag":442,"props":5032,"children":5033},{"class":444,"line":476},[5034],{"type":61,"tag":442,"props":5035,"children":5036},{},[5037],{"type":67,"value":5038},"    version = 3\n",{"type":61,"tag":442,"props":5040,"children":5041},{"class":444,"line":486},[5042],{"type":61,"tag":442,"props":5043,"children":5044},{"emptyLinePlaceholder":480},[5045],{"type":67,"value":483},{"type":61,"tag":442,"props":5047,"children":5048},{"class":444,"line":495},[5049],{"type":61,"tag":442,"props":5050,"children":5051},{},[5052],{"type":67,"value":4929},{"type":61,"tag":70,"props":5054,"children":5055},{},[5056,5058,5064],{"type":67,"value":5057},"This is useful when the class name doesn't follow the ",{"type":61,"tag":91,"props":5059,"children":5061},{"className":5060},[],[5062],{"type":67,"value":5063},"NameV{N}",{"type":67,"value":5065}," convention or when you want clearer control.",{"type":61,"tag":424,"props":5067,"children":5069},{"id":5068},"using-versions-in-yaml",[5070],{"type":67,"value":5071},"Using Versions in YAML",{"type":61,"tag":431,"props":5073,"children":5075},{"className":1658,"code":5074,"language":1660,"meta":436,"style":436},"steps:\n  # Pin to v1\n  legacy_extract:\n    blueprint: extract\n    version: 1\n    source_table: raw.data\n\n  # Use latest (v2)\n  new_extract:\n    blueprint: extract\n    sources: [{table: orders}]\n",[5076],{"type":61,"tag":91,"props":5077,"children":5078},{"__ignoreMap":436},[5079,5090,5098,5110,5125,5142,5157,5164,5172,5184,5199],{"type":61,"tag":442,"props":5080,"children":5081},{"class":444,"line":445},[5082,5086],{"type":61,"tag":442,"props":5083,"children":5084},{"style":1670},[5085],{"type":67,"value":1673},{"type":61,"tag":442,"props":5087,"children":5088},{"style":1676},[5089],{"type":67,"value":1679},{"type":61,"tag":442,"props":5091,"children":5092},{"class":444,"line":455},[5093],{"type":61,"tag":442,"props":5094,"children":5095},{"style":449},[5096],{"type":67,"value":5097},"  # Pin to v1\n",{"type":61,"tag":442,"props":5099,"children":5100},{"class":444,"line":476},[5101,5106],{"type":61,"tag":442,"props":5102,"children":5103},{"style":1670},[5104],{"type":67,"value":5105},"  legacy_extract",{"type":61,"tag":442,"props":5107,"children":5108},{"style":1676},[5109],{"type":67,"value":1679},{"type":61,"tag":442,"props":5111,"children":5112},{"class":444,"line":486},[5113,5117,5121],{"type":61,"tag":442,"props":5114,"children":5115},{"style":1670},[5116],{"type":67,"value":1699},{"type":61,"tag":442,"props":5118,"children":5119},{"style":1676},[5120],{"type":67,"value":530},{"type":61,"tag":442,"props":5122,"children":5123},{"style":470},[5124],{"type":67,"value":2541},{"type":61,"tag":442,"props":5126,"children":5127},{"class":444,"line":495},[5128,5133,5137],{"type":61,"tag":442,"props":5129,"children":5130},{"style":1670},[5131],{"type":67,"value":5132},"    version",{"type":61,"tag":442,"props":5134,"children":5135},{"style":1676},[5136],{"type":67,"value":530},{"type":61,"tag":442,"props":5138,"children":5139},{"style":1960},[5140],{"type":67,"value":5141}," 1\n",{"type":61,"tag":442,"props":5143,"children":5144},{"class":444,"line":862},[5145,5149,5153],{"type":61,"tag":442,"props":5146,"children":5147},{"style":1670},[5148],{"type":67,"value":1936},{"type":61,"tag":442,"props":5150,"children":5151},{"style":1676},[5152],{"type":67,"value":530},{"type":61,"tag":442,"props":5154,"children":5155},{"style":470},[5156],{"type":67,"value":3588},{"type":61,"tag":442,"props":5158,"children":5159},{"class":444,"line":871},[5160],{"type":61,"tag":442,"props":5161,"children":5162},{"emptyLinePlaceholder":480},[5163],{"type":67,"value":483},{"type":61,"tag":442,"props":5165,"children":5166},{"class":444,"line":880},[5167],{"type":61,"tag":442,"props":5168,"children":5169},{"style":449},[5170],{"type":67,"value":5171},"  # Use latest (v2)\n",{"type":61,"tag":442,"props":5173,"children":5174},{"class":444,"line":889},[5175,5180],{"type":61,"tag":442,"props":5176,"children":5177},{"style":1670},[5178],{"type":67,"value":5179},"  new_extract",{"type":61,"tag":442,"props":5181,"children":5182},{"style":1676},[5183],{"type":67,"value":1679},{"type":61,"tag":442,"props":5185,"children":5186},{"class":444,"line":898},[5187,5191,5195],{"type":61,"tag":442,"props":5188,"children":5189},{"style":1670},[5190],{"type":67,"value":1699},{"type":61,"tag":442,"props":5192,"children":5193},{"style":1676},[5194],{"type":67,"value":530},{"type":61,"tag":442,"props":5196,"children":5197},{"style":470},[5198],{"type":67,"value":2541},{"type":61,"tag":442,"props":5200,"children":5201},{"class":444,"line":907},[5202,5207,5211,5216,5220,5224,5229],{"type":61,"tag":442,"props":5203,"children":5204},{"style":1670},[5205],{"type":67,"value":5206},"    sources",{"type":61,"tag":442,"props":5208,"children":5209},{"style":1676},[5210],{"type":67,"value":530},{"type":61,"tag":442,"props":5212,"children":5213},{"style":1676},[5214],{"type":67,"value":5215}," [{",{"type":61,"tag":442,"props":5217,"children":5218},{"style":1670},[5219],{"type":67,"value":178},{"type":61,"tag":442,"props":5221,"children":5222},{"style":1676},[5223],{"type":67,"value":530},{"type":61,"tag":442,"props":5225,"children":5226},{"style":470},[5227],{"type":67,"value":5228}," orders",{"type":61,"tag":442,"props":5230,"children":5231},{"style":1676},[5232],{"type":67,"value":5233},"}]\n",{"type":61,"tag":424,"props":5235,"children":5237},{"id":5236},"version-rules",[5238],{"type":67,"value":5239},"Version Rules",{"type":61,"tag":1183,"props":5241,"children":5242},{},[5243,5264],{"type":61,"tag":137,"props":5244,"children":5245},{},[5246,5248,5254,5256,5262],{"type":67,"value":5247},"A blueprint's discovered versions must form a contiguous ",{"type":61,"tag":91,"props":5249,"children":5251},{"className":5250},[],[5252],{"type":67,"value":5253},"1..N",{"type":67,"value":5255}," sequence. A gap (e.g. v1 and v3 with no v2) raises ",{"type":61,"tag":91,"props":5257,"children":5259},{"className":5258},[],[5260],{"type":67,"value":5261},"NonContiguousVersionError",{"type":67,"value":5263}," during discovery.",{"type":61,"tag":137,"props":5265,"children":5266},{},[5267,5269,5275],{"type":67,"value":5268},"Pinning a version that doesn't exist in YAML raises ",{"type":61,"tag":91,"props":5270,"children":5272},{"className":5271},[],[5273],{"type":67,"value":5274},"InvalidVersionError",{"type":67,"value":589},{"type":61,"tag":168,"props":5277,"children":5278},{},[],{"type":61,"tag":121,"props":5280,"children":5282},{"id":5281},"schema-generation",[5283],{"type":67,"value":392},{"type":61,"tag":70,"props":5285,"children":5286},{},[5287],{"type":67,"value":5288},"Generate JSON schemas for editor autocompletion or external tooling:",{"type":61,"tag":431,"props":5290,"children":5292},{"className":433,"code":5291,"language":435,"meta":436,"style":436},"# Generate schema for a blueprint (step template)\nuvx --from airflow-blueprint blueprint schema extract -o extract.schema.json\n\n# Generate schema for DAG-level YAML fields (dag_id, steps, + custom BlueprintDagArgs fields)\nuvx --from airflow-blueprint blueprint schema --dag-args -o dag-args.schema.json\n",[5293],{"type":61,"tag":91,"props":5294,"children":5295},{"__ignoreMap":436},[5296,5304,5343,5350,5358],{"type":61,"tag":442,"props":5297,"children":5298},{"class":444,"line":445},[5299],{"type":61,"tag":442,"props":5300,"children":5301},{"style":449},[5302],{"type":67,"value":5303},"# Generate schema for a blueprint (step template)\n",{"type":61,"tag":442,"props":5305,"children":5306},{"class":444,"line":455},[5307,5311,5315,5319,5323,5328,5333,5338],{"type":61,"tag":442,"props":5308,"children":5309},{"style":459},[5310],{"type":67,"value":652},{"type":61,"tag":442,"props":5312,"children":5313},{"style":470},[5314],{"type":67,"value":657},{"type":61,"tag":442,"props":5316,"children":5317},{"style":470},[5318],{"type":67,"value":662},{"type":61,"tag":442,"props":5320,"children":5321},{"style":470},[5322],{"type":67,"value":667},{"type":61,"tag":442,"props":5324,"children":5325},{"style":470},[5326],{"type":67,"value":5327}," schema",{"type":61,"tag":442,"props":5329,"children":5330},{"style":470},[5331],{"type":67,"value":5332}," extract",{"type":61,"tag":442,"props":5334,"children":5335},{"style":470},[5336],{"type":67,"value":5337}," -o",{"type":61,"tag":442,"props":5339,"children":5340},{"style":470},[5341],{"type":67,"value":5342}," extract.schema.json\n",{"type":61,"tag":442,"props":5344,"children":5345},{"class":444,"line":476},[5346],{"type":61,"tag":442,"props":5347,"children":5348},{"emptyLinePlaceholder":480},[5349],{"type":67,"value":483},{"type":61,"tag":442,"props":5351,"children":5352},{"class":444,"line":486},[5353],{"type":61,"tag":442,"props":5354,"children":5355},{"style":449},[5356],{"type":67,"value":5357},"# Generate schema for DAG-level YAML fields (dag_id, steps, + custom BlueprintDagArgs fields)\n",{"type":61,"tag":442,"props":5359,"children":5360},{"class":444,"line":495},[5361,5365,5369,5373,5377,5381,5386,5390],{"type":61,"tag":442,"props":5362,"children":5363},{"style":459},[5364],{"type":67,"value":652},{"type":61,"tag":442,"props":5366,"children":5367},{"style":470},[5368],{"type":67,"value":657},{"type":61,"tag":442,"props":5370,"children":5371},{"style":470},[5372],{"type":67,"value":662},{"type":61,"tag":442,"props":5374,"children":5375},{"style":470},[5376],{"type":67,"value":667},{"type":61,"tag":442,"props":5378,"children":5379},{"style":470},[5380],{"type":67,"value":5327},{"type":61,"tag":442,"props":5382,"children":5383},{"style":470},[5384],{"type":67,"value":5385}," --dag-args",{"type":61,"tag":442,"props":5387,"children":5388},{"style":470},[5389],{"type":67,"value":5337},{"type":61,"tag":442,"props":5391,"children":5392},{"style":470},[5393],{"type":67,"value":5394}," dag-args.schema.json\n",{"type":61,"tag":70,"props":5396,"children":5397},{},[5398,5399,5405,5407,5412,5414,5419,5420,5425,5427,5432],{"type":67,"value":573},{"type":61,"tag":91,"props":5400,"children":5402},{"className":5401},[],[5403],{"type":67,"value":5404},"--dag-args",{"type":67,"value":5406}," (with no blueprint name) to generate the schema for ",{"type":61,"tag":83,"props":5408,"children":5409},{},[5410],{"type":67,"value":5411},"DAG-level",{"type":67,"value":5413}," YAML fields — ",{"type":61,"tag":91,"props":5415,"children":5417},{"className":5416},[],[5418],{"type":67,"value":1823},{"type":67,"value":1203},{"type":61,"tag":91,"props":5421,"children":5423},{"className":5422},[],[5424],{"type":67,"value":1673},{"type":67,"value":5426},", and any fields your custom ",{"type":61,"tag":91,"props":5428,"children":5430},{"className":5429},[],[5431],{"type":67,"value":624},{"type":67,"value":5433}," exposes — rather than a single step template's config.",{"type":61,"tag":70,"props":5435,"children":5436},{},[5437,5439,5445,5447,5453,5455,5461,5463,5469,5471,5476],{"type":67,"value":5438},"As of 0.3.0, each emitted schema includes a top-level ",{"type":61,"tag":91,"props":5440,"children":5442},{"className":5441},[],[5443],{"type":67,"value":5444},"templateType",{"type":67,"value":5446}," field — ",{"type":61,"tag":91,"props":5448,"children":5450},{"className":5449},[],[5451],{"type":67,"value":5452},"\"blueprint\"",{"type":67,"value":5454}," for a step template, ",{"type":61,"tag":91,"props":5456,"children":5458},{"className":5457},[],[5459],{"type":67,"value":5460},"\"dag_args\"",{"type":67,"value":5462}," for DAG-level fields — so consumers can tell them apart. The command emits raw JSON when piped or written via ",{"type":61,"tag":91,"props":5464,"children":5466},{"className":5465},[],[5467],{"type":67,"value":5468},"-o\u002F--output",{"type":67,"value":5470}," (and pretty, syntax-highlighted JSON when run interactively), so ",{"type":61,"tag":91,"props":5472,"children":5474},{"className":5473},[],[5475],{"type":67,"value":467},{"type":67,"value":5477}," redirection produces valid JSON.",{"type":61,"tag":424,"props":5479,"children":5481},{"id":5480},"astro-project-auto-detection",[5482],{"type":67,"value":5483},"Astro Project Auto-Detection",{"type":61,"tag":70,"props":5485,"children":5486},{},[5487,5489,5494,5496,5502,5504,5510],{"type":67,"value":5488},"After creating or modifying a blueprint, ",{"type":61,"tag":83,"props":5490,"children":5491},{},[5492],{"type":67,"value":5493},"automatically check",{"type":67,"value":5495}," if the project is an Astro project by looking for a ",{"type":61,"tag":91,"props":5497,"children":5499},{"className":5498},[],[5500],{"type":67,"value":5501},".astro\u002F",{"type":67,"value":5503}," directory (created by ",{"type":61,"tag":91,"props":5505,"children":5507},{"className":5506},[],[5508],{"type":67,"value":5509},"astro dev init",{"type":67,"value":5511},").",{"type":61,"tag":70,"props":5513,"children":5514},{},[5515,5517,5522],{"type":67,"value":5516},"If the project is an Astro project, ",{"type":61,"tag":83,"props":5518,"children":5519},{},[5520],{"type":67,"value":5521},"automatically regenerate schemas",{"type":67,"value":5523}," without prompting:",{"type":61,"tag":431,"props":5525,"children":5527},{"className":433,"code":5526,"language":435,"meta":436,"style":436},"mkdir -p blueprint\u002Fgenerated-schemas\n# For each name from `blueprint list`:\n#   uvx --from airflow-blueprint blueprint schema NAME -o blueprint\u002Fgenerated-schemas\u002FNAME.schema.json\n# Also emit the DAG-level args schema:\n#   uvx --from airflow-blueprint blueprint schema --dag-args -o blueprint\u002Fgenerated-schemas\u002Fdag-args.schema.json\n",[5528],{"type":61,"tag":91,"props":5529,"children":5530},{"__ignoreMap":436},[5531,5549,5557,5565,5573],{"type":61,"tag":442,"props":5532,"children":5533},{"class":444,"line":445},[5534,5539,5544],{"type":61,"tag":442,"props":5535,"children":5536},{"style":459},[5537],{"type":67,"value":5538},"mkdir",{"type":61,"tag":442,"props":5540,"children":5541},{"style":470},[5542],{"type":67,"value":5543}," -p",{"type":61,"tag":442,"props":5545,"children":5546},{"style":470},[5547],{"type":67,"value":5548}," blueprint\u002Fgenerated-schemas\n",{"type":61,"tag":442,"props":5550,"children":5551},{"class":444,"line":455},[5552],{"type":61,"tag":442,"props":5553,"children":5554},{"style":449},[5555],{"type":67,"value":5556},"# For each name from `blueprint list`:\n",{"type":61,"tag":442,"props":5558,"children":5559},{"class":444,"line":476},[5560],{"type":61,"tag":442,"props":5561,"children":5562},{"style":449},[5563],{"type":67,"value":5564},"#   uvx --from airflow-blueprint blueprint schema NAME -o blueprint\u002Fgenerated-schemas\u002FNAME.schema.json\n",{"type":61,"tag":442,"props":5566,"children":5567},{"class":444,"line":486},[5568],{"type":61,"tag":442,"props":5569,"children":5570},{"style":449},[5571],{"type":67,"value":5572},"# Also emit the DAG-level args schema:\n",{"type":61,"tag":442,"props":5574,"children":5575},{"class":444,"line":495},[5576],{"type":61,"tag":442,"props":5577,"children":5578},{"style":449},[5579],{"type":67,"value":5580},"#   uvx --from airflow-blueprint blueprint schema --dag-args -o blueprint\u002Fgenerated-schemas\u002Fdag-args.schema.json\n",{"type":61,"tag":70,"props":5582,"children":5583},{},[5584,5586,5592],{"type":67,"value":5585},"The Astro IDE reads ",{"type":61,"tag":91,"props":5587,"children":5589},{"className":5588},[],[5590],{"type":67,"value":5591},"blueprint\u002Fgenerated-schemas\u002F",{"type":67,"value":5593}," to render configuration forms. Keeping schemas in sync ensures the visual builder always reflects the latest blueprint configs.",{"type":61,"tag":70,"props":5595,"children":5596},{},[5597],{"type":67,"value":5598},"If you cannot determine whether the project is an Astro project, ask the user once and remember for the rest of the session.",{"type":61,"tag":168,"props":5600,"children":5601},{},[],{"type":61,"tag":121,"props":5603,"children":5605},{"id":5604},"troubleshooting",[5606],{"type":67,"value":409},{"type":61,"tag":424,"props":5608,"children":5610},{"id":5609},"blueprint-not-found",[5611],{"type":67,"value":5612},"\"Blueprint not found\"",{"type":61,"tag":70,"props":5614,"children":5615},{},[5616,5621],{"type":61,"tag":83,"props":5617,"children":5618},{},[5619],{"type":67,"value":5620},"Cause",{"type":67,"value":5622},": Blueprint class not in Python path.",{"type":61,"tag":70,"props":5624,"children":5625},{},[5626,5631,5633,5639],{"type":61,"tag":83,"props":5627,"children":5628},{},[5629],{"type":67,"value":5630},"Fix",{"type":67,"value":5632},": Check template directory or use ",{"type":61,"tag":91,"props":5634,"children":5636},{"className":5635},[],[5637],{"type":67,"value":5638},"--template-dir",{"type":67,"value":530},{"type":61,"tag":431,"props":5641,"children":5643},{"className":433,"code":5642,"language":435,"meta":436,"style":436},"uvx --from airflow-blueprint blueprint list --template-dir dags\u002Ftemplates\u002F\n",[5644],{"type":61,"tag":91,"props":5645,"children":5646},{"__ignoreMap":436},[5647],{"type":61,"tag":442,"props":5648,"children":5649},{"class":444,"line":445},[5650,5654,5658,5662,5666,5670,5674],{"type":61,"tag":442,"props":5651,"children":5652},{"style":459},[5653],{"type":67,"value":652},{"type":61,"tag":442,"props":5655,"children":5656},{"style":470},[5657],{"type":67,"value":657},{"type":61,"tag":442,"props":5659,"children":5660},{"style":470},[5661],{"type":67,"value":662},{"type":61,"tag":442,"props":5663,"children":5664},{"style":470},[5665],{"type":67,"value":667},{"type":61,"tag":442,"props":5667,"children":5668},{"style":470},[5669],{"type":67,"value":784},{"type":61,"tag":442,"props":5671,"children":5672},{"style":470},[5673],{"type":67,"value":789},{"type":61,"tag":442,"props":5675,"children":5676},{"style":470},[5677],{"type":67,"value":5678}," dags\u002Ftemplates\u002F\n",{"type":61,"tag":424,"props":5680,"children":5682},{"id":5681},"extra-inputs-are-not-permitted",[5683],{"type":67,"value":5684},"\"Extra inputs are not permitted\"",{"type":61,"tag":70,"props":5686,"children":5687},{},[5688,5692,5694,5699],{"type":61,"tag":83,"props":5689,"children":5690},{},[5691],{"type":67,"value":5620},{"type":67,"value":5693},": YAML field name typo with ",{"type":61,"tag":91,"props":5695,"children":5697},{"className":5696},[],[5698],{"type":67,"value":1389},{"type":67,"value":5700}," enabled.",{"type":61,"tag":70,"props":5702,"children":5703},{},[5704,5708,5710,5716],{"type":61,"tag":83,"props":5705,"children":5706},{},[5707],{"type":67,"value":5630},{"type":67,"value":5709},": Run ",{"type":61,"tag":91,"props":5711,"children":5713},{"className":5712},[],[5714],{"type":67,"value":5715},"uvx --from airflow-blueprint blueprint describe \u003Cname>",{"type":67,"value":5717}," to see valid field names.",{"type":61,"tag":424,"props":5719,"children":5721},{"id":5720},"dag-not-appearing-in-airflow",[5722],{"type":67,"value":5723},"DAG not appearing in Airflow",{"type":61,"tag":70,"props":5725,"children":5726},{},[5727,5731,5733,5738],{"type":61,"tag":83,"props":5728,"children":5729},{},[5730],{"type":67,"value":5620},{"type":67,"value":5732},": Missing or broken loader — including a loader that imports the deprecated ",{"type":61,"tag":91,"props":5734,"children":5736},{"className":5735},[],[5737],{"type":67,"value":587},{"type":67,"value":5739},", which Airflow safe-mode may skip.",{"type":61,"tag":70,"props":5741,"children":5742},{},[5743,5747,5749,5754,5756,5762],{"type":61,"tag":83,"props":5744,"children":5745},{},[5746],{"type":67,"value":5630},{"type":67,"value":5748},": Ensure ",{"type":61,"tag":91,"props":5750,"children":5752},{"className":5751},[],[5753],{"type":67,"value":528},{"type":67,"value":5755}," exists and calls ",{"type":61,"tag":91,"props":5757,"children":5759},{"className":5758},[],[5760],{"type":67,"value":5761},"build_all_dags()",{"type":67,"value":530},{"type":61,"tag":431,"props":5764,"children":5766},{"className":533,"code":5765,"language":535,"meta":436,"style":436},"from blueprint import build_all_dags\nbuild_all_dags()\n",[5767],{"type":61,"tag":91,"props":5768,"children":5769},{"__ignoreMap":436},[5770,5777],{"type":61,"tag":442,"props":5771,"children":5772},{"class":444,"line":445},[5773],{"type":61,"tag":442,"props":5774,"children":5775},{},[5776],{"type":67,"value":547},{"type":61,"tag":442,"props":5778,"children":5779},{"class":444,"line":455},[5780],{"type":61,"tag":442,"props":5781,"children":5782},{},[5783],{"type":67,"value":562},{"type":61,"tag":424,"props":5785,"children":5787},{"id":5786},"modulenotfounderror-no-module-named-airflowprovidersx-from-blueprint-listlintschema",[5788,5790,5795,5796,5801,5802],{"type":67,"value":5789},"\"ModuleNotFoundError: No module named 'airflow.providers.X'\" from ",{"type":61,"tag":91,"props":5791,"children":5793},{"className":5792},[],[5794],{"type":67,"value":4621},{"type":67,"value":721},{"type":61,"tag":91,"props":5797,"children":5799},{"className":5798},[],[5800],{"type":67,"value":727},{"type":67,"value":721},{"type":61,"tag":91,"props":5803,"children":5805},{"className":5804},[],[5806],{"type":67,"value":734},{"type":61,"tag":70,"props":5808,"children":5809},{},[5810,5814,5816,5821],{"type":61,"tag":83,"props":5811,"children":5812},{},[5813],{"type":67,"value":5620},{"type":67,"value":5815},": The standalone ",{"type":61,"tag":91,"props":5817,"children":5819},{"className":5818},[],[5820],{"type":67,"value":696},{"type":67,"value":5822}," CLI environment doesn't include Airflow provider packages that your Astro Runtime project has. A template importing provider operators can't be imported by the CLI. This is the CLI's isolated environment, not your project.",{"type":61,"tag":70,"props":5824,"children":5825},{},[5826,5830,5832,5838,5840,5846,5847,5853,5854,5860],{"type":61,"tag":83,"props":5827,"children":5828},{},[5829],{"type":67,"value":5630},{"type":67,"value":5831},": Add ",{"type":61,"tag":91,"props":5833,"children":5835},{"className":5834},[],[5836],{"type":67,"value":5837},"--with apache-airflow-providers-X",{"type":67,"value":5839}," to the uvx invocation (common: ",{"type":61,"tag":91,"props":5841,"children":5843},{"className":5842},[],[5844],{"type":67,"value":5845},"apache-airflow-providers-standard",{"type":67,"value":1203},{"type":61,"tag":91,"props":5848,"children":5850},{"className":5849},[],[5851],{"type":67,"value":5852},"-google",{"type":67,"value":1203},{"type":61,"tag":91,"props":5855,"children":5857},{"className":5856},[],[5858],{"type":67,"value":5859},"-snowflake",{"type":67,"value":5861},"):",{"type":61,"tag":431,"props":5863,"children":5865},{"className":433,"code":5864,"language":435,"meta":436,"style":436},"uvx --from airflow-blueprint --with apache-airflow-providers-snowflake blueprint lint\n",[5866],{"type":61,"tag":91,"props":5867,"children":5868},{"__ignoreMap":436},[5869],{"type":61,"tag":442,"props":5870,"children":5871},{"class":444,"line":445},[5872,5876,5880,5884,5888,5893,5897],{"type":61,"tag":442,"props":5873,"children":5874},{"style":459},[5875],{"type":67,"value":652},{"type":61,"tag":442,"props":5877,"children":5878},{"style":470},[5879],{"type":67,"value":657},{"type":61,"tag":442,"props":5881,"children":5882},{"style":470},[5883],{"type":67,"value":662},{"type":61,"tag":442,"props":5885,"children":5886},{"style":470},[5887],{"type":67,"value":770},{"type":61,"tag":442,"props":5889,"children":5890},{"style":470},[5891],{"type":67,"value":5892}," apache-airflow-providers-snowflake",{"type":61,"tag":442,"props":5894,"children":5895},{"style":470},[5896],{"type":67,"value":667},{"type":61,"tag":442,"props":5898,"children":5899},{"style":470},[5900],{"type":67,"value":4798},{"type":61,"tag":424,"props":5902,"children":5904},{"id":5903},"validation-errors-shown-as-airflow-import-errors",[5905],{"type":67,"value":5906},"Validation errors shown as Airflow import errors",{"type":61,"tag":70,"props":5908,"children":5909},{},[5910,5912,5917,5918,5924],{"type":67,"value":5911},"As of v0.2.0, Pydantic validation errors are surfaced as Airflow import errors with actionable messages instead of being silently swallowed. The error message includes details on missing fields, unexpected fields, and type mismatches, along with guidance to run ",{"type":61,"tag":91,"props":5913,"children":5915},{"className":5914},[],[5916],{"type":67,"value":4672},{"type":67,"value":1093},{"type":61,"tag":91,"props":5919,"children":5921},{"className":5920},[],[5922],{"type":67,"value":5923},"blueprint describe",{"type":67,"value":589},{"type":61,"tag":424,"props":5926,"children":5928},{"id":5927},"cyclic-dependency-detected",[5929],{"type":67,"value":5930},"\"Cyclic dependency detected\"",{"type":61,"tag":70,"props":5932,"children":5933},{},[5934,5938,5940,5945],{"type":61,"tag":83,"props":5935,"children":5936},{},[5937],{"type":67,"value":5620},{"type":67,"value":5939},": Circular ",{"type":61,"tag":91,"props":5941,"children":5943},{"className":5942},[],[5944],{"type":67,"value":2149},{"type":67,"value":5946}," references.",{"type":61,"tag":70,"props":5948,"children":5949},{},[5950,5954],{"type":61,"tag":83,"props":5951,"children":5952},{},[5953],{"type":67,"value":5630},{"type":67,"value":5955},": Review step dependencies and remove cycles.",{"type":61,"tag":424,"props":5957,"children":5959},{"id":5958},"multipledagargserror",[5960],{"type":67,"value":5961},"\"MultipleDagArgsError\"",{"type":61,"tag":70,"props":5963,"children":5964},{},[5965,5969,5971,5976],{"type":61,"tag":83,"props":5966,"children":5967},{},[5968],{"type":67,"value":5620},{"type":67,"value":5970},": More than one ",{"type":61,"tag":91,"props":5972,"children":5974},{"className":5973},[],[5975],{"type":67,"value":624},{"type":67,"value":5977}," subclass discovered in the project.",{"type":61,"tag":70,"props":5979,"children":5980},{},[5981,5985,5987,5992],{"type":61,"tag":83,"props":5982,"children":5983},{},[5984],{"type":67,"value":5630},{"type":67,"value":5986},": Only one ",{"type":61,"tag":91,"props":5988,"children":5990},{"className":5989},[],[5991],{"type":67,"value":624},{"type":67,"value":5993}," subclass is allowed. Remove or merge duplicates.",{"type":61,"tag":424,"props":5995,"children":5997},{"id":5996},"noncontiguousversionerror-invalidversionerror",[5998],{"type":67,"value":5999},"\"NonContiguousVersionError\" \u002F \"InvalidVersionError\"",{"type":61,"tag":70,"props":6001,"children":6002},{},[6003,6007,6009,6014,6016,6021,6023,6028],{"type":61,"tag":83,"props":6004,"children":6005},{},[6006],{"type":67,"value":5620},{"type":67,"value":6008},": A blueprint's versions don't form a contiguous ",{"type":61,"tag":91,"props":6010,"children":6012},{"className":6011},[],[6013],{"type":67,"value":5253},{"type":67,"value":6015}," sequence (",{"type":61,"tag":91,"props":6017,"children":6019},{"className":6018},[],[6020],{"type":67,"value":5261},{"type":67,"value":6022},"), or YAML pins a version that doesn't exist (",{"type":61,"tag":91,"props":6024,"children":6026},{"className":6025},[],[6027],{"type":67,"value":5274},{"type":67,"value":5511},{"type":61,"tag":70,"props":6030,"children":6031},{},[6032,6036,6038,6044],{"type":61,"tag":83,"props":6033,"children":6034},{},[6035],{"type":67,"value":5630},{"type":67,"value":6037},": Ensure versions increment by one with no gaps; run ",{"type":61,"tag":91,"props":6039,"children":6041},{"className":6040},[],[6042],{"type":67,"value":6043},"uvx --from airflow-blueprint blueprint list",{"type":67,"value":6045}," to see available versions.",{"type":61,"tag":424,"props":6047,"children":6049},{"id":6048},"non-yaml-compatible-fields-typeerror-at-import",[6050],{"type":67,"value":6051},"\"non-YAML-compatible fields\" (TypeError at import)",{"type":61,"tag":70,"props":6053,"children":6054},{},[6055,6059,6061,6066,6068,6073,6075,6080],{"type":61,"tag":83,"props":6056,"children":6057},{},[6058],{"type":67,"value":5620},{"type":67,"value":6060},": A config field uses a type Blueprint rejects since 0.3.0 — a multi-type union (e.g. ",{"type":61,"tag":91,"props":6062,"children":6064},{"className":6063},[],[6065],{"type":67,"value":1149},{"type":67,"value":6067},"), bare ",{"type":61,"tag":91,"props":6069,"children":6071},{"className":6070},[],[6072],{"type":67,"value":1306},{"type":67,"value":6074},", or ",{"type":61,"tag":91,"props":6076,"children":6078},{"className":6077},[],[6079],{"type":67,"value":1314},{"type":67,"value":589},{"type":61,"tag":70,"props":6082,"children":6083},{},[6084,6088,6090,6095,6096,6101,6103,6108],{"type":61,"tag":83,"props":6085,"children":6086},{},[6087],{"type":67,"value":5630},{"type":67,"value":6089},": Use a single, explicit type. ",{"type":61,"tag":91,"props":6091,"children":6093},{"className":6092},[],[6094],{"type":67,"value":1260},{"type":67,"value":1262},{"type":61,"tag":91,"props":6097,"children":6099},{"className":6098},[],[6100],{"type":67,"value":1268},{"type":67,"value":6102}," is still allowed. See ",{"type":61,"tag":83,"props":6104,"children":6105},{},[6106],{"type":67,"value":6107},"Creating Blueprints → Config Field Types Must Be YAML-Compatible",{"type":67,"value":589},{"type":61,"tag":424,"props":6110,"children":6112},{"id":6111},"debugging-in-airflow-ui",[6113],{"type":67,"value":6114},"Debugging in Airflow UI",{"type":61,"tag":70,"props":6116,"children":6117},{},[6118,6120,6125],{"type":67,"value":6119},"Every Blueprint task has extra fields in ",{"type":61,"tag":83,"props":6121,"children":6122},{},[6123],{"type":67,"value":6124},"Rendered Template",{"type":67,"value":530},{"type":61,"tag":1183,"props":6127,"children":6128},{},[6129,6140],{"type":61,"tag":137,"props":6130,"children":6131},{},[6132,6138],{"type":61,"tag":91,"props":6133,"children":6135},{"className":6134},[],[6136],{"type":67,"value":6137},"blueprint_step_config",{"type":67,"value":6139}," - resolved YAML config",{"type":61,"tag":137,"props":6141,"children":6142},{},[6143,6149],{"type":61,"tag":91,"props":6144,"children":6146},{"className":6145},[],[6147],{"type":67,"value":6148},"blueprint_step_code",{"type":67,"value":6150}," - Python source of blueprint",{"type":61,"tag":168,"props":6152,"children":6153},{},[],{"type":61,"tag":121,"props":6155,"children":6157},{"id":6156},"verification-checklist",[6158],{"type":67,"value":6159},"Verification Checklist",{"type":61,"tag":70,"props":6161,"children":6162},{},[6163],{"type":67,"value":6164},"Before finishing, verify with user:",{"type":61,"tag":1183,"props":6166,"children":6169},{"className":6167},[6168],"contains-task-list",[6170,6188,6217,6237],{"type":61,"tag":137,"props":6171,"children":6174},{"className":6172},[6173],"task-list-item",[6175,6180,6181,6186],{"type":61,"tag":6176,"props":6177,"children":6179},"input",{"disabled":480,"type":6178},"checkbox",[],{"type":67,"value":3609},{"type":61,"tag":91,"props":6182,"children":6184},{"className":6183},[],[6185],{"type":67,"value":4621},{"type":67,"value":6187}," shows their templates",{"type":61,"tag":137,"props":6189,"children":6191},{"className":6190},[6173],[6192,6195,6196,6201,6203,6208,6210,6216],{"type":61,"tag":6176,"props":6193,"children":6194},{"disabled":480,"type":6178},[],{"type":67,"value":3609},{"type":61,"tag":91,"props":6197,"children":6199},{"className":6198},[],[6200],{"type":67,"value":4672},{"type":67,"value":6202}," passes (run it bare to scan all ",{"type":61,"tag":91,"props":6204,"children":6206},{"className":6205},[],[6207],{"type":67,"value":4683},{"type":67,"value":6209}," recursively, or pass a specific file — passing a directory path fails with ",{"type":61,"tag":91,"props":6211,"children":6213},{"className":6212},[],[6214],{"type":67,"value":6215},"Is a directory",{"type":67,"value":2665},{"type":61,"tag":137,"props":6218,"children":6220},{"className":6219},[6173],[6221,6224,6225,6230,6232],{"type":61,"tag":6176,"props":6222,"children":6223},{"disabled":480,"type":6178},[],{"type":67,"value":3609},{"type":61,"tag":91,"props":6226,"children":6228},{"className":6227},[],[6229],{"type":67,"value":528},{"type":67,"value":6231}," exists with ",{"type":61,"tag":91,"props":6233,"children":6235},{"className":6234},[],[6236],{"type":67,"value":5761},{"type":61,"tag":137,"props":6238,"children":6240},{"className":6239},[6173],[6241,6244],{"type":61,"tag":6176,"props":6242,"children":6243},{"disabled":480,"type":6178},[],{"type":67,"value":6245}," DAG appears in Airflow UI without parse errors",{"type":61,"tag":168,"props":6247,"children":6248},{},[],{"type":61,"tag":121,"props":6250,"children":6252},{"id":6251},"reference",[6253],{"type":67,"value":6254},"Reference",{"type":61,"tag":1183,"props":6256,"children":6257},{},[6258,6268],{"type":61,"tag":137,"props":6259,"children":6260},{},[6261,6263],{"type":67,"value":6262},"GitHub: ",{"type":61,"tag":106,"props":6264,"children":6266},{"href":108,"rel":6265},[110],[6267],{"type":67,"value":108},{"type":61,"tag":137,"props":6269,"children":6270},{},[6271,6273],{"type":67,"value":6272},"PyPI: ",{"type":61,"tag":106,"props":6274,"children":6277},{"href":6275,"rel":6276},"https:\u002F\u002Fpypi.org\u002Fproject\u002Fairflow-blueprint\u002F",[110],[6278],{"type":67,"value":6275},{"type":61,"tag":424,"props":6280,"children":6282},{"id":6281},"astro-ide",[6283],{"type":67,"value":6284},"Astro IDE",{"type":61,"tag":1183,"props":6286,"children":6287},{},[6288],{"type":61,"tag":137,"props":6289,"children":6290},{},[6291,6293],{"type":67,"value":6292},"Astro IDE Blueprint docs: ",{"type":61,"tag":106,"props":6294,"children":6297},{"href":6295,"rel":6296},"https:\u002F\u002Fdocs.astronomer.io\u002Fastro\u002Fide-blueprint",[110],[6298],{"type":67,"value":6295},{"type":61,"tag":6300,"props":6301,"children":6302},"style",{},[6303],{"type":67,"value":6304},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":6306,"total":6463},[6307,6321,6333,6349,6363,6380,6393,6404,6419,6433,6443,6450],{"slug":14,"name":14,"fn":6308,"description":6309,"org":6310,"tags":6311,"stars":25,"repoUrl":26,"updatedAt":6320},"manage and troubleshoot Airflow via CLI","Queries, manages, and troubleshoots Apache Airflow using the `af` CLI. Use when working with anything related to Airflow - a DAG, a DAG run, a task log, an import or parse error, a broken DAG, or any Airflow operation. Covers listing and triggering DAGs, retrying runs, reading task logs, diagnosing failures, debugging import and parse errors, checking connections, variables and pools, exploring the REST API, and monitoring health (for example \"trigger a pipeline\", \"retry a run\", \"list connections\", \"check Airflow health\", \"why did my DAG fail\"). This is the entrypoint that routes to sibling skills for authoring, testing, deploying, and migrating Airflow 2 to 3. Not for warehouse\u002FSQL analytics on Airflow metadata tables (use analyzing-data); for deep root-cause reports use debugging-dags or airflow-investigation.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6312,6313,6316,6317],{"name":13,"slug":14,"type":15},{"name":6314,"slug":6315,"type":15},"CLI","cli",{"name":20,"slug":21,"type":15},{"name":6318,"slug":6319,"type":15},"Debugging","debugging","2026-04-06T18:01:43.992997",{"slug":6322,"name":6322,"fn":6323,"description":6324,"org":6325,"tags":6326,"stars":25,"repoUrl":26,"updatedAt":6332},"airflow-hitl","add human-in-the-loop steps to Airflow DAGs","Builds human-in-the-loop (HITL) Airflow workflows - approval gates, form input, and human-driven branching. Use when a DAG needs a human in the loop - an approval or reject step, sign-off before a task runs, a decision or approval UI, branching on a human choice, or collecting form input mid-run; also on mentions of ApprovalOperator, HITLOperator, HITLBranchOperator, HITLEntryOperator, or HITLTrigger. Requires Airflow 3.1+. Not for AI\u002FLLM task calls (see migrating-ai-sdk-to-common-ai).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6327,6328,6331],{"name":13,"slug":14,"type":15},{"name":6329,"slug":6330,"type":15},"Approvals","approvals",{"name":20,"slug":21,"type":15},"2026-04-06T18:01:46.758548",{"slug":6334,"name":6334,"fn":6335,"description":6336,"org":6337,"tags":6338,"stars":25,"repoUrl":26,"updatedAt":6348},"airflow-plugins","build Airflow UI plugins","Builds Airflow 3.1+ plugins that embed FastAPI apps, custom UI pages, React components, middleware, macros, and operator links directly into the Airflow UI. Use when building anything custom inside Airflow 3.1+ that involves Python and a browser-facing interface - creating an Airflow plugin, adding a custom UI page or nav entry, building FastAPI-backed endpoints inside Airflow, serving static assets from a plugin, embedding a React app, adding middleware to the API server, creating custom operator extra links, or calling the Airflow REST API from inside a plugin; also when AirflowPlugin, fastapi_apps, external_views, react_apps, or plugin registration come up.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6339,6340,6343,6345],{"name":13,"slug":14,"type":15},{"name":6341,"slug":6342,"type":15},"Plugin Development","plugin-development",{"name":6344,"slug":535,"type":15},"Python",{"name":6346,"slug":6347,"type":15},"UI Components","ui-components","2026-04-06T18:01:56.827891",{"slug":6350,"name":6350,"fn":6351,"description":6352,"org":6353,"tags":6354,"stars":25,"repoUrl":26,"updatedAt":6362},"airflow-state-store","persist Airflow task and asset state","Persists task and asset state across retries and DAG runs using Airflow 3.3's AIP-103 key\u002Fvalue stores (`task_state_store`, `asset_state_store`) and the crash-safe `ResumableJobMixin`. Use when the user asks about task state store, checkpointing in tasks, persisting state across retries, job IDs surviving worker crashes, watermarks, asset metadata, resumable tasks, crash-safe operators, or \"what's new in Airflow 3.3\". Also use proactively when reading a DAG that uses Variables or XCom for intra-task coordination state — flag the anti-pattern and recommend task_state_store or asset_state_store instead. Requires Airflow 3.3+.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6355,6356,6358,6359],{"name":13,"slug":14,"type":15},{"name":6357,"slug":39,"type":15},"Data Engineering",{"name":20,"slug":21,"type":15},{"name":6360,"slug":6361,"type":15},"Operations","operations","2026-07-07T06:43:11.160671",{"slug":6364,"name":6364,"fn":6365,"description":6366,"org":6367,"tags":6368,"stars":25,"repoUrl":26,"updatedAt":6379},"analyzing-data","query data warehouses for business questions","Queries the data warehouse with SQL and answers business questions about data. Use when answering anything that needs warehouse data - counts, metrics, trends, aggregations, joins across tables, data lookups, or ad-hoc SQL analysis (for example \"who uses X\", \"how many Y\", \"show me Z\", \"find customers\", \"what is the count\").",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6369,6372,6375,6376],{"name":6370,"slug":6371,"type":15},"Analytics","analytics",{"name":6373,"slug":6374,"type":15},"Data Analysis","data-analysis",{"name":6357,"slug":39,"type":15},{"name":6377,"slug":6378,"type":15},"SQL","sql","2026-04-06T18:01:49.599775",{"slug":6381,"name":6381,"fn":6382,"description":6383,"org":6384,"tags":6385,"stars":25,"repoUrl":26,"updatedAt":6392},"annotating-task-lineage","annotate Airflow tasks with data lineage","Annotate Airflow tasks with data lineage using inlets and outlets. Use when the user wants to add lineage metadata to tasks, specify input\u002Foutput datasets, or enable lineage tracking for operators without built-in OpenLineage extraction.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6386,6387,6388,6389],{"name":13,"slug":14,"type":15},{"name":6357,"slug":39,"type":15},{"name":20,"slug":21,"type":15},{"name":6390,"slug":6391,"type":15},"Observability","observability","2026-04-06T18:02:03.487365",{"slug":6394,"name":6394,"fn":6395,"description":6396,"org":6397,"tags":6398,"stars":25,"repoUrl":26,"updatedAt":6403},"authoring-dags","author Airflow DAGs","Workflow and best practices for writing Apache Airflow DAGs. Use when creating a new DAG, write pipeline code, handling questions about DAG patterns and conventions or extending an existing DAG with a follow-up\u002Fdownstream task. ANY request shaped like 'add a DAG named X', 'write a pipeline', 'add a task that runs after Y', or 'extend the DAG'. For testing and debugging DAGs, see the testing-dags skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6399,6400,6401,6402],{"name":13,"slug":14,"type":15},{"name":20,"slug":21,"type":15},{"name":17,"slug":18,"type":15},{"name":6344,"slug":535,"type":15},"2026-04-06T18:01:52.679888",{"slug":6405,"name":6405,"fn":6406,"description":6407,"org":6408,"tags":6409,"stars":25,"repoUrl":26,"updatedAt":6418},"authoring-go-sdk-tasks","implement Airflow tasks in Go","Writes Airflow task logic in Go using the Airflow Go SDK. Use when the user wants to implement Airflow tasks in Go, asks about `BundleProvider`\u002F`RegisterDags`, the `bundlev1` Registry\u002FDag interfaces, registering Go tasks (`AddTask`\u002F`AddTaskWithName`), dependency injection by parameter type (`context.Context`, `sdk.TIRunContext`, `*slog.Logger`, `sdk.Client`), or reading connections\u002Fvariables\u002FXComs from Go. This skill covers the Go-specific native API; the shared Python-stub pattern and conceptual model live in authoring-language-sdk-tasks. For building\u002Fpacking\u002Fshipping the bundle see deploying-go-sdk-bundles; for coordinator config see configuring-airflow-language-sdks.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6410,6411,6414,6415],{"name":13,"slug":14,"type":15},{"name":6412,"slug":6413,"type":15},"API Development","api-development",{"name":20,"slug":21,"type":15},{"name":6416,"slug":6417,"type":15},"Go","go","2026-07-11T05:39:13.552213",{"slug":6420,"name":6420,"fn":6421,"description":6422,"org":6423,"tags":6424,"stars":25,"repoUrl":26,"updatedAt":6432},"authoring-java-sdk-tasks","implement Airflow tasks in Java","Writes Airflow task logic in Java, Kotlin, or any JVM language using the Airflow Java SDK. Use when the user wants to implement Airflow tasks in Java\u002FJVM, asks about `@Builder.Dag`\u002F`@Builder.Task`\u002F`@Builder.XCom`, the `Task`\u002F`BundleBuilder` interfaces, reading connections\u002Fvariables\u002FXComs from Java, the JSON-to-Java type mapping, or logging from Java tasks. This skill covers the Java-specific native API; the shared Python-stub pattern and conceptual model live in authoring-language-sdk-tasks. For building\u002Fshipping the bundle see deploying-java-sdk-bundles; for coordinator config see configuring-airflow-language-sdks.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6425,6426,6429],{"name":20,"slug":21,"type":15},{"name":6427,"slug":6428,"type":15},"Engineering","engineering",{"name":6430,"slug":6431,"type":15},"Java","java","2026-07-18T05:48:13.374003",{"slug":6434,"name":6434,"fn":6435,"description":6436,"org":6437,"tags":6438,"stars":25,"repoUrl":26,"updatedAt":6442},"authoring-language-sdk-tasks","implement non-Python Airflow tasks","The language-neutral foundation for Airflow language SDKs — implement task logic in a non-Python language while the DAG stays in Python. Use when the user wants to run an Airflow task in another language (Java, Kotlin, Go, or other JVM\u002Fnative languages), asks how the Python `@task.stub` pairs with native task code, how task\u002FDAG IDs must match across the two sides, how data passes via XCom as JSON, or which language SDKs exist. This skill owns the shared Python-stub pattern and conceptual model; for a specific language's native API, build, and runtime, use that language's skill (e.g. authoring-java-sdk-tasks, authoring-go-sdk-tasks).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6439,6440,6441],{"name":13,"slug":14,"type":15},{"name":20,"slug":21,"type":15},{"name":6427,"slug":6428,"type":15},"2026-07-18T05:11:54.496539",{"slug":4,"name":4,"fn":5,"description":6,"org":6444,"tags":6445,"stars":25,"repoUrl":26,"updatedAt":27},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6446,6447,6448,6449],{"name":13,"slug":14,"type":15},{"name":20,"slug":21,"type":15},{"name":17,"slug":18,"type":15},{"name":23,"slug":24,"type":15},{"slug":6451,"name":6451,"fn":6452,"description":6453,"org":6454,"tags":6455,"stars":25,"repoUrl":26,"updatedAt":6462},"checking-freshness","check data freshness in warehouses","Quick data freshness check. Use when the user asks if data is up to date, when a table was last updated, if data is stale, or needs to verify data currency before using it.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6456,6457,6458,6461],{"name":13,"slug":14,"type":15},{"name":6357,"slug":39,"type":15},{"name":6459,"slug":6460,"type":15},"Data Quality","data-quality",{"name":17,"slug":18,"type":15},"2026-04-06T18:02:02.138565",34,{"items":6465,"total":6463},[6466,6473,6479,6486,6493,6500,6507],{"slug":14,"name":14,"fn":6308,"description":6309,"org":6467,"tags":6468,"stars":25,"repoUrl":26,"updatedAt":6320},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6469,6470,6471,6472],{"name":13,"slug":14,"type":15},{"name":6314,"slug":6315,"type":15},{"name":20,"slug":21,"type":15},{"name":6318,"slug":6319,"type":15},{"slug":6322,"name":6322,"fn":6323,"description":6324,"org":6474,"tags":6475,"stars":25,"repoUrl":26,"updatedAt":6332},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6476,6477,6478],{"name":13,"slug":14,"type":15},{"name":6329,"slug":6330,"type":15},{"name":20,"slug":21,"type":15},{"slug":6334,"name":6334,"fn":6335,"description":6336,"org":6480,"tags":6481,"stars":25,"repoUrl":26,"updatedAt":6348},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6482,6483,6484,6485],{"name":13,"slug":14,"type":15},{"name":6341,"slug":6342,"type":15},{"name":6344,"slug":535,"type":15},{"name":6346,"slug":6347,"type":15},{"slug":6350,"name":6350,"fn":6351,"description":6352,"org":6487,"tags":6488,"stars":25,"repoUrl":26,"updatedAt":6362},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6489,6490,6491,6492],{"name":13,"slug":14,"type":15},{"name":6357,"slug":39,"type":15},{"name":20,"slug":21,"type":15},{"name":6360,"slug":6361,"type":15},{"slug":6364,"name":6364,"fn":6365,"description":6366,"org":6494,"tags":6495,"stars":25,"repoUrl":26,"updatedAt":6379},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6496,6497,6498,6499],{"name":6370,"slug":6371,"type":15},{"name":6373,"slug":6374,"type":15},{"name":6357,"slug":39,"type":15},{"name":6377,"slug":6378,"type":15},{"slug":6381,"name":6381,"fn":6382,"description":6383,"org":6501,"tags":6502,"stars":25,"repoUrl":26,"updatedAt":6392},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6503,6504,6505,6506],{"name":13,"slug":14,"type":15},{"name":6357,"slug":39,"type":15},{"name":20,"slug":21,"type":15},{"name":6390,"slug":6391,"type":15},{"slug":6394,"name":6394,"fn":6395,"description":6396,"org":6508,"tags":6509,"stars":25,"repoUrl":26,"updatedAt":6403},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[6510,6511,6512,6513],{"name":13,"slug":14,"type":15},{"name":20,"slug":21,"type":15},{"name":17,"slug":18,"type":15},{"name":6344,"slug":535,"type":15}]