UAT: Plan.namespaced_name is required for all plans including child plans — spec says child plans are identified solely by ULID and should not carry a namespaced name #4850

Open
opened 2026-04-08 20:08:35 +00:00 by HAL9000 · 1 comment
Owner

Summary

The Plan domain model requires namespaced_name as a mandatory field (...) for all plans, including child (sub)plans. The specification explicitly states that child plans are identified solely by their plan ID (ULID) and do not carry a namespaced name. This forces callers to invent a fake namespaced name when creating child plans, which is incorrect.

What Was Tested

Code-level analysis of src/cleveragents/domain/models/core/plan.py (Plan, NamespacedName) against the specification's Plan Lifecycle definition.

Expected Behavior (from spec §Plan Lifecycle)

A ULID-identified, hierarchical unit of work instantiated from an Action template. Progresses through four phases — Action, Strategize, Execute, Apply — persisting a decision tree at each step. May spawn child plans. Scoped to one or more projects. Top-level plans carry a namespaced name ([[server:]namespace/]name); child plans are identified solely by their plan ID (ULID).

Child plans should:

  • Have namespaced_name as None or optional
  • Be identified exclusively by their ULID (plan_id)
  • Have parent_plan_id set to the parent plan's ULID

Actual Behavior

In src/cleveragents/domain/models/core/plan.py, line 574:

namespaced_name: NamespacedName = Field(
    ..., description="Namespaced name of the plan"
)

The ... (Ellipsis) makes this field required for all Plan instances. There is no model validator that makes namespaced_name optional when parent_plan_id is set (i.e., when the plan is a child plan).

The is_subplan property correctly identifies child plans:

@property
def is_subplan(self) -> bool:
    """Check if this plan is a subplan (has a parent)."""
    return self.identity.parent_plan_id is not None

But there is no corresponding relaxation of the namespaced_name requirement for subplans.

Code Location

  • src/cleveragents/domain/models/core/plan.py, line 574 (namespaced_name field definition)
  • src/cleveragents/domain/models/core/plan.py, lines 984–986 (is_subplan property)

Impact

  1. Incorrect domain model: Child plans must be given a namespaced name even though the spec says they don't have one. This either forces callers to invent synthetic names or silently violates the spec.
  2. Serialization mismatch: When child plans are serialized (e.g., in plan tree output), they will show a namespaced name that doesn't exist in the spec's data model.
  3. Validation bypass: The spec's identity model for child plans is plan_id only. Requiring namespaced_name creates an extra constraint that the spec doesn't impose.

Correct Fix

Make namespaced_name optional for child plans:

namespaced_name: NamespacedName | None = Field(
    None, description="Namespaced name of the plan (None for child plans)"
)

And add a model validator:

@model_validator(mode="after")
def validate_name_required_for_root_plans(self) -> Plan:
    if self.identity.parent_plan_id is None and self.namespaced_name is None:
        raise ValueError("Top-level plans must have a namespaced_name")
    return self

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

## Summary The `Plan` domain model requires `namespaced_name` as a mandatory field (`...`) for all plans, including child (sub)plans. The specification explicitly states that child plans are identified solely by their plan ID (ULID) and do not carry a namespaced name. This forces callers to invent a fake namespaced name when creating child plans, which is incorrect. ## What Was Tested Code-level analysis of `src/cleveragents/domain/models/core/plan.py` (`Plan`, `NamespacedName`) against the specification's Plan Lifecycle definition. ## Expected Behavior (from spec §Plan Lifecycle) > A ULID-identified, hierarchical unit of work instantiated from an Action template. Progresses through four phases — **Action**, **Strategize**, **Execute**, **Apply** — persisting a decision tree at each step. May spawn child plans. Scoped to one or more projects. **Top-level plans carry a namespaced name (`[[server:]namespace/]name`); child plans are identified solely by their plan ID (ULID).** Child plans should: - Have `namespaced_name` as `None` or optional - Be identified exclusively by their ULID (`plan_id`) - Have `parent_plan_id` set to the parent plan's ULID ## Actual Behavior In `src/cleveragents/domain/models/core/plan.py`, line 574: ```python namespaced_name: NamespacedName = Field( ..., description="Namespaced name of the plan" ) ``` The `...` (Ellipsis) makes this field **required** for all `Plan` instances. There is no model validator that makes `namespaced_name` optional when `parent_plan_id` is set (i.e., when the plan is a child plan). The `is_subplan` property correctly identifies child plans: ```python @property def is_subplan(self) -> bool: """Check if this plan is a subplan (has a parent).""" return self.identity.parent_plan_id is not None ``` But there is no corresponding relaxation of the `namespaced_name` requirement for subplans. ## Code Location - `src/cleveragents/domain/models/core/plan.py`, line 574 (`namespaced_name` field definition) - `src/cleveragents/domain/models/core/plan.py`, lines 984–986 (`is_subplan` property) ## Impact 1. **Incorrect domain model**: Child plans must be given a namespaced name even though the spec says they don't have one. This either forces callers to invent synthetic names or silently violates the spec. 2. **Serialization mismatch**: When child plans are serialized (e.g., in `plan tree` output), they will show a namespaced name that doesn't exist in the spec's data model. 3. **Validation bypass**: The spec's identity model for child plans is `plan_id` only. Requiring `namespaced_name` creates an extra constraint that the spec doesn't impose. ## Correct Fix Make `namespaced_name` optional for child plans: ```python namespaced_name: NamespacedName | None = Field( None, description="Namespaced name of the plan (None for child plans)" ) ``` And add a model validator: ```python @model_validator(mode="after") def validate_name_required_for_root_plans(self) -> Plan: if self.identity.parent_plan_id is None and self.namespaced_name is None: raise ValueError("Top-level plans must have a namespaced_name") return self ``` --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
HAL9000 added this to the v3.3.0 milestone 2026-04-08 20:17:25 +00:00
Owner

Issue triaged by project owner:

  • State: Verified
  • Priority: Medium — Child plans incorrectly require namespaced_name; spec says child plans use ULID only
  • Milestone: v3.3.0 (Corrections + Subplans + Checkpoints)
  • Story Points: 3 — M — Fixing the Plan model to make namespaced_name optional for child plans is a medium task
  • MoSCoW: Should Have — Correct plan identity model per spec is important for subplan functionality
  • Parent Epic: #4942 (Decision Intelligence Legendary)

Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner

Issue triaged by project owner: - **State**: Verified - **Priority**: Medium — Child plans incorrectly require `namespaced_name`; spec says child plans use ULID only - **Milestone**: v3.3.0 (Corrections + Subplans + Checkpoints) - **Story Points**: 3 — M — Fixing the Plan model to make `namespaced_name` optional for child plans is a medium task - **MoSCoW**: Should Have — Correct plan identity model per spec is important for subplan functionality - **Parent Epic**: #4942 (Decision Intelligence Legendary) --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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#4850
No description provided.