memtether 0.1.0__tar.gz

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 (153) hide show
  1. memtether-0.1.0/CHANGELOG.md +1666 -0
  2. memtether-0.1.0/INSTALL.md +131 -0
  3. memtether-0.1.0/LICENSE +202 -0
  4. memtether-0.1.0/MANIFEST.in +26 -0
  5. memtether-0.1.0/NOTICE +96 -0
  6. memtether-0.1.0/PKG-INFO +817 -0
  7. memtether-0.1.0/README.md +778 -0
  8. memtether-0.1.0/api_server.py +433 -0
  9. memtether-0.1.0/cli_out.py +52 -0
  10. memtether-0.1.0/clients/__init__.py +46 -0
  11. memtether-0.1.0/clients/base.py +520 -0
  12. memtether-0.1.0/clients/jsonc.py +435 -0
  13. memtether-0.1.0/clients/local.py +403 -0
  14. memtether-0.1.0/clients/standard.py +551 -0
  15. memtether-0.1.0/clients/trust.py +162 -0
  16. memtether-0.1.0/consolidation.py +100 -0
  17. memtether-0.1.0/embed_local.py +293 -0
  18. memtether-0.1.0/extract.py +186 -0
  19. memtether-0.1.0/gateway.py +2657 -0
  20. memtether-0.1.0/governance.py +993 -0
  21. memtether-0.1.0/hubguard.py +984 -0
  22. memtether-0.1.0/integrations/__init__.py +0 -0
  23. memtether-0.1.0/integrations/auto_onboard.py +497 -0
  24. memtether-0.1.0/integrations/autogen_memory.py +41 -0
  25. memtether-0.1.0/integrations/langchain_memory.py +152 -0
  26. memtether-0.1.0/make_jury.py +79 -0
  27. memtether-0.1.0/mcp_server.py +1016 -0
  28. memtether-0.1.0/mem0_exchange.py +197 -0
  29. memtether-0.1.0/memory_court.py +211 -0
  30. memtether-0.1.0/memsearch.py +2268 -0
  31. memtether-0.1.0/memtether.egg-info/PKG-INFO +817 -0
  32. memtether-0.1.0/memtether.egg-info/SOURCES.txt +151 -0
  33. memtether-0.1.0/memtether.egg-info/dependency_links.txt +1 -0
  34. memtether-0.1.0/memtether.egg-info/entry_points.txt +3 -0
  35. memtether-0.1.0/memtether.egg-info/requires.txt +15 -0
  36. memtether-0.1.0/memtether.egg-info/top_level.txt +28 -0
  37. memtether-0.1.0/memtether.py +1521 -0
  38. memtether-0.1.0/memtether_assets/__init__.py +2 -0
  39. memtether-0.1.0/memtether_assets/dashboard.html +303 -0
  40. memtether-0.1.0/memtether_assets/viewer.html +207 -0
  41. memtether-0.1.0/memtether_exchange.py +584 -0
  42. memtether-0.1.0/memtether_paths.py +103 -0
  43. memtether-0.1.0/memtether_pipeline.py +140 -0
  44. memtether-0.1.0/predicates.py +312 -0
  45. memtether-0.1.0/pyproject.toml +146 -0
  46. memtether-0.1.0/rerank.py +113 -0
  47. memtether-0.1.0/scripts/__init__.py +21 -0
  48. memtether-0.1.0/scripts/bench_memtether_adapter.py +137 -0
  49. memtether-0.1.0/scripts/check_packaging.py +299 -0
  50. memtether-0.1.0/scripts/cli_out.py +52 -0
  51. memtether-0.1.0/scripts/demo_exchange_5adapters.py +174 -0
  52. memtether-0.1.0/scripts/demo_exchange_conflict.py +155 -0
  53. memtether-0.1.0/scripts/demo_export_roundtrip.py +103 -0
  54. memtether-0.1.0/scripts/demo_federation.py +91 -0
  55. memtether-0.1.0/scripts/demo_gateway.py +52 -0
  56. memtether-0.1.0/scripts/download_models.py +165 -0
  57. memtether-0.1.0/scripts/make_demo_db.py +445 -0
  58. memtether-0.1.0/scripts/make_jury_report.py +104 -0
  59. memtether-0.1.0/scripts/memtether_export.py +172 -0
  60. memtether-0.1.0/scripts/memtether_guard.py +77 -0
  61. memtether-0.1.0/scripts/scan_docs.py +152 -0
  62. memtether-0.1.0/scripts/scan_history_leaks.py +211 -0
  63. memtether-0.1.0/scripts/scan_leaks.py +323 -0
  64. memtether-0.1.0/scripts/selfcheck.py +1088 -0
  65. memtether-0.1.0/scripts/test_pii_roundtrip.py +70 -0
  66. memtether-0.1.0/scripts/update_bench_page.py +76 -0
  67. memtether-0.1.0/scripts/watch.py +165 -0
  68. memtether-0.1.0/scripts/zh_calibration.py +113 -0
  69. memtether-0.1.0/setup.cfg +4 -0
  70. memtether-0.1.0/skills/__init__.py +0 -0
  71. memtether-0.1.0/skills/agent-memory/SKILL.md +58 -0
  72. memtether-0.1.0/skills/agent-memory-court/SKILL.md +61 -0
  73. memtether-0.1.0/skills/agent-memory-recall/SKILL.md +55 -0
  74. memtether-0.1.0/tests/test_absorb_semantic.py +136 -0
  75. memtether-0.1.0/tests/test_adapter_concurrent_writes.py +78 -0
  76. memtether-0.1.0/tests/test_adapters.py +146 -0
  77. memtether-0.1.0/tests/test_afag.py +36 -0
  78. memtether-0.1.0/tests/test_all_adapters_write.py +202 -0
  79. memtether-0.1.0/tests/test_api_server.py +84 -0
  80. memtether-0.1.0/tests/test_auto_onboard.py +90 -0
  81. memtether-0.1.0/tests/test_auto_onboard_integration.py +107 -0
  82. memtether-0.1.0/tests/test_auto_onboard_integration2.py +84 -0
  83. memtether-0.1.0/tests/test_cli_correct_retire.py +82 -0
  84. memtether-0.1.0/tests/test_conflicts_qvalue.py +93 -0
  85. memtether-0.1.0/tests/test_consolidation.py +111 -0
  86. memtether-0.1.0/tests/test_core.py +103 -0
  87. memtether-0.1.0/tests/test_court_e2e_integration.py +274 -0
  88. memtether-0.1.0/tests/test_court_export_e2e.py +61 -0
  89. memtether-0.1.0/tests/test_cross_process_writes.py +78 -0
  90. memtether-0.1.0/tests/test_dashboard_e2e.py +68 -0
  91. memtether-0.1.0/tests/test_dashboard_xss.py +60 -0
  92. memtether-0.1.0/tests/test_docker_compose_config.py +62 -0
  93. memtether-0.1.0/tests/test_docs_claims.py +290 -0
  94. memtether-0.1.0/tests/test_error_signals.py +151 -0
  95. memtether-0.1.0/tests/test_examples_run.py +89 -0
  96. memtether-0.1.0/tests/test_exchange_adapters.py +41 -0
  97. memtether-0.1.0/tests/test_exchange_adapters_io.py +136 -0
  98. memtether-0.1.0/tests/test_extract_unit.py +45 -0
  99. memtether-0.1.0/tests/test_federation.py +44 -0
  100. memtether-0.1.0/tests/test_fresh_db.py +48 -0
  101. memtether-0.1.0/tests/test_gateway_cli.py +70 -0
  102. memtether-0.1.0/tests/test_gateway_cli_governance.py +105 -0
  103. memtether-0.1.0/tests/test_gateway_deep.py +104 -0
  104. memtether-0.1.0/tests/test_gateway_deep2.py +31 -0
  105. memtether-0.1.0/tests/test_gateway_func.py +95 -0
  106. memtether-0.1.0/tests/test_gateway_incident.py +83 -0
  107. memtether-0.1.0/tests/test_gateway_qvalue.py +117 -0
  108. memtether-0.1.0/tests/test_gateway_rebuild.py +102 -0
  109. memtether-0.1.0/tests/test_gateway_reflector.py +150 -0
  110. memtether-0.1.0/tests/test_governance.py +44 -0
  111. memtether-0.1.0/tests/test_governance_d2.py +60 -0
  112. memtether-0.1.0/tests/test_governance_deep.py +214 -0
  113. memtether-0.1.0/tests/test_governance_detect.py +92 -0
  114. memtether-0.1.0/tests/test_hubguard.py +34 -0
  115. memtether-0.1.0/tests/test_hubguard_d4.py +92 -0
  116. memtether-0.1.0/tests/test_hubguard_ext.py +80 -0
  117. memtether-0.1.0/tests/test_layers.py +168 -0
  118. memtether-0.1.0/tests/test_mcp_server_smoke.py +224 -0
  119. memtether-0.1.0/tests/test_memory_court.py +168 -0
  120. memtether-0.1.0/tests/test_memsearch.py +57 -0
  121. memtether-0.1.0/tests/test_memsearch_branches.py +161 -0
  122. memtether-0.1.0/tests/test_memsearch_d2.py +98 -0
  123. memtether-0.1.0/tests/test_memsearch_d3.py +133 -0
  124. memtether-0.1.0/tests/test_memsearch_d5.py +88 -0
  125. memtether-0.1.0/tests/test_memsearch_ext.py +79 -0
  126. memtether-0.1.0/tests/test_no_conn_leak.py +65 -0
  127. memtether-0.1.0/tests/test_plugin_install_scripts.py +81 -0
  128. memtether-0.1.0/tests/test_poisoning_defense.py +298 -0
  129. memtether-0.1.0/tests/test_predicates.py +97 -0
  130. memtether-0.1.0/tests/test_predicates_wired.py +89 -0
  131. memtether-0.1.0/tests/test_profile.py +165 -0
  132. memtether-0.1.0/tests/test_qvalue_activation.py +95 -0
  133. memtether-0.1.0/tests/test_r6_integrity.py +88 -0
  134. memtether-0.1.0/tests/test_rate_limit.py +25 -0
  135. memtether-0.1.0/tests/test_reconcile_gate.py +38 -0
  136. memtether-0.1.0/tests/test_remaining.py +51 -0
  137. memtether-0.1.0/tests/test_rollback_chain.py +96 -0
  138. memtether-0.1.0/tests/test_scale_behavior.py +100 -0
  139. memtether-0.1.0/tests/test_security.py +73 -0
  140. memtether-0.1.0/tests/test_security_redteam.py +133 -0
  141. memtether-0.1.0/tests/test_sgm.py +130 -0
  142. memtether-0.1.0/tests/test_sgm_scale.py +43 -0
  143. memtether-0.1.0/tests/test_tenant.py +57 -0
  144. memtether-0.1.0/tests/test_tether_connect_deep.py +126 -0
  145. memtether-0.1.0/tests/test_timeline_api.py +62 -0
  146. memtether-0.1.0/tests/test_tool_audit.py +90 -0
  147. memtether-0.1.0/tests/test_trigger_detector.py +89 -0
  148. memtether-0.1.0/tests/test_trust_adapter.py +76 -0
  149. memtether-0.1.0/tests/test_version.py +69 -0
  150. memtether-0.1.0/tether_connect.py +740 -0
  151. memtether-0.1.0/tool_audit.py +186 -0
  152. memtether-0.1.0/trigger_detector.py +201 -0
  153. memtether-0.1.0/zep_exchange.py +196 -0
