UAT: PlanExecutor.run_strategize() does not persist StrategyDecision objects to the decisions table — plan tree is always empty after strategize #5336

Open
opened 2026-04-09 05:53:40 +00:00 by HAL9000 · 1 comment
Owner

Bug Report

Feature Area: Decision Recording — Strategize Phase
Severity: Critical — agents plan tree always shows "No decisions found" after strategize because decisions are never written to the DB
Source: src/cleveragents/application/services/plan_executor.py lines 695–734


What Was Tested

Code-level analysis of PlanExecutor.run_strategize() to verify that StrategyDecision objects returned by the strategize actor are persisted to the decisions table via DecisionService.record_decision().

Expected Behavior (from spec §Decision Tree — Recording Protocol, line 46766)

Decision recording during Strategize with full context snapshots — agents plan tree <plan_id> renders non-empty tree after strategize

The spec requires that every decision made during the Strategize phase is persisted to the decisions database table with:

  • A ULID decision ID
  • The plan ID
  • Decision type (strategy_choice, prompt_definition, invariant_enforced, etc.)
  • The question and chosen option
  • A context snapshot (including hot_context_hash)
  • Parent-child relationships forming the decision tree

Actual Behavior

PlanExecutor.run_strategize() (lines 695–734) calls the strategize actor and receives a StrategizeResult containing a list of StrategyDecision objects. However, none of these decisions are persisted to the database. The code only stores the count in plan.error_details:

# plan_executor.py lines 712-718 — decisions are NOT persisted:
plan.error_details = {
    "strategy_decisions": str(len(result.decisions)),  # Only the COUNT is stored!
    "invariant_records": str(len(result.invariant_records)),
}
self._lifecycle._commit_plan(plan)
# ← No call to decision_service.record_decision() for any StrategyDecision

The PlanLifecycleService.start_strategize() does call _try_record_decision() once (line 1390–1395), but this records only a single generic strategy_choice decision ("Which strategy should the plan follow?"), not the actual decisions produced by the actor.

Root Cause

PlanExecutor has no reference to DecisionService. The StrategizeResult.decisions list (containing the actual StrategyDecision objects with step text, sequence, and parent IDs) is never converted to domain Decision objects and never passed to DecisionService.record_decision().

Impact

  1. agents plan tree <plan_id> always shows "No decisions found" after strategize — the tree is empty because no decisions were written to the DB
  2. agents plan explain <decision_id> cannot find any decisions from the strategize phase
  3. agents plan correct cannot target any decisions because none exist in the DB
  4. The v3.2.0 milestone acceptance criterion "Decisions are recorded during Strategize with full context snapshots" is not met

Code Location

  • src/cleveragents/application/services/plan_executor.py lines 695–734 — run_strategize() method
  • src/cleveragents/application/services/plan_executor.py lines 84–100 — StrategyDecision model (has decision_id, step_text, sequence, parent_id but never persisted)
  • src/cleveragents/application/services/decision_service.pyDecisionService.record_decision() exists and works but is never called from PlanExecutor

Fix Required

PlanExecutor.run_strategize() must be updated to:

  1. Accept a DecisionService dependency (or access it via the lifecycle service)
  2. After the actor returns StrategizeResult, iterate over result.decisions and call decision_service.record_decision() for each one, converting StrategyDecision → domain Decision with:
    • decision_type = "strategy_choice" (or "prompt_definition" for root)
    • question = decision.step_text
    • chosen_option = decision.step_text
    • parent_decision_id = decision.parent_id
    • context_snapshot auto-captured
  3. The root decision should use decision_type = "prompt_definition"
  • #4988 — Feature issue tracking this work (open, not yet implemented)
  • #5183plan tree rich output gaps (separate issue)
  • Part of #4958 (EPIC: Decision Recording & Tree Visualization)

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

