memorymaster 3.19.0__tar.gz → 3.22.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 (396) hide show
  1. {memorymaster-3.19.0/memorymaster.egg-info → memorymaster-3.22.0}/PKG-INFO +8 -3
  2. {memorymaster-3.19.0 → memorymaster-3.22.0}/README.md +7 -2
  3. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/__init__.py +1 -1
  4. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/cli.py +32 -1
  5. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/cli_handlers_basic.py +113 -0
  6. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/cli_handlers_curation.py +56 -0
  7. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/config.py +6 -0
  8. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/config_templates/hooks/memorymaster-auto-ingest.py +24 -0
  9. memorymaster-3.22.0/memorymaster/contradiction_probe.py +326 -0
  10. memorymaster-3.22.0/memorymaster/delta_sync.py +172 -0
  11. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/mcp_server.py +51 -0
  12. memorymaster-3.22.0/memorymaster/migrations/0001_initial.py +25 -0
  13. memorymaster-3.22.0/memorymaster/migrations/0002_miner_state.py +38 -0
  14. memorymaster-3.22.0/memorymaster/migrations/0003_contradiction_verdicts.py +42 -0
  15. memorymaster-3.22.0/memorymaster/migrations/0004_query_cache.py +114 -0
  16. memorymaster-3.22.0/memorymaster/migrations/__init__.py +41 -0
  17. memorymaster-3.22.0/memorymaster/migrations/runner.py +268 -0
  18. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/postgres_store.py +9 -0
  19. memorymaster-3.22.0/memorymaster/query_cache.py +129 -0
  20. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/retrieval.py +91 -30
  21. memorymaster-3.22.0/memorymaster/rule_miner.py +443 -0
  22. memorymaster-3.22.0/memorymaster/rules.py +104 -0
  23. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/service.py +98 -1
  24. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/storage.py +10 -0
  25. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/verbatim_store.py +41 -9
  26. {memorymaster-3.19.0 → memorymaster-3.22.0/memorymaster.egg-info}/PKG-INFO +8 -3
  27. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster.egg-info/SOURCES.txt +20 -0
  28. {memorymaster-3.19.0 → memorymaster-3.22.0}/pyproject.toml +1 -1
  29. memorymaster-3.22.0/tests/conftest.py +103 -0
  30. memorymaster-3.22.0/tests/test_backend_parity.py +167 -0
  31. memorymaster-3.22.0/tests/test_contradiction_probe.py +153 -0
  32. memorymaster-3.22.0/tests/test_delta_sync.py +261 -0
  33. memorymaster-3.22.0/tests/test_floor_gate.py +90 -0
  34. memorymaster-3.22.0/tests/test_migrations.py +292 -0
  35. memorymaster-3.22.0/tests/test_qrels_regression.py +88 -0
  36. memorymaster-3.22.0/tests/test_query_cache.py +127 -0
  37. memorymaster-3.22.0/tests/test_rule_claims.py +181 -0
  38. memorymaster-3.22.0/tests/test_rule_miner.py +297 -0
  39. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_verbatim_dedup.py +36 -0
  40. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_wiki_freshness.py +4 -1
  41. memorymaster-3.19.0/tests/conftest.py +0 -37
  42. {memorymaster-3.19.0 → memorymaster-3.22.0}/LICENSE +0 -0
  43. {memorymaster-3.19.0 → memorymaster-3.22.0}/artifacts/bm25-per-field-eval-harness.py +0 -0
  44. {memorymaster-3.19.0 → memorymaster-3.22.0}/benchmarks/longmemeval_runner.py +0 -0
  45. {memorymaster-3.19.0 → memorymaster-3.22.0}/benchmarks/longmemeval_vector_runner.py +0 -0
  46. {memorymaster-3.19.0 → memorymaster-3.22.0}/benchmarks/perf_smoke.py +0 -0
  47. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/__main__.py +0 -0
  48. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/_storage_lifecycle.py +0 -0
  49. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/_storage_read.py +0 -0
  50. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/_storage_schema.py +0 -0
  51. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/_storage_shared.py +0 -0
  52. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/_storage_sources.py +0 -0
  53. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/_storage_write_claims.py +0 -0
  54. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/access_control.py +0 -0
  55. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/action_exporters.py +0 -0
  56. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/action_extractor.py +0 -0
  57. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/atlas_claim_extractor.py +0 -0
  58. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/atlas_contract.py +0 -0
  59. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/auto_extractor.py +0 -0
  60. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/auto_resolver.py +0 -0
  61. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/candidate_dedupe.py +0 -0
  62. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/claim_edges.py +0 -0
  63. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/claim_verifier.py +0 -0
  64. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/cli_helpers.py +0 -0
  65. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/closets.py +0 -0
  66. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/config_templates/claude-md-append.md +0 -0
  67. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/config_templates/codex-agents-md-append.md +0 -0
  68. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/config_templates/hooks/memorymaster-classify.py +0 -0
  69. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/config_templates/hooks/memorymaster-dream-sync.py +0 -0
  70. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/config_templates/hooks/memorymaster-precompact.py +0 -0
  71. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/config_templates/hooks/memorymaster-recall.py +0 -0
  72. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/config_templates/hooks/memorymaster-session-start.py +0 -0
  73. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/config_templates/hooks/memorymaster-steward-cycle.py +0 -0
  74. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/config_templates/hooks/memorymaster-validate-wiki.py +0 -0
  75. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/conflict_resolver.py +0 -0
  76. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/connectors/__init__.py +0 -0
  77. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/connectors/whatsapp.py +0 -0
  78. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/context_hook.py +0 -0
  79. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/context_optimizer.py +0 -0
  80. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/daily_notes.py +0 -0
  81. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/dashboard.py +0 -0
  82. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/dashboard_auth.py +0 -0
  83. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/db_merge.py +0 -0
  84. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/dream_bridge.py +0 -0
  85. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/embeddings.py +0 -0
  86. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/entity_extractor.py +0 -0
  87. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/entity_graph.py +0 -0
  88. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/entity_registry.py +0 -0
  89. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/federated_graphify.py +0 -0
  90. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/feedback.py +0 -0
  91. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/graph_store.py +0 -0
  92. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/hook_log.py +0 -0
  93. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/jobs/__init__.py +0 -0
  94. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/jobs/calibration.py +0 -0
  95. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/jobs/compact_summaries.py +0 -0
  96. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/jobs/compactor.py +0 -0
  97. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/jobs/daydream_ingest.py +0 -0
  98. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/jobs/decay.py +0 -0
  99. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/jobs/dedup.py +0 -0
  100. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/jobs/deterministic.py +0 -0
  101. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/jobs/entity_graph_export.py +0 -0
  102. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/jobs/extractor.py +0 -0
  103. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/jobs/staleness.py +0 -0
  104. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/jobs/validator.py +0 -0
  105. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/key_rotator.py +0 -0
  106. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/lifecycle.py +0 -0
  107. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/llm_budget.py +0 -0
  108. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/llm_provider.py +0 -0
  109. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/llm_rerank.py +0 -0
  110. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/llm_steward.py +0 -0
  111. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/mcp_path_policy.py +0 -0
  112. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/mcp_usage.py +0 -0
  113. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/media_processing.py +0 -0
  114. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/media_providers.py +0 -0
  115. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/metrics_exporter.py +0 -0
  116. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/models.py +0 -0
  117. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/observability.py +0 -0
  118. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/operator.py +0 -0
  119. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/operator_queue.py +0 -0
  120. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/plugins.py +0 -0
  121. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/policy.py +0 -0
  122. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/qdrant_backend.py +0 -0
  123. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/qdrant_recall_fallback.py +0 -0
  124. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/qmd_bridge.py +0 -0
  125. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/query_classifier.py +0 -0
  126. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/query_expansion.py +0 -0
  127. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/recall_fusion.py +0 -0
  128. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/recall_tokenizer.py +0 -0
  129. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/retry.py +0 -0
  130. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/review.py +0 -0
  131. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/rl_trainer.py +0 -0
  132. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/scheduler.py +0 -0
  133. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/schema.py +0 -0
  134. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/schema.sql +0 -0
  135. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/schema_postgres.sql +0 -0
  136. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/scope_utils.py +0 -0
  137. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/security.py +0 -0
  138. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/session_tracker.py +0 -0
  139. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/setup_hooks.py +0 -0
  140. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/skill_evolver.py +0 -0
  141. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/snapshot.py +0 -0
  142. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/steward.py +0 -0
  143. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/steward_classifier.py +0 -0
  144. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/steward_features.py +0 -0
  145. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/store_factory.py +0 -0
  146. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/transcript_miner.py +0 -0
  147. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/turn_schema.py +0 -0
  148. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/vault_bases.py +0 -0
  149. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/vault_curator.py +0 -0
  150. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/vault_exporter.py +0 -0
  151. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/vault_linter.py +0 -0
  152. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/vault_log.py +0 -0
  153. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/vault_query_capture.py +0 -0
  154. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/vault_synthesis.py +0 -0
  155. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/verbatim_recall.py +0 -0
  156. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/webhook.py +0 -0
  157. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/wiki_engine.py +0 -0
  158. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/wiki_freshness.py +0 -0
  159. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/wiki_similarity.py +0 -0
  160. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/wiki_suggest.py +0 -0
  161. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster/wiki_validate.py +0 -0
  162. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster.egg-info/dependency_links.txt +0 -0
  163. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster.egg-info/entry_points.txt +0 -0
  164. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster.egg-info/requires.txt +0 -0
  165. {memorymaster-3.19.0 → memorymaster-3.22.0}/memorymaster.egg-info/top_level.txt +0 -0
  166. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/agg_recall_latency.py +0 -0
  167. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/alert_operator_metrics.py +0 -0
  168. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/audit_dedupe_precision.py +0 -0
  169. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/autoresearch_daemon.py +0 -0
  170. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/backfill_entity_extraction.py +0 -0
  171. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/backfill_graph_store.py +0 -0
  172. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/backfill_stop_hook_citations.py +0 -0
  173. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/backtest_steward_classifier.py +0 -0
  174. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/build_steward_training_set.py +0 -0
  175. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/check_hook_template_drift.py +0 -0
  176. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/claude_to_turns.py +0 -0
  177. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/codex_live_to_turns.py +0 -0
  178. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/compaction_edge_cases.py +0 -0
  179. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/compaction_trace_report.py +0 -0
  180. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/compaction_trace_validate.py +0 -0
  181. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/confusion_matrix_eval.py +0 -0
  182. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/conversation_importer.py +0 -0
  183. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/conversation_to_turns.py +0 -0
  184. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/e2e_operator.py +0 -0
  185. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/email_live_to_turns.py +0 -0
  186. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/eval_bm25_sweep.py +0 -0
  187. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/eval_classify_f1.py +0 -0
  188. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/eval_memorymaster.py +0 -0
  189. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/eval_recall_precision_at_5.py +0 -0
  190. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/eval_recall_quality.py +0 -0
  191. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/eval_steward_pareto.py +0 -0
  192. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/eval_verbatim_recall.py +0 -0
  193. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/expand_recall_eval.py +0 -0
  194. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/generate_drill_signoff.py +0 -0
  195. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/git_to_turns.py +0 -0
  196. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/github_live_to_turns.py +0 -0
  197. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/gitnexus_to_claims.py +0 -0
  198. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/grid_recall_weights.py +0 -0
  199. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/index_claims_to_qdrant.py +0 -0
  200. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/ingest_planning_docs.py +0 -0
  201. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/jira_live_to_turns.py +0 -0
  202. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/label_prompts_with_judge.py +0 -0
  203. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/llm_benchmark.py +0 -0
  204. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/measure_dedupe_thresholds.py +0 -0
  205. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/merge_scope_variants.py +0 -0
  206. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/messages_to_turns.py +0 -0
  207. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/operator_metrics.py +0 -0
  208. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/precompute_candidates.py +0 -0
  209. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/recurring_incident_drill.py +0 -0
  210. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/release_readiness.py +0 -0
  211. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/run_codex_autologger.py +0 -0
  212. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/run_incident_drill.py +0 -0
  213. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/scheduled_ingest.py +0 -0
  214. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/setup-hooks.py +0 -0
  215. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/slack_live_to_turns.py +0 -0
  216. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/sync_hook_templates.py +0 -0
  217. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/tickets_to_turns.py +0 -0
  218. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/train_steward_classifier.py +0 -0
  219. {memorymaster-3.19.0 → memorymaster-3.22.0}/scripts/webhook_to_turns.py +0 -0
  220. {memorymaster-3.19.0 → memorymaster-3.22.0}/setup.cfg +0 -0
  221. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/bench_longmemeval.py +0 -0
  222. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/integration/test_extract_llm_ollama_live.py +0 -0
  223. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_access_control.py +0 -0
  224. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_action_exporters.py +0 -0
  225. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_action_extractor.py +0 -0
  226. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_atlas_claim_extractor.py +0 -0
  227. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_atlas_contract.py +0 -0
  228. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_atlas_source_schema.py +0 -0
  229. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_auto_extractor.py +0 -0
  230. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_auto_ingest_hook_citations.py +0 -0
  231. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_auto_ingest_hook_schema.py +0 -0
  232. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_auto_resolver.py +0 -0
  233. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_auto_validate.py +0 -0
  234. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_bm25_per_field.py +0 -0
  235. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_calibration.py +0 -0
  236. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_calibration_priors_applied.py +0 -0
  237. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_candidate_dedupe.py +0 -0
  238. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_claim_edges.py +0 -0
  239. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_claim_links.py +0 -0
  240. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_claim_type_ranking.py +0 -0
  241. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_classify_hook_f1.py +0 -0
  242. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_classify_hook_latency.py +0 -0
  243. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_claude_to_turns.py +0 -0
  244. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_cli_dry_run.py +0 -0
  245. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_cli_json_flag.py +0 -0
  246. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_cli_ready.py +0 -0
  247. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_cli_review_queue.py +0 -0
  248. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_cli_subcommands.py +0 -0
  249. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_closets.py +0 -0
  250. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_closets_recall_integration.py +0 -0
  251. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_compact_summaries.py +0 -0
  252. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_compact_summaries_sensitivity.py +0 -0
  253. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_compaction_trace.py +0 -0
  254. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_compactor_artifact_order.py +0 -0
  255. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_config.py +0 -0
  256. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_conflict_resolver.py +0 -0
  257. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_confusion_matrix_eval.py +0 -0
  258. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_connection_retry.py +0 -0
  259. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_connectors.py +0 -0
  260. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_context_hook.py +0 -0
  261. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_context_optimizer.py +0 -0
  262. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_context_optimizer_provider.py +0 -0
  263. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_conversation_to_turns.py +0 -0
  264. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_dashboard.py +0 -0
  265. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_dashboard_auth.py +0 -0
  266. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_dashboard_coverage.py +0 -0
  267. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_dashboard_latency.py +0 -0
  268. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_dashboard_lineage.py +0 -0
  269. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_dashboard_review_queue.py +0 -0
  270. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_daydream_ingest.py +0 -0
  271. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_db_merge_confidence_conflict.py +0 -0
  272. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_db_merge_coverage_v2.py +0 -0
  273. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_decay_coverage.py +0 -0
  274. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_decay_respects_pinned.py +0 -0
  275. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_dedup.py +0 -0
  276. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_dedup_cli.py +0 -0
  277. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_dedup_conflict_disambiguation.py +0 -0
  278. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_deterministic_predicates.py +0 -0
  279. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_dream_bridge_coverage_v2.py +0 -0
  280. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_dream_bridge_sensitivity.py +0 -0
  281. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_embeddings_coverage.py +0 -0
  282. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_entity_extractor.py +0 -0
  283. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_entity_extractor_llm.py +0 -0
  284. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_entity_graph.py +0 -0
  285. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_entity_graph_export.py +0 -0
  286. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_entity_new_kinds.py +0 -0
  287. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_entity_regex_v3.py +0 -0
  288. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_entity_registry.py +0 -0
  289. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_eval_harness.py +0 -0
  290. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_events_schema.py +0 -0
  291. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_extract_llm_ollama.py +0 -0
  292. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_federated_graphify_mcp.py +0 -0
  293. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_federated_query_safety.py +0 -0
  294. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_feedback.py +0 -0
  295. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_fts5_search.py +0 -0
  296. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_graph_distance.py +0 -0
  297. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_graph_store.py +0 -0
  298. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_handler_regressions.py +0 -0
  299. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_hook_env_isolation.py +0 -0
  300. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_human_id.py +0 -0
  301. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_incident_drill_runner.py +0 -0
  302. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_integration_workflows.py +0 -0
  303. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_key_rotator.py +0 -0
  304. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_lifecycle.py +0 -0
  305. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_lifecycle_supersede_invariant.py +0 -0
  306. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_llm_budget.py +0 -0
  307. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_llm_fallback.py +0 -0
  308. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_llm_provider_claude_cli.py +0 -0
  309. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_llm_provider_key_rotation.py +0 -0
  310. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_llm_steward_coverage.py +0 -0
  311. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_llm_steward_key_rotation.py +0 -0
  312. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_mcp_filter_bypass.py +0 -0
  313. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_mcp_helpers.py +0 -0
  314. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_mcp_path_policy.py +0 -0
  315. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_mcp_rate_limit.py +0 -0
  316. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_mcp_server_validation.py +0 -0
  317. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_mcp_usage.py +0 -0
  318. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_media_processing.py +0 -0
  319. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_meta_decisions.py +0 -0
  320. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_metrics_exporter.py +0 -0
  321. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_observability.py +0 -0
  322. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_obsidian_mind_patterns.py +0 -0
  323. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_operator.py +0 -0
  324. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_operator_queue.py +0 -0
  325. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_perf_smoke_config.py +0 -0
  326. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_plugins.py +0 -0
  327. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_policy_coverage.py +0 -0
  328. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_policy_mode_env.py +0 -0
  329. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_postgres_parity.py +0 -0
  330. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_qdrant_backend.py +0 -0
  331. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_qmd_bridge.py +0 -0
  332. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_query_classifier.py +0 -0
  333. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_query_expansion.py +0 -0
  334. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_recall_entity_fanout.py +0 -0
  335. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_recall_fusion.py +0 -0
  336. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_recall_latency.py +0 -0
  337. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_recall_precision_at_5.py +0 -0
  338. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_recall_tokenizer.py +0 -0
  339. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_recall_vector_fallback.py +0 -0
  340. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_reliability_hardening.py +0 -0
  341. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_resolvers_concurrent_supersede.py +0 -0
  342. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_retrieval_profile.py +0 -0
  343. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_retrieval_profiles.py +0 -0
  344. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_retrieval_rrf_tiebreaker.py +0 -0
  345. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_retrieval_weights.py +0 -0
  346. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_review.py +0 -0
  347. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_rl_trainer.py +0 -0
  348. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_rrf_auto_gate.py +0 -0
  349. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_scheduler.py +0 -0
  350. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_schema.py +0 -0
  351. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_scope_boost.py +0 -0
  352. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_scope_utils.py +0 -0
  353. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_security_access.py +0 -0
  354. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_security_patterns.py +0 -0
  355. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_sensitivity_filter_adversarial.py +0 -0
  356. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_sensitivity_filter_adversarial_v2.py +0 -0
  357. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_sensitivity_filter_t07.py +0 -0
  358. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_service_coverage.py +0 -0
  359. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_session_tracker.py +0 -0
  360. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_snapshot.py +0 -0
  361. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_snapshot_roundtrip.py +0 -0
  362. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_sqlite_core.py +0 -0
  363. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_staleness.py +0 -0
  364. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_stealth_mode.py +0 -0
  365. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_steward.py +0 -0
  366. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_steward_classifier.py +0 -0
  367. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_steward_daydream_hook.py +0 -0
  368. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_steward_features.py +0 -0
  369. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_steward_features_v3.py +0 -0
  370. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_steward_resolution_parity.py +0 -0
  371. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_storage_parity.py +0 -0
  372. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_store_factory.py +0 -0
  373. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_tenant_isolation.py +0 -0
  374. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_turn_schema.py +0 -0
  375. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_two_pass_recall.py +0 -0
  376. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_v311_fixes.py +0 -0
  377. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_v313_e2e.py +0 -0
  378. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_v313_run_cycle_dedupe.py +0 -0
  379. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_v390_e2e.py +0 -0
  380. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_v391_strict_warnings.py +0 -0
  381. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_vault_exporter.py +0 -0
  382. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_vault_linter_orphan.py +0 -0
  383. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_vector_search.py +0 -0
  384. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_verbatim_recall.py +0 -0
  385. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_verbatim_store.py +0 -0
  386. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_verbatim_store_qdrant.py +0 -0
  387. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_webhook.py +0 -0
  388. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_webhook_hmac.py +0 -0
  389. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_whatsapp_importer.py +0 -0
  390. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_wiki_autopromote.py +0 -0
  391. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_wiki_binding.py +0 -0
  392. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_wiki_engine_idempotency.py +0 -0
  393. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_wiki_explored_and_contradictions.py +0 -0
  394. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_wiki_similarity_multiscope.py +0 -0
  395. {memorymaster-3.19.0 → memorymaster-3.22.0}/tests/test_wiki_suggest.py +0 -0
  396. {memorymaster-3.19.0 → memorymaster-3.22.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.19.0
3
+ Version: 3.22.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
@@ -52,7 +52,7 @@ Lifecycle-managed claims with citations, conflict detection, steward governance,
52
52
 
53
53
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
54
54
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
55
- [![Tests](https://img.shields.io/badge/tests-1953-green.svg)]()
55
+ [![Tests](https://img.shields.io/badge/tests-2214-green.svg)]()
56
56
  [![MCP Tools](https://img.shields.io/badge/MCP%20tools-24-purple.svg)]()
57
57
  [![CLI Commands](https://img.shields.io/badge/CLI%20commands-86-orange.svg)]()
58
58
  [![PyPI](https://img.shields.io/pypi/v/memorymaster.svg)](https://pypi.org/project/memorymaster/)
@@ -87,6 +87,11 @@ recent PR status, and sensitivity-filter invariants.
87
87
  - **Hybrid retrieval**: vector (sentence-transformers / Gemini) + FTS5 + freshness + confidence
88
88
  - **Context optimizer**: `query_for_context(budget=4000)` returns auto-curated memory that fits your token budget
89
89
  - **Entity graph** with typed relationships and alias resolution
90
+ - **Rule-shaped claims** (new in v3.21.0): prescriptive `when <trigger>, do <action> because <rationale>` claims (`ingest_rule` / `query_rules`) — the shape an agent needs to actually change behaviour next time, not just recall a fact
91
+ - **Correction mining** (new in v3.21.0): `mine-rules` scans the verbatim transcript archive for user corrections and distills them into rule claims; the Stop hook also mines each session's latest correction automatically
92
+ - **Versioned schema migrations** (new in v3.20.0): `migrate` applies SQLite/Postgres migrations with sha256 drift detection; incremental `export-delta` ships small claim deltas for cheap cross-machine sync
93
+ - **Retrieval quality** (new in v3.22.0): floor-ratio boost gate (`MEMORYMASTER_BOOST_FLOOR_RATIO`) stops fresh-but-wrong claims outranking the true match; `query --explain` shows per-stage score attribution; an opt-in correctness-safe query cache (`MEMORYMASTER_QUERY_CACHE`) with a generation gate
94
+ - **Semantic contradiction probe** (new in v3.22.0): `detect-contradictions` finds claims that genuinely contradict each other (beyond the deterministic same-subject conflict check) via an LLM judge with a Wilson-CI rate and verdict cache
90
95
  - **Steward governance**: multi-probe validators (filesystem, format, citation, semantic, tool) with proposal review
91
96
  - **Conflict resolution**: 5-tier auto (confidence > freshness > citations > LLM > manual)
92
97
  - **Auto-redaction** at ingest: JWT, GitHub tokens, Bearer, AWS keys, SSH keys, custom patterns
@@ -187,7 +192,7 @@ For zero-cost offline use, install [Ollama](https://ollama.com), `ollama pull ll
187
192
  }
188
193
  ```
189
194
 
190
- 22 MCP tools: `init_db`, `ingest_claim`, `run_cycle`, `run_steward`, `classify_query`, `query_memory`, `query_for_context`, `list_claims`, `redact_claim_payload`, `pin_claim`, `compact_memory`, `list_events`, `search_verbatim`, `open_dashboard`, `list_steward_proposals`, `resolve_steward_proposal`, `extract_entities`, `entity_stats`, `find_related_claims`, `quality_scores`, `recompute_tiers`, `federated_query`.
195
+ 24 MCP tools: `init_db`, `ingest_claim`, `ingest_rule`, `query_rules`, `run_cycle`, `run_steward`, `classify_query`, `query_memory`, `query_for_context`, `list_claims`, `redact_claim_payload`, `pin_claim`, `compact_memory`, `list_events`, `search_verbatim`, `open_dashboard`, `list_steward_proposals`, `resolve_steward_proposal`, `extract_entities`, `entity_stats`, `find_related_claims`, `quality_scores`, `recompute_tiers`, `federated_query`.
191
196
 
192
197
  See [`.mcp.json.example`](.mcp.json.example) for the full template.
193
198
 
@@ -6,7 +6,7 @@ Lifecycle-managed claims with citations, conflict detection, steward governance,
6
6
 
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
8
8
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
9
- [![Tests](https://img.shields.io/badge/tests-1953-green.svg)]()
9
+ [![Tests](https://img.shields.io/badge/tests-2214-green.svg)]()
10
10
  [![MCP Tools](https://img.shields.io/badge/MCP%20tools-24-purple.svg)]()
11
11
  [![CLI Commands](https://img.shields.io/badge/CLI%20commands-86-orange.svg)]()
12
12
  [![PyPI](https://img.shields.io/pypi/v/memorymaster.svg)](https://pypi.org/project/memorymaster/)
@@ -41,6 +41,11 @@ recent PR status, and sensitivity-filter invariants.
41
41
  - **Hybrid retrieval**: vector (sentence-transformers / Gemini) + FTS5 + freshness + confidence
42
42
  - **Context optimizer**: `query_for_context(budget=4000)` returns auto-curated memory that fits your token budget
43
43
  - **Entity graph** with typed relationships and alias resolution
44
+ - **Rule-shaped claims** (new in v3.21.0): prescriptive `when <trigger>, do <action> because <rationale>` claims (`ingest_rule` / `query_rules`) — the shape an agent needs to actually change behaviour next time, not just recall a fact
45
+ - **Correction mining** (new in v3.21.0): `mine-rules` scans the verbatim transcript archive for user corrections and distills them into rule claims; the Stop hook also mines each session's latest correction automatically
46
+ - **Versioned schema migrations** (new in v3.20.0): `migrate` applies SQLite/Postgres migrations with sha256 drift detection; incremental `export-delta` ships small claim deltas for cheap cross-machine sync
47
+ - **Retrieval quality** (new in v3.22.0): floor-ratio boost gate (`MEMORYMASTER_BOOST_FLOOR_RATIO`) stops fresh-but-wrong claims outranking the true match; `query --explain` shows per-stage score attribution; an opt-in correctness-safe query cache (`MEMORYMASTER_QUERY_CACHE`) with a generation gate
48
+ - **Semantic contradiction probe** (new in v3.22.0): `detect-contradictions` finds claims that genuinely contradict each other (beyond the deterministic same-subject conflict check) via an LLM judge with a Wilson-CI rate and verdict cache
44
49
  - **Steward governance**: multi-probe validators (filesystem, format, citation, semantic, tool) with proposal review
45
50
  - **Conflict resolution**: 5-tier auto (confidence > freshness > citations > LLM > manual)
46
51
  - **Auto-redaction** at ingest: JWT, GitHub tokens, Bearer, AWS keys, SSH keys, custom patterns
@@ -141,7 +146,7 @@ For zero-cost offline use, install [Ollama](https://ollama.com), `ollama pull ll
141
146
  }
142
147
  ```
143
148
 
144
- 22 MCP tools: `init_db`, `ingest_claim`, `run_cycle`, `run_steward`, `classify_query`, `query_memory`, `query_for_context`, `list_claims`, `redact_claim_payload`, `pin_claim`, `compact_memory`, `list_events`, `search_verbatim`, `open_dashboard`, `list_steward_proposals`, `resolve_steward_proposal`, `extract_entities`, `entity_stats`, `find_related_claims`, `quality_scores`, `recompute_tiers`, `federated_query`.
149
+ 24 MCP tools: `init_db`, `ingest_claim`, `ingest_rule`, `query_rules`, `run_cycle`, `run_steward`, `classify_query`, `query_memory`, `query_for_context`, `list_claims`, `redact_claim_payload`, `pin_claim`, `compact_memory`, `list_events`, `search_verbatim`, `open_dashboard`, `list_steward_proposals`, `resolve_steward_proposal`, `extract_entities`, `entity_stats`, `find_related_claims`, `quality_scores`, `recompute_tiers`, `federated_query`.
145
150
 
146
151
  See [`.mcp.json.example`](.mcp.json.example) for the full template.
147
152
 
@@ -2,4 +2,4 @@
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "3.4.1"
5
+ __version__ = "3.22.0"
@@ -23,7 +23,9 @@ from memorymaster.cli_handlers_curation import COMMAND_HANDLERS
23
23
  from memorymaster.cli_handlers_basic import (
24
24
  _handle_decay,
25
25
  _handle_entity_graph_export,
26
+ _handle_export_delta,
26
27
  _handle_ingest_daydream,
28
+ _handle_migrate,
27
29
  _handle_recompute_confidence_priors,
28
30
  _handle_wiki_suggest_links,
29
31
  handle_mcp_usage_report,
@@ -38,6 +40,8 @@ COMMAND_HANDLERS["ingest-daydream"] = _handle_ingest_daydream
38
40
  COMMAND_HANDLERS["mcp-usage-report"] = (
39
41
  lambda args, service, parser, effective_db: handle_mcp_usage_report(args, effective_db)
40
42
  )
43
+ COMMAND_HANDLERS["migrate"] = _handle_migrate
44
+ COMMAND_HANDLERS["export-delta"] = _handle_export_delta
41
45
 
42
46
 
43
47
  def build_parser() -> argparse.ArgumentParser:
@@ -51,6 +55,11 @@ def build_parser() -> argparse.ArgumentParser:
51
55
 
52
56
  sub.add_parser("init-db", help="Create schema in SQLite database")
53
57
 
58
+ migrate = sub.add_parser("migrate", help="Apply pending versioned schema migrations (v3.20.0+)")
59
+ migrate_mode = migrate.add_mutually_exclusive_group()
60
+ migrate_mode.add_argument("--list", action="store_true", help="List known migrations without touching the DB")
61
+ migrate_mode.add_argument("--status", action="store_true", help="Report applied vs pending per migration")
62
+
54
63
  sub.add_parser("stealth-status", help="Show whether stealth mode is active and which DB is in use")
55
64
 
56
65
  ingest = sub.add_parser("ingest", help="Ingest a raw claim with citations")
@@ -176,6 +185,7 @@ def build_parser() -> argparse.ArgumentParser:
176
185
  query.add_argument("--scope-allowlist", default="", help="Comma-separated scopes to include (e.g. project,team_x)")
177
186
  query.add_argument("--as-of", default="", help="Temporal query: show claims valid at this ISO timestamp")
178
187
  query.add_argument("--auto-classify", action="store_true", help="Auto-classify query type and use optimal retrieval mode")
188
+ query.add_argument("--explain", action="store_true", help="Show per-stage score attribution (relevance vs. boosts, floor-gate status) for each result")
179
189
 
180
190
  context = sub.add_parser("context", help="Pack relevant claims into a token-budgeted context block for AI agents")
181
191
  context.add_argument("text", help="Query text describing what context is needed")
@@ -447,6 +457,20 @@ def build_parser() -> argparse.ArgumentParser:
447
457
  mine_cmd.add_argument("--scope", default="project", help="Scope for ingested claims")
448
458
  mine_cmd.add_argument("--max", type=int, default=100, help="Max claims to ingest")
449
459
 
460
+ mine_rules_cmd = sub.add_parser("mine-rules", help="Mine verbatim corrections into rule-shaped claims (v3.21.0-R1b)")
461
+ mine_rules_cmd.add_argument("--since-id", dest="since_id", type=int, default=None, help="Override the stored watermark; start scanning after this verbatim id")
462
+ mine_rules_cmd.add_argument("--limit", type=int, default=None, help="Max candidate windows to examine this run (caps LLM calls)")
463
+ mine_rules_cmd.add_argument("--batch-size", dest="batch_size", type=int, default=200, help="Rows fetched per SQL pre-filter page (default: 200)")
464
+ mine_rules_cmd.add_argument("--provider", default="claude_cli", help="LLM provider for this run (default: claude_cli)")
465
+ mine_rules_cmd.add_argument("--reset", action="store_true", help="Clear the stored watermark before running (re-scan from the start)")
466
+
467
+ detect_contra = sub.add_parser("detect-contradictions", help="Find semantic contradictions between topically-similar claims via an LLM judge (v3.22)")
468
+ detect_contra.add_argument("--limit", type=int, default=200, help="Max claims to load for pair sampling")
469
+ detect_contra.add_argument("--sample", type=int, default=50, help="Max candidate pairs to judge this run (caps LLM calls)")
470
+ detect_contra.add_argument("--sim-low", dest="sim_low", type=float, default=0.60, help="Lower similarity-band bound (below = unrelated)")
471
+ detect_contra.add_argument("--sim-high", dest="sim_high", type=float, default=0.92, help="Upper similarity-band bound (at/above = near-duplicates, dedup's job)")
472
+ detect_contra.add_argument("--apply", action="store_true", help="Flag the lower-confidence claim of each contradicting pair as conflicted (reversible)")
473
+
450
474
  verify_cmd = sub.add_parser("verify-claims", help="Cross-check claims against current codebase")
451
475
  verify_cmd.add_argument("--scope", default="", help="Scope filter")
452
476
  verify_cmd.add_argument("--limit", type=int, default=200, help="Max claims to check")
@@ -504,6 +528,13 @@ def build_parser() -> argparse.ArgumentParser:
504
528
  merge_cmd = sub.add_parser("merge-db", help="Merge claims from a remote memorymaster DB (bidirectional sync)")
505
529
  merge_cmd.add_argument("--source", required=True, help="Path to source DB file to merge from")
506
530
 
531
+ delta_cmd = sub.add_parser(
532
+ "export-delta",
533
+ help="Export claims changed since a watermark into a small SQLite delta file (incremental sync)",
534
+ )
535
+ delta_cmd.add_argument("--since", default="", help="ISO-8601 watermark; export claims with updated_at after this (empty = full export)")
536
+ delta_cmd.add_argument("--output", required=True, help="Path to write the delta SQLite file (overwritten if it exists)")
537
+
507
538
  daily = sub.add_parser("daily-note", help="Generate a daily note summarizing today's activity")
508
539
  daily.add_argument("--date", default="", help="Date to generate for (YYYY-MM-DD, default: today)")
509
540
  daily.add_argument("--output", default="", help="Directory to save .md file (default: print to stdout)")
@@ -556,7 +587,7 @@ def main(argv: list[str] | None = None) -> int:
556
587
  effective_db = _resolve_db_path(args)
557
588
 
558
589
  # Commands that don't need MemoryService run first; service is lazy-created once for all others.
559
- _NO_SERVICE_COMMANDS = {"stealth-status", "export-metrics", "wiki-freshness", "mcp-usage-report"}
590
+ _NO_SERVICE_COMMANDS = {"stealth-status", "export-metrics", "wiki-freshness", "mcp-usage-report", "export-delta"}
560
591
 
561
592
  try:
562
593
  handler = COMMAND_HANDLERS.get(args.command)
@@ -832,6 +832,25 @@ def _handle_run_cycle(args: argparse.Namespace, service, parser: argparse.Argume
832
832
  return 0
833
833
 
834
834
 
835
+ def _print_score_explanation(breakdown: dict | None) -> None:
836
+ """Render per-stage score attribution for `query --explain`.
837
+
838
+ Shows query-relevance vs. the metadata boost terms and whether the
839
+ floor-ratio gate suppressed the boosts for this result.
840
+ """
841
+ if not breakdown:
842
+ print(" explain: (no breakdown — legacy retrieval mode)")
843
+ return
844
+ terms = breakdown.get("boost_terms", {})
845
+ w = breakdown.get("weights", (0, 0, 0, 0))
846
+ applied = breakdown.get("boosts_applied", True)
847
+ gate = "applied" if applied else f"GATED (relevance < floor={breakdown.get('floor', 0.0):.3f})"
848
+ term_str = " ".join(f"{k}={v:+.3f}" for k, v in terms.items())
849
+ print(f" explain: relevance={breakdown.get('relevance', 0.0):.3f} "
850
+ f"boosts={breakdown.get('boosts_total', 0.0):+.3f} [{gate}] -> final={breakdown.get('final', 0.0):.3f}")
851
+ print(f" weights(l,c,f,v)={tuple(round(x, 2) for x in w)} boost_terms: {term_str}")
852
+
853
+
835
854
  def _handle_query(args: argparse.Namespace, service, parser: argparse.ArgumentParser, effective_db: str) -> int:
836
855
  resolve_allow_sensitive_access(allow_sensitive=args.allow_sensitive, context="cli.query")
837
856
  if getattr(args, "as_of", ""):
@@ -876,6 +895,8 @@ def _handle_query(args: argparse.Namespace, service, parser: argparse.ArgumentPa
876
895
  f"vec={sc['vector_score']:.3f} "
877
896
  f"active={int(bool(ann.get('active')))} stale={int(bool(ann.get('stale')))} "
878
897
  f"conflicted={int(bool(ann.get('conflicted')))} pinned={int(bool(ann.get('pinned')))}")
898
+ if getattr(args, "explain", False):
899
+ _print_score_explanation(row.get("breakdown"))
879
900
  print(f"rows={len(rows_data)}")
880
901
  return 0
881
902
 
@@ -1357,3 +1378,95 @@ def _handle_check_staleness(args: argparse.Namespace, service, parser: argparse.
1357
1378
  return 0
1358
1379
 
1359
1380
 
1381
+ def _handle_migrate(args: argparse.Namespace, service, parser: argparse.ArgumentParser, effective_db: str) -> int:
1382
+ """v3.20.0-S1: apply pending schema migrations, or report status.
1383
+
1384
+ Default (no flags): apply every pending migration in version order.
1385
+ --list: dump known migrations (version + description) without touching the DB.
1386
+ --status: query the DB and show applied vs pending per migration.
1387
+ """
1388
+ from memorymaster.migrations import (
1389
+ MigrationRunner,
1390
+ discover_migrations,
1391
+ )
1392
+ from memorymaster.store_factory import is_postgres_dsn
1393
+
1394
+ # --list works without a DB connection at all.
1395
+ if getattr(args, "list", False):
1396
+ migrations = discover_migrations()
1397
+ if args.json_output:
1398
+ payload = [{"version": m.version, "description": m.description} for m in migrations]
1399
+ print(_json_envelope(payload))
1400
+ else:
1401
+ print(f"known migrations ({len(migrations)}):")
1402
+ for m in migrations:
1403
+ print(f" v{m.version:04d} {m.description}")
1404
+ return 0
1405
+
1406
+ backend = "postgres" if is_postgres_dsn(effective_db) else "sqlite"
1407
+ store = service.store
1408
+ with store.connect() as conn:
1409
+ runner = MigrationRunner(conn, backend=backend)
1410
+
1411
+ if getattr(args, "status", False):
1412
+ entries = runner.status()
1413
+ if args.json_output:
1414
+ payload = [
1415
+ {
1416
+ "version": e.version,
1417
+ "description": e.description,
1418
+ "applied": e.applied,
1419
+ "applied_at": e.applied_at,
1420
+ }
1421
+ for e in entries
1422
+ ]
1423
+ print(_json_envelope(payload))
1424
+ else:
1425
+ print(f"backend={backend} db={effective_db}")
1426
+ for e in entries:
1427
+ marker = "[applied]" if e.applied else "[pending]"
1428
+ when = f" applied_at={e.applied_at}" if e.applied_at else ""
1429
+ print(f" v{e.version:04d} {marker} {e.description}{when}")
1430
+ return 0
1431
+
1432
+ # Default: apply pending
1433
+ newly = runner.apply_pending()
1434
+ if args.json_output:
1435
+ print(_json_envelope({"applied": newly, "backend": backend}))
1436
+ else:
1437
+ if not newly:
1438
+ print(f"migrate: nothing to apply (backend={backend}, db={effective_db})")
1439
+ else:
1440
+ print(f"migrate: applied {len(newly)} migration(s) on backend={backend}:")
1441
+ for v in newly:
1442
+ print(f" v{v:04d}")
1443
+ return 0
1444
+
1445
+
1446
+ def _handle_export_delta(args: argparse.Namespace, service, parser: argparse.ArgumentParser, effective_db: str) -> int:
1447
+ """Export claims changed since a watermark into a small SQLite delta file.
1448
+
1449
+ The delta file is a valid `merge-db --source` input. Prints (or JSON-emits)
1450
+ the export counts and the new watermark — callers should record
1451
+ `max_updated_at` and pass it as `--since` on the next run.
1452
+ """
1453
+ from memorymaster.delta_sync import export_delta
1454
+
1455
+ t0 = time.perf_counter()
1456
+ result = export_delta(effective_db, args.since, args.output)
1457
+ elapsed_ms = (time.perf_counter() - t0) * 1000
1458
+ if args.json_output:
1459
+ print(_json_envelope(result, query_ms=elapsed_ms))
1460
+ else:
1461
+ since_label = result["since"] or "(full export)"
1462
+ print(
1463
+ f"export-delta: {result['exported']} claims + {result['citations']} citations "
1464
+ f"since {since_label} -> {args.output}"
1465
+ )
1466
+ if result["max_updated_at"]:
1467
+ print(f" next watermark (--since): {result['max_updated_at']}")
1468
+ else:
1469
+ print(" delta is empty — nothing changed since the watermark")
1470
+ return 0
1471
+
1472
+
@@ -343,6 +343,60 @@ def _handle_mine_transcript(args: argparse.Namespace, service, parser: argparse.
343
343
  return 0
344
344
 
345
345
 
346
+ def _handle_mine_rules(args: argparse.Namespace, service, parser: argparse.ArgumentParser, effective_db: str) -> int:
347
+ from memorymaster.rule_miner import mine_rules
348
+ t0 = time.perf_counter()
349
+ result = mine_rules(
350
+ effective_db,
351
+ service,
352
+ since_id=getattr(args, "since_id", None),
353
+ limit=getattr(args, "limit", None),
354
+ batch_size=getattr(args, "batch_size", 200),
355
+ provider=getattr(args, "provider", "claude_cli"),
356
+ reset=getattr(args, "reset", False),
357
+ )
358
+ elapsed_ms = (time.perf_counter() - t0) * 1000
359
+ if args.json_output:
360
+ print(_json_envelope(result, query_ms=elapsed_ms))
361
+ else:
362
+ abort = f", ABORTED ({result['aborted_reason']})" if result.get("aborted_reason") else ""
363
+ print(
364
+ f"Mined rules: {result['candidates']} candidates, {result['llm_calls']} llm calls, "
365
+ f"{result['ingested']} ingested, {result['duplicates']} dupes, {result['skipped']} skipped "
366
+ f"(watermark={result['last_id']}{abort}, {elapsed_ms:.0f}ms)"
367
+ )
368
+ return 0
369
+
370
+
371
+ def _handle_detect_contradictions(args: argparse.Namespace, service, parser: argparse.ArgumentParser, effective_db: str) -> int:
372
+ from memorymaster.contradiction_probe import run_probe
373
+ t0 = time.perf_counter()
374
+ result = run_probe(
375
+ effective_db, service,
376
+ limit=getattr(args, "limit", 200),
377
+ sample=getattr(args, "sample", 50),
378
+ sim_low=getattr(args, "sim_low", 0.60),
379
+ sim_high=getattr(args, "sim_high", 0.92),
380
+ apply=getattr(args, "apply", False),
381
+ )
382
+ elapsed_ms = (time.perf_counter() - t0) * 1000
383
+ if args.json_output:
384
+ print(_json_envelope(result, query_ms=elapsed_ms))
385
+ else:
386
+ ci = result["rate_ci"]
387
+ abort = f", ABORTED ({result['aborted_reason']})" if result.get("aborted_reason") else ""
388
+ print(
389
+ f"Contradictions: {result['contradictions']}/{result['judged']} judged "
390
+ f"(rate={result['rate']:.2f} CI95=[{ci[0]:.2f},{ci[1]:.2f}]) from "
391
+ f"{result['candidate_pairs']} candidate pairs; cache_hits={result['cache_hits']}, "
392
+ f"errors={result['judge_errors']}, flagged={result['flagged_conflicted']}{abort} ({elapsed_ms:.0f}ms)"
393
+ )
394
+ for f in result["found"][:20]:
395
+ print(f" [{f['severity']}] claims {f['claim_a_id']} <> {f['claim_b_id']} "
396
+ f"(sim={f['similarity']}): {f['reason']}")
397
+ return 0
398
+
399
+
346
400
  def _handle_wiki_breakdown(args: argparse.Namespace, service, parser: argparse.ArgumentParser, effective_db: str) -> int:
347
401
  from memorymaster.wiki_engine import breakdown
348
402
  t0 = time.perf_counter()
@@ -703,6 +757,8 @@ COMMAND_HANDLERS: dict[str, object] = {
703
757
  "wiki-freshness": _handle_wiki_freshness,
704
758
  "bases-generate": _handle_bases_generate,
705
759
  "mine-transcript": _handle_mine_transcript,
760
+ "mine-rules": _handle_mine_rules,
761
+ "detect-contradictions": _handle_detect_contradictions,
706
762
  "verify-claims": _handle_verify_claims,
707
763
  "extract-entities": _handle_extract_entities,
708
764
  "entity-stats": _handle_entity_stats,
@@ -222,6 +222,11 @@ class Config:
222
222
  llm_rerank: bool = False
223
223
  rrf_tiebreaker_enabled: bool = False
224
224
  rrf_tiebreaker_threshold: float = 0.01
225
+ # Floor-ratio gate (gbrain v0.35.6 "hybrid.floor_ratio"): metadata boosts
226
+ # (confidence/freshness/tier/pinned) only apply to candidates whose
227
+ # query-relevance (lexical+vector) is >= boost_floor_ratio * top relevance.
228
+ # 0.0 = disabled (boosts always apply) — preserves pre-v3.22 behaviour.
229
+ boost_floor_ratio: float = 0.0
225
230
 
226
231
  # --- Initial confidence priors calibrated from validator outcomes ---
227
232
  default_initial_confidence: float = DEFAULT_INITIAL_CONFIDENCE
@@ -397,6 +402,7 @@ def load_config(config_path: str | Path | None = None) -> Config:
397
402
  _apply_env_bool(overrides, "MEMORYMASTER_LLM_RERANK", "llm_rerank")
398
403
  _apply_env_bool(overrides, "MEMORYMASTER_RRF_TIEBREAKER", "rrf_tiebreaker_enabled")
399
404
  _apply_env_float(overrides, "MEMORYMASTER_RRF_TIEBREAKER_THRESHOLD", "rrf_tiebreaker_threshold")
405
+ _apply_env_float(overrides, "MEMORYMASTER_BOOST_FLOOR_RATIO", "boost_floor_ratio")
400
406
  _apply_env_retrieval_profiles(overrides)
401
407
 
402
408
  # Filter to only valid Config fields
@@ -180,6 +180,27 @@ Only: bug root causes, decisions, gotchas, constraints. Never: credentials, IPs,
180
180
  pass
181
181
 
182
182
 
183
+ def _run_rule_extraction(transcript_path, cwd):
184
+ """R1b ongoing: mine the latest correction in this session into a rule claim.
185
+
186
+ Reuses memorymaster.rule_miner.mine_transcript_rules (single source of truth
187
+ for the correction->rule prompt + ingest path). Bounded to one window per
188
+ stop to keep the hook fast; rules land as low-confidence candidates."""
189
+ try:
190
+ if not transcript_path or not os.path.exists(transcript_path) or not os.path.exists(DB_PATH):
191
+ return
192
+ from memorymaster.rule_miner import mine_transcript_rules
193
+ from memorymaster.service import MemoryService
194
+
195
+ scope = "project:" + os.path.basename(cwd).lower().replace(" ", "-") if cwd else "global"
196
+ svc = MemoryService(DB_PATH, workspace_root=Path(cwd or PROJECT_ROOT))
197
+ stats = mine_transcript_rules(transcript_path, svc, scope=scope, max_windows=1)
198
+ if stats.get("ingested"):
199
+ sys.stderr.write(f"[MemoryMaster] mined {stats['ingested']} rule(s) from corrections\n")
200
+ except Exception:
201
+ pass
202
+
203
+
183
204
  def main():
184
205
  try:
185
206
  data = json.loads(sys.stdin.read() or "{}")
@@ -232,6 +253,9 @@ def main():
232
253
  # Not time to block — run passive Gemini extraction
233
254
  _run_gemini_extraction(transcript_path, cwd)
234
255
 
256
+ # R1b: mine the latest correction in this session into a rule claim
257
+ _run_rule_extraction(transcript_path, cwd)
258
+
235
259
  sys.stdout.write(json.dumps({"decision": "approve"}))
236
260
 
237
261