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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
//! alignment_loop.rs - Drift Detection and Correction-Vector Computation.
//!
//! Continuously evaluates the active output generation against the predicted
//! state map established by the state predictor. If the output logic deviates
//! from the projected semantic milestone, this computes a correction vector
//! that *could* steer generation back on track.
//!
//! ## Current status: detection is wired, application is not
//!
//! `check_alignment` computes `correction_strength` and (via
//! `compute_correction`) a full correction vector, but **nothing in this
//! module writes that vector back into the TTT fast-weight matrix W̃.**
//! Callers receive the suggested correction as data; applying it to a live
//! session's W̃ (and validating that doing so actually improves generation
//! rather than destabilizing it) is unimplemented follow-up work, not a
//! documentation gap.
//!
//! ## Architecture
//!
//! The alignment loop operates in three stages:
//!
//! 1. **Monitoring** - For each generated token/chunk, extract the current
//! generation state and compare it against the expected milestone in the
//! `SemanticStateMap`.
//!
//! 2. **Drift detection** - Compute a drift score between the current generation
//! state and the predicted milestone state. If the drift exceeds a threshold,
//! flag a misalignment.
//!
//! 3. **Correction (vector only)** - Compute a localized TTT fast-weight
//! update vector that *would* nudge W̃ toward the predicted milestone
//! state. This is returned to the caller; it is not applied here.
//!
//! ## Key difference from existing grounding
//!
//! The existing `hallucination.rs` grounding check is *post-hoc* (verify after
//! generation). The alignment loop is *continuous* (monitor during generation)
//! and computes correction data mid-stream. It does not replace grounding —
//! it complements it by catching drift early — but unlike grounding, its
//! correction output is not yet actionable without further wiring.
use serde::{Deserialize, Serialize};
use crate::belief::BetaBelief;
use crate::state_predictor::{SemanticStateMap, SemanticMilestone};
/// Default drift threshold: if the drift score exceeds this, a correction is applied.
pub const DEFAULT_DRIFT_THRESHOLD: f32 = 0.5;
/// Default correction strength: the learning rate for localized W̃ updates.
pub const DEFAULT_CORRECTION_LR: f32 = 0.01;
/// Maximum number of corrections per generation session.
pub const MAX_CORRECTIONS: usize = 16;
/// A single alignment check - the result of comparing generation state to the
/// predicted milestone.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlignmentCheck {
/// The milestone index that was being compared against.
pub milestone_idx: usize,
/// The milestone label.
pub milestone_label: String,
/// Drift score (0 = perfectly aligned, 1 = completely misaligned).
pub drift_score: f32,
/// Whether a correction was applied.
pub corrected: bool,
/// The correction strength applied (0 if no correction).
pub correction_strength: f32,
/// Confidence in the alignment assessment.
pub confidence: BetaBelief,
}
/// The alignment loop's state - tracks corrections across a generation session.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlignmentLoopState {
/// The state map being aligned against.
pub state_map: SemanticStateMap,
/// All alignment checks performed so far.
pub checks: Vec<AlignmentCheck>,
/// Current milestone index (how far through the state map we are).
pub current_milestone: usize,
/// Total corrections applied.
pub corrections_applied: usize,
/// Drift threshold.
pub drift_threshold: f32,
/// Correction learning rate.
pub correction_lr: f32,
/// Whether the loop has reached the final milestone.
pub completed: bool,
}
/// The alignment loop - monitors and corrects generation drift.
pub struct AlignmentLoop {
drift_threshold: f32,
correction_lr: f32,
max_corrections: usize,
}
impl AlignmentLoop {
/// Create a new alignment loop with the given configuration.
pub fn new(
drift_threshold: f32,
correction_lr: f32,
max_corrections: usize,
) -> Self {
Self {
drift_threshold,
correction_lr,
max_corrections,
}
}
/// Create an alignment loop with default settings.
pub fn default_config() -> Self {
Self::new(
DEFAULT_DRIFT_THRESHOLD,
DEFAULT_CORRECTION_LR,
MAX_CORRECTIONS,
)
}
/// Initialize the alignment loop state for a generation session.
pub fn init(&self, state_map: SemanticStateMap) -> AlignmentLoopState {
AlignmentLoopState {
state_map,
checks: Vec::new(),
current_milestone: 0,
corrections_applied: 0,
drift_threshold: self.drift_threshold,
correction_lr: self.correction_lr,
completed: false,
}
}
/// Check alignment between the current generation state and the expected
/// milestone. If drift exceeds the threshold, record a correction.
///
/// `generation_state` is the current hidden state of the generation (as a
/// flat vector). `predicted_state` is the expected state from the milestone.
pub fn check_alignment(
&self,
state: &mut AlignmentLoopState,
generation_state: &[f32],
) -> AlignmentCheck {
// Get the current milestone (or the last one if we've passed them all).
let milestone_idx = state.current_milestone.min(
state.state_map.milestones.len().saturating_sub(1),
);
let milestone = match state.state_map.milestones.get(milestone_idx) {
Some(m) => m,
None => {
return AlignmentCheck {
milestone_idx,
milestone_label: "none".to_string(),
drift_score: 0.0,
corrected: false,
correction_strength: 0.0,
confidence: BetaBelief::uniform(),
};
}
};
// Compute drift score: cosine distance between generation and predicted states.
let drift_score = cosine_distance(generation_state, &milestone.predicted_state);
// Determine if a correction is needed.
let needs_correction = drift_score > state.drift_threshold
&& state.corrections_applied < self.max_corrections;
let correction_strength = if needs_correction {
// Scale correction by drift magnitude (more drift = stronger correction).
let strength = state.correction_lr * (drift_score - state.drift_threshold).min(1.0);
state.corrections_applied += 1;
strength
} else {
0.0
};
// Confidence: higher when drift is low (aligned) or when correction was applied.
let confidence = BetaBelief::from_confidence(1.0 - drift_score, 4.0);
let check = AlignmentCheck {
milestone_idx,
milestone_label: milestone.label.clone(),
drift_score,
corrected: needs_correction,
correction_strength,
confidence,
};
state.checks.push(check.clone());
// Advance to the next milestone if alignment is good.
if drift_score < state.drift_threshold {
state.current_milestone += 1;
if state.current_milestone >= state.state_map.milestones.len() {
state.completed = true;
}
}
check
}
/// Compute the localized fast-weight correction vector.
///
/// This returns a correction vector that can be applied to the TTT fast-weight
/// matrix W̃ to steer generation toward the predicted milestone state.
/// The correction is: `correction = lr * (predicted_state - generation_state)`
pub fn compute_correction(
&self,
generation_state: &[f32],
milestone: &SemanticMilestone,
lr: f32,
) -> Vec<f32> {
let n = generation_state.len().min(milestone.predicted_state.len());
let mut correction = vec![0.0f32; n];
for i in 0..n {
correction[i] = lr * (milestone.predicted_state[i] - generation_state[i]);
}
correction
}
/// Get a summary of the alignment loop's state.
pub fn summary(state: &AlignmentLoopState) -> String {
let total_checks = state.checks.len();
let corrections = state.corrections_applied;
let avg_drift: f32 = if total_checks > 0 {
state.checks.iter().map(|c| c.drift_score).sum::<f32>() / total_checks as f32
} else {
0.0
};
let aligned = state.checks.iter().filter(|c| !c.corrected).count();
format!(
"AlignmentLoop: {} checks, {} corrections, avg_drift={:.3}, aligned={}/{}, milestone={}/{}, completed={}",
total_checks,
corrections,
avg_drift,
aligned,
total_checks,
state.current_milestone,
state.state_map.milestones.len(),
state.completed
)
}
}
/// Cosine distance between two vectors (1 - cosine_similarity).
/// Returns 0 for identical directions, 1 for orthogonal, 2 for opposite.
fn cosine_distance(a: &[f32], b: &[f32]) -> f32 {
let n = a.len().min(b.len());
if n == 0 {
return 0.0;
}
let mut dot = 0.0f32;
let mut norm_a = 0.0f32;
let mut norm_b = 0.0f32;
for i in 0..n {
dot += a[i] * b[i];
norm_a += a[i] * a[i];
norm_b += b[i] * b[i];
}
let denom = (norm_a * norm_b).sqrt().max(1e-6);
1.0 - (dot / denom)
}
/// Render an alignment check as a human-readable string.
pub fn render_alignment_check(check: &AlignmentCheck) -> String {
let status = if check.corrected { "CORRECTED" } else { "ALIGNED" };
format!(
"AlignmentCheck [{}] {} drift={:.3} correction={:.4} confidence={:.2}",
check.milestone_label,
status,
check.drift_score,
check.correction_strength,
check.confidence.mean()
)
}
#[cfg(test)]
mod tests {
use super::*;
fn make_state_map() -> SemanticStateMap {
SemanticStateMap {
milestones: vec![
SemanticMilestone {
label: "hypothesis".to_string(),
label_idx: 0,
predicted_state: vec![1.0, 0.0, 0.0, 0.0, 0.5, 0.3, 0.1, 0.0],
token_budget: 128,
branch_hints: vec![],
confidence: BetaBelief::from_confidence(0.8, 4.0),
},
SemanticMilestone {
label: "execution".to_string(),
label_idx: 2,
predicted_state: vec![0.0, 1.0, 0.0, 0.5, 0.0, 0.2, 0.0, 0.1],
token_budget: 256,
branch_hints: vec![],
confidence: BetaBelief::from_confidence(0.7, 4.0),
},
],
confidence: BetaBelief::from_confidence(0.75, 4.0),
session_id: "test".to_string(),
context_state: vec![0.5; 8],
}
}
#[test]
fn init_creates_valid_state() {
let loop_ = AlignmentLoop::default_config();
let map = make_state_map();
let state = loop_.init(map);
assert_eq!(state.current_milestone, 0);
assert_eq!(state.corrections_applied, 0);
assert!(!state.completed);
}
#[test]
fn aligned_generation_advances_milestone() {
let loop_ = AlignmentLoop::default_config();
let map = make_state_map();
let mut state = loop_.init(map);
// Generation state matches the first milestone exactly.
let gen_state = vec![1.0, 0.0, 0.0, 0.0, 0.5, 0.3, 0.1, 0.0];
let check = loop_.check_alignment(&mut state, &gen_state);
assert!(!check.corrected, "aligned state should not need correction");
assert!(check.drift_score < 0.01, "identical states should have ~0 drift");
assert_eq!(state.current_milestone, 1, "should advance to next milestone");
}
#[test]
fn drifted_generation_triggers_correction() {
let loop_ = AlignmentLoop::default_config();
let map = make_state_map();
let mut state = loop_.init(map);
// Generation state is very different from the first milestone.
let gen_state = vec![0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0];
let check = loop_.check_alignment(&mut state, &gen_state);
assert!(check.corrected, "drifted state should trigger correction");
assert!(check.drift_score > 0.5, "orthogonal states should have high drift");
assert_eq!(state.corrections_applied, 1);
}
#[test]
fn max_corrections_is_respected() {
let loop_ = AlignmentLoop::new(0.1, 0.01, 2); // max 2 corrections
let map = make_state_map();
let mut state = loop_.init(map);
// Keep sending drifted state.
let gen_state = vec![0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0];
for _ in 0..10 {
loop_.check_alignment(&mut state, &gen_state);
}
assert!(state.corrections_applied <= 2, "should not exceed max corrections");
}
#[test]
fn completion_is_detected() {
let loop_ = AlignmentLoop::default_config();
let map = make_state_map();
let mut state = loop_.init(map);
// Align with first milestone.
let gen1 = vec![1.0, 0.0, 0.0, 0.0, 0.5, 0.3, 0.1, 0.0];
loop_.check_alignment(&mut state, &gen1);
// Align with second milestone.
let gen2 = vec![0.0, 1.0, 0.0, 0.5, 0.0, 0.2, 0.0, 0.1];
loop_.check_alignment(&mut state, &gen2);
assert!(state.completed, "should complete after aligning with all milestones");
}
#[test]
fn correction_vector_steers_toward_milestone() {
let loop_ = AlignmentLoop::default_config();
let milestone = SemanticMilestone {
label: "test".to_string(),
label_idx: 0,
predicted_state: vec![1.0, 0.0, 0.0],
token_budget: 100,
branch_hints: vec![],
confidence: BetaBelief::from_confidence(0.8, 4.0),
};
let gen_state = vec![0.0, 0.0, 0.0];
let correction = loop_.compute_correction(&gen_state, &milestone, 0.1);
// Correction should point toward the milestone state.
assert!(correction[0] > 0.0, "correction should steer toward milestone");
assert!((correction[0] - 0.1).abs() < 1e-6, "correction magnitude should be lr * delta");
}
#[test]
fn cosine_distance_is_correct() {
assert!((cosine_distance(&[1.0, 0.0], &[1.0, 0.0])).abs() < 1e-6);
assert!((cosine_distance(&[1.0, 0.0], &[0.0, 1.0]) - 1.0).abs() < 1e-6);
assert!((cosine_distance(&[1.0, 0.0], &[-1.0, 0.0]) - 2.0).abs() < 1e-6);
}
#[test]
fn summary_is_readable() {
let loop_ = AlignmentLoop::default_config();
let map = make_state_map();
let mut state = loop_.init(map);
let gen = vec![0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0];
loop_.check_alignment(&mut state, &gen);
let summary = AlignmentLoop::summary(&state);
assert!(summary.contains("AlignmentLoop"));
assert!(summary.contains("checks"));
assert!(summary.contains("corrections"));
}
#[test]
fn render_check_is_readable() {
let check = AlignmentCheck {
milestone_idx: 0,
milestone_label: "hypothesis".to_string(),
drift_score: 0.3,
corrected: false,
correction_strength: 0.0,
confidence: BetaBelief::from_confidence(0.7, 4.0),
};
let rendered = render_alignment_check(&check);
assert!(rendered.contains("hypothesis"));
assert!(rendered.contains("ALIGNED"));
}
}