@@ -0,0 +1,1666 @@
1
+ ## [Unreleased] — Wave 1.5: handoff + review + doctor + READY probe
2
+
3
+ ### Added
4
+
5
+ - **Governance viewer v1** (`/viewer`): 4-tab live panel — Memories
6
+ (search + recent, click a uid for its timeline), Conflicts (points to the
7
+ CLI review loop until `GET /conflicts` lands), Court (live hash-chain
8
+ verify + per-uid dossier), Stats. 5s health polling with live/offline
9
+ indicator. Ships in the wheel via memtether_assets. E2E tested: /viewer
10
+ 200, court/verify verified:true on a demo server.
11
+ - **`scripts/watch.py` session capture (experimental)**: extracts decisions and
12
+ lessons from a session transcript (JSONL or text) at a boundary and writes
13
+ them through the governed pipeline. Narrow heuristics by design; dry-run
14
+ mode; honest limitation = batch-at-boundary, live tail is future work.
15
+ End-to-end tested: 3/3 candidates extracted from a synthetic transcript,
16
+ 3 governed writes, search-back + hash-chain intact.
17
+ - **`@memtether/client` npm package** (`npm-client/`): zero-dependency thin REST
18
+ wrapper — health / stats / remember / search / correct / retire / list /
19
+ timeline / courtVerify / court. End-to-end tested against a live demo server
20
+ (remember PASS → search PASS → courtVerify verified:true).
21
+ - **`memtether handoff --to <agent>`**: exports a governed handoff bundle
22
+ (pinned memories, recent decisions/lessons, open-conflict count, integrity
23
+ pointer) for the next agent. Privacy by construction — MemTether stores no
24
+ transcripts, so none can leak into the bundle.
25
+ - **`memtether review`**: one-stop pending-conflict review queue reusing the
26
+ `conflicts` pipeline (`--accept`/`--discard`/`--by`/`--note`); verdicts land
27
+ in `conflict_reviews` (human adjudication on record).
28
+ - **`memtether connect doctor`**: five-layer diagnosis (python / config / db /
29
+ mcp / hooks) with per-layer fix hints and an explicit `status: READY` line
30
+ when all reachable layers pass. Layered failures cross-checked against
31
+ `tether_connect verify` on a live install (the two FAILs it reported match
32
+ known config drift in codex + dsh:wbpj — pre-existing, not doctor bugs).
33
+ - **READY probe on setup**: `memtether setup <client>` now ends with a real
34
+ remember → search → `court --verify` triple and prints `status: READY` only
35
+ when all three pass. Written config alone is not "ready" (tigerless-style
36
+ install-trust culture).
37
+ - **"Governance, provably" README section**: every governance claim paired
38
+ with a runnable command (audit-chain PASS→FAIL demo, `court --verify`,
39
+ `conflicts --list`). Security section documents fail-closed setup.
40
+ - New topics: `audit-chain`, `human-in-the-loop` (GitHub search surface for
41
+ the governance niche).
42
+
43
+ ## [0.1.0a83] — Documentation and registry surfaces aligned with reality
44
+
45
+ ### Fixed
46
+
47
+ - **PyPI project page** shipped a broken first-screen image: the README's
48
+ `assets/demo.gif` is a repo-relative path and PyPI does not resolve those.
49
+ Both READMEs now use a `<picture>` + `onerror` fallback to the raw GitHub
50
+ URL (GitHub keeps the relative path, PyPI falls back automatically). This
51
+ fallback existed once before and was lost in a later rewrite.
52
+ - `server.json` (MCP Registry source) declared version 0.1.0a81 with a
53
+ package version of 0.1.0a73 — nine releases behind. Both are now 0.1.0a83
54
+ and the description mentions the a82 capabilities.
55
+ - `mcp-server.json` listed 9 tools but was missing `profile`, and its version
56
+ was 0.1.0a81. Now 10 tools in the same order as `mcp_server.TOOLS`, version
57
+ 0.1.0a83.
58
+ - README's "Memory Operations" block documented 14 of the 20 CLI subcommands;
59
+ `backup`, `conflicts`, `demo`, `import-docs`, `provenance`, `reconcile`,
60
+ `remember-batch`, and `retire` were missing.
61
+
62
+ ### Note
63
+
64
+ The PyPI long description is the README verbatim, so the stale "9 tools /
65
+ 12 endpoints" text on pypi.org could not be fixed without a release. This
66
+ release publishes the corrected README.
67
+
68
+ ## [0.1.0a82] — Layered recall (L0/L1/L2) + deterministic subject profile
69
+
70
+ ### Added
71
+
72
+ - **Layered recall**: every search can now return the same hit at three depths,
73
+ so an agent scans cheap one-liners first and only pays for full text on the
74
+ hits it wants.
75
+ - `memsearch.content_layers()` / `apply_layers()`: deterministic split,
76
+ zero LLM, zero new tables. L2 (the default) stays byte-identical to the
77
+ previous behaviour.
78
+ - `gateway.search(..., layer='L0'|'L1'|'L2')` and
79
+ `memtether search "q" --layer L0`.
80
+ - Measured on a 2,221-fact production DB (limit=5): L0 costs **10%** of L2's
81
+ characters, L1 **44%**.
82
+ - **Deterministic subject profile** (`gateway.profile()`): counts by
83
+ type/source, time span, high-confidence count, top facts by q_value+recency,
84
+ and an L0 digest ready to paste into a system prompt. Zero LLM calls by
85
+ design — the same DB always yields the same profile, and every line traces
86
+ back to a uid.
87
+ - CLI: `memtether profile --subject user [--json]`
88
+ - MCP: `profile` tool (9 → 10 tools)
89
+
90
+ ### Fixed
91
+
92
+ - `cluster_semantic()` returned `[]` for empty input but a `(clusters, vecs)`
93
+ tuple otherwise, so `clusters, vecs = ...` crashed on an empty DB.
94
+ - The degraded (token-merge) search fallback now also applies layers; the first
95
+ version applied them before the merge, so all three layers silently no-op'd.
96
+ - `governance` query functions crashed with `no such table: facts` on a fresh
97
+ DB (`python governance.py conflicts` on a brand-new install). Added an
98
+ idempotent bootstrap that reuses gateway's SCHEMA.
99
+ - `governance.DB` ignored `MEM_DB`, so direct importers (api_server, tests,
100
+ external scripts) read the repo placeholder DB while remember/rebuild read
101
+ `MEM_DB` — one session, two databases.
102
+ - `_load_entity_index()` selected a non-existent `entity_name` column; the
103
+ resulting OperationalError was swallowed, leaving the facts side of the
104
+ entity index permanently empty. (The release-repo test for this module had
105
+ been green the whole time because its fixture built a table matching the bug.)
106
+
107
+
108
+ ## [0.1.0a81] — Governance tooling, SGM 2-hop, predicate quality rebuild
109
+
110
+ ### Added
111
+
112
+ - **MCP governance tools** (`correct_memory` / `retire_memory` / `pin_memory` /
113
+ `as_of_query` / `timeline`): the five governance functions were CLI-only; agents can
114
+ now correct, retire, pin, and run bi-temporal queries over MCP
115
+ - **`record_recipe` write API + CLI**: recipes table previously had no write
116
+ path (manual SQL only); stable-uid upsert (same primary pattern = update);
117
+ 7 high-frequency recipes seeded from real operations
118
+ - **SGM 2-hop bridge expansion**: direct-hit facts co-occur with bridge
119
+ entities; facts sharing those bridges join the candidate pool at RRF level
120
+ (score 0.5, below 1-hop); `_diag["sgm_2hop"]` observable
121
+ - **predicate extraction quality rebuild**: prose-junk entities 56%→0% on the
122
+ full active corpus; multi-word English entities ("Claude Code") now extract
123
+ cleanly; `_FUNCT_WORDS` filter + tail-ASCII recovery + split SUPPORT patterns
124
+ - **dynamic rebuild quota**: `max(static_base, 5% of type active count)` —
125
+ with 1,022 active facts the old static 45 quota hid 95% of memory;
126
+ `MEM_PROJ_STATIC_QUOTA=1` restores old behavior
127
+ - **external hash-chain anchor** (`court --anchor-external` / `--verify-external`)
128
+
129
+ ### Fixed
130
+
131
+ - **search use_count double-count**: MCP `tool_search` bumped `use_count` while
132
+ memsearch R1 write-back already had (+2 per search); A1 block removed
133
+ - **governance reviewed-pair leak**: a human `no_conflict` verdict was applied
134
+ only to the exact-match layer; the generic polarity layer re-reported the
135
+ same uid pair under different entity names — filter now runs after both layers
136
+ - **trigger_detector reset_cache/__main__ merge**: `reset_cache()` had swallowed
137
+ the `if __name__` self-test block (both repos had the same defect)
138
+ - **OSSF Scorecard CI**: scorecard-action v2.4.0 pulled from gcr.io (image
139
+ abandoned) → v2.4.2 (ghcr.io); CI green again
140
+ - **record_recipe idempotency**: `_uid()` embeds a timestamp so the same
141
+ pattern created a new row per write; stable `recipe-md5(primary)[:16]` now
142
+
143
+ ### Tests
144
+
145
+ - governance layer coverage 11→27 (court hardening ×8 + poisoning defense ×8);
146
+ **proj_judge 96/100** (A30+B27+C24+D15)
147
+ - MCP smoke: 5 new governance round-trips (10 total)
148
+
149
+
150
+ ## [0.1.0a79] — Persona testing + graceful vector degradation
151
+
152
+ ### Fixed
153
+
154
+ - **vec upsert/delete without chromadb**: returns `skipped` instead of raising
155
+ ModuleNotFoundError — no scary warning for users without vector extras
156
+ - **README**: PyPI official index fallback + China mirror lag note
157
+
158
+ ### Added
159
+
160
+ - **8 user personas tested**: concurrent writes, crash recovery, 10K scale,
161
+ English user, PII privacy, Windows beginner, multi-client detection, Docker
162
+
163
+
164
+ ## [0.1.0a78] — zh-CN README rewrite + version alignment
165
+
166
+ ### Changed
167
+
168
+ - **Full zh-CN README rewrite** (5KB → 10.4KB, matches EN structure)
169
+ - reproduce.html links a43 EAF artifact bundle
170
+ - Version bump after a74–a77 (8 real bug fixes, 23 new tests, 3 benchmarks)
171
+
172
+
173
+ ## [0.1.0a80] — Deep audit: 21 bug fixes from full line-by-line code review
174
+
175
+ ### Fixed — correctness & reliability
176
+
177
+ - **correct() rejects dead entries**: correcting a superseded/retired fact
178
+ no longer creates a duplicate active entry (was silently reviving dead facts)
179
+ - **near-dup respects source boundaries**: the same content written by two
180
+ different agents (e.g. `codex` and `openclaw`) is no longer collapsed into
181
+ one entry — each source stores independently by design
182
+ - **fact_entities schema alignment**: production table had 2 columns
183
+ (`fact_uid`, `entity_name`) but predicates.py expected 8; `ALTER TABLE`
184
+ added missing columns and migrated 21,168 existing rows
185
+ - **predicates multi-word entity**: "Claude Code的MCP配置路径" now extracts
186
+ `entity='Claude Code'` (was `'Code'` due to space boundary loss)
187
+ - **predicates attr truncation**: attr stops at 是/在/为/了 instead of
188
+ consuming the rest of the sentence
189
+ - **predicates entity excludes 的**: the character 的 is no longer matched
190
+ inside entity names, fixing over-matching in Chinese text
191
+
192
+ ### Fixed — performance & reliability
193
+
194
+ - **NREM trigger async**: `_maybe_nrem_trigger()` no longer blocks `remember()`
195
+ for up to 120 seconds (runs in a daemon thread)
196
+ - **MCP tool_add auto-rebuild opt-in**: `gateway.rebuild()` after every write
197
+ is now opt-in (`MEM_AUTO_REBUILD=1`); default avoids seconds-long blocking
198
+ on large databases
199
+ - **index consistency gate async**: rebuild runs in a background thread with
200
+ a cross-process file lock (`.gate_rebuild.lock`) instead of blocking writes
201
+ - **_vec_upsert rebuild race guard**: skips upsert when rebuild lock is held
202
+ (skipped entries caught by consistency gate on next write)
203
+
204
+ ### Fixed — search quality
205
+
206
+ - **Layer 3 dedup exempts pinned entries**: two different memories with
207
+ `tags='pin'` are no longer collapsed (pin is a retention marker, not a
208
+ content marker); 75% of active facts have empty tags and were already exempt
209
+ - **four-factor sem saturation**: procedure type boost (×1.5) no longer
210
+ pushes the sem factor to the 1.0 ceiling (divisor changed 0.1→0.15)
211
+ - **vec_ok requires actual hits**: a successful ChromaDB query returning
212
+ 0 valid results (all ghost UIDs) no longer reports `vec_ok=True`
213
+ - **FTS5 quote safety**: double quotes in query tokens are stripped before
214
+ FTS5 MATCH (both primary and fallback paths)
215
+
216
+ ### Fixed — security & tooling
217
+
218
+ - **trust.py env value gap documented**: env values are not hashed (matching
219
+ the Electron client); a malicious env value would not invalidate trust —
220
+ now explicitly documented as a known trade-off
221
+ - **R1 write-back audit trail**: `use_count` bumps from search retrieval
222
+ now leave an `audit_log` entry (was a bare SQL UPDATE with no trace)
223
+ - **gate.py readback field**: the `readback` field in sync_rules() compared
224
+ against the wrong string on CRLF files (always showed FAIL)
225
+ - **dual_repo_guard relative paths**: HUB/MT paths are no longer hardcoded
226
+ to `E:\RUANJIAN\` — derived from `__file__` with env override
227
+
228
+ ### Changed
229
+
230
+ - Version bumped to `0.1.0a80`
231
+ - Test count: 538 collected (was 523 in README)
232
+
233
+ ## [0.1.0a76] — Security hardening, integrity proofs, competition readiness
234
+
235
+ ### Fixed — critical correctness & privacy defects
236
+
237
+ - **supersession cycle detection**: `correct()` walks the chain and refuses
238
+ cycles that would make the version history unresolvable
239
+ - **correct() race guard**: concurrent corrections on the same uid no longer
240
+ fork the chain (two supersessions edges to two active children); loser
241
+ receives an explicit `not active` error
242
+ - **sink.json scope filter**: private/restricted facts leaked into the
243
+ projection read by every client — now filtered at the SQL level
244
+ - **rebuild() without WorkBuddy targets**: no longer crashes with IndexError
245
+ on open-source / CI environments (returns `skipped: no_wb_targets`)
246
+ - **sink.json atomic write**: was bare `open(w)`; now uses hubguard atomic
247
+ write (tmpfile + os.replace) matching the documented behavior
248
+
249
+ ### Added — memory-poisoning defense evidence
250
+
251
+ - `SECURITY.md`: full threat model with attack→mitigation mapping
252
+ (overwrite / erasure / trust forgery / tamper-evidence / sleeper /
253
+ initial injection / cross-tenant / stale values)
254
+ - `tests/test_poisoning_defense.py`: 8 executable proofs, including a
255
+ negative test (test_F) that pins the initial-injection limitation
256
+ - `gateway.audit()`: auto-anchors the hash chain every BATCH writes
257
+ (was only triggered by court CLI / api_server — silent tamper window)
258
+ - `tests/test_r6_integrity.py`: 4 regression locks (cycle, race, privacy,
259
+ no-wb-targets)
260
+ - `examples/audit_chain_demo.py`: 60-second executable demo — tamper with
261
+ the audit log, watch `verify_chain()` turn red
262
+
263
+ ### Added — benchmark harnesses
264
+
265
+ - `_dev/benchmarks/bench_sgm_noisy.py`: 300+ distractor facts — SGM 84%
266
+ vs embedding 64% recall under noise
267
+ - `_dev/benchmarks/bench_memdaily.py`: MemTether × MemDaily (MemSim,
268
+ arXiv:2409.20163) official dataset harness — simple 46.7% / noisy 0%
269
+ (honest first-run numbers, offline protocol)
270
+
271
+ ### Improved — judge-readiness
272
+
273
+ - README: TL;DR section, audit demo output block, Docker security note,
274
+ Troubleshooting table, tests count 488 → 523
275
+ - README.zh-CN: 30-second TL;DR + honest-limits pointer
276
+ - fuseeval: 4-judge heterogeneous review pipeline (z-score aggregation,
277
+ findings ledger, prompt versioning)
278
+
279
+ ### Tests
280
+
281
+ - 523 passed, 4 skipped (was 488 at a73)
282
+ - gateway.py coverage 54% → 76%
283
+
284
+ ## [0.1.0a73] — SGM Phase 1 验收 PASS (82.6%)
285
+
286
+ ### Improved — entity extraction action/keyword patterns
287
+
288
+ - Replaced broken regex _ACTION_PATTERN with keyword-based extraction for
289
+ completion phrases ("XX完成", "XX发布", "XX跑完", "XX解除", etc.)
290
+ - Phase 1 验收: 100 production facts → 82.6% entity extraction rate
291
+ (fact/environment/preference types; PASS at ≥80% threshold)
292
+ - Global rate: 82/100 = 82% (up from 66% before action patterns)
293
+
294
+ ## [0.1.0a72] — SGM scale validation
295
+
296
+ ### Added — multi-session recall at scale validation
297
+
298
+ - Scale test: 10 entities × 3 attributes × multi-session = 30 facts.
299
+ SQL-deterministic path finds ALL matching facts for each entity.
300
+ - Validated: SGM achieves 100% recall on entity-aggregation queries
301
+ (vs 62% for embedding-only baseline). This is the r^N escape —
302
+ deterministic SQL lookup vs probabilistic retrieval.
303
+ - Tests: 488 passed, 4 skipped.
304
+
305
+ ## [0.1.0a71] — SGM: 100% multi-session recall achieved
306
+
307
+ ### Fixed — compile_packet truncation defeats SGM
308
+
309
+ - **SGM recall: 64% → 100%**. The compile_packet() step (max_items=16)
310
+ was truncating SGM's SQL-deterministic results after injection — 25
311
+ correct answers reduced to 16. When SGM has SQL-deterministic results,
312
+ compile_packet is now skipped (the SQL result set IS the answer).
313
+ - Validated: 250 facts across 25 sessions, 10 entities. Query "list all
314
+ ModuleN" returns 25/25 (100%) with SGM vs 16/25 (64%) without.
315
+
316
+ ## [0.1.0a70] — SGM Phase 3: aggregation queries
317
+
318
+ ### Added — COUNT/LIST via deterministic SQL
319
+
320
+ - **Counting queries** ("how many X"): `SELECT COUNT(DISTINCT fact_uid)
321
+ FROM fact_entities WHERE entity = ?` → SQL COUNT hint injected into diag.
322
+ - **Aggregation queries** ("list all X"): `SELECT DISTINCT value FROM
323
+ fact_entities WHERE entity = ?` → SQL LIST hint with top-5 values.
324
+ - Entity extraction for aggregation uses `extract_ascii_entities` (not just
325
+ `_query_attrs`) — works for "how many ComfyUI" without the 的 pattern.
326
+ - 3 new tests. Tests: 487 passed, 4 skipped.
327
+
328
+ ## [0.1.0a69] — Schema-Grounded Memory Phase 1+2
329
+
330
+ ### Added — SQL-deterministic entity query routing
331
+
332
+ - **`fact_entities` table**: every `remember` now writes structured
333
+ (entity, attr) pairs extracted from content — stored as independent rows
334
+ (not JSON blobs) to enable direct SQL lookup.
335
+ - **Query routing in `search_hybrid()`**: entity-attr queries are routed
336
+ through `SELECT ... FROM fact_entities JOIN facts WHERE entity = ?` —
337
+ deterministic results boosted to score 1.0 with `sql_deterministic`
338
+ reason. Escape path from the r^N multi-session recall problem.
339
+ - `predicates.store_entities()` + `predicates.query_entities()` — write and
340
+ read paths for the structured entity index.
341
+ - Toggle: `MEM_SGM=0` disables. Default: ON.
342
+ - 8 new tests. Tests: 485 passed, 4 skipped.
343
+
344
+ ### Why
345
+
346
+ Multi-session recall failure is a probability multiplication: r^N where
347
+ r = per-query recall rate, N = sessions the answer is scattered across.
348
+ Embedding-based retrieval cannot escape this. Structured SQL queries can —
349
+ deterministic lookup returns ALL matching facts regardless of session count.
350
+
351
+ Reference: xmemory (arXiv:2604.27906) validated the approach (97.1% F1),
352
+ but is a remote SaaS. MemTether is the first local-first, open-source,
353
+ bi-temporal implementation.
354
+
355
+ ## [0.1.0a68] - 2026-10-08
356
+
357
+ ### Fixed — fresh-DB crash on first search (black-box audit round 4)
358
+
359
+ - `memtether search x` on a brand-new install (before any remember/init)
360
+ crashed with "no such table: facts" — the very first command a new user
361
+ runs. `_cmd_search` now calls `init_db()` first.
362
+ - 10 fresh-DB regression tests: every CLI command must not crash on an
363
+ empty/nonexistent database. Tests: 468 passed, 4 skipped.
364
+
365
+ ## [0.1.0a67] - 2026-10-08
366
+
367
+ ### Fixed — reconcile graceful degradation without chromadb
368
+
369
+ - `reconcile` on a machine without the optional chromadb extra exited 1 with
370
+ "ChromaDB unavailable". chromadb is optional everywhere else (search
371
+ degrades to keyword gracefully) — reconcile now does the same: reports the
372
+ SQLite side OK, notes the vector store is unavailable, exits 0.
373
+ - Found via CI (ubuntu/windows jobs run without the vector extra).
374
+
375
+ ## [0.1.0a66] - 2026-10-08
376
+
377
+ ### Fixed — court export browser verification (CRITICAL, black-box audit round 3)
378
+
379
+ - **Exported evidence ALWAYS failed browser verification** on a valid chain.
380
+ Two compounding bugs: ① `court --export` filtered audit entries by target
381
+ uid, but the anchor's hash covers ALL entries in id order — the subset can
382
+ never re-verify; ② the embedded JS hashed raw 5-field rows, while the CLI
383
+ hashes 6-field row_hashes (including `target`).
384
+ - Fixed: export now ships the FULL audit chain, and verify.html replicates
385
+ `memory_court._row_hash` exactly. Browser-verified: authentic export →
386
+ PASS, tampered copy → FAIL (both headless-tested).
387
+ - 2 round-trip regression tests. Tests: 466 passed, 4 skipped.
388
+
389
+ ## [0.1.0a65] - 2026-10-08
390
+
391
+ ### Fixed — stale numbers sweep + CLI UX (black-box audit round 2b)
392
+
393
+ - **Stale numbers across all public pages**: EN reproduce.html still claimed
394
+ "100 tests / 26% coverage"; README comparison table said "325 tests";
395
+ zh badges said 461. All synced to 466 / ~55% (a63-a64 口径).
396
+ - `conflicts --stats` / `--list` printed nothing on empty DBs (silent
397
+ no-op) — now prints an explanatory zero-state line.
398
+ - Docs updated: docs/INTRO + TECHNICAL test counts, benchmark table.
399
+
400
+ ## [0.1.0a64] - 2026-10-08
401
+
402
+ ### Fixed — reconcile mutation gate (black-box audit round 2)
403
+
404
+ - Bare `memtether reconcile` previously **silently mutated** the vector
405
+ store: deleted every "ghost" vector and upserted gaps — catastrophic if
406
+ the store directory is shared across databases (test db pairing with a
407
+ production store would mass-delete production vectors).
408
+ - Now: bare/`--dry-run` = report only. Mutation requires explicit
409
+ `--apply`. All three mutation paths (upsert/delete/summary) gated.
410
+ - Found via black-box audit: reconcile on a throwaway test db reported
411
+ 1122 "ghost" vectors that actually belonged to the production store
412
+ sharing the directory.
413
+ - 2 gate regression tests. Tests: 466 passed, 4 skipped.
414
+
415
+ ## [0.1.0a63] - 2026-10-08
416
+
417
+ ### Added — `correct` / `retire` CLI subcommands (black-box audit finding)
418
+
419
+ - Black-box wheel-install audit found `gateway.correct()` and
420
+ `governance.retire()` had **no CLI surface**: pip users could not call
421
+ them without writing Python. Added `memtether correct <uid> <content>
422
+ [--reason --by --valid-from]` and `memtether retire <uid> [--reason
423
+ --by --apply --force]`.
424
+ - `retire` routes through governance.retire: **dry-run default**,
425
+ residual-facts guard (blocks `--apply` when independent facts share the
426
+ entry; `--force` is the documented override; plain `--apply` on a
427
+ residual-guarded entry exits 1 with the residual list).
428
+ - **`reconcile` mutation gate**: bare `memtether reconcile` previously
429
+ silently FIXED the vector store (deleting all "ghost" vectors — dangerous
430
+ when the store directory is shared across databases). Mutation now
431
+ requires explicit `--apply`; bare/`--dry-run` is report-only.
432
+ - 5 new tests (CLI ×3, reconcile gate ×2). Tests: 464 passed, 4 skipped.
433
+
434
+ ## [0.1.0a62] - 2026-10-08
435
+
436
+ ### Fixed — Court Playground v2 (live demo)
437
+
438
+ - **EN/ZH language toggle** with localStorage persistence; all UI strings,
439
+ placeholders, case-file sections, and chain-status messages localized.
440
+ - **Bug: "Correct" button never worked** — `INSERT INTO supersessions
441
+ VALUES (?,?,?,?)` had 4 placeholders for 5 values (sql.js threw on every
442
+ click). Fixed to 5. Nobody had reported it because the demo page had no
443
+ error surfacing.
444
+ - **Bug: case-file section 4 always threw** — SELECT referenced
445
+ `verdict_reason`, a column that doesn't exist (actual name: `reason`).
446
+ - Visual polish: gradient bg, hover states, focus rings, scrollable memory
447
+ list, mobile responsive, EU AI Act callout card, localized badges.
448
+ - Live: https://memtether.github.io/MemTether/court/
449
+
450
+ ## [0.1.0a61] - 2026-10-08
451
+
452
+ ### Tests — memsearch retrieval-branch net (10 tests)
453
+
454
+ - R8 low-confidence marker consistency (kw_count/max_vec/low_confidence
455
+ invariant), R7 three-layer dedup (content dedup counter, supersession
456
+ removal), R10 scaffold prepending + question-type detection matrix
457
+ (counting/comparison/temporal/knowledge-update/aggregation/single-session),
458
+ T-B AFAG hints (COUNT/FREQUENT NUMBERS/EARLIEST/LATEST — string block
459
+ shape), P4-2 multi-round disabled-by-default + trigger-on-low-score.
460
+ - API shape notes: afag_hints is a formatted string; question_type always
461
+ in diag. Tests: 461 passed, 4 skipped.
462
+
463
+ ## [0.1.0a60] - 2026-10-08
464
+
465
+ ### Removed — consolidation.build_topic_timelines (user decision B)
466
+
467
+ - Removed the topic-timelines index: it JOINed a `fact_entities` table that
468
+ **no shipped schema creates** — the function could never have run against a
469
+ real database (would raise `no such table`). Deleted the function, its
470
+ `build_all()` entry, and the `topic_index.json` branch of `search_index()`.
471
+ Shipped index types are now: **value histories** and **standing
472
+ instructions** (both tested). Re-add topic timelines together with
473
+ write-side entity extraction if that feature is ever built.
474
+ - Regression test locks the removal (function absent, `build_all()` has no
475
+ topics key, stale `topic_index.json` on disk cannot resurrect the path).
476
+ - Tests: 450 passed, 4 skipped.
477
+
478
+ ## [0.1.0a59] - 2026-10-08
479
+
480
+ ### Tests — rebuild projection pipeline locked (7 tests)
481
+
482
+ - sink.json structure/type routing/supersession exclusion; pin
483
+ unconditional inclusion; MEM_PROJ_BUDGET hard cap; retired exclusion;
484
+ idempotency (byte-identical re-runs); usage-header presence.
485
+ - Isolation contract exercised: MEM_SINK_PATH + MEM_PROJ_PATH + HG=None
486
+ (the documented sandbox path for testing rebuild without touching the
487
+ live shared projection).
488
+ - Tests: 450 passed, 4 skipped.
489
+
490
+ ## [0.1.0a58] - 2026-10-08
491
+
492
+ ### Tests — gateway 32% -> 37% + Q-Value contract locked
493
+
494
+ - 11 new tests: bump_qvalue full contract (readonly distribution mode,
495
+ reward validation, convergence formula, dry-run, tool_assets fallback,
496
+ ghost-uid error, audit trail), record_tool roundtrip, incident ->
497
+ run_events contract, on_miss.
498
+ - Tests: 443 passed, 4 skipped.
499
+
500
+ ## [0.1.0a57] - 2026-10-08
501
+
502
+ ### Tests — gateway 27% -> 32% + bi-temporal surface locked
503
+
504
+ - 16 new tests: remember validation matrix (length/type/confidence/scope),
505
+ dup-noop, explicit valid_from backdating, **as_of dual-axis semantics**
506
+ (valid vs known — the docstring example now enforced by tests), timeline
507
+ forward-chain walking, stats/set_pin/bump_qvalue (incl. ghost-uid safety),
508
+ private-scope search exclusion.
509
+ - Tests: 432 passed, 4 skipped.
510
+
511
+ ## [0.1.0a56] - 2026-10-08
512
+
513
+ ### Tests — tether_connect 14% -> 49%
514
+
515
+ - 13 new tests covering the no-real-config surface: hub_defaults venv
516
+ resolution, default_spec env parsing, _register_sources idempotency +
517
+ active-only registration (agents.json pollution guard), rollback from
518
+ crafted manifests, selftest end-to-end, pick_adapters filtering.
519
+ - Tests: 416 passed, 4 skipped.
520
+
521
+ ## [0.1.0a55] - 2026-10-08
522
+
523
+ ### Tests — governance 44% -> 57% (v19 target 50% reached)
524
+
525
+ - 8 new tests: `_canonical` entity normalization (alias table, suffix
526
+ stripping, bracket/whitespace removal) and `detect_explicit_conflicts`
527
+ cross-entity polarity logic — positive/negative/newer-wins/suggest-retire,
528
+ single-polarity no-report, and window exclusion.
529
+ - Tests: 403 passed, 4 skipped.
530
+
531
+ ## [0.1.0a54] - 2026-10-08
532
+
533
+ ### Fixed — P2-2 wired: retrieval-side attribute coverage
534
+
535
+ - **`check_attr_coverage` is now actually called** by `search_hybrid()`
536
+ (after dedup layers, before low-confidence marking). "X的Y" queries now
537
+ downweight candidates that mention X but not Y (`not_answered`,
538
+ score ×0.15) and surface `predicates_downweighted` in diag. Written in
539
+ 十三修 but never wired — the retrieval half of the predicate feature
540
+ was dead code until now. Default ON; `MEM_PREDICATES=0` disables.
541
+ - 3 new wiring tests (`test_predicates_wired.py`). Tests: 395 passed,
542
+ 4 skipped.
543
+ - chore: promo/ gitignored (local-only assets per v19).
544
+
545
+ ## [0.1.0a53] - 2026-10-08
546
+
547
+ ### Fixed — Test coverage round (v19 tech-debt #1/#2)
548
+
549
+ - **predicates.py regex fix**: `_ATTR_PATTERN`/`_SUPPORT_PATTERN` never matched
550
+ spaced Chinese ("Clash 的端口") — retrieval-side `_QUERY_ATTR` had `\s*`,
551
+ write-side did not. Predicates were silently missing for the most common
552
+ input format. Coverage 28% -> 95%.
553
+ - **New tests (54)**: test_predicates.py (12), test_governance_deep.py (22),
554
+ test_exchange_adapters_io.py (13 — mem0/zep adapters were 0%),
555
+ test_tool_audit.py (7), test_consolidation.py (4).
556
+ - **v19 correction**: consolidation.py and memtether_pipeline.py are NOT dead
557
+ (memsearch lazy-import + exchange sanitize path). Kept + tested instead of
558
+ deleted. Honest note: consolidation.build_topic_timelines JOINs
559
+ fact_entities — a table no shipped schema creates; that path remains
560
+ untested/would crash on real DBs.
561
+ - governance.py 21% -> 44% (decay/apply_decay/stale/review/retire/residual/
562
+ self-contradiction paths). TOTAL coverage 41% -> 46%.
563
+ - Tests: 392 passed, 4 skipped.
564
+
565
+ ## [0.1.0a52] - 2026-10-08
566
+
567
+ ### Fixed — Packaging
568
+
569
+ - **`memory_court.py` missing from wheel (a50/a51)**: the module was never
570
+ registered in `pyproject.toml` `py-modules`, so `pip install` shipped a
571
+ wheel without the Court CLI module — and setuptools silently accepts the
572
+ omission (same failure class as the a49 Dockerfile bug). Caught by
573
+ `scripts/check_packaging.py` exit 1 on a fresh-instance audit. Verified:
574
+ fresh wheel now contains `memory_court.py`; the PyPI a51 wheel does NOT.
575
+
576
+ ## [0.1.0a51] - 2026-10-07
577
+
578
+ ### Added — Production hardening
579
+
580
+ - **Semantic near-dup dedup**: removed SequenceMatcher gate (fails on
581
+ CJK paraphrases). Embedding cosine >= 0.92 is now the sole criterion.
582
+ Verified: Chinese paraphrases correctly deduped.
583
+ - **`memtether remember-batch <file.json>`**: batch write with single
584
+ commit + batch ChromaDB upsert. 500 items in 8s (63/sec).
585
+ - **`memtether reconcile`**: detects and fixes SQLite ↔ ChromaDB
586
+ vector store inconsistencies (missing vectors upserted, ghost vectors
587
+ deleted). First memory system with a built-in reconciliation pass.
588
+ - **`memtether backup`**: point-in-time snapshot via sqlite3 backup API
589
+ with 7-day retention.
590
+ - **`reconcile --dry-run`**: report without fixing.
591
+
592
+ ### Note
593
+
594
+ Q-Value "rich-get-richer" is already mitigated: `governance.decay_factor`
595
+ applies a 30-day half-life (DECAY_FLOOR=0.35) to fact scores on every
596
+ search. Heavily-used facts that stop being accessed decay naturally.
597
+
598
+ Tests: 334 passed, 4 skipped.
599
+
600
+ ## [0.1.0a50] - 2026-10-07
601
+
602
+ ### Added — Memory Court: the AI memory司法基础设施
603
+
604
+ The first memory hub with a **tamper-evident evidence chain** for
605
+ memory-level decisions — aligned with EU AI Act Article 12
606
+ record-keeping requirements.
607
+
608
+ - **`memory_court.py`**: hash-chained anchors over audit_log (every 10
609
+ entries or forced). Full-chain sha256 recomputation detects any edit
610
+ or deletion of anchored entries. Design trade-off: full recompute over
611
+ Merkle tree (simpler + faster at local-first scale, no trusted
612
+ third-party anchor service needed).
613
+ - **`memtether court <uid>`**: five-section evidence dossier CLI —
614
+ content, bi-temporal timestamps, provenance chain, conflict
615
+ adjudications, integrity proof. Exit 1 on tamper detection.
616
+ - **`memtether court --verify`**: full chain recompute.
617
+ - **`memtether court <uid> --export out.zip`**: self-verifying evidence
618
+ package — case.json (Art.12 format), chain.jsonl, verify.html
619
+ (recomputes sha256 in the reader's browser, zero dependencies),
620
+ README.txt for auditors.
621
+ - **GET `/court/{uid}`** + **GET `/court/verify`**: machine-readable API.
622
+ - **Dashboard Memory Court tab**: UID lookup renders case file; verify
623
+ button runs full hash recompute with color-coded PASS/FAIL.
624
+ - **README repositioned**: "The AI Memory Court" narrative with court
625
+ CLI demo block, EU AI Act Article 12 hook, and competitor matrix rows
626
+ for evidence chain and human adjudication.
627
+
628
+ ### Fixed
629
+
630
+ - **Config file race**: two `memtether setup` processes racing on the
631
+ same config produced lost updates (Windows CI caught writer-1 entry
632
+ vanishing). `_FileLock` (O_CREAT|O_EXCL + stale detection) now
633
+ serializes read-modify-write in `JsonAdapter.write` — all 23 adapters
634
+ inherit the guarantee.
635
+ - **`/remember` 500 on oversized content**: gateway ValueError (content
636
+ > 10240 chars) now mapped to HTTP 422 with error message.
637
+ - **`/court/verify` 404**: route ordering — `/court/verify` was shadowed
638
+ by `/court/{uid}`. Moved above.
639
+
640
+ ### Security
641
+
642
+ - **JSON config corruption via server-name injection** (found by red
643
+ team): `jsonc.set_entry` interpolated server name raw into config
644
+ text — a name containing quotes produced an invalid config file that
645
+ would break the client. Names are now JSON-escaped via `_jstr()`.
646
+
647
+ Tests: 334 passed, 4 skipped (was 313+4).
648
+
649
+ ## [0.1.0a49] - 2026-10-07
650
+
651
+ ### Fixed
652
+
653
+ - **docker build never worked**: pyproject declares readme=README.md and
654
+ packages=memtether_assets, but the Dockerfile COPYed neither — every
655
+ `docker build` died at setuptools. First-run docker CI job caught both.
656
+ README first install path now actually builds and boots, verified by
657
+ CI booting the container and probing /health + /dashboard.
658
+
659
+ ### Changed
660
+
661
+ - **absorb related threshold 0.62 -> 0.80**: 23 LLM-generated ZH probe
662
+ pairs (scripts/zh_calibration.py, free-credit WB2API pool) showed
663
+ unrelated max sim 0.782 — the old 0.62 let unrelated pairs classify
664
+ as related. dup stays 0.92 (conservative: a misjudged duplicate
665
+ degrades to update = still written).
666
+
667
+ ### Tests
668
+
669
+ - 172 passed, 2 skipped. New: trust adapter (10), zh absorb calibration (2),
670
+ macos matrix, docker build+boot CI job.
671
+ - download_models: fixed truncation acceptance (real defect found by
672
+ downloading the real 46MB model through a proxy) + stale-cache guard.
673
+
674
+ ## [0.1.0a48] - 2026-10-07
675
+
676
+ ### Fixed
677
+
678
+ - **init_db race**: `_ensure_columns` ALTER TABLE raced between PRAGMA and
679
+ ALTER when two clients started simultaneously against a fresh shared db
680
+ (Linux CI caught it via the new cross-process test; Windows serialized by
681
+ luck). Duplicate-column errors are now swallowed as benign. This is the
682
+ multi-client-startup path — the product's core scenario.
683
+
684
+ Tests: 172 passed, 2 skipped.
685
+
686
+ ## [0.1.0a47] - 2026-10-07
687
+
688
+ ### Tests - closed the biggest untested gaps (144 -> 172)
689
+
690
+ - **MCP server smoke** (4): spawn `python -m mcp_server` subprocess,
691
+ speak JSON-RPC over stdio — initialize handshake, tools/list contract,
692
+ add->search roundtrip, invalid-method robustness. First automated
693
+ coverage of the MCP entry path every client uses.
694
+ - **Dashboard end-to-end** (3): real uvicorn + GET /dashboard — regression
695
+ for the a40 "Dashboard not found" wheel incident.
696
+ - **Timeline API** (3): /timeline/{uid} supersession chain.
697
+ - **Docker compose config** (5): YAML parses, no deleted-module reference
698
+ (a25 regression), MEMTETHER_API_KEY passthrough matches api_server,
699
+ Dockerfile entry modules exist.
700
+ - **Cross-process concurrent writes** (1): 4 real processes x 25 writes
701
+ on one shared db via hub_lock — zero crashes, integrity_check ok.
702
+ - **Trust adapter** (10): Electron MCP trust hash algorithm, approve/
703
+ is_trusted roundtrip, command-change detection, corrupt-file
704
+ fail-closed (trust.py was 16% coverage).
705
+
706
+ ### Fixed
707
+
708
+ - **/correct REST endpoint 500**: api_server passed `source=` but
709
+ `gateway.correct` expects `by_agent=` — every REST correction call
710
+ raised TypeError. Found by test_timeline_api on first run.
711
+
712
+ ### Changed
713
+
714
+ - absorb thresholds recalibrated with 8 ZH probe pairs (ZH paraphrase
715
+ 0.86-0.94): duplicate 0.95->0.92, related 0.65->0.62. Digit guard
716
+ still forces update for "port 7890 -> 7897" (ZH sim 0.931).
717
+
718
+ Tests: 172 passed, 2 skipped (was 144+2).
719
+
720
+ ## [0.1.0a46] - 2026-10-07
721
+
722
+ ### Fixed - all four known real bugs
723
+
724
+ - **Semantic absorb (P0-HIGH)**: `/absorb` now uses local embeddings
725
+ (`embed_local`, bge-m3-int8, 1024-dim) instead of word overlap.
726
+ Digit-aware duplicate guard: same-sentence with different numbers
727
+ (port 7890 vs 7897) classifies as update/contradiction, never duplicate.
728
+ Calibrated on 12 probe pairs (paraphrase 0.84-0.93 / unrelated 0.53-0.64).
729
+ Falls back to keyword overlap when the model is unavailable (honest
730
+ degradation, same pattern as memsearch). `method` field in the response
731
+ reports which path ran. New tests: `tests/test_absorb_semantic.py` (5).
732
+ - **Q-Value learning loop (P0-HIGH)**: `memtether conflicts --accept` now
733
+ rewards the kept (newer) fact (+0.05); `--discard` penalises both (-0.05).
734
+ Every bump is logged to `audit_log` with agent + detail. This gives the
735
+ governance review loop an actual learning signal — q_value no longer stuck
736
+ at 0.5 for reviewed facts. Also fixes a latent sqlite conn-lock bug
737
+ (review UPDATE is now committed before bump opens its own connection).
738
+ New tests: `tests/test_conflicts_qvalue.py` (4).
739
+ - **Async I/O offload**: `/remember` and `/search` use `asyncio.to_thread`
740
+ (Py3.9+ dedicated pool) instead of `run_in_executor(None, ...)` shared
741
+ executor. Docstring honestly notes this is I/O offload, not a native-async
742
+ engine rewrite.
743
+ - **Plugin slash commands**: Claude plugin ships `/mem-remember`, `/mem-search`,
744
+ `/mem-stats` (commands/*.md); Cursor gets `.cursorrules` hints; OpenClaw
745
+ gets `AGENTS.md` hints. Claude manifest bumped to 0.1.2.
746
+
747
+ Tests: 144 passed, 2 skipped (was 135+2).
748
+
749
+ ## [0.1.0a45] - 2026-10-07
750
+
751
+ ### Changed - Narrative repositioning + community tooling
752
+
753
+ - **README first-screen**: new positioning "The only memory hub where corrections don't delete" — leads with supersession chain, bi-temporal timestamps, and exchange format instead of "no governance" competitor comparison. Competitor table now includes agentmemory (29.2K stars) with honest positioning.
754
+ - **memtether-js removed**: 0 tests / 0 lockfile Node wrapper was a liability, not an asset. MCP protocol already provides cross-language support.
755
+ - **HF Space claim**: "Try in browser" -> "Project page" (the Space is static HTML, not a live demo).
756
+ - **New**: `scripts/make_jury_report.py` — one-command verification report for reviewers.
757
+ - **New**: `docs/NEW_ADAPTER.md` — 30-minute guide for adding new AI client adapters.
758
+ - **New**: `docs/DEMO_SCRIPT.md` — 3-minute demo walkthrough.
759
+ - **Fixed**: reproduce.html EAF "repro pending" -> done with artifact bundle links. EAF delta corrected to +19.4pp (500Q paired).
760
+ - **Fixed**: memsearch 3 sqlite conn leaks (multi-hop, active-dict, qvalue bump) that locked bench scratch db.
761
+ - **New**: `memtether conflicts --stats/--list/--accept/--discard` governance review CLI.
762
+ - **New**: `tests/test_adapters.py` (9 tests) + `tests/test_no_conn_leak.py` regression.
763
+ - **CI**: removed both continue-on-error; lint + PII roundtrip now gate.
764
+ - **docker-compose**: MEMTETHER_API_KEY passthrough.
765
+
766
+ Tests: 135 passed, 2 skipped.
767
+ ## [0.1.0a43] - 2026-10-06
768
+
769
+ ### Added - `memtether download-models` (closes the semantic-search gap)
770
+
771
+ - New stdlib-only downloader (`scripts/download_models.py`, wired as
772
+ `memtether download-models`): fetches ONNX weights for `bge-m3-int8`
773
+ (~560 MB, default) or `bge-small-zh` (~46 MB) from huggingface.co with
774
+ automatic hf-mirror.com fallback, resume support, and layout that matches
775
+ `embed_local.MODELS` target dirs (end-to-end verified: download → load →
776
+ encode 512-dim vectors).
777
+ - README/README.zh-CN: honest 3-step semantic install (deps + weights +
778
+ rebuild) with graceful-degradation note — before this, `[vector]` extra
779
+ installed deps but there was no documented way to get the weights, so
780
+ pip users' semantic search silently never worked.
781
+
782
+ ## [0.1.0a42] - 2026-10-06
783
+
784
+ ### Fixed - portable paths + dockerignore __init__ regression
785
+
786
+ - **memsearch._embed_zhipu**: removed the hard-coded private vault path.
787
+ Key resolution is now env-first (`ZHIPU_KEY`), with optional
788
+ `MEM_CRED_ENV_PATH` for a local credentials module. Missing key raises a
789
+ clear actionable error instead of ImportError on machines without `E:\`.
790
+ - **gateway._llm_json**: same env-first pattern (the old code had a
791
+ sanitized `<AUDIT>` placeholder that silently disabled auto-reflection
792
+ everywhere). Event-driven reflection now works when keys are present.
793
+ - **13 test files**: replaced `sys.path.insert(0, r'E:\RUANJIAN\memtether')`
794
+ with a path derived from `__file__` — tests now run from any checkout
795
+ location (CI passed before only because pytest rootdir insertion saved it).
796
+ - **.dockerignore**: `_*` also matched `__init__.py`, so Docker builds
797
+ dropped every package `__init__` and the in-image `pip install .` produced
798
+ a broken package. Mirrors git's `_[!_]*` pattern; simulated matcher
799
+ verified (init files kept, dev junk still ignored).
800
+
801
+ Tests: 125 passed, 2 skipped.
802
+ ## [0.1.0a40] - 2026-10-06
803
+
804
+ ### Added - D5 memsearch RRF/multi-path tests + PyPI README star hint
805
+
806
+ - tests/test_memsearch_d5.py: 7 new tests (multi-keyword search, all_unanswered diagnostics, semantic field, scope asset, q_value ranking)
807
+ - README.md: star hint at bottom
808
+ - Coverage: memsearch 49% -> 51%, TOTAL 27% -> 28%
809
+ - Total tests: 121 -> 125 (2 skipped: state pollution + vector model)
810
+
811
+ ## [0.1.0a39] - 2026-10-06
812
+
813
+ ### Added - D3+D4 memsearch + hubguard deep tests + P1-M init star hint
814
+
815
+ - tests/test_memsearch_d3.py: 10 new tests (scoring order, score/uid/type/source fields, superseded/retired excluded, engine metadata, limit, asset_text)
816
+ - tests/test_hubguard_d4.py: 12 new tests (db_path env/explicit, typ_abbr, src_code, snapshot consistent/changed, safe_append_bytes, format/parse no-source, detect_newline LF/CRLF)
817
+ - memtether.py _cmd_init: star hint after successful init
818
+ - Coverage: memsearch 47% -> 49%, hubguard 61% -> 62%, TOTAL 26% -> 27%
819
+ - Total tests: 100 -> 121 (1 skipped: state pollution in full suite)
820
+
821
+ ## [0.1.0a38] - 2026-10-06
822
+
823
+ ### Fixed - P0-1 tenant_id SQL injection (parameterized query)
824
+
825
+ - memsearch.py _tenant_filter(): was string-concatenation with tid inline in SQL. Semicolons/quotes could inject SQL via MEM_TENANT_ID env. Now returns (sql_fragment, param_value) and all 3 call sites use .execute(sql, (tid,)) parameterized query.
826
+ - gateway.py _tenant_filter(): same fix (was unused but now has correct signature for future callers).
827
+ - tests/test_memsearch_d2.py: 3 tests updated to verify parameterized behavior. Injection payload "x; DROP TABLE facts; --" becomes inert bound value.
828
+
829
+ ### Added - D9 Pages root redirect + README Engram comparison
830
+
831
+ - docs/index.html: redirect to docs/site/en/ (GitHub Pages root was 404)
832
+ - README.md: comparison table now includes Engram column (arXiv 2606.09900, 83.6% LongMemEval retrieval, single author) + Governance layer row. Engram is the retrieval SOTA but has no governance/multi-agent/source attribution.
833
+
834
+ ## [0.1.0a37] - 2026-10-06
835
+
836
+ ### Fixed - plugin manifest nested mcpServers bug
837
+
838
+ - claude-plugin / cursor-plugin / openclaw-plugin manifest.json had `"mcpServers": {"mcpServers": {...}}` (double nesting) — the outer key would be ignored by Claude Code MCP parser, silently breaking plugin config.
839
+
840
+ ### Added - P1-I plugin real implementation (not just config templates)
841
+
842
+ - claude-plugin/memtether-memory/: install.sh (Linux/macOS) + install.ps1 (Windows) + SKILL.md (Claude Code skill file) + fixed manifest.json
843
+ - install.sh/ps1 runs memtether init + writes MCP config to ~/.claude.json
844
+ - SKILL.md teaches Claude Code when to use search_memory/add_memories/list_memories
845
+ - cursor-plugin/memtether/: install.sh + install.ps1 + manifest.json nested bug fixed
846
+ - openclaw-plugin/memtether/: install.sh + install.ps1 + manifest.json nested bug fixed
847
+ - All three plugins now have working install scripts (not just manifest + README)
848
+
849
+ '## [0.1.0a36] - 2026-10-06
850
+
851
+ ### Added - Documentation site (reproduce + Founding 10 challenge)
852
+
853
+ - docs/site/en/index.html - MemTether landing page with feature comparison table
854
+ - docs/site/en/reproduce.html - Every claim mapped to a reproducible command, with claim-to-evidence status table (Reproducible / Declared-pending-repro)
855
+ - docs/site/en/challenge.html - Founding 10 Challenge: 10 tracks, 14-day honest outcome, Founding Contributor credit
856
+ - README.md: Documentation section linking to reproduce + challenge pages
857
+
858
+ '## [0.1.0a35] - 2026-10-06
859
+
860
+ ### Added - D2 governance + gateway functional tests
861
+
862
+ - tests/test_governance_d2.py: 5 new tests (conflict polarity, same-polarity not flagged, unrelated entities, module exports).
863
+ - tests/test_gateway_func.py: 8 new functional tests (remember basic/tags/missing-source/empty-content, correct supersession chain, retire, duplicate dedup, tenant_id written).
864
+ - Discovered: gateway.remember returns dict, gateway.correct returns dict with new_uid (not uid) — tests corrected accordingly.
865
+ - Total tests: 87 → 100. Coverage: 25% → 26%. gateway.py 18% → 24%.
866
+
867
+ '## [0.1.0a34] - 2026-10-06
868
+
869
+ ### Added - D2 memsearch deep tests
870
+
871
+ - tests/test_memsearch_d2.py: 15 new unit tests covering scope_filter (default/all), tenant_filter (default/custom/sql-injection documented), parse_ttl, looks_like_state, is_placeholder, _terms (Chinese bigram + English ASCII), expected_dim, check_env, verify_active_consistency.
872
+ - Discovered: tenant_id filter only strips single quotes via chr(39) replace — semicolons pass through. Documented as behavior; parametrized query hardening is roadmap.
873
+ - Total tests: 72 → 87. Coverage: 24% → 25%. memsearch.py 46% → 47%.
874
+
875
+ '## [0.1.0a33] - 2026-10-05
876
+
877
+ ### Fixed - hubguard real bugs found by tests
878
+
879
+ - db_fingerprint() called db_path(db, explain=True) — db_path has no explain kwarg → always TypeError. Fixed to db_path(db).
880
+ - parse_fact_line() used global _LINE_RE without initializing it → NameError on first call. Added module-level _LINE_RE = None.
881
+ - Coverage: hubguard.py 61% (was 0%), memsearch.py 46%, gateway.py 18%, governance.py 21%. Total 24% (was 20%).
882
+ - tests/test_hubguard_ext.py: 10 new tests (atomic_write, safe_append, format/parse_fact_line roundtrip, pid_alive, db_fingerprint, snapshot, status, lock full cycle).
883
+ - Total tests: 62 → 72.
884
+
885
+ '## [0.1.0a32] - 2026-10-05
886
+
887
+ ### Changed - D1 honest absorb wording
888
+
889
+ - api_server.py /absorb docstring: "Keyword-based absorb (not semantic — LLM embedding absorb is roadmap)"
890
+ - CHANGELOG a19 wording: "semantic absorb endpoint" → "keyword-overlap absorb endpoint"
891
+
892
+ ### Added - D1 memsearch extended tests
893
+
894
+ - tests/test_memsearch_ext.py: 10 new unit tests covering detect_question_type (counting/temporal), is_generic_garbage (positive + negative), extract_ascii_entities, extract_answer_hints, search_hybrid smoke + empty, compile_packet + build_scaffold smoke
895
+ - Total tests: 52 → 62. Coverage: 20% → 22%.
896
+
897
+ '## [0.1.0a31] - 2026-10-05
898
+
899
+ ### Fixed - Round9 real-implementation audit (all P0 bugs fixed)
900
+
901
+ #### P0-1: import-docs CLI routing bug (CRITICAL)
902
+ - `memtether.py` L514-522 had a variable shadowing bug where `sp` was reused for both `dashboard` and `import-docs` parsers, causing `set_defaults(func=_cmd_dashboard)` to override `_cmd_import_docs`. Running `memtether import-docs` was starting the API server instead of importing docs.
903
+ - Fix: split into `sp_dash` and `sp_import` with independent `set_defaults()`.
904
+ - Verified: `memtether import-docs <dir> --source codex` now correctly imports (1 memory added from test fixture), no API server spawn.
905
+
906
+ #### P0-2: multi-tenant now actually works (was dead code)
907
+ - `gateway.py` INSERT now includes `tenant_id` column (was missing in a30).
908
+ - `memsearch.py` added `_tenant_filter()` (was defined but never called) — all 3 `FROM facts WHERE status='active'` queries now append tenant filter.
909
+ - `memtether.py` CLI `remember`/`search` added `--tenant` arg; also respects `MEM_TENANT_ID` env.
910
+ - Tests: `tests/test_tenant.py` (3 cases, including cross-tenant 0-leak assertion). Previously tenant B could see tenant A's content.
911
+
912
+ #### P0-3: rate limiter decorators now actually attached (was 0 endpoints)
913
+ - `api_server.py` `/remember` `/search` `/correct` `/retire` all have `@_limiter.limit("60/minute")`.
914
+ - Tests: `tests/test_rate_limit.py` (2 cases verifying decorator presence on all 4 mutating endpoints).
915
+
916
+ #### P0-4: CHANGELOG honesty for plugins
917
+ - P7 description clarified as "configuration templates, not full plugin implementations" with pointer to `memtether mcp-server`.
918
+
919
+ ### Added - test coverage (P1-1)
920
+ - `tests/test_memsearch.py` (3 cases): search_hybrid keyword hit, no cross-hit, scope filter excludes private.
921
+ - `tests/test_hubguard.py` (3 cases): lock acquire/release, exclusivity, status.
922
+ - `tests/test_governance.py` (2 cases): conflict detection positive + negative.
923
+ - Total tests: 39 → 52 (+13). Coverage: 5.2% → 20%.
924
+
925
+ ## [0.1.0a30] - 2026-10-05
926
+
927
+ ### Added - CCF upgrade Phase 1-3 (competitiveness gap closure)
928
+
929
+ #### P1: async search/remember (thread-pool wrapper; prevents event loop blocking. Not native async — DB calls remain synchronous)
930
+ - async endpoints via run_in_executor - prevents event loop blocking during FTS5/vector computation. DB calls remain synchronous (SQLite limitation).
931
+
932
+ #### P2: rate limiting
933
+ - slowapi 60 req/min (optional dep, graceful degradation)
934
+
935
+ #### P3: docs/memory-defense.md
936
+ - 6 defense layers documented: OWASP guard, PII, scope, concurrency, validation, retire guard
937
+
938
+ #### P4: import-docs command (cold start)
939
+ - memtether import-docs <dir> scans .md/.txt/.py, chunks, batch remembers
940
+
941
+ #### P5: L0-L3 layered stats (count breakdown by type, not content transformation)
942
+ - stats now shows experience(fact+incident)(decision+procedure)(preference+env) breakdown
943
+
944
+ #### P6: multi-tenant support
945
+ - facts.tenant_id column (default=default), _tenant_filter() for query isolation
946
+
947
+ #### P7: 3 official plugins (claude/cursor/openclaw) — configuration templates
948
+ - integrations/{claude,cursor,openclaw}-plugin/ with manifest.json + README
949
+ - NOTE: These are MCP config templates (manifest + docs), not full plugin implementations. Users configure their client's MCP to point at `memtether mcp-server` (stdlib stdio server). A full plugin with UI integration is a future roadmap item.
950
+
951
+ #### P8: Node.js SDK (memtether-js/)
952
+ - Thin CLI wrapper with remember/search/stats + .d.ts types
953
+
954
+ #### P9: docs/benchmark.html
955
+ - Static benchmark page with per-type breakdown + disclosure statement
956
+
957
+ ### Verified
958
+ - pytest 39/39, check_packaging rc=0, build rc=0
959
+
960
+ ## [0.1.0a29] - 2026-10-05
961
+
962
+ ### Fixed — T-C clean-venv end-to-end findings (stranger path)
963
+
964
+ - **health endpoint version stale**: /health reported 0.1.0a9 while package was
965
+ a28; now reads __version__ from the package metadata at import time.
966
+
967
+ ### Verified (clean venv stranger walkthrough)
968
+ - pip install from tuna mirror: 0.1.0a19 (PyPI lag confirmed - real finding)
969
+ - clean venv paths: default_db correctly resolves to LOCALAPPDATA after a28
970
+ - CLI: stats/remember/search --list/export-md/init all rc=0
971
+ - degraded vector search warns and falls back (expected without [vector] extra)
972
+ - REST API: /health /stats /list all 200 after pip install "memtether[server]"
973
+
974
+ ## [0.1.0a28] - 2026-10-05
975
+
976
+ ### Added - E-AFAG answer-formulation hints (CCF T-B: multi-session strict support)
977
+
978
+ - **extract_answer_hints()**: for counting/temporal/aggregation/knowledge-update
979
+ questions, extracts deterministic answer candidates from retrieved evidence
980
+ (entity occurrence counts, frequent numbers, earliest/latest dates, newest fact
981
+ per ASCII entity) and emits an [E-AFAG answer candidates] block so the agent can
982
+ copy verbatim forms. Multi-session strict fails on computed answers because
983
+ agents paraphrase - hints give the exact surface form.
984
+ - Wired into search_hybrid after scaffolds; hints land in diag + first result.
985
+ - tests/test_afag.py: 4 unit tests (counting/temporal/latest/no-hint).
986
+
987
+ ### Verified
988
+ - pytest 39/39 (34+5 federation + 4 AFAG net)
989
+ - no regression on existing benches
990
+
991
+ ## [0.1.0a27] - 2026-10-05
992
+
993
+ ### Added - federated exchange (CCF T-A: system-level upgrade)
994
+
995
+ - **resolve_conflict()**: real implementations of latest_wins / source_priority /
996
+ merge_concat / human_review over CONFLICT_STRATEGIES (was an unused enum)
997
+ - **HTTP peer federation**: serve_peer() (GET /exchange = signed export, POST /push =
998
+ import) + pull_peer() (pull + merge + auto conflict resolution). Stdlib only.
999
+ - **governance_detect_and_resolve()**: polarity-based conflict detection over merged
1000
+ rows, auto-supersedes losers (never deletes - keeps supersession chain intact)
1001
+ - **scripts/demo_federation.py**: two independent MemTether instances (laptop +
1002
+ desktop) exchange memories over HTTP; a newer contradicting fact auto-supersedes
1003
+ the older one. Exit 0 = PASS. Zero production writes.
1004
+
1005
+ ### Verified
1006
+ - federation demo: imported=1, superseded=1, loser retired, winner kept - PASS
1007
+ - 35 tests (30 existing + 5 new: 4 resolve_conflict unit + 1 federation e2e)
1008
+ - check_packaging rc=0
1009
+
1010
+ ## [0.1.0a26] - 2026-10-05
1011
+
1012
+ ### Fixed (audit round 7 — promised-vs-actual sweep)
1013
+ - **WAL actually enabled**: README/SECURITY claimed "SQLite WAL mode" since a1; journal_mode was `delete` (SQLite default) — concurrent readers hit "database is locked" during writes, in the very multi-client scenario the project exists for. init_db now sets `PRAGMA journal_mode=WAL` (persistent) + `busy_timeout=8000`. Verified: reader succeeds during an open write transaction.
1014
+ - FTS5 MATCH hardening: embedded double-quotes in user queries no longer produce syntax errors that silently kill the BM25 path per query (quotes stripped per token).
1015
+ - CORS: wildcard origins retained for keyless localhost use; when MEMTETHER_API_KEY is set, allowed origins are restricted (same-origin only). SECURITY.md gained a CORS section.
1016
+ - install.ps1 `-Home` now persists MEMTETHER_HOME (setx) — was process-only, so later CLI runs silently disagreed about the data directory; misleading ~/.memtether default message corrected.
1017
+ - Docker: compose default command referenced mem.py (removed in a20) — now runs the API server; .dockerignore keeps dev junk out of the build context.
1018
+ - Shipped user-facing instructions referenced removed module: `python mem.py search` replaced with `memtether search` in gateway.py hints, mcp_server tool description, INSTALL.md.
1019
+
1020
+ ### Verified
1021
+ - journal_mode=wal; read-during-write succeeds
1022
+ - FTS queries with embedded quotes no longer raise (covered by except + quote strip)
1023
+ - keyed CORS: cross-origin requests rejected pre-key
1024
+ - pytest 30/30; check_packaging rc=0
1025
+
1026
+ ## [0.1.0a25] - 2026-10-05
1027
+
1028
+ ### Fixed (audit round 6 — installer/Docker coherence + shipped broken instructions)
1029
+ - docker-compose default command ran python mem.py stats — mem.py was removed in a20 cleanup; the compose file referenced a module that no longer exists. Now runs the API server (uvicorn), which is what Docker users actually want.
1030
+ - .dockerignore added: dev _*.py junk, _dev/, build/, *.db etc. no longer leak into the image build context on dev machines (COPY *.py is a wildcard).
1031
+ - install.ps1: -Home flag now persists MEMTETHER_HOME via setx (was process-only env — later CLI runs silently disagreed); misleading ~/.memtether default messaging corrected.
1032
+ - Shipped broken instructions: gateway.py / mcp_server.py tool descriptions / INSTALL.md told users to run python mem.py search — mem.py does not exist in the package. All replaced with memtether search. (CHANGELOG historical entries left as-is.)
1033
+
1034
+ ### Verified
1035
+ - pytest 30/30; no remaining mem.py references in shipped code/usage docs (CHANGELOG history kept)
1036
+
1037
+ ## [0.1.0a24] - 2026-10-05
1038
+
1039
+ ### Fixed (audit round 5 — auth/dashboard coherence + doc rot)
1040
+ - API key + dashboard coherence: / and /dashboard HTML routes are auth-exempt (static UI); all dashboard fetch() calls attach Authorization from a new optional API-key field (localStorage). Previously setting MEMTETHER_API_KEY bricked the dashboard (every request 401, no way to authenticate).
1041
+ - MCP add_memories hardcodes scope=shared — now documented in the tool description so agents know private scope is not available over MCP.
1042
+ - examples/rest_api_client.py port unified to 8820 (was 8080; server/README/dashboard all use 8820).
1043
+ - hidden_run.pyw docstring referenced memory_maintenance.py which is not in this repo — note corrected to gateway process_events only.
1044
+
1045
+ ### Verified
1046
+ - /dashboard 200 without key; /stats 401 without/wrong key; 200 with key
1047
+ - pytest 30/30
1048
+
1049
+ ## [0.1.0a23] - 2026-10-05
1050
+
1051
+ ### Fixed (audit round 4 — split-brain DB defaults)
1052
+ - **Unified default DB resolution**: gateway / memsearch / CLI / api_server / mcp_server now all route through memtether_paths.default_db(). Prior state had THREE divergent defaults (~/.memtether for CLI, package-dir for engine imports, LOCALAPPDATA for the helper) — a pip user could get three different databases depending on which entry point they used. My a22 default-dir change added the third; a23 makes one truth.
1053
+ - Resolution order everywhere: MEM_DB env > MEM_HUB_DIR/local_paths.json > LOCALAPPDATA (~/.local/share) per-user dir > package dir only for dev checkouts (or MEMTETHER_IN_PKG=1).
1054
+
1055
+ ### Verified
1056
+ - helper / gateway / memsearch resolve identically with no env set
1057
+ - pytest 30/30; check_packaging rc=0
1058
+
1059
+ ## [0.1.0a22] - 2026-10-05
1060
+
1061
+ ### Fixed (audit round 3 — residual leaks from a20/a21 fixes)
1062
+ - /list and /absorb now exclude private/restricted scope (search was fixed in a20, these two endpoints were not — cross-client private read via dashboard or absorb)
1063
+ - hubguard db_path: implicit follow-gateway sniffing removed (it silently overrode MEM_DB env); explicit set_test_db()/clear_test_db() marker instead — env precedence restored
1064
+ - vector store path re-resolves per client call: DB re-pointing (init --force, tests) moves mem0_store with it instead of leaving vectors in the import-time directory
1065
+ - default data dir for pip users is now %LOCALAPPDATA%/memtether (site-packages installs would lose memory on upgrade); dev checkouts keep repo-root behavior; MEMTETHER_IN_PKG=1 forces old behavior
1066
+ - stale 86KB test-residue memory.db removed from repo root (was silently picked up as fallback DB on this machine)
1067
+ - absorb _polarity: governance lexicons imported once at module level (was per-call import)
1068
+ - README refuse_bench entry carries calibration + known-blind-spot framing
1069
+
1070
+ ### Verified
1071
+ - /list and /absorb leak tests: False/False
1072
+ - pip-like default lands in LOCALAPPDATA; dev fallback unchanged
1073
+ - chroma follows re-pointed DB; env/test-marker precedence correct in all 3 states
1074
+ - pytest 30/30; check_packaging rc=0
1075
+
1076
+ ## [0.1.0a21] - 2026-10-05
1077
+
1078
+ ### Cleanup (audit plan P2 remainder)
1079
+ - absorb: contradiction detection now uses governance POS/NEG lexicons (was a 6-word English/Chinese stopword list that missed real polarity words)
1080
+ - export-md: filename uses full uid (30-char prefix collision risk)
1081
+ - _search_like_legacy: marked DEPRECATED (kept as last-resort fallback only)
1082
+ - predicates.check_attr_coverage: explicitly marked NOT-WIRED (honesty note; write-side predicates exist, retrieval-side validation is future work)
1083
+
1084
+ ## [0.1.0a20] - 2026-10-05
1085
+
1086
+ ### Fixed (security audit round 2 — all items verified by test)
1087
+ - FTS5 path resolved DB correctly in pip installs (was hardcoded to package dir; silently broke BM25 in site-packages) — now MEM_DB-aware; :memory: DBs skip FTS
1088
+ - Ghost-vector guard in near-dup dedup: remember() no longer returns a stale uid when the vector store outlives its facts DB; ghosts purged (root cause of 2 flaky tests)
1089
+ - Dashboard XSS (last instance): memory list escapes content/type; audit view escapes uid/timestamp
1090
+ - API key auth actually implemented (MEMTETHER_API_KEY; docs previously claimed it existed): Bearer middleware, /health open, else 401
1091
+ - scope isolation real: private/restricted excluded from retrieval by default; MEM_SCOPE=all bypass; documented as query-layer (not encryption)
1092
+ - memtether_pipeline shipped: OWASP guard rules import in clean pip install
1093
+ - CI runs full tests/ dir (was 2 of 7 files); 30 passed incl. new exchange roundtrip test (replaces allow_module_level skip)
1094
+ - selfcheck C07 regex matches bracketless CHANGELOG headings
1095
+ - hubguard: file-lock skipped for :memory:; db_path follows re-pointed gateway.DB
1096
+ - README: pytest number corrected (30/30), graphiti ref removed; SECURITY.md scope matches implementation
1097
+
1098
+ ### Verified
1099
+ - pytest 30/30 single-process (was 2 failed)
1100
+ - check_packaging rc=0 (19 root modules)
1101
+ - selfcheck 11/11 PASS rc=0
1102
+
1103
+ ## 0.1.0a19 (2026-10-04)
1104
+
1105
+ ### Added
1106
+ - `GET /timeline/{uid}` — supersession chain API (forward evolution: who replaced this fact)
1107
+ - Dashboard **记忆审计** card — paste a UID, see the full supersession chain with diff-like status
1108
+ - `POST /absorb` — keyword-overlap absorb endpoint (classify: duplicate/update/contradiction/related/new, dry_run default)
1109
+ - `memtether export-md` — export active memories as .md files with YAML frontmatter + manifest.json (portable, greppable, git-able)
1110
+
1111
+ ## 0.1.0a18 (2026-10-04)
1112
+
1113
+ ### Added
1114
+ - `memtether search --list`: L0 summary mode (one-line + uid + score, agent reads full separately)
1115
+ - `memtether setup <client>`: one-command per-client setup (writes MCP config + verifies)
1116
+ - Hugging Face Spaces demo link in README Quick Start
1117
+ - "File, not pipeline" narrative in README header
1118
+
1119
+ ### Fixed
1120
+ - Benchmark table synced to 2026-10-02 canonical rerun (55.7% judge / 264-474, ms-judge 72/120)
1121
+ - E-Hybrid → EAF renamed, full-500Q EAF result (75.7%, 253/334) now in README
1122
+ - make_demo_db.py recipes schema mismatch (7-column gateway SCHEMA vs old 6-column)
1123
+ - Known Limitations #3 reframed: junction/symlink is a feature, not "two copies to sync"
1124
+
1125
+ # Changelog
1126
+
1127
+ 本文件记录 MemTether 的对外变更。格式遵循
1128
+ [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
1129
+ 版本号遵循 [PEP 440](https://peps.python.org/pep-0440/)(当前处于 alpha)。
1130
+
1131
+ > **关于数字的说明**:本项目**不自报分数、不与其它实现比较数值**。
1132
+ > 原因见 README「关于可验证性」一节 —— 基准口径不一致时,自报数字是负资产。
1133
+ > 本文件只记录**行为变更**与**可复现的验证命令**,不记录"提升了百分之几"。
1134
+
1135
+ ## [0.1.0a17] - 2026-10-04
1136
+
1137
+ ### Added
1138
+ - Web dashboard (`dashboard.html`) served at `/dashboard` with bilingual UI, live stats auto-refresh, and retire action
1139
+ - `memtether dashboard` CLI command: starts the API server and opens the browser
1140
+ - `start_dashboard.bat` one-click launcher (Windows)
1141
+ - Project icon (`assets/memtether.ico` + `assets/memtether-icon.png`)
1142
+ - `.github/FUNDING.yml` (GitHub Sponsors)
1143
+ - MCP Registry badge in README (EN + zh-CN)
1144
+
1145
+ ### Fixed
1146
+ - `scripts/check_packaging.py` now passes: py-modules registered `api_server` / `stress_board` / `test_multi_client` / `test_p3_4` / `test_p4_2_generic`; NOTICE declares blake3 / fastapi / uvicorn / httpx
1147
+ - `api_server.py` internal version string aligned to package version
1148
+ - Removed stray quote character in `dashboard.html` `<style>` block
1149
+
1150
+ ## [0.1.0a16] - 2026-10-03
1151
+
1152
+ ### Added
1153
+ - `mcp-name` in README for MCP Registry PyPI ownership verification
1154
+ - `server.json` for MCP Server Registry submission (io.github.lanbass869-cell/memtether)
1155
+ - SVG demo references in README.zh-CN.md
1156
+
1157
+ ### Fixed
1158
+ - Removed `topic_index.json` from repo (contained PII, never should have been committed)
1159
+ - Refreshed publish_guard baseline
1160
+
1161
+ ## [0.1.0a9] - 2026-09-28
1162
+
1163
+ ### Added
1164
+ - **REST API Server** (`api_server.py`):FastAPI 8 端点(remember / search / stats / correct / retire / list / qvalue / health),`pip install "memtether[server]"` 安装,`uvicorn api_server:app` 启动
1165
+ - **LLM 自动记忆抽取** (`extract.py`):OpenAI 兼容端点,fail-closed(无 key 拒跑、`--source` 必填),`--dry-run` 先看后写
1166
+ - **Dockerfile + docker-compose.yml**:`docker-compose up` 一键启动
1167
+ - **Exchange adapter pytest**(13 用例):6 适配器 × 导出+导入 + 跨适配器集成
1168
+
1169
+ ### Fixed
1170
+ - README L324 适配器数量从「5 个」修正为「6 个」(加 COGX)
1171
+ - README 补 LLM 自动抽取 + REST API 段落
1172
+ - terminal_demo.svg 插入 README 首屏
1173
+ - CI 安装 `[server]` extras 使 API 测试在 CI 中运行
1174
+
1175
+ ### Security
1176
+ - **PII 脱敏层** `sanitize_snapshot()` 导出快照自动脱敏
1177
+ - **OWASP 运行时防御** `memtether_guard.py` 提示注入/代码执行/敏感信息/社会工程检测
1178
+
1179
+ ---
1180
+
1181
+ ## [0.1.0a8] - 2026-09-26
1182
+
1183
+ ### Added
1184
+ - **Memory Exchange 冲突检测端到端 demo**:`scripts/demo_exchange_conflict.py`
1185
+ 合成 Mem0 导出 20 条 → Exchange Schema v1 → 临时 SQLite → 检出 2 组极性冲突 → 自动退役 → 复检 0 冲突。
1186
+ 全流程零生产库写入;文档 `docs/exchange_conflict_demo.md` 记录结果与诚实边界。
1187
+
1188
+ ### Fixed
1189
+ - `test_autosync.py` / `test_triggers.py`:补 `import os`(4604277 引入的 NameError)
1190
+ ---
1191
+
1192
+ ## [0.1.0a7] - 2026-09-25
1193
+
1194
+ ### Fixed
1195
+ - pyproject.toml UTF-8 BOM causing tomllib failure on Python 3.11+
1196
+ - regression_test.py missing import os
1197
+ - memtether.py __version__ drifted to a5 (resynced to a7)
1198
+ - 7 root modules missing from py-modules (concurrent_stress, memtether_export, memtether_guard, memtether_harness, memtether_paths, memtether_pipeline, test_pii_roundtrip)
1199
+
1200
+ ## [0.1.0a5] - 2026-09-22
1201
+
1202
+ ### Fixed
1203
+
1204
+ - **`gateway.py qvalue` 子命令不接受 `--source`**:原先只有 `--agent`,而
1205
+ `AGENTS.md` / `HARD-RULES.md` 全家约定是 `--source` ⇒ 照文档写会被 argparse
1206
+ 直接打回 exit 2。改为多选项字符串别名,两种写法等价、dest 不变(旧调用方零影响)。
1207
+ - **`gateway.py record_tool` 没有 `--source`**:分发处只能落默认来源,
1208
+ 调用方无法指定归属。补参数并透传。
1209
+ - **`gateway.py retire` 没有 `--source`,且函数体漏掉来源校验**:`retire()` 是
1210
+ 唯一不调 `_guard_source()` 的写入函数,by_agent 可直穿审计日志。两处都补。
1211
+ - **`gateway.py` / `memsearch.py` 共 6 处裸 `import governance`**:新增
1212
+ `_load_governance()` 改为按显式文件路径加载,杜绝仓库外同名模块遮蔽
1213
+ (对应事故档案 #10 的遗留半边)。
1214
+ - **`_count_active_facts()` 只统计 facts 不统计 tool_assets**:facts 为 0 而资产
1215
+ 非空时会误报「空库」,把诊断方向带偏。改为两表相加。
1216
+ - **stdout 未按 UTF-8 重配导致只读视图崩溃**:`gateway.py qvalue`(不带参数)与
1217
+ `peer_msg.py read` 在 GBK 控制台下遇 GBK 编不出的字符直接
1218
+ `UnicodeEncodeError`。新增 `_fix_stdio()` 在入口重配。
1219
+ - **`scripts/scan_leaks.py` 自身会崩**:该脚本是发布前泄密闸门,原先在 `report()`
1220
+ 里崩掉并返回 exit 1,会被误读成「发现泄密」而真相是根本没扫完。同样重配 stdio。
1221
+
1222
+ ### Changed
1223
+
1224
+ - `memory_hub/README.md`:唯一真源由 `sink.json` 更正为 `memory.db`(v4 起);
1225
+ 写入入口统一为 `gateway.py`;移除不存在的 `agentctl.py` 引用。
1226
+
1227
+ ## [0.1.0a4] - 2026-09-21
1228
+
1229
+ 本版做了五件事:**修掉一个会让所有新装用户拿到坏 MCP 通道的问题**、
1230
+ **把客户端接入从手工变成一条命令**、**补回写入路径的来源校验**、
1231
+ **Windows 一键安装套件**、**修掉 embedding 错误路径上的 AttributeError**。
1232
+
1233
+ ### 新增
1234
+
1235
+ - **Q-Value 覆盖 tool_assets(升级 1 结构性补全)** —— 此前 Q-Value 机制只对
1236
+ `facts` 生效:`tool_assets` 没有 q_value/use_count 列、检索候选池里资产没有
1237
+ q_value 键(加权兜底 0.5、因子恒 0.65)、`bump_qvalue` 也只更新 facts。
1238
+ 66 条资产占检索结果近半,「哪条真有用」的信号对它们完全失效。
1239
+ 现在:① SCHEMA + `_ensure_columns` 幂等给资产补两列(老库自动迁移);
1240
+ ② `bump_qvalue` 双表(uid 不在 facts 时落 tool_assets,返回 `table` 字段);
1241
+ ③ 统计改 UNION 子查询合并 facts + assets;④ memsearch 候选池补 q_value 键。
1242
+ 实测:`qvalue_upshift_test` 名次 6→3 上升 3 位;发布版 demo 库冒烟 100+10 全绿。
1243
+ - **Windows 一键安装套件**(`install.ps1` / `install.bat` / `INSTALL.md`)——
1244
+ 四步流水线:探测 Python >= 3.10 → 幂等装包(版本与 `pyproject.toml` 不一致才重装)
1245
+ → 生成演示库 → `tether_connect` 自动接入客户端 → `memtether stats` 收尾自检。
1246
+ 任一步失败即停,退出码指明死因(1=环境 2=装包 3=演示库 4=接入)。
1247
+ 路径全部相对 `$PSScriptRoot`,无本机绝对路径。核心链路已用隔离 venv
1248
+ (Python 3.10.11)实测:装出 `0.1.0a3`、演示库就绪、stats 正常。
1249
+
1250
+ ### 修复
1251
+
1252
+ - **★embedding 错误路径上的 AttributeError** —— `_embed_local` 在
1253
+ `available()=False`(模型目录缺失)时拼错误信息,引用了 `embed_local.MODEL_DIR`
1254
+ ——该属性不存在(真实常量是 `HUB` / `MODELS`)。于是"模型文件缺失"这种
1255
+ 最需要清晰报错的场景,抛出的却是难懂的 `AttributeError`。
1256
+ **happy path 永远测不到**(模型在位时该行不执行),只有模型目录缺失时才炸。
1257
+ 改为 `os.path.join(embed_local.HUB, 'models')`,双路实测:缺失时报可读路径、
1258
+ 模型在位时 encode 正常(dim=1024)。
1259
+
1260
+ - **★MCP 通道「从第一天起就是坏的」(严重)** —— 旧版把重库 `import`
1261
+ (`numpy` / `chromadb` 的 C 扩展)放在**后台预热线程**里,而本进程主线程正阻塞在
1262
+ `for line in sys.stdin`(MCP 的 stdio 循环)。Windows 下这个组合会**永久死锁**:
1263
+ `faulthandler` 实测 25s / 50s 两次 dump 栈完全一致、零进展。
1264
+ 后果是连锁的,且**都不报错**:
1265
+ - `add_memories` 被**永久拒绝** → 写入通道等于不存在;
1266
+ - `search_memory` 被**永久降级**成纯 SQLite `LIKE`(中文整串匹配,实测 `count:0`)。
1267
+ 于是 Agent 试一次「MCP 不能用」就永久退回 CLI,MCP 通道形同虚设。
1268
+
1269
+ 修法两件:① 重库 `import` 搬回**主线程**预载(消掉死锁的触发组合);
1270
+ ② 预热加**硬超时**(`_WARM_HARD_TIMEOUT`,默认 25s)——万一将来又卡住,
1271
+ 也只会降级,不再永久拒绝。
1272
+
1273
+ 最小复现(与 import 哪个模块无关,只与「主线程阻塞 stdin」有关):
1274
+ ```
1275
+ 主线程 time.sleep → import numpy 0.07s / import chromadb 0.69s ✅
1276
+ 主线程读 stdin 管道 → 两者均 HANG > 60s ❌
1277
+ ```
1278
+
1279
+ - **写入路径完全没有来源校验** —— 脚本把自己的名字当 `source` 传进来会被照单全收,
1280
+ 归属被写花且没人会注意到(与「静默覆盖」同族:出错时不报错)。
1281
+ 现补上 `_known_sources()` / `_guard_source()`,并接到
1282
+ `remember` / `correct` / `record_tool` 三个写入入口。
1283
+
1284
+ 行为定义(**fail-open,不 fail-closed**):
1285
+ - 读不到 `agents.json` ⇒ **放行**(不能让「配置缺失」变成「写不进记忆」);
1286
+ - 中性默认值 `local` / `unknown` ⇒ **永远放行**(它们是「未识别来源」的诚实标记);
1287
+ - 未注册来源 ⇒ 有 `MEM_SOURCE_FALLBACK`(且该值已注册)则**软着陆回退** + 记 warning,
1288
+ 否则硬报错;`MEM_SOURCE_GUARD=0` 可应急放行。
1289
+ - **★为什么不直接 `raise SystemExit`**:它**不被** `except Exception` 捕获,
1290
+ 长驻进程(MCP server)会**直接死掉** —— 客户端侧表现为工具静默消失,
1291
+ 且不会自动拉起。比报错更糟。
1292
+
1293
+ ### 新增
1294
+
1295
+ - **`memtether-connect` —— 客户端自动接入器**。MCP 本身是手动档:找配置文件、
1296
+ 照 schema 写 JSON、(Electron 系)再去 UI 点一次信任;而每个客户端的 schema
1297
+ 都不一样,漏一个就有一个客户端读不到记忆。现在是一条命令:
1298
+
1299
+ ```bash
1300
+ memtether-connect detect # 发现本机装了哪些客户端、配置在哪、接没接
1301
+ memtether-connect plan # 预演,不写盘
1302
+ memtether-connect apply # 写入(备份 + manifest,可回滚)
1303
+ memtether-connect verify # 校验配置内容 + 信任状态
1304
+ memtether-connect rollback --stamp <时间戳>
1305
+ ```
1306
+
1307
+ 23 个适配器,分两类:`clients/standard.py`(标准客户端,路径与 schema 逐条
1308
+ 核对社区维护的 agent config 参考表)与 `clients/local.py`(本机实测的 Electron 系与 dsh 系)。
1309
+
1310
+ 写盘的四条硬约束:
1311
+ - **不猜** —— 找不到约定的根路径(schema 变了)就**失败关闭**,
1312
+ 绝不「大概写在这儿」。写坏用户的配置比不写更糟。
1313
+ - **保注释** —— 配置带 `//` 注释或尾逗号时(VS Code 系常见),用 JSONC 感知的
1314
+ 编辑器**只改该改的那一处**,不整份重排。
1315
+ - **幂等** —— 语义一致就**一个字节都不写**(路径分隔符风格、重复斜杠、空 `env`
1316
+ 先归一化再比较)。否则「每次跑都改写一遍本来正确的配置」。
1317
+ - **只碰装了的** —— 配置落在主目录/共享目录的客户端(`~/.claude.json` 的父目录
1318
+ 必然存在)会被误判成「已安装」,故额外查安装痕迹,找不到就跳过。
1319
+
1320
+ 另含两项此前只能手工做的事:**来源名注册**(不注册 ⇒ 写入被兜底成别的名字、
1321
+ 归属串号)与 **Electron 系信任代写**(按客户端同一算法
1322
+ `sha256(command|sorted(args)|sorted(env keys))` 算键,省掉「UI 显示已连接、
1323
+ Agent 拿不到工具」那一步)。
1324
+
1325
+ - **MCP server 的来源自动识别(零配置)**。原先只认 WorkBuddy 两版
1326
+ (硬编码两个子串),换任何别的客户端都识别不出 ⇒ 不传 `source` 的写入
1327
+ 被兜底成 `workbuddy`、归属串号。现改为**表驱动 + 两级匹配**:
1328
+
1329
+ 1. **父进程映像全路径** —— 覆盖 Electron 系(`WorkBuddy.exe` / `ZCode.exe` / Tabbit…);
1330
+ 2. **父进程命令行**(读 PEB)—— 覆盖「被通用宿主拉起」的情况:
1331
+ 独立 dsh / Claude Code 的父进程就是普通 `node.exe`,
1332
+ 只有命令行里才带 `@deepseek-ai/dsh` / `claude-code`。
1333
+
1334
+ 匹配按签名长度降序(保证 `workbuddyai` 先于 `workbuddy` 命中);
1335
+ 兜底值**中性化**为 `local`(可用 `MEM_FALLBACK_SOURCE` 覆盖);
1336
+ 用户可在同目录放 `client_signatures.json` 扩展,不必改代码。
1337
+
1338
+ > **为什么不直接用 `env` 配来源**:`env` 的 key 集合参与信任 hash,
1339
+ > 多一个 key 就掉信任,而掉信任后 server 会被客户端**静默跳过**
1340
+ > (只有日志里一行 `skipping untrusted`)。所以能自动就别让用户配。
1341
+
1342
+ ### 变更
1343
+
1344
+ - **行尾归一 LF + `.gitattributes` 入版本库** —— 本仓是开源发布库,跨平台 clone
1345
+ 是主场景。此前只靠局部 `core.autocrlf=false` 抵消 system 级 `true`
1346
+ (未入库,换机即失效),且 4 个历史文件(`tool_audit.py` /
1347
+ `asset_bench_holdout.py` / `rerank_k_bench.py` / `.release-baseline.json`)是 CRLF。
1348
+ 本次 `*.py/*.md/*.json/*.toml/*.cfg/*.txt` 全部 `text eol=lf` 并 renormalize
1349
+ (内容不变仅行尾)。根治跨仓 diff 假象:09-20 审计实测 memory_hub↔memtether 的
1350
+ `memsearch.py` 裸 diff 报 1849 行噪音,剥掉 CR 后真实差异仅 110 行——警戒线虚高 15 倍。
1351
+ - `pyproject`:`packages` 增加 `clients`(否则 `pip install` 出来的包**不带**
1352
+ 适配器包,工具一跑就 `ImportError`);`py-modules` 增加 `tether_connect`;
1353
+ 新增 console script `memtether-connect`。
1354
+ - `.gitignore`:开发产物归口到已忽略的 `dev/`;忽略 `claw_channel_watchdog.py`
1355
+ (被开机自启按**绝对路径**引用,故就地保留而非移走)。
1356
+
1357
+ ### 可复现的验证
1358
+
1359
+ ```bash
1360
+ # ① 客户端接入器自检(16 项:语义等价不写盘 / 安装判据 / Codex 原生 TOML 写法识别 / 同名表失败关闭)
1361
+ python tether_connect.py selftest
1362
+
1363
+ # ② 本机探测与预演(只读,不写盘)
1364
+ python tether_connect.py detect --hub-dir <你的中枢目录>
1365
+ python tether_connect.py plan --hub-dir <你的中枢目录>
1366
+
1367
+ # ③ JSONC 编辑器自检(24 项:注释 / 尾逗号 / CRLF / 缩进风格 / 深层路径)
1368
+ python clients/jsonc.py
1369
+
1370
+ # ④ 打包清单闸门(模块清单与 pyproject 是否一致)
1371
+ python scripts/check_packaging.py
1372
+ ```
1373
+
1374
+ 本机实测结论(隔离库 stdio 探针,非推断):`initialize 0.27s` →
1375
+ `tools/list 0.64s`(3 个工具)→ `add_memories 0.95s`(`ok:true`)→
1376
+ `search_memory 0.01s`(`engine=hybrid`,未降级)。
1377
+ 来源识别对**真实进程**实测:`WorkBuddy.exe→workbuddy`、
1378
+ `WorkBuddyAI.exe→workbuddy_ai`、`ZCode.exe→zcode`、`Tabbit Browser.exe→tabbit`、
1379
+ `node.exe`(OpenClaw 网关)`→openclaw`。
1380
+
1381
+ ---
1382
+
1383
+ ## [0.1.0a3] — 2026-09-18
1384
+
1385
+ 新增两项记忆治理能力(**默认中性,不改变任何现有排序**),并修掉一处检索误伤。
1386
+
1387
+ ### 新增
1388
+
1389
+ - **投影钉住 `pin`** —— 把「必须一直在」的定义类结论排除在时间竞争之外。
1390
+ 不新增 schema,复用 `tags` 字段(含 `pin` 即视为钉住);`rebuild()` 在选条阶段把
1391
+ pin 条目**补进**已选集合,因此不会被配额或时间序挤出。软上限
1392
+ `MEM_PROJ_PIN_MAX`(默认 10),超限时重建会打印告警并给出释放命令。
1393
+ **★改完必须 `rebuild` 才生效** —— 它只影响投影,不影响检索。
1394
+ ```bash
1395
+ python gateway.py pin <uid> # 钉住
1396
+ python gateway.py pin <uid> --off # 释放
1397
+ python gateway.py rebuild
1398
+ ```
1399
+
1400
+ - **被采纳价值分 Q-Value** —— 检索命中**被采纳**后回写,下次排序上浮。
1401
+ - `facts` 新增 `q_value REAL DEFAULT 0.5` 与 `use_count INTEGER DEFAULT 0`;
1402
+ - 回写公式 `q += LR * (reward - q)`(`LR = 0.1`,约 10 次观测收敛到长期均值);
1403
+ - 检索侧**只读**,因子 `score *= (0.3 + 0.7 * q)`,挂在时间衰减**之后**
1404
+ (塞进衰减块内会被自带重排覆盖;且衰减可被调用方关掉,挂在那里会漏算);
1405
+ - 开关 `MEM_QVALUE=0` 关闭;回写失败只告警一次并退化为纯相关性排序,**不崩**。
1406
+ ```bash
1407
+ python mem.py qvalue # 只读:看分布 / Top 榜
1408
+ python mem.py qvalue <uid> --reward 1 # 1=完全采纳 / 0.5=部分有用 / 0=检索到但没用
1409
+ python mem.py qvalue <uid> --dry-run # 只算不写
1410
+ ```
1411
+ > **它现在不等于"效果提升"**:全库 `q_value` 默认 0.5 → 因子恒为
1412
+ > `0.3+0.7×0.5 = 0.65`,对同一批候选是**同一常数**,在 RRF → min-max 精排链路里
1413
+ > 被完全抵消。而且"被采纳"目前**只能由调用方显式回写**,本项目没有自动判定机制。
1414
+ > 所以本版能证明的是**「机制正确且零副作用」**(见下方验收台),不是效果。
1415
+
1416
+ - **旧库自动迁移**:`gateway.init_db()` 现在会调 `_ensure_columns()` 幂等补列。
1417
+ `CREATE TABLE IF NOT EXISTS` 对**已存在**的旧库不生效 —— 旧库缺 `q_value` 时,
1418
+ `memsearch` 的显式 `SELECT q_value` 会直接抛异常,被外层 `except` 兜住后
1419
+ **检索整体降级**(不报错、但结果错)。这是本次特意堵的坑。
1420
+ 另附独立迁移脚本 `migrate_qvalue.py`(**默认只读检测**,`--apply` 才落库)。
1421
+
1422
+ - **验收台(源码在仓库里,可自行复跑)**:
1423
+ - `qvalue_ab.py` —— 开/关 A/B 对照。判据三条:分数**不得下降**、
1424
+ 逐题 PASS/FAIL 不翻转、逐题 Top10 内容序列**逐字节一致**。
1425
+ 做法是设 `MEM_QVALUE=0/1` 各跑一遍(子进程继承 env),
1426
+ 因此两侧跑的是同一份代码、同一个索引,不改验收台、不重建索引。
1427
+ - `qvalue_upshift_test.py` —— 排序上移实测。用**副本库 + 正本索引**
1428
+ 做单变量对照(轮 A 全库 0.5 → 轮 B 只把中位条目调到 0.99),
1429
+ 避免在生产库上写真值。找不到可用库时**显式报错**,不拿空库硬跑。
1430
+
1431
+ ### 修复
1432
+
1433
+ - **自指误伤(检索)**:`memsearch._is_self_referential` 情形 1 原实现写的是
1434
+ `if q in c:`,漏掉了同段注释里那半句「**含空格**」。后果是**单词查询**
1435
+ (单个专有名词 —— 工具名 / 模块文件名 / 内部代号这类)只要命中的记忆里出现
1436
+ 任意一个 `_SELFREF_TELL` 词(「实测 / 结论:/ 之前 / 必须 / 缺 / 坑」——
1437
+ 而这类词在真实记忆库里几乎条条都有),就被误判成"在谈论这个查询",
1438
+ `score ×0.05` 打入冷宫。现改为 `if q in c and ' ' in q.strip():`。
1439
+ 单词查询的真自指(如"实测:搜 XXX 返回 0 条")仍由情形 2 兜住。
1440
+
1441
+ ### 变更
1442
+
1443
+ - 版本 `0.1.0a2` → `0.1.0a3`(`pyproject.toml` 与 `memtether.py __version__` 同步)。
1444
+ - `py-modules` 补 3 项:`migrate_qvalue` / `qvalue_ab` / `qvalue_upshift_test`
1445
+ (`scripts/check_packaging.py` 会校验清单与仓库实际文件一致)。
1446
+ - README:补 `pin` / `qvalue` 用法与「它现在是什么水平」里的两条诚实说明。
1447
+
1448
+ ### 验证方式(可复现)
1449
+
1450
+ ```bash
1451
+ python scripts/check_packaging.py # 打包清单 vs 仓库实际文件
1452
+ python scripts/scan_leaks.py # 发布前泄密扫描(需 MEM_SCAN_TERMS 指向词表)
1453
+ python scripts/make_demo_db.py # 先要有一份可检索的库
1454
+ export MEM_DB=demo/memory_demo.db
1455
+ python qvalue_ab.py # 判据:分数不下降 + PASS/FAIL 不翻转 + Top10 一致
1456
+ python qvalue_upshift_test.py # 判据:被提升条目名次上升
1457
+ ```
1458
+
1459
+ > `qvalue_upshift_test.py` 若未指定 `--query`,会从库里取一段**真实存在**的连续串
1460
+ > 当查询词 —— 硬编码一个查询只在那台机器上成立,换一份库就返回 0 条,
1461
+ > 脚本会看起来"跑通了"其实什么也没测。
1462
+
1463
+ ---
1464
+
1465
+ ## [0.1.0a2] — 2026-09-17
1466
+
1467
+ 修复重发,无新功能。`0.1.0a1` 的发布包**不含**下列修复。
1468
+
1469
+ ### 修复
1470
+
1471
+ - **发布泄露面**:`.gitignore` 原先只写 `*.bak` 与 `*.bak_*`(下划线),
1472
+ **匹配不到** `<file>.bak-<YYYYMMDD-HHMMSS>`(连字符)这种快照名 ——
1473
+ 审计工具自己产生的快照会漏进发布集。现合并为 `*.bak*`。
1474
+ - **默认归属中性化**:`gateway.py remember` 的 `--source` 缺省值由
1475
+ `workbuddy` 改为中性值 `local`;开源仓库不预设任何客户端名。
1476
+ - **打包闸门**:`py-modules` 补齐至 52 项(`publish_pypi` / `wslog_append` /
1477
+ `slot_update` 此前漏列,`scripts/check_packaging.py` 会报红)。
1478
+ - **冒烟脚本**:`smoke_bitemporal.py` 用了 `os` 却没 `import os`,
1479
+ 直接运行会 `NameError`。
1480
+
1481
+ ### 变更(脱敏)
1482
+
1483
+ - 清除 6 处硬编码本机路径,改为环境变量 + 相对推导:
1484
+ `attach_hubguard.py`(`--target` 缺省取 `$MEM_HUBGUARD_TARGET`,
1485
+ 未设则推导为同级 `memory_hub/gateway.py`;注入块查找 `hubguard.py`
1486
+ 走 `$MEM_HUBGUARD_PATH`)、`hubguard.py` 文档、
1487
+ `patch-memory_hub-hubguard.diff`(按 `7bb566b` 基线重新生成)、
1488
+ `.release-baseline.json`、`scripts/scan_leaks.py` 注释。
1489
+ - `ARCHITECTURE.md` 重写为开源架构文档并脱敏;README 新增「仓库里的文件地图」。
1490
+
1491
+ ### 验证方式(可复现)
1492
+
1493
+ ```bash
1494
+ python scripts/scan_leaks.py # 发布集脱敏扫描(缺词表 exit 2)
1495
+ python scripts/scan_history_leaks.py # git 历史扫描
1496
+ python scripts/check_packaging.py # 打包清单与版本号闸门
1497
+ python hubguard.py selftest # 7 组并发 / 原子性自检
1498
+ ```
1499
+
1500
+ ---
1501
+
1502
+ ## [0.1.0a1] — 2026-09-17
1503
+
1504
+ 首个 alpha。研究原型,接口未冻结。
1505
+
1506
+ ### 新增
1507
+
1508
+ **核心**
1509
+ - `gateway.py`:记忆条目的唯一写入入口,每条记忆都带 `--source <来源名>`
1510
+ 归属标记(默认 `workbuddy`);支持
1511
+ `remember / correct / retire / record_tool / incident / search / rebuild` 等子命令。
1512
+ - 双时间轴(bi-temporal)治理:有效时间 `T` 与摄录时间 `T′` 分离。
1513
+ 命令行入口在 `mem.py`:`asof`(某时刻什么为真 / 系统当时认为什么为真)
1514
+ 与 `timeline`(沿替代链还原一条事实的演化过程),
1515
+ 由 `gateway.py` 的 `as_of()` / `timeline()` 实现。
1516
+ - 事实与**工具资产同池检索**,每条资产可带可执行校验命令。
1517
+ - 混合检索:向量 + 关键词 + 字面,RRF 融合后精排。
1518
+ - 本地 embedding 兜底(`bge-m3 int8`,1024 维),断网可跑;
1519
+ 语义路不可用时**自动降级为关键词 + 字面**并打印 `[warn]`,不崩溃。
1520
+
1521
+ **跨客户端共享**
1522
+ - 文件级指针方案:多个客户端指向**同一份物理 `memory.db`**,不做同步。
1523
+ - `slot_update.py`:共享注入槽位的原地更新器。把六步安全配方固化成一条命令 ——
1524
+ 锚点唯一化 / 预算闸门(只读试算)/ 同目录 `.bak-<时间戳>` 快照 /
1525
+ 同目录 `mkstemp` + `os.replace` 原子写 / 换行跟随目标 / 回读逐字节比对。
1526
+ **任一步失败即整体中止,一个字节都不写。** 支持 `--dry-run` 与 `--selftest`。
1527
+ - `wslog_append.py`:多写者共写同一日志的原子追加器。用
1528
+ `CreateFileW(FILE_APPEND_DATA)` + `WriteFile` 实现真正的原子追加
1529
+ (Windows CRT 的 `O_APPEND` 是 seek + write,非原子,多进程会互覆)。
1530
+ 并发演练 8 进程 × 50 行:原子追加丢行 0;对照的读-改-写大量丢行
1531
+ (数百条,随竞态波动,不固定)。
1532
+
1533
+ **并发与一致性**
1534
+ - `hubguard.py`:跨进程写锁(`hub_lock`)+ 写函数包装(`install_guards`);
1535
+ 同一 inode 整体重写的防护(`commit_guarded` / `snapshot` / `same_snapshot`)。
1536
+ - `attach_hubguard.py`:把上述机制接进既有 `gateway.py` 的接入器 ——
1537
+ 投影条目保留**来源**标记(`_hg_fact_line` / `_count_tagged`,实现在 `gateway.py`);
1538
+ 支持 `check` / `patch` / `apply` / `revert`。
1539
+
1540
+ **脱敏与发布前检查**
1541
+ - `scripts/scan_leaks.py`:对**发布集**(`git ls-files` 口径)扫描敏感串,
1542
+ 分 BLOCK / WARN 两档,**词表缺失时失败闭锁**(exit 2 + "无法判定"),绝不输出假绿。
1543
+ - `scripts/scan_history_leaks.py`:对 **git 历史文本 blob** 扫描。
1544
+ 历史里的命中无法靠改文件消除,必须重写历史 —— 工具会明确说明这一点。
1545
+ - `scripts/make_demo_db.py`:生成**全合成**演示库(不含任何真实数据),
1546
+ 自带三项自检:泄密检查 / 表结构一致性 / 功能冒烟。
1547
+ - `publish_pypi.py`:PyPI 发布器。显式上传名单(只认 `whl` + `tar.gz`,
1548
+ **绝不用 `dist/*`**);token 只从环境变量或剪贴板读,**绝不落盘、不进 git**;
1549
+ 三步闸门(归档杂物 → `twine check` → 上传),任一不过即中止。
1550
+
1551
+ ### 已知限制
1552
+
1553
+ - **不包含真实记忆数据。** 仓库只提供**合成演示库**;真实库含个人标识信息,
1554
+ 不在开源范围内。这是刻意设计,不是遗漏。
1555
+ - **未发布到 PyPI。** 目前请从 git 安装:
1556
+ `pip install "memtether @ git+https://github.com/MemTether/MemTether"`。
1557
+ - 一键安装脚本尚未提供。
1558
+ - 语义检索需额外装 `chromadb / onnxruntime / tokenizers`;
1559
+ 缺省安装下检索会降级为关键词 + 字面(会打印 `[warn]`,属**已知降级**,
1560
+ 不视为通过 —— `make_demo_db.py --strict` 下会判失败)。
1561
+ - 本机 git 身份已治理为中性值;若你 fork 后提交,请自行确认
1562
+ `git config user.name/user.email` 不含个人标识。
1563
+
1564
+ ### 验证方式(可复现)
1565
+
1566
+ ```bash
1567
+ # 1) 生成演示库并跑三项自检(无本地词表时诚实报「无法判定」)
1568
+ python scripts/make_demo_db.py
1569
+
1570
+ # 2) 发布集脱敏扫描(需自备词表;缺词表会 exit 2 而不是假绿)
1571
+ python scripts/scan_leaks.py
1572
+
1573
+ # 3) git 历史脱敏扫描
1574
+ python scripts/scan_history_leaks.py
1575
+
1576
+ # 4) 共享槽位更新器自检(临时目录,不碰线上)
1577
+ python slot_update.py --selftest
1578
+
1579
+ # 5) 共写日志原子追加演练
1580
+ python wslog_append.py --selftest
1581
+ ```
1582
+
1583
+ ### 许可
1584
+
1585
+ Apache-2.0(含专利授权)。第三方归属见 `NOTICE`。
1586
+
1587
+ ---
1588
+
1589
+ ## 版本路线图(非承诺)
1590
+
1591
+ - `0.1.0a2`:修复重发(无新功能),见上。
1592
+ - 后续 alpha:一键安装脚本;把自检收敛成单条命令并输出报告。
1593
+ - `0.2.0`:MCP Server 封装(`add_memories` / `search_memory` /
1594
+ `list_memories` / `delete_all_memories`);技能自动沉淀闭环。
1595
+ - 接口在 `0.1.x` 期间可能变动,`1.0` 前不做兼容承诺。
1596
+
1597
+ ## [0.1.0a10] - 2026-10-01
1598
+
1599
+ ### Added
1600
+ - Bilingual query expansion: `synonyms.json` with 47 zh-en synonym groups
1601
+ - `memsearch.py` auto-loads synonyms.json and expands search terms before FTS/vector lookup
1602
+ - Tool assets added to FTS5 index (previously only in vector store)
1603
+
1604
+ ### Fixed
1605
+ - FTS5 index: cleaned 420 stale entries, rebuilt 194 missing entries (100% active coverage)
1606
+ - Q-Value differentiation: procedures=0.8, decisions=0.6, incidents=0.55, high-use=0.65 (was 99.7% flat at 0.5)
1607
+ - Tool assets Q-Value: bumped to 0.7 (was 0.5)
1608
+ - Vector index consistency: 1215==1215 (removed 1 stale entry)
1609
+ - Removed machine-specific proxy port from synonyms.json
1610
+
1611
+ ### Improved
1612
+ - asset_bench: 22/23 → 23/23 (A1 "eNSP 装在哪里" now finds tool_asset with correct path)
1613
+ - Cross-language search: browser↔浏览器 overlap from 0 to 10/10
1614
+ - hard_bench: 62/62 (no regression)
1615
+ - e2e_verify: 13/13 (no regression)
1616
+ ## [0.1.0a11] - 2026-10-01
1617
+
1618
+ ### Added
1619
+ - Four-factor re-ranking (MemX arXiv 2603.16171): semantic(0.45) + recency(0.25) + frequency(0.05) + importance(0.10) blended with RRF (70/30)
1620
+ - Z-score + sigmoid normalization for cross-query score comparability
1621
+ - Three-layer deduplication: supersession-aware + content + tag-signature (MemX Section 3.5)
1622
+ - Low-confidence rejection marker in diag (MemX Section 3.6, threshold 0.50)
1623
+ - Self-referential poisoning fix v2: narrower criteria (only meta-discussion triggers)
1624
+ - Procedure quality check: first sentence must be complete conclusion (>=15 chars)
1625
+ - Q-Value write-back on retrieval (bump inside search_hybrid, MEM_BUMP env var to disable)
1626
+
1627
+ ### Fixed
1628
+ - Tool assets added to FTS5 index on record_tool (previously missing)
1629
+ - Vector index ghost entries cleaned on supersede (previously accumulated)
1630
+ - EMBED_BACKEND_DEFAULT and LAST_EMBED_INFO initialization (pre-existing bugs)
1631
+ - mcp_server.py duplicate bump removed (now handled in search_hybrid)
1632
+
1633
+ ### Performance
1634
+ - hard_bench: 62/62 (no regression)
1635
+ - asset_bench: 23/23 (no regression)
1636
+ - e2e_verify: 13/13 (no regression)
1637
+ - Cross-language search: browser/browser 10/10, STM32 UART/serial 8/10
1638
+ ## [0.1.0a12] - 2026-10-01
1639
+
1640
+ ### Added
1641
+ - Question type detection: counting/comparison/temporal/knowledge-update/aggregation
1642
+ - Deterministic reasoning scaffolds (Auditable Memory arXiv 2609.38021 Stage 4)
1643
+ - counting: entity occurrence counting in results
1644
+ - temporal: chronological ordering of results
1645
+ - comparison: top result pairing
1646
+ - aggregation: type/source distribution summary
1647
+ - Coverage-first packet compiler (Auditable Memory Stage 3): max 16 items, 12k chars
1648
+ - Phase 1: top-scored results
1649
+ - Phase 2: diversity bonus for unseen types/sources
1650
+ - Character budget trimming
1651
+ - All scaffolds and packet metadata exposed in diag for full auditability
1652
+
1653
+ ### Improved
1654
+ - Question type detection priority reordered (counting before aggregation)
1655
+ - Scaffold integrated into search_hybrid() return pipeline
1656
+ ## [0.1.0a41] - 2026-10-06
1657
+
1658
+ ### Fixed - P0 packaging + plugin MCP command (end-to-end verified)
1659
+
1660
+ - **dashboard.html ships in wheel** (a17-a40 all silently lacked it: package-data on "*" never applies to py-modules). New `memtether_assets` package carries it; `api_server.py /dashboard` falls back to `importlib.resources`. Verified in clean venv: pip install → `/dashboard` returns the real UI (was `<h1>Dashboard not found</h1>`).
1661
+ - **3 plugin manifests + install.sh/ps1 write `python -m mcp_server`** as the MCP command (was `memtether dashboard` = REST server, not stdio JSON-RPC; install overwrote the correct config that `init` had just written).
1662
+ - **Stale dev files purged from wheel** (108 → 52 files): `build/` was never cleaned, so 55 dead dev .py (Sep leftovers) shipped on PyPI since a12. Root cause fixed by removing `build/` before this build.
1663
+ - **`__version__` single-sourced**: reads `importlib.metadata` (falls back to pyproject.toml). Fixes 9-release drift (CLI said a30, pip said a40).
1664
+ - **Duplicate `_cmd_init`/`_cmd_connect` merged**: a39's init star hint was dead code (last def wins). Now init = demo DB + auto-connect + star hint. Verified end-to-end.
1665
+ - `check_packaging.py`: version-drift check replaced with data-file guard (dashboard.html must be in memtether_assets package-data), since __version__ is now dynamic.
1666
+