bug(plan): Plan.can_revert_to() incorrectly rejects STRATEGIZE → STRATEGIZE reversion — errored Strategize plans cannot be restarted #2380

Open
opened 2026-04-03 17:26:49 +00:00 by freemo · 0 comments
Owner

Metadata

  • Branch: fix/plan-can-revert-to-strategize-self
  • Commit Message: fix(plan): allow STRATEGIZE→STRATEGIZE reversion in can_revert_to() for errored plans
  • Milestone: v3.5.0
  • Parent Epic: #368

Background and Context

Plan.can_revert_to() in src/cleveragents/domain/models/core/plan.py delegates to can_transition(self.phase, phase) to validate whether a reversion is allowed. The VALID_PHASE_TRANSITIONS map is:

VALID_PHASE_TRANSITIONS: dict[PlanPhase, list[PlanPhase]] = {
    PlanPhase.ACTION: [PlanPhase.STRATEGIZE],      # plan use
    PlanPhase.STRATEGIZE: [PlanPhase.EXECUTE],     # execute command
    PlanPhase.EXECUTE: [PlanPhase.APPLY, PlanPhase.STRATEGIZE],  # apply or revert
    PlanPhase.APPLY: [PlanPhase.STRATEGIZE],       # revert via constrained
}

This means:

  • EXECUTE → STRATEGIZE is allowed (Execute can revert to Strategize)
  • APPLY → STRATEGIZE is allowed (constrained Apply can revert to Strategize)
  • STRATEGIZE → STRATEGIZE is NOT in the map (missing)

The problem: after a plan is reverted from Execute back to Strategize, it is now in the Strategize phase. If the Strategize phase then fails (ERRORED state), the can_revert_to(PlanPhase.STRATEGIZE) call returns False because STRATEGIZE → STRATEGIZE is not a valid transition. This means a plan that has been reverted to Strategize and then errored in Strategize cannot be re-run in Strategize — it is permanently stuck.

The spec states: "ERRORED and CONSTRAINED plans can still be reverted to an earlier phase via revert_plan or resumed through PlanResumeService." A plan in STRATEGIZE/ERRORED state should be able to revert to STRATEGIZE (i.e., restart Strategize).

Reproduction Steps

  1. Create and use an action to create a plan (now in STRATEGIZE/QUEUED)
  2. Start strategize: plan transitions to STRATEGIZE/PROCESSING
  3. Fail strategize: plan transitions to STRATEGIZE/ERRORED
  4. Try to revert to Strategize: plan.can_revert_to(PlanPhase.STRATEGIZE) returns False
  5. The plan is stuck — cannot be restarted in Strategize

Alternatively, the same bug is triggered via the revert path:

  1. Plan progresses to EXECUTE/ERRORED
  2. Revert to Strategize: plan is now in STRATEGIZE/QUEUED
  3. Execute Strategize: plan transitions to STRATEGIZE/PROCESSING
  4. Strategize fails: plan transitions to STRATEGIZE/ERRORED
  5. can_revert_to(PlanPhase.STRATEGIZE) returns False — plan is stuck

Current Behavior

Plan.can_revert_to(PlanPhase.STRATEGIZE) returns False when the plan is already in STRATEGIZE phase (regardless of processing state), because VALID_PHASE_TRANSITIONS[STRATEGIZE] only contains [EXECUTE], not [STRATEGIZE].

Code locations:

  • src/cleveragents/domain/models/core/plan.pyVALID_PHASE_TRANSITIONS dict (~line 1185)
  • src/cleveragents/domain/models/core/plan.pycan_revert_to() method (~line 1060)

Expected Behavior

A plan in STRATEGIZE/ERRORED or STRATEGIZE/CONSTRAINED state should be able to call can_revert_to(PlanPhase.STRATEGIZE) and receive True, allowing the plan to be restarted in the Strategize phase. The fix should be scoped to can_revert_to() (not can_transition()), since STRATEGIZE → STRATEGIZE is not a valid forward transition — only a valid self-reversion when in a recoverable terminal state.

Subtasks

  • Investigate can_revert_to() and can_transition() in plan.py to understand the full delegation chain
  • Fix can_revert_to() to allow STRATEGIZE → STRATEGIZE when the plan's processing state is ERRORED or CONSTRAINED
  • Ensure can_transition() is not modified (forward transitions must remain unchanged)
  • Add unit tests for can_revert_to(PlanPhase.STRATEGIZE) when plan is in STRATEGIZE/ERRORED state
  • Add unit tests for can_revert_to(PlanPhase.STRATEGIZE) when plan is in STRATEGIZE/CONSTRAINED state
  • Add unit tests confirming can_revert_to(PlanPhase.STRATEGIZE) still returns False for STRATEGIZE/PROCESSING and STRATEGIZE/QUEUED (non-terminal states should not be revertable)
  • Tests (Behave): Add BDD scenario for reverting an errored Strategize plan back to Strategize
  • Verify coverage >= 97% via nox -s coverage_report
  • Run nox (all default sessions), fix any errors

Definition of Done

This issue is complete when:

  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation.
  • The commit is pushed to the remote on the branch matching the Branch in Metadata exactly.
  • The commit is submitted as a pull request to master, reviewed, and merged before this issue is marked done.
  • All nox stages pass.
  • Coverage >= 97%.

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

