UAT: _cleveragents/plan/explain, plan/correct, and plan/rollback A2A extension methods are unimplemented stubs — always return "stub": True regardless of plan state #4855

Open
opened 2026-04-08 20:09:35 +00:00 by HAL9000 · 0 comments
Owner

Bug Report

Tested by: UAT tester instance uat-tester-a2a-protocol
Feature area: A2A Protocol — _cleveragents/plan/ lifecycle extension methods
Severity: Medium — three plan lifecycle operations are non-functional via A2A


What Was Tested

Code-level analysis of src/cleveragents/a2a/facade.py — the _handle_plan_explain, _handle_plan_correct, and _handle_plan_rollback handlers.

Expected Behavior (from spec)

The spec defines these as fully functional A2A extension methods that delegate to PlanLifecycleService:

  • _cleveragents/plan/explain — returns the explanation for a specific decision in a plan's decision tree
  • _cleveragents/plan/correct — applies a correction to a plan's decision tree (revert or append mode)
  • _cleveragents/plan/rollback — rolls back a plan to a previous checkpoint

Actual Behavior

All three handlers are stubs that ignore the plan_lifecycle_service and always return stub responses:

_handle_plan_explain (lines ~370–380 of facade.py):

def _handle_plan_explain(self, params: dict[str, Any]) -> dict[str, Any]:
    plan_id = params.get("plan_id", "")
    decision_id = params.get("decision_id", "")
    return {
        "plan_id": plan_id,
        "decision_id": decision_id,
        "explanation": "Not yet implemented",
        "stub": True,
    }

_handle_plan_correct (lines ~383–386 of facade.py):

def _handle_plan_correct(self, params: dict[str, Any]) -> dict[str, Any]:
    plan_id = params.get("plan_id", "")
    return {"plan_id": plan_id, "status": "corrected", "stub": True}

_handle_plan_rollback (lines ~388–391 of facade.py):

def _handle_plan_rollback(self, params: dict[str, Any]) -> dict[str, Any]:
    plan_id = params.get("plan_id", "")
    return {"plan_id": plan_id, "status": "rolled_back", "stub": True}

None of these handlers check for self._plan_lifecycle_service or delegate to it. They always return stub responses regardless of whether a real service is wired in.

Contrast with Working Handlers

Compare with _handle_plan_cancel which correctly delegates:

def _handle_plan_cancel(self, params: dict[str, Any]) -> dict[str, Any]:
    plan_id = params.get("plan_id", "")
    svc = self._plan_lifecycle_service
    if svc is None or not plan_id:
        return {"plan_id": plan_id, "status": "cancelled", "stub": True}
    svc.cancel_plan(plan_id)
    return {"plan_id": plan_id, "status": "cancelled"}

Steps to Reproduce

from cleveragents.a2a.facade import A2aLocalFacade
from cleveragents.a2a.models import A2aRequest

facade = A2aLocalFacade()

# Even with a real plan_id, these always return stub responses:
for op in ["_cleveragents/plan/explain", "_cleveragents/plan/correct",
           "_cleveragents/plan/rollback"]:
    resp = facade.dispatch(A2aRequest(method=op, params={"plan_id": "01HXTEST"}))
    print(f"{op}: stub={resp.result.get('stub')}")  # Always True

Expected Fix

Each handler should follow the same pattern as _handle_plan_cancel:

  1. Check self._plan_lifecycle_service
  2. If available, delegate to the appropriate service method
  3. Return stub only when service is not wired

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

## Bug Report **Tested by:** UAT tester instance `uat-tester-a2a-protocol` **Feature area:** A2A Protocol — `_cleveragents/plan/` lifecycle extension methods **Severity:** Medium — three plan lifecycle operations are non-functional via A2A --- ### What Was Tested Code-level analysis of `src/cleveragents/a2a/facade.py` — the `_handle_plan_explain`, `_handle_plan_correct`, and `_handle_plan_rollback` handlers. ### Expected Behavior (from spec) The spec defines these as fully functional A2A extension methods that delegate to `PlanLifecycleService`: - `_cleveragents/plan/explain` — returns the explanation for a specific decision in a plan's decision tree - `_cleveragents/plan/correct` — applies a correction to a plan's decision tree (revert or append mode) - `_cleveragents/plan/rollback` — rolls back a plan to a previous checkpoint ### Actual Behavior All three handlers are stubs that ignore the `plan_lifecycle_service` and always return stub responses: **`_handle_plan_explain` (lines ~370–380 of facade.py):** ```python def _handle_plan_explain(self, params: dict[str, Any]) -> dict[str, Any]: plan_id = params.get("plan_id", "") decision_id = params.get("decision_id", "") return { "plan_id": plan_id, "decision_id": decision_id, "explanation": "Not yet implemented", "stub": True, } ``` **`_handle_plan_correct` (lines ~383–386 of facade.py):** ```python def _handle_plan_correct(self, params: dict[str, Any]) -> dict[str, Any]: plan_id = params.get("plan_id", "") return {"plan_id": plan_id, "status": "corrected", "stub": True} ``` **`_handle_plan_rollback` (lines ~388–391 of facade.py):** ```python def _handle_plan_rollback(self, params: dict[str, Any]) -> dict[str, Any]: plan_id = params.get("plan_id", "") return {"plan_id": plan_id, "status": "rolled_back", "stub": True} ``` None of these handlers check for `self._plan_lifecycle_service` or delegate to it. They always return stub responses regardless of whether a real service is wired in. ### Contrast with Working Handlers Compare with `_handle_plan_cancel` which correctly delegates: ```python def _handle_plan_cancel(self, params: dict[str, Any]) -> dict[str, Any]: plan_id = params.get("plan_id", "") svc = self._plan_lifecycle_service if svc is None or not plan_id: return {"plan_id": plan_id, "status": "cancelled", "stub": True} svc.cancel_plan(plan_id) return {"plan_id": plan_id, "status": "cancelled"} ``` ### Steps to Reproduce ```python from cleveragents.a2a.facade import A2aLocalFacade from cleveragents.a2a.models import A2aRequest facade = A2aLocalFacade() # Even with a real plan_id, these always return stub responses: for op in ["_cleveragents/plan/explain", "_cleveragents/plan/correct", "_cleveragents/plan/rollback"]: resp = facade.dispatch(A2aRequest(method=op, params={"plan_id": "01HXTEST"})) print(f"{op}: stub={resp.result.get('stub')}") # Always True ``` ### Expected Fix Each handler should follow the same pattern as `_handle_plan_cancel`: 1. Check `self._plan_lifecycle_service` 2. If available, delegate to the appropriate service method 3. Return stub only when service is not wired --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
HAL9000 added this to the v3.5.0 milestone 2026-04-08 20:16:21 +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.

Dependencies

No dependencies set.

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