""" Pattern validation quality gate. Validates content against required and forbidden regex patterns. """ import re import uuid from typing import List, Optional from markitect.prompts.quality.models import ( GateType, QualityGate, ValidationDiagnostic, ValidationResult, ValidationStatus, ) class PatternValidationGate(QualityGate): """ Validates artifact content against regex patterns. Checks that all required patterns are present and no forbidden patterns are found. """ def __init__( self, required_patterns: Optional[List[str]] = None, forbidden_patterns: Optional[List[str]] = None, gate_id: Optional[str] = None, name: str = "pattern", ): """ Initialize with pattern lists. Args: required_patterns: Regex patterns that must be found in content forbidden_patterns: Regex patterns that must NOT be found in content gate_id: Optional gate identifier name: Human-readable gate name """ super().__init__( gate_id=gate_id or str(uuid.uuid4()), name=name, gate_type=GateType.PATTERN, ) self.required_patterns = required_patterns or [] self.forbidden_patterns = forbidden_patterns or [] def validate(self, content: str, artifact_id: str) -> ValidationResult: """ Validate content against required and forbidden patterns. Args: content: Content string to validate artifact_id: ID of the artifact being validated Returns: ValidationResult with status and diagnostics """ diagnostics = [] total_checks = len(self.required_patterns) + len(self.forbidden_patterns) failures = 0 # Check required patterns for pattern in self.required_patterns: if not re.search(pattern, content): diagnostics.append( ValidationDiagnostic( code="MISSING_PATTERN", message=f"Required pattern not found: {pattern}", severity="error", ) ) failures += 1 # Check forbidden patterns for pattern in self.forbidden_patterns: match = re.search(pattern, content) if match: diagnostics.append( ValidationDiagnostic( code="FORBIDDEN_PATTERN", message=f"Forbidden pattern found: {pattern} (matched: '{match.group()}')", severity="error", ) ) failures += 1 if total_checks == 0: status = ValidationStatus.PASS score = 1.0 elif failures == 0: status = ValidationStatus.PASS score = 1.0 else: status = ValidationStatus.FAIL score = max(0.0, 1.0 - failures / total_checks) return ValidationResult.create( gate_id=self.id, gate_type=self.gate_type, artifact_id=artifact_id, status=status, score=score, diagnostics=diagnostics, )