UAT: Session.cost_budget field not persisted — data silently lost on session save/load #3969

Open
opened 2026-04-06 08:03:00 +00:00 by freemo · 0 comments
Owner

Metadata

  • Branch: fix/session-cost-budget-persistence
  • Commit Message: fix(db): persist Session.cost_budget field in SessionModel ORM — add cost_budget_json column and migration
  • Milestone: (none — routed to backlog per Milestone Scope Guard)
  • Parent Epic: #3370

Bug Report

Feature Area: Infrastructure / Database Layer — Session ORM Persistence
Severity: Medium — silent data loss; per-session cost budget enforcement is permanently broken after restart
Found by: UAT tester (code analysis of infrastructure/database/models.py vs domain/models/core/session.py)


What Was Tested

Cross-referencing the Session domain model field list against the SessionModel ORM class to verify all domain fields are correctly persisted and rehydrated.

Expected Behavior

When a Session with a cost_budget: SessionCostBudget value is saved to the database and loaded back, the cost_budget field should be preserved exactly as set.

Actual Behavior

The cost_budget field is silently dropped when a session is persisted. After loading a session from the database, session.cost_budget is always None, even if it was set before saving. There is no error or warning — the data is lost silently.

Root Cause

SessionModel (src/cleveragents/infrastructure/database/models.py, line 2029) has no cost_budget_json column. Neither from_domain() (lines 2109–2142) nor to_domain() (lines 2067–2106) reference the cost_budget field. No Alembic migration adds this column to the sessions table.

Impact

  1. Cost enforcement broken after restart: CostBudgetService.check_budget_hierarchy() will always see None for the session-level budget after any process restart, allowing unlimited spending even when a budget was explicitly configured.
  2. CLI display broken: agents session show (references session.cost_budget at src/cleveragents/cli/commands/session.py line 420) will never display cost budget information for any persisted session.
  3. Silent data loss: No exception or warning is raised — callers have no indication the field was dropped.

Affected Code Locations

File Location Issue
src/cleveragents/domain/models/core/session.py Line 220 cost_budget: SessionCostBudget | None field defined
src/cleveragents/infrastructure/database/models.py Line 2029 (SessionModel class) Missing cost_budget_json column
src/cleveragents/infrastructure/database/models.py Lines 2109–2142 (from_domain()) Does not serialize cost_budget
src/cleveragents/infrastructure/database/models.py Lines 2067–2106 (to_domain()) Does not deserialize cost_budget_json
Alembic migrations (none) No migration adds cost_budget_json to sessions table
src/cleveragents/cli/commands/session.py Line 420 References session.cost_budget for display — always None after load

Subtasks

  • Add cost_budget_json = Column(Text, nullable=True) to SessionModel in src/cleveragents/infrastructure/database/models.py
  • Implement serialization in SessionModel.from_domain(): cost_budget_json=session.cost_budget.model_dump_json() if session.cost_budget else None
  • Implement deserialization in SessionModel.to_domain(): parse cost_budget_json back to SessionCostBudget using SessionCostBudget.model_validate_json()
  • Write a TDD issue-capture Behave scenario tagged @tdd_expected_fail in features/ proving the bug (session with cost_budget set → save → load → cost_budget is None)
  • Add an Alembic migration to add cost_budget_json TEXT column to the sessions table
  • Update existing Behave scenarios for Session persistence to cover cost_budget round-trip
  • Verify agents session show correctly displays cost budget for persisted sessions after fix

Definition of Done

  • cost_budget_json column added to SessionModel with nullable=True
  • SessionModel.from_domain() serializes cost_budget to JSON string
  • SessionModel.to_domain() deserializes cost_budget_json back to SessionCostBudget
  • Alembic migration adds cost_budget_json column to the sessions table
  • TDD issue-capture Behave scenario (@tdd_expected_fail) proves the bug before fix is applied
  • Behave scenarios cover full cost_budget round-trip persistence (set → save → load → assert equal)
  • agents session show displays cost budget for persisted sessions
  • All nox stages pass
  • Coverage >= 97%

Backlog note: This issue was discovered during autonomous operation
on milestone v3.6.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-uat-tester

