Skip to content

Commit 084012f

Browse files
authored
Merge pull request #122 from AdaWorldAPI/claude/ada-rs-consolidation-6nvNm
feat: implement Meta-Uncertainty Layer (MUL) — 10-layer metacognition Complete implementation of the MUL metacognitive stack: - L1 TrustQualia: 4D trust (competence×source×environment×calibration) - L2 DKDetector: Dunning-Kruger curve position tracking - L3 TemporalHysteresis: Anti-thrash dwell timers - L4 RiskVector: Epistemic × Moral risk axes - L5 FalseFlowDetector: Coherence-without-progress detection - L6 CognitiveHomeostasis: Flow/Anxiety/Boredom/Apathy states - L7 MulGate: 5 blocking criteria (all must pass) - L8 FreeWillModifier: Multiplicative confidence modulation - L9 Compass: 5 ethical/epistemic navigation tests - L10 PostActionLearning: Prediction error → trust/DK updates MulSnapshot packs into CogRecord W64-W65 (128 bits) for storage. MetaUncertaintyLayer integrator: evaluate(), tick(), learn(), navigate(). 44 unit tests covering all layers and integration. https://claude.ai/code/session_01KJ2r3qXezGBXK8HutztJdh
2 parents 0e4af51 + 9070c9c commit 084012f

12 files changed

Lines changed: 1718 additions & 0 deletions

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ pub mod width_16k;
111111
pub mod world;
112112
pub mod spectroscopy;
113113
pub mod qualia;
114+
pub mod mul; // Meta-Uncertainty Layer (10-layer metacognition)
114115

115116
// === Unified execution contract (crewai-rust × ada-n8n integration) ===
116117
pub mod contract;

