- 1
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] - 2
- 3
use std::sync::Arc; - 4
use vak_core::learning; - 5
use vak_core::memory; - 6
use vak_core::reflection::{apply, jaccard, parse_proposals, propose}; - 7
use vak_llm::types::ContentBlock; - 8
- 9
#[test] - 10
fn jaccard_catches_near_duplicates() { - 11
let a = "the deploy script must pause before rollback windows"; - 12
let b = "deploy script: pause before rollback windows"; - 13
let c = "favorite pizza toppings debate"; - 14
assert!(jaccard(a, b) > 0.55); - 15
assert!(jaccard(a, c) < 0.1); - 16
} - 17
- 18
#[test] - 19
fn parser_extracts_notes_and_skill_from_messy_reply() { - 20
let reply = r#"Sure! Here's what I'd keep: - 21
{"notes":[ - 22
{"note":"the deploy script must pause before rollback windows","kind":"decision","tag":"deploy"}, - 23
{"note":"x","kind":"fact"}, - 24
{"note":"user prefers small PRs","kind":"preference"} - 25
], - 26
"skill":{"name":"Deploy Safely!","description":"","instructions":""}, - 27
"trailing":1}"#; - 28
let p = parse_proposals(reply); - 29
// Short notes dropped; kinds normalized; tag sanitized. - 30
assert_eq!(p.notes.len(), 2); - 31
assert_eq!(p.notes[0].kind, "decision"); - 32
assert_eq!(p.notes[0].tag, "deploy"); - 33
assert!(p.skill.is_none(), "invalid skill name/description rejected"); - 34
} - 35
- 36
#[test] - 37
fn non_json_reply_yields_nothing() { - 38
let p = parse_proposals("Nothing worth remembering here at all."); - 39
assert!(p.notes.is_empty()); - 40
assert!(p.skill.is_none()); - 41
} - 42
- 43
fn scripted(json_reply: &str) -> Arc<dyn vak_llm::Provider> { - 44
struct Once(String); - 45
#[async_trait::async_trait] - 46
impl vak_llm::Provider for Once { - 47
fn name(&self) -> &str { - 48
"once" - 49
} - 50
async fn stream( - 51
&self, - 52
_req: vak_llm::types::ChatRequest, - 53
_cancel: tokio_util::sync::CancellationToken, - 54
) -> Result<vak_llm::EventStream, vak_llm::LlmError> { - 55
let (mut sink, rx) = vak_llm::stream::channel(8); - 56
sink.close_message(vak_llm::types::AssistantMessage { - 57
content: vec![ContentBlock::text(self.0.clone())], - 58
stop_reason: vak_llm::types::StopReason::EndTurn, - 59
usage: vak_llm::types::Usage::default(), - 60
model: "m".into(), - 61
response_id: None, - 62
}) - 63
.await; - 64
Ok(rx) - 65
} - 66
} - 67
- 68
Arc::new(Once(json_reply.to_string())) - 69
} - 70
- 71
const GOOD_REPLY: &str = r#"{"notes":[{"note":"the deploy pipeline pauses before every rollback window","kind":"decision","tag":"deploys"}],"skill":{"name":"ship-it","description":"Ship with rollbacks guarded","instructions":"Run scripts/ship.sh"}}"#; - 72
- 73
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] - 74
async fn reflection_writes_once_and_dedups_repeats() { - 75
let dir = tempfile::tempdir().unwrap(); - 76
let home = dir.path().join("home"); - 77
let cwd = dir.path().to_path_buf(); - 78
- 79
let proposals = propose( - 80
scripted(GOOD_REPLY), - 81
"m", - 82
"assistant: we shipped the deploy pipeline fix", - 83
tokio_util::sync::CancellationToken::new(), - 84
) - 85
.await - 86
.unwrap(); - 87
assert_eq!(proposals.notes.len(), 1); - 88
assert_eq!(proposals.skill.as_ref().unwrap().name, "ship-it"); - 89
- 90
// First apply writes the note and queues the skill. - 91
let (notes, queued) = apply(&home, &cwd, "sess-1", &proposals).unwrap(); - 92
assert_eq!(notes, 1); - 93
assert!(queued); - 94
assert_eq!(memory::list_notes(&home, &cwd).len(), 1); - 95
assert_eq!(learning::list_proposals(&home, &cwd).len(), 1); - 96
- 97
// A near-identical reflection later adds NOTHING (bounded growth). - 98
let again = propose( - 99
scripted(GOOD_REPLY), - 100
"m", - 101
"same conversation again", - 102
tokio_util::sync::CancellationToken::new(), - 103
) - 104
.await - 105
.unwrap(); - 106
let (notes2, queued2) = apply(&home, &cwd, "sess-2", &again).unwrap(); - 107
assert_eq!(notes2, 0, "near-duplicate note must be skipped"); - 108
// A second identical skill proposal queues under a distinct id; review - 109
// queue dedup is the human reviewer's call. - 110
assert!(!queued2 || !learning::list_proposals(&home, &cwd).is_empty(),); - 111
} - 112
- 113
#[test] - 114
fn parser_accepts_invariant_notes() { - 115
let reply = r#"{"notes":[{"note":"always inspect schema before writing migration scripts","kind":"invariant","tag":"schema"}]}"#; - 116
let p = parse_proposals(reply); - 117
assert_eq!(p.notes.len(), 1); - 118
assert_eq!(p.notes[0].kind, "invariant"); - 119
assert_eq!(p.notes[0].tag, "schema"); - 120
} - 121
Indexing the workspace…
Vakyartha documentation is discovering safe artifacts, anchors, and source references.