Skip to content

#229: Airflow local + CI test-environment spike - #238

Merged
wjduenow merged 8 commits into
devfrom
spike/229-airflow-test-environment
Jun 15, 2026
Merged

#229: Airflow local + CI test-environment spike#238
wjduenow merged 8 commits into
devfrom
spike/229-airflow-test-environment

Conversation

@wjduenow

@wjduenow wjduenow commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Closes #229. First child of epic #228 — establishes the local + CI test environment every later Airflow child certifies against, and ships the first user-facing Airflow surface (example DAG + ops docs).

What this ships

  • docs/research/airflow-test-environment.md — decision record (DEC-1…DEC-5): floor apache-airflow>=2.8,<3 certified at 2.10.4 / py3.11; [airflow] extra under the constraints file; gated airflow pytest marker; label-gated CI job; fake-first split + one live e2e DAG; constraints-pinned install + env-var contract.
  • examples/airflow/signalforge_generate_dag.py — shipped example DAG. PythonOperator wrapping signalforge generate: four-tier exit-code → task-state, graded tier counts → XCom, a gate task that fails on a configurable flagged-count threshold. Config via Airflow Variables / env. (Dedicated SignalForgeGenerateOperator is a roadmap drop-in swap — same result→task-state + XCom contract.)
  • docs/airflow-ops.md + MkDocs nav — install-under-constraints, the example walkthrough, the contract, gating, live-run env gates.
  • pyproject.toml — gated airflow marker (excluded from default addopts); tests/airflow excluded from pyright (Airflow isn't a typecheck dep).
  • .github/workflows/ci.yml — label-gated airflow CI job (sibling to lint-test, not in the default 3.11–3.13 matrix; runs on PRs labelled airflow or workflow_dispatch). The if: is guarded behind an explicit pull_request event.
  • tests/airflow/test_dag_parse.py — gated parse test + a gated live dag.test()-style test (self-skips without SF_RUN_AIRFLOW + Anthropic/BigQuery gates). importorskip is inside the tests so a default pytest never imports Airflow.
  • .gitignore.venv-airflow/ + .airflow-home/ scratch.

Certification

  • Install: apache-airflow==2.10.4 resolves cleanly under constraints-2.10.4/constraints-3.11.txt in an isolated venv; signalforge editable install carries --constraint (else protobuf 4→5 risks breaking Airflow).
  • Parse: gated DagBag parse green (CI airflow job).
  • Live: the example's generate task ran the real pipeline against an init-demo project on real BigQuery + Anthropic (1 passed in 125s).

Validation

ruff · ruff format · pyright (0 errors) · pytest (3570 passed) · mkdocs build — all green. CI: lint-test ×3, airflow, docs-build all success.

Out of scope (later epic #228 children)

The [airflow] extra + signalforge.airflow package skeleton; dedicated operators; result→task-state/XCom operator; connection hook; drift mode.

Summary by CodeRabbit

  • New Features
    • Added an Apache Airflow integration with an example DAG that runs the SignalForge generate stage and publishes tier counts to XCom for downstream tasks.
    • Introduced configurable maximum “flagged” threshold gating for pass/fail behavior.
  • Documentation
    • Added an Airflow operations guide and a reproducible Airflow test-environment setup.
    • Updated documentation navigation and changelog entry (Unreleased).
  • Tests
    • Added marker- and environment-gated Airflow tests for DAG parsing, plus optional live execution.
  • Chores
    • Updated CI to support manual trigger and label-gated Airflow runs; excluded Airflow tests from default checks and added Airflow scratch directory ignores.

wjduenow added 3 commits June 15, 2026 08:19
First child of epic #228. Decides and records the Airflow install + test stack
so later children reference one contract instead of re-litigating it.

- docs/research/airflow-test-environment.md: DEC-1..DEC-5 (version floor
  >=2.8,<3 certified at 2.10.4/py3.11; [airflow] extra under constraints;
  gated airflow marker; label-gated CI job; fake-first split + one live E2E
  DAG), constraints-pinned install, env-var contract, in-process vs subprocess
  decision input, maintainer certification checklist.
- pyproject.toml: register gated `airflow` marker (excluded from default
  addopts); exclude tests/airflow from pyright (Airflow is not a typecheck dep).
- examples/airflow/signalforge_spike_dag.py: trivial DAG + inline placeholder
  operator proving the authoring pattern.
- tests/airflow/test_dag_parse.py: gated DagBag parse certification with
  belt-and-suspenders gating (marker + runtime importorskip).
- .gitignore: ignore .venv-airflow/ and .airflow-home/ scratch.
Certified the spike against a real constraints-pinned Airflow install. Two bugs
the live run caught, now fixed:

- Parse leg must read DagBag(...).dags, not bag.get_dag(): get_dag() consults
  the metadata DB (DagModel.get_current) and fails with "no such table: dag" in
  a fresh venv. Parse certification must not require `airflow db init`.
- CI install step must add pytest/pytest-cov/pytest-asyncio under the same
  constraints file, or `--no-cov` is an unrecognised flag.

Doc now records the certification results, the DB-init split (DagBag parse vs
dag.test() execute), and the benign older-pytest strict_markers ini warning.
Add the label-gated `airflow` job (sibling to lint-test, not in the default
matrix) + `workflow_dispatch` trigger. Runs only on PRs labelled `airflow` or
manual dispatch.

Recipe corrected against the live certification:
- `uv venv` + constrained `uv pip install` + `.venv/bin/python -m pytest`, instead
  of the inconsistent `--system` + `uv run --no-sync` pairing (uv run targets the
  project .venv, not the --system env).
- signalforge editable install carries --constraint (else protobuf 4->5 upgrade
  risks breaking Airflow 2.10.4); `-e .` until the skeleton child adds [airflow].
- pytest/pytest-cov/pytest-asyncio installed under constraints so --no-cov is valid.

Doc updated to match the verified recipe + records the two findings.
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0d049c3a-3969-46b7-8b96-c8dc4db7d708

📥 Commits

Reviewing files that changed from the base of the PR and between ee498ef and f204c4d.

📒 Files selected for processing (1)
  • tests/airflow/test_dag_parse.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/airflow/test_dag_parse.py

📝 Walkthrough

Walkthrough

Adds a complete Apache Airflow integration: an example DAG (signalforge_generate) that shells out to the signalforge CLI, maps exit codes to task states, and publishes XCom tier counts with a threshold gate. Introduces a gated airflow pytest marker, a constrained CI job, DAG-parse and live integration tests, operational docs, and a research contract doc.

Changes

Airflow Integration

Layer / File(s) Summary
Example signalforge_generate DAG
examples/airflow/signalforge_generate_dag.py
Implements _config (env/Variable resolution), _run_generate (CLI subprocess with 3600s timeout, JSON parsing, exit-code validation, and XCom tier counts), _gate (flagged threshold enforcement), and DAG wiring generate >> gate with manual scheduling.
pytest marker gating, Pyright exclusion, and .gitignore
pyproject.toml, .gitignore
Registers the airflow marker in pytest, excludes not airflow from default addopts, excludes tests/airflow from Pyright, and ignores .venv-airflow/ and .airflow-home/ scratch directories.
DAG-parse and live integration tests
tests/airflow/test_dag_parse.py
Adds pytestmark and runtime importorskip gating, _load_example_dag via DagBag.dags, test_example_dag_parses_without_import_errors asserting generate/gate task structure, _live_skip_reason helper, and test_generate_task_runs_live_against_demo gated by four environment variables with monkeypatch support.
Gated Airflow CI job
.github/workflows/ci.yml
Adds workflow_dispatch trigger and an airflow job gated by PR label or manual dispatch, installing Airflow 2.10.4 on Python 3.11 under the Apache constraints file before running marker-filtered pytest with --no-cov.
Airflow ops documentation and site wiring
docs/airflow-ops.md, mkdocs.yml, CHANGELOG.md
Adds full operational documentation (install, config, exit-code contract, XCom shape, scheduling, manual/UI triggering, CI tests, caveats), wires it into the MkDocs nav, and records it in the changelog.
Airflow test-environment research contract
docs/research/airflow-test-environment.md
Records the full test-environment contract: version floor (2.10.4 on Python 3.11), constraints-pinned install mechanism, marker gating strategy, CI job shape, local setup steps, fake-first vs live split, parse-leg DagBag.dags constraint, certification results, scope/deferrals, and maintainer checklist.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(135, 206, 235, 0.5)
    Note over pytest,DagBag: Parse test: verify DAG structure (no DB, no credentials)
    pytest->>test_dag_parse: collect -m airflow
    test_dag_parse->>test_dag_parse: importorskip("airflow")
    test_dag_parse->>_load_example_dag: load DAG via DagBag
    _load_example_dag->>DagBag: DagBag(dag_folder=examples/airflow, include_examples=False)
    DagBag-->>_load_example_dag: bag.dags["signalforge_generate"], import_errors={}
    _load_example_dag-->>test_dag_parse: DAG instance
    test_dag_parse->>test_dag_parse: assert tasks == {generate, gate}, generate upstream of gate
  end
  rect rgba(144, 238, 144, 0.5)
    Note over pytest,signalforge_CLI: Live test: invoke generate task (gated by SF_RUN_AIRFLOW + credentials)
    pytest->>test_dag_parse: test_generate_task_runs_live_against_demo
    test_dag_parse->>test_dag_parse: _live_skip_reason() → skip if env vars absent
    test_dag_parse->>test_dag_parse: create demo via copy_demo, set SF_PROJECT_DIR/SF_MODEL
    test_dag_parse->>_run_generate: invoke python_callable directly
    _run_generate->>signalforge_CLI: subprocess ["signalforge", "generate", "--format", "json"]
    signalforge_CLI-->>_run_generate: {model_unique_id, flagged, ok, total, ...}
    _run_generate-->>test_dag_parse: XCom payload dict
    test_dag_parse->>test_dag_parse: assert model_unique_id suffix, non-negative counts, required fields present
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • #229: This PR directly implements all deliverables specified in issue #229 — the airflow pytest marker, constraints-pinned CI job with workflow_dispatch, docs/research/airflow-test-environment.md contract, and the DAG-parse certification test with documented local setup steps.
  • #228: These changes implement the spike and test-harness deliverables from the Airflow integration epic #228, establishing the CI infrastructure, example DAG, version floor (2.10.4), marker gating, and test environment contract.
  • #232: This PR establishes the foundational Airflow integration with PythonOperator-based example DAGs and gated CI job, while issue #232 proposes a custom SignalForgeGenerateOperator class as a follow-up enhancement to wrap the same CLI invocation more robustly.
  • #230: This PR implements Airflow integration examples and tests that logically build upon the [airflow] optional-extra infrastructure and signalforge.airflow subpackage skeleton planned in #230.

Poem

🐇 Hoppity-hop through the DAG we go,
generate first, then gate in a row,
XCom counts flow where the flagged tiers grow,
Constraints pinned tight so the install won't blow —
The rabbit has merged what the scheduler will know! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the PR as implementing the Airflow test environment spike, matching the primary deliverable of establishing local and CI testing infrastructure.
Linked Issues check ✅ Passed All five objectives from issue #229 are met: Airflow version floor (2.10.4 on Python 3.11), constraints-based packaging with [airflow] extra, airflow pytest marker registration and gating, label-gated CI job implementation, and fake-first operator testing strategy with live E2E DAG gating.
Out of Scope Changes check ✅ Passed All changes are scoped to establishing the Airflow test environment: documentation, configuration, example DAG, workflow automation, and test infrastructure directly align with spike #229 objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands and usage tips.

@wjduenow wjduenow added the airflow Apache Airflow orchestration integration label Jun 15, 2026
@wjduenow
wjduenow marked this pull request as ready for review June 15, 2026 15:34
@wjduenow
wjduenow requested a review from Copilot June 15, 2026 15:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Establishes a reproducible, constraints-pinned Apache Airflow “spike” environment (local + CI) that later Airflow-integration work will certify against, without adding Airflow as a default dev/runtime dependency.

Changes:

  • Adds a gated airflow pytest marker + default deselection, and excludes tests/airflow from pyright.
  • Introduces a minimal example DAG and a gated DAG-parse certification test using DagBag.
  • Documents the full local/CI constraints-based install contract and adds a label-gated airflow CI job (plus workflow_dispatch) and local scratch .gitignore entries.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/airflow/test_dag_parse.py Adds a gated DagBag parse test to certify example DAG parsing under real Airflow.
pyproject.toml Registers airflow marker, excludes it from default pytest selection, and excludes tests/airflow from pyright.
examples/airflow/signalforge_spike_dag.py Adds a minimal “spike” DAG with an inline placeholder operator for parse certification.
docs/research/airflow-test-environment.md Records decisions/contract for constraints-pinned Airflow local setup + CI job shape.
.gitignore Ignores .venv-airflow/ and .airflow-home/ scratch directories.
.github/workflows/ci.yml Adds a label-gated airflow CI job and enables manual workflow_dispatch.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/airflow/test_dag_parse.py Outdated
Comment thread .github/workflows/ci.yml Outdated
Comment thread docs/research/airflow-test-environment.md Outdated
Comment thread .github/workflows/ci.yml Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

81-81: ⚡ Quick win

Guard PR-label access by event type in the job condition.

The current if reads PR-label fields even on non-PR events. Make the event-type check explicit first to avoid brittle expression evaluation across trigger types.

Suggested change
-    if: contains(github.event.pull_request.labels.*.name, 'airflow') || github.event_name == 'workflow_dispatch'
+    if: github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'airflow'))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 81, The job condition attempts to access
PR-label fields regardless of the event type, which can fail or behave
unexpectedly on non-PR events like workflow_dispatch. Reorder the condition to
check the event type first: add an explicit check for github.event_name ==
'pull_request' before accessing github.event.pull_request.labels.*.name, so that
PR-label evaluation only happens when the event is actually a pull_request
event. This prevents brittle expression evaluation across different trigger
types.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/research/airflow-test-environment.md`:
- Around line 70-77: The DEC-4 section header incorrectly describes the CI
gating as "path-filtered" when the actual implemented contract uses label-based
triggering with manual dispatch. Additionally, the checklist item about wiring
the CI job is now stale since this work has already been completed. Update the
header of the DEC-4 section to accurately reflect the label/manual trigger
mechanism rather than path filtering, update any checklist items related to
"wire the CI job" to reflect that this is now completed, and apply the same
corrections at line 285 where this contract is also referenced.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Line 81: The job condition attempts to access PR-label fields regardless of
the event type, which can fail or behave unexpectedly on non-PR events like
workflow_dispatch. Reorder the condition to check the event type first: add an
explicit check for github.event_name == 'pull_request' before accessing
github.event.pull_request.labels.*.name, so that PR-label evaluation only
happens when the event is actually a pull_request event. This prevents brittle
expression evaluation across different trigger types.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 53ad94e2-be42-4af5-80d5-16e4e3f9ed11

📥 Commits

Reviewing files that changed from the base of the PR and between d99390e and 7db7827.

📒 Files selected for processing (6)
  • .github/workflows/ci.yml
  • .gitignore
  • docs/research/airflow-test-environment.md
  • examples/airflow/signalforge_spike_dag.py
  • pyproject.toml
  • tests/airflow/test_dag_parse.py

Comment thread docs/research/airflow-test-environment.md Outdated
wjduenow added 2 commits June 15, 2026 09:02
Replaces the placeholder spike DAG with a real, parameterized example and the
user-facing ops surface (epic #228's example + docs).

- examples/airflow/signalforge_generate_dag.py: PythonOperator wrapping
  `signalforge generate` — four-tier exit-code -> task-state, graded tier counts
  -> XCom, a `gate` task that fails on a configurable flagged-count threshold.
  Config via Airflow Variables with env overrides; manual schedule by default.
  (Dedicated operators remain a roadmap drop-in swap; the contract is identical.)
- docs/airflow-ops.md + MkDocs nav: install-under-constraints, the example
  walkthrough, the result->task-state + XCom contract, gating, live env gates.
- tests/airflow/test_dag_parse.py: repoint the gated parse test at the shipped
  example; add a gated live `generate`-callable test (self-skips without
  SF_RUN_AIRFLOW + ANTHROPIC_API_KEY + GOOGLE_CLOUD_PROJECT + SF_RUN_BQ).
- CHANGELOG: Unreleased entry. Research doc references updated spike->generate.

Certified: parse test green under Airflow 2.10.4/py3.11; live test green against
the init-demo project (real BigQuery + Anthropic).
- tests/airflow: move pytest.importorskip out of module scope into each test so
  a default `uv run pytest` collection never imports Airflow (Copilot). The
  gated tests now deselect cleanly rather than collect-and-skip.
- ci.yml: guard the airflow job `if:` behind an explicit pull_request event so
  `github.event.pull_request.labels` is never evaluated on push events (Copilot).
- research doc: DEC-4 heading "path-filtered" -> "label-gated"; mark the
  "wire the CI job" checklist item done (Copilot + CodeRabbit — stale wording).
@wjduenow

Copy link
Copy Markdown
Owner Author

PR Review Summary

All five threads addressed — fixes in 231194f (+ PR description updated). No false positives.

Fixed (5 items)

File Issue Resolution
tests/airflow/test_dag_parse.py importorskip at module scope imports Airflow during default collection (Copilot) Moved pytest.importorskip("airflow") inside each test; default pytest now deselects without importing Airflow (matches the repo's e2e in-test gating).
.github/workflows/ci.yml job if: references github.event.pull_request.labels on push events (Copilot) Guarded behind explicit github.event_name == 'pull_request'; workflow_dispatch handled separately.
docs/research/airflow-test-environment.md (DEC-4 heading) "path-filtered" wording vs actual label-gating (Copilot) Heading → "label-gated job"; the body already described the label/dispatch trigger.
docs/research/airflow-test-environment.md (checklist) "wire the CI job" item stale — now implemented (CodeRabbit) Marked done; notes the label-gated job + workflow_dispatch, certified green.
PR description Stale "draft because CI job not wired" note (Copilot) Description rewritten to reflect the wired+green CI job and the shipped example DAG; PR is no longer draft.

False Positives (0)

None — every comment was a real, valid fix.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/airflow/test_dag_parse.py (1)

71-95: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Apply the required external-service test markers to the live test.

This test uses Anthropic, BigQuery, and subprocess execution, but it only inherits the airflow marker. Please add explicit markers (e2e, anthropic, bigquery, cli_subprocess) on this test to preserve the project’s gated-test contract.

As per coding guidelines, “Gated test markers (bigquery, anthropic, cli_subprocess, e2e, wheel_smoke, snowflake) must be applied to tests that require external services or subprocess execution; these tests are excluded by default in local runs and CI runs them with --no-cov.”

Suggested fix
+@pytest.mark.e2e
+@pytest.mark.cli_subprocess
+@pytest.mark.anthropic
+@pytest.mark.bigquery
 def test_generate_task_runs_live_against_demo(tmp_path: Path) -> None:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/airflow/test_dag_parse.py` around lines 71 - 95, The test function
