UAT: merge_invariants() and InvariantSet.merge() lack action_invariants parameter — inconsistent with four-scope architecture #2102

Open
opened 2026-04-03 04:04:32 +00:00 by freemo · 1 comment
Owner

Metadata

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

Background and Context

The four-scope invariant architecture (GLOBAL, PROJECT, ACTION, PLAN) is a core design principle of CleverAgents, defined in docs/specification.md and ADR-016. The InvariantScope enum correctly exposes all four scopes, and _SCOPE_PRECEDENCE in src/cleveragents/actor/reconciliation.py defines the full precedence chain: plan > action > project > global.

However, the domain-level merge utilities — merge_invariants() and InvariantSet.merge() — only accept three parameters (plan_invariants, project_invariants, global_invariants), silently omitting the ACTION scope. This creates an API gap: callers cannot merge action-scoped invariants through the standard utility, forcing them to either bypass the utility or silently drop action invariants.

Current Behavior

merge_invariants() (lines 160–191 of src/cleveragents/domain/models/core/invariant.py) and InvariantSet.merge() (lines 130–155 of the same file) only accept three parameters:

merge_invariants(plan_invs, project_invs, global_invs)  # works
InvariantSet.merge(plan_invs, project_invs, global_invs)  # works
# But there is no way to include action_invs in the merge!

Inspecting the signature confirms the gap:

from cleveragents.domain.models.core.invariant import merge_invariants, Invariant, InvariantScope
import inspect
sig = inspect.signature(merge_invariants)
print(sig)  # (plan_invariants, project_invariants, global_invariants) — no action_invariants

Note: InvariantReconciliationActor in reconciliation.py correctly handles action invariants through its own reconcile_invariants() function, but the domain-level utility is incomplete.

Expected Behavior

Both merge_invariants() and InvariantSet.merge() should accept an optional action_invariants parameter and merge action-scoped invariants at the correct precedence level, consistent with _SCOPE_PRECEDENCE:

plan > action > project > global

Example target API:

merge_invariants(plan_invs, project_invs, global_invs, action_invariants=action_invs)
InvariantSet.merge(plan_invs, project_invs, global_invs, action_invariants=action_invs)

Acceptance Criteria

  • merge_invariants() accepts an optional action_invariants parameter (default None or empty)
  • InvariantSet.merge() accepts an optional action_invariants parameter (default None or empty)
  • Action invariants are merged at the correct precedence: plan > action > project > global
  • Existing call sites with three positional arguments continue to work without modification (backward-compatible)
  • Unit tests cover: action invariants present, action invariants absent, action invariants conflicting with project/global invariants

Supporting Information

Affected files:

  • src/cleveragents/domain/models/core/invariant.py
    • merge_invariants() function (lines 160–191): signature missing action_invariants
    • InvariantSet.merge() class method (lines 130–155): same issue

Reference implementation (correct precedence):

  • src/cleveragents/actor/reconciliation.py_SCOPE_PRECEDENCE and reconcile_invariants()

Spec references: docs/specification.md (four-scope invariant architecture), ADR-016

Steps to reproduce:

from cleveragents.domain.models.core.invariant import merge_invariants
import inspect
print(inspect.signature(merge_invariants))
# Expected: (..., action_invariants=None, ...)
# Actual:   (plan_invariants, project_invariants, global_invariants)

Subtasks

  • Add optional action_invariants parameter to merge_invariants() in invariant.py
  • Add optional action_invariants parameter to InvariantSet.merge() in invariant.py
  • Implement correct merge precedence: plan > action > project > global
  • Verify backward compatibility — existing three-argument call sites must not break
  • Tests (pytest/Behave): Add unit tests for merge_invariants() with action_invariants
  • Tests (pytest/Behave): Add unit tests for InvariantSet.merge() with action_invariants
  • Tests (pytest/Behave): Add conflict-resolution tests (action vs project, action vs global)
  • Verify coverage ≥ 97% via nox -s coverage_report
  • Run nox (all default sessions), fix any errors

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(domain): add action_invariants parameter to merge_invariants and InvariantSet.merge), followed by a blank line, then additional lines providing relevant implementation details
  • The commit is pushed to the remote on branch fix/invariant-merge-action-scope
  • 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

