[Bug Hunt][Cycle 2][Config] Configuration merge order inconsistency in read vs resolve operations #7180

Open
opened 2026-04-10 08:34:00 +00:00 by HAL9000 · 4 comments
Owner

Metadata

  • Branch: fix/config-merge-order-inconsistency
  • Commit Message: fix(config): align read_merged_config precedence with resolve() chain
  • Milestone: None (Backlog)
  • Parent Epic: TBD (see orphan note below)

Background and Context

The configuration service (ConfigService) exposes two distinct APIs for accessing merged configuration values:

  1. read_merged_config() — returns a fully merged dict[str, Any] of all config layers
  2. resolve() — resolves a single key through the full precedence chain

These two APIs use different precedence orders for the local vs project config layers, meaning the same key can resolve to a different value depending on which API is used. This is a data-flow consistency bug that violates the documented configuration precedence chain defined in ADR-024 and the project specification.


Current Behavior

read_merged_config() precedence (src/cleveragents/application/services/config_service.py, lines 1250–1258):

def read_merged_config(self) -> dict[str, Any]:
    merged = self.read_config()                               # global (lowest)
    merged = _deep_merge(merged, self.read_project_config())  # project (middle)
    merged = _deep_merge(merged, self.read_local_config())    # local (highest)
    return merged

Effective order (lowest → highest): global → project → local

resolve() precedence (src/cleveragents/application/services/config_service.py, lines 1442–1615):

CLI flag > env var > local > project > global > default

Effective order (lowest → highest): default → global → project → local → env var → CLI flag

The conflict: In read_merged_config(), local overrides project. In resolve(), project overrides local (when no CLI/env values are present). A key set in both project and local config will resolve to different winning values depending on which API is called.


Expected Behavior

All configuration APIs must use the same consistent precedence order, aligned with the documented specification:

CLI flag > env var > project-scoped > global file > built-in default
(Source: docs/specification.md §6b)

read_merged_config() should apply layers in the order that matches resolve():

def read_merged_config(self) -> dict[str, Any]:
    merged = self.read_config()                               # global (lowest)
    merged = _deep_merge(merged, self.read_local_config())    # local > global
    merged = _deep_merge(merged, self.read_project_config())  # project > local > global
    return merged

Acceptance Criteria

  • read_merged_config() applies config layers in the order: global → local → project (project wins over local)
  • resolve() and read_merged_config() produce identical results for any key present in both project and local config (when no CLI/env override is present)
  • Existing tests for read_merged_config() are updated to reflect the corrected precedence
  • A regression test verifies that a key set in both project and local config resolves to the project value via read_merged_config()
  • All nox stages pass
  • Coverage ≥ 97%

Supporting Information

  • File: src/cleveragents/application/services/config_service.py
  • Functions: ConfigService.read_merged_config() (lines 1250–1258) vs ConfigService.resolve() (lines 1442–1615)
  • Specification reference: docs/specification.md §6b — Configuration Precedence
  • ADR reference: ADR-024 (Configuration System)
  • Category: data-flow

TDD Note

After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD. The test will use tags: @tdd_issue, @tdd_issue_<this-issue-number>, and @tdd_expected_fail to prove the bug exists before fixing it.


Subtasks

  • Reproduce the inconsistency with a targeted test (project key + local key, same path)
  • Swap the merge order in read_merged_config() to match resolve() precedence
  • Update or add unit tests for read_merged_config() to assert correct precedence
  • Verify resolve() and read_merged_config() agree on all project-scopable keys
  • Run nox (all default sessions), fix any errors
  • Verify coverage ≥ 97% via nox -s coverage_report

Definition of Done

This issue is complete when:

  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line of the commit message matches the Commit Message in Metadata exactly (fix(config): align read_merged_config precedence with resolve() chain), followed by a blank line, then additional lines providing relevant details about the implementation.
  • The commit is pushed to the remote on the branch matching the Branch in Metadata exactly (fix/config-merge-order-inconsistency).
  • The commit is submitted as a pull request to master, reviewed, and merged before this issue is marked done.
  • All nox stages pass.
  • Coverage ≥ 97%.

Backlog note: This issue was discovered during autonomous operation
on milestone v3.2.0. It does not block milestone completion and has been
placed in the backlog for human review and future milestone assignment.


Automated by CleverAgents Bot
Supervisor: Bug Hunting | Agent: new-issue-creator

