superlocalmemory 4.1.8 → 4.1.9

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 (41) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +19 -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 +1 -1
  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 +1 -1
  23. package/plugin/skills/slm-status/SKILL.md +1 -1
  24. package/plugin-src/agents/slm-memory-advisor.md +1 -1
  25. package/plugin-src/agents/slm-optimize-advisor.md +1 -1
  26. package/plugin-src/rules/AGENTS.md +1 -1
  27. package/plugin-src/skills/slm-cache/SKILL.md +1 -1
  28. package/plugin-src/skills/slm-compress/SKILL.md +1 -1
  29. package/plugin-src/skills/slm-governance/SKILL.md +1 -1
  30. package/plugin-src/skills/slm-graph/SKILL.md +1 -1
  31. package/plugin-src/skills/slm-loop/SKILL.md +1 -1
  32. package/plugin-src/skills/slm-mesh/SKILL.md +1 -1
  33. package/plugin-src/skills/slm-profile/SKILL.md +1 -1
  34. package/plugin-src/skills/slm-recall/SKILL.md +1 -1
  35. package/plugin-src/skills/slm-remember/SKILL.md +1 -1
  36. package/plugin-src/skills/slm-scope/SKILL.md +1 -1
  37. package/plugin-src/skills/slm-session/SKILL.md +1 -1
  38. package/plugin-src/skills/slm-status/SKILL.md +1 -1
  39. package/pyproject.toml +1 -1
  40. package/src/superlocalmemory/__init__.py +1 -1
  41. package/src/superlocalmemory/learning/reward.py +39 -4
@@ -21,7 +21,7 @@
21
21
  "license": "AGPL-3.0-or-later",
22
22
  "name": "superlocalmemory",
23
23
  "source": "./plugin",
24
- "version": "4.1.8"
24
+ "version": "4.1.9"
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.8"
42
+ "version": "4.1.9"
43
43
  }
44
44
  ]
45
45
  }
package/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ 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.9] — Silence is not a verdict
9
+
10
+ ### Fixed
11
+ - **A recall nobody engaged with was scored as an average one.** How useful a recall
12
+ was is worked out from what happened next — whether a memory was quoted, edited, or
13
+ asked about again. When nothing at all had happened, that calculation still landed
14
+ on a middling score, and it was saved as if it had been measured. That is not a
15
+ blank: downstream it reads as mild approval, nudging results toward whatever was
16
+ shown and making the system steadily more certain it has no preference either way.
17
+ Recalls with nothing observed are now closed without a score, which is what the
18
+ batch pass already did — the two paths simply disagreed. On a store in daily use,
19
+ every one of 492 saved scores turned out to be this value, so nothing recorded there
20
+ reflected real use.
21
+ - **Signals recorded as explicitly absent were counted as present.** A recall whose
22
+ signals were all recorded as "did not happen" was treated as having something to
23
+ say, and scored, because the record existed even though everything in it was empty.
24
+ Both paths now ask whether anything actually happened rather than whether a record
25
+ was written.
26
+
8
27
  ## [4.1.8] — The same question, answered the same way
9
28
 
10
29
  ### 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>
8
+ <h1 align="center">SuperLocalMemory V4.1.9</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.8</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.1.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/>
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.1.8-Current_Release-2ea44f?style=for-the-badge&logo=checkmarx&logoColor=white" alt="v4.1.8 — Current Release"/></a>
35
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/v4.1.9-Current_Release-2ea44f?style=for-the-badge&logo=checkmarx&logoColor=white" alt="v4.1.9 — 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.1.8",
3
+ "version": "4.1.9",
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.1.8"
18
+ "version": "4.1.9"
19
19
  }
package/plugin/CLAUDE.md CHANGED
@@ -1,4 +1,4 @@
1
- <!-- BEGIN SuperLocalMemory v4.1.8 -->
1
+ <!-- BEGIN SuperLocalMemory v4.1.9 -->
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.8 -->
42
+ <!-- END SuperLocalMemory v4.1.9 -->
43
43
 
