Implemented persistence for three previously missing Plan fields to ensure proper resume and reversion behavior across restarts. The changes address the gaps in the database, ORM mapping, repository logic, and tests.
- Alembic migration
- File: alembic/versions/m9_002_plan_resume_fields.py
- Adds three columns to the v3_plans table:
- reversion_count: INTEGER NOT NULL DEFAULT 0 (server_default)
- last_completed_step: INTEGER NOT NULL DEFAULT -1 (server_default)
- last_checkpoint_id: TEXT nullable (no server_default)
- Migration chains from m9_001_session_name_column to align with existing plan schema evolution.
- SQLAlchemy model
- File: src/cleveragents/infrastructure/database/models.py
- Updated LifecyclePlanModel to include reversion_count, last_completed_step, and last_checkpoint_id columns.
- from_domain(): updated to serialize all three fields.
- to_domain(): updated to deserialize all three fields with proper cast() typing, ensuring correct domain conversions.
- Repository fix
- File: src/cleveragents/infrastructure/database/repositories.py
- Fixed LifecyclePlanRepository.update() which was silently dropping the three fields on every update.
- This remediation ensures updates preserve reversion_count, last_completed_step, and last_checkpoint_id.
- Behave tests
- Files: features/plan_resume_fields_persistence.feature and associated steps
- Added 7 scenarios validating individual field persistence, combined persistence, default values, cross-reconnection persistence, and update persistence.
- Tests ensure correctness of persistence behavior across restarts and updates.
Key Design Decisions
- server_default used for migration columns to backfill defaults automatically for existing rows without a separate backfill step.
- last_checkpoint_id is nullable (no server_default) because None is the correct default in the domain model.
- The update() fix in LifecyclePlanRepository was a bonus discovery; without it, updates could silently drop the resume fields and break persistence guarantees.
ISSUES CLOSED: #2864