Skip to content

otto: stop setting AF_CONFIG=/dev/null in the no-Airflow fallback#2121

Open
jlaneve wants to merge 3 commits into
mainfrom
drop-af-config-devnull
Open

otto: stop setting AF_CONFIG=/dev/null in the no-Airflow fallback#2121
jlaneve wants to merge 3 commits into
mainfrom
drop-af-config-devnull

Conversation

@jlaneve
Copy link
Copy Markdown
Contributor

@jlaneve jlaneve commented May 4, 2026

Summary

The else branch in pkg/otto/config.go:BuildEnv set AF_CONFIG=/dev/null when otto couldn't match the cwd to a running astro-dev project. That blanked af's config view to prevent silent fallthrough to a globally-scoped current-instance that often pointed at whatever project the user last ran astro dev start in.

af 0.8.0 (shipped in astro-airflow-mcp 0.8.0, picked up by Otto 0.1.3) introduced a layered config: project-local > project-shared > global, with project root discovered by walking up from cwd looking for .astro/. That makes the sentinel counterproductive in two cases:

  1. Inside a project, no astro-dev running. <project>/.astro/config.yaml may have the team's committed deployment inventory (the headline use case for the layered config). Today the model sees "no instances configured" and dead-ends; without the sentinel it sees the team's staging/prod/etc. and can answer questions like "what DAGs are failing on staging?".
  2. Outside any project. The resolved global current-instance is typically a sensible default ("the Airflow I was last working with"), and surfacing the resolved instance to the user is a better safety net than blanking the config.

Changes

  • pkg/otto/config.go: drop the else { set("AF_CONFIG", os.DevNull) } block. Comment block explains the new contract: af owns instance resolution; otto's env-var injection still wins for the "active" Airflow when one was detected.
  • pkg/otto/config_test.go: flip TestBuildEnv_SkipsEmptyValues to assert AF_CONFIG is NOT set in the no-Airflow case.

Why this is safe

  • AIRFLOW_API_URL injection is unchanged. When otto detects a project Airflow, env vars beat per-instance config in af 0.8.0, so otto's deployment selection still wins for actual API calls.
  • The "stale global pointer surprise" risk is real but small: af 0.8.0's project-aware resolution already does the right thing inside a project (project-local current-instance shadows global). It only matters when running astro otto from a non-project dir with a stale global pointer — a case better solved by the otto-side change to surface the resolved instance in the UI.

Test plan

  • go test ./pkg/otto/... passes (9 tests in ConfigSuite)
  • Updated TestBuildEnv_SkipsEmptyValues to assert the new behavior
  • Manual verification: astro otto from a non-project dir, ask "list my Airflow instances" → returns the layered-config view instead of "no instances"

Follow-ups (separate PRs)

  1. Otto: surface the resolved af instance in the startup banner so silent picks become explicit
  2. Agents (skill): nudge the model to disclose which instance it's hitting on the first af call of a session

🤖 Generated with Claude Code

…ected

The previous behavior blanked af's config view in the fallback case to
keep an unrelated global current-instance from leaking in. af 0.8.0's
layered config (project-local > project-shared > global) makes that
guard counterproductive: it hides the team's committed deployment
inventory in `<project>/.astro/config.yaml` from the model, so questions
like "what DAGs are failing on staging?" hit a dead-end "no instances
configured" wall even though the user has staging defined.

Drop the sentinel. af's project-aware resolution already does the right
thing inside a project, and outside one the resolved global instance is
generally what the user wants ("the deployment I last worked on" is a
reasonable default for an open-ended question).

The remaining risk — silently picking up a stale global current-instance
when running `astro otto` from a non-project dir — is better mitigated
by surfacing the resolved instance in Otto's UI (separate change in the
otto repo) than by blanking the config.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jlaneve jlaneve requested a review from a team as a code owner May 4, 2026 16:51
@coveralls-official
Copy link
Copy Markdown

coveralls-official Bot commented May 4, 2026

Coverage Report for CI Build 0

Coverage decreased (-0.005%) to 39.701%

Details

  • Coverage decreased (-0.005%) from the base build.
  • Patch coverage: 3 of 3 lines across 1 file are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 65771
Covered Lines: 26112
Line Coverage: 39.7%
Coverage Strength: 9.38 hits per line

💛 - Coveralls

jlaneve and others added 2 commits May 4, 2026 13:02
Drop the multi-line "previously we did X..." narrative; keep a tight
WHY explaining the current contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both checks pinned the absence of an env var that the implementation
no longer touches anywhere, which is something you could write for any
of the env vars BuildEnv doesn't set. Not a useful regression guard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant