From maigo
Surfaces Apache Airflow contributor conventions — naming, Breeze/uv environment, Ruff/Mypy style, coding rules, pytest patterns, PR hygiene — for any task in the apache/airflow repo.
How this skill is triggered — by the user, by Claude, or both
Slash command
/maigo:airflow-awareThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<!-- mkdocs-include-start -->
Loaded by: repo-detect hook (SessionStart) when apache/airflow is detected in git remote or file structure.
Applies to: Any skill execution within an Apache Airflow contributor checkout — review, quick-fix, refactoring, or otherwise.
This is a knowledge layer, not a review checklist.
It is loaded automatically by the
repo_detect hook
at session start when an Apache Airflow repo is detected.
It mirrors and supplements the repo's own AGENTS.md / CLAUDE.md, condensed for agent context.
It can also be triggered manually via a memory entry with triggers: [airflow-aware]
(see Memory-triggered skill loading).
Once loaded, treat every convention as background — do not re-read each task.
The single source of truth for contributor conventions lives in the repo itself. This skill is a condensed onramp formatted for agent startup context, not a competing authority.
AGENTS.md
(or CLAUDE.md — both are identical, 455 lines).AGENTS.md, the repo file wins..github/instructions/code-review.instructions.md
for review-specific guidance.DAG (code)In prose, write Dag (title case) — never all-caps DAG unless referring to the Python
class, and never expand "Directed Acyclic Graph". Code tokens stay as-is: class DAG,
dag_id, airflow dags list, dag_processing/, DAG_FOLDER. See the "Naming" section of
AGENTS.md when in doubt.
Do not run pytest, mypy, or pre-commit directly on the host.
Use the Breeze container or uv run --project <PROJECT> for isolation.
Airflow is a monorepo; dependency isolation relies on --project.
# run tests for a specific project
uv run --project airflow-core pytest tests/unit/foo/test_bar.py
# format and lint
uv run --project airflow-core ruff format src/
uv run --project airflow-core ruff check --fix src/
# pre-commit equivalent (use prek, not pre-commit)
prek run --all-files
Scratch scripts go in dev/ — not in repo root or scripts/.
Note: prek is the runner currently specified by upstream. It is compatible with the same hooks as pre-commit.
verify_completion in this reporepo_detect writes .claude/skip-test-verification on first detection so the
Stop-hook does not try to run a host-side uv run pytest (which pulls jpype1
→ cmake FindJava and fails on machines without a JDK; the 90s timeout also
cannot cover any Airflow subproject's suite). To re-enable verification for a
focused area, delete that file or replace it with a targeted
.claude/test-command such as
uv run --project airflow-core pytest tests/unit/<path>/test_<file>.py.
jpype1 fallback: if the Stop-hook still encounters a jpype1 / cmake /
FindJava error (because .claude/skip-test-verification was missing or the
hook executed before repo_detect had a chance to write it), treat it as a
known upstream constraint and move on. Do not try to install a JDK, patch
jpype1 import paths, or rewire the lock file — those are upstream issues, not
part of any maigo task scope. If verification was genuinely needed, run a
targeted single-file pytest manually instead.
uv.lock phantom diff diagnosticA persistent uv.lock diff you did not introduce — and that returns after
git checkout HEAD -- uv.lock + the next uv sync — is almost always
lockfile drift on main (a contributor edited a pyproject.toml without
re-running uv lock), not a local environment problem. Don't fold the
re-lock into your feature PR; git checkout HEAD -- uv.lock keeps the noise
out, and the diff re-appearing on the next uv sync is expected.
Full diagnostic — confirm the drift, find when it was introduced, worked case
study, and how to handle in a feature PR — is in the "uv.lock drift diagnostic"
section of references/review-checks.md.
uv run ruff formatuv run ruff check --fix.py
file in the repo (do not hand-retype it).prek — do not invoke it directly on the host.These are extracted from the "Coding rules" section of AGENTS.md. They are listed here
because they are the conventions agents most frequently overlook.
assert in production code — use a real exception (e.g., ValueError, RuntimeError).raise AirflowException — use a more specific exception class instead.session parameter must be keyword-only and the callee must not call session.commit();
transaction management is the caller's responsibility.time.monotonic() for elapsed-time measurement — not time.time().TYPE_CHECKING guard.__init__ and always-called functions: default to top-level imports. Reserve the
lazy / function-body form for exactly four cases: (1) TYPE_CHECKING block, (2) breaking
a known circular-import cycle, (3) multi-process worker-isolation path, (4) deferred-execution
callback (e.g., a deserialize() body that is not called at module import time).
Self-check: "does this function run on every public touch of the class?" If yes → top-level.
Note: §10.3 and §10.4 address distinct but adjacent rules — Unix-only module gates and heavy
type-only imports in multi-process paths; this rule is about the default for eager call sites.User-facing features (new public class / SDK symbol / scheduling behaviour) must ship all three deliverables in the same PR:
example_asset_partition.py);
reuse the existing block style and existing asset/producer objects. Do not open a new
example file or add a bare new with DAG(...) block — consistent with the
"don't proliferate example Dags" convention..rst (e.g., airflow-core/docs/authoring-and-scheduling/assets.rst).
Prose uses "Dag"; known limitations are written as constraints, not # TODO placeholders.Public-symbol sync (docs side): whenever a public symbol is added or removed,
task-sdk/docs/api.rst and the corresponding airflow-core/docs/ .. autoapiclass:: entries
must be updated together with __init__.py / __all__ / lazy-import table.
Dropping a removed symbol and adding a new one are both required — a missing deletion causes
breeze build-docs to crash at Sphinx import time; a missing addition causes a silent gap
in the API reference. After editing, grep the old symbol name across docs/ to confirm
no stale references remain.
Why: apache/airflow PR #64571 (Window / RollupMapper) and the partition-mapper
refactor both shipped with example + docs in the same PR; missing either was flagged in
round-1 review.
unittest.TestCase.spec= or autospec=True — bare MagicMock() without a spec is not acceptable.assert_* methods for mock assertions (assert_called_once_with, assert_called_with,
assert_not_called, etc.) — do not compare .mock_calls lists directly.
The assert_* methods verify calling signature and raise a clear failure message;
raw list comparison is fragile and obscures intent.time_machine — do not monkey-patch datetime.now manually.@pytest.mark.db_test.caplog — Airflow has a different approach to log assertion; see the
"Testing" section of AGENTS.md
for the prescribed pattern. The typical alternative is to attach a mock log handler and use
its assert_called_once_with or other assert_* methods to verify the calling signature
rather than inspecting caplog output.airflow-core/src/airflow/foo/bar.py ↔ airflow-core/tests/unit/foo/test_bar.pyGenerated-by: footer in the PR body
(see the "Pull requests" section of AGENTS.md for the exact format).airflow-core/, chart/, or dev/mypy/.
Changes in other subdirectories do not need one.Airflow's subsystems have strict ownership boundaries that must not be crossed:
airflow core internals.Cross-boundary changes require extra scrutiny. Before proposing or reviewing such a change,
read
.github/instructions/code-review.instructions.md.
Several public APIs exist as paired classes — one in airflow-core/ and one in
task-sdk/ with the same conceptual role and the same name (e.g., the temporal
partition mappers _BaseTemporalMapper in airflow.partition_mappers.temporal
vs airflow.sdk.definitions.partition_mappers.temporal). When the two
implementations drift on a same-named attribute, the divergence is a
bug, not a nit — even if both sides happen to work in practice because
downstream code (encoders, serializers, consumers) tolerates both shapes.
Things to compare side-by-side when reviewing or self-reviewing such a class:
__init__ (e.g., is _timezone always
Timezone | FixedTimezone on both sides, or is one side keeping a raw
str?).__init__ (e.g., one side runs
parse_timezone(), the other stores verbatim).Do not accept "this can be a separate PR" framing just because the gap
is "merely" a type or validation asymmetry. Fix it in the current PR. The
related question — whether the overall patch is a bugfix or feature
completion, which determines newsfragment + backport label — is a
separate axis covered by the SDK/Core release-state framing memory
(check release tags via git tag --contains <sha> and git show <tag>:<file> to confirm).
When airflow-aware is loaded during a review task (🟡 Soyo running
strict-review on an Airflow diff), read references/review-checks.md in this
skill's directory and append its sub-checks as items 10+, with the severity each
sub-check states. The file covers:
TYPE_CHECKING guards for heavy type-only imports (Request changes)plus the Airflow case studies backing strict-review's recurring must-fix patterns.
Outside of a review context (quick-fix / refactor), skip the file — these checks
are review-only and do not gate other tasks.
This skill pairs with
strict-review.
Review scenario — run strict-review's 9-item base checklist as usual, then apply
airflow-aware conventions as Airflow-specific supplements:
references/review-checks.md) become items 10+
in the checklist output, with Block / Request-changes severity inherited from each
sub-section.Quick-fix / refactor scenario — use conventions 3, 4, 5, 6, and 7 as background knowledge.
Flag violations you notice, but do not run the full strict-review checklist unless the
task calls for it. §10 sub-checks are review-only — do not gate non-review tasks on them.
npx claudepluginhub lee-w/maigo --plugin maigoGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.