UAT: NamespacedProject domain model missing invariants and invariant_actor fields — project invariants stored via raw SQL bypass #4818

Open
opened 2026-04-08 19:41:45 +00:00 by HAL9000 · 1 comment
Owner

Bug Report

Tested by: UAT tester instance uat-tester-project-model-1
Feature area: Project Model — Invariant Attachment

What Was Tested

The NamespacedProject domain model's ability to carry project-level invariants as specified.

Expected Behavior (from spec)

The NamespacedProject domain model should expose invariants (list of invariant texts) and invariant_actor (the actor used for invariant reconciliation) as first-class fields, so they can be retrieved and used by the Invariant Reconciliation Actor at Strategize start.

Actual Behavior

The NamespacedProject Pydantic model in src/cleveragents/domain/models/core/project.py has no invariants or invariant_actor fields:

class NamespacedProject(BaseModel):
    # Identity
    name: str
    namespace: str
    server: str | None
    description: str | None
    linked_resources: list[LinkedResource]
    context_config: ContextConfig
    created_at: datetime
    updated_at: datetime
    # ← NO invariants field
    # ← NO invariant_actor field

However, the agents project create CLI command stores these via raw SQL directly on the ns_projects table:

# src/cleveragents/cli/commands/project.py
def _store_project_extras(
    namespaced_name: str,
    invariant_texts: list[str] | None = None,
    inv_actor: str | None = None,
) -> None:
    """Persist invariant list and invariant_actor directly via SQL session.

    These fields live on the `ns_projects` table but are not exposed on
    the `NamespacedProject` Pydantic domain model (they're JSON columns),
    so we update them via a lightweight session outside the repository API.
    """
    ...
    sql = text(
        f"UPDATE ns_projects SET {', '.join(updates)} "
        f"WHERE namespaced_name = :ns_name"
    )

The comment in the code itself acknowledges this is a workaround: "These fields live on the ns_projects table but are not exposed on the NamespacedProject Pydantic domain model".

Steps to Reproduce

# Create a project with invariants
agents project create local/myapp --invariant "Never delete production data" --invariant-actor anthropic/claude-opus-4-5

# Show the project - invariants are NOT displayed
agents project show local/myapp
# Output: No invariants shown in project details

# Try to retrieve invariants via domain model - impossible
# NamespacedProjectRepository.get("local/myapp") returns NamespacedProject
# with no invariants field

Code Location

  • src/cleveragents/domain/models/core/project.pyNamespacedProject class missing invariants and invariant_actor fields
  • src/cleveragents/cli/commands/project.py_store_project_extras() bypasses domain model with raw SQL
  • src/cleveragents/cli/commands/project.py_project_spec_dict() doesn't include invariants in output

Impact

  • Project invariants set at creation time cannot be retrieved through the domain model
  • agents project show doesn't display project invariants
  • The Invariant Reconciliation Actor cannot load project-scoped invariants from the NamespacedProject object
  • The domain model is inconsistent with the database schema

Fix Direction

Add invariants: list[str] and invariant_actor: str | None fields to NamespacedProject. Update NamespacedProjectRepository to persist and retrieve these fields. Remove the _store_project_extras raw SQL workaround.


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

## Bug Report **Tested by:** UAT tester instance `uat-tester-project-model-1` **Feature area:** Project Model — Invariant Attachment ### What Was Tested The `NamespacedProject` domain model's ability to carry project-level invariants as specified. ### Expected Behavior (from spec) The `NamespacedProject` domain model should expose `invariants` (list of invariant texts) and `invariant_actor` (the actor used for invariant reconciliation) as first-class fields, so they can be retrieved and used by the Invariant Reconciliation Actor at Strategize start. ### Actual Behavior The `NamespacedProject` Pydantic model in `src/cleveragents/domain/models/core/project.py` has **no `invariants` or `invariant_actor` fields**: ```python class NamespacedProject(BaseModel): # Identity name: str namespace: str server: str | None description: str | None linked_resources: list[LinkedResource] context_config: ContextConfig created_at: datetime updated_at: datetime # ← NO invariants field # ← NO invariant_actor field ``` However, the `agents project create` CLI command stores these via raw SQL directly on the `ns_projects` table: ```python # src/cleveragents/cli/commands/project.py def _store_project_extras( namespaced_name: str, invariant_texts: list[str] | None = None, inv_actor: str | None = None, ) -> None: """Persist invariant list and invariant_actor directly via SQL session. These fields live on the `ns_projects` table but are not exposed on the `NamespacedProject` Pydantic domain model (they're JSON columns), so we update them via a lightweight session outside the repository API. """ ... sql = text( f"UPDATE ns_projects SET {', '.join(updates)} " f"WHERE namespaced_name = :ns_name" ) ``` The comment in the code itself acknowledges this is a workaround: *"These fields live on the `ns_projects` table but are not exposed on the `NamespacedProject` Pydantic domain model"*. ### Steps to Reproduce ```bash # Create a project with invariants agents project create local/myapp --invariant "Never delete production data" --invariant-actor anthropic/claude-opus-4-5 # Show the project - invariants are NOT displayed agents project show local/myapp # Output: No invariants shown in project details # Try to retrieve invariants via domain model - impossible # NamespacedProjectRepository.get("local/myapp") returns NamespacedProject # with no invariants field ``` ### Code Location - `src/cleveragents/domain/models/core/project.py` — `NamespacedProject` class missing `invariants` and `invariant_actor` fields - `src/cleveragents/cli/commands/project.py` — `_store_project_extras()` bypasses domain model with raw SQL - `src/cleveragents/cli/commands/project.py` — `_project_spec_dict()` doesn't include invariants in output ### Impact - Project invariants set at creation time cannot be retrieved through the domain model - `agents project show` doesn't display project invariants - The Invariant Reconciliation Actor cannot load project-scoped invariants from the `NamespacedProject` object - The domain model is inconsistent with the database schema ### Fix Direction Add `invariants: list[str]` and `invariant_actor: str | None` fields to `NamespacedProject`. Update `NamespacedProjectRepository` to persist and retrieve these fields. Remove the `_store_project_extras` raw SQL workaround. --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
Author
Owner

Issue triaged by project owner:

  • State: Verified
  • Priority: Medium — spec compliance bug identified by UAT testing
  • Story Points: 3 (M) — targeted fix to align implementation with spec
  • MoSCoW: Must Have — spec compliance is required for correct system behavior

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

Issue triaged by project owner: - **State**: Verified - **Priority**: Medium — spec compliance bug identified by UAT testing - **Story Points**: 3 (M) — targeted fix to align implementation with spec - **MoSCoW**: Must Have — spec compliance is required for correct system behavior --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner
HAL9000 added this to the v3.5.0 milestone 2026-04-09 03:03:09 +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#4818
No description provided.