agileplus-domain 0.3.0

Typed intent graphs for engineering work — convert prompts into validated DAGs of intents, features, tasks, tests, and edges, then store, query, and serve them from Rust, HTTP, or MCP.
Documentation
//! Top-level `IntentGraphBuilder` — composes nodes, edges, and metadata into
//! a full `IntentGraph`. Other builders (`NodeBuilder`, `EdgeBuilder`,
//! `MetaBuilder`, `CanonicalMapBuilder`, `GraphMetadataBuilder`) live in
//! sibling submodules of this `builder` module and are re-exported here for
//! backward compatibility.

use std::collections::HashMap;

use crate::intent_graph::{GraphMetadata, IntentGraph, Node, NodeId};

mod canonical_map;
mod edge;
pub mod extended;
mod meta;
mod metadata;
mod node;

pub use canonical_map::CanonicalMapBuilder;
pub use edge::EdgeBuilder;
pub use extended::{BugBuilder, ExtendedNodeBuilder, HypothesisBuilder, MetricBuilder, StoryBuilder};
pub use meta::MetaBuilder;
pub use metadata::GraphMetadataBuilder;
pub use node::NodeBuilder;

/// Builder for constructing an `IntentGraph` incrementally.
#[derive(Debug, Default)]
pub struct IntentGraphBuilder {
    metadata: Option<GraphMetadata>,
    nodes: Vec<Node>,
    edges: Vec<crate::intent_graph::Edge>,
    node_index: HashMap<NodeId, usize>,
}

impl IntentGraphBuilder {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn metadata(mut self, metadata: GraphMetadata) -> Self {
        self.metadata = Some(metadata);
        self
    }

    pub fn with_default_metadata(mut self) -> Self {
        let now = chrono::Utc::now().to_rfc3339();
        self.metadata = Some(GraphMetadata {
            version: "1.0.0".to_string(),
            schema_uri: "https://phenotype.dev/schemas/agileplus-intent-ontology/v1.json"
                .to_string(),
            created_at: now.clone(),
            updated_at: Some(now),
            node_count: None,
            edge_count: None,
            dag_valid: None,
            source_system: Some("agileplus-rust".to_string()),
        });
        self
    }

    pub fn node(mut self, node: Node) -> Self {
        self.node_index.insert(node.id.clone(), self.nodes.len());
        self.nodes.push(node);
        self
    }

    pub fn edge(mut self, edge: crate::intent_graph::Edge) -> Self {
        self.edges.push(edge);
        self
    }

    pub fn build(self) -> Result<IntentGraph, String> {
        let mut graph = IntentGraph::new(
            self.metadata
                .ok_or_else(|| "GraphMetadata is required".to_string())?,
        );
        graph.nodes = self.nodes;
        graph.edges = self.edges;
        graph.update_counts();
        Ok(graph)
    }
}