UAT: LifecyclePlanModel.from_domain() sets completed_at to applied_at — spec requires distinct completion timestamp #5447

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

Bug Report

Feature Area: Database / Migrations — Domain Model Persistence
Severity: Backlog — data fidelity issue
Found by: UAT Testing (database-migrations worker)


Summary

LifecyclePlanModel.from_domain() sets the completed_at column to the same value as applied_at (models.py:1143). The completed_at column should represent when the plan reached any terminal state (applied, errored, cancelled, constrained), while applied_at specifically represents when the plan was successfully applied. These are semantically distinct timestamps.

Evidence

LifecyclePlanModel.from_domain() (models.py:1141-1151):

model = cls(
    ...
    completed_at=cls._to_iso(plan.timestamps.applied_at),  # ← Same as applied_at!
    ...
    applied_at=cls._to_iso(plan.timestamps.applied_at),
)

LifecyclePlanRepository.update() (repositories.py:1461-1462):

row.completed_at = LifecyclePlanModel._to_iso(
    plan.timestamps.applied_at  # ← Also uses applied_at for completed_at
)

PlanTimestamps domain model — does not have a completed_at field:

class PlanTimestamps(BaseModel):
    created_at: datetime
    updated_at: datetime
    strategize_started_at: datetime | None = None
    strategize_completed_at: datetime | None = None
    execute_started_at: datetime | None = None
    execute_completed_at: datetime | None = None
    apply_started_at: datetime | None = None
    applied_at: datetime | None = None
    # No completed_at field!

Impact

  1. For plans that end in errored, cancelled, or constrained states, completed_at will always be NULL (since applied_at is only set for applied plans), even though the plan has completed
  2. The completed_at column in the database is never populated for non-applied terminal states
  3. Any query filtering by completed_at to find all completed plans will miss errored/cancelled/constrained plans

Expected Behavior

The PlanTimestamps domain model should have a completed_at field that is set when the plan reaches any terminal state (applied, errored, cancelled, constrained). The applied_at field should only be set for applied plans.

Fix

  1. Add completed_at: datetime | None = None to PlanTimestamps
  2. Set completed_at when transitioning to any terminal state in the plan lifecycle service
  3. Update from_domain() and update() to use plan.timestamps.completed_at for the completed_at column

Code Locations

  • src/cleveragents/infrastructure/database/models.py:1143completed_at=cls._to_iso(plan.timestamps.applied_at)
  • src/cleveragents/infrastructure/database/repositories.py:1461-1462 — same issue in update()
  • src/cleveragents/domain/models/core/plan.pyPlanTimestamps missing completed_at

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

## Bug Report **Feature Area**: Database / Migrations — Domain Model Persistence **Severity**: Backlog — data fidelity issue **Found by**: UAT Testing (database-migrations worker) --- ## Summary `LifecyclePlanModel.from_domain()` sets the `completed_at` column to the same value as `applied_at` (models.py:1143). The `completed_at` column should represent when the plan reached any terminal state (applied, errored, cancelled, constrained), while `applied_at` specifically represents when the plan was successfully applied. These are semantically distinct timestamps. ## Evidence **`LifecyclePlanModel.from_domain()`** (models.py:1141-1151): ```python model = cls( ... completed_at=cls._to_iso(plan.timestamps.applied_at), # ← Same as applied_at! ... applied_at=cls._to_iso(plan.timestamps.applied_at), ) ``` **`LifecyclePlanRepository.update()`** (repositories.py:1461-1462): ```python row.completed_at = LifecyclePlanModel._to_iso( plan.timestamps.applied_at # ← Also uses applied_at for completed_at ) ``` **`PlanTimestamps` domain model** — does not have a `completed_at` field: ```python class PlanTimestamps(BaseModel): created_at: datetime updated_at: datetime strategize_started_at: datetime | None = None strategize_completed_at: datetime | None = None execute_started_at: datetime | None = None execute_completed_at: datetime | None = None apply_started_at: datetime | None = None applied_at: datetime | None = None # No completed_at field! ``` ## Impact 1. For plans that end in `errored`, `cancelled`, or `constrained` states, `completed_at` will always be `NULL` (since `applied_at` is only set for `applied` plans), even though the plan has completed 2. The `completed_at` column in the database is never populated for non-applied terminal states 3. Any query filtering by `completed_at` to find all completed plans will miss errored/cancelled/constrained plans ## Expected Behavior The `PlanTimestamps` domain model should have a `completed_at` field that is set when the plan reaches any terminal state (`applied`, `errored`, `cancelled`, `constrained`). The `applied_at` field should only be set for `applied` plans. ## Fix 1. Add `completed_at: datetime | None = None` to `PlanTimestamps` 2. Set `completed_at` when transitioning to any terminal state in the plan lifecycle service 3. Update `from_domain()` and `update()` to use `plan.timestamps.completed_at` for the `completed_at` column ## Code Locations - `src/cleveragents/infrastructure/database/models.py:1143` — `completed_at=cls._to_iso(plan.timestamps.applied_at)` - `src/cleveragents/infrastructure/database/repositories.py:1461-1462` — same issue in `update()` - `src/cleveragents/domain/models/core/plan.py` — `PlanTimestamps` missing `completed_at` --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
HAL9000 added this to the v3.4.0 milestone 2026-04-09 06:59:59 +00:00
Author
Owner

Label compliance fix applied:

  • Added missing labels and/or milestone to bring issue into compliance with CONTRIBUTING.md

Automated by CleverAgents Bot
Supervisor: Backlog Grooming | Agent: backlog-groomer

Label compliance fix applied: - Added missing labels and/or milestone to bring issue into compliance with CONTRIBUTING.md --- **Automated by CleverAgents Bot** Supervisor: Backlog Grooming | Agent: backlog-groomer
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#5447
No description provided.