1pub mod chunking;
2mod core;
3mod fact;
4mod format;
5mod import_export;
6pub mod maintenance;
7mod persist;
8mod query;
9mod ranking;
10mod types;
11
12pub use import_export::{parse_import_data, ImportMerge, ImportResult, SimpleFactEntry};
13pub use ranking::{find_cross_key_similar, SimilarFact};
14pub use types::*;
15
16#[cfg(test)]
17mod tests {
18 use super::*;
19 use crate::core::memory_boundary::FactPrivacy;
20 use crate::core::memory_policy::MemoryPolicy;
21 use chrono::Utc;
22
23 fn default_policy() -> MemoryPolicy {
24 MemoryPolicy::default()
25 }
26
27 #[test]
28 fn remember_and_recall() {
29 let policy = default_policy();
30 let mut k = ProjectKnowledge::new("/tmp/test-project");
31 k.remember(
32 "architecture",
33 "auth",
34 "JWT RS256",
35 "session-1",
36 0.9,
37 &policy,
38 );
39 k.remember("api", "rate-limit", "100/min", "session-1", 0.8, &policy);
40
41 let results = k.recall("auth");
42 assert_eq!(results.len(), 1);
43 assert_eq!(results[0].value, "JWT RS256");
44
45 let results = k.recall("api rate");
46 assert_eq!(results.len(), 1);
47 assert_eq!(results[0].key, "rate-limit");
48 }
49
50 #[test]
51 fn facts_evict_down_to_cap_not_double() {
52 let mut policy = default_policy();
56 policy.knowledge.max_facts = 5;
57 let mut k = ProjectKnowledge::new("/tmp/test-evict");
58 for i in 0..40 {
59 k.remember(
60 "finding",
61 &format!("key-{i}"),
62 &format!("value number {i}"),
63 "s1",
64 0.7,
65 &policy,
66 );
67 }
68 assert!(
69 k.facts.len() <= policy.knowledge.max_facts,
70 "expected <= {} facts after eviction, got {}",
71 policy.knowledge.max_facts,
72 k.facts.len()
73 );
74 }
75
76 #[test]
77 fn import_facts_evict_down_to_cap_not_double() {
78 let mut policy = default_policy();
84 policy.knowledge.max_facts = 5;
85
86 let mut source_policy = default_policy();
88 source_policy.knowledge.max_facts = 1000;
89 let mut source = ProjectKnowledge::new("/tmp/test-import-source");
90 for i in 0..40 {
91 source.remember(
92 "finding",
93 &format!("key-{i}"),
94 &format!("value number {i}"),
95 "s1",
96 0.7,
97 &source_policy,
98 );
99 }
100 let incoming = source.facts.clone();
101 assert!(
102 incoming.len() > policy.knowledge.max_facts * 2,
103 "test needs a batch larger than 2x cap to exercise the old guard, got {}",
104 incoming.len()
105 );
106
107 let mut k = ProjectKnowledge::new("/tmp/test-import-target");
108 k.import_facts(incoming, ImportMerge::Append, "s2", &policy);
109 assert!(
110 k.facts.len() <= policy.knowledge.max_facts,
111 "expected <= {} facts after import eviction, got {}",
112 policy.knowledge.max_facts,
113 k.facts.len()
114 );
115 }
116
117 #[test]
118 fn upsert_existing_fact() {
119 let policy = default_policy();
120 let mut k = ProjectKnowledge::new("/tmp/test");
121 k.remember("arch", "db", "PostgreSQL", "s1", 0.7, &policy);
122 k.remember(
123 "arch",
124 "db",
125 "PostgreSQL 16 with pgvector",
126 "s2",
127 0.95,
128 &policy,
129 );
130
131 let current: Vec<_> = k.facts.iter().filter(|f| f.is_current()).collect();
132 assert_eq!(current.len(), 1);
133 assert_eq!(current[0].value, "PostgreSQL 16 with pgvector");
134 }
135
136 #[test]
137 fn contradiction_detection() {
138 let policy = default_policy();
139 let mut k = ProjectKnowledge::new("/tmp/test");
140 k.remember("arch", "db", "PostgreSQL", "s1", 0.95, &policy);
141 k.facts[0].confirmation_count = 3;
142
143 let contradiction = k.check_contradiction("arch", "db", "MySQL", &policy);
144 assert!(contradiction.is_some());
145 let c = contradiction.unwrap();
146 assert_eq!(c.severity, ContradictionSeverity::High);
147 }
148
149 #[test]
150 fn temporal_validity() {
151 let policy = default_policy();
152 let mut k = ProjectKnowledge::new("/tmp/test");
153 k.remember("arch", "db", "PostgreSQL", "s1", 0.95, &policy);
154 k.facts[0].confirmation_count = 3;
155
156 k.remember("arch", "db", "MySQL", "s2", 0.9, &policy);
157
158 let current: Vec<_> = k.facts.iter().filter(|f| f.is_current()).collect();
159 assert_eq!(current.len(), 1);
160 assert_eq!(current[0].value, "MySQL");
161
162 let all_db: Vec<_> = k.facts.iter().filter(|f| f.key == "db").collect();
163 assert_eq!(all_db.len(), 2);
164 }
165
166 #[test]
167 fn confirmation_count() {
168 let policy = default_policy();
169 let mut k = ProjectKnowledge::new("/tmp/test");
170 k.remember("arch", "db", "PostgreSQL", "s1", 0.9, &policy);
171 assert_eq!(k.facts[0].confirmation_count, 1);
172
173 k.remember("arch", "db", "PostgreSQL", "s2", 0.9, &policy);
174 assert_eq!(k.facts[0].confirmation_count, 2);
175 }
176
177 #[test]
178 fn remove_fact() {
179 let policy = default_policy();
180 let mut k = ProjectKnowledge::new("/tmp/test");
181 k.remember("arch", "db", "PostgreSQL", "s1", 0.9, &policy);
182 assert!(k.remove_fact("arch", "db"));
183 assert!(k.facts.is_empty());
184 assert!(!k.remove_fact("arch", "db"));
185 }
186
187 #[test]
188 fn list_rooms() {
189 let policy = default_policy();
190 let mut k = ProjectKnowledge::new("/tmp/test");
191 k.remember("architecture", "auth", "JWT", "s1", 0.9, &policy);
192 k.remember("architecture", "db", "PG", "s1", 0.9, &policy);
193 k.remember("deploy", "host", "AWS", "s1", 0.8, &policy);
194
195 let rooms = k.list_rooms();
196 assert_eq!(rooms.len(), 2);
197 }
198
199 #[test]
200 fn aaak_format() {
201 let policy = default_policy();
202 let mut k = ProjectKnowledge::new("/tmp/test");
203 k.remember("architecture", "auth", "JWT RS256", "s1", 0.95, &policy);
204 k.remember("architecture", "db", "PostgreSQL", "s1", 0.7, &policy);
205
206 let aaak = k.format_aaak();
207 assert!(aaak.contains("ARCHITECTURE:"));
208 assert!(aaak.contains("auth=JWT RS256"));
209 }
210
211 #[test]
212 fn consolidate_history() {
213 let policy = default_policy();
214 let mut k = ProjectKnowledge::new("/tmp/test");
215 k.consolidate(
216 "Migrated from REST to GraphQL",
217 vec!["s1".into(), "s2".into()],
218 &policy,
219 );
220 assert_eq!(k.history.len(), 1);
221 assert_eq!(k.history[0].from_sessions.len(), 2);
222 }
223
224 #[test]
225 fn format_summary_output() {
226 let policy = default_policy();
227 let mut k = ProjectKnowledge::new("/tmp/test");
228 k.remember("architecture", "auth", "JWT RS256", "s1", 0.9, &policy);
229 k.add_pattern(
230 "naming",
231 "snake_case for functions",
232 vec!["get_user()".into()],
233 "s1",
234 &policy,
235 );
236 let summary = k.format_summary();
237 assert!(summary.contains("PROJECT KNOWLEDGE:"));
238 assert!(summary.contains("auth: JWT RS256"));
239 assert!(summary.contains("PROJECT PATTERNS:"));
240 }
241
242 #[test]
243 fn temporal_recall_at_time() {
244 let policy = default_policy();
245 let mut k = ProjectKnowledge::new("/tmp/test");
246 k.remember("arch", "db", "PostgreSQL", "s1", 0.95, &policy);
247 k.facts[0].confirmation_count = 3;
248
249 let before_change = Utc::now();
250 std::thread::sleep(std::time::Duration::from_millis(10));
251
252 k.remember("arch", "db", "MySQL", "s2", 0.9, &policy);
253
254 let results = k.recall_at_time("db", before_change);
255 assert_eq!(results.len(), 1);
256 assert_eq!(results[0].value, "PostgreSQL");
257
258 let results_now = k.recall_at_time("db", Utc::now());
259 assert_eq!(results_now.len(), 1);
260 assert_eq!(results_now[0].value, "MySQL");
261 }
262
263 #[test]
264 fn timeline_shows_history() {
265 let policy = default_policy();
266 let mut k = ProjectKnowledge::new("/tmp/test");
267 k.remember("arch", "db", "PostgreSQL", "s1", 0.95, &policy);
268 k.facts[0].confirmation_count = 3;
269 k.remember("arch", "db", "MySQL", "s2", 0.9, &policy);
270
271 let timeline = k.timeline("arch");
272 assert_eq!(timeline.len(), 2);
273 assert!(!timeline[0].is_current());
274 assert!(timeline[1].is_current());
275 }
276
277 #[test]
278 fn wakeup_format() {
279 let policy = default_policy();
280 let mut k = ProjectKnowledge::new("/tmp/test");
281 k.remember("arch", "auth", "JWT", "s1", 0.95, &policy);
282 k.remember("arch", "db", "PG", "s1", 0.8, &policy);
283
284 let wakeup = k.format_wakeup();
287 assert!(wakeup.contains("FACTS:"));
288 assert!(wakeup.contains("[arch]"));
289 assert!(wakeup.contains("auth=JWT"));
290 assert!(wakeup.contains("db=PG"));
291 }
292
293 #[test]
294 fn salience_prioritizes_decisions_over_findings_at_similar_confidence() {
295 let policy = default_policy();
296 let mut k = ProjectKnowledge::new("/tmp/test");
297 k.remember("finding", "f1", "some thing", "s1", 0.9, &policy);
298 k.remember("decision", "d1", "important", "s1", 0.85, &policy);
299
300 let wakeup = k.format_wakeup();
303 let d = wakeup.find("d1=important").expect("decision in wakeup");
304 let f = wakeup.find("f1=some thing").expect("finding in wakeup");
305 assert!(
306 d < f,
307 "expected decision before finding in wakeup: {wakeup}"
308 );
309 }
310
311 #[test]
312 fn low_confidence_contradiction() {
313 let policy = default_policy();
314 let mut k = ProjectKnowledge::new("/tmp/test");
315 k.remember("arch", "db", "PostgreSQL", "s1", 0.4, &policy);
316
317 let c = k.check_contradiction("arch", "db", "MySQL", &policy);
318 assert!(c.is_some());
319 assert_eq!(c.unwrap().severity, ContradictionSeverity::Low);
320 }
321
322 #[test]
323 fn no_contradiction_for_same_value() {
324 let policy = default_policy();
325 let mut k = ProjectKnowledge::new("/tmp/test");
326 k.remember("arch", "db", "PostgreSQL", "s1", 0.95, &policy);
327
328 let c = k.check_contradiction("arch", "db", "PostgreSQL", &policy);
329 assert!(c.is_none());
330 }
331
332 #[test]
333 fn no_contradiction_for_similar_values() {
334 let policy = default_policy();
335 let mut k = ProjectKnowledge::new("/tmp/test");
336 k.remember(
337 "arch",
338 "db",
339 "PostgreSQL 16 production database server",
340 "s1",
341 0.95,
342 &policy,
343 );
344
345 let c = k.check_contradiction(
346 "arch",
347 "db",
348 "PostgreSQL 16 production database server config",
349 &policy,
350 );
351 assert!(c.is_none());
352 }
353
354 #[test]
355 fn import_skip_existing() {
356 let policy = default_policy();
357 let mut k = ProjectKnowledge::new("/tmp/test");
358 k.remember("arch", "db", "PostgreSQL", "s1", 0.95, &policy);
359
360 let incoming = vec![KnowledgeFact {
361 category: "arch".into(),
362 key: "db".into(),
363 value: "MySQL".into(),
364 source_session: "import".into(),
365 confidence: 0.8,
366 created_at: Utc::now(),
367 last_confirmed: Utc::now(),
368 retrieval_count: 0,
369 last_retrieved: None,
370 valid_from: Some(Utc::now()),
371 valid_until: None,
372 supersedes: None,
373 confirmation_count: 1,
374 feedback_up: 0,
375 feedback_down: 0,
376 last_feedback: None,
377 privacy: FactPrivacy::default(),
378 sensitivity: crate::core::sensitivity::SensitivityLevel::default(),
379 imported_from: None,
380 archetype: KnowledgeArchetype::default(),
381 fidelity: None,
382 revision_count: 0,
383 }];
384
385 let result = k.import_facts(incoming, ImportMerge::SkipExisting, "imp-1", &policy);
386 assert_eq!(result.skipped, 1);
387 assert_eq!(result.added, 0);
388 assert_eq!(k.facts.iter().filter(|f| f.is_current()).count(), 1);
389 }
390
391 #[test]
392 fn import_replace_existing() {
393 let policy = default_policy();
394 let mut k = ProjectKnowledge::new("/tmp/test");
395 k.remember("arch", "db", "PostgreSQL", "s1", 0.95, &policy);
396
397 let incoming = vec![KnowledgeFact {
398 category: "arch".into(),
399 key: "db".into(),
400 value: "MySQL".into(),
401 source_session: "import".into(),
402 confidence: 0.8,
403 created_at: Utc::now(),
404 last_confirmed: Utc::now(),
405 retrieval_count: 0,
406 last_retrieved: None,
407 valid_from: Some(Utc::now()),
408 valid_until: None,
409 supersedes: None,
410 confirmation_count: 1,
411 feedback_up: 0,
412 feedback_down: 0,
413 last_feedback: None,
414 privacy: FactPrivacy::default(),
415 sensitivity: crate::core::sensitivity::SensitivityLevel::default(),
416 imported_from: None,
417 archetype: KnowledgeArchetype::default(),
418 fidelity: None,
419 revision_count: 0,
420 }];
421
422 let result = k.import_facts(incoming, ImportMerge::Replace, "imp-1", &policy);
423 assert_eq!(result.replaced, 1);
424 let current: Vec<_> = k.facts.iter().filter(|f| f.is_current()).collect();
425 assert_eq!(current.len(), 1);
426 assert_eq!(current[0].value, "MySQL");
427 }
428
429 #[test]
430 fn import_adds_new_facts() {
431 let policy = default_policy();
432 let mut k = ProjectKnowledge::new("/tmp/test");
433 k.remember("arch", "db", "PostgreSQL", "s1", 0.95, &policy);
434
435 let incoming = vec![KnowledgeFact {
436 category: "security".into(),
437 key: "auth".into(),
438 value: "JWT".into(),
439 source_session: "import".into(),
440 confidence: 0.9,
441 created_at: Utc::now(),
442 last_confirmed: Utc::now(),
443 retrieval_count: 0,
444 last_retrieved: None,
445 valid_from: Some(Utc::now()),
446 valid_until: None,
447 supersedes: None,
448 confirmation_count: 1,
449 feedback_up: 0,
450 feedback_down: 0,
451 last_feedback: None,
452 privacy: FactPrivacy::default(),
453 sensitivity: crate::core::sensitivity::SensitivityLevel::default(),
454 imported_from: None,
455 archetype: KnowledgeArchetype::default(),
456 fidelity: None,
457 revision_count: 0,
458 }];
459
460 let result = k.import_facts(incoming, ImportMerge::SkipExisting, "imp-1", &policy);
461 assert_eq!(result.added, 1);
462 assert_eq!(k.facts.iter().filter(|f| f.is_current()).count(), 2);
463 }
464
465 #[test]
466 fn parse_simple_json_array() {
467 let data = r#"[
468 {"category": "arch", "key": "db", "value": "PostgreSQL"},
469 {"category": "security", "key": "auth", "value": "JWT", "confidence": 0.9}
470 ]"#;
471 let facts = parse_import_data(data).unwrap();
472 assert_eq!(facts.len(), 2);
473 assert_eq!(facts[0].category, "arch");
474 assert_eq!(facts[1].confidence, 0.9);
475 }
476
477 #[test]
478 fn parse_jsonl_format() {
479 let data = "{\"category\":\"arch\",\"key\":\"db\",\"value\":\"PG\"}\n\
480 {\"category\":\"security\",\"key\":\"auth\",\"value\":\"JWT\"}";
481 let facts = parse_import_data(data).unwrap();
482 assert_eq!(facts.len(), 2);
483 }
484
485 #[test]
486 fn export_simple_only_current() {
487 let policy = default_policy();
488 let mut k = ProjectKnowledge::new("/tmp/test");
489 k.remember("arch", "db", "PostgreSQL", "s1", 0.95, &policy);
490 k.remember("arch", "db", "MySQL", "s2", 0.9, &policy);
491
492 let exported = k.export_simple();
493 assert_eq!(exported.len(), 1);
494 assert_eq!(exported[0].value, "MySQL");
495 }
496
497 #[test]
498 fn import_merge_parse() {
499 assert_eq!(ImportMerge::parse("replace"), Some(ImportMerge::Replace));
500 assert_eq!(ImportMerge::parse("append"), Some(ImportMerge::Append));
501 assert_eq!(
502 ImportMerge::parse("skip-existing"),
503 Some(ImportMerge::SkipExisting)
504 );
505 assert_eq!(
506 ImportMerge::parse("skip_existing"),
507 Some(ImportMerge::SkipExisting)
508 );
509 assert_eq!(ImportMerge::parse("skip"), Some(ImportMerge::SkipExisting));
510 assert!(ImportMerge::parse("invalid").is_none());
511 }
512
513 #[test]
514 fn revision_count_on_new_fact() {
515 let policy = default_policy();
516 let mut k = ProjectKnowledge::new("/tmp/test");
517 k.remember("arch", "db", "PostgreSQL", "s1", 0.9, &policy);
518 let cur = k.facts.iter().find(|f| f.is_current()).unwrap();
519 assert_eq!(cur.revision_count, 1);
520 }
521
522 #[test]
523 fn revision_count_increments_on_confirm() {
524 let policy = default_policy();
525 let mut k = ProjectKnowledge::new("/tmp/test");
526 k.remember("arch", "db", "PostgreSQL", "s1", 0.9, &policy);
527 k.remember("arch", "db", "PostgreSQL", "s2", 0.9, &policy);
528 k.remember("arch", "db", "PostgreSQL", "s3", 0.9, &policy);
529 let cur = k.facts.iter().find(|f| f.is_current()).unwrap();
530 assert_eq!(cur.revision_count, 3);
531 assert_eq!(cur.confirmation_count, 3);
532 }
533
534 #[test]
535 fn revision_count_carries_over_on_supersede() {
536 let policy = default_policy();
537 let mut k = ProjectKnowledge::new("/tmp/test");
538 k.remember("arch", "db", "PostgreSQL", "s1", 0.95, &policy);
539 k.remember("arch", "db", "PostgreSQL", "s2", 0.9, &policy);
540 assert_eq!(
541 k.facts
542 .iter()
543 .find(|f| f.is_current())
544 .unwrap()
545 .revision_count,
546 2
547 );
548 k.facts[0].confirmation_count = 3;
549 k.remember("arch", "db", "MySQL", "s3", 0.9, &policy);
550 let cur: Vec<_> = k.facts.iter().filter(|f| f.is_current()).collect();
551 assert_eq!(cur.len(), 1);
552 assert_eq!(cur[0].value, "MySQL");
553 assert_eq!(cur[0].revision_count, 3);
554 assert!(cur[0].supersedes.is_some());
555 }
556
557 #[test]
558 fn revision_count_default_zero_for_legacy() {
559 let json = r#"{
560 "category": "test", "key": "k", "value": "v",
561 "source_session": "s", "confidence": 0.8,
562 "created_at": "2024-01-01T00:00:00Z",
563 "last_confirmed": "2024-01-01T00:00:00Z"
564 }"#;
565 let fact: KnowledgeFact = serde_json::from_str(json).unwrap();
566 assert_eq!(fact.revision_count, 0);
567 }
568
569 #[test]
570 fn judged_pairs_default_empty_for_legacy() {
571 let json = r#"{
572 "project_root": "/test", "project_hash": "abc",
573 "facts": [], "patterns": [], "history": [],
574 "updated_at": "2024-01-01T00:00:00Z"
575 }"#;
576 let pk: ProjectKnowledge = serde_json::from_str(json).unwrap();
577 assert!(pk.judged_pairs.is_empty());
578 }
579
580 #[test]
581 fn cross_key_similar_finds_related_facts() {
582 let policy = default_policy();
583 let mut k = ProjectKnowledge::new("/tmp/test");
584 k.remember(
585 "architecture",
586 "auth",
587 "JWT RS256 token based authentication with Redis session store",
588 "s1",
589 0.9,
590 &policy,
591 );
592 k.remember(
593 "decision",
594 "session-model",
595 "JWT token authentication stored in Redis for session management",
596 "s1",
597 0.85,
598 &policy,
599 );
600 k.remember("deploy", "host", "AWS us-east-1", "s1", 0.8, &policy);
601
602 let similar = find_cross_key_similar(
603 "architecture",
604 "auth",
605 "JWT RS256 token based authentication with Redis session store",
606 &k.facts,
607 &k.judged_pairs,
608 3,
609 );
610 assert!(!similar.is_empty(), "should find session-model as similar");
611 assert_eq!(similar[0].category, "decision");
612 assert_eq!(similar[0].key, "session-model");
613 assert!(similar[0].similarity > 0.35);
614 }
615
616 #[test]
617 fn cross_key_similar_excludes_same_key() {
618 let policy = default_policy();
619 let mut k = ProjectKnowledge::new("/tmp/test");
620 k.remember("arch", "db", "PostgreSQL 16", "s1", 0.9, &policy);
621
622 let similar =
623 find_cross_key_similar("arch", "db", "PostgreSQL 16", &k.facts, &k.judged_pairs, 3);
624 assert!(similar.is_empty());
625 }
626
627 #[test]
628 fn cross_key_similar_excludes_judged_pairs() {
629 let policy = default_policy();
630 let mut k = ProjectKnowledge::new("/tmp/test");
631 k.remember(
632 "architecture",
633 "auth",
634 "JWT RS256 token based authentication with Redis",
635 "s1",
636 0.9,
637 &policy,
638 );
639 k.remember(
640 "decision",
641 "session-model",
642 "JWT token authentication stored in Redis",
643 "s1",
644 0.85,
645 &policy,
646 );
647
648 k.judged_pairs.push(JudgedPair {
649 key_a: "architecture/auth".into(),
650 key_b: "decision/session-model".into(),
651 verdict: "compatible".into(),
652 judged_at: Utc::now(),
653 });
654
655 let similar = find_cross_key_similar(
656 "architecture",
657 "auth",
658 "JWT RS256 token based authentication with Redis",
659 &k.facts,
660 &k.judged_pairs,
661 3,
662 );
663 assert!(similar.is_empty(), "judged pairs should be excluded");
664 }
665
666 #[test]
667 fn cross_key_similar_ignores_unrelated_facts() {
668 let policy = default_policy();
669 let mut k = ProjectKnowledge::new("/tmp/test");
670 k.remember(
671 "arch",
672 "db",
673 "PostgreSQL 16 with pgvector",
674 "s1",
675 0.9,
676 &policy,
677 );
678 k.remember("deploy", "host", "AWS us-east-1 region", "s1", 0.8, &policy);
679
680 let similar = find_cross_key_similar(
681 "arch",
682 "db",
683 "PostgreSQL 16 with pgvector",
684 &k.facts,
685 &k.judged_pairs,
686 3,
687 );
688 assert!(similar.is_empty(), "unrelated facts should not match");
689 }
690
691 #[test]
692 fn judge_supersedes_archives_target() {
693 let policy = default_policy();
694 let mut k = ProjectKnowledge::new("/tmp/test");
695 k.remember("architecture", "auth", "JWT RS256", "s1", 0.9, &policy);
696 k.remember("decision", "session", "JWT tokens", "s1", 0.85, &policy);
697
698 assert!(k.facts.iter().all(KnowledgeFact::is_current));
699
700 if let Some(tf) = k
701 .facts
702 .iter_mut()
703 .find(|f| f.category == "decision" && f.key == "session" && f.is_current())
704 {
705 tf.valid_until = Some(Utc::now());
706 }
707 k.judged_pairs.push(JudgedPair {
708 key_a: "architecture/auth".into(),
709 key_b: "decision/session".into(),
710 verdict: "supersedes".into(),
711 judged_at: Utc::now(),
712 });
713
714 let cur: Vec<_> = k.facts.iter().filter(|f| f.is_current()).collect();
715 assert_eq!(cur.len(), 1);
716 assert_eq!(cur[0].category, "architecture");
717 }
718}