test_generate_task_runs_live_against_demo is missing required external-service
markers that indicate it depends on Anthropic, BigQuery, and subprocess
execution. Add pytest.mark decorators with the markers e2e, anthropic, bigquery,
and cli_subprocess above the function definition (before the def line) to
properly gate this test and ensure it is excluded from local runs and marked for
special handling in CI as per the project's testing guidelines.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/airflow/signalforge_generate_dag.py`:
- Around line 131-136: The code attempts to convert threshold to an integer
without first validating that it contains a valid numeric value. Add validation
after the None check and before the comparison in the line with int(threshold)
to catch non-numeric values early. Wrap the int() conversion in a try-except
block or validate the threshold string beforehand, and when conversion fails,
raise or log a clear error message that identifies the configuration key
SF_MAX_FLAGGED and the invalid value provided, rather than letting a raw
ValueError propagate.
- Line 94: The subprocess.run() call invocation lacks a timeout parameter, which
can cause indefinite blocking if the CLI or dependencies hang. Add a timeout
parameter to the subprocess.run() call to prevent worker threads from being tied
up. The timeout value should be set to an appropriate duration that allows
normal execution while protecting against hangs, and consider wrapping the call
in a try-except block to handle subprocess.TimeoutExpired exceptions gracefully.

In `@tests/airflow/test_dag_parse.py`:
- Around line 85-86: Replace the direct os.environ mutations for SF_PROJECT_DIR
and SF_MODEL with monkeypatch.setenv calls to prevent environment variable state
leakage across tests. The monkeypatch fixture (which should be a parameter to
the test function) provides automatic cleanup, so instead of
os.environ["SF_PROJECT_DIR"] = str(project_dir), use
monkeypatch.setenv("SF_PROJECT_DIR", str(project_dir)), and similarly for the
SF_MODEL environment variable.

