Skip to content

feat(config): scope-split settings precedence; project config.toml support#608

Merged
jdx merged 6 commits into
mainfrom
claude/zealous-swanson-d273fa
May 11, 2026
Merged

feat(config): scope-split settings precedence; project config.toml support#608
jdx merged 6 commits into
mainfrom
claude/zealous-swanson-d273fa

Conversation

@jdx

@jdx jdx commented May 11, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #601. Three coupled changes that establish a principled, locality-preserving settings precedence:

  1. aube config set/delete no longer edits .npmrc. For aube-known keys, writes land in ~/.config/aube/config.toml (user) or the new <cwd>/.config/aube/config.toml (project). The shared .npmrc is preserved for npm/pnpm/yarn.

  2. <cwd>/.config/aube/config.toml is a new project-scope source. Mirrors the XDG layout used at user-scope. It's an alternative to committing aube-specific settings into a project .npmrc that other package managers also read.

  3. Settings precedence is split by scope. The full default file-source chain, high-to-low:

    cli > env
        > project_aube_config (<cwd>/.config/aube/config.toml)
        > project_npmrc       (<cwd>/.npmrc + npmrcAuthFile)
        > user_aube_config    (~/.config/aube/config.toml)
        > user_npmrc          (~/.npmrc + pnpm auth.ini)
        > workspace_yaml
    

    Two principles: scope locality (project beats user) and aube authority within a scope (aube's config beats .npmrc).

Implementation

  • ResolveCtx now exposes {project,user}_{aube_config,npmrc} slices instead of merged npmrc/aube_config fields.
  • New aube_registry::config::load_npmrc_entries_split(cwd) returns user+project halves; reuses the existing tagged loader internally so no duplicate parsing.
  • New commands::FileSources::load(cwd) helper consolidates the four-source loading boilerplate that 18 call sites used to inline.
  • Per-setting precedence overrides in settings.toml keep working: bare npmrc / aubeConfig names expand to their project+user pair (project first); scope-qualified projectNpmrc / userNpmrc / projectAubeConfig / userAubeConfig are available for fine-grained control.

Test plan

  • cargo build
  • cargo clippy --all-targets -- -D warnings
  • cargo fmt --check
  • cargo test --workspace — 1507 passed, 0 failed
  • mise run test:bats test/config.bats — 38/38 (4 new tests covering project config.toml + scope locality)
  • mise run test:bats test/minimum_release_age.bats — 5/5 (per-setting precedence override still wired)
  • mise run test:bats test/settings.bats — 18/18
  • New Rust tests in values.rs: user_aube_config_wins_over_user_npmrc_by_default, project_npmrc_wins_over_user_aube_config_by_default, project_aube_config_wins_over_project_npmrc_by_default
  • New bats tests: config set --location project writes aube-owned keys to project config.toml, config set --location project writes unknown keys to ./.npmrc, config get prefers project config.toml over project .npmrc, config get prefers project npmrc over user config.toml

This PR was generated by Claude.


Note

Medium Risk
Changes settings-resolution precedence across the CLI and redirects config set/delete writes for known keys away from .npmrc, which can affect effective configuration in existing projects/users.

Overview
Reworks settings resolution to split file-based sources by scope (project vs user) and apply a new default precedence: project config.toml > project .npmrc > workspace YAML > user config.toml > user .npmrc (still under cli > env).

Adds project-scope aube config at <cwd>/.config/aube/config.toml, updates the settings accessor generator to understand scope-qualified precedence names (with npmrc/aubeConfig aliases expanding to project+user), and introduces load_npmrc_entries_split plus a FileSources helper to wire the new ResolveCtx through many commands.

Updates aube config get/list/set/delete behavior: known settings now write/delete in config.toml (or workspace YAML for supported keys when no project config exists) and no longer modify .npmrc, with improved errors for stale .npmrc entries; docs and bats/unit tests are updated accordingly.

Reviewed by Cursor Bugbot for commit 09b8604. Bugbot is set up for automated code reviews on this repo. Configure here.

`aube config set <known-key>` was deleting matching aliases from
`~/.npmrc` so the new value in `~/.config/aube/config.toml` wouldn't be
shadowed by the lower-precedence (then) `.npmrc`. That silently broke
`script-shell` and any other setting shared with npm/pnpm/yarn.

Flip the default file precedence to `aubeConfig > npmrc > workspaceYaml`
so aube's own config wins, then drop the stale-alias cleanup from
`config set` and the symmetric cleanup from `config delete`. Per-setting
`precedence` overrides in `settings.toml` (e.g. `minimumReleaseAge`)
continue to apply.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR establishes a principled, scope-aware settings precedence for aube, splitting the previously merged ResolveCtx.npmrc/aube_config fields into four scoped slices (project_aube_config, project_npmrc, user_aube_config, user_npmrc) and adding <cwd>/.config/aube/config.toml as a new project-scope config source.

  • Precedence chain (high→low): cli > env > project_aube_config > project_npmrc > workspace_yaml > user_aube_config > user_npmrc — enforcing scope locality (project beats user) and aube authority within a scope (aube config beats .npmrc). The resolve_precedence code-generator is updated to expand bare npmrc/aubeConfig aliases into their scope-qualified pairs for backward compatibility with existing per-setting overrides.
  • config set/delete no-touch policy on .npmrc: known keys now write exclusively to config.toml (user or project); the delete path sweeps both workspace yaml and project config.toml to prevent silent resurrection when a value exists in both files; stale-.npmrc error surfaces the correct file and gives actionable guidance.
  • FileSources helper consolidates the four-slice loading boilerplate that was inlined at ~18 call sites, reducing structural duplication while correctly plumbing project aube config into every command that previously only read user-scope config.

Confidence Score: 5/5

Safe to merge — the core precedence logic is correct, exhaustively tested, and the refactor is mechanically consistent across all call sites.

The precedence ordering is verified at three independent layers: the code-generator in build.rs, the generated ResolveCtx accessors, and manual lookups in settings.rs and main.rs — all agree. The NpmrcSource match in load_npmrc_entries_split is exhaustive (five variants, all handled). The dual-sweep in delete and the once-config.toml-exists guard in aube_config_target both prevent the known resurrection edge cases. Test coverage is thorough: 3 new Rust unit tests and 14 new bats tests cover every new code path including regression cases from prior review rounds.

No files require special attention.

Important Files Changed

Filename Overview
crates/aube-registry/src/config.rs Adds load_npmrc_entries_split with a process-scoped OnceLock<Mutex<HashMap>> cache keyed on project_dir; match on NpmrcSource is exhaustive (5 variants) and correctly assigns User/PnpmAuth → user and Project/NpmrcAuthFile → project.
crates/aube-settings/build.rs Updates resolve_precedence to expand bare npmrc/aubeConfig aliases into scope-qualified pairs; default file order correctly encodes scope-locality and aube-authority principles.
crates/aube-settings/src/values.rs Splits ResolveCtx.npmrc/aube_config into four scoped fields; files_only helper maps the incoming slice to project_npmrc for backward-compatibility with existing callers; new unit tests cover all cross-scope precedence assertions.
crates/aube/src/commands/mod.rs Introduces FileSources struct that owns the four slices and exposes a ctx() helper; cleanly replaces ~18 boilerplate call sites; read_merged low-to-high order correctly mirrors the resolver precedence.
crates/aube/src/commands/config/set.rs Refactors set_value to route aube-known keys to aube_config_target; once-config.toml-exists logic prevents silent shadowing; .yaml extension check guards the workspace-yaml branch correctly.
crates/aube/src/commands/config/delete.rs Project-scope delete sweeps both workspace yaml and project config.toml; stale-npmrc error path uses location.path() to surface the correct file per scope.
crates/aube/src/commands/install/settings.rs resolve_resolution_mode and merge_json_object_setting updated to walk sources in correct low-to-high order matching the declared default precedence.
test/config.bats Adds 14 new bats tests covering project config.toml writes, workspace-yaml routing, scope-locality assertions, sweep-both-on-delete regression, and stale-npmrc error message.
crates/aube/src/commands/config/mod.rs Adds read_workspace_yaml_flat helper; exports load_project_aube_config_entries; read_merged low-to-high order accurately mirrors the resolver.

Reviews (5): Last reviewed commit: "fix(settings): rank workspace yaml above..." | Re-trigger Greptile

Comment thread crates/aube/src/commands/config/delete.rs Outdated
Settings precedence is split by scope so project-scope entries (project
`.npmrc` and the new project `<cwd>/.config/aube/config.toml`) outrank
user-scope entries, and within a scope aube's own config file outranks
`.npmrc`. The full default chain is:

  cli > env
      > project_aube_config (<cwd>/.config/aube/config.toml)
      > project_npmrc       (<cwd>/.npmrc + npmrcAuthFile)
      > user_aube_config    (~/.config/aube/config.toml)
      > user_npmrc          (~/.npmrc + pnpm auth.ini)
      > workspace_yaml

`aube config set/delete --location project` for aube-known keys now
writes to `<cwd>/.config/aube/config.toml` instead of project `.npmrc`,
mirroring the user-scope behavior introduced previously. The shared
`.npmrc` is left alone so it can stay coordinated with npm/pnpm/yarn.

`ResolveCtx` is split into `{project,user}_{aube_config,npmrc}` slices
and a `FileSources::load(cwd)` helper in `commands/mod.rs` consolidates
the loading boilerplate. Per-setting `precedence` overrides in
`settings.toml` keep working: bare `npmrc` / `aubeConfig` names expand
to their project+user pair (project first).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jdx jdx changed the title fix(config): stop clobbering ~/.npmrc on set; config.toml outranks npmrc feat(config): scope-split settings precedence; project config.toml support May 11, 2026
jdx and others added 2 commits May 11, 2026 11:07
A user upgrading from a pre-#517 aube has aube-owned keys still living
in `~/.npmrc` (where old aube used to write them). `aube config delete
<key>` would then fail with a bare "not set in config.toml", with no
hint about the active value in `~/.npmrc`.

aube still doesn't modify `.npmrc` for aube-known keys — it's shared
with npm/pnpm/yarn, which is the whole point of #601. But the error now
surfaces the stale entry's location and tells the user to edit it
manually (or override it from `config.toml` via `aube config set`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a project already has `pnpm-workspace.yaml` or `aube-workspace.yaml`,
`aube config set/delete --location project` now writes the aube-known
setting into that existing file instead of creating a separate
`<cwd>/.config/aube/config.toml`. Keeps the per-project config story to
a single file when one already exists. Format and surrounding comments
are preserved via `aube_manifest::workspace::edit_workspace_yaml`.

Settings without a `workspace_yaml` source (e.g. `scriptShell`) still
fall back to project `config.toml` — there's no point writing a value
the resolver wouldn't read back. User-scope writes are unchanged and
keep landing in `~/.config/aube/config.toml`.

`aube config get/list` now also surfaces flat-scalar entries from the
workspace yaml so a value just written there round-trips through the
display commands. Nested mappings (catalogs, allowBuilds, …) stay
hidden — they don't fit the simple `(key, raw)` view.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread crates/aube/src/commands/config/delete.rs
Comment thread crates/aube-registry/src/config.rs
Two coupled fixes from review feedback on #608:

`aube config delete --location project` used to short-circuit after
removing the key from the workspace yaml, even when the same key also
lived in project `config.toml`. The config.toml copy then silently
became the active value again — the opposite of what the user asked
for. Delete now sweeps both files (and errors when neither held the
key).

`aube config set --location project` only routes to the workspace yaml
when no project `config.toml` exists. Once `config.toml` has been
adopted, all project-scope writes stay there — otherwise a yaml write
would be silently shadowed by the higher-precedence config.toml entry
on read. This matches the user's "if it exists and no config exists"
intent.

Also splits a fused docstring in aube-registry: the new
`load_npmrc_entries_split` doc block was directly adjacent to the
existing `load_npmrc_entries` doc, so rustdoc attached the merged
text to the new function and the old function lost its doc entirely.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 062f89c. Configure here.

Comment thread crates/aube/src/commands/config/set.rs
`workspace_yaml` (`pnpm-workspace.yaml` / `aube-workspace.yaml`) lives
at the project root, so by the scope-locality principle it must
outrank both `~/.npmrc` and `~/.config/aube/config.toml`. The previous
default left it at the bottom of the file chain, which meant a project
write routed to the workspace yaml could be silently shadowed by any
user-scope setting on the same key — directly contradicting the
PR's own "project beats user" guarantee.

New default file precedence, high-to-low:
  project_aube_config > project_npmrc > workspace_yaml >
  user_aube_config   > user_npmrc

Per-setting `precedence` overrides in `settings.toml` are unchanged
in effect — `minimumReleaseAge` still pulls workspace_yaml first via
its own override; the only difference is where unspecified sources
fall in the append-missing tail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jdx jdx merged commit 4ee8f43 into main May 11, 2026
17 checks passed
@jdx jdx deleted the claude/zealous-swanson-d273fa branch May 11, 2026 17:00
@greptile-apps greptile-apps Bot mentioned this pull request May 11, 2026
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