mindforge-memory 6.4.2__tar.gz → 6.4.5__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 (138) hide show
  1. {mindforge_memory-6.4.2/mindforge_memory.egg-info → mindforge_memory-6.4.5}/PKG-INFO +8 -6
  2. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/README.md +7 -5
  3. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/core/storage.py +9 -0
  4. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/core/version.py +2 -2
  5. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/connectors.py +9 -1
  6. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5/mindforge_memory.egg-info}/PKG-INFO +8 -6
  7. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge_memory.egg-info/SOURCES.txt +2 -0
  8. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v568_fixes.py +1 -1
  9. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v575_release_consistency.py +2 -2
  10. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v612_fixes.py +2 -2
  11. mindforge_memory-6.4.5/tests/test_v643_fixes.py +101 -0
  12. mindforge_memory-6.4.5/tests/test_v645_fixes.py +108 -0
  13. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/LICENSE +0 -0
  14. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/MindForge.py +0 -0
  15. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/__init__.py +0 -0
  16. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/adapters/__init__.py +0 -0
  17. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/adapters/claude_adapter.py +0 -0
  18. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/adapters/crewai_memory.py +0 -0
  19. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/adapters/generic_api.py +0 -0
  20. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/adapters/langgraph_store.py +0 -0
  21. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/adapters/openclaw_adapter.py +0 -0
  22. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/api/__init__.py +0 -0
  23. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/api/server.py +0 -0
  24. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/cli/__init__.py +0 -0
  25. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/cli/main.py +0 -0
  26. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/core/__init__.py +0 -0
  27. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/core/embedding.py +0 -0
  28. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/core/encryption.py +0 -0
  29. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/core/indexer.py +0 -0
  30. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/core/mindforge.py +0 -0
  31. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/core/paths.py +0 -0
  32. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/core/query.py +0 -0
  33. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/core/types.py +0 -0
  34. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/daemon/__init__.py +0 -0
  35. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/daemon/server.py +0 -0
  36. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/mcp/__init__.py +0 -0
  37. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/mcp/server.py +0 -0
  38. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/__init__.py +0 -0
  39. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/categorizer.py +0 -0
  40. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/conflict_detector.py +0 -0
  41. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/context_window.py +0 -0
  42. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/event_bus.py +0 -0
  43. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/evolution.py +0 -0
  44. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/experience.py +0 -0
  45. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/federated.py +0 -0
  46. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/federated_acl.py +0 -0
  47. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/follow_up.py +0 -0
  48. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/hybrid_search.py +0 -0
  49. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/importer.py +0 -0
  50. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/integrator.py +0 -0
  51. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/intent_router.py +0 -0
  52. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/knowledge_graph.py +0 -0
  53. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/memory_compressor.py +0 -0
  54. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/memory_decay.py +0 -0
  55. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/memory_passport.py +0 -0
  56. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/multi_agent.py +0 -0
  57. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/multimodal.py +0 -0
  58. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/outcome_learning.py +0 -0
  59. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/personality.py +0 -0
  60. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/principled_forgetting.py +0 -0
  61. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/privacy.py +0 -0
  62. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/recall.py +0 -0
  63. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/session_focus.py +0 -0
  64. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/share_conflict.py +0 -0
  65. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/skill_extractor.py +0 -0
  66. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/skill_pack.py +0 -0
  67. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/timeline_viz.py +0 -0
  68. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge/modules/v6.py +0 -0
  69. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge_memory.egg-info/dependency_links.txt +0 -0
  70. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge_memory.egg-info/entry_points.txt +0 -0
  71. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge_memory.egg-info/requires.txt +0 -0
  72. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/mindforge_memory.egg-info/top_level.txt +0 -0
  73. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/pyproject.toml +0 -0
  74. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/setup.cfg +0 -0
  75. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/setup.py +0 -0
  76. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_core.py +0 -0
  77. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_security_hardening.py +0 -0
  78. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v546_features.py +0 -0
  79. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v547_fixes.py +0 -0
  80. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v548_features.py +0 -0
  81. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v550_features.py +0 -0
  82. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v552_features.py +0 -0
  83. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v554_features.py +0 -0
  84. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v556_features.py +0 -0
  85. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v557_banner_insight_wal.py +0 -0
  86. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v557_security_fixes.py +0 -0
  87. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v557_webhook_sig_export.py +0 -0
  88. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v558_audit_fixes.py +0 -0
  89. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v558_features.py +0 -0
  90. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v560_decay_gc.py +0 -0
  91. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v560_kdf_versioning.py +0 -0
  92. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v560_module_coverage.py +0 -0
  93. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v561_security.py +0 -0
  94. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v562_p2_fixes.py +0 -0
  95. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v563_entry_coverage.py +0 -0
  96. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v563_perf_fixes.py +0 -0
  97. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v565_security.py +0 -0
  98. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v566_encrypted_search.py +0 -0
  99. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v567_security_fixes.py +0 -0
  100. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v569_fts_sync.py +0 -0
  101. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v570_coverage_expansion.py +0 -0
  102. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v570_drama_and_utils.py +0 -0
  103. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v570_encrypted_fallback.py +0 -0
  104. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v571_fixes.py +0 -0
  105. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v572_security.py +0 -0
  106. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v573_encrypted_export.py +0 -0
  107. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v573_security.py +0 -0
  108. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v576_bitemporal.py +0 -0
  109. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v576_gdpr.py +0 -0
  110. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v577_agent_governance.py +0 -0
  111. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v578_embedding.py +0 -0
  112. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v579_graph.py +0 -0
  113. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v580_experience.py +0 -0
  114. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v5812_bugfixes.py +0 -0
  115. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v583_security.py +0 -0
  116. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v584_adapters.py +0 -0
  117. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v585_benchmark.py +0 -0
  118. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v586_federated_mcp.py +0 -0
  119. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v590_features.py +0 -0
  120. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v592_features.py +0 -0
  121. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v593_features.py +0 -0
  122. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v595_features.py +0 -0
  123. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v600_forgetting_followup.py +0 -0
  124. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v600_import_timeline.py +0 -0
  125. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v600_multi_agent_formal.py +0 -0
  126. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v600_outcome_passport.py +0 -0
  127. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v600_skillpack_evolution.py +0 -0
  128. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v611_fixes.py +0 -0
  129. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v612_cascade_fixes.py +0 -0
  130. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v613_integrity.py +0 -0
  131. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v620_growth_governance.py +0 -0
  132. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v630_search_explain.py +0 -0
  133. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v631_scrypt.py +0 -0
  134. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v632_explain_interfaces.py +0 -0
  135. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v635_features.py +0 -0
  136. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v635_integration.py +0 -0
  137. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v636_fixes.py +0 -0
  138. {mindforge_memory-6.4.2 → mindforge_memory-6.4.5}/tests/test_v6_ports.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mindforge-memory
