Skip to content

fix: unified base path resolution semantics#2227

Merged
aknysh merged 3 commits intoaknysh/fix-import-not-foundfrom
osterman/base-path-resolution
Mar 18, 2026
Merged

fix: unified base path resolution semantics#2227
aknysh merged 3 commits intoaknysh/fix-import-not-foundfrom
osterman/base-path-resolution

Conversation

@osterman
Copy link
Copy Markdown
Member

@osterman osterman commented Mar 18, 2026

What

Establishes a unified convention for base path resolution where the meaning of a value is determined by its form and the context is determined by its source:

  • Empty ("") → smart defaults (git root search)
  • Dot (".", "./foo", "..", "../foo") → explicit anchor to "here" (context-dependent: config dir in atmos.yaml, CWD in env vars/CLI/provider)
  • Bare ("foo", "stacks") → search path (git root search, source-independent)
  • Absolute ("/path") → pass through unchanged

This eliminates source-dependent behavior for bare paths: ATMOS_BASE_PATH=stacks now goes through the same git root search as base_path: stacks in atmos.yaml.

Why

The previous PRD had inverted semantics for runtime sources (FR6), causing confusion about whether env vars should be CWD-relative or config-relative. The new convention is logically consistent:

  • Empty is absence of opinion
  • Dot is a signal ("here") that adapts to context (just like . works in every shell tool)
  • Bare paths are names to search, not location-relative references

This fixes issues #2183 and #1858 while preserving the "run from anywhere" behavior that users expect.

Testing

  • 8 new test functions covering source-aware resolution
  • All existing tests pass
  • config package tests: 100% passing

References

Stacked on #2215 (aknysh/fix-import-not-found).
Fixes #2183 (Spacelift scenario).
Fixes #1858 (ATMOS_CLI_CONFIG_PATH with empty base_path).

Summary by CodeRabbit

  • Bug Fixes

    • Fixed import resolution errors caused by base path configuration and Git root discovery
    • Enhanced error messages with additional context and guidance for path resolution failures
  • New Features

    • Improved base path resolution to correctly distinguish between runtime-provided and configuration-file-sourced paths
    • Better handling of relative and absolute paths with appropriate resolution logic per source type

…ndent semantics

Establish a unified convention where:
- Empty ("") = smart defaults (git root search)
- Dot (".", "./foo", "..", "../foo") = explicit anchor, context-dependent
  - In atmos.yaml: anchor to config directory (config-file convention)
  - In env var/CLI/provider: anchor to CWD (shell convention)
- Bare ("foo", "stacks") = search path, source-independent (always git root search)
- Absolute ("/path") = pass through unchanged

This eliminates the incongruence where "" and "." were treated identically or where
the same bare path value behaved differently based on source. The convention is now
unambiguous: empty is absence of opinion, dot is contextual anchor, bare is a search.

## Changes

**Core implementation:**
- Add `BasePathSource` field to `AtmosConfiguration` to track source (runtime vs config)
- Make `resolveAbsolutePath()` source-aware: dot-prefix resolves to CWD for runtime, config dir for config
- Mark source as "runtime" when base_path comes from env var, CLI flag, or provider parameter
- Remove `resolveSimpleRelativeBasePath()` — no longer needed with source-aware resolution
- Extract `resolveDotPrefixPath()` helper to reduce cyclomatic complexity
- Bare paths continue through git root search regardless of source

**Testing:**
- Add comprehensive tests proving dot-prefix is source-dependent, bare paths are not
- Update existing tests to reflect new convention (ATMOS_BASE_PATH=. now goes to CWD)
- All tests pass; no regressions

**Documentation:**
- Rewrite PRD with 4-category classification (Empty/Dot/Bare/Absolute)
- Add 18-row comprehensive examples table showing all scenarios
- Include source-awareness explanation and consistency proof
- Update fix documentation to reference source-aware resolution

