Files
markitect-main/markitect/prompts/quality/models.py
tegwick 704272644c
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
feat(prompts): implement Phase 7 - Quality & Validation (FR-9, FR-10)
Add quality gate framework with schema validation (JSON Schema via
jsonschema library), pattern validation (regex-based), multi-gate
QualityValidator with SQLite persistence, HaltingPolicyEngine with
budget/iteration/improvement checks, and RefinementLoop for iterative
execute-validate-halt cycles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 13:31:37 +01:00

284 lines
8.9 KiB
Python

"""
Data models for quality validation and halting policies.
Implements FR-9: QualityGate Validation
Implements FR-10: Halting and Refinement Policy
"""
import uuid
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Any, Dict, List, Optional
class GateType(Enum):
"""Type classification for quality gates."""
SCHEMA = "schema"
PATTERN = "pattern"
CUSTOM = "custom"
class ValidationStatus(Enum):
"""Outcome status of a quality gate check."""
PASS = "pass"
FAIL = "fail"
WARNING = "warning"
SKIPPED = "skipped"
class HaltDecision(Enum):
"""Decision outcome from halting policy evaluation."""
CONTINUE = "continue"
HALT_QUALITY_MET = "halted_quality_met"
HALT_ITERATION_LIMIT = "halted_iteration_limit"
HALT_BUDGET_EXHAUSTED = "halted_budget_exhausted"
HALT_NO_IMPROVEMENT = "halted_no_improvement"
@dataclass
class ValidationDiagnostic:
"""
Single diagnostic message from a quality gate.
Attributes:
code: Machine-readable diagnostic code
message: Human-readable description
severity: Severity level (error, warning, info)
"""
code: str
message: str
severity: str = "error"
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary."""
return {
"code": self.code,
"message": self.message,
"severity": self.severity,
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "ValidationDiagnostic":
"""Create from dictionary."""
return cls(
code=data["code"],
message=data["message"],
severity=data.get("severity", "error"),
)
@dataclass
class ValidationResult:
"""
Result of applying a quality gate to an artifact.
Implements FR-9.3: Record pass/fail results and diagnostics.
Attributes:
id: Unique result identifier
gate_id: ID of the quality gate that produced this result
gate_type: Type of the quality gate
artifact_id: ID of the validated artifact
status: Pass/fail outcome
score: Optional quality score (0.0-1.0)
diagnostics: List of diagnostic messages
validated_at: When validation occurred
"""
id: str
gate_id: str
gate_type: GateType
artifact_id: str
status: ValidationStatus
score: Optional[float] = None
diagnostics: List[ValidationDiagnostic] = field(default_factory=list)
validated_at: datetime = field(default_factory=datetime.utcnow)
@classmethod
def create(
cls,
gate_id: str,
gate_type: GateType,
artifact_id: str,
status: ValidationStatus,
score: Optional[float] = None,
diagnostics: Optional[List[ValidationDiagnostic]] = None,
) -> "ValidationResult":
"""Create a new ValidationResult."""
return cls(
id=str(uuid.uuid4()),
gate_id=gate_id,
gate_type=gate_type,
artifact_id=artifact_id,
status=status,
score=score,
diagnostics=diagnostics or [],
)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for serialization."""
return {
"id": self.id,
"gate_id": self.gate_id,
"gate_type": self.gate_type.value,
"artifact_id": self.artifact_id,
"status": self.status.value,
"score": self.score,
"diagnostics": [d.to_dict() for d in self.diagnostics],
"validated_at": self.validated_at.isoformat(),
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "ValidationResult":
"""Create from dictionary."""
return cls(
id=data["id"],
gate_id=data["gate_id"],
gate_type=GateType(data["gate_type"]),
artifact_id=data["artifact_id"],
status=ValidationStatus(data["status"]),
score=data.get("score"),
diagnostics=[
ValidationDiagnostic.from_dict(d)
for d in data.get("diagnostics", [])
],
validated_at=datetime.fromisoformat(data["validated_at"]),
)
class QualityGate(ABC):
"""
Abstract base class for quality gates.
Implements FR-9.1/FR-9.2: Pluggable validation framework
supporting multiple gates per artifact.
"""
def __init__(self, gate_id: str, name: str, gate_type: GateType):
self.id = gate_id
self.name = name
self.gate_type = gate_type
@abstractmethod
def validate(self, content: str, artifact_id: str) -> ValidationResult:
"""
Validate content against this quality gate.
Args:
content: Content to validate
artifact_id: ID of the artifact being validated
Returns:
ValidationResult with status and diagnostics
"""
pass
@dataclass
class QualityPolicy:
"""
Configuration for halting and refinement policy.
Implements FR-10.1: Configurable QualityPolicies.
Attributes:
max_iterations: Maximum refinement iterations
min_improvement: Minimum score improvement to continue
fail_on_gate_failure: Whether any gate failure halts execution
resource_budget: Maximum total runs allowed
required_gate_ids: Gate IDs that must pass for quality to be met
"""
max_iterations: int = 3
min_improvement: float = 0.05
fail_on_gate_failure: bool = True
resource_budget: int = 10
required_gate_ids: List[str] = field(default_factory=list)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary."""
return {
"max_iterations": self.max_iterations,
"min_improvement": self.min_improvement,
"fail_on_gate_failure": self.fail_on_gate_failure,
"resource_budget": self.resource_budget,
"required_gate_ids": self.required_gate_ids,
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "QualityPolicy":
"""Create from dictionary."""
return cls(
max_iterations=data.get("max_iterations", 3),
min_improvement=data.get("min_improvement", 0.05),
fail_on_gate_failure=data.get("fail_on_gate_failure", True),
resource_budget=data.get("resource_budget", 10),
required_gate_ids=data.get("required_gate_ids", []),
)
@dataclass
class HaltingRecord:
"""
Record of a halting decision.
Implements FR-10.3: Record halting decisions in the RunManifest.
Attributes:
decision: The halting decision
iteration: Current iteration number
max_iterations: Maximum allowed iterations
scores: Score history across iterations
reason: Human-readable reason for decision
recorded_at: When the decision was made
"""
decision: HaltDecision
iteration: int
max_iterations: int
scores: List[float] = field(default_factory=list)
reason: str = ""
recorded_at: datetime = field(default_factory=datetime.utcnow)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for serialization."""
return {
"decision": self.decision.value,
"iteration": self.iteration,
"max_iterations": self.max_iterations,
"scores": self.scores,
"reason": self.reason,
"recorded_at": self.recorded_at.isoformat(),
}
@dataclass
class RefinementResult:
"""
Result of a refinement loop execution.
Attributes:
iterations_run: Number of iterations executed
final_results: Validation results from the last iteration
halting_record: Record of the halting decision
all_results: Validation results from all iterations
run_ids: List of run IDs produced during refinement
"""
iterations_run: int
final_results: List[ValidationResult] = field(default_factory=list)
halting_record: Optional[HaltingRecord] = None
all_results: List[List[ValidationResult]] = field(default_factory=list)
run_ids: List[str] = field(default_factory=list)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary."""
return {
"iterations_run": self.iterations_run,
"final_results": [r.to_dict() for r in self.final_results],
"halting_record": self.halting_record.to_dict() if self.halting_record else None,
"all_results": [
[r.to_dict() for r in iteration]
for iteration in self.all_results
],
"run_ids": self.run_ids,
}