feat(prompts): implement Phase 8 - Observability & Traceability (FR-11)
Complete implementation of Phase 8, the final phase of prompt dependency resolution infrastructure, adding full observability and traceability. ## Features (FR-11) ### FR-11.1: Complete Artifact Provenance Tracing - TraceabilityService: composition layer for full artifact lineage - Trace any artifact to producing PromptTemplate, input artifacts, generator runs, and quality validation results - ProvenanceTrace model with complete dependency chain reconstruction - RunSummary and ArtifactLineage models for structured trace output ### FR-11.2: Recomputation Query Infrastructure - PromptQueryService: cross-service complex queries - Run history queries with template and status filters - Stale artifact detection via impact debt analysis - Dependency graph statistics (nodes, edges, cycles, roots, leaves) - Content-based artifact lookups by digest ### Visualization Support - GraphExporter: DOT (Graphviz) and Mermaid format export - Supports all edge types (requires, generates, includes) - Handles isolated nodes, linear chains, diamonds, and complex graphs ### CLI Commands (prompt group) - `prompt trace <artifact_id>` - Full provenance trace as JSON - `prompt graph <artifact_id>` - Dependency graph (DOT/Mermaid) - `prompt runs` - List execution runs with filters - `prompt debt` - Show impact debt and stale artifacts - `prompt stats` - Dependency graph statistics ## Implementation Source files (8): - markitect/prompts/traceability/models.py - Trace data models - markitect/prompts/traceability/service.py - TraceabilityService - markitect/prompts/visualization/graph.py - Graph export - markitect/prompts/queries/operations.py - PromptQueryService - markitect/prompts/cli.py - Click CLI commands - Package __init__.py files (3) Tests (64 total, all passing): - tests/unit/prompts/test_traceability_service.py (21 tests) - tests/unit/prompts/test_visualization.py (14 tests) - tests/unit/prompts/test_query_operations.py (12 tests) - tests/integration/prompts/test_traceability_workflow.py (7 tests) - tests/integration/prompts/test_prompt_cli.py (10 tests) ## Architecture TraceabilityService is a composition layer that delegates to: - DependencyQueryService (transitive dependency lookups) - QualityValidator (validation history) - IncrementalExecutionEngine (impact debt queries) - Direct repository access (artifacts, edges) No duplicate data storage - all data comes from existing Phase 1-7 infrastructure (artifact repo, dependency repo, validation DB, debt DB). ## Verification All 2250 tests pass with 0 regressions. Phase 8 completes the full 8-phase implementation roadmap. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
116
markitect/prompts/traceability/models.py
Normal file
116
markitect/prompts/traceability/models.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""
|
||||
Traceability models for provenance tracking.
|
||||
|
||||
Implements FR-11.1: Trace artifacts to producing runs, inputs, and validation.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class RunSummary:
|
||||
"""Summary of a PromptRun for traceability output."""
|
||||
|
||||
run_id: str
|
||||
template_id: str
|
||||
status: str
|
||||
stage: str
|
||||
parent_run_id: Optional[str]
|
||||
depth: int
|
||||
input_bundle_hash: str
|
||||
started_at: datetime
|
||||
completed_at: Optional[datetime]
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
cls,
|
||||
run_id: str,
|
||||
template_id: str,
|
||||
status: str,
|
||||
stage: str,
|
||||
input_bundle_hash: str,
|
||||
started_at: datetime,
|
||||
parent_run_id: Optional[str] = None,
|
||||
depth: int = 0,
|
||||
completed_at: Optional[datetime] = None,
|
||||
) -> "RunSummary":
|
||||
"""Create a RunSummary."""
|
||||
return cls(
|
||||
run_id=run_id,
|
||||
template_id=template_id,
|
||||
status=status,
|
||||
stage=stage,
|
||||
parent_run_id=parent_run_id,
|
||||
depth=depth,
|
||||
input_bundle_hash=input_bundle_hash,
|
||||
started_at=started_at,
|
||||
completed_at=completed_at,
|
||||
)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert to dictionary."""
|
||||
return {
|
||||
"run_id": self.run_id,
|
||||
"template_id": self.template_id,
|
||||
"status": self.status,
|
||||
"stage": self.stage,
|
||||
"parent_run_id": self.parent_run_id,
|
||||
"depth": self.depth,
|
||||
"input_bundle_hash": self.input_bundle_hash,
|
||||
"started_at": self.started_at.isoformat(),
|
||||
"completed_at": self.completed_at.isoformat() if self.completed_at else None,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class ArtifactLineage:
|
||||
"""Lineage record for a single artifact in a trace."""
|
||||
|
||||
artifact_id: str
|
||||
name: str
|
||||
space_id: str
|
||||
artifact_type: str
|
||||
content_digest: str
|
||||
role: str # "input", "output", "template"
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert to dictionary."""
|
||||
return {
|
||||
"artifact_id": self.artifact_id,
|
||||
"name": self.name,
|
||||
"space_id": self.space_id,
|
||||
"artifact_type": self.artifact_type,
|
||||
"content_digest": self.content_digest,
|
||||
"role": self.role,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class ProvenanceTrace:
|
||||
"""Complete provenance trace for an artifact."""
|
||||
|
||||
artifact_id: str
|
||||
producing_run: Optional[RunSummary] = None
|
||||
template: Optional[ArtifactLineage] = None
|
||||
input_artifacts: List[ArtifactLineage] = field(default_factory=list)
|
||||
output_artifacts: List[ArtifactLineage] = field(default_factory=list)
|
||||
generator_runs: List[RunSummary] = field(default_factory=list)
|
||||
validation_results: List[Dict[str, Any]] = field(default_factory=list)
|
||||
impact_debt: List[Dict[str, Any]] = field(default_factory=list)
|
||||
dependency_chain: List[str] = field(default_factory=list)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert to dictionary."""
|
||||
return {
|
||||
"artifact_id": self.artifact_id,
|
||||
"producing_run": self.producing_run.to_dict() if self.producing_run else None,
|
||||
"template": self.template.to_dict() if self.template else None,
|
||||
"input_artifacts": [a.to_dict() for a in self.input_artifacts],
|
||||
"output_artifacts": [a.to_dict() for a in self.output_artifacts],
|
||||
"generator_runs": [r.to_dict() for r in self.generator_runs],
|
||||
"validation_results": self.validation_results,
|
||||
"impact_debt": self.impact_debt,
|
||||
"dependency_chain": self.dependency_chain,
|
||||
}
|
||||
Reference in New Issue
Block a user