3
- Version: 6.4.2
3
+ Version: 6.4.5
4
4
  Summary: AI Agent 终身记忆系统 - 四层记忆架构 · 知识图谱 · 多模态支持
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/opok-ops/MindForge
@@ -36,11 +36,11 @@ Dynamic: license-file
36
36
 
37
37
  MindForge gives AI agents a persistent, structured long-term memory: a four-tier lifecycle model, knowledge graphs, hybrid retrieval, end-to-end encryption, and MCP / CLI / REST interfaces. All data stays on your machine.
38
38
 
39
- > **The story** — one developer, seven months, from v1.0 (March 2026) to v6.4.2: no team, no funding, 1,224 passing tests. [Read the journey →](https://opok-ops.github.io/MindForge/#developer)
39
+ > **The story** — one developer, seven months, from v1.0 (March 2026) to v6.4.5: no team, no funding, 1,232 passing tests. [Read the journey →](https://opok-ops.github.io/MindForge/#developer)
40
40
 
41
41
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
42
42
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
43
- [![Version](https://img.shields.io/badge/version-6.4.2-green.svg)](https://github.com/opok-ops/MindForge/releases)
43
+ [![Version](https://img.shields.io/badge/version-6.4.5-green.svg)](https://github.com/opok-ops/MindForge/releases)
44
44
  [![Stars](https://img.shields.io/github/stars/opok-ops/MindForge.svg)](https://github.com/opok-ops/MindForge/stargazers)
45
45
  [![Forks](https://img.shields.io/github/forks/opok-ops/MindForge.svg)](https://github.com/opok-ops/MindForge/forks)
46
46
  [![Open Issues](https://img.shields.io/github/issues/opok-ops/MindForge.svg)](https://github.com/opok-ops/MindForge/issues)
@@ -96,9 +96,11 @@ MindForge stats
96
96
 
97
97
  ## What's New
98
98
 
99
- v6.1.3 – v6.4.2 shipped in nine days (2026-11-01 → 11-09), guarded by **1,224 tests** (all passing).
99
+ v6.1.3 – v6.4.5 shipped in ten days (2026-11-01 → 11-10; two same-day releases), guarded by **1,233 tests** (1,232 passing, 1 environment-skipped).
100
100
 
101
- - **v6.4.2 — tens-of-thousands-chars latency: another 2×** (2026-11-09). Same-machine A/B benchmark (400 memories / ~47k chars, 3 runs each): **search p50 6.3→3.0ms (2.1×), p95 11.7→4.2ms (2.8×)**; 24k-char library p50 2.5ms; fast machine state 1.8ms. Five surgical cuts: WAL + `synchronous=NORMAL` (commits skip per-write fsync), enum-downgrade memo, `row.keys()` set reuse, first-visit access-count batching merged into one `flush_access()` transaction per search, and search-audit INSERT folded into the same commit. The v5.6.3 throttle contract and v5.5.4 `recently_accessed` semantics are pinned by 23 contract tests — all green. Compliance-grade audits still commit immediately.
101
+ - **v6.4.5 — audit-driven fixes** (2026-11-10; v6.4.4 skipped by maintainer decision). Two inverse doc-code inconsistencies (v6.4.3 fixed "claimed but not done"; this one fixes "done but not tracked"): `connectors.py` outgoing HTTP User-Agent was hardcoded `MindForge-Connector/5.8.9` — lying about the version to every target site since the 5.8.9 era (`event_bus.py` fixed the same disease in v5.6.8; this one slipped through). Now dynamic from the single version truth. Website "core modules" count was stale (17 in prose, 15 in stat card, 31 measured) — both fixed to the measured 31; MCP tool count 87 verified exact, untouched. Audit archive (not a defect): WAL auto-checkpoint verified healthy under daemon-like load — 4 rounds × 400 mixed ops, WAL converged at ~4.1MB (default 1000-page threshold), TRUNCATE resets to zero. 5 new guards in `tests/test_v645_fixes.py`.
102
+ - **v6.4.3 — documentation-code consistency fix** (2026-11-10). Pre-release audit found that v6.4.2's claimed first cut — WAL + `synchronous=NORMAL` — **never landed in code**: `_get_conn()` set only `journal_mode=WAL`, leaving `synchronous` at SQLite's default FULL. The 2.1× search gain was real, but carried entirely by the other four cuts — real numbers, misattributed cause. This release adds `PRAGMA synchronous=NORMAL` on the WAL branch (per-connection, cross-thread; network-FS DELETE fallback stays FULL by design). Same-machine A/B on SSD shows no statistically significant latency delta (search p50 1.8 → 1.8ms — fsync is ~0.1ms-scale here); the benefit targets slow disks and IO-saturated containers, and more importantly makes the documented power-loss semantics (un-checkpointed access counts / search audits may be lost; compliance audits still commit immediately) actually hold. 4 new guard tests in `tests/test_v643_fixes.py`.
103
+ - **v6.4.2 — tens-of-thousands-chars latency: another 2×** (2026-11-09). Same-machine A/B benchmark (400 memories / ~47k chars, 3 runs each): **search p50 6.3→3.0ms (2.1×), p95 11.7→4.2ms (2.8×)**; 24k-char library p50 2.5ms; fast machine state 1.8ms. Five surgical cuts: WAL + `synchronous=NORMAL` (commits skip per-write fsync), enum-downgrade memo, `row.keys()` set reuse, first-visit access-count batching merged into one `flush_access()` transaction per search, and search-audit INSERT folded into the same commit. The v5.6.3 throttle contract and v5.5.4 `recently_accessed` semantics are pinned by 23 contract tests — all green. Compliance-grade audits still commit immediately. *(Note: the `synchronous=NORMAL` cut landed in v6.4.3 — see above.)*
102
104
  - **v6.4.1 — million-character millisecond search** (2026-11-09). Scale benchmark (8,000 memories / ~990k chars) exposed a fuzzy-fallback long tail: search p95 881ms. Root causes: SequenceMatcher wasted on mathematically-impossible matches (ratio = 2M/(m+n), M ≤ m, so n ≥ 4m can never pass 0.4) + a ~50μs/entry Python full-scan tax. Fixed with a math-capped prune + SQLite-level candidate pre-filtering: **search p95 881→30.7ms (28×)**, add p50 2.6ms flat at 10k-scale. New `scripts/bench_scale.py` + CI scale guard (3000 memories, millisecond contract) + 4 behavior-level regression tests pin the semantics.
103
105
 
104
106
  - **v6.4.0 — resident daemon & performance guards** (2026-11-08). New `mindforge daemon` subcommand: line-JSON over stdio, engine resident in a long-lived process — per-request p50 2.5ms vs 400–700ms for CLI subprocess spawning (two orders of magnitude; 2026-11-09 quiet-machine re-bench: 2.5ms vs 401ms p50, ~160×). CI now guards latency (engine in-process thresholds) and daemon 13-step E2E alongside the 16-step CLI E2E guard.
@@ -166,7 +168,7 @@ v6.1.3 – v6.4.2 shipped in nine days (2026-11-01 → 11-09), guarded by **1,22
166
168
 
167
169
  ```
168
170
  ┌──────────────────────────────────────────────────────┐
169
- │ MindForge v6.4.2 │
171
+ │ MindForge v6.4.5 │
170
172
  │ Cognitive Layer Personality · KnowledgeGraph │
171
173
  │ MemoryEvolution · FederatedMemory │
172
174
  ├──────────────────────────────────────────────────────┤
@@ -6,11 +6,11 @@
6
6
 
7
7
  MindForge gives AI agents a persistent, structured long-term memory: a four-tier lifecycle model, knowledge graphs, hybrid retrieval, end-to-end encryption, and MCP / CLI / REST interfaces. All data stays on your machine.
8
8
 
9
- > **The story** — one developer, seven months, from v1.0 (March 2026) to v6.4.2: no team, no funding, 1,224 passing tests. [Read the journey →](https://opok-ops.github.io/MindForge/#developer)
9
+ > **The story** — one developer, seven months, from v1.0 (March 2026) to v6.4.5: no team, no funding, 1,232 passing tests. [Read the journey →](https://opok-ops.github.io/MindForge/#developer)
10
10
 
11
11
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
12
12
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
13
- [![Version](https://img.shields.io/badge/version-6.4.2-green.svg)](https://github.com/opok-ops/MindForge/releases)
13
+ [![Version](https://img.shields.io/badge/version-6.4.5-green.svg)](https://github.com/opok-ops/MindForge/releases)
14
14
  [![Stars](https://img.shields.io/github/stars/opok-ops/MindForge.svg)](https://github.com/opok-ops/MindForge/stargazers)
15
15
  [![Forks](https://img.shields.io/github/forks/opok-ops/MindForge.svg)](https://github.com/opok-ops/MindForge/forks)
16
16
  [![Open Issues](https://img.shields.io/github/issues/opok-ops/MindForge.svg)](https://github.com/opok-ops/MindForge/issues)
@@ -66,9 +66,11 @@ MindForge stats
66
66
 
67
67
  ## What's New
68
68
 
69
- v6.1.3 – v6.4.2 shipped in nine days (2026-11-01 → 11-09), guarded by **1,224 tests** (all passing).
69
+ v6.1.3 – v6.4.5 shipped in ten days (2026-11-01 → 11-10; two same-day releases), guarded by **1,233 tests** (1,232 passing, 1 environment-skipped).
70
70
 
71
- - **v6.4.2 — tens-of-thousands-chars latency: another 2×** (2026-11-09). Same-machine A/B benchmark (400 memories / ~47k chars, 3 runs each): **search p50 6.3→3.0ms (2.1×), p95 11.7→4.2ms (2.8×)**; 24k-char library p50 2.5ms; fast machine state 1.8ms. Five surgical cuts: WAL + `synchronous=NORMAL` (commits skip per-write fsync), enum-downgrade memo, `row.keys()` set reuse, first-visit access-count batching merged into one `flush_access()` transaction per search, and search-audit INSERT folded into the same commit. The v5.6.3 throttle contract and v5.5.4 `recently_accessed` semantics are pinned by 23 contract tests — all green. Compliance-grade audits still commit immediately.
71
+ - **v6.4.5 — audit-driven fixes** (2026-11-10; v6.4.4 skipped by maintainer decision). Two inverse doc-code inconsistencies (v6.4.3 fixed "claimed but not done"; this one fixes "done but not tracked"): `connectors.py` outgoing HTTP User-Agent was hardcoded `MindForge-Connector/5.8.9` — lying about the version to every target site since the 5.8.9 era (`event_bus.py` fixed the same disease in v5.6.8; this one slipped through). Now dynamic from the single version truth. Website "core modules" count was stale (17 in prose, 15 in stat card, 31 measured) — both fixed to the measured 31; MCP tool count 87 verified exact, untouched. Audit archive (not a defect): WAL auto-checkpoint verified healthy under daemon-like load — 4 rounds × 400 mixed ops, WAL converged at ~4.1MB (default 1000-page threshold), TRUNCATE resets to zero. 5 new guards in `tests/test_v645_fixes.py`.
72
+ - **v6.4.3 — documentation-code consistency fix** (2026-11-10). Pre-release audit found that v6.4.2's claimed first cut — WAL + `synchronous=NORMAL` — **never landed in code**: `_get_conn()` set only `journal_mode=WAL`, leaving `synchronous` at SQLite's default FULL. The 2.1× search gain was real, but carried entirely by the other four cuts — real numbers, misattributed cause. This release adds `PRAGMA synchronous=NORMAL` on the WAL branch (per-connection, cross-thread; network-FS DELETE fallback stays FULL by design). Same-machine A/B on SSD shows no statistically significant latency delta (search p50 1.8 → 1.8ms — fsync is ~0.1ms-scale here); the benefit targets slow disks and IO-saturated containers, and more importantly makes the documented power-loss semantics (un-checkpointed access counts / search audits may be lost; compliance audits still commit immediately) actually hold. 4 new guard tests in `tests/test_v643_fixes.py`.
73
+ - **v6.4.2 — tens-of-thousands-chars latency: another 2×** (2026-11-09). Same-machine A/B benchmark (400 memories / ~47k chars, 3 runs each): **search p50 6.3→3.0ms (2.1×), p95 11.7→4.2ms (2.8×)**; 24k-char library p50 2.5ms; fast machine state 1.8ms. Five surgical cuts: WAL + `synchronous=NORMAL` (commits skip per-write fsync), enum-downgrade memo, `row.keys()` set reuse, first-visit access-count batching merged into one `flush_access()` transaction per search, and search-audit INSERT folded into the same commit. The v5.6.3 throttle contract and v5.5.4 `recently_accessed` semantics are pinned by 23 contract tests — all green. Compliance-grade audits still commit immediately. *(Note: the `synchronous=NORMAL` cut landed in v6.4.3 — see above.)*
72
74
  - **v6.4.1 — million-character millisecond search** (2026-11-09). Scale benchmark (8,000 memories / ~990k chars) exposed a fuzzy-fallback long tail: search p95 881ms. Root causes: SequenceMatcher wasted on mathematically-impossible matches (ratio = 2M/(m+n), M ≤ m, so n ≥ 4m can never pass 0.4) + a ~50μs/entry Python full-scan tax. Fixed with a math-capped prune + SQLite-level candidate pre-filtering: **search p95 881→30.7ms (28×)**, add p50 2.6ms flat at 10k-scale. New `scripts/bench_scale.py` + CI scale guard (3000 memories, millisecond contract) + 4 behavior-level regression tests pin the semantics.
73
75
 
74
76
  - **v6.4.0 — resident daemon & performance guards** (2026-11-08). New `mindforge daemon` subcommand: line-JSON over stdio, engine resident in a long-lived process — per-request p50 2.5ms vs 400–700ms for CLI subprocess spawning (two orders of magnitude; 2026-11-09 quiet-machine re-bench: 2.5ms vs 401ms p50, ~160×). CI now guards latency (engine in-process thresholds) and daemon 13-step E2E alongside the 16-step CLI E2E guard.
@@ -136,7 +138,7 @@ v6.1.3 – v6.4.2 shipped in nine days (2026-11-01 → 11-09), guarded by **1,22
136
138
 
137
139
  ```
138
140
  ┌──────────────────────────────────────────────────────┐
139
- │ MindForge v6.4.2 │
141
+ │ MindForge v6.4.5 │
140
142
  │ Cognitive Layer Personality · KnowledgeGraph │
141
143
  │ MemoryEvolution · FederatedMemory │
142
144
  ├──────────────────────────────────────────────────────┤
@@ -757,6 +757,15 @@ class StorageEngine:
757
757
  conn.execute("PRAGMA journal_mode=DELETE")
758
758
  else:
759
759
  conn.execute("PRAGMA journal_mode=WAL")
760
+ # v6.4.3 fix: WAL 配套 synchronous=NORMAL(SQLite 官方推荐
761
+ # 组合)。v6.4.2 文档宣称了该优化但代码从未落地——本补丁
762
+ # 将其补齐:WAL+NORMAL 下 commit 免逐次 fsync,挂账批量
763
+ # 落库(search 尾事务)不再付 fsync 代价。
764
+ # 注意:synchronous 是 per-connection 参数,必须在每个
765
+ # 新连接创建时设置,仅在 WAL 分支生效;网络 FS 降级
766
+ # DELETE journal 的分支维持默认 FULL(远程盘耐久性语义
767
+ # 从严,不做削弱)。
768
+ conn.execute("PRAGMA synchronous=NORMAL")
760
769
  conn.execute("PRAGMA foreign_keys=ON")
761
770
  conn.execute("PRAGMA busy_timeout=5000")
762
771
  self._conn_local.conn = conn
@@ -20,10 +20,10 @@ core/mindforge.py、cli/main.py、mcp/server.py),每处都带一份硬编码
20
20
  from typing import Tuple
21
21
 
22
22
  #: 当前发布版本(唯一真值)
23
- __version__: str = "6.4.2"
23
+ __version__: str = "6.4.5"
24
24
 
25
25
  #: 结构化版本元组,便于比较(如 `VERSION_INFO >= (6, 1, 0)`)
26
- VERSION_INFO: Tuple[int, int, int] = (6, 4, 2)
26
+ VERSION_INFO: Tuple[int, int, int] = (6, 4, 5)
27
27
 
28
28
  #: 下一个大版本的目标版本号(供 v6 端口预留引用)
29
29
  NEXT_MAJOR_TARGET: str = "7.0.0"
@@ -18,6 +18,10 @@ import ipaddress
18
18
  import ssl
19
19
  import urllib.parse
20
20
  import urllib.request
21
+
22
+ # v6.4.5: User-Agent 版本动态化(唯一真值 mindforge/core/version.py)。
23
+ # version.py 仅依赖 typing,无循环导入风险。
24
+ from mindforge.core.version import __version__
21
25
  from abc import ABC, abstractmethod
22
26
  from typing import Any, Dict, List, Optional, Type
23
27
 
@@ -308,7 +312,11 @@ class UrlConnector(BaseConnector):
308
312
  _FixedIPHTTPSHandler(fixed_ip),
309
313
  )
310
314
  req = urllib.request.Request(
311
- source, headers={"User-Agent": "MindForge-Connector/5.8.9"})
315
+ # v6.4.5 fix: 此前硬编码 "MindForge-Connector/5.8.9"(5.8.9 时代
316
+ # 写死后再未跟上),对外请求谎报版本。改为动态引用唯一真值
317
+ # mindforge/core/version.py(与 event_bus.py / cli URL Importer
318
+ # 同款模式,v5.6.8 起已验证)。
319
+ source, headers={"User-Agent": f"MindForge-Connector/{__version__}"})
312
320
  with opener.open(req, timeout=kwargs.get("timeout", 15)) as resp:
313
321
  content_type = resp.headers.get("Content-Type", "")
314
322
  data = _read_limited(resp)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mindforge-memory
3
- Version: 6.4.2
3
+ Version: 6.4.5
4
4
  Summary: AI Agent 终身记忆系统 - 四层记忆架构 · 知识图谱 · 多模态支持
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/opok-ops/MindForge
@@ -36,11 +36,11 @@ Dynamic: license-file
36
36
 
37
37
  MindForge gives AI agents a persistent, structured long-term memory: a four-tier lifecycle model, knowledge graphs, hybrid retrieval, end-to-end encryption, and MCP / CLI / REST interfaces. All data stays on your machine.
38
38
 
39
- > **The story** — one developer, seven months, from v1.0 (March 2026) to v6.4.2: no team, no funding, 1,224 passing tests. [Read the journey →](https://opok-ops.github.io/MindForge/#developer)
39
+ > **The story** — one developer, seven months, from v1.0 (March 2026) to v6.4.5: no team, no funding, 1,232 passing tests. [Read the journey →](https://opok-ops.github.io/MindForge/#developer)
40
40
 
41
41
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
42
42
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
43
- [![Version](https://img.shields.io/badge/version-6.4.2-green.svg)](https://github.com/opok-ops/MindForge/releases)
43
+ [![Version](https://img.shields.io/badge/version-6.4.5-green.svg)](https://github.com/opok-ops/MindForge/releases)
44
44
  [![Stars](https://img.shields.io/github/stars/opok-ops/MindForge.svg)](https://github.com/opok-ops/MindForge/stargazers)
45
45
  [![Forks](https://img.shields.io/github/forks/opok-ops/MindForge.svg)](https://github.com/opok-ops/MindForge/forks)
46
46
  [![Open Issues](https://img.shields.io/github/issues/opok-ops/MindForge.svg)](https://github.com/opok-ops/MindForge/issues)
@@ -96,9 +96,11 @@ MindForge stats
96
96
 
97
97
  ## What's New
98
98
 
99
- v6.1.3 – v6.4.2 shipped in nine days (2026-11-01 → 11-09), guarded by **1,224 tests** (all passing).
99
+ v6.1.3 – v6.4.5 shipped in ten days (2026-11-01 → 11-10; two same-day releases), guarded by **1,233 tests** (1,232 passing, 1 environment-skipped).
100
100
 
101
- - **v6.4.2 — tens-of-thousands-chars latency: another 2×** (2026-11-09). Same-machine A/B benchmark (400 memories / ~47k chars, 3 runs each): **search p50 6.3→3.0ms (2.1×), p95 11.7→4.2ms (2.8×)**; 24k-char library p50 2.5ms; fast machine state 1.8ms. Five surgical cuts: WAL + `synchronous=NORMAL` (commits skip per-write fsync), enum-downgrade memo, `row.keys()` set reuse, first-visit access-count batching merged into one `flush_access()` transaction per search, and search-audit INSERT folded into the same commit. The v5.6.3 throttle contract and v5.5.4 `recently_accessed` semantics are pinned by 23 contract tests — all green. Compliance-grade audits still commit immediately.
101
+ - **v6.4.5 — audit-driven fixes** (2026-11-10; v6.4.4 skipped by maintainer decision). Two inverse doc-code inconsistencies (v6.4.3 fixed "claimed but not done"; this one fixes "done but not tracked"): `connectors.py` outgoing HTTP User-Agent was hardcoded `MindForge-Connector/5.8.9` — lying about the version to every target site since the 5.8.9 era (`event_bus.py` fixed the same disease in v5.6.8; this one slipped through). Now dynamic from the single version truth. Website "core modules" count was stale (17 in prose, 15 in stat card, 31 measured) — both fixed to the measured 31; MCP tool count 87 verified exact, untouched. Audit archive (not a defect): WAL auto-checkpoint verified healthy under daemon-like load — 4 rounds × 400 mixed ops, WAL converged at ~4.1MB (default 1000-page threshold), TRUNCATE resets to zero. 5 new guards in `tests/test_v645_fixes.py`.
102
+ - **v6.4.3 — documentation-code consistency fix** (2026-11-10). Pre-release audit found that v6.4.2's claimed first cut — WAL + `synchronous=NORMAL` — **never landed in code**: `_get_conn()` set only `journal_mode=WAL`, leaving `synchronous` at SQLite's default FULL. The 2.1× search gain was real, but carried entirely by the other four cuts — real numbers, misattributed cause. This release adds `PRAGMA synchronous=NORMAL` on the WAL branch (per-connection, cross-thread; network-FS DELETE fallback stays FULL by design). Same-machine A/B on SSD shows no statistically significant latency delta (search p50 1.8 → 1.8ms — fsync is ~0.1ms-scale here); the benefit targets slow disks and IO-saturated containers, and more importantly makes the documented power-loss semantics (un-checkpointed access counts / search audits may be lost; compliance audits still commit immediately) actually hold. 4 new guard tests in `tests/test_v643_fixes.py`.
103
+ - **v6.4.2 — tens-of-thousands-chars latency: another 2×** (2026-11-09). Same-machine A/B benchmark (400 memories / ~47k chars, 3 runs each): **search p50 6.3→3.0ms (2.1×), p95 11.7→4.2ms (2.8×)**; 24k-char library p50 2.5ms; fast machine state 1.8ms. Five surgical cuts: WAL + `synchronous=NORMAL` (commits skip per-write fsync), enum-downgrade memo, `row.keys()` set reuse, first-visit access-count batching merged into one `flush_access()` transaction per search, and search-audit INSERT folded into the same commit. The v5.6.3 throttle contract and v5.5.4 `recently_accessed` semantics are pinned by 23 contract tests — all green. Compliance-grade audits still commit immediately. *(Note: the `synchronous=NORMAL` cut landed in v6.4.3 — see above.)*
102
104
  - **v6.4.1 — million-character millisecond search** (2026-11-09). Scale benchmark (8,000 memories / ~990k chars) exposed a fuzzy-fallback long tail: search p95 881ms. Root causes: SequenceMatcher wasted on mathematically-impossible matches (ratio = 2M/(m+n), M ≤ m, so n ≥ 4m can never pass 0.4) + a ~50μs/entry Python full-scan tax. Fixed with a math-capped prune + SQLite-level candidate pre-filtering: **search p95 881→30.7ms (28×)**, add p50 2.6ms flat at 10k-scale. New `scripts/bench_scale.py` + CI scale guard (3000 memories, millisecond contract) + 4 behavior-level regression tests pin the semantics.
103
105
 
104
106
  - **v6.4.0 — resident daemon & performance guards** (2026-11-08). New `mindforge daemon` subcommand: line-JSON over stdio, engine resident in a long-lived process — per-request p50 2.5ms vs 400–700ms for CLI subprocess spawning (two orders of magnitude; 2026-11-09 quiet-machine re-bench: 2.5ms vs 401ms p50, ~160×). CI now guards latency (engine in-process thresholds) and daemon 13-step E2E alongside the 16-step CLI E2E guard.
@@ -166,7 +168,7 @@ v6.1.3 – v6.4.2 shipped in nine days (2026-11-01 → 11-09), guarded by **1,22
166
168
 
167
169
  ```
168
170
  ┌──────────────────────────────────────────────────────┐
169
- │ MindForge v6.4.2 │
171
+ │ MindForge v6.4.5 │
170
172
  │ Cognitive Layer Personality · KnowledgeGraph │
171
173
  │ MemoryEvolution · FederatedMemory │
172
174
  ├──────────────────────────────────────────────────────┤
@@ -131,4 +131,6 @@ tests/test_v632_explain_interfaces.py
131
131
  tests/test_v635_features.py
132
132
  tests/test_v635_integration.py
133
133
  tests/test_v636_fixes.py
134
+ tests/test_v643_fixes.py
135
+ tests/test_v645_fixes.py
134
136
  tests/test_v6_ports.py
@@ -39,7 +39,7 @@ import mindforge.core.version as core_version
39
39
 
40
40
 
41
41
  _REPO = Path(__file__).resolve().parent.parent
42
- _EXPECTED_VERSION = "6.4.2"
42
+ _EXPECTED_VERSION = "6.4.5"
43
43
 
44
44
  # 本轮修复涉及的、必须与 mindforge/core/version.py 保持一致的源码文件
45
45
  _VERSION_TRUTH_FILES = (
@@ -40,8 +40,8 @@ class TestVersionTruth(unittest.TestCase):
40
40
  def test_core_version_matches_release(self):
41
41
  # 版本推进时必须一并更新此处的钉值(与 test_v568_fixes 的
42
42
  # _EXPECTED_VERSION 同一约定:让「忘记 bump 版本」在测试里显形)。
43
- self.assertEqual(core_version.__version__, "6.4.2")
44
- self.assertEqual(core_version.VERSION_INFO, (6, 4, 2))
43
+ self.assertEqual(core_version.__version__, "6.4.5")
44
+ self.assertEqual(core_version.VERSION_INFO, (6, 4, 5))
45
45
 
46
46
  def test_pyproject_matches_core_version(self):
47
47
  # v5.8.13 起 pyproject 不再写死静态版本:
@@ -662,8 +662,8 @@ class TestVersionConsistency(unittest.TestCase):
662
662
  def test_version_is_612(self):
663
663
  from mindforge.core.version import VERSION_INFO, __version__
664
664
  # v6.1.3:版本钉值随发版推进更新(历史方法名保留)
665
- self.assertEqual(__version__, "6.4.2")
666
- self.assertEqual(VERSION_INFO, (6, 4, 2))
665
+ self.assertEqual(__version__, "6.4.5")
666
+ self.assertEqual(VERSION_INFO, (6, 4, 5))
667
667
 
668
668
  def test_json_round_trip_safety_of_preview(self):
669
669
  """save_evolution 的预览序列化不得抛(不可 json 化的对象走 str 兜底)。"""
@@ -0,0 +1,101 @@
1
+ """
2
+ MindForge v6.4.3 回归测试
3
+
4
+ 覆盖测试过程中发现的 1 个文档-代码不一致缺陷(2026-11-10):
5
+
6
+ - Bug-A(P0): v6.4.2 宣称的「WAL + synchronous=NORMAL」五刀第一刀
7
+ 从未落地——CHANGELOG/README/website 与 commit message 均宣称该
8
+ 组合已上线,但 _get_conn() 仅设置了 journal_mode=WAL,
9
+ synchronous 维持 SQLite 默认 FULL(WAL 下逐 commit fsync)。
10
+ 修复:WAL 分支补 PRAGMA synchronous=NORMAL(per-connection,
11
+ 每连接创建时设置;网络 FS 降级 DELETE 分支维持 FULL 从严)。
12
+
13
+ 守卫目标:让「PRAGMA 只在部分连接生效」「优化宣称了但没落地」
14
+ 这两类回归在测试里显形。
15
+ """
16
+
17
+ import os
18
+ import sys
19
+ import tempfile
20
+ import threading
21
+ import unittest
22
+ from pathlib import Path
23
+
24
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
25
+
26
+ from mindforge.core import storage as core_storage
27
+ from mindforge.core.version import __version__
28
+
29
+
30
+ class _WALCase(unittest.TestCase):
31
+ """本地盘临时库(走 WAL 分支)公共脚手架。"""
32
+
33
+ def setUp(self):
34
+ self.tmpdir = tempfile.TemporaryDirectory(prefix="mf_v643_fix_")
35
+ self.db = os.path.join(self.tmpdir.name, "memory.db")
36
+ self.storage = core_storage.StorageEngine(
37
+ db_path=self.db, encrypted=False
38
+ )
39
+
40
+ def tearDown(self):
41
+ try:
42
+ self.storage.close()
43
+ finally:
44
+ self.tmpdir.cleanup()
45
+
46
+
47
+ class TestSynchronousNormalGuard(_WALCase):
48
+ """Bug-A 守卫:WAL + synchronous=NORMAL 必须真实生效。"""
49
+
50
+ def test_synchronous_normal_on_local_wal(self):
51
+ # SQLite PRAGMA synchronous 返回值:0=OFF, 1=NORMAL, 2=FULL。
52
+ # v6.4.2 缺陷形态即此值恒为 2(FULL)。
53
+ conn = self.storage._get_conn()
54
+ mode = conn.execute("PRAGMA synchronous").fetchone()[0]
55
+ self.assertEqual(
56
+ mode, 1,
57
+ f"synchronous 应为 NORMAL(1),实际 {mode}——"
58
+ "v6.4.3 的 WAL+synchronous=NORMAL 修复未生效或被回退",
59
+ )
60
+
61
+ def test_journal_mode_wal_on_local_disk(self):
62
+ conn = self.storage._get_conn()
63
+ journal = conn.execute("PRAGMA journal_mode").fetchone()[0]
64
+ self.assertEqual(
65
+ str(journal).lower(), "wal",
66
+ f"本地盘 journal_mode 应为 wal,实际 {journal}",
67
+ )
68
+
69
+ def test_synchronous_is_per_connection(self):
70
+ # synchronous 是 per-connection 参数:StorageEngine 用
71
+ # threading.local 维护连接,新线程的连接也必须带上 NORMAL,
72
+ # 否则只保护了首线程(REST/daemon 多线程场景将静默回退 FULL)。
73
+ results = {}
74
+
75
+ def probe():
76
+ try:
77
+ conn = self.storage._get_conn()
78
+ results["mode"] = conn.execute(
79
+ "PRAGMA synchronous"
80
+ ).fetchone()[0]
81
+ except Exception as exc: # pragma: no cover - 显形优先
82
+ results["error"] = repr(exc)
83
+
84
+ t = threading.Thread(target=probe)
85
+ t.start()
86
+ t.join(timeout=10)
87
+ self.assertNotIn("error", results, results.get("error", ""))
88
+ self.assertEqual(results.get("mode"), 1)
89
+
90
+
91
+ class TestVersionBump(unittest.TestCase):
92
+ """版本钉值:忘记 bump 时在此显形(与 v568/v575/v612 守卫同一约定)。
93
+ v6.4.5 教训:每版 fixes 文件的版本断言在后续发版都要一并 bump——
94
+ 钉点集合随版本数递增,发版链清单必须扫全部 test_v*fixes.py。"""
95
+
96
+ def test_version_is_current(self):
97
+ self.assertEqual(__version__, "6.4.5")
98
+
99
+
100
+ if __name__ == "__main__":
101
+ unittest.main()
@@ -0,0 +1,108 @@
1
+ """
2
+ MindForge v6.4.5 回归测试
3
+
4
+ 覆盖发版前审计发现的两类缺陷(2026-11-10):
5
+
6
+ - Bug-A(P1): connectors.py 对外请求的 User-Agent 硬编码为
7
+ "MindForge-Connector/5.8.9"——5.8.9 时代写死后未跟上,v6.x 连发
8
+ 20+ 版本一直在对外谎报版本(event_bus.py v5.6.8 修过同款病,
9
+ 此处漏网)。修复:动态引用 mindforge/core/version.py 唯一真值。
10
+ - Bug-B(P2,数据过时): 官网「核心模块」数正文 17 / 统计卡 15,
11
+ 实测 mindforge/modules/ 为 31 个模块文件——两处口径双双过时且
12
+ 互相矛盾。修正为实测 31,并由本守卫钉住(website 数字必须等于
13
+ modules/ 实测文件数,防再漂移)。
14
+
15
+ 审计存档(非缺陷):WAL auto-checkpoint 行为验证——四轮 × 400 次
16
+ 混合操作(add+search),WAL 稳定收敛于 ~4.1MB(默认 1000 页阈值),
17
+ 无无限膨胀;主动 TRUNCATE 可归零。daemon 长连接场景健康。
18
+ """
19
+
20
+ import os
21
+ import re
22
+ import sys
23
+ import unittest
24
+ from pathlib import Path
25
+
26
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
27
+
28
+ _REPO = Path(__file__).resolve().parent.parent
29
+
30
+ from mindforge.core.version import __version__
31
+
32
+
33
+ class TestUserAgentVersionTruth(unittest.TestCase):
34
+ """对外 User-Agent 的版本必须动态引用唯一真值,不得硬编码。"""
35
+
36
+ def test_connector_ua_is_dynamic(self):
37
+ t = (_REPO / "mindforge" / "modules" / "connectors.py").read_text(
38
+ encoding="utf-8")
39
+ # 修复后的形态:f-string 引用 __version__
40
+ self.assertIn('f"MindForge-Connector/{__version__}"', t,
41
+ "connectors UA 应动态引用 __version__")
42
+ # 病灶形态必须消失:任何带版本号的硬编码 UA
43
+ bad = re.findall(r'User-Agent[^)]*?/[0-9]+\.[0-9]+\.[0-9]+', t)
44
+ self.assertEqual(bad, [], f"发现硬编码版本 UA: {bad}")
45
+ # import 必须存在(防 f-string 引用悬空)
46
+ self.assertIn("from mindforge.core.version import __version__", t)
47
+
48
+ def test_no_stale_version_in_runtime_ua(self):
49
+ """全包扫描:运行时 UA/标识字符串不得携带过时版本字面量。"""
50
+ pat = re.compile(r'MindForge[-A-Za-z]*/(?:\d+\.\d+\.\d+)')
51
+ for py in (_REPO / "mindforge").rglob("*.py"):
52
+ hits = pat.findall(py.read_text(encoding="utf-8"))
53
+ # 允许出现的位置:注释里的历史说明(v6.4.5 fix 注释提及旧值)
54
+ for line_no, line in enumerate(
55
+ py.read_text(encoding="utf-8").splitlines(), 1):
56
+ if pat.search(line) and "v6.4.5 fix" not in line:
57
+ # schema 标识(mindforge-knowledge-graph/1.0)为协议
58
+ # 格式版本,非产品版本,豁免。
59
+ if "knowledge-graph" in line:
60
+ continue
61
+ self.fail(
62
+ f"{py.name}:{line_no} 疑似硬编码版本 UA: {line.strip()}")
63
+
64
+
65
+ class TestWebsiteModuleCountTruth(unittest.TestCase):
66
+ """官网「核心模块」数 = mindforge/modules/ 实测文件数(防再过时)。"""
67
+
68
+ def _modules_count(self) -> int:
69
+ return len([p for p in (_REPO / "mindforge" / "modules").glob("*.py")
70
+ if not p.name.startswith("_")])
71
+
72
+ def test_website_module_count_matches_reality(self):
73
+ want = str(self._modules_count())
74
+ index = (_REPO / "website" / "index.html").read_text(encoding="utf-8")
75
+ spots = {
76
+ "正文": re.findall(r'(\d+) 个核心模块', index),
77
+ "统计卡": re.findall(
78
+ r'class="about-stat"><b>(\d+)</b><span>核心模块<', index),
79
+ }
80
+ for k, v in spots.items():
81
+ self.assertEqual(len(v), 1, f"官网「{k}」核心模块数不唯一: {v}")
82
+ self.assertEqual(v[0], want,
83
+ f"官网「{k}」={v[0]},实测 modules/={want}")
84
+
85
+ def test_website_mcp_tool_count_matches_reality(self):
86
+ """MCP 工具数守卫(87 实测口径;import server 拿 TOOL_SCHEMAS)。"""
87
+ try:
88
+ from mindforge.mcp.server import TOOL_SCHEMAS
89
+ except Exception as exc: # pragma: no cover
90
+ self.skipTest(f"server 模块不可导入: {exc}")
91
+ want = str(len(TOOL_SCHEMAS))
92
+ index = (_REPO / "website" / "index.html").read_text(encoding="utf-8")
93
+ hits = re.findall(r'(\d+) 个? ?MCP 工具|(\d+) MCP tools', index)
94
+ flat = [x for pair in hits for x in pair if x]
95
+ self.assertTrue(flat, "官网未声明 MCP 工具数")
96
+ for v in flat:
97
+ self.assertEqual(v, want, f"官网 MCP 工具数 {v} ≠ 实测 {want}")
98
+
99
+
100
+ class TestVersionBump(unittest.TestCase):
101
+ """版本钉值:忘记 bump 时在此显形。"""
102
+
103
+ def test_version_is_645(self):
104
+ self.assertEqual(__version__, "6.4.5")
105
+
106
+
107
+ if __name__ == "__main__":
108
+ unittest.main()