memorymaster 3.9.0__tar.gz → 3.10.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 (293) hide show
  1. {memorymaster-3.9.0 → memorymaster-3.10.0}/PKG-INFO +1 -1
  2. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/claim_edges.py +20 -2
  3. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/context_hook.py +135 -22
  4. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster.egg-info/PKG-INFO +1 -1
  5. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster.egg-info/SOURCES.txt +2 -0
  6. {memorymaster-3.9.0 → memorymaster-3.10.0}/pyproject.toml +1 -1
  7. memorymaster-3.10.0/tests/test_closets_recall_integration.py +50 -0
  8. memorymaster-3.10.0/tests/test_v391_strict_warnings.py +73 -0
  9. {memorymaster-3.9.0 → memorymaster-3.10.0}/LICENSE +0 -0
  10. {memorymaster-3.9.0 → memorymaster-3.10.0}/README.md +0 -0
  11. {memorymaster-3.9.0 → memorymaster-3.10.0}/artifacts/bm25-per-field-eval-harness.py +0 -0
  12. {memorymaster-3.9.0 → memorymaster-3.10.0}/benchmarks/longmemeval_runner.py +0 -0
  13. {memorymaster-3.9.0 → memorymaster-3.10.0}/benchmarks/longmemeval_vector_runner.py +0 -0
  14. {memorymaster-3.9.0 → memorymaster-3.10.0}/benchmarks/perf_smoke.py +0 -0
  15. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/__init__.py +0 -0
  16. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/__main__.py +0 -0
  17. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/_storage_lifecycle.py +0 -0
  18. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/_storage_read.py +0 -0
  19. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/_storage_schema.py +0 -0
  20. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/_storage_shared.py +0 -0
  21. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/_storage_write_claims.py +0 -0
  22. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/access_control.py +0 -0
  23. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/auto_extractor.py +0 -0
  24. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/auto_resolver.py +0 -0
  25. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/claim_verifier.py +0 -0
  26. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/cli.py +0 -0
  27. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/cli_handlers_basic.py +0 -0
  28. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/cli_handlers_curation.py +0 -0
  29. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/cli_helpers.py +0 -0
  30. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/closets.py +0 -0
  31. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/config.py +0 -0
  32. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/config_templates/claude-md-append.md +0 -0
  33. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/config_templates/codex-agents-md-append.md +0 -0
  34. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/config_templates/hooks/memorymaster-auto-ingest.py +0 -0
  35. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/config_templates/hooks/memorymaster-classify.py +0 -0
  36. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/config_templates/hooks/memorymaster-dream-sync.py +0 -0
  37. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/config_templates/hooks/memorymaster-observe.py +0 -0
  38. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/config_templates/hooks/memorymaster-precompact.py +0 -0
  39. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/config_templates/hooks/memorymaster-recall.py +0 -0
  40. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/config_templates/hooks/memorymaster-session-start.py +0 -0
  41. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/config_templates/hooks/memorymaster-steward-cycle.py +0 -0
  42. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/config_templates/hooks/memorymaster-validate-wiki.py +0 -0
  43. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/conflict_resolver.py +0 -0
  44. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/context_optimizer.py +0 -0
  45. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/daily_notes.py +0 -0
  46. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/dashboard.py +0 -0
  47. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/db_merge.py +0 -0
  48. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/dream_bridge.py +0 -0
  49. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/embeddings.py +0 -0
  50. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/entity_extractor.py +0 -0
  51. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/entity_graph.py +0 -0
  52. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/entity_registry.py +0 -0
  53. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/federated_graphify.py +0 -0
  54. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/feedback.py +0 -0
  55. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/graph_store.py +0 -0
  56. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/hook_log.py +0 -0
  57. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/jobs/__init__.py +0 -0
  58. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/jobs/compact_summaries.py +0 -0
  59. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/jobs/compactor.py +0 -0
  60. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/jobs/decay.py +0 -0
  61. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/jobs/dedup.py +0 -0
  62. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/jobs/deterministic.py +0 -0
  63. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/jobs/extractor.py +0 -0
  64. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/jobs/staleness.py +0 -0
  65. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/jobs/validator.py +0 -0
  66. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/key_rotator.py +0 -0
  67. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/lifecycle.py +0 -0
  68. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/llm_provider.py +0 -0
  69. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/llm_steward.py +0 -0
  70. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/mcp_server.py +0 -0
  71. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/metrics_exporter.py +0 -0
  72. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/models.py +0 -0
  73. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/operator.py +0 -0
  74. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/operator_queue.py +0 -0
  75. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/plugins.py +0 -0
  76. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/policy.py +0 -0
  77. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/postgres_store.py +0 -0
  78. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/qdrant_backend.py +0 -0
  79. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/qdrant_recall_fallback.py +0 -0
  80. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/qmd_bridge.py +0 -0
  81. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/query_classifier.py +0 -0
  82. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/query_expansion.py +0 -0
  83. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/recall_fusion.py +0 -0
  84. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/recall_tokenizer.py +0 -0
  85. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/retrieval.py +0 -0
  86. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/retry.py +0 -0
  87. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/review.py +0 -0
  88. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/rl_trainer.py +0 -0
  89. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/scheduler.py +0 -0
  90. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/schema.py +0 -0
  91. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/schema.sql +0 -0
  92. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/schema_postgres.sql +0 -0
  93. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/scope_utils.py +0 -0
  94. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/security.py +0 -0
  95. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/service.py +0 -0
  96. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/session_tracker.py +0 -0
  97. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/setup_hooks.py +0 -0
  98. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/skill_evolver.py +0 -0
  99. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/snapshot.py +0 -0
  100. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/steward.py +0 -0
  101. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/steward_classifier.py +0 -0
  102. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/steward_features.py +0 -0
  103. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/storage.py +0 -0
  104. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/store_factory.py +0 -0
  105. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/transcript_miner.py +0 -0
  106. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/turn_schema.py +0 -0
  107. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/vault_bases.py +0 -0
  108. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/vault_curator.py +0 -0
  109. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/vault_exporter.py +0 -0
  110. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/vault_linter.py +0 -0
  111. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/vault_log.py +0 -0
  112. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/vault_query_capture.py +0 -0
  113. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/vault_synthesis.py +0 -0
  114. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/verbatim_recall.py +0 -0
  115. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/verbatim_store.py +0 -0
  116. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/webhook.py +0 -0
  117. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/wiki_engine.py +0 -0
  118. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/wiki_freshness.py +0 -0
  119. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/wiki_similarity.py +0 -0
  120. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster/wiki_validate.py +0 -0
  121. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster.egg-info/dependency_links.txt +0 -0
  122. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster.egg-info/entry_points.txt +0 -0
  123. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster.egg-info/requires.txt +0 -0
  124. {memorymaster-3.9.0 → memorymaster-3.10.0}/memorymaster.egg-info/top_level.txt +0 -0
  125. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/agg_recall_latency.py +0 -0
  126. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/alert_operator_metrics.py +0 -0
  127. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/autoresearch_daemon.py +0 -0
  128. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/backfill_entity_extraction.py +0 -0
  129. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/backfill_graph_store.py +0 -0
  130. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/backfill_stop_hook_citations.py +0 -0
  131. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/backtest_steward_classifier.py +0 -0
  132. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/build_steward_training_set.py +0 -0
  133. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/check_hook_template_drift.py +0 -0
  134. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/claude_to_turns.py +0 -0
  135. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/codex_live_to_turns.py +0 -0
  136. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/compaction_edge_cases.py +0 -0
  137. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/compaction_trace_report.py +0 -0
  138. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/compaction_trace_validate.py +0 -0
  139. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/confusion_matrix_eval.py +0 -0
  140. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/conversation_importer.py +0 -0
  141. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/conversation_to_turns.py +0 -0
  142. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/e2e_operator.py +0 -0
  143. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/email_live_to_turns.py +0 -0
  144. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/eval_bm25_sweep.py +0 -0
  145. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/eval_classify_f1.py +0 -0
  146. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/eval_memorymaster.py +0 -0
  147. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/eval_recall_precision_at_5.py +0 -0
  148. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/eval_recall_quality.py +0 -0
  149. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/eval_steward_pareto.py +0 -0
  150. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/eval_verbatim_recall.py +0 -0
  151. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/expand_recall_eval.py +0 -0
  152. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/generate_drill_signoff.py +0 -0
  153. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/git_to_turns.py +0 -0
  154. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/github_live_to_turns.py +0 -0
  155. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/gitnexus_to_claims.py +0 -0
  156. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/grid_recall_weights.py +0 -0
  157. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/index_claims_to_qdrant.py +0 -0
  158. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/ingest_planning_docs.py +0 -0
  159. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/jira_live_to_turns.py +0 -0
  160. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/label_prompts_with_judge.py +0 -0
  161. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/llm_benchmark.py +0 -0
  162. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/merge_scope_variants.py +0 -0
  163. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/messages_to_turns.py +0 -0
  164. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/operator_metrics.py +0 -0
  165. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/precompute_candidates.py +0 -0
  166. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/recurring_incident_drill.py +0 -0
  167. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/release_readiness.py +0 -0
  168. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/run_codex_autologger.py +0 -0
  169. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/run_incident_drill.py +0 -0
  170. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/run_longmemeval.py +0 -0
  171. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/scheduled_ingest.py +0 -0
  172. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/setup-hooks.py +0 -0
  173. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/slack_live_to_turns.py +0 -0
  174. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/sync_hook_templates.py +0 -0
  175. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/tickets_to_turns.py +0 -0
  176. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/train_steward_classifier.py +0 -0
  177. {memorymaster-3.9.0 → memorymaster-3.10.0}/scripts/webhook_to_turns.py +0 -0
  178. {memorymaster-3.9.0 → memorymaster-3.10.0}/setup.cfg +0 -0
  179. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/conftest.py +0 -0
  180. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/integration/test_extract_llm_ollama_live.py +0 -0
  181. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_access_control.py +0 -0
  182. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_auto_extractor.py +0 -0
  183. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_auto_ingest_hook_citations.py +0 -0
  184. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_auto_ingest_hook_schema.py +0 -0
  185. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_auto_resolver.py +0 -0
  186. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_auto_validate.py +0 -0
  187. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_bm25_per_field.py +0 -0
  188. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_claim_edges.py +0 -0
  189. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_claim_links.py +0 -0
  190. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_claim_type_ranking.py +0 -0
  191. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_classify_hook_f1.py +0 -0
  192. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_classify_hook_latency.py +0 -0
  193. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_claude_to_turns.py +0 -0
  194. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_cli_json_flag.py +0 -0
  195. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_cli_ready.py +0 -0
  196. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_cli_review_queue.py +0 -0
  197. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_cli_subcommands.py +0 -0
  198. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_closets.py +0 -0
  199. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_compact_summaries.py +0 -0
  200. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_compaction_trace.py +0 -0
  201. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_config.py +0 -0
  202. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_conflict_resolver.py +0 -0
  203. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_confusion_matrix_eval.py +0 -0
  204. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_connection_retry.py +0 -0
  205. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_connectors.py +0 -0
  206. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_context_hook.py +0 -0
  207. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_context_optimizer.py +0 -0
  208. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_conversation_to_turns.py +0 -0
  209. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_dashboard.py +0 -0
  210. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_dedup.py +0 -0
  211. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_deterministic_predicates.py +0 -0
  212. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_embeddings_coverage.py +0 -0
  213. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_entity_extractor.py +0 -0
  214. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_entity_extractor_llm.py +0 -0
  215. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_entity_graph.py +0 -0
  216. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_entity_new_kinds.py +0 -0
  217. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_entity_regex_v3.py +0 -0
  218. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_entity_registry.py +0 -0
  219. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_eval_harness.py +0 -0
  220. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_events_schema.py +0 -0
  221. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_extract_llm_ollama.py +0 -0
  222. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_federated_graphify_mcp.py +0 -0
  223. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_feedback.py +0 -0
  224. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_fts5_search.py +0 -0
  225. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_graph_distance.py +0 -0
  226. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_graph_store.py +0 -0
  227. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_handler_regressions.py +0 -0
  228. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_hook_env_isolation.py +0 -0
  229. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_human_id.py +0 -0
  230. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_incident_drill_runner.py +0 -0
  231. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_integration_workflows.py +0 -0
  232. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_key_rotator.py +0 -0
  233. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_lifecycle.py +0 -0
  234. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_llm_fallback.py +0 -0
  235. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_llm_provider_claude_cli.py +0 -0
  236. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_llm_steward_coverage.py +0 -0
  237. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_llm_steward_key_rotation.py +0 -0
  238. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_mcp_helpers.py +0 -0
  239. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_metrics_exporter.py +0 -0
  240. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_obsidian_mind_patterns.py +0 -0
  241. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_operator.py +0 -0
  242. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_operator_queue.py +0 -0
  243. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_perf_smoke_config.py +0 -0
  244. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_plugins.py +0 -0
  245. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_policy_coverage.py +0 -0
  246. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_policy_mode_env.py +0 -0
  247. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_postgres_parity.py +0 -0
  248. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_qdrant_backend.py +0 -0
  249. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_qmd_bridge.py +0 -0
  250. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_query_classifier.py +0 -0
  251. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_query_expansion.py +0 -0
  252. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_recall_entity_fanout.py +0 -0
  253. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_recall_fusion.py +0 -0
  254. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_recall_latency.py +0 -0
  255. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_recall_precision_at_5.py +0 -0
  256. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_recall_tokenizer.py +0 -0
  257. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_recall_vector_fallback.py +0 -0
  258. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_reliability_hardening.py +0 -0
  259. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_review.py +0 -0
  260. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_rl_trainer.py +0 -0
  261. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_rrf_auto_gate.py +0 -0
  262. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_scheduler.py +0 -0
  263. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_schema.py +0 -0
  264. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_scope_boost.py +0 -0
  265. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_scope_utils.py +0 -0
  266. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_security_access.py +0 -0
  267. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_security_patterns.py +0 -0
  268. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_sensitivity_filter_adversarial.py +0 -0
  269. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_sensitivity_filter_adversarial_v2.py +0 -0
  270. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_service_coverage.py +0 -0
  271. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_session_tracker.py +0 -0
  272. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_snapshot.py +0 -0
  273. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_sqlite_core.py +0 -0
  274. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_staleness.py +0 -0
  275. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_stealth_mode.py +0 -0
  276. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_steward.py +0 -0
  277. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_steward_classifier.py +0 -0
  278. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_steward_features.py +0 -0
  279. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_steward_features_v3.py +0 -0
  280. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_steward_resolution_parity.py +0 -0
  281. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_store_factory.py +0 -0
  282. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_tenant_isolation.py +0 -0
  283. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_turn_schema.py +0 -0
  284. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_two_pass_recall.py +0 -0
  285. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_v390_e2e.py +0 -0
  286. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_vault_exporter.py +0 -0
  287. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_vector_search.py +0 -0
  288. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_verbatim_recall.py +0 -0
  289. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_webhook.py +0 -0
  290. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_wiki_binding.py +0 -0
  291. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_wiki_freshness.py +0 -0
  292. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_wiki_similarity_multiscope.py +0 -0
  293. {memorymaster-3.9.0 → memorymaster-3.10.0}/tests/test_wiki_validate_cli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memorymaster