---

Outside diff comments:
In `@tests/airflow/test_dag_parse.py`:
- Around line 71-95: The test function test_generate_task_runs_live_against_demo
is missing required external-service markers that indicate it depends on
Anthropic, BigQuery, and subprocess execution. Add pytest.mark decorators with
the markers e2e, anthropic, bigquery, and cli_subprocess above the function
definition (before the def line) to properly gate this test and ensure it is
excluded from local runs and marked for special handling in CI as per the
project's testing guidelines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c9ceae1e-7039-4523-b7a0-7c22ecfc37e6

📥 Commits

Reviewing files that changed from the base of the PR and between 7db7827 and 8c8067a.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • docs/airflow-ops.md
  • docs/research/airflow-test-environment.md
  • examples/airflow/signalforge_generate_dag.py
  • mkdocs.yml
  • tests/airflow/test_dag_parse.py
✅ Files skipped from review due to trivial changes (3)
  • mkdocs.yml
  • CHANGELOG.md
  • docs/research/airflow-test-environment.md

Comment thread examples/airflow/signalforge_generate_dag.py Outdated
Comment thread examples/airflow/signalforge_generate_dag.py
Comment thread tests/airflow/test_dag_parse.py Outdated
- example DAG: bound the `signalforge` subprocess with timeout=3600 (+ raise
  AirflowFailException on TimeoutExpired) so a hung CLI can't tie up a worker.
