lean_ctx/core/memory_salience.rs
1//! Content salience for memory: a cheap, deterministic keyword score over a
2//! fact/finding's raw text.
3//!
4//! It answers "how much signal does this string carry?" without any model call,
5//! and feeds two consumers:
6//! - the cognition loop's auto-promotion gate (only promote findings above a
7//! floor), and
8//! - the write-time admission floor (#970) that keeps low-signal facts out of a
9//! capped store.
10//!
11//! Pure and total so it never perturbs output determinism (#498): the same text
12//! always scores the same value, independent of process state.
13
14/// Boost table: substrings that mark a finding as high-signal (errors, security,
15/// failures). A base score of [`BASE`] is always granted so a plain, valid fact
16/// is never zero — the floor (when enabled) is what decides admission.
17const BASE: u32 = 20;
18const BOOSTS: &[(&str, u32)] = &[
19 ("error", 25),
20 ("failed", 25),
21 ("panic", 30),
22 ("assert", 20),
23 ("forbidden", 25),
24 ("timeout", 20),
25 ("deadlock", 25),
26 ("security", 25),
27 ("vuln", 25),
28 ("e0", 15), // Rust error codes often start with E0xxx.
29];
30
31/// Score the salience of a piece of memory text. Always `>= BASE`.
32#[must_use]
33pub fn text_salience(text: &str) -> u32 {
34 let s = text.to_lowercase();
35 let mut score = BASE;
36 for (pat, b) in BOOSTS {
37 if s.contains(pat) {
38 score = score.saturating_add(*b);
39 }
40 }
41 score
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn plain_text_gets_base_only() {
50 assert_eq!(text_salience("the database is postgres"), BASE);
51 }
52
53 #[test]
54 fn high_signal_terms_boost_above_base() {
55 assert!(text_salience("auth failed with a security error") > BASE);
56 // Each distinct boost term stacks.
57 assert!(
58 text_salience("panic deadlock timeout") > text_salience("timeout"),
59 "multiple boosts must accumulate"
60 );
61 }
62
63 #[test]
64 fn is_deterministic_and_case_insensitive() {
65 assert_eq!(
66 text_salience("SECURITY VULN"),
67 text_salience("security vuln")
68 );
69 assert_eq!(text_salience("x"), text_salience("x"));
70 }
71}