headroom/tests/test_memory
chopratejas 0be0eede9e fix(memory): traffic_learner indexes system-reminder fragments as user preferences (refs #464)
`TrafficLearner._extract_preferences` ran three regex patterns over raw
user-message text and saved any match as a `User preference: <captured>`
memory. Two compounding bugs made ~10% of the reporter's saved memories
(187 of 1796) garbage:

1. **System-reminder content was matched.** Claude Code injects
   `<system-reminder>…</system-reminder>` blocks into user-role
   messages — scaffolding ("don't mention this reminder", "use colgrep
   instead of Grep", "never bypass signing") that hits every correction
   trigger. The learner happily persisted scaffolding as authoritative
   user preferences.
2. **Capture groups were fixed-length windows.** `(.{10,100})` grabbed
   the next 10–100 chars with no boundary awareness, producing
   mid-word truncations like `User preference: of Grep, Glob. When
   spawning agents, mention colgrep features a`.

This change rewrites `_extract_preferences` to be **regex-free** and
adds two layered defences:

- `_strip_system_reminders` (literal `str.find` scan, no regex)
  removes `<system-reminder>…</system-reminder>` blocks from user
  text before any pattern matching. Unclosed reminders drop to
  end-of-string. Case-insensitive on the tag name only. ~95% of the
  reporter's noise sample comes from this single layer.
- A token-based correction scanner replaces the three `re.compile`
  patterns. It tokenises on whitespace (lowercasing once, up front),
  matches trigger sequences as ordered token lists (`don't`, `do not`,
  `stop`, `never`, `avoid`, `no use`, `no try`, `no do`, `instead`),
  and captures the trailing content until a sentence terminator
  (`.!?\n`) or end-of-input. Captures shorter than 10 chars are
  rejected (stray triggers), and captures that hit the 78/98-char cap
  without finding a terminator are rejected (rambling fragments). The
  former noise — `colgrep instead of Grep, Glob. When spawning…` —
  fails this gate; short complete user utterances
  (`don't use git push, I'll push manually`) still pass because
  end-of-input counts as a boundary.

Net regex count in this file: -3, +0.

`_hydrate_persisted_state` already runs in `start()` and seeds
`_saved_hashes`/`_persisted_ids` from prior rows, so cross-restart
dedup is already wired up — the reporter's "doesn't survive restarts"
note was partially outdated. The narrow remaining edge (in-process
`_dedup_window=100` eviction within a single very-long-running
process) self-heals on next restart and is left as a separate
follow-up.

Tests: 17 new across `TestStripSystemReminders`,
`TestExtractPreferencesSystemReminderFiltering`,
`TestExtractPreferencesRealCorrections`, and
`TestExtractPreferencesSentenceBoundary`. Full traffic_learner suite:
139 passing. ci-precheck green.
2026-05-13 16:13:04 -07:00
..
__init__.py Add persistent memory system with zero-latency inline extraction 2026-01-14 21:32:09 -08:00
conftest.py Add global httpx.ReadTimeout handler for memory tests 2026-01-28 09:43:44 -08:00
test_budget.py Fix ruff lint errors in test files 2026-03-24 15:54:12 -07:00
test_core_operations.py Add multi-provider memory system with auto-detection 2026-02-01 14:42:50 -08:00
test_easy.py fix(tests): skip memory tests when hnswlib not available 2026-01-31 16:09:53 -08:00
test_extraction.py Add hierarchical memory system with graph + vector storage 2026-01-26 21:58:47 -08:00
test_factory.py Add centralized ML model configuration 2026-02-01 23:47:42 -08:00
test_factory_external.py chore(memory): add EXTERNAL backend extension points 2026-04-20 16:42:10 -07:00
test_hierarchical.py fix(memory): batch onnx embeddings and sqlite-vec ops 2026-04-24 09:49:28 +00:00
test_learn_flag.py fix(traffic-learner): raise min-evidence default and make it configurable 2026-04-30 17:44:22 +09:00
test_mcp_server.py fix(memory): batch onnx embeddings and sqlite-vec ops 2026-04-24 09:49:28 +00:00
test_qdrant_env.py feat(memory): resolve Qdrant connection from HEADROOM_QDRANT_* env vars (#31) 2026-04-24 22:16:16 -07:00
test_traffic_learner.py fix(memory): traffic_learner indexes system-reminder fragments as user preferences (refs #464) 2026-05-13 16:13:04 -07:00
test_writers.py fix: harden learn path handling across platforms 2026-05-09 15:45:26 -07:00