Files
cleveragents-core/features/config_three_scope.feature
freemo 7b3fcaf466
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 19s
CI / helm (pull_request) Successful in 20s
CI / lint (pull_request) Successful in 3m19s
CI / quality (pull_request) Successful in 3m49s
CI / typecheck (pull_request) Successful in 3m57s
CI / integration_tests (pull_request) Successful in 3m57s
CI / security (pull_request) Successful in 4m6s
CI / unit_tests (pull_request) Successful in 7m19s
CI / docker (pull_request) Successful in 1m18s
CI / coverage (pull_request) Successful in 11m53s
CI / e2e_tests (pull_request) Successful in 21m25s
CI / status-check (pull_request) Successful in 1s
CI / build (push) Successful in 18s
CI / helm (push) Successful in 22s
CI / lint (push) Successful in 3m16s
CI / quality (push) Successful in 3m41s
CI / integration_tests (push) Successful in 3m48s
CI / typecheck (push) Successful in 3m54s
CI / unit_tests (push) Successful in 3m54s
CI / security (push) Successful in 4m4s
CI / docker (push) Successful in 1m19s
CI / benchmark-regression (push) Has been skipped
CI / e2e_tests (push) Successful in 14m58s
CI / coverage (push) Successful in 11m55s
CI / status-check (push) Successful in 1s
CI / benchmark-publish (push) Successful in 28m23s
CI / benchmark-regression (pull_request) Successful in 54m47s
feat(config): implement three-scope config resolution with local config file support
Implement three-scope configuration resolution (local > project > global)
with deep merge. Add ConfigScope enum, project-root discovery, and
config.local.toml file support.

Changes:
- Add LOCAL to ConfigLevel enum and new ConfigScope enum (GLOBAL, PROJECT, LOCAL)
- Implement discover_project_root() walking up from CWD for cleveragents.toml/.cleveragents
- Implement config.local.toml file loading in ConfigService
- Implement three-scope deep merge via _deep_merge() helper
- Add read_project_config(), read_local_config(), read_merged_config() methods
- Add write_scoped_config() for writing to any scope file
- Update set_value() to accept optional scope parameter
- Update resolve() with six-level precedence chain (CLI > env > local > project > global > default)
- Update CLI config set with --scope flag (global/project/local)
- Add config.local.toml to .gitignore and DEFAULT_IGNORE_PATTERNS
- Add Behave BDD scenarios for three-scope resolution, deep merge, and project-root discovery

ISSUES CLOSED: #937
2026-03-30 21:48:01 +00:00

120 lines
6.6 KiB
Gherkin

Feature: Three-scope configuration resolution
As a developer working on a CleverAgents project
I want configuration to resolve across global, project, and local scopes
So that I can have developer-specific overrides without affecting shared config
Background:
Given a clean three-scope config environment
# ── Scope precedence ──────────────────────────────────────────────────
Scenario: Local scope overrides project scope
Given a project config value "core.automation-profile" = "supervised"
And a local config value "core.automation-profile" = "manual"
When I resolve three-scope key "core.automation-profile"
Then the three-scope resolved value should be "manual"
And the three-scope resolved source should be "local"
Scenario: Project scope overrides global scope
Given a global config value for three-scope "core.automation-profile" = "full-auto"
And a project config value "core.automation-profile" = "supervised"
When I resolve three-scope key "core.automation-profile"
Then the three-scope resolved value should be "supervised"
And the three-scope resolved source should be "project"
Scenario: Local scope overrides both project and global scopes
Given a global config value for three-scope "core.automation-profile" = "full-auto"
And a project config value "core.automation-profile" = "supervised"
And a local config value "core.automation-profile" = "manual"
When I resolve three-scope key "core.automation-profile"
Then the three-scope resolved value should be "manual"
And the three-scope resolved source should be "local"
Scenario: Global scope used when no project or local override exists
Given a global config value for three-scope "core.automation-profile" = "supervised"
When I resolve three-scope key "core.automation-profile"
Then the three-scope resolved value should be "supervised"
And the three-scope resolved source should be "global"
Scenario: Default used when no scope provides a value
When I resolve three-scope key "core.automation-profile"
Then the three-scope resolved source should be "default"
# ── Deep merge ────────────────────────────────────────────────────────
Scenario: Deep merge combines nested keys from all scopes
Given a global config with nested data "section_a.key1" = "global_val1"
And a project config with nested data "section_a.key2" = "project_val2"
And a local config with nested data "section_a.key3" = "local_val3"
When I read the three-scope merged config
Then the merged config "section_a" should contain key "key1" with value "global_val1"
And the merged config "section_a" should contain key "key2" with value "project_val2"
And the merged config "section_a" should contain key "key3" with value "local_val3"
Scenario: Deep merge local overrides project on conflict
Given a project config with nested data "section_a.key1" = "project_val"
And a local config with nested data "section_a.key1" = "local_val"
When I read the three-scope merged config
Then the merged config "section_a" should contain key "key1" with value "local_val"
# ── Project root discovery ────────────────────────────────────────────
Scenario: Project root discovered via cleveragents.toml marker
Given a directory tree with "cleveragents.toml" marker at the root
When I discover the project root from a subdirectory
Then the discovered project root should match the marker directory
Scenario: Project root discovered via .cleveragents directory marker
Given a directory tree with ".cleveragents" directory marker at the root
When I discover the project root from a subdirectory
Then the discovered project root should match the marker directory
Scenario: No project root returns None when no markers found
Given a directory tree with no project markers
When I discover the project root
Then the discovered project root should be None
# ── config.local.toml file loading ────────────────────────────────────
Scenario: Local config file is loaded when present
Given a local config file with "plan.concurrency" = "16"
When I resolve three-scope key "plan.concurrency"
Then the three-scope resolved value should be "16"
And the three-scope resolved source should be "local"
Scenario: Missing local config file is handled gracefully
When I read the local config
Then the local config should be an empty dict
# ── Scoped write ──────────────────────────────────────────────────────
Scenario: Set value with local scope writes to config.local.toml
When I set three-scope value "core.automation-profile" to "manual" with scope "local"
Then the local config file should contain "core.automation-profile" = "manual"
Scenario: Set value with project scope writes to project config.toml
When I set three-scope value "core.automation-profile" to "supervised" with scope "project"
Then the project config file should contain "core.automation-profile" = "supervised"
Scenario: Set value with global scope writes to global config.toml
When I set three-scope value "core.automation-profile" to "full-auto" with scope "global"
Then the global config file should contain "core.automation-profile" = "full-auto"
# ── ConfigScope enum ──────────────────────────────────────────────────
Scenario: ConfigScope enum has all three values
Then ConfigScope should have value "global"
And ConfigScope should have value "project"
And ConfigScope should have value "local"
# ── ConfigLevel enum includes LOCAL ───────────────────────────────────
Scenario: ConfigLevel enum includes LOCAL between ENV_VAR and PROJECT
Then ConfigLevel should have value "local"
And ConfigLevel "local" should exist between "env_var" and "project"
# ── .gitignore template includes config.local.toml ────────────────────
Scenario: Default ignore patterns include config.local.toml
Then the default ignore patterns should include "config.local.toml"