UAT: UnitOfWorkContext does not expose ResourceRepository, ResourceTypeRepository, or NamespacedProjectRepository — resource/project operations bypass UoW transactions #5435

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

Bug Report

Feature Area: Database / Migrations — Unit of Work Pattern
Severity: Backlog — architectural gap
Found by: UAT Testing (database-migrations worker)


Summary

The UnitOfWorkContext class (the context yielded by UnitOfWork.transaction()) exposes repositories for plans, actions, decisions, checkpoints, actors, and correction attempts — but does not expose ResourceRepository, ResourceTypeRepository, or NamespacedProjectRepository. This means resource and project operations cannot participate in the same database transaction as plan operations, violating the Unit of Work pattern's atomicity guarantee.

Evidence

UnitOfWorkContext properties (unit_of_work.py:175-332):

class UnitOfWorkContext:
    @property
    def projects(self) -> ProjectRepository:  # Legacy projects table only
    @property
    def plans(self) -> PlanRepository:  # Deprecated legacy plans
    @property
    def actions(self) -> ActionRepository:
    @property
    def lifecycle_plans(self) -> LifecyclePlanRepository:
    @property
    def checkpoints(self) -> CheckpointRepository:
    @property
    def contexts(self) -> ContextRepository:
    @property
    def changes(self) -> ChangeRepository:
    @property
    def debug_attempts(self) -> DebugAttemptRepository:
    @property
    def actors(self) -> ActorRepository:
    @property
    def decisions(self) -> DecisionRepository:
    @property
    def correction_attempts(self) -> CorrectionAttemptRepository:
    # MISSING: resource_types, resources, namespaced_projects

NamespacedProjectRepository.create() (repositories.py:2960):

def create(self, project: Any) -> Any:
    session = self._session()
    try:
        db_model = NamespacedProjectModel.from_domain(project)
        session.add(db_model)
        session.commit()  # ← Commits its own transaction independently!
        return project

The NamespacedProjectRepository commits its own transactions, making it impossible to atomically create a project and its first plan in a single transaction.

Impact

  1. Creating a project and linking it to a plan cannot be done atomically — partial failures leave orphaned data
  2. Resource registration and plan creation cannot be rolled back together
  3. The spec's Unit of Work pattern (ADR-007) is only partially implemented

Expected Behavior

Per ADR-007 (Repository Pattern), all repositories should be accessible through UnitOfWorkContext so that cross-entity operations can be committed or rolled back atomically:

with uow.transaction() as ctx:
    project = ctx.namespaced_projects.create(project_domain)
    plan = ctx.lifecycle_plans.create(plan_domain)
    # Both committed atomically, or both rolled back on error

Fix

  1. Add namespaced_projects, resource_types, and resources properties to UnitOfWorkContext
  2. Refactor NamespacedProjectRepository to use the session-factory pattern (flush but not commit) consistent with other repositories
  3. Add corresponding _namespaced_projects, _resource_types, _resources private fields to UnitOfWorkContext.__init__

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

## Bug Report **Feature Area**: Database / Migrations — Unit of Work Pattern **Severity**: Backlog — architectural gap **Found by**: UAT Testing (database-migrations worker) --- ## Summary The `UnitOfWorkContext` class (the context yielded by `UnitOfWork.transaction()`) exposes repositories for plans, actions, decisions, checkpoints, actors, and correction attempts — but **does not expose** `ResourceRepository`, `ResourceTypeRepository`, or `NamespacedProjectRepository`. This means resource and project operations cannot participate in the same database transaction as plan operations, violating the Unit of Work pattern's atomicity guarantee. ## Evidence **`UnitOfWorkContext` properties** (unit_of_work.py:175-332): ```python class UnitOfWorkContext: @property def projects(self) -> ProjectRepository: # Legacy projects table only @property def plans(self) -> PlanRepository: # Deprecated legacy plans @property def actions(self) -> ActionRepository: @property def lifecycle_plans(self) -> LifecyclePlanRepository: @property def checkpoints(self) -> CheckpointRepository: @property def contexts(self) -> ContextRepository: @property def changes(self) -> ChangeRepository: @property def debug_attempts(self) -> DebugAttemptRepository: @property def actors(self) -> ActorRepository: @property def decisions(self) -> DecisionRepository: @property def correction_attempts(self) -> CorrectionAttemptRepository: # MISSING: resource_types, resources, namespaced_projects ``` **`NamespacedProjectRepository.create()`** (repositories.py:2960): ```python def create(self, project: Any) -> Any: session = self._session() try: db_model = NamespacedProjectModel.from_domain(project) session.add(db_model) session.commit() # ← Commits its own transaction independently! return project ``` The `NamespacedProjectRepository` commits its own transactions, making it impossible to atomically create a project and its first plan in a single transaction. ## Impact 1. Creating a project and linking it to a plan cannot be done atomically — partial failures leave orphaned data 2. Resource registration and plan creation cannot be rolled back together 3. The spec's Unit of Work pattern (ADR-007) is only partially implemented ## Expected Behavior Per ADR-007 (Repository Pattern), all repositories should be accessible through `UnitOfWorkContext` so that cross-entity operations can be committed or rolled back atomically: ```python with uow.transaction() as ctx: project = ctx.namespaced_projects.create(project_domain) plan = ctx.lifecycle_plans.create(plan_domain) # Both committed atomically, or both rolled back on error ``` ## Fix 1. Add `namespaced_projects`, `resource_types`, and `resources` properties to `UnitOfWorkContext` 2. Refactor `NamespacedProjectRepository` to use the session-factory pattern (flush but not commit) consistent with other repositories 3. Add corresponding `_namespaced_projects`, `_resource_types`, `_resources` private fields to `UnitOfWorkContext.__init__` --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
Author
Owner

🏷️ Label Fix Applied by Backlog Groomer

The State/Verified label has been added to this issue.

Reason: During a routine backlog grooming pass, this issue was found to have Type/Bug and a Priority/ label but was missing a State/* label entirely — a violation of the CONTRIBUTING.md requirement that every issue must have exactly one State/ label.

Since this issue has been triaged with a priority and type, State/Verified is the appropriate state: the issue has been confirmed as legitimate and is now part of the active backlog.

No other changes were made.


Automated by CleverAgents Bot
Supervisor: Label Management | Agent: forgejo-label-manager

## 🏷️ Label Fix Applied by Backlog Groomer The `State/Verified` label has been added to this issue. **Reason**: During a routine backlog grooming pass, this issue was found to have `Type/Bug` and a `Priority/` label but was missing a `State/*` label entirely — a violation of the CONTRIBUTING.md requirement that every issue must have exactly one `State/` label. Since this issue has been triaged with a priority and type, `State/Verified` is the appropriate state: the issue has been confirmed as legitimate and is now part of the active backlog. No other changes were made. --- **Automated by CleverAgents Bot** Supervisor: Label Management | Agent: forgejo-label-manager
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#5435
No description provided.