## Bug Report **Feature Area:** Decision Recording — Strategize Phase **Severity:** Critical — `agents plan tree` always shows "No decisions found" after strategize because decisions are never written to the DB **Source:** `src/cleveragents/application/services/plan_executor.py` lines 695–734 --- ## What Was Tested Code-level analysis of `PlanExecutor.run_strategize()` to verify that `StrategyDecision` objects returned by the strategize actor are persisted to the `decisions` table via `DecisionService.record_decision()`. ## Expected Behavior (from spec §Decision Tree — Recording Protocol, line 46766) > Decision recording during Strategize with full context snapshots — `agents plan tree <plan_id>` renders non-empty tree after strategize The spec requires that every decision made during the Strategize phase is persisted to the `decisions` database table with: - A ULID decision ID - The plan ID - Decision type (`strategy_choice`, `prompt_definition`, `invariant_enforced`, etc.) - The question and chosen option - A context snapshot (including `hot_context_hash`) - Parent-child relationships forming the decision tree ## Actual Behavior `PlanExecutor.run_strategize()` (lines 695–734) calls the strategize actor and receives a `StrategizeResult` containing a list of `StrategyDecision` objects. However, **none of these decisions are persisted to the database**. The code only stores the count in `plan.error_details`: ```python # plan_executor.py lines 712-718 — decisions are NOT persisted: plan.error_details = { "strategy_decisions": str(len(result.decisions)), # Only the COUNT is stored! "invariant_records": str(len(result.invariant_records)), } self._lifecycle._commit_plan(plan) # ← No call to decision_service.record_decision() for any StrategyDecision ``` The `PlanLifecycleService.start_strategize()` does call `_try_record_decision()` once (line 1390–1395), but this records only a single generic `strategy_choice` decision ("Which strategy should the plan follow?"), not the actual decisions produced by the actor. ## Root Cause `PlanExecutor` has no reference to `DecisionService`. The `StrategizeResult.decisions` list (containing the actual `StrategyDecision` objects with step text, sequence, and parent IDs) is never converted to domain `Decision` objects and never passed to `DecisionService.record_decision()`. ## Impact 1. **`agents plan tree <plan_id>` always shows "No decisions found"** after strategize — the tree is empty because no decisions were written to the DB 2. **`agents plan explain <decision_id>` cannot find any decisions** from the strategize phase 3. **`agents plan correct`** cannot target any decisions because none exist in the DB 4. **The v3.2.0 milestone acceptance criterion** "Decisions are recorded during Strategize with full context snapshots" is not met ## Code Location - `src/cleveragents/application/services/plan_executor.py` lines 695–734 — `run_strategize()` method - `src/cleveragents/application/services/plan_executor.py` lines 84–100 — `StrategyDecision` model (has `decision_id`, `step_text`, `sequence`, `parent_id` but never persisted) - `src/cleveragents/application/services/decision_service.py` — `DecisionService.record_decision()` exists and works but is never called from `PlanExecutor` ## Fix Required `PlanExecutor.run_strategize()` must be updated to: 1. Accept a `DecisionService` dependency (or access it via the lifecycle service) 2. After the actor returns `StrategizeResult`, iterate over `result.decisions` and call `decision_service.record_decision()` for each one, converting `StrategyDecision` → domain `Decision` with: - `decision_type = "strategy_choice"` (or `"prompt_definition"` for root) - `question = decision.step_text` - `chosen_option = decision.step_text` - `parent_decision_id = decision.parent_id` - `context_snapshot` auto-captured 3. The root decision should use `decision_type = "prompt_definition"` ## Related Issues - #4988 — Feature issue tracking this work (open, not yet implemented) - #5183 — `plan tree` rich output gaps (separate issue) - Part of #4958 (EPIC: Decision Recording & Tree Visualization) --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
HAL9000 added this to the v3.2.0 milestone 2026-04-09 05:53:51 +00:00
Author
Owner

Label compliance fix applied:

  • Added missing label: Points/5 (L — large complexity)
  • Reason: Issue is in State/Verified but was missing a story points estimate. Estimated as Points/5 (L) based on complex bug requiring PlanExecutor changes.

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

Label compliance fix applied: - Added missing label: `Points/5` (L — large complexity) - Reason: Issue is in `State/Verified` but was missing a story points estimate. Estimated as Points/5 (L) based on complex bug requiring PlanExecutor changes. --- **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.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#5336
No description provided.