UAT: AutomationProfileService.get_effective_profile() maps default_profile parameter to project-level precedence — callers expecting global-level fallback get wrong precedence tier #3130

Open
opened 2026-04-05 06:38:38 +00:00 by freemo · 2 comments
Owner

Background and Context

AutomationProfileService.get_effective_profile() is a convenience method that resolves the effective automation profile for a given plan/action/project combination. It accepts a default_profile third parameter that callers intend to supply as the global-level fallback (precedence level 4 in the 4-level hierarchy defined by docs/specification.md lines 28458–28463: plan > action > project > global).

However, the implementation incorrectly passes this argument as project_profile in the internal call to resolve_profile(), meaning the caller's "global default" is silently treated as project-level precedence (level 3) instead of global-level (level 4). This causes the wrong profile to win in any scenario where a project-level profile is also set — the caller's intended global fallback will override the project-level value rather than being overridden by it.

This is a distinct bug from #2056 (which concerns the wrong hardcoded constant _DEFAULT_PROFILE = "manual"). This bug is a parameter mis-mapping in the method's call chain and exists independently of the default value fix.

Current Behavior

In src/cleveragents/application/services/automation_profile_service.py, lines 290–313:

def get_effective_profile(
    self,
    plan_profile: str | None,
    action_profile: str | None,
    default_profile: str | None = None,   # ← caller intends: global-level fallback
) -> str:
    return self.resolve_profile(
        plan_profile=plan_profile,
        action_profile=action_profile,
        project_profile=default_profile,  # ← BUG: passed as project-level (tier 3)
        # global_profile is never passed → always None
    )

The default_profile argument is mapped to project_profile in the resolve_profile() call. The global_profile parameter of resolve_profile() is never supplied, so it defaults to None. As a result:

  • A caller passing default_profile="supervised" gets it treated as project-level (tier 3), not global-level (tier 4).
  • If a real project-level profile is also present, the caller's intended global fallback will override it — the opposite of the intended precedence.
  • The true global-level slot in resolve_profile() is always None, so the global config value is never consulted via this convenience method.

Expected Behavior

Per docs/specification.md lines 28458–28463, the 4-level precedence is:

plan > action > project > global

get_effective_profile() should pass default_profile as global_profile (not project_profile) in the call to resolve_profile():

def get_effective_profile(
    self,
    plan_profile: str | None,
    action_profile: str | None,
    default_profile: str | None = None,
) -> str:
    return self.resolve_profile(
        plan_profile=plan_profile,
        action_profile=action_profile,
        global_profile=default_profile,   # ← correct: global-level (tier 4)
    )

Steps to Reproduce

from cleveragents.application.services.automation_profile_service import AutomationProfileService

service = AutomationProfileService()

# Scenario: project_profile="full-auto", caller passes global fallback="supervised"
# Expected: project wins (tier 3 > tier 4) → "full-auto"
# Actual:   default_profile is treated as project-level → "supervised" wins (last-write wins or override)
result = service.get_effective_profile(
    plan_profile=None,
    action_profile=None,
    default_profile="supervised",
)
# The project_profile slot in resolve_profile() receives "supervised"
# The global_profile slot in resolve_profile() receives None
# Precedence is therefore wrong for any caller relying on tier separation

Acceptance Criteria

  • get_effective_profile() passes default_profile as global_profile (not project_profile) in the call to resolve_profile().
  • A BDD scenario is added to features/consolidated_automation_profile.feature covering the case where project_profile is set and default_profile is also set, asserting that project_profile wins (tier 3 > tier 4).
  • The corresponding step implementation in features/steps/automation_profile_service_steps.py is updated.
  • All nox sessions pass with coverage ≥ 97%.

Supporting Information

  • Spec reference: docs/specification.md lines 28458–28463, 4-level precedence hierarchy (plan > action > project > global).
  • Affected file: src/cleveragents/application/services/automation_profile_service.py (lines 290–313, get_effective_profile() method).
  • Related issue: #2056 (wrong _DEFAULT_PROFILE constant — separate bug, same service).
  • Discovered during: UAT testing of AutomationProfileService.

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


