bctx-conductor 0.1.28

bctx-conductor — Spiral Cycle agent runtime, SignalGraph, PassageRun
Documentation
use super::sense::HarvestedSignal;
use crate::graph::SignalGraph;

#[derive(Debug, Clone)]
pub struct SituPicture {
    pub active_signal_count: usize,
    pub open_questions: Vec<OpenQuestion>,
    pub has_recent_errors: bool,
    pub vault_fact_count: usize,
}

#[derive(Debug, Clone)]
pub struct OpenQuestion {
    pub kind: QuestionKind,
    pub detail: String,
}

#[derive(Debug, Clone, PartialEq)]
pub enum QuestionKind {
    InvestigateErrors,
    RecallRelatedFacts,
    DescribeProjectLayout,
}

pub fn contextualize(signals: &[HarvestedSignal], _graph: &SignalGraph) -> SituPicture {
    let mut open_questions = Vec::new();
    let mut has_errors = false;
    let vault_facts = signals
        .iter()
        .filter(|s| s.source.starts_with("vault:"))
        .count();

    for sig in signals {
        let content_lower = sig.content.to_lowercase();
        if content_lower.contains("exit=1")
            || content_lower.contains("exit=-1")
            || content_lower.contains("error")
            || content_lower.contains("failed")
        {
            has_errors = true;
        }
    }

    if has_errors {
        open_questions.push(OpenQuestion {
            kind: QuestionKind::InvestigateErrors,
            detail: "recent executions show errors or non-zero exits".into(),
        });
        // Recall any vault facts that might explain the errors
        open_questions.push(OpenQuestion {
            kind: QuestionKind::RecallRelatedFacts,
            detail: "error".into(),
        });
    }

    if signals.is_empty() {
        open_questions.push(OpenQuestion {
            kind: QuestionKind::DescribeProjectLayout,
            detail: "no signals yet — describe project layout".into(),
        });
    }

    SituPicture {
        active_signal_count: signals.len(),
        open_questions,
        has_recent_errors: has_errors,
        vault_fact_count: vault_facts,
    }
}