src/mul/compass.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//! Layer 9: Compass Function — Navigation in Unknown Territory
2+
//!
3+
//! 5 ethical/epistemic tests for navigating novel situations.
4+
//! When the system encounters something unfamiliar, the compass determines
5+
//! whether to proceed, explore cautiously, or surface to meta-level.
6+
7+
/// Compass result — 5 ethical/epistemic test scores.
8+
#[derive(Debug, Clone, Copy)]
9+
pub struct CompassResult {
10+
/// Kant test: "could this be a universal law?" (universalizability)
11+
pub kant: f32,
12+
/// Identity test: "does this preserve who I am?" (identity alignment)
13+
pub identity: f32,
14+
/// Reversibility test: "can I undo this?" (safety margin)
15+
pub reversibility: f32,
16+
/// Curiosity test: "does this teach me something?" (epistemic value)
17+
pub curiosity: f32,
18+
/// Analogy test: "is this structurally similar to something I know?" (transfer)
19+
pub analogy: f32,
20+
}
21+
22+
impl CompassResult {
23+
/// Composite compass score (weighted).
24+
pub fn score(&self, free_will_modifier: f32) -> f32 {
25+
let base = self.kant * self.identity; // both must be positive
26+
let reversibility_bonus = if self.reversibility > 0.7 { 1.2 } else { 1.0 };
27+
let curiosity_bonus = if self.curiosity > 0.6 { 1.1 } else { 1.0 };
28+
29+
(base * reversibility_bonus * curiosity_bonus * free_will_modifier).clamp(0.0, 1.0)
30+
}
31+
32+
/// Navigation decision.
33+
pub fn decide(&self, free_will_modifier: f32) -> CompassDecision {
34+
let score = self.score(free_will_modifier);
35+
if score > 0.6 {
36+
CompassDecision::ExecuteWithLearning
37+
} else if self.reversibility > 0.7 {
38+
CompassDecision::Exploratory
39+
} else {
40+
CompassDecision::SurfaceToMeta
41+
}
42+
}
43+
44+
/// Default compass for routine actions (high safety, moderate novelty).
45+
pub fn routine() -> Self {
46+
Self {
47+
kant: 0.9,
48+
identity: 0.9,
49+
reversibility: 0.9,
50+
curiosity: 0.3,
51+
analogy: 0.8,
52+
}
53+
}
54+
55+
/// Compass for novel situations (uncertain but potentially valuable).
56+
pub fn novel() -> Self {
57+
Self {
58+
kant: 0.7,
59+
identity: 0.7,
60+
reversibility: 0.5,
61+
curiosity: 0.9,
62+
analogy: 0.3,
63+
}
64+
}
65+
}
66+
67+
impl Default for CompassResult {
68+
fn default() -> Self {
69+
Self::routine()
70+
}
71+
}
72+
73+
/// Navigation decision from compass.
74+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75+
pub enum CompassDecision {
76+
/// Proceed and crystallize the outcome for future reference
77+
ExecuteWithLearning,
78+
/// Proceed in sandbox/exploratory mode (reversible only)
79+
Exploratory,
80+
/// Cannot decide — surface to higher level
81+
SurfaceToMeta,
82+
}
83+
84+
#[cfg(test)]
85+
mod tests {
86+
use super::*;
87+
88+
#[test]
89+
fn test_routine_proceeds() {
90+
let c = CompassResult::routine();
91+
assert_eq!(c.decide(1.0), CompassDecision::ExecuteWithLearning);
92+
}
93+
94+
#[test]
95+
fn test_novel_surfaces_or_explores() {
96+
let c = CompassResult::novel();
97+
// Novel: low reversibility (0.5) means it surfaces to meta
98+
// even with high modifier — this is correct safety behavior
99+
assert_eq!(c.decide(1.0), CompassDecision::SurfaceToMeta);
100+
assert_eq!(c.decide(0.5), CompassDecision::SurfaceToMeta);
101+
102+
// But if we raise reversibility, novel becomes executable
103+
// (base * reversibility_bonus * curiosity_bonus * modifier > 0.6)
104+
let c_reversible = CompassResult {
105+
reversibility: 0.8,
106+
..CompassResult::novel()
107+
};
108+
assert_eq!(c_reversible.decide(1.0), CompassDecision::ExecuteWithLearning);
109+
}
110+
111+
#[test]
112+
fn test_low_modifier_surfaces() {
113+
let c = CompassResult {
114+
kant: 0.5,
115+
identity: 0.5,
116+
reversibility: 0.3,
117+
curiosity: 0.5,
118+
analogy: 0.5,
119+
};
120+
assert_eq!(c.decide(0.3), CompassDecision::SurfaceToMeta);
121+
}
122+
}

