Files
markitect-main/markitect/prompts/traceability/models.py
tegwick 7b4bd461c9 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>
2026-02-09 20:32:18 +01:00

117 lines
3.7 KiB
Python

"""
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,
}