Fixes #2183 (Tyler Rankin's ATMOS_BASE_PATH=.terraform/modules/monorepo scenario).
Fixes #1858 (empty base_path with ATMOS_CLI_CONFIG_PATH).
Stacked onto PR #2215 (aknysh/fix-import-not-found).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@osterman osterman requested a review from a team as a code owner March 18, 2026 03:06
@github-actions github-actions bot added the size/l Large size PR label Mar 18, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 18, 2026

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

@osterman osterman changed the base branch from main to aknysh/fix-import-not-found March 18, 2026 03:17
@osterman osterman added the patch A minor, backward compatible change label Mar 18, 2026
@mergify mergify bot added the stacked Stacked label Mar 18, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 18, 2026

Caution

Review failed

Failed to post review comments

📝 Walkthrough

Walkthrough

This PR addresses "failed to find import" errors by implementing source-aware base path resolution. It introduces a BasePathSource field to track whether base paths originate from runtime sources (env vars/CLI) or config files, adds dot-prefix resolution anchored to source-appropriate directories, enhances error messaging for glob matching failures, and provides comprehensive documentation and test coverage for the new semantics.

Changes

Cohort / File(s) Summary
Documentation
docs/fixes/2026-03-17-failed-to-find-import-base-path-resolution.md, docs/prd/base-path-resolution-semantics.md
Introduces formal base path resolution semantics with category-driven classification (Empty, Dot, Bare, Absolute), source tracking, and resolution algorithm. Documents root cause of import failures and interim fixes.
Schema
pkg/schema/schema.go
Added BasePathSource field to AtmosConfiguration struct to track whether base path originates from runtime sources or config file.
Core Path Resolution
pkg/config/config.go
Enhanced resolveAbsolutePath with new source parameter to enable source-aware path resolution. Added resolveDotPrefixPath helper for dot-prefixed paths anchored to source-appropriate directories. Updated initialization to mark runtime-sourced base paths.
Error Handling
pkg/config/utils.go, pkg/utils/glob_utils.go
Enriched error messages for glob matching failures with structured context and user-facing hints about base_path correctness and relative path handling.
Tests
pkg/config/base_path_resolution_test.go, pkg/config/config_test.go
New comprehensive test file covering dot-slash, absolute, empty, and bare path resolution across sources. Updated existing tests to reflect runtime semantics for dot-prefixed paths resolved from shell context.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • #1773 — Both modify base_path resolution and git-root discovery behavior, affecting the same core path resolution logic.
  • #1941 — Both introduce source-aware base path resolution semantics and modify git-root discovery behavior in configuration initialization.
  • #2029 — Both address base path and component path resolution relative to AtmosBasePath and source-dependent anchoring.

Suggested labels

minor

Suggested reviewers

  • aknysh
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: unified base path resolution semantics' directly and concisely summarizes the main objective of the PR: establishing a unified convention for resolving base paths based on their form and source.
Linked Issues check ✅ Passed Changes directly address both issues: #2183 (failed to find import in affected workflows) and #1858 (stack validation in CI pipelines) by implementing source-aware path resolution, ensuring bare paths use git-root search, and handling dot-prefixed paths differently for runtime vs config sources.
Out of Scope Changes check ✅ Passed All changes align with the stated objectives: documentation updates formalize the resolution convention, test files cover new resolution scenarios, and code changes implement source-aware path resolution with enhanced error handling. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 90.48% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch osterman/base-path-resolution
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Update CLI test cases to use correct relative paths (e.g., "../..") instead
of "." for ATMOS_BASE_PATH env var, since env vars are now "runtime" source
and "." resolves to CWD (shell convention) not config dir.

Add unit tests for source-aware resolution: dot-prefix fallback, absolute
pass-through, bare path without git root, and BasePathSource tracking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 18, 2026

Codecov Report

❌ Patch coverage is 80.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.88%. Comparing base (c984616) to head (d75fe97).
⚠️ Report is 7 commits behind head on aknysh/fix-import-not-found.

Files with missing lines Patch % Lines
pkg/config/config.go 77.77% 2 Missing and 2 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@                       Coverage Diff                       @@
##           aknysh/fix-import-not-found    #2227      +/-   ##
===============================================================
- Coverage                        76.88%   76.88%   -0.01%     
===============================================================
  Files                             1001     1001              
  Lines                            95412    95411       -1     