## Metadata - **Branch**: `fix/session-cost-budget-persistence` - **Commit Message**: `fix(db): persist Session.cost_budget field in SessionModel ORM — add cost_budget_json column and migration` - **Milestone**: _(none — routed to backlog per Milestone Scope Guard)_ - **Parent Epic**: #3370 --- ## Bug Report **Feature Area:** Infrastructure / Database Layer — Session ORM Persistence **Severity:** Medium — silent data loss; per-session cost budget enforcement is permanently broken after restart **Found by:** UAT tester (code analysis of `infrastructure/database/models.py` vs `domain/models/core/session.py`) --- ## What Was Tested Cross-referencing the `Session` domain model field list against the `SessionModel` ORM class to verify all domain fields are correctly persisted and rehydrated. ## Expected Behavior When a `Session` with a `cost_budget: SessionCostBudget` value is saved to the database and loaded back, the `cost_budget` field should be preserved exactly as set. ## Actual Behavior The `cost_budget` field is silently dropped when a session is persisted. After loading a session from the database, `session.cost_budget` is always `None`, even if it was set before saving. There is no error or warning — the data is lost silently. ## Root Cause `SessionModel` (`src/cleveragents/infrastructure/database/models.py`, line 2029) has no `cost_budget_json` column. Neither `from_domain()` (lines 2109–2142) nor `to_domain()` (lines 2067–2106) reference the `cost_budget` field. No Alembic migration adds this column to the `sessions` table. ## Impact 1. **Cost enforcement broken after restart**: `CostBudgetService.check_budget_hierarchy()` will always see `None` for the session-level budget after any process restart, allowing unlimited spending even when a budget was explicitly configured. 2. **CLI display broken**: `agents session show` (references `session.cost_budget` at `src/cleveragents/cli/commands/session.py` line 420) will never display cost budget information for any persisted session. 3. **Silent data loss**: No exception or warning is raised — callers have no indication the field was dropped. ## Affected Code Locations | File | Location | Issue | |------|----------|-------| | `src/cleveragents/domain/models/core/session.py` | Line 220 | `cost_budget: SessionCostBudget \| None` field defined | | `src/cleveragents/infrastructure/database/models.py` | Line 2029 (`SessionModel` class) | Missing `cost_budget_json` column | | `src/cleveragents/infrastructure/database/models.py` | Lines 2109–2142 (`from_domain()`) | Does not serialize `cost_budget` | | `src/cleveragents/infrastructure/database/models.py` | Lines 2067–2106 (`to_domain()`) | Does not deserialize `cost_budget_json` | | Alembic migrations | _(none)_ | No migration adds `cost_budget_json` to `sessions` table | | `src/cleveragents/cli/commands/session.py` | Line 420 | References `session.cost_budget` for display — always `None` after load | --- ## Subtasks - [ ] Add `cost_budget_json = Column(Text, nullable=True)` to `SessionModel` in `src/cleveragents/infrastructure/database/models.py` - [ ] Implement serialization in `SessionModel.from_domain()`: `cost_budget_json=session.cost_budget.model_dump_json() if session.cost_budget else None` - [ ] Implement deserialization in `SessionModel.to_domain()`: parse `cost_budget_json` back to `SessionCostBudget` using `SessionCostBudget.model_validate_json()` - [ ] Write a TDD issue-capture Behave scenario tagged `@tdd_expected_fail` in `features/` proving the bug (session with `cost_budget` set → save → load → `cost_budget` is `None`) - [ ] Add an Alembic migration to add `cost_budget_json TEXT` column to the `sessions` table - [ ] Update existing Behave scenarios for `Session` persistence to cover `cost_budget` round-trip - [ ] Verify `agents session show` correctly displays cost budget for persisted sessions after fix ## Definition of Done - [ ] `cost_budget_json` column added to `SessionModel` with `nullable=True` - [ ] `SessionModel.from_domain()` serializes `cost_budget` to JSON string - [ ] `SessionModel.to_domain()` deserializes `cost_budget_json` back to `SessionCostBudget` - [ ] Alembic migration adds `cost_budget_json` column to the `sessions` table - [ ] TDD issue-capture Behave scenario (`@tdd_expected_fail`) proves the bug before fix is applied - [ ] Behave scenarios cover full `cost_budget` round-trip persistence (set → save → load → assert equal) - [ ] `agents session show` displays cost budget for persisted sessions - [ ] All nox stages pass - [ ] Coverage >= 97% --- > **Backlog note:** This issue was discovered during autonomous operation > on milestone v3.6.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-uat-tester
HAL9000 added this to the v3.5.0 milestone 2026-04-09 03:12:25 +00:00
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#3969
No description provided.