3
- Version: 3.9.0
3
+ Version: 3.10.0
4
4
  Summary: Production-grade memory reliability system for AI coding agents. Lifecycle-managed claims with citations, conflict detection, steward governance, and MCP integration.
5
5
  Author: wolverin0
6
6
  License: MIT
@@ -15,12 +15,18 @@ flag in a future patch without re-touching this module.
15
15
  """
16
16
  from __future__ import annotations
17
17
 
18
+ import logging
18
19
  import re
19
20
  import sqlite3
20
21
  from collections import deque
21
22
  from datetime import datetime, timezone
22
23
  from pathlib import Path
23
24
 
25
+ logger = logging.getLogger(__name__)
26
+
27
+ # v3.9.1 S2 — once-per-process so the missing-table warning doesn't spam recall callers.
28
+ _MISSING_TABLE_WARNED: bool = False
29
+
24
30
  __all__ = [
25
31
  "ensure_claim_edges_schema",
26
32
  "extract_edges_for_claim",
@@ -195,8 +201,20 @@ def walk_neighbors(
195
201
  (cid,),
196
202
  )
197
203
  ids.update(int(r[0]) for r in cursor.fetchall())
198
- except sqlite3.OperationalError:
199
- # claim_edges table missing just bail
204
+ except sqlite3.OperationalError as exc:
205
+ # v3.9.1 S2 — surface the missing-table case once per process
206
+ # so the user knows to bootstrap with rebuild_edges(). We
207
+ # still bail (returning {}) to keep the recall hook's
208
+ # silent-fail invariant.
209
+ global _MISSING_TABLE_WARNED
210
+ if not _MISSING_TABLE_WARNED:
211
+ logger.warning(
212
+ "claim_edges table missing or unreadable (%s) — F8 "
213
+ "two-pass walks will return empty results until you "
214
+ "run memorymaster.claim_edges.rebuild_edges(db_path).",
215
+ exc,
216
+ )
217
+ _MISSING_TABLE_WARNED = True
200
218
  return {}
201
219
  for n in ids:
202
220
  if n in seen:
@@ -30,6 +30,11 @@ from memorymaster.hook_log import log_hook
30
30
 
31
31
  logger = logging.getLogger(__name__)
32
32
 
33
+ # v3.9.1 S1 — once-per-process flags so import-failure warnings don't spam.
34
+ _VERBATIM_IMPORT_WARNED: bool = False
35
+ # v3.9.1 S2 — once-per-process flag for missing claim_edges table.
36
+ _CLAIM_EDGES_MISSING_WARNED: bool = False
37
+
33
38
 
34
39
  # Retrieval latency instrumentation (roadmap 5.1).
35
40
  #
@@ -349,6 +354,13 @@ _RECALL_WEIGHT_DEFAULTS: dict[str, float] = {
349
354
  # legacy ranking bit-identical even when the stream is on (rows are still
350
355
  # added as candidates but contribute zero to scoring).
351
356
  "W_TWO_PASS": 0.0,
357
+ # W_CLOSETS (v3.10 W1, ported from MemPalace v3.3.0). When the closets
358
+ # stream is ON (MEMORYMASTER_RECALL_CLOSETS=1), each claim_id surfaced
359
+ # via ``search_closets`` gets ``closet_score = 1.0`` (constant — closets
360
+ # are a presence boost, not a ranked score). Default 0.0 keeps ranking
361
+ # bit-identical even when the stream is on. MemPalace measured R@1
362
+ # 0.42 → 0.58 (+38%) with regex-derived closets.
363
+ "W_CLOSETS": 0.0,
352
364
  }
353
365
 
354
366
 
@@ -402,6 +414,18 @@ def _two_pass_enabled() -> bool:
402
414
  return raw not in ("0", "false", "False", "no", "off", "")
403
415
 
404
416
 
417
+ def _two_pass_use_edges() -> bool:
418
+ """v3.10 W2 — when the two-pass stream is on, ALSO walk claim_edges in addition to entity_aliases."""
419
+ raw = os.environ.get("MEMORYMASTER_RECALL_TWO_PASS_USE_EDGES", "0").strip()
420
+ return raw not in ("0", "false", "False", "no", "off", "")
421
+
422
+
423
+ def _closets_enabled() -> bool:
424
+ """v3.10 W1 — opt-in closets stream gate."""
425
+ raw = os.environ.get("MEMORYMASTER_RECALL_CLOSETS", "0").strip()
426
+ return raw not in ("0", "false", "False", "no", "off", "")
427
+
428
+
405
429
  def _two_pass_max_neighbors() -> int:
406
430
  """Cap neighbors discovered per recall call (default 20)."""
407
431
  raw = os.environ.get("MEMORYMASTER_RECALL_TWO_PASS_MAX", "20").strip()
@@ -1143,7 +1167,20 @@ def _recall_impl(
1143
1167
  is_enabled as _verbatim_enabled,
1144
1168
  recall_verbatim,
1145
1169
  )
1146
- except Exception: # pragma: no cover - importless path
1170
+ except Exception as _verbatim_import_exc: # pragma: no cover - importless path
1171
+ # v3.9.1 S1 — surface this rather than silently using no-op lambdas.
1172
+ # User needs to know that even if MEMORYMASTER_RECALL_VERBATIM=1 is set,
1173
+ # the stream is unavailable because the install is incomplete or the
1174
+ # module raised at import time. Logged at WARNING so it surfaces in
1175
+ # default log levels but doesn't crash recall().
1176
+ if not _VERBATIM_IMPORT_WARNED:
1177
+ logger.warning(
1178
+ "verbatim_recall import failed (%s) — verbatim stream is OFF "
1179
+ "regardless of MEMORYMASTER_RECALL_VERBATIM. Reinstall "
1180
+ "memorymaster or check the import error.",
1181
+ _verbatim_import_exc,
1182
+ )
1183
+ globals()["_VERBATIM_IMPORT_WARNED"] = True
1147
1184
  _verbatim_enabled = lambda: False # type: ignore[assignment] # noqa: E731
1148
1185
  recall_verbatim = lambda *a, **k: [] # type: ignore[assignment] # noqa: E731
1149
1186
  hit_to_synthetic_row = None # type: ignore[assignment]
@@ -1211,30 +1248,100 @@ def _recall_impl(
1211
1248
  and getattr(c, "id", None) is not None
1212
1249
  ][:10] # cap seeds so the fanout stays bounded
1213
1250
  neighbor_ids = _two_pass_neighbor_ids(svc.store, seed_ids, set(seen_ids))
1214
- if neighbor_ids:
1215
- for nid in neighbor_ids:
1216
- try:
1217
- claim = svc.store.get_claim(nid)
1218
- except Exception:
1219
- claim = None
1220
- if claim is None:
1221
- continue
1222
- rows.append(
1223
- {
1224
- "claim": claim,
1225
- "lexical_score": 0.0,
1226
- "freshness_score": 0.0,
1227
- "confidence_score": float(getattr(claim, "confidence", 0.0) or 0.0),
1228
- "vector_score": 0.0,
1229
- "entity_score": 0.0,
1230
- "two_pass_score": 1.0,
1231
- "source": "two_pass",
1232
- }
1233
- )
1234
- seen_ids.add(nid)
1251
+ neighbor_distances: dict[int, int] = {n: 1 for n in neighbor_ids}
1252
+ # v3.10 W2 — also walk claim_edges (F8 structural edges)
1253
+ # when MEMORYMASTER_RECALL_TWO_PASS_USE_EDGES=1. Distances
1254
+ # from the structural walk OVERRIDE entity-fanout where
1255
+ # they're shorter (closer neighbors via explicit references).
1256
+ if _two_pass_use_edges() and seed_ids:
1257
+ try:
1258
+ from memorymaster.claim_edges import walk_neighbors as _walk_edges
1259
+ edge_distances = _walk_edges(db, seed_ids, max_hops=2)
1260
+ for nid, hops in edge_distances.items():
1261
+ existing = neighbor_distances.get(nid)
1262
+ if existing is None or hops < existing:
1263
+ neighbor_distances[nid] = int(hops)
1264
+ except Exception as exc: # noqa: BLE001
1265
+ logger.debug("two_pass edges walk skipped: %s", exc)
1266
+ for nid, hops in neighbor_distances.items():
1267
+ if nid in seen_ids:
1268
+ continue
1269
+ try:
1270
+ claim = svc.store.get_claim(nid)
1271
+ except Exception:
1272
+ claim = None
1273
+ if claim is None:
1274
+ continue
1275
+ # Distance-decayed score so closer neighbors weigh more.
1276
+ score = 1.0 / (1.0 + float(hops))
1277
+ rows.append(
1278
+ {
1279
+ "claim": claim,
1280
+ "lexical_score": 0.0,
1281
+ "freshness_score": 0.0,
1282
+ "confidence_score": float(getattr(claim, "confidence", 0.0) or 0.0),
1283
+ "vector_score": 0.0,
1284
+ "entity_score": 0.0,
1285
+ "two_pass_score": score,
1286
+ "source": "two_pass",
1287
+ }
1288
+ )
1289
+ seen_ids.add(nid)
1235
1290
  except Exception as exc: # noqa: BLE001 — defensive
1236
1291
  logger.debug("two_pass stream skipped: %s", exc)
1237
1292
 
1293
+ # Closets stream (v3.10 W1, MemPalace v3.3.0). Gated on
1294
+ # MEMORYMASTER_RECALL_CLOSETS=1. Calls search_closets(query) → for each
1295
+ # matched wiki article, hydrates its claim_ids as new rows annotated with
1296
+ # ``closet_score = 1.0``. When a closet-hydrated claim is already in
1297
+ # ``rows`` we BOOST its closet_score on the existing row instead of
1298
+ # adding a duplicate.
1299
+ if _closets_enabled():
1300
+ with _phase_timer(phase_ms, "closets"):
1301
+ try:
1302
+ from memorymaster.closets import search_closets as _search_closets
1303
+ except Exception as exc: # noqa: BLE001 — module may be missing
1304
+ logger.debug("closets stream skipped (import): %s", exc)
1305
+ _search_closets = None
1306
+ if _search_closets is not None:
1307
+ try:
1308
+ closet_hits = _search_closets(db, query, limit=5)
1309
+ except Exception as exc: # noqa: BLE001
1310
+ logger.debug("closets stream skipped (search): %s", exc)
1311
+ closet_hits = []
1312
+ if closet_hits:
1313
+ existing_by_id: dict[int, dict] = {}
1314
+ for row in rows:
1315
+ c = row.get("claim")
1316
+ if c is not None and getattr(c, "id", None) is not None:
1317
+ existing_by_id[int(c.id)] = row
1318
+ for _slug, claim_ids in closet_hits:
1319
+ for cid in claim_ids:
1320
+ if cid in existing_by_id:
1321
+ # Boost: closet_score=1.0 is the constant signal
1322
+ existing_by_id[cid]["closet_score"] = 1.0
1323
+ continue
1324
+ try:
1325
+ claim = svc.store.get_claim(cid)
1326
+ except Exception:
1327
+ claim = None
1328
+ if claim is None:
1329
+ continue
1330
+ rows.append(
1331
+ {
1332
+ "claim": claim,
1333
+ "lexical_score": 0.0,
1334
+ "freshness_score": 0.0,
1335
+ "confidence_score": float(getattr(claim, "confidence", 0.0) or 0.0),
1336
+ "vector_score": 0.0,
1337
+ "entity_score": 0.0,
1338
+ "closet_score": 1.0,
1339
+ "source": "closets",
1340
+ }
1341
+ )
1342
+ seen_ids.add(cid)
1343
+ existing_by_id[cid] = rows[-1]
1344
+
1238
1345
  if _graph_enabled():
1239
1346
  with _phase_timer(phase_ms, "graph"):
1240
1347
  # Roadmap 12.1 — distance-weighted score breaks the
@@ -1300,6 +1407,7 @@ def _recall_impl(
1300
1407
  # bit-identical ranking with the 5-stream baseline.
1301
1408
  w_graph = _recall_weight("W_GRAPH")
1302
1409
  w_two_pass = _recall_weight("W_TWO_PASS")
1410
+ w_closets = _recall_weight("W_CLOSETS")
1303
1411
  # W_CLAIM_TYPE (v3.9.0 F1, MemPalace "Halls"-inspired). When > 0, classify
1304
1412
  # the query via classify_observation() and look up its inferred claim_type;
1305
1413
  # rows whose claim.claim_type matches get a (1 + w_claim_type) multiplier
@@ -1479,6 +1587,10 @@ def _recall_impl(
1479
1587
  # F5 two-pass entity-fanout stream. W_TWO_PASS=0.0 (default) makes
1480
1588
  # this contribute nothing even when the stream is on.
1481
1589
  two_pass = float(row.get("two_pass_score") or 0.0)
1590
+ # closet_score is non-zero only when the row was added/boosted via
1591
+ # the v3.10 W1 closets stream. W_CLOSETS=0.0 (default) makes this
1592
+ # contribute nothing even when MEMORYMASTER_RECALL_CLOSETS=1.
1593
+ closets = float(row.get("closet_score") or 0.0)
1482
1594
  base = (
1483
1595
  matches * w_matches
1484
1596
  + phrase_bonus * w_phrase
@@ -1491,6 +1603,7 @@ def _recall_impl(
1491
1603
  + verbatim * w_verbatim
1492
1604
  + graph * w_graph
1493
1605
  + two_pass * w_two_pass
1606
+ + closets * w_closets
1494
1607
  )
1495
1608
  # claim_type-aware boost (v3.9.0 F1). When w_claim_type > 0 AND the
1496
1609
  # query was classified into a type AND this claim's claim_type matches,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memorymaster
3
- Version: 3.9.0
3
+ Version: 3.10.0
4
4
  Summary: Production-grade memory reliability system for AI coding agents. Lifecycle-managed claims with citations, conflict detection, steward governance, and MCP integration.
5
5
  Author: wolverin0
6
6
  License: MIT
@@ -191,6 +191,7 @@ tests/test_cli_ready.py
191
191
  tests/test_cli_review_queue.py
192
192
  tests/test_cli_subcommands.py
193
193
  tests/test_closets.py
194
+ tests/test_closets_recall_integration.py
194
195
  tests/test_compact_summaries.py
195
196
  tests/test_compaction_trace.py
196
197
  tests/test_config.py
@@ -278,6 +279,7 @@ tests/test_tenant_isolation.py
278
279
  tests/test_turn_schema.py
279
280
  tests/test_two_pass_recall.py
280
281
  tests/test_v390_e2e.py
282
+ tests/test_v391_strict_warnings.py
281
283
  tests/test_vault_exporter.py
282
284
  tests/test_vector_search.py
283
285
  tests/test_verbatim_recall.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "memorymaster"
7
- version = "3.9.0"
7
+ version = "3.10.0"
8
8
  description = "Production-grade memory reliability system for AI coding agents. Lifecycle-managed claims with citations, conflict detection, steward governance, and MCP integration."
9
9
  license = {text = "MIT"}
10
10
  authors = [{name = "wolverin0"}]
@@ -0,0 +1,50 @@
1
+ """Tests for v3.10 W1 — closets stream wired into context_hook.recall."""
2
+ from __future__ import annotations
3
+
4
+ import pytest
5
+
6
+ from memorymaster import context_hook
7
+
8
+
9
+ @pytest.fixture(autouse=True)
10
+ def _clean_env(monkeypatch):
11
+ for k in [
12
+ "MEMORYMASTER_RECALL_CLOSETS",
13
+ "MEMORYMASTER_RECALL_W_CLOSETS",
14
+ "MEMORYMASTER_RECALL_TWO_PASS",
15
+ "MEMORYMASTER_RECALL_TWO_PASS_USE_EDGES",
16
+ ]:
17
+ monkeypatch.delenv(k, raising=False)
18
+
19
+
20
+ def test_closets_disabled_by_default():
21
+ assert context_hook._closets_enabled() is False
22
+
23
+
24
+ def test_closets_enabled_via_env(monkeypatch):
25
+ monkeypatch.setenv("MEMORYMASTER_RECALL_CLOSETS", "1")
26
+ assert context_hook._closets_enabled() is True
27
+
28
+
29
+ def test_closets_disabled_for_falsy_values(monkeypatch):
30
+ for v in ["0", "false", "False", "no", "off", ""]:
31
+ monkeypatch.setenv("MEMORYMASTER_RECALL_CLOSETS", v)
32
+ assert context_hook._closets_enabled() is False, f"failed for {v!r}"
33
+
34
+
35
+ def test_w_closets_default_is_zero():
36
+ assert context_hook._RECALL_WEIGHT_DEFAULTS["W_CLOSETS"] == 0.0
37
+
38
+
39
+ def test_w_closets_override(monkeypatch):
40
+ monkeypatch.setenv("MEMORYMASTER_RECALL_W_CLOSETS", "0.4")
41
+ assert context_hook._recall_weight("W_CLOSETS") == 0.4
42
+
43
+
44
+ def test_two_pass_use_edges_disabled_by_default():
45
+ assert context_hook._two_pass_use_edges() is False
46
+
47
+
48
+ def test_two_pass_use_edges_enabled_via_env(monkeypatch):
49
+ monkeypatch.setenv("MEMORYMASTER_RECALL_TWO_PASS_USE_EDGES", "1")
50
+ assert context_hook._two_pass_use_edges() is True
@@ -0,0 +1,73 @@
1
+ """Tests for v3.9.1 STRICT items.
2
+
3
+ S1: verbatim_recall import failure now logs WARNING once.
4
+ S2: claim_edges missing table now logs WARNING once via walk_neighbors.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import sqlite3
9
+ from pathlib import Path
10
+
11
+ import pytest
12
+
13
+ import memorymaster.claim_edges as claim_edges_module
14
+
15
+
16
+ def test_walker_logs_warning_when_table_missing(tmp_path, caplog):
17
+ """S2 — when claim_edges table doesn't exist, walk_neighbors warns once."""
18
+ # Reset the module-level flag so this test doesn't depend on order
19
+ claim_edges_module._MISSING_TABLE_WARNED = False
20
+ db = tmp_path / "no-edges.db"
21
+ sqlite3.connect(str(db)).close() # empty DB, no claim_edges table
22
+ with caplog.at_level("WARNING", logger="memorymaster.claim_edges"):
23
+ result = claim_edges_module.walk_neighbors(db, [1, 2, 3], max_hops=2)
24
+ assert result == {}
25
+ assert any("claim_edges table missing" in r.message for r in caplog.records), \
26
+ f"expected missing-table warning, got records: {[r.message for r in caplog.records]}"
27
+
28
+
29
+ def test_walker_warns_only_once_per_process(tmp_path, caplog):
30
+ """S2 — second + third walks against missing table don't re-spam the log."""
31
+ claim_edges_module._MISSING_TABLE_WARNED = False
32
+ db = tmp_path / "no-edges.db"
33
+ sqlite3.connect(str(db)).close()
34
+ with caplog.at_level("WARNING", logger="memorymaster.claim_edges"):
35
+ claim_edges_module.walk_neighbors(db, [1], max_hops=1)
36
+ caplog.clear()
37
+ claim_edges_module.walk_neighbors(db, [2], max_hops=1)
38
+ claim_edges_module.walk_neighbors(db, [3], max_hops=1)
39
+ # Second + third walks should NOT produce new warning records
40
+ assert not any("claim_edges table missing" in r.message for r in caplog.records), \
41
+ "duplicate warning surfaced — _MISSING_TABLE_WARNED gate broken"
42
+
43
+
44
+ def test_walker_does_not_warn_when_table_exists(tmp_path, caplog):
45
+ """When the table is present (even if empty), no warning fires."""
46
+ claim_edges_module._MISSING_TABLE_WARNED = False
47
+ db = tmp_path / "with-edges.db"
48
+ conn = sqlite3.connect(str(db))
49
+ try:
50
+ claim_edges_module.ensure_claim_edges_schema(conn)
51
+ finally:
52
+ conn.close()
53
+ with caplog.at_level("WARNING", logger="memorymaster.claim_edges"):
54
+ result = claim_edges_module.walk_neighbors(db, [1, 2], max_hops=2)
55
+ assert result == {} # empty table → no neighbors
56
+ assert not any("claim_edges table missing" in r.message for r in caplog.records)
57
+
58
+
59
+ def test_module_exposes_warned_flag():
60
+ """The flag is module-level so tests + production callers can reset it."""
61
+ assert hasattr(claim_edges_module, "_MISSING_TABLE_WARNED")
62
+
63
+
64
+ def test_context_hook_exposes_verbatim_warned_flag():
65
+ """S1 — _VERBATIM_IMPORT_WARNED gate exists so the warning is once-per-process."""
66
+ from memorymaster import context_hook
67
+ assert hasattr(context_hook, "_VERBATIM_IMPORT_WARNED")
68
+
69
+
70
+ def test_context_hook_exposes_claim_edges_warned_flag():
71
+ """S2 — _CLAIM_EDGES_MISSING_WARNED was reserved on context_hook for the F8 wiring."""
72
+ from memorymaster import context_hook
73
+ assert hasattr(context_hook, "_CLAIM_EDGES_MISSING_WARNED")
File without changes
File without changes