superlocalmemory 4.0.9 → 4.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/README.md +3 -3
  3. package/package.json +1 -1
  4. package/plugin/.claude-plugin/plugin.json +1 -1
  5. package/plugin/CLAUDE.md +3 -3
  6. package/plugin/agents/slm-governance-advisor.md +1 -1
  7. package/plugin/agents/slm-loop-runner.md +1 -1
  8. package/plugin/agents/slm-memory-advisor.md +1 -1
  9. package/plugin/agents/slm-optimize-advisor.md +1 -1
  10. package/plugin/requirements.txt +1 -1
  11. package/plugin/skills/slm-cache/SKILL.md +1 -1
  12. package/plugin/skills/slm-compress/SKILL.md +1 -1
  13. package/plugin/skills/slm-governance/SKILL.md +1 -1
  14. package/plugin/skills/slm-graph/SKILL.md +1 -1
  15. package/plugin/skills/slm-loop/SKILL.md +1 -1
  16. package/plugin/skills/slm-mesh/SKILL.md +1 -1
  17. package/plugin/skills/slm-profile/SKILL.md +1 -1
  18. package/plugin/skills/slm-recall/SKILL.md +1 -1
  19. package/plugin/skills/slm-remember/SKILL.md +1 -1
  20. package/plugin/skills/slm-scope/SKILL.md +1 -1
  21. package/plugin/skills/slm-session/SKILL.md +1 -1
  22. package/plugin/skills/slm-status/SKILL.md +1 -1
  23. package/plugin-src/rules/AGENTS.md +1 -1
  24. package/pyproject.toml +1 -1
  25. package/src/superlocalmemory/__init__.py +1 -1
  26. package/src/superlocalmemory/cli/commands.py +45 -2
  27. package/src/superlocalmemory/cli/main.py +2 -2
  28. package/src/superlocalmemory/code_graph/bridge/maintenance.py +8 -0
  29. package/src/superlocalmemory/core/fact_consolidator.py +316 -125
  30. package/src/superlocalmemory/core/maintenance.py +44 -6
  31. package/src/superlocalmemory/core/memory_health.py +266 -0
  32. package/src/superlocalmemory/core/operation_policy_registry.py +1 -1
  33. package/src/superlocalmemory/core/operation_request.py +1 -1
  34. package/src/superlocalmemory/core/ops_remediation.py +2 -2
  35. package/src/superlocalmemory/core/store_pipeline.py +78 -3
  36. package/src/superlocalmemory/encoding/cognitive_consolidator.py +15 -1
  37. package/src/superlocalmemory/mcp/server.py +1 -1
  38. package/src/superlocalmemory/mcp/session_binding.py +92 -0
  39. package/src/superlocalmemory/mcp/tools_core.py +40 -39
  40. package/src/superlocalmemory/mcp/tools_ops.py +2 -2
  41. package/src/superlocalmemory/retrieval/bm25_channel.py +4 -8
  42. package/src/superlocalmemory/retrieval/entity_channel.py +7 -1
  43. package/src/superlocalmemory/retrieval/scope_policy.py +22 -1
  44. package/src/superlocalmemory/retrieval/temporal_channel.py +13 -1
  45. package/src/superlocalmemory/retrieval/vector_store.py +63 -0
  46. package/src/superlocalmemory/server/api.py +6 -1
  47. package/src/superlocalmemory/server/asset_versions.py +171 -0
  48. package/src/superlocalmemory/server/routes/abstraction.py +201 -0
  49. package/src/superlocalmemory/server/routes/data_io.py +29 -1
  50. package/src/superlocalmemory/server/routes/entity.py +13 -1
  51. package/src/superlocalmemory/server/routes/mesh.py +1 -1
  52. package/src/superlocalmemory/server/routes/v3_api.py +2 -2
  53. package/src/superlocalmemory/server/ui.py +8 -1
  54. package/src/superlocalmemory/server/unified_daemon.py +111 -9
  55. package/src/superlocalmemory/storage/_migration_internals.py +4 -0
  56. package/src/superlocalmemory/storage/database.py +128 -30
  57. package/src/superlocalmemory/storage/migration_runner.py +11 -0
  58. package/src/superlocalmemory/storage/migrations/M043_quarantine_display_summaries.py +488 -0
  59. package/src/superlocalmemory/storage/schema.py +98 -0
  60. package/src/superlocalmemory/summaries/base.py +1 -1
  61. package/src/superlocalmemory/summaries/non_answer.py +223 -0
  62. package/src/superlocalmemory/ui/index.html +1 -1
  63. package/src/superlocalmemory/ui/js/od-memories.js +190 -1
  64. package/src/superlocalmemory/ui/js/od-ops-health.js +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,81 @@ All notable changes to SuperLocalMemory will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [4.0.10] — Your memories, not the summarizer's
