superlocalmemory 4.1.4 → 4.1.6
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.
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +68 -0
- package/README.md +3 -3
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/CLAUDE.md +3 -3
- package/plugin/agents/slm-governance-advisor.md +1 -1
- package/plugin/agents/slm-loop-runner.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +1 -1
- package/plugin/agents/slm-optimize-advisor.md +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-cache/SKILL.md +1 -1
- package/plugin/skills/slm-compress/SKILL.md +1 -1
- package/plugin/skills/slm-governance/SKILL.md +1 -1
- package/plugin/skills/slm-graph/SKILL.md +1 -1
- package/plugin/skills/slm-loop/SKILL.md +1 -1
- package/plugin/skills/slm-mesh/SKILL.md +2 -2
- package/plugin/skills/slm-profile/SKILL.md +1 -1
- package/plugin/skills/slm-recall/SKILL.md +1 -1
- package/plugin/skills/slm-remember/SKILL.md +1 -1
- package/plugin/skills/slm-scope/SKILL.md +1 -1
- package/plugin/skills/slm-session/SKILL.md +2 -2
- package/plugin/skills/slm-status/SKILL.md +1 -1
- package/plugin-src/rules/AGENTS.md +1 -1
- package/plugin-src/skills/slm-cache/SKILL.md +1 -1
- package/plugin-src/skills/slm-compress/SKILL.md +1 -1
- package/plugin-src/skills/slm-governance/SKILL.md +1 -1
- package/plugin-src/skills/slm-graph/SKILL.md +1 -1
- package/plugin-src/skills/slm-loop/SKILL.md +1 -1
- package/plugin-src/skills/slm-mesh/SKILL.md +2 -2
- package/plugin-src/skills/slm-profile/SKILL.md +1 -1
- package/plugin-src/skills/slm-recall/SKILL.md +1 -1
- package/plugin-src/skills/slm-remember/SKILL.md +1 -1
- package/plugin-src/skills/slm-scope/SKILL.md +1 -1
- package/plugin-src/skills/slm-session/SKILL.md +2 -2
- package/plugin-src/skills/slm-status/SKILL.md +1 -1
- package/pyproject.toml +1 -1
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/cli/diagnostics_cmd.py +74 -1
- package/src/superlocalmemory/cli/main.py +12 -0
- package/src/superlocalmemory/core/maintenance_scheduler.py +36 -0
- package/src/superlocalmemory/core/remember_runtime.py +12 -1
- package/src/superlocalmemory/encoding/fact_extractor.py +7 -3
- package/src/superlocalmemory/mcp/session_binding.py +7 -1
- package/src/superlocalmemory/reliability/__init__.py +45 -0
- package/src/superlocalmemory/reliability/join_liveness.py +301 -0
- package/src/superlocalmemory/reliability/prior_distance.py +243 -0
- package/src/superlocalmemory/server/routes/backup.py +1 -1
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"license": "AGPL-3.0-or-later",
|
|
22
22
|
"name": "superlocalmemory",
|
|
23
23
|
"source": "./plugin",
|
|
24
|
-
"version": "4.1.
|
|
24
|
+
"version": "4.1.6"
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"author": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"license": "AGPL-3.0-or-later",
|
|
40
40
|
"name": "superlocalmemory-codex",
|
|
41
41
|
"source": "./codex-plugin",
|
|
42
|
-
"version": "4.1.
|
|
42
|
+
"version": "4.1.6"
|
|
43
43
|
}
|
|
44
44
|
]
|
|
45
45
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,74 @@ 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.1.6] — A learning loop that never learns looks identical to one that works
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **The engagement-learning loop settled every outcome at neutral, regardless of
|
|
12
|
+
real usage.** `SESSION_ENV_VARS` checked `SLM_SESSION_ID` and `CLAUDE_SESSION_ID`
|
|
13
|
+
but not `CLAUDE_CODE_SESSION_ID` — the variable Claude Code's own MCP subprocess
|
|
14
|
+
actually sets. Every recall from Claude Code fell through to the synthetic
|
|
15
|
+
`mcp:<agent_id>` id, which step 4 of the resolution ladder deliberately excludes
|
|
16
|
+
from ever matching a pending outcome, by design — a real session should never be
|
|
17
|
+
confused with a placeholder. The result: measured on a live store, 6,013 of 6,039
|
|
18
|
+
settled outcomes carried exactly `reward=0.5`, and all 414 bandit arms held
|
|
19
|
+
`alpha == beta` at every play count. Adding the missing name is what closes the
|
|
20
|
+
loop; two test files that had never isolated session resolution from the ambient
|
|
21
|
+
environment were also hardened, since a live Claude Code session now genuinely
|
|
22
|
+
satisfies the check they'd only ever run without.
|
|
23
|
+
- **Auto-captured facts could assert a date nothing in the source text contained.**
|
|
24
|
+
The fact-extraction system prompt taught relative-date resolution with a fixed
|
|
25
|
+
worked example — `'Yesterday' with session date 2024-01-15 becomes '2024-01-14'`
|
|
26
|
+
— sent identically on every extraction call. A smaller local model under
|
|
27
|
+
uncertainty would echo that literal example date back as if it were extracted
|
|
28
|
+
content, rather than resolving against the real conversation date already present
|
|
29
|
+
in the same prompt. Reproduced live and fixed by removing the concrete date from
|
|
30
|
+
the instruction entirely, teaching the same rule without anything to leak.
|
|
31
|
+
Several existing memories carrying this exact signature — including one date
|
|
32
|
+
claim accessed 28 times — were identified and quarantined.
|
|
33
|
+
- **A store that drifted from M048's classification rule stayed unready for up to a
|
|
34
|
+
full maintenance interval (6 hours) after every restart.** The migration itself
|
|
35
|
+
is idempotent and safe to replay — its own `verify()` docstring says so — but the
|
|
36
|
+
migration runner never replays a completed migration, and the periodic
|
|
37
|
+
maintenance cycle that's supposed to reconcile it only runs once every
|
|
38
|
+
`scheduler_interval_minutes`. A restart landing between cycles reported
|
|
39
|
+
`ready=false, migrations=false` with "automatic replay is disabled" and stayed
|
|
40
|
+
that way until the next scheduled tick. A third staggered one-shot startup timer,
|
|
41
|
+
matching the two that already exist for the same reason (cache GC, graph
|
|
42
|
+
metrics), now runs the same reconciliation pass within seconds of boot instead.
|
|
43
|
+
|
|
44
|
+
## [4.1.5] — Ask whether a feature is working, not whether it is present
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
- **`slm diagnostics reliability`** — two read-only checks that answer a question
|
|
48
|
+
neither a code search nor a call trace can: is a wired feature actually taking
|
|
49
|
+
effect on *your* store?
|
|
50
|
+
- The first asks whether the retrieval learner's posteriors have moved away from
|
|
51
|
+
where they started. A learner that records activity and never shifts its
|
|
52
|
+
distribution looks healthy from every counter it keeps; this reports it plainly.
|
|
53
|
+
- The second asks whether a feature gated on a table or column can run here at
|
|
54
|
+
all, and when it cannot, searches the rest of the store for the data it needs
|
|
55
|
+
and tells you where it is, how much of what is needed it covers, and whether
|
|
56
|
+
connecting it would need a backfill.
|
|
57
|
+
Both are read-only, run in a second, and print what they found in plain language.
|
|
58
|
+
Run it on your own store: it may tell you something you assumed was on is not.
|
|
59
|
+
|
|
60
|
+
### Fixed
|
|
61
|
+
- **A refused write said only "temporarily unavailable."** Three unrelated causes
|
|
62
|
+
produced that one message — a busy write journal, another process holding the
|
|
63
|
+
writer, and a rejected stale write. They call for different responses, and one of
|
|
64
|
+
them is not a fault at all. The cause is now named, so a log line says which
|
|
65
|
+
happened instead of leaving you to guess.
|
|
66
|
+
- **The Codex and Antigravity plugins still advertised 4.1.3 after 4.1.4 shipped.**
|
|
67
|
+
Their manifests are generated from a shared source and were not regenerated during
|
|
68
|
+
that release, so both surfaces reported a version they were not. All four editor
|
|
69
|
+
surfaces are regenerated and checked against the package version on every release.
|
|
70
|
+
- **The benchmark instructions could not be followed.** The documented command named
|
|
71
|
+
a directory that does not exist and an interpreter path pointing outside the
|
|
72
|
+
project, and the environment it suggested carries an older release — so anyone
|
|
73
|
+
following it measured the wrong code. Corrected, with a check that the interpreter
|
|
74
|
+
you choose has the version you mean to test.
|
|
75
|
+
|
|
8
76
|
## [4.1.4] — A busy daemon is not a dead daemon
|
|
9
77
|
|
|
10
78
|
### Fixed
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
</picture>
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
|
-
<h1 align="center">SuperLocalMemory V4.1.
|
|
8
|
+
<h1 align="center">SuperLocalMemory V4.1.6</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.1.
|
|
30
|
+
<p align="center"><code>v4.1.6</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 & 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> · MCP: add <code>slm_compress</code> to your config · 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.1.
|
|
35
|
+
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/v4.1.6-Current_Release-2ea44f?style=for-the-badge&logo=checkmarx&logoColor=white" alt="v4.1.6 — 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
package/plugin/CLAUDE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- BEGIN SuperLocalMemory v4.1.
|
|
1
|
+
<!-- BEGIN SuperLocalMemory v4.1.6 -->
|
|
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.1.
|
|
42
|
+
<!-- END SuperLocalMemory v4.1.6 -->
|
|
43
43
|
|
|
44
|
-
SuperLocalMemory v4.1.
|
|
44
|
+
SuperLocalMemory v4.1.6 · 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.1.
|
|
80
|
+
SuperLocalMemory v4.1.6 · 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.1.
|
|
49
|
+
SuperLocalMemory v4.1.6 · 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.1.
|
|
44
|
+
SuperLocalMemory v4.1.6 · Qualixar · AGPL-3.0-or-later
|
package/plugin/requirements.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
superlocalmemory==4.1.
|
|
1
|
+
superlocalmemory==4.1.6
|
|
@@ -97,7 +97,7 @@ mesh_send(to=target_id, message="I'm starting work on auth/handler.py — please
|
|
|
97
97
|
mesh_send(to="broadcast", message="Deploying to staging in 5 minutes")
|
|
98
98
|
|
|
99
99
|
# Message all sessions working in the same project
|
|
100
|
-
mesh_send(to="project
|
|
100
|
+
mesh_send(to="project:~/myproject", message="Tests are green on main")
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
**4 KB message cap.** For large payloads (diffs, file contents), write to a file
|
|
@@ -279,4 +279,4 @@ mesh availability.
|
|
|
279
279
|
|
|
280
280
|
---
|
|
281
281
|
|
|
282
|
-
*SuperLocalMemory v4.1.
|
|
282
|
+
*SuperLocalMemory v4.1.6 · Qualixar · AGPL-3.0-or-later*
|
|
@@ -52,7 +52,7 @@ recalls or remembers in that session.
|
|
|
52
52
|
|
|
53
53
|
```
|
|
54
54
|
session_init(
|
|
55
|
-
project_path: str = "", # working directory path, e.g. "
|
|
55
|
+
project_path: str = "", # working directory path, e.g. "~/projects/foo"
|
|
56
56
|
query: str = "", # topic override; if omitted, derived from project_path
|
|
57
57
|
max_results: int = 10, # max memories to return (default: 10)
|
|
58
58
|
max_age_days: int = 30, # suppress memories older than N days unless score >= 0.7
|
|
@@ -253,4 +253,4 @@ explicitly and call `recall` with `include_global`/`include_shared` after
|
|
|
253
253
|
|
|
254
254
|
---
|
|
255
255
|
|
|
256
|
-
*SuperLocalMemory v4.1.
|
|
256
|
+
*SuperLocalMemory v4.1.6 · Qualixar · AGPL-3.0-or-later*
|
|
@@ -137,4 +137,4 @@ When the SLM MCP server is unavailable, use these CLI equivalents:
|
|
|
137
137
|
- **slm-optimize-advisor** — context compression and KV cache
|
|
138
138
|
- **slm-governance-advisor** — scope/role compliance, retention policies, GDPR
|
|
139
139
|
|
|
140
|
-
SuperLocalMemory v4.1.
|
|
140
|
+
SuperLocalMemory v4.1.6 · Qualixar · AGPL-3.0-or-later
|
|
@@ -97,7 +97,7 @@ mesh_send(to=target_id, message="I'm starting work on auth/handler.py — please
|
|
|
97
97
|
mesh_send(to="broadcast", message="Deploying to staging in 5 minutes")
|
|
98
98
|
|
|
99
99
|
# Message all sessions working in the same project
|
|
100
|
-
mesh_send(to="project
|
|
100
|
+
mesh_send(to="project:~/myproject", message="Tests are green on main")
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
**4 KB message cap.** For large payloads (diffs, file contents), write to a file
|
|
@@ -279,4 +279,4 @@ mesh availability.
|
|
|
279
279
|
|
|
280
280
|
---
|
|
281
281
|
|
|
282
|
-
*SuperLocalMemory v4.1.
|
|
282
|
+
*SuperLocalMemory v4.1.6 · Qualixar · AGPL-3.0-or-later*
|
|
@@ -52,7 +52,7 @@ recalls or remembers in that session.
|
|
|
52
52
|
|
|
53
53
|
```
|
|
54
54
|
session_init(
|
|
55
|
-
project_path: str = "", # working directory path, e.g. "
|
|
55
|
+
project_path: str = "", # working directory path, e.g. "~/projects/foo"
|
|
56
56
|
query: str = "", # topic override; if omitted, derived from project_path
|
|
57
57
|
max_results: int = 10, # max memories to return (default: 10)
|
|
58
58
|
max_age_days: int = 30, # suppress memories older than N days unless score >= 0.7
|
|
@@ -253,4 +253,4 @@ explicitly and call `recall` with `include_global`/`include_shared` after
|
|
|
253
253
|
|
|
254
254
|
---
|
|
255
255
|
|
|
256
|
-
*SuperLocalMemory v4.1.
|
|
256
|
+
*SuperLocalMemory v4.1.6 · Qualixar · AGPL-3.0-or-later*
|
package/pyproject.toml
CHANGED
|
@@ -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.1.
|
|
35
|
+
__version__ = "4.1.6"
|
|
36
36
|
|
|
37
37
|
_REQUIRED_VERSIONS = {
|
|
38
38
|
"sentence_transformers": "5.3.0",
|
|
@@ -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
|
)
|
|
@@ -77,11 +77,43 @@ class MaintenanceScheduler:
|
|
|
77
77
|
)
|
|
78
78
|
self._initial_metrics_timer.daemon = True
|
|
79
79
|
self._initial_metrics_timer.start()
|
|
80
|
+
# M048's re-read pass is idempotent and safe to replay (see its
|
|
81
|
+
# verify() docstring), but the migration runner never replays a
|
|
82
|
+
# completed migration and the cycle above only reaches it every
|
|
83
|
+
# ``scheduler_interval_minutes`` (360 by default). A store that
|
|
84
|
+
# drifted since the last cycle stays blocking — ready=false,
|
|
85
|
+
# migrations=false — for up to that whole interval after every
|
|
86
|
+
# restart, not because the fix is slow, but because nothing asks
|
|
87
|
+
# for it sooner. Staggered behind the other two so none of the
|
|
88
|
+
# three one-shots contend for the write lock.
|
|
89
|
+
self._initial_reclassify_timer = threading.Timer(
|
|
90
|
+
210.0, self._initial_reclassify_upcoming,
|
|
91
|
+
)
|
|
92
|
+
self._initial_reclassify_timer.daemon = True
|
|
93
|
+
self._initial_reclassify_timer.start()
|
|
80
94
|
logger.info(
|
|
81
95
|
"Maintenance scheduler started (interval=%dm)",
|
|
82
96
|
self._config.forgetting.scheduler_interval_minutes,
|
|
83
97
|
)
|
|
84
98
|
|
|
99
|
+
def _initial_reclassify_upcoming(self) -> None:
|
|
100
|
+
"""Best-effort one-shot M048 re-read shortly after boot.
|
|
101
|
+
|
|
102
|
+
Runs the same pass the periodic cycle already runs (see below) so a
|
|
103
|
+
store that drifted before this restart converges within seconds
|
|
104
|
+
instead of waiting up to a full maintenance interval with the
|
|
105
|
+
daemon reporting itself not ready.
|
|
106
|
+
"""
|
|
107
|
+
if not self._running:
|
|
108
|
+
return
|
|
109
|
+
try:
|
|
110
|
+
from superlocalmemory.storage.migrations import (
|
|
111
|
+
M048_upcoming_holds_only_what_is_upcoming as _reclassify,
|
|
112
|
+
)
|
|
113
|
+
_reclassify.apply(open_connection=self._db.raw_connection)
|
|
114
|
+
except Exception as exc:
|
|
115
|
+
logger.debug("Startup plan re-read skipped: %s", exc)
|
|
116
|
+
|
|
85
117
|
def _initial_cache_gc(self) -> None:
|
|
86
118
|
"""Best-effort one-shot activation-cache GC shortly after boot."""
|
|
87
119
|
if not self._running:
|
|
@@ -133,6 +165,10 @@ class MaintenanceScheduler:
|
|
|
133
165
|
if _metrics_timer is not None:
|
|
134
166
|
_metrics_timer.cancel()
|
|
135
167
|
self._initial_metrics_timer = None
|
|
168
|
+
_reclassify_timer = getattr(self, "_initial_reclassify_timer", None)
|
|
169
|
+
if _reclassify_timer is not None:
|
|
170
|
+
_reclassify_timer.cancel()
|
|
171
|
+
self._initial_reclassify_timer = None
|
|
136
172
|
logger.info("Maintenance scheduler stopped")
|
|
137
173
|
|
|
138
174
|
def _schedule_next(self) -> None:
|
|
@@ -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)
|
|
@@ -134,9 +134,13 @@ _SYSTEM_PROMPT = (
|
|
|
134
134
|
"must name the subject explicitly.\n"
|
|
135
135
|
"2. Each fact must be a COMPLETE, STANDALONE statement understandable "
|
|
136
136
|
"without the original conversation.\n"
|
|
137
|
-
"3. Convert ALL relative time to ABSOLUTE dates when possible
|
|
138
|
-
"
|
|
139
|
-
"
|
|
137
|
+
"3. Convert ALL relative time to ABSOLUTE dates when possible, using "
|
|
138
|
+
"ONLY the conversation date given in the user message below as your "
|
|
139
|
+
"reference point — never invent or assume a date. 'Yesterday' resolves "
|
|
140
|
+
"to one calendar day before that conversation date; 'next month' "
|
|
141
|
+
"resolves to the following calendar month of that same date. If no "
|
|
142
|
+
"conversation date is given, leave the date unresolved rather than "
|
|
143
|
+
"guessing one.\n"
|
|
140
144
|
"4. Resolve ALL coreferences. 'He went there' must become "
|
|
141
145
|
"'[Person name] went to [Place name]'.\n"
|
|
142
146
|
"5. Extract relationships between people when mentioned.\n"
|