## Metadata - **Branch**: `fix/config-merge-order-inconsistency` - **Commit Message**: `fix(config): align read_merged_config precedence with resolve() chain` - **Milestone**: None (Backlog) - **Parent Epic**: TBD (see orphan note below) --- ## Background and Context The configuration service (`ConfigService`) exposes two distinct APIs for accessing merged configuration values: 1. `read_merged_config()` — returns a fully merged `dict[str, Any]` of all config layers 2. `resolve()` — resolves a single key through the full precedence chain These two APIs use **different precedence orders** for the `local` vs `project` config layers, meaning the same key can resolve to a different value depending on which API is used. This is a data-flow consistency bug that violates the documented configuration precedence chain defined in ADR-024 and the project specification. --- ## Current Behavior **`read_merged_config()` precedence** (`src/cleveragents/application/services/config_service.py`, lines 1250–1258): ```python def read_merged_config(self) -> dict[str, Any]: merged = self.read_config() # global (lowest) merged = _deep_merge(merged, self.read_project_config()) # project (middle) merged = _deep_merge(merged, self.read_local_config()) # local (highest) return merged ``` Effective order (lowest → highest): `global → project → local` **`resolve()` precedence** (`src/cleveragents/application/services/config_service.py`, lines 1442–1615): ``` CLI flag > env var > local > project > global > default ``` Effective order (lowest → highest): `default → global → project → local → env var → CLI flag` **The conflict**: In `read_merged_config()`, `local` overrides `project`. In `resolve()`, `project` overrides `local` (when no CLI/env values are present). A key set in both project and local config will resolve to **different winning values** depending on which API is called. --- ## Expected Behavior All configuration APIs must use the same consistent precedence order, aligned with the documented specification: > **CLI flag > env var > project-scoped > global file > built-in default** > *(Source: `docs/specification.md` §6b)* `read_merged_config()` should apply layers in the order that matches `resolve()`: ```python def read_merged_config(self) -> dict[str, Any]: merged = self.read_config() # global (lowest) merged = _deep_merge(merged, self.read_local_config()) # local > global merged = _deep_merge(merged, self.read_project_config()) # project > local > global return merged ``` --- ## Acceptance Criteria - [ ] `read_merged_config()` applies config layers in the order: `global → local → project` (project wins over local) - [ ] `resolve()` and `read_merged_config()` produce identical results for any key present in both project and local config (when no CLI/env override is present) - [ ] Existing tests for `read_merged_config()` are updated to reflect the corrected precedence - [ ] A regression test verifies that a key set in both project and local config resolves to the **project** value via `read_merged_config()` - [ ] All nox stages pass - [ ] Coverage ≥ 97% --- ## Supporting Information - **File**: `src/cleveragents/application/services/config_service.py` - **Functions**: `ConfigService.read_merged_config()` (lines 1250–1258) vs `ConfigService.resolve()` (lines 1442–1615) - **Specification reference**: `docs/specification.md` §6b — Configuration Precedence - **ADR reference**: ADR-024 (Configuration System) - **Category**: `data-flow` ### TDD Note After this bug issue is verified, a corresponding `Type/Testing` issue will be created for TDD. The test will use tags: `@tdd_issue`, `@tdd_issue_<this-issue-number>`, and `@tdd_expected_fail` to prove the bug exists before fixing it. --- ## Subtasks - [ ] Reproduce the inconsistency with a targeted test (project key + local key, same path) - [ ] Swap the merge order in `read_merged_config()` to match `resolve()` precedence - [ ] Update or add unit tests for `read_merged_config()` to assert correct precedence - [ ] Verify `resolve()` and `read_merged_config()` agree on all project-scopable keys - [ ] Run `nox` (all default sessions), fix any errors - [ ] Verify coverage ≥ 97% via `nox -s coverage_report` --- ## Definition of Done This issue is complete when: - All subtasks above are completed and checked off. - A Git commit is created where the **first line** of the commit message matches the Commit Message in Metadata exactly (`fix(config): align read_merged_config precedence with resolve() chain`), followed by a blank line, then additional lines providing relevant details about the implementation. - The commit is pushed to the remote on the branch matching the **Branch** in Metadata exactly (`fix/config-merge-order-inconsistency`). - The commit is submitted as a **pull request** to `master`, reviewed, and **merged** before this issue is marked done. - All nox stages pass. - Coverage ≥ 97%. --- > **Backlog note:** This issue was discovered during autonomous operation > on milestone v3.2.0. It does not block milestone completion and has been > placed in the backlog for human review and future milestone assignment. --- **Automated by CleverAgents Bot** Supervisor: Bug Hunting | Agent: new-issue-creator
Author
Owner

⚠️ Orphan Issue — Manual Linking Required

This issue was created without a known parent Epic. Per CONTRIBUTING.md, all non-Epic, non-Legendary issues should be linked to at least one parent Epic when one exists.

Action required for maintainers:

  1. Identify the appropriate parent Epic for configuration service data-flow bugs (likely a Config Service Epic or a Data Flow / Architecture Epic)
  2. Open the parent Epic and add this issue (#7180) under "depends on", OR open this issue and add the parent under "blocks"
  3. Ensure the direction is correct: this child issue blocks the parent Epic (parent cannot be complete until this child is done)

If no parent Epic exists yet, one should be created to group configuration service correctness issues.


Automated by CleverAgents Bot
Supervisor: Bug Hunting | Agent: new-issue-creator

⚠️ **Orphan Issue — Manual Linking Required** This issue was created without a known parent Epic. Per CONTRIBUTING.md, all non-Epic, non-Legendary issues **should** be linked to at least one parent Epic when one exists. **Action required for maintainers:** 1. Identify the appropriate parent Epic for configuration service data-flow bugs (likely a Config Service Epic or a Data Flow / Architecture Epic) 2. Open the parent Epic and add this issue (#7180) under **"depends on"**, OR open this issue and add the parent under **"blocks"** 3. Ensure the direction is correct: **this child issue blocks the parent Epic** (parent cannot be complete until this child is done) If no parent Epic exists yet, one should be created to group configuration service correctness issues. --- **Automated by CleverAgents Bot** Supervisor: Bug Hunting | Agent: new-issue-creator
Author
Owner

Verified — Bug: configuration merge order inconsistency between read and resolve operations. MoSCoW: Should-have. Priority: Medium.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Bug: configuration merge order inconsistency between read and resolve operations. MoSCoW: Should-have. Priority: Medium. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Author
Owner

Verified — Bug: configuration merge order inconsistency between read and resolve operations. MoSCoW: Should-have. Priority: Medium.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Bug: configuration merge order inconsistency between read and resolve operations. MoSCoW: Should-have. Priority: Medium. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Author
Owner

Verified — Bug: configuration merge order inconsistency between read and resolve operations. MoSCoW: Should-have. Priority: Medium.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Bug: configuration merge order inconsistency between read and resolve operations. MoSCoW: Should-have. Priority: Medium. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#7180
No description provided.