superlocalmemory 4.1.3 → 4.1.5

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 (47) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +64 -0
  3. package/README.md +3 -3
  4. package/package.json +1 -1
  5. package/plugin/.claude-plugin/plugin.json +1 -1
  6. package/plugin/CLAUDE.md +3 -3
  7. package/plugin/agents/slm-governance-advisor.md +1 -1
  8. package/plugin/agents/slm-loop-runner.md +1 -1
  9. package/plugin/agents/slm-memory-advisor.md +1 -1
  10. package/plugin/agents/slm-optimize-advisor.md +1 -1
  11. package/plugin/requirements.txt +1 -1
  12. package/plugin/skills/slm-cache/SKILL.md +1 -1
  13. package/plugin/skills/slm-compress/SKILL.md +1 -1
  14. package/plugin/skills/slm-governance/SKILL.md +1 -1
  15. package/plugin/skills/slm-graph/SKILL.md +1 -1
  16. package/plugin/skills/slm-loop/SKILL.md +1 -1
  17. package/plugin/skills/slm-mesh/SKILL.md +2 -2
  18. package/plugin/skills/slm-profile/SKILL.md +1 -1
  19. package/plugin/skills/slm-recall/SKILL.md +1 -1
  20. package/plugin/skills/slm-remember/SKILL.md +1 -1
  21. package/plugin/skills/slm-scope/SKILL.md +1 -1
  22. package/plugin/skills/slm-session/SKILL.md +2 -2
  23. package/plugin/skills/slm-status/SKILL.md +1 -1
  24. package/plugin-src/rules/AGENTS.md +1 -1
  25. package/plugin-src/skills/slm-cache/SKILL.md +1 -1
  26. package/plugin-src/skills/slm-compress/SKILL.md +1 -1
  27. package/plugin-src/skills/slm-governance/SKILL.md +1 -1
  28. package/plugin-src/skills/slm-graph/SKILL.md +1 -1
  29. package/plugin-src/skills/slm-loop/SKILL.md +1 -1
  30. package/plugin-src/skills/slm-mesh/SKILL.md +2 -2
  31. package/plugin-src/skills/slm-profile/SKILL.md +1 -1
  32. package/plugin-src/skills/slm-recall/SKILL.md +1 -1
  33. package/plugin-src/skills/slm-remember/SKILL.md +1 -1
  34. package/plugin-src/skills/slm-scope/SKILL.md +1 -1
  35. package/plugin-src/skills/slm-session/SKILL.md +2 -2
  36. package/plugin-src/skills/slm-status/SKILL.md +1 -1
  37. package/pyproject.toml +1 -1
  38. package/src/superlocalmemory/__init__.py +1 -1
  39. package/src/superlocalmemory/cli/commands.py +8 -2
  40. package/src/superlocalmemory/cli/daemon.py +69 -6
  41. package/src/superlocalmemory/cli/diagnostics_cmd.py +74 -1
  42. package/src/superlocalmemory/cli/main.py +12 -0
  43. package/src/superlocalmemory/core/remember_runtime.py +12 -1
  44. package/src/superlocalmemory/reliability/__init__.py +45 -0
  45. package/src/superlocalmemory/reliability/join_liveness.py +301 -0
  46. package/src/superlocalmemory/reliability/prior_distance.py +243 -0
  47. package/src/superlocalmemory/server/routes/backup.py +1 -1
@@ -10,8 +10,11 @@ from argparse import Namespace
10
10
 
11
11
  def cmd_diagnostics(args: Namespace) -> None:
12
12
  action = getattr(args, "diagnostics_command", None)
13
+ if action == "reliability":
14
+ _cmd_reliability(args)
15
+ return
13
16
  if action != "export":
14
- raise SystemExit("choose a diagnostics subcommand: export")
17
+ raise SystemExit("choose a diagnostics subcommand: export, reliability")
15
18
 
16
19
  from superlocalmemory.cli.json_output import json_print
