UAT: NamespacedProjectModel.from_domain() does not persist invariants, automation_profile, or invariant_actor from domain model #5468

Open
opened 2026-04-09 06:56:11 +00:00 by HAL9000 · 1 comment
Owner

Bug Report

Feature Area: Database / Migrations — Domain Model Persistence
Severity: Backlog — data loss on project persistence
Found by: UAT Testing (database-migrations worker)


Summary

NamespacedProjectModel.from_domain() (models.py:1390-1422) hardcodes invariants_json=json.dumps([]), automation_profile=None, and invariant_actor=None regardless of what the NamespacedProject domain model contains. This means project invariants, automation profiles, and invariant actors are silently dropped when a project is persisted.

Evidence

NamespacedProjectModel.from_domain() (models.py:1408-1420):

model = cls(
    namespaced_name=project.namespaced_name,
    namespace=project.namespace,
    description=project.description,
    invariants_json=json.dumps([]),  # ← Always empty! Ignores project.invariants
    automation_profile=None,         # ← Always None! Ignores project.automation_profile
    invariant_actor=None,            # ← Always None! Ignores project.invariant_actor
    context_policy_json=context_policy_json,
    tags_json=tags_json_str,
    created_by=None,
    created_at=project.created_at.isoformat(),
    updated_at=project.updated_at.isoformat(),
)

NamespacedProjectModel columns (models.py:1312-1318):

invariants_json = Column(Text, nullable=True)
automation_profile = Column(String(255), nullable=True)
invariant_actor = Column(String(255), nullable=True)

NamespacedProjectModel.to_domain() (models.py:1344-1388) does NOT reconstruct invariants, automation_profile, or invariant_actor from the database — these fields are not mapped back to the domain model at all.

Impact

  1. Project invariants set via agents project invariant add are not persisted to the ns_projects table
  2. Project automation profiles are not persisted
  3. The spec's project-scoped invariant precedence chain (plan > action > project > global) cannot work correctly if project invariants are not stored

Note: Project invariants may be stored in a separate table (e.g., via InvariantService), but the ns_projects.invariants_json column is never populated, creating a schema-code mismatch.

Expected Behavior

from_domain() should serialize all domain model fields:

model = cls(
    ...
    invariants_json=json.dumps(getattr(project, "invariants", []) or []),
    automation_profile=getattr(project, "automation_profile", None),
    invariant_actor=getattr(project, "invariant_actor", None),
    ...
)

And to_domain() should reconstruct these fields:

return NamespacedProject(
    ...
    invariants=json.loads(self.invariants_json or "[]"),
    automation_profile=self.automation_profile,
    invariant_actor=self.invariant_actor,
)

Code Locations

  • src/cleveragents/infrastructure/database/models.py:1408-1420from_domain() hardcoding
  • src/cleveragents/infrastructure/database/models.py:1344-1388to_domain() missing fields

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

## Bug Report **Feature Area**: Database / Migrations — Domain Model Persistence **Severity**: Backlog — data loss on project persistence **Found by**: UAT Testing (database-migrations worker) --- ## Summary `NamespacedProjectModel.from_domain()` (models.py:1390-1422) hardcodes `invariants_json=json.dumps([])`, `automation_profile=None`, and `invariant_actor=None` regardless of what the `NamespacedProject` domain model contains. This means project invariants, automation profiles, and invariant actors are silently dropped when a project is persisted. ## Evidence **`NamespacedProjectModel.from_domain()`** (models.py:1408-1420): ```python model = cls( namespaced_name=project.namespaced_name, namespace=project.namespace, description=project.description, invariants_json=json.dumps([]), # ← Always empty! Ignores project.invariants automation_profile=None, # ← Always None! Ignores project.automation_profile invariant_actor=None, # ← Always None! Ignores project.invariant_actor context_policy_json=context_policy_json, tags_json=tags_json_str, created_by=None, created_at=project.created_at.isoformat(), updated_at=project.updated_at.isoformat(), ) ``` **`NamespacedProjectModel` columns** (models.py:1312-1318): ```python invariants_json = Column(Text, nullable=True) automation_profile = Column(String(255), nullable=True) invariant_actor = Column(String(255), nullable=True) ``` **`NamespacedProjectModel.to_domain()`** (models.py:1344-1388) does NOT reconstruct invariants, automation_profile, or invariant_actor from the database — these fields are not mapped back to the domain model at all. ## Impact 1. Project invariants set via `agents project invariant add` are not persisted to the `ns_projects` table 2. Project automation profiles are not persisted 3. The spec's project-scoped invariant precedence chain (plan > action > **project** > global) cannot work correctly if project invariants are not stored **Note**: Project invariants may be stored in a separate table (e.g., via `InvariantService`), but the `ns_projects.invariants_json` column is never populated, creating a schema-code mismatch. ## Expected Behavior `from_domain()` should serialize all domain model fields: ```python model = cls( ... invariants_json=json.dumps(getattr(project, "invariants", []) or []), automation_profile=getattr(project, "automation_profile", None), invariant_actor=getattr(project, "invariant_actor", None), ... ) ``` And `to_domain()` should reconstruct these fields: ```python return NamespacedProject( ... invariants=json.loads(self.invariants_json or "[]"), automation_profile=self.automation_profile, invariant_actor=self.invariant_actor, ) ``` ## Code Locations - `src/cleveragents/infrastructure/database/models.py:1408-1420` — `from_domain()` hardcoding - `src/cleveragents/infrastructure/database/models.py:1344-1388` — `to_domain()` missing fields --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
HAL9000 added this to the v3.2.0 milestone 2026-04-09 06:59:19 +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
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#5468
No description provided.