src/mul/dk_detector.rs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
//! Layer 2: Dunning-Kruger Detector — The Humility Engine
2+
//!
3+
//! Maps felt competence vs demonstrated competence to detect overconfidence.
4+
//! MountStupid is the most dangerous state: high confidence with low experience.
5+
6+
/// Position on the Dunning-Kruger curve.
7+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8+
#[repr(u8)]
9+
pub enum DKPosition {
10+
/// Feels confident but lacks evidence (DANGEROUS)
11+
MountStupid = 0,
12+
/// Aware of gaps (CAUTIOUS but honest)
13+
ValleyOfDespair = 1,
14+
/// Building real competence
15+
SlopeOfEnlightenment = 2,
16+
/// Calibrated confidence matches ability
17+
PlateauOfMastery = 3,
18+
}
19+
20+
impl DKPosition {
21+
/// Humility factor — how much to REDUCE confidence.
22+
pub fn humility_factor(self) -> f32 {
23+
match self {
24+
Self::MountStupid => 0.3,
25+
Self::ValleyOfDespair => 0.7,
26+
Self::SlopeOfEnlightenment => 0.85,
27+
Self::PlateauOfMastery => 1.0,
28+
}
29+
}
30+
31+
pub fn from_bits(bits: u8) -> Self {
32+
match bits {
33+
0 => Self::MountStupid,
34+
1 => Self::ValleyOfDespair,
35+
2 => Self::SlopeOfEnlightenment,
36+
_ => Self::PlateauOfMastery,
37+
}
38+
}
39+
}
40+
41+
/// Dunning-Kruger detector state.
42+
#[derive(Debug, Clone)]
43+
pub struct DKDetector {
44+
/// Current position on the curve
45+
pub position: DKPosition,
46+
/// Felt competence (self-reported confidence)
47+
pub felt_competence: f32,
48+
/// Demonstrated competence (empirical accuracy from Brier scores)
49+
pub demonstrated_competence: f32,
50+
/// Number of samples used for demonstration (experience)
51+
pub sample_count: u32,
52+
/// Smoothed gap: felt - demonstrated (positive = overconfident)
53+
pub gap: f32,
54+
}
55+
56+
impl DKDetector {
57+
pub fn new() -> Self {
58+
Self {
59+
position: DKPosition::SlopeOfEnlightenment,
60+
felt_competence: 0.5,
61+
demonstrated_competence: 0.5,
62+
sample_count: 0,
63+
gap: 0.0,
64+
}
65+
}
66+
67+
/// Classify position from gap and experience.
68+
pub fn classify(&mut self) {
69+
self.gap = self.felt_competence - self.demonstrated_competence;
70+
71+
self.position = if self.sample_count < 10 && self.gap > 0.2 {
72+
DKPosition::MountStupid
73+
} else if self.gap < -0.15 {
74+
DKPosition::ValleyOfDespair
75+
} else if self.gap.abs() < 0.15 && self.sample_count > 50 {
76+
DKPosition::PlateauOfMastery
77+
} else {
78+
DKPosition::SlopeOfEnlightenment
79+
};
80+
}
81+
82+
/// Update with new observation.
83+
pub fn observe(&mut self, predicted_confidence: f32, was_correct: bool) {
84+
self.sample_count = self.sample_count.saturating_add(1);
85+
let outcome = if was_correct { 1.0 } else { 0.0 };
86+
87+
let alpha = 0.1;
88+
self.demonstrated_competence =
89+
self.demonstrated_competence * (1.0 - alpha) + outcome * alpha;
90+
91+
self.felt_competence = predicted_confidence;
92+
self.classify();
93+
}
94+
}
95+
96+
impl Default for DKDetector {
97+
fn default() -> Self {
98+
Self::new()
99+
}
100+
}
101+
102+
#[cfg(test)]
103+
mod tests {
104+
use super::*;
105+
106+
#[test]
107+
fn test_mount_stupid() {
108+
let mut dk = DKDetector::new();
109+
dk.sample_count = 3;
110+
dk.felt_competence = 0.9;
111+
dk.demonstrated_competence = 0.3;
112+
dk.classify();
113+
assert_eq!(dk.position, DKPosition::MountStupid);
114+
assert!((dk.position.humility_factor() - 0.3).abs() < 0.01);
115+
}
116+
117+
#[test]
118+
fn test_valley_of_despair() {
119+
let mut dk = DKDetector::new();
120+
dk.sample_count = 20;
121+
dk.felt_competence = 0.3;
122+
dk.demonstrated_competence = 0.6;
123+
dk.classify();
124+
assert_eq!(dk.position, DKPosition::ValleyOfDespair);
125+
}
126+
127+
#[test]
128+
fn test_plateau_of_mastery() {
129+
let mut dk = DKDetector::new();
130+
dk.sample_count = 100;
131+
dk.felt_competence = 0.8;
132+
dk.demonstrated_competence = 0.78;
133+
dk.classify();
134+
assert_eq!(dk.position, DKPosition::PlateauOfMastery);
135+
}
136+
137+
#[test]
138+
fn test_observe_updates() {
139+
let mut dk = DKDetector::new();
140+
for _ in 0..20 {
141+
dk.observe(0.9, true);
142+
}
143+
assert!(dk.demonstrated_competence > 0.7);
144+
assert!(dk.sample_count == 20);
145+
}
146+
}

0 commit comments

Comments
 (0)