sdd-layer 0.24.0

Spec-Driven Development CLI and agent harness
use anyhow::Result;
use std::path::{Path, PathBuf};

/// Read-only, ranked context shared by CLI, TUI, MCP and ACP. The artifact
/// store remains canonical; this snapshot is derived for one consumer turn.
pub(crate) struct ContextSnapshot {
    pub(crate) path: PathBuf,
    pub(crate) content: String,
    pub(crate) sources_included: usize,
    pub(crate) conflicts: usize,
    pub(crate) used_chars: usize,
    pub(crate) stale: Vec<String>,
    pub(crate) decision_conflicts: Vec<String>,
    pub(crate) handoff: crate::runtime::optimization::ContextHandoff,
}

pub(crate) struct ContextService<'a> {
    root: &'a Path,
}

impl<'a> ContextService<'a> {
    pub(crate) fn new(root: &'a Path) -> Self {
        Self { root }
    }

    pub(crate) fn build(
        &self,
        orchestration: &str,
        stage: &str,
        task: Option<&str>,
        persist_status: bool,
    ) -> Result<ContextSnapshot> {
        let pack =
            crate::build_context_pack(self.root, orchestration, stage, task, persist_status)?;
        Ok(ContextSnapshot {
            path: pack.path,
            content: pack.content,
            sources_included: pack.sources_included,
            conflicts: pack.conflicts,
            used_chars: pack.used_chars,
            stale: pack.stale,
            decision_conflicts: pack.decision_conflicts,
            handoff: pack.handoff,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::ContextService;
    use std::fs;
    use tempfile::tempdir;

    #[test]
    fn context_service_is_the_ranked_source_for_all_consumers() {
        let root = tempdir().unwrap();
        let store = root.path().join("docs/flow");
        fs::create_dir_all(&store).unwrap();
        fs::write(
            store.join("traceability-map.yaml"),
            "orchestration:\n  name: Flow\n  slug: flow\nartifacts:\n  prd:\n    file: 02-prd.md\n    state: approved\nrelated_orchestrations: []\n",
        )
        .unwrap();
        fs::write(store.join("02-prd.md"), "# PRD\n\nContexto canĂ´nico.\n").unwrap();

        let snapshot = ContextService::new(root.path())
            .build("Flow", "execution", None, false)
            .unwrap();

        assert!(snapshot.content.contains("Contexto canĂ´nico"));
        assert_eq!(snapshot.conflicts, 0);
        assert!(snapshot.sources_included >= 2);
    }
}