17
20
  from superlocalmemory.infra.local_diagnostics import default_diagnostics
@@ -25,4 +28,74 @@ def cmd_diagnostics(args: Namespace) -> None:
25
28
  print("No automatic reporting was enabled.")
26
29
 
27
30
 
31
+ def _cmd_reliability(args: Namespace) -> None:
32
+ """Report whether wired mechanisms are effective, not merely present.
33
+
34
+ ``implemented``, ``reachable`` and ``effective`` are three different
35
+ questions. A grep answers the first and an import answers the second; only
36
+ querying this store answers the third. Both checks are read-only.
37
+ """
38
+ from superlocalmemory.cli.json_output import json_print
39
+ from superlocalmemory.infra.data_root import state_path
40
+ from superlocalmemory.reliability import (
41
+ DEFAULT_MIN_OBSERVATIONS,
42
+ check_beta_learners,
43
+ check_schema_guards,
44
+ )
45
+
46
+ floor = getattr(args, "min_observations", None) or DEFAULT_MIN_OBSERVATIONS
47
+ learners = check_beta_learners(state_path("learning.db"), min_observations=floor)
48
+ guards = check_schema_guards(state_path("memory.db"))
49
+
50
+ payload = {
51
+ "learners": [
52
+ {
53
+ "table": v.table,
54
+ "verdict": v.verdict,
55
+ "units": v.units,
56
+ "units_at_prior_mean": v.units_at_prior_mean,
57
+ "units_matching_neutral_identity": v.units_matching_neutral_identity,
58
+ "observations": v.observations,
59
+ "detail": v.detail,
60
+ }
61
+ for v in learners
62
+ ],
63
+ "schema_guards": [
64
+ {
65
+ "name": g.name,
66
+ "verdict": g.verdict,
67
+ "missing": list(g.missing),
68
+ "found_elsewhere": [
69
+ {
70
+ "table": tbl,
71
+ "column": col,
72
+ "populated_rows": n,
73
+ # -1 when coverage over the guarded table could not be
74
+ # measured. Populated rows alone overstate the remedy.
75
+ "coverage_pct_of_guarded_table": cov,
76
+ }
77
+ for tbl, col, n, cov in g.found_elsewhere
78
+ ],
79
+ "detail": g.detail,
80
+ }
81
+ for g in guards
82
+ ],
83
+ }
84
+
85
+ if bool(getattr(args, "json", False)):
86
+ json_print("diagnostics-reliability", data=payload)
87
+ return
88
+
89
+ if not learners and not guards:
90
+ print("No Bayesian learners or schema-guarded paths found in this store.")
91
+ return
92
+
93
+ for v in learners:
94
+ print(f"[{v.verdict}] {v.table}")
95
+ print(f" {v.detail}")
96
+ for g in guards:
97
+ print(f"[{g.verdict}] {g.name}")
98
+ print(f" {g.detail}")
99
+
100
+
28
101
  __all__ = ["cmd_diagnostics"]
@@ -875,6 +875,18 @@ def main() -> None:
875
875
  "export", help="Write a deterministic content-free JSON report",
876
876
  )
877
877
  diagnostics_export.add_argument("destination")
878
+ diagnostics_reliability = diagnostics_sub.add_parser(
879
+ "reliability",
880
+ help=(
881
+ "Ask whether wired mechanisms are actually effective: has each "
882
+ "Bayesian learner moved off its prior, and has each schema-guarded "
883
+ "path ever executed against this store"
884
+ ),
885
+ )
886
+ diagnostics_reliability.add_argument(
887
+ "--min-observations", type=int, default=None,
888
+ help="Observation floor below which an unmoved posterior is not reported",
889
+ )
878
890
  diagnostics_export.add_argument(
879
891
  "--json", action="store_true", help="Output structured JSON",
880
892
  )
@@ -419,8 +419,19 @@ class CanonicalRememberRuntime:
419
419
  OwnershipRequiredError,