44
- SuperLocalMemory v4.1.8 · Qualixar · AGPL-3.0-or-later
44
+ SuperLocalMemory v4.1.9 · 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.8 · Qualixar · AGPL-3.0-or-later
80
+ SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later
71
+ SuperLocalMemory v4.1.9 · 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.8 · Qualixar · AGPL-3.0-or-later
49
+ SuperLocalMemory v4.1.9 · 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.8 · Qualixar · AGPL-3.0-or-later
44
+ SuperLocalMemory v4.1.9 · Qualixar · AGPL-3.0-or-later
@@ -1 +1 @@
1
- superlocalmemory==4.1.8
1
+ superlocalmemory==4.1.9
@@ -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.1.8 · Qualixar · AGPL-3.0-or-later
148
+ SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later
150
+ SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later*
248
+ *SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later
315
+ SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later
99
+ SuperLocalMemory v4.1.9 · Qualixar · AGPL-3.0-or-later
@@ -279,4 +279,4 @@ mesh availability.
279
279
 
280
280
  ---
281
281
 
282
- *SuperLocalMemory v4.1.8 · Qualixar · AGPL-3.0-or-later*
282
+ *SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later*
149
+ *SuperLocalMemory v4.1.9 · Qualixar · AGPL-3.0-or-later*
@@ -323,4 +323,4 @@ before recalling, then switch back. See `slm-profile` for workspace switching.
323
323
 
324
324
  ---
325
325
 
326
- *SuperLocalMemory v4.1.8 · Qualixar · AGPL-3.0-or-later*
326
+ *SuperLocalMemory v4.1.9 · Qualixar · AGPL-3.0-or-later*
@@ -270,4 +270,4 @@ different workspace, use `switch_profile` first. See `slm-profile`.
270
270
 
271
271
  ---
272
272
 
273
- *SuperLocalMemory v4.1.8 · Qualixar · AGPL-3.0-or-later*
273
+ *SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later*
176
+ *SuperLocalMemory v4.1.9 · Qualixar · AGPL-3.0-or-later*
@@ -253,4 +253,4 @@ explicitly and call `recall` with `include_global`/`include_shared` after
253
253
 
254
254
  ---
255
255
 
256
- *SuperLocalMemory v4.1.8 · Qualixar · AGPL-3.0-or-later*
256
+ *SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later
166
+ SuperLocalMemory v4.1.9 · 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.8 · Qualixar · AGPL-3.0-or-later
49
+ SuperLocalMemory v4.1.9 · 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.8 · Qualixar · AGPL-3.0-or-later
44
+ SuperLocalMemory v4.1.9 · 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.8 · Qualixar · AGPL-3.0-or-later
140
+ SuperLocalMemory v4.1.9 · Qualixar · AGPL-3.0-or-later
@@ -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.1.8 · Qualixar · AGPL-3.0-or-later
148
+ SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later
150
+ SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later*
248
+ *SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later
315
+ SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later
99
+ SuperLocalMemory v4.1.9 · Qualixar · AGPL-3.0-or-later
@@ -279,4 +279,4 @@ mesh availability.
279
279
 
280
280
  ---
281
281
 
282
- *SuperLocalMemory v4.1.8 · Qualixar · AGPL-3.0-or-later*
282
+ *SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later*
149
+ *SuperLocalMemory v4.1.9 · Qualixar · AGPL-3.0-or-later*
@@ -323,4 +323,4 @@ before recalling, then switch back. See `slm-profile` for workspace switching.
323
323
 
324
324
  ---
325
325
 
326
- *SuperLocalMemory v4.1.8 · Qualixar · AGPL-3.0-or-later*
326
+ *SuperLocalMemory v4.1.9 · Qualixar · AGPL-3.0-or-later*
@@ -270,4 +270,4 @@ different workspace, use `switch_profile` first. See `slm-profile`.
270
270
 
271
271
  ---
272
272
 
273
- *SuperLocalMemory v4.1.8 · Qualixar · AGPL-3.0-or-later*
273
+ *SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later*
176
+ *SuperLocalMemory v4.1.9 · Qualixar · AGPL-3.0-or-later*
@@ -253,4 +253,4 @@ explicitly and call `recall` with `include_global`/`include_shared` after
253
253
 
254
254
  ---
255
255
 
