bctx_conductor/spiral/
orient.rs1use super::sense::HarvestedSignal;
2use crate::graph::SignalGraph;
3
4#[derive(Debug, Clone)]
5pub struct SituPicture {
6 pub active_signal_count: usize,
7 pub open_questions: Vec<OpenQuestion>,
8 pub has_recent_errors: bool,
9 pub vault_fact_count: usize,
10}
11
12#[derive(Debug, Clone)]
13pub struct OpenQuestion {
14 pub kind: QuestionKind,
15 pub detail: String,
16}
17
18#[derive(Debug, Clone, PartialEq)]
19pub enum QuestionKind {
20 InvestigateErrors,
21 RecallRelatedFacts,
22 DescribeProjectLayout,
23}
24
25pub fn contextualize(signals: &[HarvestedSignal], _graph: &SignalGraph) -> SituPicture {
26 let mut open_questions = Vec::new();
27 let mut has_errors = false;
28 let vault_facts = signals
29 .iter()
30 .filter(|s| s.source.starts_with("vault:"))
31 .count();
32
33 for sig in signals {
34 let content_lower = sig.content.to_lowercase();
35 if content_lower.contains("exit=1")
36 || content_lower.contains("exit=-1")
37 || content_lower.contains("error")
38 || content_lower.contains("failed")
39 {
40 has_errors = true;
41 }
42 }
43
44 if has_errors {
45 open_questions.push(OpenQuestion {
46 kind: QuestionKind::InvestigateErrors,
47 detail: "recent executions show errors or non-zero exits".into(),
48 });
49 open_questions.push(OpenQuestion {
51 kind: QuestionKind::RecallRelatedFacts,
52 detail: "error".into(),
53 });
54 }
55
56 if signals.is_empty() {
57 open_questions.push(OpenQuestion {
58 kind: QuestionKind::DescribeProjectLayout,
59 detail: "no signals yet — describe project layout".into(),
60 });
61 }
62
63 SituPicture {
64 active_signal_count: signals.len(),
65 open_questions,
66 has_recent_errors: has_errors,
67 vault_fact_count: vault_facts,
68 }
69}