Write Dag (title case) in all prose. Keep the all-caps or lowercase spelling only when reproducing a literal code token — never rewrite these, even inside fenced code blocks:
DAG (from airflow.sdk import DAG, dag = DAG("my_dag", ...)); identifiers like dag_id, dag, my_dag.airflow dags list, airflow dags test, etc.dag_processing/, dagprocessor, get_dag, etc.Use "DAG" — always write "Dag").Don't spell out Directed Acyclic Graph except for historical context.
uv tool install prekprek installscripts/tools/setup_breeze — installs ~/.local/bin/breeze that runs breeze via uv run --locked from the current git worktree's dev/breeze, with dependencies pinned by dev/breeze/uv.lock (so each worktree, including ephemeral agent worktrees, gets its own breeze tied to its sources). See ADR 0017.breeze.dev/ (mounted as /opt/airflow/dev/ inside Breeze).<PROJECT> is folder where pyproject.toml of the package you want to test is located. For example, airflow-core or providers/amazon. <target_branch> is the branch the PR will be merged into — usually main, but could be v3-1-test when creating a PR for the 3.1 branch.
uv run --project <PROJECT> pytest path/to/test.py::TestClass::test_method -xvsuv run --project <PROJECT> pytest path/to/test.py -xvsuv run --project <PROJECT> pytest path/to/package -xvsbreeze run pytest <tests> -xvsuv run --project <PROJECT> python dev/my_script.pybreeze testing <test_group> --run-in-parallel (test groups: core-tests, providers-tests)breeze testing <test_group> --run-db-tests-only --run-in-parallel (test groups: core-tests, providers-tests)breeze testing <test_group> --skip-db-tests --use-xdist (test groups: core-tests, providers-tests)breeze testing providers-tests --test-type "Providers[PROVIDERS_LIST]" (e.g., Providers[google] or Providers[amazon] or “Providers[amazon,google]”)breeze testing helm-tests --use-xdistbreeze testing helm-tests --use-xdist --kubernetes-version 1.35.0breeze testing helm-tests --use-xdist --test-type <type> (types: airflow_aux, airflow_core, apiserver, dagprocessor, other, redis, security, statsd, webserver)breeze testing <test_group> (test groups: airflow-ctl-tests, docker-compose-tests, task-sdk-tests)uv run --project scripts pytest scripts/tests/ -xvsbreeze run airflow dags listprek run mypy-<project> --all-files (e.g. mypy-airflow-core, mypy-task-sdk, mypy-shared-logging; each shared/<dist> workspace member has its own mypy-shared-<dist> hook). The hook uses a dedicated virtualenv and mypy cache under .build/mypy-venvs/<hook>/ and .build/mypy-caches/<hook>/; mypy itself is installed from uv.lock via the mypy dependency group (uv sync --group mypy), so it never mutates your project .venv. The hook prefers uv from the project's main .venv/bin/uv (installed by uv sync — uv is part of the dev dependency group via the all extras) for a project-pinned uv version; it falls back to uv on $PATH with a warning if that binary is missing. Clear with breeze down --cleanup-mypy-cache.breeze run mypy path/to/codeprek run ruff --from-ref <target_branch>prek run ruff-format --from-ref <target_branch>prek run --from-ref <target_branch> --stage pre-commitprek run --from-ref <target_branch> --stage manual --skip compile-ui-assets-dev --skip view-skill-eval --skip run-skill-eval-codex (the skipped hooks start long-running local servers or provision the opt-in Codex environment rather than run checks that complete)breeze build-docsbreeze verify (add --full for everything CI runs for the default matrix cell except static checks, --json for machine-readable output). It uses the same selective-checks logic as CI.breeze ci selective-check --commit-ref <commit_with_squashed_changes>SQLite is the default backend. Use --backend postgres or --backend mysql for integration tests that need those databases. If Docker networking fails, run docker network prune.
UV workspace monorepo. Key paths:
airflow-core/src/airflow/ — core scheduler, API, CLI, modelsmodels/ — SQLAlchemy models (DagModel, TaskInstance, DagRun, Asset, etc.)jobs/ — scheduler, triggerer, Dag processor runnersapi_fastapi/core_api/ — public REST API v2, UI endpointsapi_fastapi/execution_api/ — task execution communication APIdag_processing/ — Dag parsing and validationcli/ — command-line interfaceui/ — React/TypeScript web interface (Vite)task-sdk/ — lightweight SDK for Dag authoring and task execution runtimesrc/airflow/sdk/execution_time/ — task runner, supervisorproviders/ — 100+ provider packages, each with its own pyproject.tomlairflow-ctl/ — management CLI toolchart/ — Helm chart for Kubernetes deploymentdev/ — development utilities and scripts used to bootstrap the environment, releases, breeze dev envscripts/ — utility scripts for CI, Docker, and prek hooks (workspace distribution apache-airflow-scripts)ci/prek/ — prek (pre-commit) hook scripts; shared utilities in common_prek_utils.pytests/ — pytest tests for the scripts; run with uv run --project scripts pytest scripts/tests/The uv.lock file is generated by uv lock, uv sync and is committed to the repo - it contains snapshot of versions of all dependencies used for development of Airflow. If at any point in time you have a conflict with uv.lock, simply delete it and run uv lock to regenerate it.
airflow.sdk).shared folder.uv workspace feature to keep all the distributions sharing dependencies and venvuv --project <FOLDER> sync command acts on the selected project in the monorepo with only dependencies that it hasWhen reviewing code, writing security documentation, or performing security research, keep in mind the following aspects of Airflow's security model. The authoritative reference is airflow-core/docs/security/security_model.rst and airflow-core/docs/security/jwt_token_authentication.rst.
Particularly, the intentional design choices, that are not security vulnerabilities and should not be reported as such are described in “What is NOT considered a security vulnerability” chapter of the security model.
When flagging security concerns, distinguish between:
shared folderairflow-core, task-sdk for example)uv run ruff format <file_path> and uv run ruff check --fix <file_path>. Do this for every Python file you create or modify, before moving on to the next step.assert in production code.# Log for debugging above logging.debug(...), # Validate for safety, # Retry for reliability and similar provide no useful information. Useful comments record a specific constraint, invariant, compatibility quirk, or tradeoff. Before adding a comment, identify the misunderstanding or incorrect change it would prevent. If removing a comment will lose non-code-related context, omit it. Do not narrate code, repeat code, or invent a rationale in comments. Keep necessary explanations short, precise and at the source of truth. Do not repeat comments at each call site. Judge each comment value by the preserved context only.time.monotonic() for durations, not time.time().airflow-core, functions with a session parameter must not call session.commit(). Use keyword-only session parameters.TYPE_CHECKING blocks.kubernetes.client) with TYPE_CHECKING in multi-process code paths.ValueError instead of raising the broad AirflowException directly. Each error case should have a specific exception type that conveys what went wrong. Never add new direct raise AirflowException(...) usages — the community is actively reducing them, not adding more, and the check-no-new-airflow-exceptions prek hook enforces this across airflow-core, airflow-ctl, task-sdk, providers, and shared. Prefer a Python built-in (ValueError, TypeError, OSError, …) or a dedicated class in the appropriate exceptions.py. The only acceptable way an AirflowException line may move is relocating an already-existing one verbatim during a refactor (e.g. moving code between files) — that is not a new usage. When you touch code that already raises AirflowException, prefer narrowing it to a more specific exception rather than leaving or duplicating it.HTTPException at FastAPI route boundaries. In airflow-core/src/airflow/api_fastapi/core_api/ route handlers, catch errors raised by domain code (e.g., ValueError from airflow.state.metastore.MetastoreBackend for a missing row or invalid input) and re-raise as HTTPException with the right status (404 for not-found, 400 for invalid input). Otherwise they propagate as 500 Internal Server Error, leaking internals and misleading clients.DELETE/UPDATE in the scheduler loop or any synchronous interval task (e.g. call_regular_interval callbacks) must be batched with LIMIT and committed between batches — never issue a single unbounded bulk write against a user-driven table. Unbounded bulk writes hold row locks for the entire transaction (blocking concurrent writers) and stall the scheduler main loop. Filter columns used by the cleanup must be indexed. Follow the batching pattern in airflow-core/src/airflow/utils/db_cleanup.py.get_, extract_, find_, compute_, build_, etc. Avoid noun-only names like _serialize_keys or _base_names — they read as attributes, not callables. Predicates (is_, has_) are the one exception.dev/breeze/src/airflow_breeze/utils/selective_checks.py (run-mode decisions, file-group matching, test-type selection, prek-hook skipping). Whenever you change a rule there — add/rename a file group, change what forces full_tests_needed/all_versions, alter how providers or test types are selected, or change which prek hooks are skipped — update dev/breeze/doc/ci/04_selective_checks.md in the same PR (the decision-rules list, the diagrams, the outputs table, and the worked examples as applicable) and add/adjust tests in dev/breeze/tests/test_selective_checks.py. The doc is the human-facing explanation of that file; letting them drift makes CI behaviour impossible to reason about.airflow-core/newsfragments/, chart/newsfragments/, and dev/mypy/newsfragments/ — and only for major or breaking changes. Golden rule: never create a newsfragment unless you are certain the change is user-facing. If you are not sure the change is visible to users — build/release tooling, CI, packaging, internal refactors with no behavior change, dev-only scripts, and test-only changes are not user-facing — do not add one. Default to omitting it; a maintainer will ask for a newsfragment during review if the change warrants one. Adding a spurious newsfragment for a non-user-facing change is a defect, not a safe default. Never add newsfragments for providers/ or airflow-ctl/ — those distributions are released from main and their release managers regenerate the changelog from git log, so per-PR newsfragments are not consumed (see dev/README_RELEASE_PROVIDERS.md and dev/README_RELEASE_AIRFLOWCTL.md). For a user-visible note in those distributions, edit the changelog directly: providers/<provider>/docs/changelog.rst for providers, airflow-ctl/RELEASE_NOTES.rst for airflow-ctl. Changes to task-sdk/ ship in airflow-core — use airflow-core/newsfragments/.unittest.TestCase.spec/autospec when mocking.@mock.patch decorators over with mock.patch(...) context managers for patching. Use conf_vars (from tests_common.test_utils.config) for Airflow config overrides — as a decorator when the value is fixed, as a context manager when it varies via @pytest.mark.parametrize.time_machine for time-dependent tests. Do not use datetime.now()@pytest.mark.parametrize for multiple similar inputs — consolidate tests that only differ in input/expected values into a single parametrized test.@pytest.mark.db_test for tests that require database access.devel-common/src/tests_common/pytest_plugin.py.airflow/cli/cli_parser.py → tests/cli/test_cli_parser.py.caplog.text for example), these are legacy string matching APIs planned for removal. Structured assertions via caplog (which resolves to cap_structlog under structlog) are fine and preferred: "event name" in caplog or {"event": ..., "field": ...} in caplog.files/.files/ if it doesn't exist.Use the contribution skills below when available. If the runtime does not support skill discovery, read the linked SKILL.md directly. If either skill is unavailable, do not publish, push, create or modify GitHub issues or pull requests, or rename or add Git remotes. You may draft text or propose commands, but must state that the relevant skill could not be read and request confirmation before any remote-changing action.
Before working on an issue, check for existing PRs. Push only to the user's fork, never upstream or main. Never list an agent as a commit co-author. Retain the required Gen-AI disclosure and GitHub-message attribution; do not tag individuals outside the exceptions in the message guidance. Drafting does not authorize posting.
This repo uses the apache/magpie framework, installed from its plugin marketplace. The framework provides the pr-management-* skills (triage, code-review, stats, mentor) among others. Nothing framework-related is committed here — install it in your own agent harness. In Claude Code:
/plugin marketplace add apache/magpie /plugin install magpie-pr-management@apache-magpie
magpie@apache-magpie installs every family at once; other families (magpie-security, magpie-release-management, …) install individually. magpie-release-management is in the project floor (.apache-magpie.lock): its verify-rc skill verifies a release candidate and, per .apache-magpie-overrides/release-verify-rc.md, can test your own changes against a providers wave. The contributor-facing summary lives in the Agent-assisted contribution section of README.md.
Airflow-specific modifications to framework-skill workflows live in .apache-magpie-overrides/ — the installed plugin reads them at run time. Never edit the installed plugin itself; framework changes go via PR to apache/magpie.
With the magpie-pr-management plugin installed, use the magpie-pr-management:pr-management-code-review skill for PR code review. It posts findings as inline review comments anchored to file:line, presented individually for accept/skip before anything is submitted — prefer it over an ad-hoc review pass or a generic review command. A body-only review is the explicit opt-out (inline:off).