UAT: NamespacedProject domain model missing invariants and invariant_actor fields — spec-required project data model fields absent #1848

Open
opened 2026-04-02 23:57:59 +00:00 by freemo · 2 comments
Owner

Metadata

  • Branch: fix/namespaced-project-invariants
  • Commit Message: fix(domain): add invariants and invariant_actor fields to NamespacedProject model
  • Milestone: v3.7.0
  • Parent Epic: (see dependency link — no suitable Epic found; linked to v3.7.0 milestone)

Description

The specification defines a Project as "A named scope linking resources (from the Resource Registry), context policies, invariants, and validation attachments." The agents project create command accepts --invariant and --invariant-actor flags. However, the NamespacedProject domain model does not expose these fields, causing the CLI to bypass the repository pattern with raw SQL to persist them.

What was tested: Code-level analysis of src/cleveragents/domain/models/core/project.py and src/cleveragents/infrastructure/database/models.py.

Expected Behavior (from spec)

Per the specification (Project Data Model section and agents project create command):

  • Projects have invariants (list of constraint strings) and invariant_actor (reconciliation actor name) as first-class fields.
  • The NamespacedProject domain model should expose these fields.
  • The NamespacedProjectRepository should persist/retrieve these fields through the normal domain model.

Actual Behavior

  1. NamespacedProject domain model (src/cleveragents/domain/models/core/project.py) has NO invariants or invariant_actor fields.

  2. NamespacedProjectModel.to_domain() (src/cleveragents/infrastructure/database/models.py) does NOT populate invariants or invariant_actor when converting from DB to domain model — even though the ns_projects table has invariants_json and invariant_actor columns.

  3. NamespacedProjectModel.from_domain() hardcodes invariants_json=json.dumps([]) and invariant_actor=None, discarding any invariants.

  4. CLI workaround: src/cleveragents/cli/commands/project.py uses a _store_project_extras() function that bypasses the repository pattern entirely, using raw SQL to update invariants_json and invariant_actor directly:

sql = text(
    f"UPDATE ns_projects SET {', '.join(updates)} "
    f"WHERE namespaced_name = :ns_name"
)
session.execute(sql, params)
session.commit()

Impact

  1. Project invariants set via agents project create --invariant "..." are persisted to the DB via raw SQL but are NEVER returned when reading a project back through the repository — NamespacedProjectRepository.get() returns a NamespacedProject with no invariants.
  2. The invariant data is silently lost from the domain model perspective.
  3. Any service that reads a project via the repository will not see its invariants, breaking the invariant enforcement chain.

Code Locations

  • src/cleveragents/domain/models/core/project.pyNamespacedProject class (missing fields)
  • src/cleveragents/infrastructure/database/models.pyNamespacedProjectModel.to_domain() and from_domain() (not mapping invariant fields)
  • src/cleveragents/cli/commands/project.py_store_project_extras() (raw SQL workaround)

Steps to Reproduce

# Check NamespacedProject fields
python3 -c "
from cleveragents.domain.models.core.project import NamespacedProject
print(list(NamespacedProject.model_fields.keys()))
# Expected: includes 'invariants', 'invariant_actor'
# Actual: no invariants or invariant_actor fields
"

Subtasks

  • Add invariants: list[str] field (default []) to NamespacedProject domain model in src/cleveragents/domain/models/core/project.py
  • Add invariant_actor: str | None field (default None) to NamespacedProject domain model
  • Update NamespacedProjectModel.to_domain() in src/cleveragents/infrastructure/database/models.py to populate invariants and invariant_actor from DB columns
  • Update NamespacedProjectModel.from_domain() to correctly serialize invariants and invariant_actor from the domain model instead of hardcoding empty values
  • Remove or refactor _store_project_extras() raw SQL workaround in src/cleveragents/cli/commands/project.py to use the repository pattern
  • Add/update unit tests for NamespacedProject model fields
  • Add/update unit tests for NamespacedProjectModel.to_domain() and from_domain() round-trip with invariant data
  • Add integration/Behave test verifying agents project create --invariant "..." --invariant-actor "..." persists and retrieves correctly via repository

Definition of Done

  • NamespacedProject domain model exposes invariants: list[str] and invariant_actor: str | None as first-class Pydantic fields
  • NamespacedProjectModel.to_domain() correctly maps invariants_json and invariant_actor DB columns to domain model fields
  • NamespacedProjectModel.from_domain() correctly serializes domain model invariants and invariant_actor to DB columns
  • _store_project_extras() raw SQL workaround is removed; CLI uses repository pattern for all project persistence
  • Round-trip test: project created with invariants via CLI is retrievable with same invariants via NamespacedProjectRepository.get()
  • All nox stages pass
  • Coverage >= 97%

Automated by CleverAgents Bot
Supervisor: UAT Testing | Agent: ca-new-issue-creator

## Metadata - **Branch**: `fix/namespaced-project-invariants` - **Commit Message**: `fix(domain): add invariants and invariant_actor fields to NamespacedProject model` - **Milestone**: v3.7.0 - **Parent Epic**: *(see dependency link — no suitable Epic found; linked to v3.7.0 milestone)* ## Description The specification defines a Project as "A named scope linking resources (from the Resource Registry), context policies, **invariants**, and validation attachments." The `agents project create` command accepts `--invariant` and `--invariant-actor` flags. However, the `NamespacedProject` domain model does not expose these fields, causing the CLI to bypass the repository pattern with raw SQL to persist them. **What was tested**: Code-level analysis of `src/cleveragents/domain/models/core/project.py` and `src/cleveragents/infrastructure/database/models.py`. ## Expected Behavior (from spec) Per the specification (Project Data Model section and `agents project create` command): - Projects have `invariants` (list of constraint strings) and `invariant_actor` (reconciliation actor name) as first-class fields. - The `NamespacedProject` domain model should expose these fields. - The `NamespacedProjectRepository` should persist/retrieve these fields through the normal domain model. ## Actual Behavior 1. **`NamespacedProject` domain model** (`src/cleveragents/domain/models/core/project.py`) has NO `invariants` or `invariant_actor` fields. 2. **`NamespacedProjectModel.to_domain()`** (`src/cleveragents/infrastructure/database/models.py`) does NOT populate `invariants` or `invariant_actor` when converting from DB to domain model — even though the `ns_projects` table has `invariants_json` and `invariant_actor` columns. 3. **`NamespacedProjectModel.from_domain()`** hardcodes `invariants_json=json.dumps([])` and `invariant_actor=None`, discarding any invariants. 4. **CLI workaround**: `src/cleveragents/cli/commands/project.py` uses a `_store_project_extras()` function that bypasses the repository pattern entirely, using raw SQL to update `invariants_json` and `invariant_actor` directly: ```python sql = text( f"UPDATE ns_projects SET {', '.join(updates)} " f"WHERE namespaced_name = :ns_name" ) session.execute(sql, params) session.commit() ``` ## Impact 1. Project invariants set via `agents project create --invariant "..."` are persisted to the DB via raw SQL but are NEVER returned when reading a project back through the repository — `NamespacedProjectRepository.get()` returns a `NamespacedProject` with no invariants. 2. The invariant data is silently lost from the domain model perspective. 3. Any service that reads a project via the repository will not see its invariants, breaking the invariant enforcement chain. ## Code Locations - `src/cleveragents/domain/models/core/project.py` — `NamespacedProject` class (missing fields) - `src/cleveragents/infrastructure/database/models.py` — `NamespacedProjectModel.to_domain()` and `from_domain()` (not mapping invariant fields) - `src/cleveragents/cli/commands/project.py` — `_store_project_extras()` (raw SQL workaround) ## Steps to Reproduce ```bash # Check NamespacedProject fields python3 -c " from cleveragents.domain.models.core.project import NamespacedProject print(list(NamespacedProject.model_fields.keys())) # Expected: includes 'invariants', 'invariant_actor' # Actual: no invariants or invariant_actor fields " ``` ## Subtasks - [ ] Add `invariants: list[str]` field (default `[]`) to `NamespacedProject` domain model in `src/cleveragents/domain/models/core/project.py` - [ ] Add `invariant_actor: str | None` field (default `None`) to `NamespacedProject` domain model - [ ] Update `NamespacedProjectModel.to_domain()` in `src/cleveragents/infrastructure/database/models.py` to populate `invariants` and `invariant_actor` from DB columns - [ ] Update `NamespacedProjectModel.from_domain()` to correctly serialize `invariants` and `invariant_actor` from the domain model instead of hardcoding empty values - [ ] Remove or refactor `_store_project_extras()` raw SQL workaround in `src/cleveragents/cli/commands/project.py` to use the repository pattern - [ ] Add/update unit tests for `NamespacedProject` model fields - [ ] Add/update unit tests for `NamespacedProjectModel.to_domain()` and `from_domain()` round-trip with invariant data - [ ] Add integration/Behave test verifying `agents project create --invariant "..." --invariant-actor "..."` persists and retrieves correctly via repository ## Definition of Done - [ ] `NamespacedProject` domain model exposes `invariants: list[str]` and `invariant_actor: str | None` as first-class Pydantic fields - [ ] `NamespacedProjectModel.to_domain()` correctly maps `invariants_json` and `invariant_actor` DB columns to domain model fields - [ ] `NamespacedProjectModel.from_domain()` correctly serializes domain model `invariants` and `invariant_actor` to DB columns - [ ] `_store_project_extras()` raw SQL workaround is removed; CLI uses repository pattern for all project persistence - [ ] Round-trip test: project created with invariants via CLI is retrievable with same invariants via `NamespacedProjectRepository.get()` - [ ] 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-02 23:58:30 +00:00
Author
Owner

⚠️ Orphan Issue — Manual Epic Linking Required

No suitable parent Epic was found for this issue during automated creation. A search of all open Type/Epic issues in this repository returned only:

  • #1678: Epic: CI Execution Time Optimization — Timeouts, Concurrency, and Coverage Artifact Sharing (unrelated — CI/test infrastructure)

This issue (UAT: NamespacedProject domain model missing invariants and invariant_actor fields) is a domain model bug related to the Project data model and repository pattern. It would most naturally belong under an Epic covering:

  • Domain model completeness / spec compliance
  • Project feature implementation
  • UAT findings for the agents project command group

Action required: A project maintainer should either:

  1. Create a new Epic for "UAT: Domain Model Spec Compliance" or "Project Feature Completeness" and link this issue as a child (this issue blocks the parent Epic), or
  2. Manually link this issue to the most appropriate existing Epic using Forgejo's dependency system.

Dependency direction reminder: This child issue should block the parent Epic (child → blocks → parent).


Automated by CleverAgents Bot
Supervisor: UAT Testing | Agent: ca-new-issue-creator

⚠️ **Orphan Issue — Manual Epic Linking Required** No suitable parent Epic was found for this issue during automated creation. A search of all open `Type/Epic` issues in this repository returned only: - **#1678**: Epic: CI Execution Time Optimization — Timeouts, Concurrency, and Coverage Artifact Sharing *(unrelated — CI/test infrastructure)* This issue (`UAT: NamespacedProject domain model missing invariants and invariant_actor fields`) is a **domain model bug** related to the Project data model and repository pattern. It would most naturally belong under an Epic covering: - Domain model completeness / spec compliance - Project feature implementation - UAT findings for the `agents project` command group **Action required**: A project maintainer should either: 1. Create a new Epic for "UAT: Domain Model Spec Compliance" or "Project Feature Completeness" and link this issue as a child (this issue **blocks** the parent Epic), or 2. Manually link this issue to the most appropriate existing Epic using Forgejo's dependency system. **Dependency direction reminder**: This child issue should **block** the parent Epic (child → blocks → parent). --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: ca-new-issue-creator
Author
Owner

Issue triaged by project owner:

  • State: Verified
  • MoSCoW: MoSCoW/Should Have — bug or error handling improvement.

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

Issue triaged by project owner: - **State**: Verified - **MoSCoW**: MoSCoW/Should Have — bug or error handling improvement. --- **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.

Dependencies

No dependencies set.

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