9
+
10
+ ### Fixed
11
+ - **A background pass had been writing its own summaries into your memory, and
12
+ they answered your questions.** SuperLocalMemory groups related memories and
13
+ writes a short summary of each group, meant for the dashboard. Since June
14
+ those summaries were being stored as memories, where they competed with your
15
+ own notes — and won, because each one carried the combined subjects of
16
+ everything it summarised and so looked more connected than any single note you
17
+ had written. Asked what you were working on, a store could answer with the
18
+ summarizer apologising for having nothing to say. Summaries now live
19
+ separately and are shown on the dashboard only. They are never returned as
20
+ answers, by any route: recall, search, the recent list, pinned context, an
21
+ export, or the memory count.
22
+ - **Summarising a group set aside the memories in it.** That made sense while
23
+ the summary stood in for them in your answers. It does not now, so it has
24
+ stopped — and upgrading brings back the memories it had already set aside.
25
+ Where a store had been running long enough for those memories to have faded on
26
+ their own, they are restored to whatever standing their own history warrants,
27
+ not promoted.
28
+ - **Upgrading repairs an affected store by itself.** No commands, no files to
29
+ edit. A verified backup is taken first, the repair is safe to interrupt and
30
+ safe to repeat, and it reports in plain language what it changed. It also
31
+ keeps checking: if the problem ever reappears, the next start handles it.
32
+ - **The setting that turns summarising off did nothing.** It has been
33
+ documented and ignored since it was added.
34
+ - **A summary that is a refusal is no longer stored as one.** When a group has
35
+ nothing in common, a model does not fail — it answers in prose, and that prose
36
+ was indistinguishable from a summary to anything that only checked whether
37
+ text came back. Model output is now cleaned of chat framing and rejected if it
38
+ turns out to be about the request rather than about your memories. A summary
39
+ assembled from your own sentences is exempt, so a note that happens to be
40
+ phrased like a chatbot is not thrown away.
41
+ - **A summary of a group could pull real memories into a later grouping pass
42
+ and set them aside with it.**
43
+ - **Summarising an unchanged group repeated its work indefinitely**, including
44
+ calling a language model to regenerate text it already had. It now recognises
45
+ that nothing has changed and stops.
46
+ - **An exported backup carried the summaries**, and importing it would have
47
+ turned them back into memories on a machine where nothing was wrong.
48
+ - **A memory can now be dated to when it happened**, not just to when it was
49
+ written. Everything was previously stamped with its ingestion date, so a note
50
+ written today about something from March filed itself under today. A date that
51
+ cannot be understood is refused rather than quietly dropped.
52
+ - **A dated memory is now findable by its date even when nothing in it was
53
+ recognised as a name.** Roughly a quarter of a typical store was reachable
54
+ only by being recent, rather than by being about the right time.
55
+ - **Stored memories now record which conversation they came from.** Reading
56
+ memories worked this out; writing them did not, so almost nothing carried it —
57
+ which left the feature that spreads an answer across different conversations
58
+ with nothing to work with.
59
+
60
+ ### Added
61
+ - **`slm doctor` and the dashboard now tell you whether your memories can
62
+ actually be found**, with the counts behind the answer: how many you have, how
63
+ many are searchable by meaning, and how many are being held back and why. This
64
+ was previously unknowable without writing your own queries against the
65
+ database — one machine ran for months with 44% of its memory unreachable while
66
+ every status line said it was healthy.
67
+ - **A Knowledge Overview on the Summaries tab.** What your memory knows, how
68
+ many memories each summary covers, and the stretch of time it spans. Summaries
69
+ that came back empty are counted rather than shown, and near-identical ones
70
+ are collapsed, so the page reflects what is there instead of padding itself.
71
+
72
+ ### Changed
73
+ - **Searching by meaning no longer spends its results on held-back summaries.**
74
+ On a store with a lot of them, a question close to their subject matter could
75
+ come back with nothing usable at all while appearing to have answered.
76
+ - **The startup repair no longer reports itself finished while memories remain
77
+ unsearchable.** It says how many are left, why, and that it will retry — and
78
+ it does retry, instead of recording success and never looking again.
79
+ - **Dashboard files now carry a version taken from their own contents**, so a
80
+ changed script or stylesheet is always served as a new address. The version
81
+ numbers were maintained by hand and had stopped matching the files they named.
82
+
8
83
  ## [4.0.9] — Findable the moment you write it
