UAT: agents invariant add CLI uses isolated in-memory service — invariants not persisted and not enforced during strategize #6038

Open
opened 2026-04-09 13:58:45 +00:00 by HAL9000 · 2 comments
Owner

Bug Report

Feature Area: Invariant Management
Commands: agents invariant add, agents invariant list
Milestone: v3.2.0
Severity: Critical — blocks v3.2.0 acceptance criteria


What Was Tested

Code analysis of src/cleveragents/cli/commands/invariant.py against the v3.2.0 acceptance criteria:

"agents invariant add creates invariants; agents invariant list displays them; Invariants are enforced during strategize"


Expected Behavior (from spec and v3.2.0 acceptance criteria)

  1. agents invariant add --global "Never delete production data" should persist the invariant to the shared InvariantService singleton registered in the DI container.
  2. When a plan enters the Strategize phase, the InvariantReconciliationActor should collect invariants from the same InvariantService instance that the CLI wrote to.
  3. Invariants added via CLI should survive across CLI invocations (i.e., be persisted or at minimum shared within the same process lifecycle as the plan lifecycle service).

Actual Behavior (from code analysis)

The CLI's invariant.py creates its own isolated in-memory InvariantService instance that is completely disconnected from the DI container:

# src/cleveragents/cli/commands/invariant.py, lines ~55-62
# Module-level service instance (in-memory, same lifetime as CLI process)
_service: InvariantService | None = None

def _get_service() -> InvariantService:
    """Return (or lazily create) the module-level InvariantService."""
    global _service
    if _service is None:
        _service = InvariantService()   # ← creates a NEW instance, not the container singleton
    return _service

Consequences:

  1. Invariants are lost on process exit — the in-memory dict is discarded when the CLI process exits.
  2. Invariants are NOT enforced during strategize — the InvariantReconciliationActor (in src/cleveragents/actor/reconciliation.py) uses the InvariantService from the DI container (src/cleveragents/application/container.py), which is a completely different instance from the one the CLI writes to.
  3. agents invariant list shows invariants that were added in the same CLI session only — a subsequent agents invariant list invocation will show an empty list.

Code Locations

  • Bug location: src/cleveragents/cli/commands/invariant.py, _get_service() function (lines ~55-62)
  • Container registration: src/cleveragents/application/container.pyinvariant_service is registered as a Singleton
  • Reconciliation actor: src/cleveragents/actor/reconciliation.py — uses InvariantService from DI container
  • Plan lifecycle integration: src/cleveragents/application/services/plan_lifecycle_service.py — calls reconciliation at strategize entry

Steps to Reproduce

# Add a global invariant
agents invariant add --global "Never delete production data"
# Output: Invariant added: 01HXYZ...

# List invariants in the SAME process — works
agents invariant list
# Shows the invariant

# List invariants in a NEW process — FAILS
agents invariant list
# Output: No invariants found.  ← BUG: invariant was lost

# Start a plan and enter strategize — invariant NOT enforced
agents plan use my-action
# InvariantReconciliationActor finds 0 invariants ← BUG

Fix Required

The CLI's _get_service() should use the DI container's singleton InvariantService instead of creating a new in-memory instance. This requires either:

  1. Injecting the container's InvariantService into the CLI command (preferred), or
  2. Implementing database-backed persistence in InvariantService so that in-memory instances are backed by the same storage layer.

The InvariantService docstring itself says "Uses in-memory storage (same pattern as PlanLifecycleService)" — but PlanLifecycleService IS the container singleton, while the CLI creates a separate instance.


Impact

This is a critical bug that directly blocks the v3.2.0 acceptance criterion:

"Invariants are enforced during strategize"

Without this fix, the invariant management feature is non-functional end-to-end.


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

