3577bf95ba
CI / lint (pull_request) Successful in 56s
CI / quality (pull_request) Successful in 55s
CI / typecheck (pull_request) Successful in 1m2s
CI / security (pull_request) Successful in 1m11s
CI / helm (pull_request) Successful in 31s
CI / build (pull_request) Successful in 33s
CI / push-validation (pull_request) Successful in 29s
CI / unit_tests (pull_request) Successful in 6m49s
CI / docker (pull_request) Successful in 1m32s
CI / integration_tests (pull_request) Successful in 9m20s
CI / coverage (pull_request) Successful in 8m1s
CI / status-check (pull_request) Successful in 5s
Implementation of the four-level automation profile precedence chain
(plan > action > project > global) as defined in the v3.5.0 specification.
Core implementation (src/):
- PrecedenceSource StrEnum with PLAN, ACTION, PROJECT, GLOBAL levels
- PrecedenceResolution frozen dataclass capturing full resolution state
- resolve_precedence_chain() with plan > action > project > global logic
- _resolve_global_profile() with explicit > env var > default fallback
- Comprehensive debug logging for observability
BDD tests (features/):
- automation_profile_precedence_chain.feature: 30 scenarios covering all
16 combinations of plan/action/project/global configurations plus edge
cases, logging verification, enum values, env var override, and custom registry
- step definitions with proper log handler cleanup via context._cleanup_handlers
CI compliance fixes (bd8b6748):
- ruff format applied to step definitions (fixes CI lint gate)
- CHANGELOG.md updated with accurate 4-level chain description
- test_reports/ artifacts removed; directory added to .gitignore
- tdd_a2a_sdk_dependency.feature reverted to use correct Client import
ISSUES CLOSED: #8234
237 lines
12 KiB
Gherkin
237 lines
12 KiB
Gherkin
Feature: Automation Profile Precedence Chain (plan > action > project > global)
|
||
The automation profile resolution follows a strict four-level precedence
|
||
chain: plan-level > action-level > project-level > global-level. This ensures that
|
||
fine-grained overrides work as intended for autonomous plan execution.
|
||
|
||
# ============================================================
|
||
# All 16 combinations: plan(set/unset) × action(set/unset) × project(set/unset) × global(set/default)
|
||
# ============================================================
|
||
|
||
# --- Combination 1: plan=set, action=set, project=set, global=set → plan wins ---
|
||
|
||
Scenario: Combination 1 - plan overrides action, project, and explicit global
|
||
Given the global profile is configured as "cautious"
|
||
When I resolve the precedence chain with plan "ci" action "auto" project "trusted"
|
||
Then the resolved precedence profile name should be "ci"
|
||
And the precedence source should be "plan"
|
||
|
||
# --- Combination 2: plan=set, action=set, project=set, global=default → plan wins ---
|
||
|
||
Scenario: Combination 2 - plan overrides action and project when global is default
|
||
Given the global profile is the default
|
||
When I resolve the precedence chain with plan "ci" action "auto" project "trusted"
|
||
Then the resolved precedence profile name should be "ci"
|
||
And the precedence source should be "plan"
|
||
|
||
# --- Combination 3: plan=set, action=set, project=unset, global=set → plan wins ---
|
||
|
||
Scenario: Combination 3 - plan overrides action and explicit global when project is absent
|
||
Given the global profile is configured as "cautious"
|
||
When I resolve the precedence chain with plan "ci" action "auto" and no project
|
||
Then the resolved precedence profile name should be "ci"
|
||
And the precedence source should be "plan"
|
||
|
||
# --- Combination 4: plan=set, action=set, project=unset, global=default → plan wins ---
|
||
|
||
Scenario: Combination 4 - plan overrides action when project and global are default
|
||
Given the global profile is the default
|
||
When I resolve the precedence chain with plan "ci" action "auto" and no project
|
||
Then the resolved precedence profile name should be "ci"
|
||
And the precedence source should be "plan"
|
||
|
||
# --- Combination 5: plan=set, action=unset, project=set, global=set → plan wins ---
|
||
|
||
Scenario: Combination 5 - plan overrides project and explicit global when action is absent
|
||
Given the global profile is configured as "cautious"
|
||
When I resolve the precedence chain with plan "ci" and no action project "trusted"
|
||
Then the resolved precedence profile name should be "ci"
|
||
And the precedence source should be "plan"
|
||
|
||
# --- Combination 6: plan=set, action=unset, project=set, global=default → plan wins ---
|
||
|
||
Scenario: Combination 6 - plan overrides project when action and global are default
|
||
Given the global profile is the default
|
||
When I resolve the precedence chain with plan "ci" and no action project "trusted"
|
||
Then the resolved precedence profile name should be "ci"
|
||
And the precedence source should be "plan"
|
||
|
||
# --- Combination 7: plan=set, action=unset, project=unset, global=set → plan wins ---
|
||
|
||
Scenario: Combination 7 - plan overrides explicit global when action and project are absent
|
||
Given the global profile is configured as "cautious"
|
||
When I resolve the precedence chain with plan "ci" and no action and no project
|
||
Then the resolved precedence profile name should be "ci"
|
||
And the precedence source should be "plan"
|
||
|
||
# --- Combination 8: plan=set, action=unset, project=unset, global=default → plan wins ---
|
||
|
||
Scenario: Combination 8 - plan overrides default global when action and project are absent
|
||
Given the global profile is the default
|
||
When I resolve the precedence chain with plan "ci" and no action and no project
|
||
Then the resolved precedence profile name should be "ci"
|
||
And the precedence source should be "plan"
|
||
|
||
# --- Combination 9: plan=unset, action=set, project=set, global=set → action wins ---
|
||
|
||
Scenario: Combination 9 - action overrides project and explicit global when plan is absent
|
||
Given the global profile is configured as "cautious"
|
||
When I resolve the precedence chain with no plan and action "auto" project "trusted"
|
||
Then the resolved precedence profile name should be "auto"
|
||
And the precedence source should be "action"
|
||
|
||
# --- Combination 10: plan=unset, action=set, project=set, global=default → action wins ---
|
||
|
||
Scenario: Combination 10 - action overrides project when plan and global are default
|
||
Given the global profile is the default
|
||
When I resolve the precedence chain with no plan and action "auto" project "trusted"
|
||
Then the resolved precedence profile name should be "auto"
|
||
And the precedence source should be "action"
|
||
|
||
# --- Combination 11: plan=unset, action=set, project=unset, global=set → action wins ---
|
||
|
||
Scenario: Combination 11 - action overrides explicit global when plan and project are absent
|
||
Given the global profile is configured as "cautious"
|
||
When I resolve the precedence chain with no plan and action "auto" and no project
|
||
Then the resolved precedence profile name should be "auto"
|
||
And the precedence source should be "action"
|
||
|
||
# --- Combination 12: plan=unset, action=set, project=unset, global=default → action wins ---
|
||
|
||
Scenario: Combination 12 - action overrides default global when plan and project are absent
|
||
Given the global profile is the default
|
||
When I resolve the precedence chain with no plan and action "auto" and no project
|
||
Then the resolved precedence profile name should be "auto"
|
||
And the precedence source should be "action"
|
||
|
||
# --- Combination 13: plan=unset, action=unset, project=set, global=set → project wins ---
|
||
|
||
Scenario: Combination 13 - project overrides explicit global when plan and action are absent
|
||
Given the global profile is configured as "cautious"
|
||
When I resolve the precedence chain with no plan and no action project "trusted"
|
||
Then the resolved precedence profile name should be "trusted"
|
||
And the precedence source should be "project"
|
||
|
||
# --- Combination 14: plan=unset, action=unset, project=set, global=default → project wins ---
|
||
|
||
Scenario: Combination 14 - project overrides default global when plan and action are absent
|
||
Given the global profile is the default
|
||
When I resolve the precedence chain with no plan and no action project "trusted"
|
||
Then the resolved precedence profile name should be "trusted"
|
||
And the precedence source should be "project"
|
||
|
||
# --- Combination 15: plan=unset, action=unset, project=unset, global=set → global wins ---
|
||
|
||
Scenario: Combination 15 - explicit global used when plan, action, and project are absent
|
||
Given the global profile is configured as "cautious"
|
||
When I resolve the precedence chain with no plan and no action and no project
|
||
Then the resolved precedence profile name should be "cautious"
|
||
And the precedence source should be "global"
|
||
|
||
# --- Combination 16: plan=unset, action=unset, project=unset, global=default → global default ---
|
||
|
||
Scenario: Combination 16 - default global (manual) used when nothing is set
|
||
Given the global profile is the default
|
||
When I resolve the precedence chain with no plan and no action and no project
|
||
Then the resolved precedence profile name should be "manual"
|
||
And the precedence source should be "global"
|
||
|
||
# ============================================================
|
||
# Debug logging verification
|
||
# ============================================================
|
||
|
||
Scenario: Resolution chain is logged at debug level
|
||
Given the global profile is the default
|
||
And debug logging is captured for the precedence module
|
||
When I resolve the precedence chain with plan "ci" action "auto" project "trusted"
|
||
Then a debug log entry should mention the resolved profile name "ci"
|
||
And the debug log entry should mention the source "plan"
|
||
|
||
# ============================================================
|
||
# Resolution result fields
|
||
# ============================================================
|
||
|
||
Scenario: Resolution result exposes plan profile field
|
||
Given the global profile is the default
|
||
When I resolve the precedence chain with plan "ci" action "auto" project "trusted"
|
||
Then the resolution plan_profile field should be "ci"
|
||
And the resolution action_profile field should be "auto"
|
||
And the resolution project_profile field should be "trusted"
|
||
|
||
Scenario: Resolution result exposes global profile field
|
||
Given the global profile is configured as "cautious"
|
||
When I resolve the precedence chain with no plan and no action and no project
|
||
Then the resolution global_profile field should be "cautious"
|
||
|
||
Scenario: Resolution result exposes the full AutomationProfile object
|
||
Given the global profile is the default
|
||
When I resolve the precedence chain with plan "manual" and no action and no project
|
||
Then the resolved precedence profile object should have name "manual"
|
||
And the resolved precedence profile decompose_task should be 1.0
|
||
|
||
# ============================================================
|
||
# Environment variable global override
|
||
# ============================================================
|
||
|
||
Scenario: Env var sets global profile when no explicit global is configured
|
||
Given the global profile is the default
|
||
And the env var CLEVERAGENTS_AUTOMATION_PROFILE is "trusted"
|
||
When I resolve the precedence chain with no plan and no action and no project
|
||
Then the resolved precedence profile name should be "trusted"
|
||
And the precedence source should be "global"
|
||
|
||
Scenario: Plan-level still overrides env var global
|
||
Given the global profile is the default
|
||
And the env var CLEVERAGENTS_AUTOMATION_PROFILE is "trusted"
|
||
When I resolve the precedence chain with plan "ci" and no action and no project
|
||
Then the resolved precedence profile name should be "ci"
|
||
And the precedence source should be "plan"
|
||
|
||
# ============================================================
|
||
# Custom profile registry
|
||
# ============================================================
|
||
|
||
Scenario: Custom profile registry is used for resolution
|
||
Given the global profile is the default
|
||
And a custom profile registry contains "acme/deploy" with select_tool 0.5
|
||
When I resolve the precedence chain with plan "acme/deploy" and no action and no project
|
||
Then the resolved precedence profile name should be "acme/deploy"
|
||
And the precedence source should be "plan"
|
||
|
||
# ============================================================
|
||
# Empty string treated as absent
|
||
# ============================================================
|
||
|
||
Scenario: Empty string plan profile is treated as absent
|
||
Given the global profile is the default
|
||
When I resolve the precedence chain with empty plan and action "auto" and no project
|
||
Then the resolved precedence profile name should be "auto"
|
||
And the precedence source should be "action"
|
||
|
||
Scenario: Empty string action profile is treated as absent
|
||
Given the global profile is the default
|
||
When I resolve the precedence chain with plan absent and empty action and no project
|
||
Then the resolved precedence profile name should be "manual"
|
||
And the precedence source should be "global"
|
||
|
||
Scenario: Empty string project profile is treated as absent
|
||
Given the global profile is the default
|
||
When I resolve the precedence chain with plan absent and action absent and empty project
|
||
Then the resolved precedence profile name should be "manual"
|
||
And the precedence source should be "global"
|
||
|
||
# ============================================================
|
||
# PrecedenceSource enum values
|
||
# ============================================================
|
||
|
||
Scenario: PrecedenceSource PLAN has value "plan"
|
||
Then the PrecedenceSource PLAN value should be "plan"
|
||
|
||
Scenario: PrecedenceSource ACTION has value "action"
|
||
Then the PrecedenceSource ACTION value should be "action"
|
||
|
||
Scenario: PrecedenceSource PROJECT has value "project"
|
||
Then the PrecedenceSource PROJECT value should be "project"
|
||
|
||
Scenario: PrecedenceSource GLOBAL has value "global"
|
||
Then the PrecedenceSource GLOBAL value should be "global"
|