Metadata

  • Branch: fix/automation-profile-service-get-effective-profile-param-mapping
  • Commit Message: fix(automation-profile): map default_profile to global_profile tier in get_effective_profile()
  • Milestone: (none — backlog)
  • Parent Epic: #362

Subtasks

  • In src/cleveragents/application/services/automation_profile_service.py lines 290–313: change project_profile=default_profile to global_profile=default_profile in the resolve_profile() call inside get_effective_profile()
  • Add BDD scenario to features/consolidated_automation_profile.feature asserting that when both project_profile and default_profile are supplied, project_profile wins (tier 3 beats tier 4)
  • Add/update corresponding step implementation in features/steps/automation_profile_service_steps.py
  • Run nox -e unit_tests — confirm all Behave scenarios pass
  • Run nox -e typecheck — confirm Pyright reports no errors
  • Run nox -e coverage_report — confirm coverage ≥ 97%
  • Run nox (all default sessions) — confirm all quality gates pass

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(automation-profile): map default_profile to global_profile tier in get_effective_profile()), 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/automation-profile-service-get-effective-profile-param-mapping).
  • 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%.

Automated by CleverAgents Bot
Supervisor: UAT Testing | Agent: ca-new-issue-creator

## Background and Context `AutomationProfileService.get_effective_profile()` is a convenience method that resolves the effective automation profile for a given plan/action/project combination. It accepts a `default_profile` third parameter that callers intend to supply as the **global-level fallback** (precedence level 4 in the 4-level hierarchy defined by `docs/specification.md` lines 28458–28463: plan > action > project > global). However, the implementation incorrectly passes this argument as `project_profile` in the internal call to `resolve_profile()`, meaning the caller's "global default" is silently treated as **project-level precedence (level 3)** instead of global-level (level 4). This causes the wrong profile to win in any scenario where a project-level profile is also set — the caller's intended global fallback will override the project-level value rather than being overridden by it. This is a **distinct bug from #2056** (which concerns the wrong hardcoded constant `_DEFAULT_PROFILE = "manual"`). This bug is a parameter mis-mapping in the method's call chain and exists independently of the default value fix. ## Current Behavior In `src/cleveragents/application/services/automation_profile_service.py`, lines 290–313: ```python def get_effective_profile( self, plan_profile: str | None, action_profile: str | None, default_profile: str | None = None, # ← caller intends: global-level fallback ) -> str: return self.resolve_profile( plan_profile=plan_profile, action_profile=action_profile, project_profile=default_profile, # ← BUG: passed as project-level (tier 3) # global_profile is never passed → always None ) ``` The `default_profile` argument is mapped to `project_profile` in the `resolve_profile()` call. The `global_profile` parameter of `resolve_profile()` is never supplied, so it defaults to `None`. As a result: - A caller passing `default_profile="supervised"` gets it treated as project-level (tier 3), not global-level (tier 4). - If a real project-level profile is also present, the caller's intended global fallback will **override** it — the opposite of the intended precedence. - The true global-level slot in `resolve_profile()` is always `None`, so the global config value is never consulted via this convenience method. ## Expected Behavior Per `docs/specification.md` lines 28458–28463, the 4-level precedence is: ``` plan > action > project > global ``` `get_effective_profile()` should pass `default_profile` as `global_profile` (not `project_profile`) in the call to `resolve_profile()`: ```python def get_effective_profile( self, plan_profile: str | None, action_profile: str | None, default_profile: str | None = None, ) -> str: return self.resolve_profile( plan_profile=plan_profile, action_profile=action_profile, global_profile=default_profile, # ← correct: global-level (tier 4) ) ``` ## Steps to Reproduce ```python from cleveragents.application.services.automation_profile_service import AutomationProfileService service = AutomationProfileService() # Scenario: project_profile="full-auto", caller passes global fallback="supervised" # Expected: project wins (tier 3 > tier 4) → "full-auto" # Actual: default_profile is treated as project-level → "supervised" wins (last-write wins or override) result = service.get_effective_profile( plan_profile=None, action_profile=None, default_profile="supervised", ) # The project_profile slot in resolve_profile() receives "supervised" # The global_profile slot in resolve_profile() receives None # Precedence is therefore wrong for any caller relying on tier separation ``` ## Acceptance Criteria - `get_effective_profile()` passes `default_profile` as `global_profile` (not `project_profile`) in the call to `resolve_profile()`. - A BDD scenario is added to `features/consolidated_automation_profile.feature` covering the case where `project_profile` is set and `default_profile` is also set, asserting that `project_profile` wins (tier 3 > tier 4). - The corresponding step implementation in `features/steps/automation_profile_service_steps.py` is updated. - All nox sessions pass with coverage ≥ 97%. ## Supporting Information - **Spec reference**: `docs/specification.md` lines 28458–28463, 4-level precedence hierarchy (plan > action > project > global). - **Affected file**: `src/cleveragents/application/services/automation_profile_service.py` (lines 290–313, `get_effective_profile()` method). - **Related issue**: #2056 (wrong `_DEFAULT_PROFILE` constant — separate bug, same service). - **Discovered during**: UAT testing of `AutomationProfileService`. > **Backlog note:** This issue was discovered during autonomous operation > on milestone v3.7.0. It does not block milestone completion and has been > placed in the backlog for human review and future milestone assignment. --- ## Metadata - **Branch**: `fix/automation-profile-service-get-effective-profile-param-mapping` - **Commit Message**: `fix(automation-profile): map default_profile to global_profile tier in get_effective_profile()` - **Milestone**: *(none — backlog)* - **Parent Epic**: #362 --- ## Subtasks - [ ] In `src/cleveragents/application/services/automation_profile_service.py` lines 290–313: change `project_profile=default_profile` to `global_profile=default_profile` in the `resolve_profile()` call inside `get_effective_profile()` - [ ] Add BDD scenario to `features/consolidated_automation_profile.feature` asserting that when both `project_profile` and `default_profile` are supplied, `project_profile` wins (tier 3 beats tier 4) - [ ] Add/update corresponding step implementation in `features/steps/automation_profile_service_steps.py` - [ ] Run `nox -e unit_tests` — confirm all Behave scenarios pass - [ ] Run `nox -e typecheck` — confirm Pyright reports no errors - [ ] Run `nox -e coverage_report` — confirm coverage ≥ 97% - [ ] Run `nox` (all default sessions) — confirm all quality gates pass --- ## 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(automation-profile): map default_profile to global_profile tier in get_effective_profile()`), 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/automation-profile-service-get-effective-profile-param-mapping`). - 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%. --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: ca-new-issue-creator
freemo added this to the v3.5.0 milestone 2026-04-05 06:42:55 +00:00
Author
Owner