===============================================================
- Hits                             73354    73353       -1     
+ Misses                           17789    17788       -1     
- Partials                          4269     4270       +1     
Flag Coverage Δ
unittests 76.88% <80.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
pkg/config/utils.go 88.05% <100.00%> (+0.03%) ⬆️
pkg/schema/schema.go 87.70% <ø> (ø)
pkg/config/config.go 69.41% <77.77%> (-1.14%) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Use t.TempDir() instead of constructing path from filepath.Separator,
which lacks a drive letter on Windows and isn't recognized as absolute.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aknysh aknysh merged commit f8a6044 into aknysh/fix-import-not-found Mar 18, 2026
51 checks passed
@aknysh aknysh deleted the osterman/base-path-resolution branch March 18, 2026 06:53
aknysh added a commit that referenced this pull request Mar 18, 2026
…lative paths (#2215)

* fix: resolve explicit base paths relative to CWD, not git root

When ATMOS_BASE_PATH env var, --base-path flag, or atmos_base_path
provider parameter provides a simple relative path (e.g.,
".terraform/modules/monorepo"), resolve it relative to CWD instead
of routing through git root discovery. This restores pre-v1.202.0
behavior for explicitly set paths while preserving git root
discovery for default/empty base paths.

Two-pronged fix:
1. configAndStacksInfo.AtmosBasePath (provider/CLI): convert to
   absolute CWD-relative immediately in InitCliConfig
2. ATMOS_BASE_PATH env var (via Viper): tryResolveWithGitRoot now
   validates the git-root-joined path exists before using it, and
   falls back to CWD-relative resolution if it doesn't

Also improves error messages for "failed to find import" using
the error builder pattern with actionable hints and context.

Fixes #2183

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add git root discovery compatibility tests and update fix doc

Verify that the os.Stat fallback in tryResolveWithGitRoot doesn't break
"run Atmos from any subdirectory" behavior. Add table-driven tests for
resolveSimpleRelativeBasePath helper. Document git root discovery
compatibility analysis with integration test evidence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review feedback and CI test failure

- Distinguish os.Stat ENOENT from permission/I/O errors in
  tryResolveWithGitRoot using os.IsNotExist() checks
- Use error builder pattern (ErrStatFile, ErrPathResolution) for
  non-ENOENT failures instead of silently falling back
- Remove WithExitCode(2) from error builders — restores default exit
  code 1, fixing helmfile apply non-existent CI test
- Replace hardcoded Unix path in test with filepath.Abs()
- Rewrite TestTryResolveWithGitRoot_ExistingPathAtGitRoot to actually
  call resolveAbsolutePath instead of just checking os.Stat preconditions
- Add TestTryResolveWithGitRoot_CWDFallback covering the core fix path
- Add TestTryResolveWithGitRoot_NeitherExists for fallback to git root

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: reconcile base path resolution PRD with source-dependent semantics

Add FR6 (Runtime Source Resolution) formalizing that --base-path,
ATMOS_BASE_PATH env var, and atmos_base_path provider parameter resolve
simple relative paths relative to CWD, not git root. Document the
design rationale for source-dependent resolution, add runtime test
cases, implementation details, and Issue #2183 resolution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: fix CWD fallback test to run inside git repo for proper coverage

The CWDFallback test was creating a temp dir outside the git repo,
causing getGitRootOrEmpty() to return "" and skip the os.Stat fallback
logic entirely. Now creates the test dir inside the repo so git root
discovery works, increasing tryResolveWithGitRoot coverage from 76%
to 84%.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: update Go dependencies to latest compatible versions

Updated: anthropic-sdk-go v1.27.0, googleapis/gax-go v2.19.0,
google.golang.org/api v0.272.0, modernc.org/sqlite v1.47.0,
google.golang.org/genproto 20260316, charmbracelet/x/exp/slice
20260316.

Pinned: gocloud.dev v0.41.0 and go-fsimpl v0.3.1 (gomplate/v3
incompatible with newer versions due to s3blob.URLOpener changes).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [autofix.ci] apply automated fixes

* fix: unified base path resolution semantics (#2227)

* docs(prd): formalize base path resolution convention with source-dependent semantics

Establish a unified convention where:
- Empty ("") = smart defaults (git root search)
- Dot (".", "./foo", "..", "../foo") = explicit anchor, context-dependent
  - In atmos.yaml: anchor to config directory (config-file convention)
  - In env var/CLI/provider: anchor to CWD (shell convention)
- Bare ("foo", "stacks") = search path, source-independent (always git root search)
- Absolute ("/path") = pass through unchanged

This eliminates the incongruence where "" and "." were treated identically or where
the same bare path value behaved differently based on source. The convention is now
unambiguous: empty is absence of opinion, dot is contextual anchor, bare is a search.

## Changes

**Core implementation:**
- Add `BasePathSource` field to `AtmosConfiguration` to track source (runtime vs config)
- Make `resolveAbsolutePath()` source-aware: dot-prefix resolves to CWD for runtime, config dir for config
- Mark source as "runtime" when base_path comes from env var, CLI flag, or provider parameter
- Remove `resolveSimpleRelativeBasePath()` — no longer needed with source-aware resolution
- Extract `resolveDotPrefixPath()` helper to reduce cyclomatic complexity
- Bare paths continue through git root search regardless of source

**Testing:**
- Add comprehensive tests proving dot-prefix is source-dependent, bare paths are not
- Update existing tests to reflect new convention (ATMOS_BASE_PATH=. now goes to CWD)
- All tests pass; no regressions

**Documentation:**
- Rewrite PRD with 4-category classification (Empty/Dot/Bare/Absolute)
- Add 18-row comprehensive examples table showing all scenarios
- Include source-awareness explanation and consistency proof
- Update fix documentation to reference source-aware resolution

Fixes #2183 (Tyler Rankin's ATMOS_BASE_PATH=.terraform/modules/monorepo scenario).
Fixes #1858 (empty base_path with ATMOS_CLI_CONFIG_PATH).
Stacked onto PR #2215 (aknysh/fix-import-not-found).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: update tests for source-aware base path resolution

Update CLI test cases to use correct relative paths (e.g., "../..") instead
of "." for ATMOS_BASE_PATH env var, since env vars are now "runtime" source
and "." resolves to CWD (shell convention) not config dir.

Add unit tests for source-aware resolution: dot-prefix fallback, absolute
pass-through, bare path without git root, and BasePathSource tracking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use real absolute path in test for Windows compatibility

Use t.TempDir() instead of constructing path from filepath.Separator,
which lacks a drive letter on Windows and isn't recognized as absolute.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Co-authored-by: aknysh <andriy.knysh@gmail.com>

* fix: use error builder pattern for all path resolution errors

Replace raw fmt.Errorf calls in resolveDotPrefixPath, tryResolveWithGitRoot,
and tryResolveWithConfigPath with errUtils.Build(errUtils.ErrPathResolution)
for consistent error classification via errors.Is().

Update fix doc to reflect source-aware resolution with BasePathSource
tracking instead of the old pre-normalize approach.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: consolidate path resolution error handling to improve coverage

Extract absPathOrError() helper to consolidate 7 identical filepath.Abs
error builder patterns into one function. Remove dead isExplicitRelative
parameter and code block from tryResolveWithGitRoot (unreachable since
dot-prefixed paths are routed to resolveDotPrefixPath before reaching it).

resolveDotPrefixPath: 80% → 100%, tryResolveWithConfigPath: 80% → 100%.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add simple pseudocode and quick-reference to base path PRD

Restore the simple if/else pseudocode block (per review feedback) and add
a comprehensive quick-reference showing resolution examples for config-file
source, runtime source, and no-git-repo scenarios. Both blocks validated
against the implementation in pkg/config/config.go.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <erik@cloudposse.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch A minor, backward compatible change size/l Large size PR stacked Stacked

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error: failed to find import Stack validation failing in pipelines in Atmos 1.201.0

2 participants