-- Dependency DAG: feature-level nodes and edges within items. -- Migration: 014_dag_nodes_edges -- Date: 2026-02 BEGIN; -------------------------------------------------------------------------------- -- DAG Nodes (feature-level nodes within an item's revision) -------------------------------------------------------------------------------- CREATE TABLE dag_nodes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), item_id UUID NOT NULL REFERENCES items(id) ON DELETE CASCADE, revision_number INTEGER NOT NULL, node_key TEXT NOT NULL, node_type TEXT NOT NULL, properties_hash TEXT, validation_state TEXT NOT NULL DEFAULT 'clean', validation_msg TEXT, metadata JSONB DEFAULT '{}', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE(item_id, revision_number, node_key) ); CREATE INDEX idx_dag_nodes_item ON dag_nodes(item_id); CREATE INDEX idx_dag_nodes_item_rev ON dag_nodes(item_id, revision_number); CREATE INDEX idx_dag_nodes_state ON dag_nodes(validation_state) WHERE validation_state != 'clean'; CREATE INDEX idx_dag_nodes_type ON dag_nodes(node_type); -------------------------------------------------------------------------------- -- DAG Edges (dependencies between nodes within a single item) -- Direction: source → target means "target depends on source" -------------------------------------------------------------------------------- CREATE TABLE dag_edges ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), source_node_id UUID NOT NULL REFERENCES dag_nodes(id) ON DELETE CASCADE, target_node_id UUID NOT NULL REFERENCES dag_nodes(id) ON DELETE CASCADE, edge_type TEXT NOT NULL DEFAULT 'depends_on', metadata JSONB DEFAULT '{}', UNIQUE(source_node_id, target_node_id, edge_type), CONSTRAINT no_self_edge CHECK (source_node_id != target_node_id) ); CREATE INDEX idx_dag_edges_source ON dag_edges(source_node_id); CREATE INDEX idx_dag_edges_target ON dag_edges(target_node_id); -------------------------------------------------------------------------------- -- Cross-item DAG edges (linking feature nodes across BOM boundaries) -------------------------------------------------------------------------------- CREATE TABLE dag_cross_edges ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), source_node_id UUID NOT NULL REFERENCES dag_nodes(id) ON DELETE CASCADE, target_node_id UUID NOT NULL REFERENCES dag_nodes(id) ON DELETE CASCADE, relationship_id UUID REFERENCES relationships(id) ON DELETE SET NULL, edge_type TEXT NOT NULL DEFAULT 'assembly_ref', metadata JSONB DEFAULT '{}', UNIQUE(source_node_id, target_node_id) ); CREATE INDEX idx_dag_cross_source ON dag_cross_edges(source_node_id); CREATE INDEX idx_dag_cross_target ON dag_cross_edges(target_node_id); COMMIT;