- example DAG: validate signalforge_max_flagged / SF_MAX_FLAGGED as int, raising
  a clear AirflowFailException instead of a raw ValueError.
- live test: use monkeypatch.setenv instead of os.environ mutation (no env leak).
@wjduenow

Copy link
Copy Markdown
Owner Author

PR Review Summary (round 2)

CodeRabbit follow-up on the fix commit — 3 quick wins, all fixed in ee498ef. No false positives.

Fixed (3 items)

File Issue Resolution
examples/airflow/signalforge_generate_dag.py subprocess.run had no timeout — a hung CLI could tie up a worker Added timeout=3600 + AirflowFailException on TimeoutExpired
examples/airflow/signalforge_generate_dag.py int(threshold) on SF_MAX_FLAGGED threw a raw ValueError Wrapped in try/except → clear AirflowFailException ("must be an int")
tests/airflow/test_dag_parse.py direct os.environ mutation could leak across tests Switched to monkeypatch.setenv

False Positives (0)

None.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/airflow/test_dag_parse.py`:
- Around line 70-72: The test function test_generate_task_runs_live_against_demo
is missing required gated markers for its external service and subprocess
execution requirements. Add the appropriate pytest markers as decorators above
the function definition in addition to the existing airflow marker. Since this
is a live test that exercises external services and subprocess execution,
include markers such as cli_subprocess and e2e to properly gate the test
according to the coding guidelines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c7b8ec07-a750-4d6e-9dfb-a0053fa9ca95

📥 Commits

Reviewing files that changed from the base of the PR and between 231194f and ee498ef.

📒 Files selected for processing (2)
  • examples/airflow/signalforge_generate_dag.py
  • tests/airflow/test_dag_parse.py

Comment thread tests/airflow/test_dag_parse.py
Add external-service + subprocess gated markers (e2e/anthropic/bigquery/
cli_subprocess) to the live Airflow test per testing-signal.md's marker
convention. Applied per-function so the parse test stays airflow-only; the
`-m airflow` CI job still selects the live test (it self-skips without the env).
@wjduenow

Copy link
Copy Markdown
Owner Author

PR Review Summary (round 3)

CodeRabbit follow-up — 1 quick win, fixed. No false positives.

Fixed (1 item)

File Issue Resolution
tests/airflow/test_dag_parse.py live test missing external-service/subprocess markers (testing-signal.md) Added @pytest.mark.{e2e,anthropic,bigquery,cli_subprocess} to the live test (per-function, so the parse test stays airflow-only; the -m airflow job still selects it and it self-skips without the live env)

False Positives (0)

None.

@wjduenow
wjduenow merged commit 258f731 into dev Jun 15, 2026
7 checks passed
@wjduenow
wjduenow deleted the spike/229-airflow-test-environment branch June 15, 2026 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

airflow Apache Airflow orchestration integration

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants