1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//! Cognitive signals — inner world → outer world communication.
//!
//! When the cognitive graph processes input, lobes may emit signals
//! that communicate to the outer execution graph. The outer graph
//! decides how to handle each signal — the library provides the
//! vocabulary, users define the policy.
use serde::{Deserialize, Serialize};
/// A signal emitted by the cognitive graph to the outer execution.
///
/// Signals are the inner world's way of influencing outer behavior
/// without directly modifying the outer graph's state. The outer
/// graph reads signals after cognitive processing completes and
/// decides how to respond.
///
/// # Example
///
/// ```
/// use pe_core::cognitive_signal::CognitiveSignal;
///
/// let signal = CognitiveSignal::ProceedWithCaution {
/// concern: "Low confidence in API response format".into(),
/// };
/// assert!(signal.is_cautionary());
/// ```
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[non_exhaustive]
pub enum CognitiveSignal {
/// "I'm confident, proceed normally."
Proceed,
/// "I need more information before I can answer."
NeedMoreContext { description: String },
/// "The current approach is wrong, try a different path."
ChangeStrategy { suggestion: String },
/// "I'm stuck, escalate to human."
Escalate { reason: String },
/// "I can answer but I'm not sure — flag for review."
ProceedWithCaution { concern: String },
/// "Diminishing returns. Stop iterating."
StopEarly { reason: String },
/// "Budget is low. Reduce cognitive depth."
SimplifyMode,
/// "I need a capability I don't have."
RequestCapability { capability: String },
/// "Block this output — safety or quality concern."
Veto { lobe: String, reason: String },
/// User-defined signal for domain-specific communication.
Custom {
name: String,
data: serde_json::Value,
},
}
impl CognitiveSignal {
/// Whether this signal blocks execution (veto, escalate).
pub fn is_blocking(&self) -> bool {
matches!(self, Self::Veto { .. } | Self::Escalate { .. })
}
/// Whether this signal suggests caution but doesn't block.
pub fn is_cautionary(&self) -> bool {
matches!(
self,
Self::ProceedWithCaution { .. }
| Self::NeedMoreContext { .. }
| Self::ChangeStrategy { .. }
)
}
/// Whether this signal suggests reducing work.
pub fn is_simplifying(&self) -> bool {
matches!(self, Self::SimplifyMode | Self::StopEarly { .. })
}
/// Human-readable summary for logging/debugging.
pub fn summary(&self) -> String {
match self {
Self::Proceed => "proceed".into(),
Self::NeedMoreContext { description } => format!("need context: {description}"),
Self::ChangeStrategy { suggestion } => format!("change strategy: {suggestion}"),
Self::Escalate { reason } => format!("escalate: {reason}"),
Self::ProceedWithCaution { concern } => format!("caution: {concern}"),
Self::StopEarly { reason } => format!("stop early: {reason}"),
Self::SimplifyMode => "simplify mode".into(),
Self::RequestCapability { capability } => format!("request: {capability}"),
Self::Veto { lobe, reason } => format!("VETO by {lobe}: {reason}"),
Self::Custom { name, .. } => format!("custom: {name}"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_blocking_signals() {
assert!(
CognitiveSignal::Veto {
lobe: "safety".into(),
reason: "dangerous".into()
}
.is_blocking()
);
assert!(
CognitiveSignal::Escalate {
reason: "stuck".into()
}
.is_blocking()
);
assert!(!CognitiveSignal::Proceed.is_blocking());
}
#[test]
fn test_cautionary_signals() {
assert!(
CognitiveSignal::ProceedWithCaution {
concern: "low confidence".into()
}
.is_cautionary()
);
assert!(!CognitiveSignal::Proceed.is_cautionary());
assert!(!CognitiveSignal::SimplifyMode.is_cautionary());
}
#[test]
fn test_simplifying_signals() {
assert!(CognitiveSignal::SimplifyMode.is_simplifying());
assert!(
CognitiveSignal::StopEarly {
reason: "done".into()
}
.is_simplifying()
);
assert!(!CognitiveSignal::Proceed.is_simplifying());
}
#[test]
fn test_serialization_roundtrip() {
let signals = vec![
CognitiveSignal::Proceed,
CognitiveSignal::Veto {
lobe: "critic".into(),
reason: "unsafe output".into(),
},
CognitiveSignal::Custom {
name: "domain_check".into(),
data: serde_json::json!({"score": 0.95}),
},
];
for signal in &signals {
let json = serde_json::to_string(signal).unwrap();
let back: CognitiveSignal = serde_json::from_str(&json).unwrap();
assert_eq!(&back, signal);
}
}
#[test]
fn test_summary_formatting() {
assert_eq!(CognitiveSignal::Proceed.summary(), "proceed");
let veto = CognitiveSignal::Veto {
lobe: "safety".into(),
reason: "PII detected".into(),
};
assert!(veto.summary().contains("VETO"));
assert!(veto.summary().contains("PII detected"));
}
}