Files
cleveragents-core/benchmarks/plan_model_bench.py
T
brent.edwards 122af46305 fix(tests): resolve unit test and benchmark failures
- Fix project_repository_steps session mismatch: use shared session
  for repos so context.pr_session.commit() commits flushed data
- Fix cli_format_bench setup/teardown to accept fmt parameter for
  parameterized ASV benchmarks
- Fix plan_model_bench PlanPhase.APPLIED -> PlanPhase.APPLY
- Fix benchmark idempotency: use batch counters to generate unique IDs
  across repeated ASV iterations (plan_phase_migration_bench,
  project_migration_bench, resource_registry_migration_bench)
- Fix uow_lifecycle_bench FK constraint by reusing pre-seeded action
- Fix plan_lifecycle_persistence_bench to create fresh plan per iteration
2026-02-17 02:24:35 +00:00

179 lines
5.7 KiB
Python

"""Airspeed Velocity benchmarks for v3 Plan domain model.
Measures validation, serialization, and as_cli_dict performance for the
Plan model across different field configurations.
"""
from __future__ import annotations
from cleveragents.domain.models.core.plan import (
AutomationProfileProvenance,
AutomationProfileRef,
InvariantSource,
NamespacedName,
Plan,
PlanIdentity,
PlanInvariant,
PlanPhase,
ProcessingState,
ProjectLink,
SubplanConfig,
SubplanStatus,
can_transition,
)
# Valid ULID constants for benchmarks
_ULID_A = "01HGZ6FE0AQDYTR4BXVQZ6EA00"
_ULID_B = "01HGZ6FE0AQDYTR4BXVQZ6EB00"
def _minimal_plan() -> Plan:
"""Create a minimal Plan with only required fields."""
return Plan(
identity=PlanIdentity(plan_id=_ULID_A),
namespaced_name=NamespacedName(
server=None, namespace="local", name="bench-plan"
),
description="Benchmark plan",
action_name="local/bench-action",
)
def _rich_plan() -> Plan:
"""Create a Plan with all optional fields populated."""
return Plan(
identity=PlanIdentity(
plan_id=_ULID_A,
parent_plan_id=_ULID_B,
root_plan_id=_ULID_B,
),
namespaced_name=NamespacedName(
server="prod", namespace="myorg", name="rich-plan"
),
description="Fully populated benchmark plan",
definition_of_done="All tests pass with >97% coverage",
action_name="myorg/code-coverage",
phase=PlanPhase.EXECUTE,
processing_state=ProcessingState.PROCESSING,
automation_profile=AutomationProfileRef(
profile_name="trusted",
provenance=AutomationProfileProvenance.ACTION,
),
project_links=[
ProjectLink(project_name="local/api-svc", alias="api", read_only=False),
ProjectLink(project_name="local/docs", read_only=True),
],
invariants=[
PlanInvariant(text="No public API changes", source=InvariantSource.ACTION),
PlanInvariant(text="Keep backward compat", source=InvariantSource.PROJECT),
],
arguments={"target": "src/main.py", "mode": "strict", "verbose": True},
arguments_order=["target", "mode", "verbose"],
strategy_actor="local/planner",
execution_actor="local/coder",
review_actor="local/reviewer",
error_message=None,
subplan_config=SubplanConfig(max_parallel=3),
subplan_statuses=[
SubplanStatus(subplan_id=_ULID_B, action_name="local/sub-action"),
],
)
class PlanValidationSuite:
"""Benchmark Plan model validation (construction)."""
def time_minimal_plan_creation(self) -> None:
"""Time creating a minimal Plan with only required fields."""
_minimal_plan()
def time_rich_plan_creation(self) -> None:
"""Time creating a Plan with all optional fields populated."""
_rich_plan()
def time_plan_model_validate_dict(self) -> None:
"""Time Plan.model_validate from a raw dict."""
Plan.model_validate(
{
"identity": {"plan_id": _ULID_A},
"namespaced_name": {"namespace": "local", "name": "bench-plan"},
"description": "Benchmark plan",
"action_name": "local/bench-action",
}
)
class PlanSerializationSuite:
"""Benchmark Plan model serialization."""
def setup(self) -> None:
self.minimal = _minimal_plan()
self.rich = _rich_plan()
def time_minimal_model_dump(self) -> None:
"""Time model_dump for a minimal Plan."""
self.minimal.model_dump()
def time_rich_model_dump(self) -> None:
"""Time model_dump for a fully populated Plan."""
self.rich.model_dump()
def time_minimal_model_dump_json(self) -> None:
"""Time model_dump_json for a minimal Plan."""
self.minimal.model_dump_json()
def time_rich_model_dump_json(self) -> None:
"""Time model_dump_json for a fully populated Plan."""
self.rich.model_dump_json()
class PlanCliDictSuite:
"""Benchmark Plan.as_cli_dict output generation."""
def setup(self) -> None:
self.minimal = _minimal_plan()
self.rich = _rich_plan()
def time_minimal_as_cli_dict(self) -> None:
"""Time as_cli_dict for a minimal Plan."""
self.minimal.as_cli_dict()
def time_rich_as_cli_dict(self) -> None:
"""Time as_cli_dict for a Plan with all optional fields."""
self.rich.as_cli_dict()
class PhaseTransitionSuite:
"""Benchmark phase transition validation."""
def time_can_transition_valid(self) -> None:
"""Time a valid phase transition check."""
can_transition(PlanPhase.STRATEGIZE, PlanPhase.EXECUTE)
def time_can_transition_invalid(self) -> None:
"""Time an invalid phase transition check."""
can_transition(PlanPhase.STRATEGIZE, PlanPhase.APPLY)
def time_all_transitions(self) -> None:
"""Time checking all possible phase transition pairs."""
for from_phase in PlanPhase:
for to_phase in PlanPhase:
can_transition(from_phase, to_phase)
class NamespacedNameSuite:
"""Benchmark NamespacedName parsing and serialization."""
def time_parse_simple(self) -> None:
"""Time parsing a simple name."""
NamespacedName.parse("my-action")
def time_parse_full(self) -> None:
"""Time parsing a full server:namespace/name string."""
NamespacedName.parse("prod:myorg/my-action")
def time_str_representation(self) -> None:
"""Time string representation of a NamespacedName."""
name = NamespacedName(server="prod", namespace="myorg", name="my-action")
str(name)