UAT: Plan, Action, Session, Tool, Skill, and Validation models use use_enum_values=False — deviates from canonical DomainBaseModel config #5608

Open
opened 2026-04-09 07:46:48 +00:00 by HAL9000 · 0 comments
Owner

Bug Report

Feature Area: pydantic-domain-models
What was tested: use_enum_values configuration in core domain models

Expected Behavior

Per the specification (§ Domain Models, line 43996), the canonical DomainBaseModel configuration includes use_enum_values=True. This means enum fields are stored and serialized as their underlying primitive values (strings/ints) rather than as enum instances. This is the standard Pydantic v2 behavior for domain models.

The DomainBaseModel in domain/models/base.py correctly sets:

model_config = ConfigDict(
    str_strip_whitespace=True,
    validate_assignment=True,
    arbitrary_types_allowed=False,
    populate_by_name=True,
    use_enum_values=True,   # ← canonical setting
)

Actual Behavior

Multiple core domain models explicitly override use_enum_values=False, deviating from the canonical config:

Model File Setting
Plan plan.py use_enum_values=False (comment: "Keep enums as enum objects")
Action action.py use_enum_values=False
Session session.py use_enum_values=False
Tool tool.py use_enum_values=False
Validation tool.py use_enum_values=False
Skill skill.py use_enum_values=False

Impact

With use_enum_values=False, enum fields are stored as enum objects rather than their primitive values. This causes:

  1. Serialization inconsistency: model_dump() returns enum objects instead of strings. Code that expects plan.phase == "strategize" will fail; it must use plan.phase == PlanPhase.STRATEGIZE or plan.phase.value == "strategize".

  2. JSON serialization requires mode="json": model_dump() returns PlanPhase.STRATEGIZE (an enum object), not "strategize" (a string). Callers must use model_dump(mode="json") to get JSON-serializable output, which is easy to forget.

  3. Inconsistency with other models: Models that inherit from DomainBaseModel (e.g., ModelError, FallbackResult, ModelProviderOption) use use_enum_values=True. This creates an inconsistent API where some models return enum objects and others return primitive values.

  4. Database layer friction: The infrastructure layer must handle both enum objects and primitive values when persisting domain models.

Example

from cleveragents.domain.models.core.plan import Plan, PlanPhase, ProcessingState, PlanIdentity, NamespacedName, PlanTimestamps
from ulid import ULID

plan = Plan(
    identity=PlanIdentity(plan_id=str(ULID())),
    namespaced_name=NamespacedName(name="my-plan"),
    description="Test plan",
    action_name="local/my-action",
    phase=PlanPhase.STRATEGIZE,
    processing_state=ProcessingState.QUEUED,
    timestamps=PlanTimestamps(),
)

data = plan.model_dump()
print(type(data["phase"]))   # <enum 'PlanPhase'> — NOT a string!
print(data["phase"])         # PlanPhase.STRATEGIZE — NOT "strategize"!

# With use_enum_values=True (canonical), this would be:
# print(type(data["phase"]))  # <class 'str'>
# print(data["phase"])        # "strategize"

Note on Intentionality

The Plan model has a comment use_enum_values=False, # Keep enums as enum objects suggesting this is intentional. However, this deviates from the spec's canonical config and creates the inconsistencies described above. The spec's use_enum_values=True is the correct setting for domain models that need to be serialized to JSON/YAML/database.

If enum objects are needed in the application layer, they can be obtained via PlanPhase(plan.phase) when use_enum_values=True is set.

Fix

Remove the explicit use_enum_values=False from all affected models, allowing them to inherit the canonical use_enum_values=True behavior. If these models are migrated to inherit from DomainBaseModel (per issue #3403), this will be resolved automatically.


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

## Bug Report **Feature Area:** pydantic-domain-models **What was tested:** `use_enum_values` configuration in core domain models ## Expected Behavior Per the specification (§ Domain Models, line 43996), the canonical `DomainBaseModel` configuration includes `use_enum_values=True`. This means enum fields are stored and serialized as their underlying primitive values (strings/ints) rather than as enum instances. This is the standard Pydantic v2 behavior for domain models. The `DomainBaseModel` in `domain/models/base.py` correctly sets: ```python model_config = ConfigDict( str_strip_whitespace=True, validate_assignment=True, arbitrary_types_allowed=False, populate_by_name=True, use_enum_values=True, # ← canonical setting ) ``` ## Actual Behavior Multiple core domain models explicitly override `use_enum_values=False`, deviating from the canonical config: | Model | File | Setting | |-------|------|---------| | `Plan` | `plan.py` | `use_enum_values=False` (comment: "Keep enums as enum objects") | | `Action` | `action.py` | `use_enum_values=False` | | `Session` | `session.py` | `use_enum_values=False` | | `Tool` | `tool.py` | `use_enum_values=False` | | `Validation` | `tool.py` | `use_enum_values=False` | | `Skill` | `skill.py` | `use_enum_values=False` | ## Impact With `use_enum_values=False`, enum fields are stored as enum objects rather than their primitive values. This causes: 1. **Serialization inconsistency**: `model_dump()` returns enum objects instead of strings. Code that expects `plan.phase == "strategize"` will fail; it must use `plan.phase == PlanPhase.STRATEGIZE` or `plan.phase.value == "strategize"`. 2. **JSON serialization requires `mode="json"`**: `model_dump()` returns `PlanPhase.STRATEGIZE` (an enum object), not `"strategize"` (a string). Callers must use `model_dump(mode="json")` to get JSON-serializable output, which is easy to forget. 3. **Inconsistency with other models**: Models that inherit from `DomainBaseModel` (e.g., `ModelError`, `FallbackResult`, `ModelProviderOption`) use `use_enum_values=True`. This creates an inconsistent API where some models return enum objects and others return primitive values. 4. **Database layer friction**: The infrastructure layer must handle both enum objects and primitive values when persisting domain models. ## Example ```python from cleveragents.domain.models.core.plan import Plan, PlanPhase, ProcessingState, PlanIdentity, NamespacedName, PlanTimestamps from ulid import ULID plan = Plan( identity=PlanIdentity(plan_id=str(ULID())), namespaced_name=NamespacedName(name="my-plan"), description="Test plan", action_name="local/my-action", phase=PlanPhase.STRATEGIZE, processing_state=ProcessingState.QUEUED, timestamps=PlanTimestamps(), ) data = plan.model_dump() print(type(data["phase"])) # <enum 'PlanPhase'> — NOT a string! print(data["phase"]) # PlanPhase.STRATEGIZE — NOT "strategize"! # With use_enum_values=True (canonical), this would be: # print(type(data["phase"])) # <class 'str'> # print(data["phase"]) # "strategize" ``` ## Note on Intentionality The `Plan` model has a comment `use_enum_values=False, # Keep enums as enum objects` suggesting this is intentional. However, this deviates from the spec's canonical config and creates the inconsistencies described above. The spec's `use_enum_values=True` is the correct setting for domain models that need to be serialized to JSON/YAML/database. If enum objects are needed in the application layer, they can be obtained via `PlanPhase(plan.phase)` when `use_enum_values=True` is set. ## Fix Remove the explicit `use_enum_values=False` from all affected models, allowing them to inherit the canonical `use_enum_values=True` behavior. If these models are migrated to inherit from `DomainBaseModel` (per issue #3403), this will be resolved automatically. --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: uat-tester
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#5608
No description provided.