feat(prompts): implement Phase 7 - Quality & Validation (FR-9, FR-10)
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
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
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>
This commit is contained in:
283
markitect/prompts/quality/models.py
Normal file
283
markitect/prompts/quality/models.py
Normal file
@@ -0,0 +1,283 @@
|
||||
"""
|
||||
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,
|
||||
}
|
||||
Reference in New Issue
Block a user