Milestone Triage Decision: Moved to Backlog

This edge case configuration issue has been moved out of v3.5.0 during aggressive milestone triage. While important for correctness, it represents an edge case that does not block core autonomy hardening functionality.

Reasoning:

  • v3.5.0 focus: Essential autonomy hardening (guard enforcement, A2A facade, plan lifecycle)
  • This issue: Automation profile precedence edge case - configuration correctness
  • Impact: Edge case handling, not core functional capability

Will be addressed in a future milestone focused on configuration robustness and edge cases.

**Milestone Triage Decision: Moved to Backlog** This edge case configuration issue has been moved out of v3.5.0 during aggressive milestone triage. While important for correctness, it represents an edge case that does not block core autonomy hardening functionality. **Reasoning:** - v3.5.0 focus: Essential autonomy hardening (guard enforcement, A2A facade, plan lifecycle) - This issue: Automation profile precedence edge case - configuration correctness - Impact: Edge case handling, not core functional capability Will be addressed in a future milestone focused on configuration robustness and edge cases.
freemo removed this from the v3.5.0 milestone 2026-04-06 22:40:59 +00:00
Author
Owner

This issue has been moved to the backlog as part of an aggressive grooming of the v3.5.0 milestone. It has been deemed non-critical for the minimal viability of the milestone and will be addressed in a future release.

This issue has been moved to the backlog as part of an aggressive grooming of the v3.5.0 milestone. It has been deemed non-critical for the minimal viability of the milestone and will be addressed in a future release.
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.

Blocks
#362 Epic: Security & Safety Hardening
cleveragents/cleveragents-core
Reference
cleveragents/cleveragents-core#3130
No description provided.