9
84
 
10
85
  ### Fixed
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  </picture>
6
6
  </p>
7
7
 
8
- <h1 align="center">SuperLocalMemory V4.0.9</h1>
8
+ <h1 align="center">SuperLocalMemory V4.0.10</h1>
9
9
 
10
10
  <h2 align="center">Rent the LLM. Own the memory.</h2>
11
11
 
@@ -27,12 +27,12 @@ guarantee here is stated as a falsifiable invariant, tested under an adversarial
27
27
  negative control, and shipped with the harness that regenerates the evidence:
28
28
  <code>python benchmark/run_all.py --trials 200 --output-dir results/</code>. What each experiment
29
29
  does <em>not</em> exercise is stated too.</p>
30
- <p align="center"><code>v4.0.9</code> — one control plane: <strong>SLM-Mesh</strong> peer coordination · multi-scope memory (personal / shared / global) · profiles · Cache · Compress · 7-layer retrieval · code graph · Entity Explorer · skill evolution · Modes A/B/C · GDPR retention &amp; audit chain · bounded loops — across CLI, MCP, dashboard, the <strong>Claude plugin</strong>, the <strong>Codex add-on</strong>, and documented IDE integrations.<br/>
30
+ <p align="center"><code>v4.0.10</code> — one control plane: <strong>SLM-Mesh</strong> peer coordination · multi-scope memory (personal / shared / global) · profiles · Cache · Compress · 7-layer retrieval · code graph · Entity Explorer · skill evolution · Modes A/B/C · GDPR retention &amp; audit chain · bounded loops — across CLI, MCP, dashboard, the <strong>Claude plugin</strong>, the <strong>Codex add-on</strong>, and documented IDE integrations.<br/>
31
31
  Proxy: <code>slm wrap claude</code> &nbsp;·&nbsp; MCP: add <code>slm_compress</code> to your config &nbsp;·&nbsp; Skill: zero-config</p>
32
32
  <p align="center"><strong>Four public arXiv preprints</strong> · V4: <a href="https://arxiv.org/abs/2608.08253">arXiv:2608.08253</a> · companion archive: <a href="https://zenodo.org/records/21853302">Zenodo 21853302</a> (<a href="https://doi.org/10.5281/zenodo.21853302">DOI 10.5281/zenodo.21853302</a>) · prior preprints: <a href="https://arxiv.org/abs/2603.02240">2603.02240</a> · <a href="https://arxiv.org/abs/2603.14588">2603.14588</a> · <a href="https://arxiv.org/abs/2604.04514">2604.04514</a>.</p>
33
33
 
34
34
  <p align="center">
35
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/v4.0.9-Current_Release-2ea44f?style=for-the-badge&logo=checkmarx&logoColor=white" alt="v4.0.9 — Current Release"/></a>
35
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/v4.0.10-Current_Release-2ea44f?style=for-the-badge&logo=checkmarx&logoColor=white" alt="v4.0.10 — Current Release"/></a>
36
36
  <a href="https://arxiv.org/abs/2608.08253"><img src="https://img.shields.io/badge/arXiv-2608.08253-b31b1b?style=for-the-badge&logo=arxiv&logoColor=white" alt="SuperLocalMemory 4.0 paper on arXiv:2608.08253"/></a>