256
- *SuperLocalMemory v4.1.8 · Qualixar · AGPL-3.0-or-later*
256
+ *SuperLocalMemory v4.1.9 · 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.1.8 · Qualixar · AGPL-3.0-or-later
166
+ SuperLocalMemory v4.1.9 · Qualixar · AGPL-3.0-or-later
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "superlocalmemory"
3
- version = "4.1.8"
3
+ version = "4.1.9"
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.1.8"
35
+ __version__ = "4.1.9"
36
36
 
37
37
  _REQUIRED_VERSIONS = {
38
38
  "sentence_transformers": "5.3.0",
@@ -113,6 +113,30 @@ _MAX_CLOCK_SKEW_MS: Final[int] = 60_000
113
113
  # ---------------------------------------------------------------------------
114
114
 
115
115
 
116
+ #: Dwell below this is not engagement; the label formula uses the same bound.
117
+ _DWELL_THRESHOLD_MS: Final[int] = 2000
118
+
119
+
120
+ def _carries_evidence(signals: Mapping[str, object]) -> bool:
121
+ """Did anything actually happen after this recall?
122
+
123
+ ``_compute_label`` is built as ``0.5 + bonuses - penalties``, so a signal
124
+ set with nothing in it evaluates to exactly one half. That number is not an
125
+ absence of judgement, it is a confident one: it becomes a mid-strength
126
+ positive training label and a Beta update that tightens a posterior around
127
+ its prior. An empty dict and a dict of falsy values mean the same thing here
128
+ and must both be refused.
129
+ """
130
+ if not signals:
131
+ return False
132
+ if signals.get("cite") or signals.get("edit") or signals.get("requery"):
133
+ return True
134
+ try:
135
+ return int(signals.get("dwell_ms", 0) or 0) >= _DWELL_THRESHOLD_MS
136
+ except (TypeError, ValueError):
137
+ return False
138
+
139
+
116
140
  def _compute_label(signals: Mapping[str, object]) -> float:
117
141
  """Deterministic label in ``[0.0, 1.0]`` per the manifest A.1 formula.
118
142
 
@@ -141,8 +165,9 @@ def _compute_label(signals: Mapping[str, object]) -> float:
141
165
  dwell_int = 0
142
166
 
143
167
  dwell_bonus = 0.0
144
- if dwell_int >= 2000:
145
- dwell_bonus = min(0.15, 0.05 + (dwell_int - 2000) / 80_000.0)
168
+ if dwell_int >= _DWELL_THRESHOLD_MS:
169
+ dwell_bonus = min(
170
+ 0.15, 0.05 + (dwell_int - _DWELL_THRESHOLD_MS) / 80_000.0)
146
171
 
147
172
  label = (
148
173
  0.5
@@ -585,9 +610,19 @@ class EngagementRewardModel:
585
610
  signals = json.loads(pending["signals_json"] or "{}")
586
611
  except json.JSONDecodeError: # pragma: no cover — defensive
587
612
  signals = {}
588
- reward = _compute_label(signals)
589
613
  now_ms = self._clock_ms()
590
614
  timestamp_iso = _iso_from_ms(now_ms)
615
+ if not _carries_evidence(signals):
616
+ # Finalize so it stops being rescanned, but record no
617
+ # outcome: nothing was observed, and a score here would
618
+ # be invented rather than measured. Mirrors reap_stale.
619
+ conn.execute(
620
+ "UPDATE pending_outcomes "
621
+ "SET status = 'settled' WHERE outcome_id = ?",
622
+ (outcome_id,),
623
+ )
624
+ return _FALLBACK_REWARD
625
+ reward = _compute_label(signals)
591
626
 
592
627
  # NOTE: Split INSERT across lines so the Stage-5b CI
593
628
  # gate's single-line regex (LLD-00 §13) does not fire.
@@ -746,7 +781,7 @@ class EngagementRewardModel:
746
781
  #
747
782
  # The row is still finalized, so it stops being rescanned; it is
748
783
  # simply not turned into evidence it never was.
749
- if not signals:
784
+ if not _carries_evidence(signals):
750
785
  settle_ids.append(row["outcome_id"])
751
786
  continue
752
787
  reward = _compute_label(signals)