UAT: InvariantSet.merge() and merge_invariants() missing action_invariants parameter — action scope cannot participate in merge precedence #2535

Open
opened 2026-04-03 18:49:41 +00:00 by freemo · 1 comment
Owner

Metadata

  • Branch: fix/invariant-merge-action-scope
  • Commit Message: fix(invariant): add action_invariants parameter to InvariantSet.merge and merge_invariants
  • Milestone: v3.4.0
  • Parent Epic: #394

Background and Context

The specification defines a four-tier invariant precedence chain: plan > action > project > global. The InvariantReconciliationActor correctly implements this four-tier precedence via _SCOPE_PRECEDENCE:

_SCOPE_PRECEDENCE: dict[InvariantScope, int] = {
    InvariantScope.PLAN: 0,
    InvariantScope.ACTION: 1,
    InvariantScope.PROJECT: 2,
    InvariantScope.GLOBAL: 3,
}

However, the InvariantSet.merge() class method and merge_invariants() function only accept three parameters (plan_invariants, project_invariants, global_invariants), with no action_invariants parameter. This means the merge utility functions cannot correctly implement the four-tier precedence chain.

Steps to Reproduce

from cleveragents.domain.models.core.invariant import InvariantSet, Invariant, InvariantScope
import inspect

sig = inspect.signature(InvariantSet.merge)
print(sig)
# (plan_invariants: list[Invariant], project_invariants: list[Invariant], global_invariants: list[Invariant]) -> InvariantSet
# Missing: action_invariants parameter!

Expected Behavior (per spec)

InvariantSet.merge() should accept four parameters:

@classmethod
def merge(
    cls,
    plan_invariants: list[Invariant],
    action_invariants: list[Invariant],
    project_invariants: list[Invariant],
    global_invariants: list[Invariant],
) -> InvariantSet:

And merge_invariants() should similarly accept four parameters with action invariants placed between plan and project in the precedence order.

Actual Behavior

Both InvariantSet.merge() and merge_invariants() only accept three parameters. The InvariantReconciliationActor works around this by using its own reconcile_invariants() function which correctly handles all four scopes, but the core merge utilities are incomplete.

Code Location

  • src/cleveragents/domain/models/core/invariant.pyInvariantSet.merge() and merge_invariants() functions

Impact

  • Any code using InvariantSet.merge() directly cannot include action-scope invariants
  • The merge utilities are inconsistent with the InvariantReconciliationActor's four-tier precedence
  • InvariantService.get_effective_invariants() uses merge_invariants() and therefore cannot include action invariants

Subtasks

  • Add action_invariants: list[Invariant] parameter to merge_invariants() between plan_invariants and project_invariants
  • Update InvariantSet.merge() to accept and pass action_invariants to merge_invariants()
  • Update all callers of merge_invariants() to pass action invariants
  • Add unit tests for action invariant inclusion in merge

Definition of Done

  • InvariantSet.merge(plan_invs, action_invs, project_invs, global_invs) correctly merges with plan > action > project > global precedence
  • merge_invariants() correctly handles action invariants
  • All existing tests pass
  • New tests cover action invariant precedence

Automated by CleverAgents Bot
Supervisor: UAT Testing | Agent: ca-uat-tester

## Metadata - **Branch**: `fix/invariant-merge-action-scope` - **Commit Message**: `fix(invariant): add action_invariants parameter to InvariantSet.merge and merge_invariants` - **Milestone**: v3.4.0 - **Parent Epic**: #394 ## Background and Context The specification defines a four-tier invariant precedence chain: `plan > action > project > global`. The `InvariantReconciliationActor` correctly implements this four-tier precedence via `_SCOPE_PRECEDENCE`: ```python _SCOPE_PRECEDENCE: dict[InvariantScope, int] = { InvariantScope.PLAN: 0, InvariantScope.ACTION: 1, InvariantScope.PROJECT: 2, InvariantScope.GLOBAL: 3, } ``` However, the `InvariantSet.merge()` class method and `merge_invariants()` function only accept three parameters (`plan_invariants`, `project_invariants`, `global_invariants`), with no `action_invariants` parameter. This means the merge utility functions cannot correctly implement the four-tier precedence chain. ## Steps to Reproduce ```python from cleveragents.domain.models.core.invariant import InvariantSet, Invariant, InvariantScope import inspect sig = inspect.signature(InvariantSet.merge) print(sig) # (plan_invariants: list[Invariant], project_invariants: list[Invariant], global_invariants: list[Invariant]) -> InvariantSet # Missing: action_invariants parameter! ``` ## Expected Behavior (per spec) `InvariantSet.merge()` should accept four parameters: ```python @classmethod def merge( cls, plan_invariants: list[Invariant], action_invariants: list[Invariant], project_invariants: list[Invariant], global_invariants: list[Invariant], ) -> InvariantSet: ``` And `merge_invariants()` should similarly accept four parameters with action invariants placed between plan and project in the precedence order. ## Actual Behavior Both `InvariantSet.merge()` and `merge_invariants()` only accept three parameters. The `InvariantReconciliationActor` works around this by using its own `reconcile_invariants()` function which correctly handles all four scopes, but the core merge utilities are incomplete. ## Code Location - `src/cleveragents/domain/models/core/invariant.py` — `InvariantSet.merge()` and `merge_invariants()` functions ## Impact - Any code using `InvariantSet.merge()` directly cannot include action-scope invariants - The merge utilities are inconsistent with the `InvariantReconciliationActor`'s four-tier precedence - `InvariantService.get_effective_invariants()` uses `merge_invariants()` and therefore cannot include action invariants ## Subtasks - [ ] Add `action_invariants: list[Invariant]` parameter to `merge_invariants()` between `plan_invariants` and `project_invariants` - [ ] Update `InvariantSet.merge()` to accept and pass `action_invariants` to `merge_invariants()` - [ ] Update all callers of `merge_invariants()` to pass action invariants - [ ] Add unit tests for action invariant inclusion in merge ## Definition of Done - `InvariantSet.merge(plan_invs, action_invs, project_invs, global_invs)` correctly merges with plan > action > project > global precedence - `merge_invariants()` correctly handles action invariants - All existing tests pass - New tests cover action invariant precedence --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: ca-uat-tester
Author
Owner

Issue triaged by project owner:

  • State: Verified
  • MoSCoW: Should Have — Spec compliance or quality improvement.

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

Issue triaged by project owner: - **State**: Verified - **MoSCoW**: Should Have — Spec compliance or quality improvement. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: ca-project-owner
freemo added this to the v3.3.0 milestone 2026-04-05 05:07:03 +00:00
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#2535
No description provided.