| applyTo | ** |
|---|---|
| excludeAgent | coding-agent |
Use these rules when reviewing pull requests to the Apache Airflow repository.
- Scheduler must never run user code. It only processes serialized Dags. Flag any scheduler-path code that deserializes or executes Dag/task code.
- Flag any task execution code that accesses the metadata DB directly instead of through the Execution API (
/executionendpoints). - Flag any code in Dag Processor or Triggerer that breaks process isolation — these components run user code in separate processes from the Scheduler and API Server, but note that they potentially have direct metadata database access and potentially bypass JWT authentication via in-process Execution API transport. This is an intentional design choice documented in the security model, not a security vulnerability.
- Flag any provider importing core internals like
SUPERVISOR_COMMSor task-runner plumbing. Providers interact through the public SDK and execution API only.
- Flag any SQLAlchemy relationship access inside a loop without
joinedload()orselectinload()— this is an N+1 query. - Flag any query on
run_idwithoutdag_id.run_idis only unique per Dag. Queries that filter, group, partition, or join onrun_idalone will silently collide across Dags. - Flag any
session.commit()call inairflow-corecode that receives asessionparameter. Session lifecycle is managed by the caller, not the callee. - Flag any
sessionparameter that is not keyword-only (*, session) inairflow-core. - Flag any database-specific SQL (e.g.,
LATERALjoins, PostgreSQL-only functions, MySQL-only syntax) without cross-DB handling. SQL must work on PostgreSQL, MySQL, and SQLite.
- Flag any
assertin non-test code.assertis stripped in optimized Python (python -O), making it a silent no-op in production. - Flag any
time.time()used for measuring durations. Usetime.monotonic()instead —time.time()is affected by system clock adjustments. - Flag any
fromorimportstatement inside a function or method body. Imports must be at the top of the file. The only valid exceptions are: (1) circular import avoidance, (2) lazy loading for worker isolation, (3)TYPE_CHECKINGblocks. If the import is inside a function, ask the author to justify why it cannot be at module level. - Flag any
@lru_cache(maxsize=None). This creates an unbounded cache — every unique argument set is cached forever. Note:@lru_cache()without arguments defaults tomaxsize=128and is fine. - Flag any heavy import (e.g.,
kubernetes.client) in multi-process code paths that is not behind aTYPE_CHECKINGguard. - Flag any file, connection, or session opened without a context manager or
try/finally. - Flag any new
raise AirflowExceptionusage. The community is reducing directAirflowExceptionusage, not increasing it; new ones are not allowed (enforced by thecheck-no-new-airflow-exceptionsprek hook) — prefer Python's standard exceptions (ValueError,TypeError,OSError), or a dedicated class in the appropriateexceptions.py. The one exception is a pure relocation: an already-existingAirflowExceptionmoved verbatim during a refactor (e.g. code moved between files) is not a new usage — do not flag it, but confirm the diff removes the identical raise elsewhere and leaves it otherwise unchanged. Do not suggest changing specific exceptions back toAirflowException.
- Flag any changed or added behaviour without a corresponding test, and flag tests that a reviewer cannot fail by reverting the PR's change. The target is exactly 100% coverage of what the PR changes — no more, no less. Tests for pre-existing logic or standard-library/third-party functions are padding; missing tests for changed behaviour are gaps. Exception: deliberate behaviour or integration tests may cross those boundaries by design.
- Flag any
unittest.TestCasesubclass. Use pytest patterns instead. - Flag any
mock.Mock()ormock.MagicMock()withoutspecorautospec. Unspec'd mocks silently accept any attribute access, hiding real bugs. - Flag any
time.sleepordatetime.now()in tests. Usetime_machinefor time-dependent tests. - Flag any issue number in test docstrings (e.g.,
"""Fix for #12345""") — test names should describe behavior, not track tickets.
- Flag any query on mapped task instances that does not filter on
map_index. Without it, queries may return arbitrary instances from the mapped set. - Flag any Execution API change without a Cadwyn version migration (CalVer format).
- Avoid
useState + useEffectto sync derived state. Use nullish coalescing or nullable override patterns instead. - Extract shared logic into custom hooks rather than copy-pasting across components.
- Flag any manual edits to files in
openapi-gen/or Task SDK generated models. These must be regenerated, not hand-edited.
Flag these patterns that indicate low-quality AI-generated contributions:
- Fabricated diffs: Changes to files or code paths that don't exist in the repository.
- Unrelated files included: Changes to files that have nothing to do with the stated purpose of the PR.
- Description doesn't match code: PR description describes something different from what the code actually does.
- No evidence of testing: Claims of fixes without test evidence, or author admitting they cannot run the test suite.
- Over-engineered solutions: Adding caching layers, complex locking, or benchmark scripts for problems that don't exist or are misunderstood.
- Narrating or redundant comments: Comments that restate what the next line does (e.g.,
# Add the item to the listbeforelist.append(item)); multi-line prose explaining a one-line change; the same rationale repeated at several sites; or explanatory comments on tests whose names already convey intent. Comments should explain why when it is non-obvious, not narrate what. Flag over-commenting as noise to be trimmed. - Empty PR descriptions: PRs with just the template filled in and no actual description of the changes.
- For bug-fix PRs, flag if there is no regression test — a test that fails without the fix and passes with it.
- Flag any existing test modified to accommodate new behavior — this may indicate a behavioral regression rather than a genuine fix.