37
37
  <a href="https://zenodo.org/records/21853302"><img src="https://img.shields.io/badge/Zenodo-10.5281%2Fzenodo.21853302-1682D4?style=for-the-badge&logo=zenodo&logoColor=white" alt="V4 paper on Zenodo: 10.5281/zenodo.21853302"/></a>
38
38
  <a href="https://arxiv.org/abs/2603.14588"><img src="https://img.shields.io/badge/arXiv-2603.14588-b31b1b?style=for-the-badge&logo=arxiv&logoColor=white" alt="arXiv Paper"/></a>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "superlocalmemory",
3
- "version": "4.0.9",
3
+ "version": "4.0.10",
4
4
  "description": "Local-first agent memory with MCP and an agent-native CLI. Documented clients include Claude Code, Cursor, and Windsurf.",
5
5
  "keywords": [
6
6
  "ai-memory",
@@ -15,5 +15,5 @@
15
15
  "mcpServers": "./.mcp.json",
16
16
  "name": "superlocalmemory",
17
17
  "repository": "https://github.com/qualixar/superlocalmemory",
18
- "version": "4.0.9"
18
+ "version": "4.0.10"
19
19
  }
package/plugin/CLAUDE.md CHANGED
@@ -1,4 +1,4 @@
1
- <!-- BEGIN SuperLocalMemory v4.0.9 -->
1
+ <!-- BEGIN SuperLocalMemory v4.0.10 -->
2
2
 
3
3
  ## SuperLocalMemory (SLM) — Agent Rules
4
4
 
@@ -39,6 +39,6 @@ slm-recall · slm-remember · slm-session · slm-status · slm-cache · slm-comp
39
39
  ### Subagents
40
40
  slm-memory-advisor (memory decisions, session hygiene, scope/profile guidance) · slm-optimize-advisor (context compression + KV cache) · slm-governance-advisor (scope/roles/compliance/GDPR)
41
41
 
42
- <!-- END SuperLocalMemory v4.0.9 -->
42
+ <!-- END SuperLocalMemory v4.0.10 -->
43
43
 
44
- SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later
44
+ SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later
@@ -77,4 +77,4 @@ slm-scope · slm-governance · slm-profile · slm-remember · slm-recall
77
77
  # What NOT to do
78
78
  Never session_init twice; never forget without dry-run preview; never store secrets; never bypass role checks; never claim an erasure succeeded without verifying via recall.
79
79
 
80
- SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later
80
+ SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later
@@ -68,4 +68,4 @@ assessment. The gate is the authority.
68
68
 
69
69
  ---
70
70
 
71
- SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later
71
+ SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later
@@ -46,4 +46,4 @@ slm-recall · slm-remember · slm-session · slm-scope · slm-profile · slm-gov
46
46
  # What NOT to do
47
47
  Never session_init twice; never forget dry_run=False without reporting preview; never dump a whole file into remember; never invent a memory; never claim "saved" without success:true / clean CLI exit; never bypass scope or governance restrictions.
48
48
 
49
- SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later
49
+ SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later
@@ -41,4 +41,4 @@ slm-compress · slm-cache · slm-status · slm-profile
41
41
  # What NOT to do
42
42
  Never compress code-for-edit/JSON-to-parse/<500 chars; never store secrets/ccr_ids; never let optimize failure block/alter the task; never claim a specific savings %; never carry ccr_ids across profile switches.
43
43
 
44
- SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later
44
+ SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later
@@ -1 +1 @@
1
- superlocalmemory==4.0.9
1
+ superlocalmemory==4.0.10
@@ -145,4 +145,4 @@ These subcommands control daemon-level cache settings. They do not read or write
145
145
 
146
146
  ---
147
147
 
148
- SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later
148
+ SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later
@@ -147,4 +147,4 @@ Content over 1 MB (1 000 000 bytes UTF-8) is processed but `reversible` is force
147
147
 
148
148
  ---
149
149
 
150
- SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later
150
+ SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later
@@ -245,4 +245,4 @@ Before running any destructive operation (`forget`, `compact_memories`):
245
245
 
246
246
  ---
247
247
 
248
- *SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later*
248
+ *SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later*
@@ -312,4 +312,4 @@ profile. See `slm-profile` for the full profile switching workflow.
312
312
 
313
313
  ---
314
314
 
