UAT: agents resource remove bypasses service layer and leaves orphaned project-resource links #3865

Open
opened 2026-04-06 07:02:04 +00:00 by freemo · 0 comments
Owner

Summary

The agents resource remove CLI command bypasses the ResourceRegistryService service layer entirely, directly manipulating ResourceModel and ResourceEdgeModel via raw SQLAlchemy queries. Additionally, it does not clean up ProjectResourceLinkModel records when a resource is removed, leaving orphaned project-resource link rows in the database.

Metadata

  • Commit Message: fix(resource): route resource remove through service layer and clean up project links
  • Branch: fix/resource-remove-service-layer
  • Milestone: Backlog
  • Parent Epic: #398

Subtasks

  • Add a remove_resource(name_or_id: str) -> None method to ResourceRegistryService (or ResourceInstanceMixin)
  • The method must: (1) check for DAG edges and reject if any exist, (2) delete all ProjectResourceLinkModel rows referencing the resource, (3) delete the ResourceModel row
  • Update agents resource remove CLI command to call service.remove_resource() instead of raw SQL
  • Add BDD scenario: removing a resource that is linked to a project also removes the project link

Definition of Done

  • agents resource remove <name> calls ResourceRegistryService.remove_resource() (no raw SQL in CLI)
  • Removing a resource that is linked to a project automatically removes the project_resource_link row
  • Removing a resource with DAG edges still raises an error (existing behavior preserved)
  • Unit tests (Behave) cover the project-link cleanup path
  • All nox stages pass
  • Coverage >= 97%

Steps to Reproduce

Code location: src/cleveragents/cli/commands/resource.py, resource_remove() function (lines ~530-590)

# Current broken implementation in resource_remove():
session = service._session()
try:
    from cleveragents.infrastructure.database.models import (
        ResourceEdgeModel,
        ResourceModel,
    )
    # Check for edges
    edge_count: int = (
        session.query(ResourceEdgeModel)
        .filter(...)
        .count()
    )
    # ... raw SQL delete — NO cleanup of ProjectResourceLinkModel
    row = session.query(ResourceModel).filter_by(...).first()
    if row is not None:
        session.delete(row)
        session.commit()

Expected behavior: The CLI delegates to ResourceRegistryService.remove_resource(), which atomically removes the resource, its DAG edges check, AND any ProjectResourceLinkModel rows referencing it.

Actual behavior: The CLI bypasses the service layer, directly accesses ResourceModel via raw SQL, and never touches ProjectResourceLinkModel. After agents resource remove local/my-repo, any project that had local/my-repo linked still shows the orphaned link in project_resource_links table.

Additional Context

  • The resource_add --update command has the same service-layer bypass issue (separate bug)
  • ProjectResourceLinkRepository.create_link() exists in infrastructure/database/repositories.py (line 3156) and remove_link() at line 3266 — these should be used
  • The spec states resources are scoped to projects via a many-to-many relationship; removing a resource must clean up those associations

Backlog note: This issue was discovered during autonomous operation
on milestone v3.2.0. It does not block milestone completion and has been
placed in the backlog for human review and future milestone assignment.


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

## Summary The `agents resource remove` CLI command bypasses the `ResourceRegistryService` service layer entirely, directly manipulating `ResourceModel` and `ResourceEdgeModel` via raw SQLAlchemy queries. Additionally, it does not clean up `ProjectResourceLinkModel` records when a resource is removed, leaving orphaned project-resource link rows in the database. ## Metadata - **Commit Message**: `fix(resource): route resource remove through service layer and clean up project links` - **Branch**: `fix/resource-remove-service-layer` - **Milestone**: Backlog - **Parent Epic**: #398 ## Subtasks - [ ] Add a `remove_resource(name_or_id: str) -> None` method to `ResourceRegistryService` (or `ResourceInstanceMixin`) - [ ] The method must: (1) check for DAG edges and reject if any exist, (2) delete all `ProjectResourceLinkModel` rows referencing the resource, (3) delete the `ResourceModel` row - [ ] Update `agents resource remove` CLI command to call `service.remove_resource()` instead of raw SQL - [ ] Add BDD scenario: removing a resource that is linked to a project also removes the project link ## Definition of Done - `agents resource remove <name>` calls `ResourceRegistryService.remove_resource()` (no raw SQL in CLI) - Removing a resource that is linked to a project automatically removes the `project_resource_link` row - Removing a resource with DAG edges still raises an error (existing behavior preserved) - Unit tests (Behave) cover the project-link cleanup path - All nox stages pass - Coverage >= 97% ## Steps to Reproduce **Code location:** `src/cleveragents/cli/commands/resource.py`, `resource_remove()` function (lines ~530-590) ```python # Current broken implementation in resource_remove(): session = service._session() try: from cleveragents.infrastructure.database.models import ( ResourceEdgeModel, ResourceModel, ) # Check for edges edge_count: int = ( session.query(ResourceEdgeModel) .filter(...) .count() ) # ... raw SQL delete — NO cleanup of ProjectResourceLinkModel row = session.query(ResourceModel).filter_by(...).first() if row is not None: session.delete(row) session.commit() ``` **Expected behavior:** The CLI delegates to `ResourceRegistryService.remove_resource()`, which atomically removes the resource, its DAG edges check, AND any `ProjectResourceLinkModel` rows referencing it. **Actual behavior:** The CLI bypasses the service layer, directly accesses `ResourceModel` via raw SQL, and never touches `ProjectResourceLinkModel`. After `agents resource remove local/my-repo`, any project that had `local/my-repo` linked still shows the orphaned link in `project_resource_links` table. ## Additional Context - The `resource_add --update` command has the same service-layer bypass issue (separate bug) - `ProjectResourceLinkRepository.create_link()` exists in `infrastructure/database/repositories.py` (line 3156) and `remove_link()` at line 3266 — these should be used - The spec states resources are scoped to projects via a many-to-many relationship; removing a resource must clean up those associations > **Backlog note:** This issue was discovered during autonomous operation > on milestone v3.2.0. It does not block milestone completion and has been > placed in the backlog for human review and future milestone assignment. --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: ca-new-issue-creator
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.

Blocks
#398 Epic: Post-MVP Resources
cleveragents/cleveragents-core
Reference
cleveragents/cleveragents-core#3865
No description provided.