## Bug Report **Feature Area:** Invariant Management **Commands:** `agents invariant add`, `agents invariant list` **Milestone:** v3.2.0 **Severity:** Critical — blocks v3.2.0 acceptance criteria --- ## What Was Tested Code analysis of `src/cleveragents/cli/commands/invariant.py` against the v3.2.0 acceptance criteria: > "agents invariant add creates invariants; agents invariant list displays them; Invariants are enforced during strategize" --- ## Expected Behavior (from spec and v3.2.0 acceptance criteria) 1. `agents invariant add --global "Never delete production data"` should persist the invariant to the shared `InvariantService` singleton registered in the DI container. 2. When a plan enters the Strategize phase, the `InvariantReconciliationActor` should collect invariants from the same `InvariantService` instance that the CLI wrote to. 3. Invariants added via CLI should survive across CLI invocations (i.e., be persisted or at minimum shared within the same process lifecycle as the plan lifecycle service). --- ## Actual Behavior (from code analysis) The CLI's `invariant.py` creates its **own isolated in-memory `InvariantService` instance** that is completely disconnected from the DI container: ```python # src/cleveragents/cli/commands/invariant.py, lines ~55-62 # Module-level service instance (in-memory, same lifetime as CLI process) _service: InvariantService | None = None def _get_service() -> InvariantService: """Return (or lazily create) the module-level InvariantService.""" global _service if _service is None: _service = InvariantService() # ← creates a NEW instance, not the container singleton return _service ``` **Consequences:** 1. **Invariants are lost on process exit** — the in-memory dict is discarded when the CLI process exits. 2. **Invariants are NOT enforced during strategize** — the `InvariantReconciliationActor` (in `src/cleveragents/actor/reconciliation.py`) uses the `InvariantService` from the DI container (`src/cleveragents/application/container.py`), which is a completely different instance from the one the CLI writes to. 3. **`agents invariant list` shows invariants that were added in the same CLI session only** — a subsequent `agents invariant list` invocation will show an empty list. --- ## Code Locations - **Bug location**: `src/cleveragents/cli/commands/invariant.py`, `_get_service()` function (lines ~55-62) - **Container registration**: `src/cleveragents/application/container.py` — `invariant_service` is registered as a Singleton - **Reconciliation actor**: `src/cleveragents/actor/reconciliation.py` — uses `InvariantService` from DI container - **Plan lifecycle integration**: `src/cleveragents/application/services/plan_lifecycle_service.py` — calls reconciliation at strategize entry --- ## Steps to Reproduce ```bash # Add a global invariant agents invariant add --global "Never delete production data" # Output: Invariant added: 01HXYZ... # List invariants in the SAME process — works agents invariant list # Shows the invariant # List invariants in a NEW process — FAILS agents invariant list # Output: No invariants found. ← BUG: invariant was lost # Start a plan and enter strategize — invariant NOT enforced agents plan use my-action # InvariantReconciliationActor finds 0 invariants ← BUG ``` --- ## Fix Required The CLI's `_get_service()` should use the DI container's singleton `InvariantService` instead of creating a new in-memory instance. This requires either: 1. Injecting the container's `InvariantService` into the CLI command (preferred), or 2. Implementing database-backed persistence in `InvariantService` so that in-memory instances are backed by the same storage layer. The `InvariantService` docstring itself says "Uses in-memory storage (same pattern as `PlanLifecycleService`)" — but `PlanLifecycleService` IS the container singleton, while the CLI creates a separate instance. --- ## Impact This is a **critical bug** that directly blocks the v3.2.0 acceptance criterion: > "Invariants are enforced during strategize" Without this fix, the invariant management feature is non-functional end-to-end. --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
HAL9000 added this to the v3.2.0 milestone 2026-04-09 14:18:42 +00:00
Author
Owner

Label compliance fix applied:

  • Added missing labels and/or milestone to bring issue into compliance with CONTRIBUTING.md

Automated by CleverAgents Bot
Supervisor: Backlog Grooming | Agent: backlog-groomer

Label compliance fix applied: - Added missing labels and/or milestone to bring issue into compliance with CONTRIBUTING.md --- **Automated by CleverAgents Bot** Supervisor: Backlog Grooming | Agent: backlog-groomer
Author
Owner

Issue triaged by project owner:

  • State: Verified
  • Priority: Critical — agents invariant add uses an isolated in-memory service, meaning invariants are never persisted and never enforced during the Strategize phase. This directly blocks v3.2.0 acceptance criteria: "invariants are enforced at every phase transition."
  • Milestone: v3.2.0 — This is a core deliverable for M3 (Decisions + Validations + Invariants). The milestone cannot ship without persistent invariant enforcement.
  • Story Points: 5 — L — Requires wiring the CLI to the persistent InvariantRepository and ensuring the service is injected correctly through the dependency chain.
  • MoSCoW: Must Have — The spec explicitly requires invariants to be enforced at phase transitions. Without persistence, the invariant system is entirely non-functional.
  • Parent Epic: Needs linking to the Invariant Management epic under Legendary #375.

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

Issue triaged by project owner: - **State**: Verified - **Priority**: Critical — `agents invariant add` uses an isolated in-memory service, meaning invariants are never persisted and never enforced during the Strategize phase. This directly blocks v3.2.0 acceptance criteria: "invariants are enforced at every phase transition." - **Milestone**: v3.2.0 — This is a core deliverable for M3 (Decisions + Validations + Invariants). The milestone cannot ship without persistent invariant enforcement. - **Story Points**: 5 — L — Requires wiring the CLI to the persistent `InvariantRepository` and ensuring the service is injected correctly through the dependency chain. - **MoSCoW**: Must Have — The spec explicitly requires invariants to be enforced at phase transitions. Without persistence, the invariant system is entirely non-functional. - **Parent Epic**: Needs linking to the Invariant Management epic under Legendary #375. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: 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#6038
No description provided.