## Metadata - **Branch**: `fix/plan-can-revert-to-strategize-self` - **Commit Message**: `fix(plan): allow STRATEGIZE→STRATEGIZE reversion in can_revert_to() for errored plans` - **Milestone**: v3.5.0 - **Parent Epic**: #368 ## Background and Context `Plan.can_revert_to()` in `src/cleveragents/domain/models/core/plan.py` delegates to `can_transition(self.phase, phase)` to validate whether a reversion is allowed. The `VALID_PHASE_TRANSITIONS` map is: ```python VALID_PHASE_TRANSITIONS: dict[PlanPhase, list[PlanPhase]] = { PlanPhase.ACTION: [PlanPhase.STRATEGIZE], # plan use PlanPhase.STRATEGIZE: [PlanPhase.EXECUTE], # execute command PlanPhase.EXECUTE: [PlanPhase.APPLY, PlanPhase.STRATEGIZE], # apply or revert PlanPhase.APPLY: [PlanPhase.STRATEGIZE], # revert via constrained } ``` This means: - `EXECUTE → STRATEGIZE` is allowed ✅ (Execute can revert to Strategize) - `APPLY → STRATEGIZE` is allowed ✅ (constrained Apply can revert to Strategize) - `STRATEGIZE → STRATEGIZE` is **NOT** in the map ❌ (missing) **The problem**: after a plan is reverted from Execute back to Strategize, it is now in the Strategize phase. If the Strategize phase then fails (`ERRORED` state), the `can_revert_to(PlanPhase.STRATEGIZE)` call returns `False` because `STRATEGIZE → STRATEGIZE` is not a valid transition. This means a plan that has been reverted to Strategize and then errored in Strategize cannot be re-run in Strategize — it is permanently stuck. The spec states: *"ERRORED and CONSTRAINED plans can still be reverted to an earlier phase via revert_plan or resumed through PlanResumeService."* A plan in `STRATEGIZE/ERRORED` state should be able to revert to `STRATEGIZE` (i.e., restart Strategize). ## Reproduction Steps 1. Create and use an action to create a plan (now in `STRATEGIZE/QUEUED`) 2. Start strategize: plan transitions to `STRATEGIZE/PROCESSING` 3. Fail strategize: plan transitions to `STRATEGIZE/ERRORED` 4. Try to revert to Strategize: `plan.can_revert_to(PlanPhase.STRATEGIZE)` returns `False` 5. The plan is stuck — cannot be restarted in Strategize Alternatively, the same bug is triggered via the revert path: 1. Plan progresses to `EXECUTE/ERRORED` 2. Revert to Strategize: plan is now in `STRATEGIZE/QUEUED` 3. Execute Strategize: plan transitions to `STRATEGIZE/PROCESSING` 4. Strategize fails: plan transitions to `STRATEGIZE/ERRORED` 5. `can_revert_to(PlanPhase.STRATEGIZE)` returns `False` — plan is stuck ## Current Behavior `Plan.can_revert_to(PlanPhase.STRATEGIZE)` returns `False` when the plan is already in `STRATEGIZE` phase (regardless of processing state), because `VALID_PHASE_TRANSITIONS[STRATEGIZE]` only contains `[EXECUTE]`, not `[STRATEGIZE]`. **Code locations**: - `src/cleveragents/domain/models/core/plan.py` — `VALID_PHASE_TRANSITIONS` dict (~line 1185) - `src/cleveragents/domain/models/core/plan.py` — `can_revert_to()` method (~line 1060) ## Expected Behavior A plan in `STRATEGIZE/ERRORED` or `STRATEGIZE/CONSTRAINED` state should be able to call `can_revert_to(PlanPhase.STRATEGIZE)` and receive `True`, allowing the plan to be restarted in the Strategize phase. The fix should be scoped to `can_revert_to()` (not `can_transition()`), since `STRATEGIZE → STRATEGIZE` is not a valid forward transition — only a valid self-reversion when in a recoverable terminal state. ## Subtasks - [ ] Investigate `can_revert_to()` and `can_transition()` in `plan.py` to understand the full delegation chain - [ ] Fix `can_revert_to()` to allow `STRATEGIZE → STRATEGIZE` when the plan's processing state is `ERRORED` or `CONSTRAINED` - [ ] Ensure `can_transition()` is **not** modified (forward transitions must remain unchanged) - [ ] Add unit tests for `can_revert_to(PlanPhase.STRATEGIZE)` when plan is in `STRATEGIZE/ERRORED` state - [ ] Add unit tests for `can_revert_to(PlanPhase.STRATEGIZE)` when plan is in `STRATEGIZE/CONSTRAINED` state - [ ] Add unit tests confirming `can_revert_to(PlanPhase.STRATEGIZE)` still returns `False` for `STRATEGIZE/PROCESSING` and `STRATEGIZE/QUEUED` (non-terminal states should not be revertable) - [ ] Tests (Behave): Add BDD scenario for reverting an errored Strategize plan back to Strategize - [ ] Verify coverage >= 97% via `nox -s coverage_report` - [ ] Run `nox` (all default sessions), fix any errors ## Definition of Done This issue is complete when: - All subtasks above are completed and checked off. - A Git commit is created where the **first line** of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation. - The commit is pushed to the remote on the branch matching the **Branch** in Metadata exactly. - The commit is submitted as a **pull request** to `master`, reviewed, and **merged** before this issue is marked done. - All nox stages pass. - Coverage >= 97%. --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: ca-new-issue-creator
freemo added this to the v3.5.0 milestone 2026-04-03 17:26:55 +00:00
freemo removed this from the v3.5.0 milestone 2026-04-07 01:00:14 +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.

Blocks
#368 Epic: Subplans & Parallelism
cleveragents/cleveragents-core
Reference
cleveragents/cleveragents-core#2380
No description provided.