## Metadata - **Branch**: `fix/invariant-merge-action-scope` - **Commit Message**: `fix(domain): add action_invariants parameter to merge_invariants and InvariantSet.merge` - **Milestone**: v3.7.0 - **Parent Epic**: #936 ## Background and Context The four-scope invariant architecture (GLOBAL, PROJECT, ACTION, PLAN) is a core design principle of CleverAgents, defined in `docs/specification.md` and ADR-016. The `InvariantScope` enum correctly exposes all four scopes, and `_SCOPE_PRECEDENCE` in `src/cleveragents/actor/reconciliation.py` defines the full precedence chain: `plan > action > project > global`. However, the domain-level merge utilities — `merge_invariants()` and `InvariantSet.merge()` — only accept three parameters (`plan_invariants`, `project_invariants`, `global_invariants`), silently omitting the ACTION scope. This creates an API gap: callers cannot merge action-scoped invariants through the standard utility, forcing them to either bypass the utility or silently drop action invariants. ## Current Behavior `merge_invariants()` (lines 160–191 of `src/cleveragents/domain/models/core/invariant.py`) and `InvariantSet.merge()` (lines 130–155 of the same file) only accept three parameters: ```python merge_invariants(plan_invs, project_invs, global_invs) # works InvariantSet.merge(plan_invs, project_invs, global_invs) # works # But there is no way to include action_invs in the merge! ``` Inspecting the signature confirms the gap: ```python from cleveragents.domain.models.core.invariant import merge_invariants, Invariant, InvariantScope import inspect sig = inspect.signature(merge_invariants) print(sig) # (plan_invariants, project_invariants, global_invariants) — no action_invariants ``` Note: `InvariantReconciliationActor` in `reconciliation.py` correctly handles action invariants through its own `reconcile_invariants()` function, but the domain-level utility is incomplete. ## Expected Behavior Both `merge_invariants()` and `InvariantSet.merge()` should accept an optional `action_invariants` parameter and merge action-scoped invariants at the correct precedence level, consistent with `_SCOPE_PRECEDENCE`: ``` plan > action > project > global ``` Example target API: ```python merge_invariants(plan_invs, project_invs, global_invs, action_invariants=action_invs) InvariantSet.merge(plan_invs, project_invs, global_invs, action_invariants=action_invs) ``` ## Acceptance Criteria - [ ] `merge_invariants()` accepts an optional `action_invariants` parameter (default `None` or empty) - [ ] `InvariantSet.merge()` accepts an optional `action_invariants` parameter (default `None` or empty) - [ ] Action invariants are merged at the correct precedence: `plan > action > project > global` - [ ] Existing call sites with three positional arguments continue to work without modification (backward-compatible) - [ ] Unit tests cover: action invariants present, action invariants absent, action invariants conflicting with project/global invariants ## Supporting Information **Affected files:** - `src/cleveragents/domain/models/core/invariant.py` - `merge_invariants()` function (lines 160–191): signature missing `action_invariants` - `InvariantSet.merge()` class method (lines 130–155): same issue **Reference implementation (correct precedence):** - `src/cleveragents/actor/reconciliation.py` — `_SCOPE_PRECEDENCE` and `reconcile_invariants()` **Spec references:** `docs/specification.md` (four-scope invariant architecture), ADR-016 **Steps to reproduce:** ```python from cleveragents.domain.models.core.invariant import merge_invariants import inspect print(inspect.signature(merge_invariants)) # Expected: (..., action_invariants=None, ...) # Actual: (plan_invariants, project_invariants, global_invariants) ``` ## Subtasks - [ ] Add optional `action_invariants` parameter to `merge_invariants()` in `invariant.py` - [ ] Add optional `action_invariants` parameter to `InvariantSet.merge()` in `invariant.py` - [ ] Implement correct merge precedence: `plan > action > project > global` - [ ] Verify backward compatibility — existing three-argument call sites must not break - [ ] Tests (pytest/Behave): Add unit tests for `merge_invariants()` with `action_invariants` - [ ] Tests (pytest/Behave): Add unit tests for `InvariantSet.merge()` with `action_invariants` - [ ] Tests (pytest/Behave): Add conflict-resolution tests (action vs project, action vs global) - [ ] Verify coverage ≥ 97% via `nox -s coverage_report` - [ ] Run `nox` (all default sessions), fix any errors ## 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(domain): add action_invariants parameter to merge_invariants and InvariantSet.merge`), followed by a blank line, then additional lines providing relevant implementation details - [ ] The commit is pushed to the remote on branch `fix/invariant-merge-action-scope` - [ ] 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.7.0 milestone 2026-04-03 04:04:37 +00:00
freemo self-assigned this 2026-04-03 16:58:07 +00:00
Author
Owner

MoSCoW classification: Should Have

Rationale: This issue addresses a spec requirement or important quality improvement. It should be included in the milestone if possible.


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

MoSCoW classification: **Should Have** Rationale: This issue addresses a spec requirement or important quality improvement. It should be included in the milestone if possible. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: ca-project-owner
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.

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