583e6b7ea2
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 14s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 17s
CI / security (pull_request) Successful in 36s
CI / typecheck (pull_request) Successful in 54s
CI / unit_tests (pull_request) Successful in 2m39s
CI / integration_tests (pull_request) Successful in 3m10s
CI / docker (pull_request) Successful in 40s
CI / coverage (pull_request) Successful in 5m0s
CI / lint (push) Successful in 14s
CI / build (push) Successful in 15s
CI / quality (push) Successful in 16s
CI / security (push) Successful in 34s
CI / typecheck (push) Successful in 35s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Successful in 2m23s
CI / docker (push) Successful in 38s
CI / integration_tests (push) Successful in 3m8s
CI / coverage (push) Successful in 5m10s
CI / benchmark-publish (push) Successful in 16m26s
CI / benchmark-regression (pull_request) Successful in 30m12s
Implement spec-mandated Layer 4 Predictive Error Prevention system: - ErrorPattern domain model with pattern text, historical failures, preventive checks, frequency tracking, and keyword-based matching. - ErrorPatternRepository with in-memory CRUD + context-matching query. - ErrorPatternService with record_failure(), match_patterns(), and get_statistics() methods. - Wire into plan execution via plan_lifecycle_service pre-execution hook. - Add error pattern statistics to CLI diagnostics output. Behave BDD: 11 scenarios covering recording, matching, formatting, stats. Robot Framework: 3 integration smoke tests. ASV benchmarks: pattern matching performance. ISSUES CLOSED: #571
180 lines
5.6 KiB
Python
180 lines
5.6 KiB
Python
"""ASV benchmarks for Invariant Reconciliation Actor throughput.
|
|
|
|
Measures the performance of:
|
|
- Reconciliation with varying numbers of invariants per scope
|
|
- Conflict detection and resolution overhead
|
|
- Decision recording throughput
|
|
- Full pipeline (collect → reconcile → record)
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import ClassVar
|
|
|
|
# Ensure the local source tree is importable
|
|
_SRC = str(Path(__file__).resolve().parents[1] / "src")
|
|
if _SRC not in sys.path:
|
|
sys.path.insert(0, _SRC)
|
|
|
|
import cleveragents # noqa: E402
|
|
|
|
importlib.reload(cleveragents)
|
|
|
|
from cleveragents.actor.reconciliation import ( # noqa: E402
|
|
InvariantReconciliationActor,
|
|
ScopeInvariants,
|
|
reconcile_invariants,
|
|
)
|
|
from cleveragents.application.services.decision_service import ( # noqa: E402
|
|
DecisionService,
|
|
)
|
|
from cleveragents.application.services.invariant_service import ( # noqa: E402
|
|
InvariantService,
|
|
)
|
|
from cleveragents.domain.models.core.invariant import ( # noqa: E402
|
|
Invariant,
|
|
InvariantScope,
|
|
)
|
|
|
|
|
|
def _make_invariant(text: str, scope: InvariantScope, source: str) -> Invariant:
|
|
"""Create an invariant without going through the service."""
|
|
return Invariant(text=text, scope=scope, source_name=source)
|
|
|
|
|
|
def _build_scope_invariants(
|
|
n_global: int = 0,
|
|
n_project: int = 0,
|
|
n_action: int = 0,
|
|
n_plan: int = 0,
|
|
) -> ScopeInvariants:
|
|
"""Build a ScopeInvariants with the given counts (unique texts)."""
|
|
return ScopeInvariants(
|
|
global_invariants=[
|
|
_make_invariant(f"global-rule-{i}", InvariantScope.GLOBAL, "system")
|
|
for i in range(n_global)
|
|
],
|
|
project_invariants=[
|
|
_make_invariant(f"project-rule-{i}", InvariantScope.PROJECT, "local/api")
|
|
for i in range(n_project)
|
|
],
|
|
action_invariants=[
|
|
_make_invariant(f"action-rule-{i}", InvariantScope.ACTION, "local/deploy")
|
|
for i in range(n_action)
|
|
],
|
|
plan_invariants=[
|
|
_make_invariant(f"plan-rule-{i}", InvariantScope.PLAN, "PLAN01")
|
|
for i in range(n_plan)
|
|
],
|
|
)
|
|
|
|
|
|
def _build_conflicting_scope_invariants(n_conflicts: int) -> ScopeInvariants:
|
|
"""Build scope invariants where each text appears at global and plan."""
|
|
return ScopeInvariants(
|
|
global_invariants=[
|
|
_make_invariant(f"shared-rule-{i}", InvariantScope.GLOBAL, "system")
|
|
for i in range(n_conflicts)
|
|
],
|
|
project_invariants=[],
|
|
action_invariants=[],
|
|
plan_invariants=[
|
|
_make_invariant(f"shared-rule-{i}", InvariantScope.PLAN, "PLAN01")
|
|
for i in range(n_conflicts)
|
|
],
|
|
)
|
|
|
|
|
|
class ReconciliationAlgorithmSuite:
|
|
"""Benchmark the pure reconciliation algorithm (no I/O)."""
|
|
|
|
params: ClassVar[list[int]] = [1, 10, 50, 100]
|
|
param_names: ClassVar[list[str]] = ["invariants_per_scope"]
|
|
|
|
def setup(self, n: int) -> None:
|
|
self._scope_invs = _build_scope_invariants(
|
|
n_global=n, n_project=n, n_action=n, n_plan=n
|
|
)
|
|
|
|
def time_reconcile_no_conflicts(self, n: int) -> None:
|
|
"""Reconcile N invariants per scope with no conflicts."""
|
|
reconcile_invariants(self._scope_invs)
|
|
|
|
|
|
class ConflictResolutionSuite:
|
|
"""Benchmark conflict detection and resolution."""
|
|
|
|
params: ClassVar[list[int]] = [1, 10, 50, 100]
|
|
param_names: ClassVar[list[str]] = ["conflict_count"]
|
|
|
|
def setup(self, n: int) -> None:
|
|
self._scope_invs = _build_conflicting_scope_invariants(n)
|
|
|
|
def time_reconcile_with_conflicts(self, n: int) -> None:
|
|
"""Reconcile N conflicting invariant pairs."""
|
|
reconcile_invariants(self._scope_invs)
|
|
|
|
|
|
class FullPipelineSuite:
|
|
"""Benchmark the full actor pipeline (collect + reconcile + record)."""
|
|
|
|
params: ClassVar[list[int]] = [1, 10, 50]
|
|
param_names: ClassVar[list[str]] = ["invariants_per_scope"]
|
|
|
|
def setup(self, n: int) -> None:
|
|
self._n = n
|
|
|
|
def time_full_pipeline(self, n: int) -> None:
|
|
"""Run full actor pipeline with N invariants per scope."""
|
|
inv_svc = InvariantService()
|
|
dec_svc = DecisionService()
|
|
|
|
for i in range(n):
|
|
inv_svc.add_invariant(f"global-rule-{i}", InvariantScope.GLOBAL, "system")
|
|
inv_svc.add_invariant(
|
|
f"project-rule-{i}", InvariantScope.PROJECT, "local/api"
|
|
)
|
|
inv_svc.add_invariant(
|
|
f"action-rule-{i}", InvariantScope.ACTION, "local/deploy"
|
|
)
|
|
inv_svc.add_invariant(
|
|
f"plan-rule-{i}", InvariantScope.PLAN, "01JQAAAAAAAAAAAAAAAAAAAA01"
|
|
)
|
|
|
|
actor = InvariantReconciliationActor(
|
|
invariant_service=inv_svc,
|
|
decision_service=dec_svc,
|
|
)
|
|
actor.run(
|
|
plan_id="01JQAAAAAAAAAAAAAAAAAAAA01",
|
|
project_name="local/api",
|
|
action_name="local/deploy",
|
|
)
|
|
|
|
|
|
class DecisionRecordingSuite:
|
|
"""Benchmark decision recording throughput."""
|
|
|
|
params: ClassVar[list[int]] = [1, 10, 50, 100]
|
|
param_names: ClassVar[list[str]] = ["invariant_count"]
|
|
|
|
def setup(self, n: int) -> None:
|
|
self._n = n
|
|
|
|
def time_record_decisions(self, n: int) -> None:
|
|
"""Record N invariant_enforced decisions."""
|
|
inv_svc = InvariantService()
|
|
dec_svc = DecisionService()
|
|
|
|
for i in range(n):
|
|
inv_svc.add_invariant(f"global-rule-{i}", InvariantScope.GLOBAL, "system")
|
|
|
|
actor = InvariantReconciliationActor(
|
|
invariant_service=inv_svc,
|
|
decision_service=dec_svc,
|
|
)
|
|
actor.run(plan_id="01JQAAAAAAAAAAAAAAAAAAAA01")
|