""" Graph builder for bridging ephemeral manifest edges to persistent storage. Bridges RunManifest ephemeral DependencyEdge instances (3-field) to persistent DependencyEdge storage (6-field), and builds DependencyGraph from repository data. """ from typing import List, Optional, Set from markitect.prompts.dependencies.models import ( DependencyEdge, DependencyGraph, EdgeType, ) from markitect.prompts.dependencies.repository import IDependencyRepository from markitect.prompts.execution.manifest import ( DependencyEdge as ManifestDependencyEdge, RunManifest, ) class GraphBuilder: """ Bridges ephemeral manifest edges to persistent dependency storage. Extracts dependency edges from RunManifest instances, persists them via IDependencyRepository, and builds in-memory DependencyGraph instances for analysis. """ def __init__(self, repository: IDependencyRepository): """ Initialize with dependency repository. Args: repository: Repository for persisting dependency edges """ self._repository = repository def extract_edges(self, manifest: RunManifest) -> List[DependencyEdge]: """ Extract persistent DependencyEdge instances from a RunManifest. Converts the ephemeral 3-field DependencyEdge from the manifest into persistent 6-field DependencyEdge instances. Args: manifest: RunManifest containing ephemeral edges Returns: List of persistent DependencyEdge instances """ edges = [] for manifest_edge in manifest.dependency_edges: edge_type = EdgeType(manifest_edge.edge_type) edge = DependencyEdge.create( source_artifact_id=manifest_edge.source_id, target_artifact_id=manifest_edge.target_id, run_id=manifest.run_id, edge_type=edge_type, ) edges.append(edge) return edges def persist_edges(self, manifest: RunManifest) -> List[DependencyEdge]: """ Extract edges from manifest and persist them to the repository. Args: manifest: RunManifest containing ephemeral edges Returns: List of persisted DependencyEdge instances """ edges = self.extract_edges(manifest) persisted = [] for edge in edges: created = self._repository.create(edge) persisted.append(created) return persisted def build_graph( self, artifact_ids: Optional[Set[str]] = None, ) -> DependencyGraph: """ Build an in-memory DependencyGraph from repository data. Args: artifact_ids: Optional set of artifact IDs to scope the graph. If None, builds graph from all edges. Returns: DependencyGraph instance """ graph = DependencyGraph() if artifact_ids is not None: # Build scoped subgraph for artifact_id in artifact_ids: edges = self._repository.get_by_source(artifact_id) for edge in edges: if edge.target_artifact_id in artifact_ids: graph.add_edge( edge.source_artifact_id, edge.target_artifact_id, edge.edge_type, ) # Ensure node exists even if it has no edges if artifact_id not in graph._forward: graph._forward[artifact_id] = set() if artifact_id not in graph._reverse: graph._reverse[artifact_id] = set() else: # Build full graph all_edges = self._repository.get_all() for edge in all_edges: graph.add_edge( edge.source_artifact_id, edge.target_artifact_id, edge.edge_type, ) return graph def build_graph_for_run(self, run_id: str) -> DependencyGraph: """ Build a DependencyGraph from edges of a specific run. Args: run_id: Run identifier Returns: DependencyGraph for the run's edges """ graph = DependencyGraph() edges = self._repository.get_by_run(run_id) for edge in edges: graph.add_edge( edge.source_artifact_id, edge.target_artifact_id, edge.edge_type, ) return graph