420
420
  WriteCoordinatorError,
421
421
  ) as exc:
422
+ # Name which of the three it was. They are not interchangeable and
423
+ # they call for different responses: an unavailable journal is I/O
424
+ # or lock contention and worth retrying, lost ownership means
425
+ # another writer holds the lease, and a coordinator error is the
426
+ # same type the generation fence raises to reject a stale epoch.
427
+ # Collapsing all three into one string makes a spurious fence
428
+ # rejection indistinguishable from a transient disk stall, for the
429
+ # operator reading a log and for a caller deciding whether to
430
+ # retry. Only the class name is included: it is the whole of the
431
+ # discriminating information and carries no request content.
422
432
  raise CanonicalRememberUnavailable(
423
- "canonical remember is temporarily unavailable"
433
+ "canonical remember is temporarily unavailable "
434
+ f"({type(exc).__name__})"
424
435
  ) from exc
425
436
  finally:
426
437
  clear_admission_epoch(request.profile_id, request.idempotency_key)
@@ -0,0 +1,45 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
+ # Part of SuperLocalMemory | https://qualixar.com | https://varunpratap.com
4
+
5
+ """Checks that ask whether a mechanism is *effective*, not merely present.
6
+
7
+ Three questions get confused in a system this size:
8
+
9
+ * **Implemented** — the code exists. A grep answers this.
10
+ * **Reachable** — something calls it. A call-graph trace answers this.
11
+ * **Effective** — it has actually changed an outcome against real data.
12
+ **Neither of the above answers this.** Only querying the store does.
13
+
14
+ A mechanism can pass the first two questions for months and fail the third
15
+ silently: a learner whose reward channel emits a constant still records plays,
16
+ and a conditional path guarded by a missing column still appears in coverage.
17
+ Nothing raises, nothing logs, and every file is present.
18
+
19
+ The two checks here answer the third question directly.
20
+
21
+ * :mod:`.prior_distance` — has a Bayesian learner's posterior actually moved
22
+ away from its prior?
23
+ * :mod:`.join_liveness` — has a schema-guarded code path ever executed against
24
+ this store, and if not, which requirement is missing?
25
+
26
+ Both are read-only, both are fail-soft, and neither is on a hot path.
27
+ """
28
+
29
+ from superlocalmemory.reliability.join_liveness import (
30
+ GuardVerdict,
31
+ check_schema_guards,
32
+ )
33
+ from superlocalmemory.reliability.prior_distance import (
34
+ DEFAULT_MIN_OBSERVATIONS,
35
+ LearnerVerdict,
36
+ check_beta_learners,
37
+ )
38
+
39
+ __all__ = [
40
+ "DEFAULT_MIN_OBSERVATIONS",
41
+ "GuardVerdict",
42
+ "LearnerVerdict",
43
+ "check_beta_learners",
44
+ "check_schema_guards",
45
+ ]
@@ -0,0 +1,301 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under AGPL-3.0-or-later - see LICENSE file
3
+ # Part of SuperLocalMemory | https://qualixar.com | https://varunpratap.com
4
+
5
+ """Can a schema-guarded code path run against this store, and has it had the data?
6
+
7
+ A note on what this proves, because the honest scope is narrower than the
8
+ convenient phrasing. The check reads the schema: it establishes whether the
9
+ guard's requirements are present *now*, and therefore whether the path is
10
+ reachable at all. It does not read an execution history, so a satisfied guard
11
+ means "this would execute", not "this has executed". The inference to "never
12
+ executed" is sound only in the other direction -- if a required column is absent
13
+ from a store that has been in service, the guarded path cannot have run against
14
+ it -- and that is the direction the findings rely on.
15
+
16
+ Some features are wired behind a guard that asks the schema a question before
17
+ doing any work — "is this table here, does that column exist" — and fall back
18
+ silently when the answer is no. The fallback is correct behaviour: it is what
19
+ keeps an old store openable. But it means a feature can be implemented, called
20
+ on the hot path, and covered by tests, while never once executing against real
21
+ data. Static analysis passes. A call-graph trace passes. Coverage passes. The
22
+ guard returns ``False`` and the feature is arithmetically absent.
23
+
24
+ Two things make that failure hard to see from inside the code:
25
+
26
+ 1. **The guard is doing its job.** There is no error to raise. Falling back is
27
+ the designed response to a missing column.
28
+ 2. **The fallback is often a neutral value**, which composes into an identity.
29
+ A decay-rate multiplier that falls back to a trust of 1.0 collapses
30
+ ``lambda * (1 + kappa * (1 - trust))`` to ``lambda`` — the feature is on, and
31
+ it computes exactly what having no feature would compute.
32
+
33
+ So this check does two things a plain schema assertion does not. It records
34
+ whether each named guard **passes right now**, and when a guard fails it looks
35
+ for the required data **elsewhere in the store** — because the common case is
36
+ not that the data is missing, it is that the guard is asking the wrong table.
37
+
38
+ A guard reported as ``SATISFIED_ELSEWHERE`` is the most actionable outcome
39
+ available: the feature is one re-keyed join away from working, and no backfill
40
+ or migration is required.
41
+ """
42
+
43
+ from __future__ import annotations
44
+
45
+ import logging
46
+ import sqlite3
47
+ from dataclasses import dataclass, field
48
+ from typing import Any
49
+
50
+ logger = logging.getLogger("superlocalmemory.reliability.join_liveness")
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class Requirement:
55
+ """One schema object a guard needs: a table, optionally a column on it."""
56
+
57
+ table: str
58
+ column: str | None = None
59
+
60
+ def __str__(self) -> str:
61
+ return f"{self.table}.{self.column}" if self.column else self.table
62
+
63
+
64
+ @dataclass(frozen=True)
65
+ class Guard:
66
+ """A named conditional path and the schema it requires to execute."""
67
+
68
+ name: str
69
+ describes: str
70
+ requires: tuple[Requirement, ...]
71
+ #: What the feature computes when the guard fails, in words. Recording this
72
+ #: is the difference between "a feature is off" and "a feature is off and
73
+ #: indistinguishable from not having it".
74
+ fallback_behaviour: str
75
+
76
+
77
+ @dataclass(frozen=True)
78
+ class GuardVerdict:
79
+ name: str
80
+ describes: str
81
+ #: LIVE = requirements present, so the path is reachable now. DEAD / #: SATISFIED_ELSEWHERE = a requirement is absent, so the path cannot have run
82
+ #: against this store. LIVE is NOT evidence of past execution.
83
+ verdict: str
84
+ missing: tuple[str, ...] = field(default=())
85
+ found_elsewhere: tuple[tuple[str, str, int, float], ...] = field(default=())
86
+ detail: str = ""
87
+
88
+ @property
89
+ def is_live(self) -> bool:
90
+ return self.verdict == "LIVE"
91
+
92
+
93
+ #: Guards worth reporting on. Each entry names a real conditional in the code,
94
+ #: so that a reader can go from this list to the line that asks the question.
95
+ GUARDS: tuple[Guard, ...] = (
96
+ Guard(
97
+ name="trust_weighted_forgetting",
98
+ describes=(
99
+ "learning/forgetting_scheduler.py::_has_trust_tables — gates the "
100
+ "per-fact trust lookup that modulates the decay rate"
101
+ ),
102
+ requires=(
103
+ Requirement("trust_scores"),
104
+ Requirement("atomic_facts", "created_by"),
105
+ ),
106
+ fallback_behaviour=(
107
+ "every fact takes trust = 1.0, so lambda_eff collapses to "
108
+ "lambda_base and the decay rate is identical to no trust weighting"
109
+ ),
110
+ ),
111
+ )
112
+
113
+
114
+ def _table_exists(conn: sqlite3.Connection, table: str) -> bool:
115
+ return (
116
+ conn.execute(
117
+ "SELECT 1 FROM sqlite_master WHERE type='table' AND name=? LIMIT 1",
118
+ (table,),
119
+ ).fetchone()
120
+ is not None
121
+ )
122
+
123
+
124
+ def _columns(conn: sqlite3.Connection, table: str) -> set[str]:
125
+ try:
126
+ return {r[1] for r in conn.execute(f'PRAGMA table_info("{table}")')}
127
+ except sqlite3.Error:
128
+ return set()
129
+
130
+
131
+ def _satisfied(conn: sqlite3.Connection, requirement: Requirement) -> bool:
132
+ if not _table_exists(conn, requirement.table):
133
+ return False
134
+ if requirement.column is None:
135
+ return True
136
+ return requirement.column in _columns(conn, requirement.table)
137
+
138
+
139
+ def _look_elsewhere(
140
+ conn: sqlite3.Connection,
141
+ column: str,
142
+ *,
143
+ join_target: str | None = None,
144
+ join_key: str = "fact_id",
145
+ ) -> list[tuple[str, str, int]]:
146
+ """Find other tables carrying ``column``, with a populated-row count.
147
+
148
+ This is the part that turns a failed guard into a fix. A column the guard
149
+ could not find on its own table is often present on a neighbouring one,
150
+ already populated.
151
+
152
+ **A populated count is not coverage, and reporting it alone overstates the
153
+ remedy.** A provenance-style table can carry a value on every one of its own
154
+ rows while describing only part of the set the join needs: rows can be
155
+ missing for older entities entirely. When ``join_target`` is given, the
156
+ coverage fraction over that table is measured and returned, because the
157
+ honest question is not "does this column exist somewhere" but "how much of
158
+ what the guard needs would the re-keyed join actually resolve".
159
+ """
160
+ out: list[tuple[str, str, int]] = []
161
+ tables = [
162
+ r[0]
163
+ for r in conn.execute(
164
+ "SELECT name FROM sqlite_master WHERE type='table' "
165
+ "AND name NOT LIKE 'sqlite_%'",
166
+ )
167
+ ]
168
+ for table in tables:
169
+ if column not in _columns(conn, table):
170
+ continue
171
+ try:
172
+ populated = conn.execute(
173
+ f'SELECT COUNT(*) FROM "{table}" '
174
+ f'WHERE "{column}" IS NOT NULL AND TRIM("{column}") <> \'\'',
175
+ ).fetchone()[0]
176
+ except sqlite3.Error:
177
+ populated = 0
178
+ covered = -1
179
+ if join_target and join_target != table:
180
+ try:
181
+ total = conn.execute(
182
+ f'SELECT COUNT(*) FROM "{join_target}"',
183
+ ).fetchone()[0]
184
+ if total:
185
+ hit = conn.execute(
186
+ f'SELECT COUNT(*) FROM "{join_target}" t WHERE EXISTS ('
187
+ f' SELECT 1 FROM "{table}" s WHERE s."{join_key}" = t."{join_key}"'
188
+ f' AND s."{column}" IS NOT NULL AND TRIM(s."{column}") <> \'\')',
189
+ ).fetchone()[0]
190
+ covered = round(100.0 * hit / total, 1)
191
+ except sqlite3.Error:
192
+ covered = -1
193
+ out.append((table, column, int(populated), covered))
194
+ return out
195
+
196
+
197
+ def _evaluate(conn: sqlite3.Connection, guard: Guard) -> GuardVerdict:
198
+ missing = [str(r) for r in guard.requires if not _satisfied(conn, r)]
199
+ if not missing:
200
+ return GuardVerdict(
201
+ name=guard.name,
202
+ describes=guard.describes,
203
+ verdict="LIVE",
204
+ detail=(
205
+ "Every requirement is present, so the guarded path is reachable "
206
+ "on this store. This is schema evidence, not execution history: "
207
+ "it does not establish that the path has ever actually run."
208
+ ),
209
+ )
210
+
211
+ elsewhere: list[tuple[str, str, int, float]] = []
212
+ for requirement in guard.requires:
213
+ if requirement.column and not _satisfied(conn, requirement):
214
+ for found in _look_elsewhere(
215
+ conn, requirement.column, join_target=requirement.table,
216
+ ):
217
+ if found[0] != requirement.table:
218
+ elsewhere.append(found)
219
+
220
+ populated = [e for e in elsewhere if e[2] > 0]
221
+ if populated:
222
+ # Rank by coverage of the guarded table, falling back to row count only
223
+ # when coverage could not be measured. Picking the largest table instead
224
+ # would recommend a 4,000-row partial source over a smaller complete one.
225
+ best = max(populated, key=lambda e: (e[3], e[2]))
226
+ cov = best[3]
227
+ if cov < 0:
228
+ remedy = (
229
+ f"Re-keying the join onto that table would make the path "
230
+ f"executable; coverage over the guarded table could not be "
231
+ f"measured here, so confirm it before relying on the remedy."
232
+ )
233
+ elif cov >= 99.5:
234
+ remedy = (
235
+ f"That table covers {cov}% of the rows the guard needs, so "
236
+ f"re-keying the join makes the path live without a backfill."
237
+ )
238
+ else:
239
+ remedy = (
240
+ f"That table covers only {cov}% of the rows the guard needs. "
241
+ f"Re-keying the join makes the path executable for those rows "
242
+ f"and leaves the remainder on the same fallback, so this is a "
243
+ f"partial remedy and a backfill decision, not a free fix."
244
+ )
245
+ return GuardVerdict(
246
+ name=guard.name,
247
+ describes=guard.describes,
248
+ verdict="SATISFIED_ELSEWHERE",
249
+ missing=tuple(missing),
250
+ found_elsewhere=tuple(elsewhere),
251
+ detail=(
252
+ f"The guard requires {', '.join(missing)}, which is absent, so "
253
+ f"the path has never executed against this store — "
254
+ f"{guard.fallback_behaviour}. The data it needs is present on "
255
+ f"{best[0]}.{best[1]} with {best[2]} populated rows. {remedy}"
256
+ ),
257
+ )
258
+
259
+ return GuardVerdict(
260
+ name=guard.name,
261
+ describes=guard.describes,
262
+ verdict="DEAD",
263
+ missing=tuple(missing),
264
+ detail=(
265
+ f"The guard requires {', '.join(missing)}, which is absent from "
266
+ f"this store and not carried by any other table, so the path has "
267
+ f"never executed — {guard.fallback_behaviour}."
268
+ ),
269
+ )
270
+
271
+
272
+ def check_schema_guards(
273
+ memory_db: Any, *, guards: tuple[Guard, ...] = GUARDS,
274
+ ) -> list[GuardVerdict]:
275
+ """Report, per registered guard, whether its path can run on this store.
276
+
277
+ ``memory_db`` may be a path or an open connection. Read-only, and fail-soft:
278
+ an error yields an empty list, because a diagnostic must never be the reason
279
+ something breaks.
280
+ """
281
+ owns_connection = not isinstance(memory_db, sqlite3.Connection)
282
+ conn: sqlite3.Connection | None = None
283
+ try:
284
+ conn = (
285
+ sqlite3.connect(f"file:{memory_db}?mode=ro", uri=True)
286
+ if owns_connection
287
+ else memory_db
288
+ )
289
+ return [_evaluate(conn, guard) for guard in guards]
290
+ except Exception:
291
+ logger.debug("join-liveness check skipped", exc_info=True)
292
+ return []
293
+ finally:
294
+ if owns_connection and conn is not None:
295
+ try:
296
+ conn.close()
297
+ except Exception:
298
+ pass
299
+
300
+
301
+ __all__ = ["GUARDS", "Guard", "GuardVerdict", "Requirement", "check_schema_guards"]