UAT: NamespacedProjectModel.to_domain() silently drops invariants_json and invariant_actor columns — project invariants lost on read #1844

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

Metadata

  • Branch: fix/namespaced-project-model-to-domain
  • Commit Message: fix(infra): map invariants_json and invariant_actor in NamespacedProjectModel.to_domain()
  • Milestone: v3.7.0
  • Parent Epic: N/A — no suitable Epic exists for infrastructure model mapping fixes; linked to v3.7.0 milestone per triage guidance

Description

The ns_projects database table has invariants_json and invariant_actor columns. However, NamespacedProjectModel.to_domain() does not map these columns to the domain model, causing project invariants to be silently lost whenever a project is read from the database through the repository.

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

Expected Behavior

NamespacedProjectModel.to_domain() should map all persisted columns to the domain model, including invariants_json (deserializing to a list of strings) and invariant_actor (a string or None).

Actual Behavior

NamespacedProjectModel.to_domain() (lines ~1315–1355 in models.py) constructs a NamespacedProject without including invariants_json or invariant_actor:

def to_domain(self) -> Any:
    # ... parses context_config, linked_resources ...
    return NamespacedProject(
        name=short_name,
        namespace=cast(str, self.namespace),
        description=cast("str | None", self.description),
        linked_resources=linked_resources,
        context_config=context_config,
        created_at=datetime.fromisoformat(cast(str, self.created_at)),
        updated_at=datetime.fromisoformat(cast(str, self.updated_at)),
        # MISSING: invariants (from invariants_json column)
        # MISSING: invariant_actor (from invariant_actor column)
    )

Similarly, from_domain() hardcodes these values:

model = cls(
    ...
    invariants_json=json.dumps([]),  # ALWAYS empty, ignores domain model
    invariant_actor=None,            # ALWAYS None, ignores domain model
    ...
)

Impact

  • Any project invariants written to the DB (e.g., via the _store_project_extras() raw SQL workaround in cli/commands/project.py) are silently discarded when the project is read back through NamespacedProjectRepository.get().
  • The invariant enforcement chain is broken: services that read projects via the repository will never see project-level invariants.
  • This is a data loss bug — data is persisted but never returned.

Code location: src/cleveragents/infrastructure/database/models.pyNamespacedProjectModel.to_domain() and from_domain()

Note: This bug is related to but distinct from the missing invariants/invariant_actor fields on the NamespacedProject domain model (separate issue). Both must be fixed together for project invariants to work end-to-end.

Subtasks

  • Audit NamespacedProjectModel.to_domain() to identify all missing column mappings (invariants_json, invariant_actor)
  • Update to_domain() to deserialize invariants_json (JSON string → list[str]) and pass invariant_actor to the NamespacedProject constructor
  • Update from_domain() to serialize invariants from the domain model to invariants_json (JSON string) and map invariant_actor correctly
  • Verify NamespacedProject domain model accepts invariants and invariant_actor fields (coordinate with domain model fix if needed)
  • Add/update unit tests for NamespacedProjectModel.to_domain() and from_domain() round-trip with non-empty invariants
  • Add/update integration tests for NamespacedProjectRepository.get() to assert invariants are preserved after write/read cycle
  • Run nox -e unit_tests and nox -e integration_tests to confirm no regressions

Definition of Done

  • NamespacedProjectModel.to_domain() correctly maps invariants_jsoninvariants: list[str] and invariant_actorinvariant_actor: str | None
  • NamespacedProjectModel.from_domain() correctly serializes invariants and invariant_actor from the domain model (no hardcoded empty values)
  • Round-trip test: a project written with non-empty invariants reads back with those invariants intact via NamespacedProjectRepository.get()
  • No existing tests broken
  • All nox stages pass
  • Coverage >= 97%

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

## Metadata - **Branch**: `fix/namespaced-project-model-to-domain` - **Commit Message**: `fix(infra): map invariants_json and invariant_actor in NamespacedProjectModel.to_domain()` - **Milestone**: v3.7.0 - **Parent Epic**: N/A — no suitable Epic exists for infrastructure model mapping fixes; linked to v3.7.0 milestone per triage guidance ## Description The `ns_projects` database table has `invariants_json` and `invariant_actor` columns. However, `NamespacedProjectModel.to_domain()` does not map these columns to the domain model, causing project invariants to be **silently lost** whenever a project is read from the database through the repository. **What was tested**: Code-level analysis of `src/cleveragents/infrastructure/database/models.py`. ### Expected Behavior `NamespacedProjectModel.to_domain()` should map all persisted columns to the domain model, including `invariants_json` (deserializing to a list of strings) and `invariant_actor` (a string or None). ### Actual Behavior `NamespacedProjectModel.to_domain()` (lines ~1315–1355 in `models.py`) constructs a `NamespacedProject` without including `invariants_json` or `invariant_actor`: ```python def to_domain(self) -> Any: # ... parses context_config, linked_resources ... return NamespacedProject( name=short_name, namespace=cast(str, self.namespace), description=cast("str | None", self.description), linked_resources=linked_resources, context_config=context_config, created_at=datetime.fromisoformat(cast(str, self.created_at)), updated_at=datetime.fromisoformat(cast(str, self.updated_at)), # MISSING: invariants (from invariants_json column) # MISSING: invariant_actor (from invariant_actor column) ) ``` Similarly, `from_domain()` hardcodes these values: ```python model = cls( ... invariants_json=json.dumps([]), # ALWAYS empty, ignores domain model invariant_actor=None, # ALWAYS None, ignores domain model ... ) ``` ### Impact - Any project invariants written to the DB (e.g., via the `_store_project_extras()` raw SQL workaround in `cli/commands/project.py`) are silently discarded when the project is read back through `NamespacedProjectRepository.get()`. - The invariant enforcement chain is broken: services that read projects via the repository will never see project-level invariants. - This is a **data loss bug** — data is persisted but never returned. **Code location**: `src/cleveragents/infrastructure/database/models.py` — `NamespacedProjectModel.to_domain()` and `from_domain()` **Note**: This bug is related to but distinct from the missing `invariants`/`invariant_actor` fields on the `NamespacedProject` domain model (separate issue). Both must be fixed together for project invariants to work end-to-end. ## Subtasks - [ ] Audit `NamespacedProjectModel.to_domain()` to identify all missing column mappings (`invariants_json`, `invariant_actor`) - [ ] Update `to_domain()` to deserialize `invariants_json` (JSON string → `list[str]`) and pass `invariant_actor` to the `NamespacedProject` constructor - [ ] Update `from_domain()` to serialize `invariants` from the domain model to `invariants_json` (JSON string) and map `invariant_actor` correctly - [ ] Verify `NamespacedProject` domain model accepts `invariants` and `invariant_actor` fields (coordinate with domain model fix if needed) - [ ] Add/update unit tests for `NamespacedProjectModel.to_domain()` and `from_domain()` round-trip with non-empty invariants - [ ] Add/update integration tests for `NamespacedProjectRepository.get()` to assert invariants are preserved after write/read cycle - [ ] Run `nox -e unit_tests` and `nox -e integration_tests` to confirm no regressions ## Definition of Done - [ ] `NamespacedProjectModel.to_domain()` correctly maps `invariants_json` → `invariants: list[str]` and `invariant_actor` → `invariant_actor: str | None` - [ ] `NamespacedProjectModel.from_domain()` correctly serializes `invariants` and `invariant_actor` from the domain model (no hardcoded empty values) - [ ] Round-trip test: a project written with non-empty invariants reads back with those invariants intact via `NamespacedProjectRepository.get()` - [ ] No existing tests broken - [ ] 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:57:41 +00:00
Author
Owner

⚠️ Orphan Issue — Manual Epic Linking Required

This issue could not be automatically linked to a parent Epic. A thorough search of all open Epics was performed:

  • #1678 Epic: CI Execution Time Optimization — not relevant (CI/infra tooling)
  • #1020 [Epic] Database resource hierarchy restructuring — not relevant (resource type hierarchy)
  • #936 Epic: Output Rendering Pipeline Integration — not relevant (CLI output)
  • #935 Epic: ACMS Context Assembly Pipeline — not relevant (ACMS)
  • #934 Epic: LSP Runtime Implementation — not relevant (LSP)
  • #933 Epic: A2A Protocol Compliance — not relevant (A2A)
  • #946 Epic: Stub Package Cleanup — not relevant

The closest related issues are:

  • #843 feat(plan): enforce invariants during Strategize phase — related to invariant enforcement but is a Feature, not an Epic
  • #1766 UAT: NamespacedProjectRepository auto-commit violation — sibling infrastructure bug

Action required: A project maintainer should either:

  1. Link this issue to an appropriate existing Epic, or
  2. Create a new Epic for "Infrastructure Model Mapping Correctness" and link this issue as a child

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

⚠️ **Orphan Issue — Manual Epic Linking Required** This issue could not be automatically linked to a parent Epic. A thorough search of all open Epics was performed: - **#1678** Epic: CI Execution Time Optimization — not relevant (CI/infra tooling) - **#1020** [Epic] Database resource hierarchy restructuring — not relevant (resource type hierarchy) - **#936** Epic: Output Rendering Pipeline Integration — not relevant (CLI output) - **#935** Epic: ACMS Context Assembly Pipeline — not relevant (ACMS) - **#934** Epic: LSP Runtime Implementation — not relevant (LSP) - **#933** Epic: A2A Protocol Compliance — not relevant (A2A) - **#946** Epic: Stub Package Cleanup — not relevant The closest related issues are: - **#843** `feat(plan): enforce invariants during Strategize phase` — related to invariant enforcement but is a Feature, not an Epic - **#1766** `UAT: NamespacedProjectRepository auto-commit violation` — sibling infrastructure bug **Action required**: A project maintainer should either: 1. Link this issue to an appropriate existing Epic, or 2. Create a new Epic for "Infrastructure Model Mapping Correctness" and link this issue as a child --- **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#1844
No description provided.