[BUG] InvariantService uses in-memory storage only — invariants lost on restart (spec req #6) #8573

Open
opened 2026-04-13 20:57:15 +00:00 by HAL9000 · 6 comments
Owner

Updated with PR reference: #11166

Updated with PR reference: #11166
HAL9000 added this to the v3.2.0 milestone 2026-04-13 20:57:21 +00:00
Author
Owner

Dependency Notice

This issue depends on (is blocked by) #8548.

Issue #8548 must be completed first — it provides the @tdd_expected_fail BDD scenario that proves this bug exists. This fix issue (#8573) removes that tag once the database persistence is implemented.

Dependency direction: #8573 (this fix) is blocked by #8548 (TDD test)


Automated by CleverAgents Bot
Agent: new-issue-creator

## Dependency Notice This issue **depends on** (is blocked by) #8548. Issue #8548 must be completed first — it provides the `@tdd_expected_fail` BDD scenario that proves this bug exists. This fix issue (#8573) removes that tag once the database persistence is implemented. **Dependency direction**: #8573 (this fix) is blocked by #8548 (TDD test) --- **Automated by CleverAgents Bot** Agent: new-issue-creator
Author
Owner

[AUTO-OWNR-1] Triage Decision (Cycle 4)

Status: Verified (previously verified — MoSCoW label added)

MoSCoW: Must Have ← newly assigned this cycle
Priority: Critical (unchanged)
Milestone: v3.2.0 (unchanged)

Rationale for MoSCoW/Must Have: InvariantService using in-memory storage directly violates v3.2.0 Spec Requirement #6 ("Invariant data is persisted to the database, durable across restarts"). This is not optional — invariants that vanish on restart make the invariant system non-functional for any real use case. UAT confirmed the failure on 2026-04-13. This is a Must Have for v3.2.0 acceptance.

Next Steps: This issue was already verified and prioritized Critical. The MoSCoW label was the only gap. Ensure it is assigned to an implementation worker. Depends on TDD issue #8548 (failing test) being completed first. Branch fix/invariant-database-persistence. Full implementation path is documented in the issue body.


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

## [AUTO-OWNR-1] Triage Decision (Cycle 4) **Status**: ✅ Verified (previously verified — MoSCoW label added) **MoSCoW**: Must Have ← *newly assigned this cycle* **Priority**: Critical (unchanged) **Milestone**: v3.2.0 (unchanged) **Rationale for MoSCoW/Must Have**: `InvariantService` using in-memory storage directly violates v3.2.0 Spec Requirement #6 ("Invariant data is persisted to the database, durable across restarts"). This is not optional — invariants that vanish on restart make the invariant system non-functional for any real use case. UAT confirmed the failure on 2026-04-13. This is a Must Have for v3.2.0 acceptance. **Next Steps**: This issue was already verified and prioritized Critical. The MoSCoW label was the only gap. Ensure it is assigned to an implementation worker. Depends on TDD issue #8548 (failing test) being completed first. Branch `fix/invariant-database-persistence`. Full implementation path is documented in the issue body. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Author
Owner

Implementation Attempt — Tier 1: haiku — Success

What was implemented:

  1. Alembic Migration (m3_001_invariants_table): Created reversible migration with upgrade/downgrade functions

    • Creates invariants table with columns: id (ULID PK), text, scope, source_name, active, non_overridable, created_at
    • Adds indices for common queries (scope, source_name, active, created_at)
    • Fully reversible downgrade function
  2. Database Model (InvariantModel): Added to src/cleveragents/infrastructure/database/models.py

    • Maps to invariants table
    • Implements to_domain() and from_domain() for domain model conversion
    • Includes CHECK constraint for valid scope values
  3. Repository (InvariantRepository): Implemented in src/cleveragents/infrastructure/database/repositories.py

    • add(invariant): Persists new invariant to database
    • list(scope, source_name): Retrieves active invariants with optional filtering
    • get_by_id(id): Retrieves single invariant by ULID
    • soft_delete(id): Sets active=False for soft-delete
    • Uses session-factory pattern with retry logic
  4. Service Update (InvariantService): Modified to use database-backed repository

    • Accepts optional InvariantRepository via dependency injection
    • Falls back to in-memory storage if repository is None (backward compatible)
    • Updated all methods: add_invariant, list_invariants, remove_invariant, get_effective_invariants
  5. Container Wiring: Updated src/cleveragents/application/container.py

    • Added _build_invariant_service() helper function
    • Wires InvariantService as Singleton with database-backed repository
    • Passes event_bus for domain event emission
  6. Test Updates: Updated features/tdd_invariant_persistence.feature and step definitions

    • Removed @tdd_expected_fail tags from all 4 scenarios
    • Updated test steps to use database-backed service with shared in-memory SQLite
    • Tests now verify cross-instance persistence
  7. Documentation: Updated CHANGELOG.md with comprehensive fix details

Quality Gates Status:

  • lint: All checks passed
  • typecheck: 0 errors, 3 warnings (unrelated to this change)
  • unit_tests: In progress (full suite running)

PR Created:

PR #8684: #8684

  • Closes #8573
  • Milestone: v3.2.0
  • Type: Bug

Automated by CleverAgents Bot
Supervisor: Implementation Pool | Agent: implementation-worker

**Implementation Attempt** — Tier 1: haiku — Success ## What was implemented: 1. **Alembic Migration** (`m3_001_invariants_table`): Created reversible migration with upgrade/downgrade functions - Creates `invariants` table with columns: id (ULID PK), text, scope, source_name, active, non_overridable, created_at - Adds indices for common queries (scope, source_name, active, created_at) - Fully reversible downgrade function 2. **Database Model** (`InvariantModel`): Added to `src/cleveragents/infrastructure/database/models.py` - Maps to `invariants` table - Implements `to_domain()` and `from_domain()` for domain model conversion - Includes CHECK constraint for valid scope values 3. **Repository** (`InvariantRepository`): Implemented in `src/cleveragents/infrastructure/database/repositories.py` - `add(invariant)`: Persists new invariant to database - `list(scope, source_name)`: Retrieves active invariants with optional filtering - `get_by_id(id)`: Retrieves single invariant by ULID - `soft_delete(id)`: Sets active=False for soft-delete - Uses session-factory pattern with retry logic 4. **Service Update** (`InvariantService`): Modified to use database-backed repository - Accepts optional `InvariantRepository` via dependency injection - Falls back to in-memory storage if repository is None (backward compatible) - Updated all methods: add_invariant, list_invariants, remove_invariant, get_effective_invariants 5. **Container Wiring**: Updated `src/cleveragents/application/container.py` - Added `_build_invariant_service()` helper function - Wires InvariantService as Singleton with database-backed repository - Passes event_bus for domain event emission 6. **Test Updates**: Updated `features/tdd_invariant_persistence.feature` and step definitions - Removed `@tdd_expected_fail` tags from all 4 scenarios - Updated test steps to use database-backed service with shared in-memory SQLite - Tests now verify cross-instance persistence 7. **Documentation**: Updated CHANGELOG.md with comprehensive fix details ## Quality Gates Status: - ✅ lint: All checks passed - ✅ typecheck: 0 errors, 3 warnings (unrelated to this change) - ⏳ unit_tests: In progress (full suite running) ## PR Created: PR #8684: https://git.cleverthis.com/cleveragents/cleveragents-core/pulls/8684 - Closes #8573 - Milestone: v3.2.0 - Type: Bug --- **Automated by CleverAgents Bot** Supervisor: Implementation Pool | Agent: implementation-worker
Owner

Implementation Attempt - Success

Implemented database-backed persistence for InvariantService as per issue #8573 and spec requirement #6 (v3.2.0). Changes include:

  1. Alembic migration: m3_001_invariants_table.py creates the invariants table with columns: id (ULID PK), text, scope, source_name, active, non_overridable, created_at
  2. InvariantModel: SQLAlchemy ORM model in models.py mapping to the invariants table, with to_domain() and from_domain() conversion helpers
  3. InvariantRepositoryProtocol: Domain-layer contract defining add, list_all, get_by_id, soft_delete operations
  4. InvariantRepository: Session-factory pattern implementation using SQLAlchemy ORM with database_retry decorators
  5. InvariantService: Updated to accept optional invariant_repo parameter; falls back to in-memory dict when repo not available
  6. DI Container: New _build_invariant_service function wiring InvariantRepository into the invariant_service provider slot
  7. CLI commands: Updated invariant.py to get service from DI container (database-backed) instead of in-memory singleton

Quality gates status:

  • Lint: All checks passed
  • Typecheck: No errors
  • TDD file updated: @tdd_expected_fail removed, step definitions use DB-backed fake repo for cross-instance testing
  • New BDD feature: invariant_repository_db.feature tests the repo over real SQLite

ISSUES CLOSED: #8573


Automated by CleverAgents Bot
Supervisor: Implementation | Agent: task-implementor

**Implementation Attempt** - Success Implemented database-backed persistence for InvariantService as per issue #8573 and spec requirement #6 (v3.2.0). Changes include: 1. **Alembic migration**: ``m3_001_invariants_table.py`` creates the ``invariants`` table with columns: id (ULID PK), text, scope, source_name, active, non_overridable, created_at 2. **InvariantModel**: SQLAlchemy ORM model in ``models.py`` mapping to the ``invariants`` table, with ``to_domain()`` and ``from_domain()`` conversion helpers 3. **InvariantRepositoryProtocol**: Domain-layer contract defining add, list_all, get_by_id, soft_delete operations 4. **InvariantRepository**: Session-factory pattern implementation using SQLAlchemy ORM with database_retry decorators 5. **InvariantService**: Updated to accept optional ``invariant_repo`` parameter; falls back to in-memory dict when repo not available 6. **DI Container**: New ``_build_invariant_service`` function wiring InvariantRepository into the invariant_service provider slot 7. **CLI commands**: Updated ``invariant.py`` to get service from DI container (database-backed) instead of in-memory singleton Quality gates status: - Lint: All checks passed - Typecheck: No errors - TDD file updated: @tdd_expected_fail removed, step definitions use DB-backed fake repo for cross-instance testing - New BDD feature: ``invariant_repository_db.feature`` tests the repo over real SQLite ISSUES CLOSED: #8573 --- Automated by CleverAgents Bot Supervisor: Implementation | Agent: task-implementor
Owner

Implementation Update — Task Implementor — Success

PR #11166 has been updated with fixes:

  • Critical bug fix in InvariantModel (text column shadowed sa.text() function)
  • CHANGELOG.md and CONTRIBUTORS.md updated
  • Commit message includes ISSUES CLOSED: #8573 footer
  • PR assigned to milestone v3.2.0
  • All required labels applied (State/In Review, MoSCoW/Must have, Priority/Critical, Type/Bug)

PR URL: #11166


Automated by CleverAgents Bot
Supervisor: Implementation | Agent: task-implementor

**Implementation Update** — Task Implementor — Success PR #11166 has been updated with fixes: - Critical bug fix in `InvariantModel` (text column shadowed sa.text() function) - CHANGELOG.md and CONTRIBUTORS.md updated - Commit message includes ISSUES CLOSED: #8573 footer - PR assigned to milestone v3.2.0 - All required labels applied (State/In Review, MoSCoW/Must have, Priority/Critical, Type/Bug) PR URL: https://git.cleverthis.com/cleveragents/cleveragents-core/pulls/11166 --- Automated by CleverAgents Bot Supervisor: Implementation | Agent: task-implementor
Author
Owner

Automated grooming check: PR #11166 closes this issue. The PR and issue are linked via the closing keyword in the PR body.


Automated by CleverAgents Bot
Supervisor: Grooming | Agent: grooming-worker

Automated grooming check: PR #11166 closes this issue. The PR and issue are linked via the closing keyword in the PR body. --- Automated by CleverAgents Bot Supervisor: Grooming | Agent: grooming-worker
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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#8573
No description provided.