315
- SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later
315
+ SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later
@@ -96,4 +96,4 @@ paused, name the approval needed; when errored, quote the short detail.
96
96
 
97
97
  ---
98
98
 
99
- SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later
99
+ SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later
@@ -279,4 +279,4 @@ mesh availability.
279
279
 
280
280
  ---
281
281
 
282
- *SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later*
282
+ *SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later*
@@ -146,4 +146,4 @@ Name them differently in your MCP config (e.g. `superlocalmemory-personal` and
146
146
 
147
147
  ---
148
148
 
149
- *SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later*
149
+ *SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later*
@@ -236,4 +236,4 @@ before recalling, then switch back. See `slm-profile` for workspace switching.
236
236
 
237
237
  ---
238
238
 
239
- *SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later*
239
+ *SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later*
@@ -238,4 +238,4 @@ different workspace, use `switch_profile` first. See `slm-profile`.
238
238
 
239
239
  ---
240
240
 
241
- *SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later*
241
+ *SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later*
@@ -173,4 +173,4 @@ to review the impact. See `slm-remember` for the full deletion discipline.
173
173
 
174
174
  ---
175
175
 
176
- *SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later*
176
+ *SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later*
@@ -227,4 +227,4 @@ explicitly and call `recall` with `include_global`/`include_shared` after
227
227
 
228
228
  ---
229
229
 
230
- *SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later*
230
+ *SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later*
@@ -163,4 +163,4 @@ multi-profile setup. To switch the active profile, see `slm-profile`.
163
163
 
164
164
  ---
165
165
 
166
- SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later
166
+ SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later
@@ -129,4 +129,4 @@ When the SLM MCP server is unavailable, use these CLI equivalents:
129
129
  - **slm-optimize-advisor** — context compression and KV cache
130
130
  - **slm-governance-advisor** — scope/role compliance, retention policies, GDPR
131
131
 
132
- SuperLocalMemory v4.0.9 · Qualixar · AGPL-3.0-or-later
132
+ SuperLocalMemory v4.0.10 · Qualixar · AGPL-3.0-or-later
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "superlocalmemory"
3
- version = "4.0.9"
3
+ version = "4.0.10"
4
4
  description = "Local-first agent memory with auditable hybrid retrieval"
5
5
  readme = "README.md"
6
6
  license = "AGPL-3.0-or-later"
@@ -32,7 +32,7 @@ if "OMP_NUM_THREADS" not in os.environ:
32
32
  os.environ["OMP_NUM_THREADS"] = "2"
33
33
  # ---------------------------------------------------------------------------
34
34
 
35
- __version__ = "4.0.9"
35
+ __version__ = "4.0.10"
36
36
 
37
37
  _REQUIRED_VERSIONS = {
38
38
  "sentence_transformers": "5.3.0",
@@ -271,7 +271,7 @@ def _cmd_loop(args: Namespace) -> None:
271
271
 
272
272
 
273
273
  def _cmd_ops(args: Namespace) -> None:
274
- """Wave-3: operational recovery & admin remediation commands."""
274
+ """Operational recovery & admin remediation commands."""
275
275
  from superlocalmemory.cli.ops_cmd import cmd_ops
276
276
  cmd_ops(args)
277
277
 
@@ -416,7 +416,7 @@ def dispatch(args: Namespace) -> None:
416
416
  "loop": _cmd_loop,
417
417
  # V3.8.2 super-help — grouped overview of every command + topics
418
418
  "help": cmd_help,
419
- # Wave-3: operational recovery & admin remediation
419
+ # Operational recovery & admin remediation
420
420
  "ops": _cmd_ops,
421
421
  # V4.0.6: GDPR subject-rights CLI (Art.15/17/20)
422
422
  "gdpr": _cmd_gdpr_dispatch,
@@ -2949,6 +2949,48 @@ def cmd_doctor(args: Namespace) -> None:
2949
2949
  f"enabled [{_surface_str}] {_stats}",
2950
2950
  )
2951
2951
 
2952
+ # Memory answer-ability.
2953
+ #
2954
+ # Every other check here asks whether a component is installed. None of
2955
+ # them asked the only question that matters to the owner: can my memories
2956
+ # actually be found? One machine ran for months with 43.7% of its store
2957
+ # unreachable while doctor reported everything green, because the reachable
2958
+ # share was never counted.
2959
+ memory_health_data: dict | None = None
2960
+ try:
2961
+ from superlocalmemory.core.config import SLMConfig as _HealthCfg
2962
+ from superlocalmemory.core.memory_health import describe, measure
2963
+ _h = measure(_HealthCfg.load().db_path)
2964
+ memory_health_data = {
2965
+ "live_facts": _h.live_facts,
2966
+ "findable_by_meaning": _h.findable_by_meaning,
2967
+ "missing_vector": _h.missing_vector,
2968
+ "withheld_summaries": _h.withheld_summaries,
2969
+ "display_summaries": _h.display_summaries,
2970
+ "hidden_by_forgetting": _h.hidden_by_forgetting,
2971
+ "inconsistently_hidden": _h.inconsistently_hidden,
2972
+ "reachability": round(_h.reachability, 4),
2973
+ "healthy": _h.healthy,
2974
+ "unavailable": list(_h.unavailable),
2975
+ "summary": describe(_h),
2976
+ }
2977
+ _detail = " ".join(describe(_h))
2978
+ if _h.healthy:
2979
+ _check("Memory answer-ability", "PASS", _detail)
2980
+ elif _h.inconsistently_hidden or _h.reachability < 0.9:
2981
+ _check(
2982
+ "Memory answer-ability", "FAIL", _detail,
2983
+ fix="slm restart",
2984
+ )
2985
+ else:
2986
+ _check("Memory answer-ability", "WARN", _detail,
2987
+ fix="slm db reembed --missing-only")
2988
+ except Exception as _mh_exc: # noqa: BLE001 — a report must not break doctor
2989
+ _check(
2990
+ "Memory answer-ability", "WARN",
2991
+ f"could not be measured: {_mh_exc}",
2992
+ )
2993
+
2952
2994
  # Summary
2953
2995
  if use_json:
2954
2996
  from superlocalmemory.cli.json_output import json_print
@@ -2959,6 +3001,7 @@ def cmd_doctor(args: Namespace) -> None:
2959
3001
  json_print("doctor", data={
2960
3002
  "checks": checks,
2961
3003
  "summary": {"passed": passed, "warned": warned, "failed": failed},
3004
+ "memory_health": memory_health_data,
2962
3005
  }, next_actions=next_actions)
2963
3006
  else:
2964
3007
  print(f"\nSummary: {passed} passed, {warned} warnings, {failed} failed")
@@ -977,7 +977,7 @@ def main() -> None:
977
977
 
978
978
  register_summary_parser(sub)
979
979
 
980
- # Wave-3 / V4.0.6: GDPR subject-rights CLI (Art.15/17/20)
980
+ # V4.0.6: GDPR subject-rights CLI (Art.15/17/20)
981
981
  gdpr_p = sub.add_parser(
982
982
  "gdpr",
983
983
  help="GDPR subject rights: status, export (Art.15/20), erase (Art.17), verify",
@@ -1051,7 +1051,7 @@ def main() -> None:
1051
1051
  "--json", action="store_true", help="Output structured JSON",
1052
1052
  )
1053
1053
 
1054
- # Wave-3: operational recovery & admin remediation
1054
+ # Operational recovery & admin remediation
1055
1055
  ops_p = sub.add_parser(
1056
1056
  "ops",
1057
1057
  help="Operational recovery: list failures, resolve stuck ops, check status",
@@ -103,6 +103,14 @@ def _fact_rows(
103
103
  # the pass linked happily in tests and would have linked NOTHING for any
104
104
  # actual user — 3,608 of 3,608 facts filtered out on this machine.
105
105
  sql += "AND COALESCE(archive_status, 'live') != 'archived' "
106
+ # Same reasoning for withheld rows: a model's non-answer has no code
107
+ # entities worth resolving, and a link from one would surface it in the
108
+ # bridge's enrichment text.
109
+ try:
110
+ if memory_db._has_quarantine_column():
111
+ sql += "AND COALESCE(quarantined, 0) = 0 "
112
+ except Exception: # pragma: no cover - helper absent on an unusual manager
113
+ pass
106
114
 
107
115
  params: list[Any] = [profile_id]
108
116
  if since: