BUG-HUNT: [domain-model-invariant] Invariant model uses BaseModel instead of DomainBaseModel losing shared configuration #7774

Open
opened 2026-04-12 03:31:20 +00:00 by HAL9000 · 3 comments
Owner

Bug Report: Domain Model Invariant — Invariant Uses BaseModel Instead of DomainBaseModel

Severity Assessment

  • Impact: Invariant and related classes in invariant.py inherit from pydantic.BaseModel directly instead of DomainBaseModel, meaning they do not inherit the shared domain configuration (str_strip_whitespace, validate_assignment, populate_by_name, use_enum_values). Instead, they each define their own model_config, which duplicates configuration and could diverge from the shared baseline.
  • Likelihood: High — any whitespace in text or source_name fields bypasses domain-wide strip behavior unless a custom validator runs first.
  • Priority: Medium

Location

  • File: src/cleveragents/domain/models/core/invariant.py
  • Classes: Invariant, InvariantSet, InvariantViolation, InvariantEnforcementRecord
  • Lines: Throughout the file

Description

The domain layer has a shared DomainBaseModel in base.py that centralizes all Pydantic configuration. However, the invariant.py models inherit directly from pydantic.BaseModel and define their own model_config inline.

This creates two specific problems:

  1. The Invariant model defines model_config = ConfigDict(str_strip_whitespace=True, frozen=True) — it is missing validate_assignment, populate_by_name, and use_enum_values that DomainBaseModel provides.
  2. The Invariant model IS frozen, so validate_assignment is less relevant (frozen models block all assignment). However, populate_by_name and use_enum_values are still missing, which may cause issues when deserializing from JSON with alias names.
  3. More critically: InvariantViolation and InvariantEnforcementRecord also inherit from plain BaseModel with their own configs — neither inherits from DomainBaseModel. These are not frozen, so missing validate_assignment means field reassignments after construction are not validated.

Evidence

# From invariant.py
from pydantic import BaseModel, ConfigDict, Field, field_validator  # NOT DomainBaseModel

class Invariant(BaseModel):  # Should be DomainBaseModel or at least consistent
    ...
    model_config = ConfigDict(
        str_strip_whitespace=True,
        frozen=True,
        # Missing: validate_assignment, populate_by_name, use_enum_values
    )

class InvariantViolation(BaseModel):  # Should be DomainBaseModel
    ...
    model_config = ConfigDict(
        str_strip_whitespace=True,
        frozen=True,
        # Missing: validate_assignment, populate_by_name, use_enum_values
    )

Vs. the DomainBaseModel:

class DomainBaseModel(BaseModel):
    model_config = ConfigDict(
        str_strip_whitespace=True,
        validate_assignment=True,
        arbitrary_types_allowed=False,
        populate_by_name=True,
        use_enum_values=True,
    )

Expected Behavior

All domain models should inherit from DomainBaseModel (or explicitly justify their deviation from it). The invariant.py models should be consistent with the rest of the domain layer.

Actual Behavior

The invariant.py models bypass the shared DomainBaseModel and define their own configs, leading to:

  • Missing populate_by_name=True (alias-based construction may fail)
  • Missing use_enum_values=True (enums stored as enum instances not primitive values)
  • Configuration fragmentation across the domain layer

Suggested Fix

from cleveragents.domain.models.base import DomainBaseModel

class Invariant(DomainBaseModel):
    ...
    model_config = ConfigDict(
        **DomainBaseModel.model_config,
        frozen=True,  # Override: invariants are immutable
    )

Category

domain-model-invariant

TDD Note

After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD.


Automated by CleverAgents Bot
Supervisor: Bug Hunting | Agent: bug-hunter

## Bug Report: Domain Model Invariant — Invariant Uses BaseModel Instead of DomainBaseModel ### Severity Assessment - **Impact**: `Invariant` and related classes in `invariant.py` inherit from `pydantic.BaseModel` directly instead of `DomainBaseModel`, meaning they do not inherit the shared domain configuration (`str_strip_whitespace`, `validate_assignment`, `populate_by_name`, `use_enum_values`). Instead, they each define their own `model_config`, which duplicates configuration and could diverge from the shared baseline. - **Likelihood**: High — any whitespace in `text` or `source_name` fields bypasses domain-wide strip behavior unless a custom validator runs first. - **Priority**: Medium ### Location - **File**: `src/cleveragents/domain/models/core/invariant.py` - **Classes**: `Invariant`, `InvariantSet`, `InvariantViolation`, `InvariantEnforcementRecord` - **Lines**: Throughout the file ### Description The domain layer has a shared `DomainBaseModel` in `base.py` that centralizes all Pydantic configuration. However, the `invariant.py` models inherit directly from `pydantic.BaseModel` and define their own `model_config` inline. This creates two specific problems: 1. The `Invariant` model defines `model_config = ConfigDict(str_strip_whitespace=True, frozen=True)` — it is missing `validate_assignment`, `populate_by_name`, and `use_enum_values` that `DomainBaseModel` provides. 2. The `Invariant` model IS frozen, so `validate_assignment` is less relevant (frozen models block all assignment). However, `populate_by_name` and `use_enum_values` are still missing, which may cause issues when deserializing from JSON with alias names. 3. More critically: `InvariantViolation` and `InvariantEnforcementRecord` also inherit from plain `BaseModel` with their own configs — neither inherits from `DomainBaseModel`. These are not frozen, so missing `validate_assignment` means field reassignments after construction are not validated. ### Evidence ```python # From invariant.py from pydantic import BaseModel, ConfigDict, Field, field_validator # NOT DomainBaseModel class Invariant(BaseModel): # Should be DomainBaseModel or at least consistent ... model_config = ConfigDict( str_strip_whitespace=True, frozen=True, # Missing: validate_assignment, populate_by_name, use_enum_values ) class InvariantViolation(BaseModel): # Should be DomainBaseModel ... model_config = ConfigDict( str_strip_whitespace=True, frozen=True, # Missing: validate_assignment, populate_by_name, use_enum_values ) ``` Vs. the `DomainBaseModel`: ```python class DomainBaseModel(BaseModel): model_config = ConfigDict( str_strip_whitespace=True, validate_assignment=True, arbitrary_types_allowed=False, populate_by_name=True, use_enum_values=True, ) ``` ### Expected Behavior All domain models should inherit from `DomainBaseModel` (or explicitly justify their deviation from it). The `invariant.py` models should be consistent with the rest of the domain layer. ### Actual Behavior The `invariant.py` models bypass the shared `DomainBaseModel` and define their own configs, leading to: - Missing `populate_by_name=True` (alias-based construction may fail) - Missing `use_enum_values=True` (enums stored as enum instances not primitive values) - Configuration fragmentation across the domain layer ### Suggested Fix ```python from cleveragents.domain.models.base import DomainBaseModel class Invariant(DomainBaseModel): ... model_config = ConfigDict( **DomainBaseModel.model_config, frozen=True, # Override: invariants are immutable ) ``` ### Category domain-model-invariant ### TDD Note After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD. --- **Automated by CleverAgents Bot** Supervisor: Bug Hunting | Agent: bug-hunter
HAL9000 added this to the v3.2.0 milestone 2026-04-12 03:43:39 +00:00
Author
Owner

Verified — Domain model bug: Invariant uses BaseModel instead of DomainBaseModel. MoSCoW: Should-have. Priority: Medium.


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

✅ **Verified** — Domain model bug: Invariant uses BaseModel instead of DomainBaseModel. MoSCoW: Should-have. Priority: Medium. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Author
Owner

Verified — Domain model bug: Invariant uses BaseModel instead of DomainBaseModel. MoSCoW: Should-have. Priority: Medium.


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

✅ **Verified** — Domain model bug: Invariant uses BaseModel instead of DomainBaseModel. MoSCoW: Should-have. Priority: Medium. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Author
Owner

Verified — Domain model bug: Invariant uses BaseModel instead of DomainBaseModel. MoSCoW: Should-have. Priority: Medium.


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

✅ **Verified** — Domain model bug: Invariant uses BaseModel instead of DomainBaseModel. MoSCoW: Should-have. Priority: Medium. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
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#7774
No description provided.