memorymaster 3.22.0__tar.gz → 3.23.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 (398) hide show
  1. {memorymaster-3.22.0/memorymaster.egg-info → memorymaster-3.23.0}/PKG-INFO +4 -3
  2. {memorymaster-3.22.0 → memorymaster-3.23.0}/README.md +3 -2
  3. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/__init__.py +1 -1
  4. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/cli.py +7 -0
  5. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/cli_handlers_basic.py +1 -0
  6. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/cli_handlers_curation.py +37 -0
  7. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/contradiction_probe.py +129 -0
  8. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/steward.py +75 -8
  9. memorymaster-3.23.0/memorymaster/verbatim_cleanup.py +175 -0
  10. {memorymaster-3.22.0 → memorymaster-3.23.0/memorymaster.egg-info}/PKG-INFO +4 -3
  11. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster.egg-info/SOURCES.txt +3 -0
  12. {memorymaster-3.22.0 → memorymaster-3.23.0}/pyproject.toml +1 -1
  13. memorymaster-3.23.0/tests/test_steward_contradiction_phase.py +181 -0
  14. memorymaster-3.23.0/tests/test_verbatim_cleanup.py +152 -0
  15. {memorymaster-3.22.0 → memorymaster-3.23.0}/LICENSE +0 -0
  16. {memorymaster-3.22.0 → memorymaster-3.23.0}/artifacts/bm25-per-field-eval-harness.py +0 -0
  17. {memorymaster-3.22.0 → memorymaster-3.23.0}/benchmarks/longmemeval_runner.py +0 -0
  18. {memorymaster-3.22.0 → memorymaster-3.23.0}/benchmarks/longmemeval_vector_runner.py +0 -0
  19. {memorymaster-3.22.0 → memorymaster-3.23.0}/benchmarks/perf_smoke.py +0 -0
  20. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/__main__.py +0 -0
  21. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/_storage_lifecycle.py +0 -0
  22. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/_storage_read.py +0 -0
  23. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/_storage_schema.py +0 -0
  24. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/_storage_shared.py +0 -0
  25. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/_storage_sources.py +0 -0
  26. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/_storage_write_claims.py +0 -0
  27. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/access_control.py +0 -0
  28. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/action_exporters.py +0 -0
  29. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/action_extractor.py +0 -0
  30. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/atlas_claim_extractor.py +0 -0
  31. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/atlas_contract.py +0 -0
  32. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/auto_extractor.py +0 -0
  33. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/auto_resolver.py +0 -0
  34. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/candidate_dedupe.py +0 -0
  35. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/claim_edges.py +0 -0
  36. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/claim_verifier.py +0 -0
  37. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/cli_helpers.py +0 -0
  38. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/closets.py +0 -0
  39. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config.py +0 -0
  40. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/claude-md-append.md +0 -0
  41. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/codex-agents-md-append.md +0 -0
  42. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-auto-ingest.py +0 -0
  43. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-classify.py +0 -0
  44. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-dream-sync.py +0 -0
  45. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-precompact.py +0 -0
  46. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-recall.py +0 -0
  47. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-session-start.py +0 -0
  48. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-steward-cycle.py +0 -0
  49. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-validate-wiki.py +0 -0
  50. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/conflict_resolver.py +0 -0
  51. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/connectors/__init__.py +0 -0
  52. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/connectors/whatsapp.py +0 -0
  53. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/context_hook.py +0 -0
  54. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/context_optimizer.py +0 -0
  55. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/daily_notes.py +0 -0
  56. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/dashboard.py +0 -0
  57. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/dashboard_auth.py +0 -0
  58. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/db_merge.py +0 -0
  59. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/delta_sync.py +0 -0
  60. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/dream_bridge.py +0 -0
  61. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/embeddings.py +0 -0
  62. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/entity_extractor.py +0 -0
  63. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/entity_graph.py +0 -0
  64. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/entity_registry.py +0 -0
  65. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/federated_graphify.py +0 -0
  66. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/feedback.py +0 -0
  67. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/graph_store.py +0 -0
  68. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/hook_log.py +0 -0
  69. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/__init__.py +0 -0
  70. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/calibration.py +0 -0
  71. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/compact_summaries.py +0 -0
  72. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/compactor.py +0 -0
  73. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/daydream_ingest.py +0 -0
  74. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/decay.py +0 -0
  75. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/dedup.py +0 -0
  76. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/deterministic.py +0 -0
  77. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/entity_graph_export.py +0 -0
  78. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/extractor.py +0 -0
  79. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/staleness.py +0 -0
  80. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/validator.py +0 -0
  81. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/key_rotator.py +0 -0
  82. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/lifecycle.py +0 -0
  83. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/llm_budget.py +0 -0
  84. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/llm_provider.py +0 -0
  85. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/llm_rerank.py +0 -0
  86. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/llm_steward.py +0 -0
  87. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/mcp_path_policy.py +0 -0
  88. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/mcp_server.py +0 -0
  89. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/mcp_usage.py +0 -0
  90. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/media_processing.py +0 -0
  91. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/media_providers.py +0 -0
  92. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/metrics_exporter.py +0 -0
  93. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/migrations/0001_initial.py +0 -0
  94. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/migrations/0002_miner_state.py +0 -0
  95. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/migrations/0003_contradiction_verdicts.py +0 -0
  96. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/migrations/0004_query_cache.py +0 -0
  97. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/migrations/__init__.py +0 -0
  98. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/migrations/runner.py +0 -0
  99. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/models.py +0 -0
  100. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/observability.py +0 -0
  101. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/operator.py +0 -0
  102. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/operator_queue.py +0 -0
  103. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/plugins.py +0 -0
  104. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/policy.py +0 -0
  105. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/postgres_store.py +0 -0
  106. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/qdrant_backend.py +0 -0
  107. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/qdrant_recall_fallback.py +0 -0
  108. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/qmd_bridge.py +0 -0
  109. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/query_cache.py +0 -0
  110. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/query_classifier.py +0 -0
  111. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/query_expansion.py +0 -0
  112. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/recall_fusion.py +0 -0
  113. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/recall_tokenizer.py +0 -0
  114. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/retrieval.py +0 -0
  115. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/retry.py +0 -0
  116. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/review.py +0 -0
  117. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/rl_trainer.py +0 -0
  118. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/rule_miner.py +0 -0
  119. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/rules.py +0 -0
  120. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/scheduler.py +0 -0
  121. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/schema.py +0 -0
  122. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/schema.sql +0 -0
  123. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/schema_postgres.sql +0 -0
  124. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/scope_utils.py +0 -0
  125. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/security.py +0 -0
  126. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/service.py +0 -0
  127. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/session_tracker.py +0 -0
  128. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/setup_hooks.py +0 -0
  129. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/skill_evolver.py +0 -0
  130. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/snapshot.py +0 -0
  131. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/steward_classifier.py +0 -0
  132. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/steward_features.py +0 -0
  133. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/storage.py +0 -0
  134. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/store_factory.py +0 -0
  135. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/transcript_miner.py +0 -0
  136. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/turn_schema.py +0 -0
  137. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/vault_bases.py +0 -0
  138. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/vault_curator.py +0 -0
  139. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/vault_exporter.py +0 -0
  140. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/vault_linter.py +0 -0
  141. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/vault_log.py +0 -0
  142. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/vault_query_capture.py +0 -0
  143. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/vault_synthesis.py +0 -0
  144. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/verbatim_recall.py +0 -0
  145. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/verbatim_store.py +0 -0
  146. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/webhook.py +0 -0
  147. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/wiki_engine.py +0 -0
  148. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/wiki_freshness.py +0 -0
  149. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/wiki_similarity.py +0 -0
  150. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/wiki_suggest.py +0 -0
  151. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/wiki_validate.py +0 -0
  152. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster.egg-info/dependency_links.txt +0 -0
  153. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster.egg-info/entry_points.txt +0 -0
  154. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster.egg-info/requires.txt +0 -0
  155. {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster.egg-info/top_level.txt +0 -0
  156. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/agg_recall_latency.py +0 -0
  157. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/alert_operator_metrics.py +0 -0
  158. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/audit_dedupe_precision.py +0 -0
  159. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/autoresearch_daemon.py +0 -0
  160. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/backfill_entity_extraction.py +0 -0
  161. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/backfill_graph_store.py +0 -0
  162. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/backfill_stop_hook_citations.py +0 -0
  163. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/backtest_steward_classifier.py +0 -0
  164. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/build_steward_training_set.py +0 -0
  165. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/check_hook_template_drift.py +0 -0
  166. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/claude_to_turns.py +0 -0
  167. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/codex_live_to_turns.py +0 -0
  168. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/compaction_edge_cases.py +0 -0
  169. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/compaction_trace_report.py +0 -0
  170. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/compaction_trace_validate.py +0 -0
  171. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/confusion_matrix_eval.py +0 -0
  172. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/conversation_importer.py +0 -0
  173. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/conversation_to_turns.py +0 -0
  174. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/e2e_operator.py +0 -0
  175. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/email_live_to_turns.py +0 -0
  176. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/eval_bm25_sweep.py +0 -0
  177. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/eval_classify_f1.py +0 -0
  178. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/eval_memorymaster.py +0 -0
  179. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/eval_recall_precision_at_5.py +0 -0
  180. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/eval_recall_quality.py +0 -0
  181. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/eval_steward_pareto.py +0 -0
  182. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/eval_verbatim_recall.py +0 -0
  183. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/expand_recall_eval.py +0 -0
  184. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/generate_drill_signoff.py +0 -0
  185. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/git_to_turns.py +0 -0
  186. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/github_live_to_turns.py +0 -0
  187. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/gitnexus_to_claims.py +0 -0
  188. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/grid_recall_weights.py +0 -0
  189. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/index_claims_to_qdrant.py +0 -0
  190. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/ingest_planning_docs.py +0 -0
  191. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/jira_live_to_turns.py +0 -0
  192. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/label_prompts_with_judge.py +0 -0
  193. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/llm_benchmark.py +0 -0
  194. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/measure_dedupe_thresholds.py +0 -0
  195. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/merge_scope_variants.py +0 -0
  196. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/messages_to_turns.py +0 -0
  197. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/operator_metrics.py +0 -0
  198. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/precompute_candidates.py +0 -0
  199. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/recurring_incident_drill.py +0 -0
  200. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/release_readiness.py +0 -0
  201. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/run_codex_autologger.py +0 -0
  202. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/run_incident_drill.py +0 -0
  203. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/scheduled_ingest.py +0 -0
  204. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/setup-hooks.py +0 -0
  205. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/slack_live_to_turns.py +0 -0
  206. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/sync_hook_templates.py +0 -0
  207. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/tickets_to_turns.py +0 -0
  208. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/train_steward_classifier.py +0 -0
  209. {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/webhook_to_turns.py +0 -0
  210. {memorymaster-3.22.0 → memorymaster-3.23.0}/setup.cfg +0 -0
  211. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/bench_longmemeval.py +0 -0
  212. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/conftest.py +0 -0
  213. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/integration/test_extract_llm_ollama_live.py +0 -0
  214. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_access_control.py +0 -0
  215. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_action_exporters.py +0 -0
  216. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_action_extractor.py +0 -0
  217. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_atlas_claim_extractor.py +0 -0
  218. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_atlas_contract.py +0 -0
  219. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_atlas_source_schema.py +0 -0
  220. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_auto_extractor.py +0 -0
  221. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_auto_ingest_hook_citations.py +0 -0
  222. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_auto_ingest_hook_schema.py +0 -0
  223. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_auto_resolver.py +0 -0
  224. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_auto_validate.py +0 -0
  225. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_backend_parity.py +0 -0
  226. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_bm25_per_field.py +0 -0
  227. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_calibration.py +0 -0
  228. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_calibration_priors_applied.py +0 -0
  229. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_candidate_dedupe.py +0 -0
  230. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_claim_edges.py +0 -0
  231. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_claim_links.py +0 -0
  232. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_claim_type_ranking.py +0 -0
  233. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_classify_hook_f1.py +0 -0
  234. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_classify_hook_latency.py +0 -0
  235. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_claude_to_turns.py +0 -0
  236. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_cli_dry_run.py +0 -0
  237. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_cli_json_flag.py +0 -0
  238. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_cli_ready.py +0 -0
  239. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_cli_review_queue.py +0 -0
  240. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_cli_subcommands.py +0 -0
  241. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_closets.py +0 -0
  242. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_closets_recall_integration.py +0 -0
  243. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_compact_summaries.py +0 -0
  244. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_compact_summaries_sensitivity.py +0 -0
  245. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_compaction_trace.py +0 -0
  246. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_compactor_artifact_order.py +0 -0
  247. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_config.py +0 -0
  248. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_conflict_resolver.py +0 -0
  249. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_confusion_matrix_eval.py +0 -0
  250. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_connection_retry.py +0 -0
  251. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_connectors.py +0 -0
  252. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_context_hook.py +0 -0
  253. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_context_optimizer.py +0 -0
  254. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_context_optimizer_provider.py +0 -0
  255. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_contradiction_probe.py +0 -0
  256. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_conversation_to_turns.py +0 -0
  257. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dashboard.py +0 -0
  258. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dashboard_auth.py +0 -0
  259. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dashboard_coverage.py +0 -0
  260. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dashboard_latency.py +0 -0
  261. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dashboard_lineage.py +0 -0
  262. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dashboard_review_queue.py +0 -0
  263. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_daydream_ingest.py +0 -0
  264. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_db_merge_confidence_conflict.py +0 -0
  265. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_db_merge_coverage_v2.py +0 -0
  266. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_decay_coverage.py +0 -0
  267. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_decay_respects_pinned.py +0 -0
  268. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dedup.py +0 -0
  269. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dedup_cli.py +0 -0
  270. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dedup_conflict_disambiguation.py +0 -0
  271. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_delta_sync.py +0 -0
  272. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_deterministic_predicates.py +0 -0
  273. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dream_bridge_coverage_v2.py +0 -0
  274. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dream_bridge_sensitivity.py +0 -0
  275. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_embeddings_coverage.py +0 -0
  276. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_entity_extractor.py +0 -0
  277. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_entity_extractor_llm.py +0 -0
  278. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_entity_graph.py +0 -0
  279. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_entity_graph_export.py +0 -0
  280. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_entity_new_kinds.py +0 -0
  281. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_entity_regex_v3.py +0 -0
  282. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_entity_registry.py +0 -0
  283. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_eval_harness.py +0 -0
  284. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_events_schema.py +0 -0
  285. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_extract_llm_ollama.py +0 -0
  286. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_federated_graphify_mcp.py +0 -0
  287. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_federated_query_safety.py +0 -0
  288. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_feedback.py +0 -0
  289. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_floor_gate.py +0 -0
  290. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_fts5_search.py +0 -0
  291. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_graph_distance.py +0 -0
  292. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_graph_store.py +0 -0
  293. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_handler_regressions.py +0 -0
  294. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_hook_env_isolation.py +0 -0
  295. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_human_id.py +0 -0
  296. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_incident_drill_runner.py +0 -0
  297. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_integration_workflows.py +0 -0
  298. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_key_rotator.py +0 -0
  299. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_lifecycle.py +0 -0
  300. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_lifecycle_supersede_invariant.py +0 -0
  301. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_llm_budget.py +0 -0
  302. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_llm_fallback.py +0 -0
  303. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_llm_provider_claude_cli.py +0 -0
  304. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_llm_provider_key_rotation.py +0 -0
  305. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_llm_steward_coverage.py +0 -0
  306. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_llm_steward_key_rotation.py +0 -0
  307. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_mcp_filter_bypass.py +0 -0
  308. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_mcp_helpers.py +0 -0
  309. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_mcp_path_policy.py +0 -0
  310. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_mcp_rate_limit.py +0 -0
  311. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_mcp_server_validation.py +0 -0
  312. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_mcp_usage.py +0 -0
  313. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_media_processing.py +0 -0
  314. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_meta_decisions.py +0 -0
  315. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_metrics_exporter.py +0 -0
  316. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_migrations.py +0 -0
  317. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_observability.py +0 -0
  318. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_obsidian_mind_patterns.py +0 -0
  319. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_operator.py +0 -0
  320. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_operator_queue.py +0 -0
  321. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_perf_smoke_config.py +0 -0
  322. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_plugins.py +0 -0
  323. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_policy_coverage.py +0 -0
  324. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_policy_mode_env.py +0 -0
  325. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_postgres_parity.py +0 -0
  326. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_qdrant_backend.py +0 -0
  327. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_qmd_bridge.py +0 -0
  328. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_qrels_regression.py +0 -0
  329. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_query_cache.py +0 -0
  330. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_query_classifier.py +0 -0
  331. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_query_expansion.py +0 -0
  332. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_recall_entity_fanout.py +0 -0
  333. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_recall_fusion.py +0 -0
  334. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_recall_latency.py +0 -0
  335. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_recall_precision_at_5.py +0 -0
  336. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_recall_tokenizer.py +0 -0
  337. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_recall_vector_fallback.py +0 -0
  338. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_reliability_hardening.py +0 -0
  339. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_resolvers_concurrent_supersede.py +0 -0
  340. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_retrieval_profile.py +0 -0
  341. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_retrieval_profiles.py +0 -0
  342. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_retrieval_rrf_tiebreaker.py +0 -0
  343. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_retrieval_weights.py +0 -0
  344. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_review.py +0 -0
  345. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_rl_trainer.py +0 -0
  346. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_rrf_auto_gate.py +0 -0
  347. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_rule_claims.py +0 -0
  348. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_rule_miner.py +0 -0
  349. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_scheduler.py +0 -0
  350. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_schema.py +0 -0
  351. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_scope_boost.py +0 -0
  352. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_scope_utils.py +0 -0
  353. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_security_access.py +0 -0
  354. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_security_patterns.py +0 -0
  355. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_sensitivity_filter_adversarial.py +0 -0
  356. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_sensitivity_filter_adversarial_v2.py +0 -0
  357. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_sensitivity_filter_t07.py +0 -0
  358. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_service_coverage.py +0 -0
  359. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_session_tracker.py +0 -0
  360. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_snapshot.py +0 -0
  361. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_snapshot_roundtrip.py +0 -0
  362. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_sqlite_core.py +0 -0
  363. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_staleness.py +0 -0
  364. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_stealth_mode.py +0 -0
  365. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_steward.py +0 -0
  366. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_steward_classifier.py +0 -0
  367. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_steward_daydream_hook.py +0 -0
  368. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_steward_features.py +0 -0
  369. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_steward_features_v3.py +0 -0
  370. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_steward_resolution_parity.py +0 -0
  371. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_storage_parity.py +0 -0
  372. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_store_factory.py +0 -0
  373. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_tenant_isolation.py +0 -0
  374. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_turn_schema.py +0 -0
  375. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_two_pass_recall.py +0 -0
  376. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_v311_fixes.py +0 -0
  377. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_v313_e2e.py +0 -0
  378. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_v313_run_cycle_dedupe.py +0 -0
  379. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_v390_e2e.py +0 -0
  380. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_v391_strict_warnings.py +0 -0
  381. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_vault_exporter.py +0 -0
  382. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_vault_linter_orphan.py +0 -0
  383. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_vector_search.py +0 -0
  384. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_verbatim_dedup.py +0 -0
  385. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_verbatim_recall.py +0 -0
  386. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_verbatim_store.py +0 -0
  387. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_verbatim_store_qdrant.py +0 -0
  388. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_webhook.py +0 -0
  389. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_webhook_hmac.py +0 -0
  390. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_whatsapp_importer.py +0 -0
  391. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_wiki_autopromote.py +0 -0
  392. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_wiki_binding.py +0 -0
  393. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_wiki_engine_idempotency.py +0 -0
  394. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_wiki_explored_and_contradictions.py +0 -0
  395. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_wiki_freshness.py +0 -0
  396. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_wiki_similarity_multiscope.py +0 -0
  397. {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_wiki_suggest.py +0 -0
  398. {memorymaster-3.22.0 → memorymaster-3.23.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.22.0
3
+ Version: 3.23.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-2214-green.svg)]()
55
+ [![Tests](https://img.shields.io/badge/tests-2224-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/)
@@ -91,7 +91,8 @@ recent PR status, and sensitivity-filter invariants.
91
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
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
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
94
+ - **Semantic contradiction probe** (new in v3.22.0, wired as a steward phase in v3.23.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; in v3.23 the same probe runs inside `run-steward` and emits paste-ready `conflicted` proposals
95
+ - **Verbatim archive cleanup** (new in v3.23.0): `verbatim-cleanup` dedups the raw-transcript table and optionally purges pre-#128 junk rows, with a dry-run default and FTS5 mirror sync
95
96
  - **Steward governance**: multi-probe validators (filesystem, format, citation, semantic, tool) with proposal review
96
97
  - **Conflict resolution**: 5-tier auto (confidence > freshness > citations > LLM > manual)
97
98
  - **Auto-redaction** at ingest: JWT, GitHub tokens, Bearer, AWS keys, SSH keys, custom patterns
@@ -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-2214-green.svg)]()
9
+ [![Tests](https://img.shields.io/badge/tests-2224-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/)
@@ -45,7 +45,8 @@ recent PR status, and sensitivity-filter invariants.
45
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
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
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
48
+ - **Semantic contradiction probe** (new in v3.22.0, wired as a steward phase in v3.23.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; in v3.23 the same probe runs inside `run-steward` and emits paste-ready `conflicted` proposals
49
+ - **Verbatim archive cleanup** (new in v3.23.0): `verbatim-cleanup` dedups the raw-transcript table and optionally purges pre-#128 junk rows, with a dry-run default and FTS5 mirror sync
49
50
  - **Steward governance**: multi-probe validators (filesystem, format, citation, semantic, tool) with proposal review
50
51
  - **Conflict resolution**: 5-tier auto (confidence > freshness > citations > LLM > manual)
51
52
  - **Auto-redaction** at ingest: JWT, GitHub tokens, Bearer, AWS keys, SSH keys, custom patterns
@@ -2,4 +2,4 @@
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "3.22.0"
5
+ __version__ = "3.23.0"
@@ -332,6 +332,7 @@ def build_parser() -> argparse.ArgumentParser:
332
332
  steward.add_argument("--probe-failure-threshold", type=int, default=3, help="Open circuit breaker for a probe type after this many timeout/error failures")
333
333
  steward.add_argument("--disable-semantic-probe", action="store_true", help="Disable semantic retrieval probe in steward planner")
334
334
  steward.add_argument("--disable-tool-probe", action="store_true", help="Disable tool/storage probe in steward planner")
335
+ steward.add_argument("--disable-contradiction-probe", action="store_true", help="Disable semantic contradiction probe in steward planner (v3.23)")
335
336
  steward.add_argument("--allow-sensitive", action="store_true", help="Include sensitive claims in stewardship scan")
336
337
  steward.add_argument("--apply", action="store_true", help="Apply proposed status transitions")
337
338
  steward.add_argument("--artifact-json", default="artifacts/steward/steward_report.json", help="Path to steward JSON report artifact")
@@ -464,6 +465,12 @@ def build_parser() -> argparse.ArgumentParser:
464
465
  mine_rules_cmd.add_argument("--provider", default="claude_cli", help="LLM provider for this run (default: claude_cli)")
465
466
  mine_rules_cmd.add_argument("--reset", action="store_true", help="Clear the stored watermark before running (re-scan from the start)")
466
467
 
468
+ verbatim_clean = sub.add_parser("verbatim-cleanup", help="Dedup the verbatim archive + optionally purge pre-#128 capture-bug junk (v3.23)")
469
+ verbatim_clean.add_argument("--analyze-only", dest="analyze_only", action="store_true", help="Report composition only; do not delete")
470
+ verbatim_clean.add_argument("--apply", action="store_true", help="Actually delete (default is dry-run)")
471
+ verbatim_clean.add_argument("--no-dedup", dest="no_dedup", action="store_true", help="Skip the (session_id, content) exact-dup pass")
472
+ verbatim_clean.add_argument("--purge-junk", dest="purge_junk", action="store_true", help="Also delete rows matching known pre-#128 junk prefixes (wiki-absorb prompt, etc.)")
473
+
467
474
  detect_contra = sub.add_parser("detect-contradictions", help="Find semantic contradictions between topically-similar claims via an LLM judge (v3.22)")
468
475
  detect_contra.add_argument("--limit", type=int, default=200, help="Max claims to load for pair sampling")
469
476
  detect_contra.add_argument("--sample", type=int, default=50, help="Max candidate pairs to judge this run (caps LLM calls)")
@@ -1228,6 +1228,7 @@ def _handle_run_steward(args: argparse.Namespace, service, parser: argparse.Argu
1228
1228
  probe_failure_threshold=args.probe_failure_threshold,
1229
1229
  enable_semantic_probe=not args.disable_semantic_probe,
1230
1230
  enable_tool_probe=not args.disable_tool_probe,
1231
+ enable_contradiction_probe=not getattr(args, "disable_contradiction_probe", False),
1231
1232
  artifact_path=Path(args.artifact_json),
1232
1233
  )
1233
1234
  elapsed_ms = (time.perf_counter() - t0) * 1000
@@ -397,6 +397,42 @@ def _handle_detect_contradictions(args: argparse.Namespace, service, parser: arg
397
397
  return 0
398
398
 
399
399
 
400
+ def _handle_verbatim_cleanup(args: argparse.Namespace, service, parser: argparse.ArgumentParser, effective_db: str) -> int:
401
+ from memorymaster.verbatim_cleanup import analyze, cleanup
402
+ t0 = time.perf_counter()
403
+ if getattr(args, "analyze_only", False):
404
+ result = analyze(effective_db)
405
+ else:
406
+ result = cleanup(
407
+ effective_db,
408
+ dedup=not getattr(args, "no_dedup", False),
409
+ purge_junk=getattr(args, "purge_junk", False),
410
+ dry_run=not getattr(args, "apply", False),
411
+ )
412
+ elapsed_ms = (time.perf_counter() - t0) * 1000
413
+ if args.json_output:
414
+ print(_json_envelope(result, query_ms=elapsed_ms))
415
+ else:
416
+ if not result.get("verbatim_present", True):
417
+ print("verbatim-cleanup: verbatim_memories table not present; nothing to do")
418
+ elif "before_total" in result: # cleanup result
419
+ tag = "(dry-run)" if result["dry_run"] else "(applied)"
420
+ print(
421
+ f"verbatim-cleanup {tag}: dedup_deleted={result['dedup_deleted']}, "
422
+ f"junk_deleted={result['junk_deleted']}, "
423
+ f"before={result['before_total']} -> after={result['after_total']} "
424
+ f"({elapsed_ms:.0f}ms)"
425
+ )
426
+ else: # analyze result
427
+ print(
428
+ f"verbatim analyze: total={result['total']}, distinct={result['distinct_content']}, "
429
+ f"duplicate_extras={result['duplicate_extras']}, "
430
+ f"empty_role={result['empty_role_rows']}, junk_prefix={result['junk_prefix_rows']} "
431
+ f"({elapsed_ms:.0f}ms)"
432
+ )
433
+ return 0
434
+
435
+
400
436
  def _handle_wiki_breakdown(args: argparse.Namespace, service, parser: argparse.ArgumentParser, effective_db: str) -> int:
401
437
  from memorymaster.wiki_engine import breakdown
402
438
  t0 = time.perf_counter()
@@ -759,6 +795,7 @@ COMMAND_HANDLERS: dict[str, object] = {
759
795
  "mine-transcript": _handle_mine_transcript,
760
796
  "mine-rules": _handle_mine_rules,
761
797
  "detect-contradictions": _handle_detect_contradictions,
798
+ "verbatim-cleanup": _handle_verbatim_cleanup,
762
799
  "verify-claims": _handle_verify_claims,
763
800
  "extract-entities": _handle_extract_entities,
764
801
  "entity-stats": _handle_entity_stats,
@@ -26,6 +26,7 @@ import logging
26
26
  import math
27
27
  import os
28
28
  import sqlite3
29
+ import time
29
30
  from datetime import datetime, timezone
30
31
  from typing import Any
31
32
 
@@ -33,6 +34,7 @@ from memorymaster import llm_budget, llm_provider
33
34
  from memorymaster.embeddings import EmbeddingProvider, cosine_similarity, create_best_provider
34
35
  from memorymaster.lifecycle import transition_claim
35
36
  from memorymaster.models import Claim
37
+ from memorymaster.security import redact_text
36
38
 
37
39
  logger = logging.getLogger(__name__)
38
40
 
@@ -324,3 +326,130 @@ def run_probe(
324
326
  lo, hi = wilson_interval(stats["contradictions"], n)
325
327
  stats["rate_ci"] = [round(lo, 4), round(hi, 4)]
326
328
  return stats
329
+
330
+
331
+ # ---------------------------------------------------------------------------
332
+ # Per-claim steward-phase entry point (v3.23)
333
+ # ---------------------------------------------------------------------------
334
+
335
+
336
+ def probe_for_claim(
337
+ service: Any,
338
+ claim: Any,
339
+ *,
340
+ sim_low: float = 0.60,
341
+ sim_high: float = 0.92,
342
+ max_pairs: int = 5,
343
+ peer_limit: int = 20,
344
+ ) -> dict[str, Any]:
345
+ """Per-claim contradiction probe for the steward cycle.
346
+
347
+ Finds topically-similar peers for ``claim`` via the existing hybrid
348
+ retrieval (so we reuse the embedder + ranker + cache), excludes pairs the
349
+ deterministic resolver / supersession owns, judges remaining candidates
350
+ against the verdict cache + LLM, and returns a normalized result dict the
351
+ steward wraps in a ``ProbeResult``.
352
+
353
+ Returns ``{passed, reasons: [...], metrics: {...}}``. ``passed`` is False
354
+ iff at least one contradiction was found.
355
+ """
356
+ started = time.monotonic()
357
+ metrics: dict[str, Any] = {
358
+ "pairs_checked": 0, "contradictions": 0,
359
+ "cache_hits": 0, "llm_calls": 0, "errors": 0,
360
+ "timed_out": False, "duration_ms": 0.0,
361
+ }
362
+ reasons: list[dict[str, Any]] = []
363
+
364
+ def _done(passed: bool) -> dict[str, Any]:
365
+ metrics["duration_ms"] = round((time.monotonic() - started) * 1000.0, 3)
366
+ return {"passed": passed, "reasons": reasons, "metrics": metrics}
367
+
368
+ if claim.status in _SKIP_STATUSES or not (claim.text or "").strip():
369
+ return _done(True)
370
+
371
+ try:
372
+ peers_rows = service.query_rows(
373
+ query_text=claim.text or "",
374
+ limit=peer_limit,
375
+ retrieval_mode="hybrid",
376
+ include_candidates=True,
377
+ include_stale=True,
378
+ include_conflicted=True,
379
+ )
380
+ except Exception as exc:
381
+ reasons.append({
382
+ "code": "contradiction_probe.peer_fetch.error",
383
+ "severity": "low",
384
+ "detail": f"peer fetch failed: {type(exc).__name__}",
385
+ "evidence": {},
386
+ })
387
+ return _done(False)
388
+
389
+ candidates: list[tuple[Any, float]] = []
390
+ for row in peers_rows:
391
+ peer = row.get("claim")
392
+ if peer is None or peer.id == claim.id:
393
+ continue
394
+ if peer.status in _SKIP_STATUSES:
395
+ continue
396
+ if _same_subject_predicate(claim, peer):
397
+ continue # deterministic resolver's domain
398
+ if _already_linked(claim, peer):
399
+ continue
400
+ vs = float(row.get("vector_score") or 0.0)
401
+ # If vectors are present, only accept pairs in the contradiction band.
402
+ # If absent (pure-lexical hybrid), accept all topical peers.
403
+ if vs > 0.0 and not (sim_low <= vs < sim_high):
404
+ continue
405
+ candidates.append((peer, vs))
406
+ if len(candidates) >= max_pairs:
407
+ break
408
+
409
+ if not candidates:
410
+ return _done(True)
411
+
412
+ db_path = getattr(service.store, "db_path", None)
413
+ if not db_path or "://" in str(db_path):
414
+ return _done(True) # verdict cache requires SQLite; skip silently on PG
415
+
416
+ conn = sqlite3.connect(str(db_path))
417
+ try:
418
+ _ensure_verdict_table(conn)
419
+ model = _model_key()
420
+ for peer, sim in candidates:
421
+ metrics["pairs_checked"] += 1
422
+ verdict = _cache_get(conn, claim.id, peer.id, model)
423
+ if verdict is not None:
424
+ metrics["cache_hits"] += 1
425
+ else:
426
+ try:
427
+ verdict = _judge_llm(claim, peer)
428
+ except llm_budget.LLMBudgetExceeded:
429
+ metrics["timed_out"] = True
430
+ break
431
+ metrics["llm_calls"] += 1
432
+ if verdict is None:
433
+ metrics["errors"] += 1
434
+ continue
435
+ _cache_put(conn, claim.id, peer.id, model, verdict)
436
+ if not verdict.get("contradicts"):
437
+ continue
438
+ verdict_reason = verdict.get("reason", "")
439
+ _, leaks = redact_text(verdict_reason)
440
+ if leaks:
441
+ continue # drop rather than surface a sensitive judge-reason
442
+ metrics["contradictions"] += 1
443
+ reasons.append({
444
+ "code": "contradiction_probe.semantic_pair",
445
+ "severity": verdict.get("severity", "medium"),
446
+ "detail": f"Semantic contradiction with claim {peer.id}: {verdict_reason}",
447
+ "evidence": {
448
+ "peer_claim_id": int(peer.id),
449
+ "similarity": round(float(sim), 4),
450
+ "from_cache": bool(verdict.get("cached")),
451
+ },
452
+ })
453
+ finally:
454
+ conn.close()
455
+ return _done(metrics["contradictions"] == 0)
@@ -24,6 +24,7 @@ ProbeType = Literal[
24
24
  "deterministic_citation_locator",
25
25
  "semantic_probe",
26
26
  "tool_probe",
27
+ "contradiction_probe",
27
28
  ]
28
29
 
29
30
  _TEXT_SUFFIXES = {
@@ -601,6 +602,46 @@ def _run_tool_probe(*, claim: Any, service: MemoryService, timeout_seconds: floa
601
602
  )
602
603
 
603
604
 
605
+ def _run_contradiction_probe(
606
+ *, claim: Any, service: MemoryService, max_pairs: int, sim_low: float, sim_high: float,
607
+ ) -> ProbeResult:
608
+ """Steward-phase wrapper around contradiction_probe.probe_for_claim.
609
+
610
+ Converts the probe's normalized dict output into a ProbeResult/Reasons.
611
+ Fail-closed: any unexpected error returns a low-severity error reason so
612
+ the circuit breaker can open the probe rather than crashing the cycle.
613
+ """
614
+ from memorymaster import contradiction_probe as cp
615
+ started = time.monotonic()
616
+ try:
617
+ out = cp.probe_for_claim(
618
+ service, claim, sim_low=sim_low, sim_high=sim_high, max_pairs=max_pairs,
619
+ )
620
+ except Exception as exc:
621
+ return ProbeResult(
622
+ probe_type="contradiction_probe", passed=False,
623
+ metrics={
624
+ "timed_out": False,
625
+ "duration_ms": round((time.monotonic() - started) * 1000.0, 3),
626
+ "error": str(exc)[:120],
627
+ },
628
+ reasons=[Reason(
629
+ code="contradiction_probe.error", probe_type="contradiction_probe",
630
+ severity="low", detail=f"probe crashed: {type(exc).__name__}",
631
+ )],
632
+ )
633
+ return ProbeResult(
634
+ probe_type="contradiction_probe",
635
+ passed=bool(out["passed"]),
636
+ metrics=out["metrics"],
637
+ reasons=[Reason(
638
+ code=r["code"], probe_type="contradiction_probe",
639
+ severity=r.get("severity", "medium"), detail=r.get("detail", ""),
640
+ evidence=r.get("evidence", {}),
641
+ ) for r in out["reasons"]],
642
+ )
643
+
644
+
604
645
  def plan_claim_probes(
605
646
  claim: Any,
606
647
  *,
@@ -608,6 +649,7 @@ def plan_claim_probes(
608
649
  max_file_bytes: int,
609
650
  include_semantic_probe: bool = True,
610
651
  include_tool_probe: bool = True,
652
+ include_contradiction_probe: bool = True,
611
653
  ) -> ClaimProbePlan:
612
654
  needle = _normalize_text(claim.object_value) or _normalize_text(claim.text)
613
655
  if len(needle) > 160:
@@ -628,6 +670,8 @@ def plan_claim_probes(
628
670
  probes.append(ProbeSpec(probe_type="semantic_probe", params={"limit": 8}))
629
671
  if include_tool_probe:
630
672
  probes.append(ProbeSpec(probe_type="tool_probe", params={}))
673
+ if include_contradiction_probe:
674
+ probes.append(ProbeSpec(probe_type="contradiction_probe", params={"max_pairs": 5}))
631
675
  return ClaimProbePlan(claim_id=claim.id, probes=probes)
632
676
 
633
677
 
@@ -709,6 +753,12 @@ def _decision_for_claim(
709
753
  proposed_status: str | None = None
710
754
  replaced_by_claim_id: int | None = None
711
755
 
756
+ contradiction_found = any(
757
+ result.probe_type == "contradiction_probe" and not result.passed
758
+ and any(r.code == "contradiction_probe.semantic_pair" for r in result.reasons)
759
+ for result in probe_results
760
+ )
761
+
712
762
  if replacement is not None:
713
763
  decision = "superseded_candidate"
714
764
  proposed_status = "superseded"
@@ -722,17 +772,20 @@ def _decision_for_claim(
722
772
  evidence={"replaced_by_claim_id": replacement.id},
723
773
  )
724
774
  )
725
- elif claim.status == "conflicted" or has_conflict:
775
+ elif claim.status == "conflicted" or has_conflict or contradiction_found:
726
776
  decision = "conflicted"
727
777
  proposed_status = "conflicted"
728
- reasons.append(
729
- Reason(
730
- code="relation.conflicting_claim",
731
- probe_type="relation",
732
- severity="high",
733
- detail="Conflicting confirmed claim exists for same tuple.",
778
+ if has_conflict or claim.status == "conflicted":
779
+ reasons.append(
780
+ Reason(
781
+ code="relation.conflicting_claim",
782
+ probe_type="relation",
783
+ severity="high",
784
+ detail="Conflicting confirmed claim exists for same tuple.",
785
+ )
734
786
  )
735
- )
787
+ # contradiction_probe.semantic_pair reasons are already in `reasons` via
788
+ # the probe_results loop above — no need to duplicate.
736
789
  else:
737
790
  no_match = any(reason.code == "filesystem_grep.no_match" for reason in reasons)
738
791
  weak_format = any(
@@ -852,6 +905,7 @@ def _run_cycle(
852
905
  probe_failure_threshold: int,
853
906
  enable_semantic_probe: bool,
854
907
  enable_tool_probe: bool,
908
+ enable_contradiction_probe: bool,
855
909
  ) -> dict[str, Any]:
856
910
  before = _status_snapshot(service, allow_sensitive=allow_sensitive)
857
911
 
@@ -900,6 +954,7 @@ def _run_cycle(
900
954
  max_file_bytes=max_probe_file_bytes,
901
955
  include_semantic_probe=enable_semantic_probe,
902
956
  include_tool_probe=enable_tool_probe,
957
+ include_contradiction_probe=enable_contradiction_probe,
903
958
  )
904
959
  probe_results: list[ProbeResult] = []
905
960
  for probe in plan.probes:
@@ -973,6 +1028,14 @@ def _run_cycle(
973
1028
  service=service,
974
1029
  timeout_seconds=probe_timeout_seconds,
975
1030
  )
1031
+ elif probe.probe_type == "contradiction_probe":
1032
+ result = _run_contradiction_probe(
1033
+ claim=claim,
1034
+ service=service,
1035
+ max_pairs=int(probe.params.get("max_pairs", 5)),
1036
+ sim_low=float(probe.params.get("sim_low", 0.60)),
1037
+ sim_high=float(probe.params.get("sim_high", 0.92)),
1038
+ )
976
1039
 
977
1040
  if result is None:
978
1041
  continue
@@ -1447,6 +1510,7 @@ def run_steward(
1447
1510
  probe_failure_threshold: int = 3,
1448
1511
  enable_semantic_probe: bool = True,
1449
1512
  enable_tool_probe: bool = True,
1513
+ enable_contradiction_probe: bool = True,
1450
1514
  artifact_path: Path | str = "artifacts/steward/steward_report.json",
1451
1515
  ) -> dict[str, Any]:
1452
1516
  if mode not in {"manual", "cadence"}:
@@ -1551,6 +1615,7 @@ def run_steward(
1551
1615
  probe_failure_threshold=probe_failure_threshold,
1552
1616
  enable_semantic_probe=enable_semantic_probe,
1553
1617
  enable_tool_probe=enable_tool_probe,
1618
+ enable_contradiction_probe=enable_contradiction_probe,
1554
1619
  )
1555
1620
  cycle_payload["cycle"] = cycle_index
1556
1621
  cycle_payload["started_at"] = cycle_started
@@ -1589,6 +1654,7 @@ def run_steward(
1589
1654
  "probe_failure_threshold": probe_failure_threshold,
1590
1655
  "enable_semantic_probe": enable_semantic_probe,
1591
1656
  "enable_tool_probe": enable_tool_probe,
1657
+ "enable_contradiction_probe": enable_contradiction_probe,
1592
1658
  "max_tool_probes": max_tool_probes,
1593
1659
  "cycles_completed": len(cycles),
1594
1660
  "cycles": cycles,
@@ -1614,6 +1680,7 @@ def run_steward(
1614
1680
  "probe_failure_threshold": probe_failure_threshold,
1615
1681
  "enable_semantic_probe": enable_semantic_probe,
1616
1682
  "enable_tool_probe": enable_tool_probe,
1683
+ "enable_contradiction_probe": enable_contradiction_probe,
1617
1684
  },
1618
1685
  "cadence": {
1619
1686
  "mode": mode,
@@ -0,0 +1,175 @@
1
+ """Verbatim archive cleanup (v3.23).
2
+
3
+ The verbatim_memories table accumulates two distinct kinds of bloat:
4
+
5
+ 1. **Junk from the pre-#128 capture bug**: the Stop hook's store_transcript
6
+ used to read top-level entry fields (the actual turns were nested under
7
+ ``message``), so the only rows it stored were a handful of non-conversation
8
+ metadata lines per transcript — most of them duplicated internal-LLM prompts
9
+ like "Rewrite ONLY the compiled truth section…". Tens of thousands of rows
10
+ carrying the same internal prompt text, with empty ``role``.
11
+
12
+ 2. **Duplicate insertions**: even after the capture fix, re-running
13
+ store_transcript on the same JSONL is dedup-checked per (session_id, content),
14
+ but cross-session duplicates and pre-WAL-fix collisions persist.
15
+
16
+ This module reports + reclaims both. It DELETES rows directly (verbatim has no
17
+ foreign keys), and reflushes the FTS5 mirror so search stays consistent. Dry-run
18
+ by default; ``--apply`` to actually delete.
19
+ """
20
+ from __future__ import annotations
21
+
22
+ import logging
23
+ import sqlite3
24
+ from typing import Any
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ # Known pre-#128 junk content prefixes (internal LLM prompts that the broken
29
+ # capture path mistakenly stored as if they were conversation turns).
30
+ _JUNK_PREFIXES: tuple[str, ...] = (
31
+ "Rewrite ONLY the compiled truth",
32
+ "You are a memory curator",
33
+ "You are an expert at compiling",
34
+ )
35
+
36
+
37
+ def _connect(db_path: str) -> sqlite3.Connection:
38
+ conn = sqlite3.connect(db_path)
39
+ conn.row_factory = sqlite3.Row
40
+ conn.execute("PRAGMA journal_mode = WAL")
41
+ return conn
42
+
43
+
44
+ def _verbatim_present(conn: sqlite3.Connection) -> bool:
45
+ row = conn.execute(
46
+ "SELECT 1 FROM sqlite_master WHERE type='table' AND name='verbatim_memories'"
47
+ ).fetchone()
48
+ return row is not None
49
+
50
+
51
+ def _fts_present(conn: sqlite3.Connection) -> bool:
52
+ row = conn.execute(
53
+ "SELECT 1 FROM sqlite_master WHERE type='table' AND name='verbatim_fts'"
54
+ ).fetchone()
55
+ return row is not None
56
+
57
+
58
+ def analyze(db_path: str) -> dict[str, Any]:
59
+ """Report verbatim composition without touching anything.
60
+
61
+ Returns counts: total rows, distinct contents, exact duplicates (per
62
+ (session_id, content)), pre-#128 junk rows (matching known prefixes), and
63
+ rows with empty role (legacy capture-bug signature).
64
+ """
65
+ if "://" in str(db_path):
66
+ raise ValueError("verbatim cleanup is SQLite-only")
67
+ conn = _connect(db_path)
68
+ try:
69
+ if not _verbatim_present(conn):
70
+ return {"verbatim_present": False}
71
+ total = conn.execute("SELECT COUNT(*) FROM verbatim_memories").fetchone()[0]
72
+ distinct_content = conn.execute(
73
+ "SELECT COUNT(DISTINCT content) FROM verbatim_memories"
74
+ ).fetchone()[0]
75
+ # Exact duplicates by (session_id, content): the "extra" copies beyond
76
+ # the first per group are deletable.
77
+ dup_extras = conn.execute(
78
+ """SELECT COALESCE(SUM(c - 1), 0) FROM (
79
+ SELECT COUNT(*) AS c FROM verbatim_memories
80
+ GROUP BY session_id, content HAVING c > 1
81
+ )"""
82
+ ).fetchone()[0]
83
+ empty_role = conn.execute(
84
+ "SELECT COUNT(*) FROM verbatim_memories WHERE COALESCE(role, '') = ''"
85
+ ).fetchone()[0]
86
+ junk = 0
87
+ for prefix in _JUNK_PREFIXES:
88
+ junk += conn.execute(
89
+ "SELECT COUNT(*) FROM verbatim_memories WHERE content LIKE ?",
90
+ (prefix + "%",),
91
+ ).fetchone()[0]
92
+ return {
93
+ "verbatim_present": True,
94
+ "total": int(total),
95
+ "distinct_content": int(distinct_content),
96
+ "duplicate_extras": int(dup_extras),
97
+ "empty_role_rows": int(empty_role),
98
+ "junk_prefix_rows": int(junk),
99
+ "junk_prefixes": list(_JUNK_PREFIXES),
100
+ }
101
+ finally:
102
+ conn.close()
103
+
104
+
105
+ def cleanup(
106
+ db_path: str,
107
+ *,
108
+ dedup: bool = True,
109
+ purge_junk: bool = False,
110
+ dry_run: bool = True,
111
+ ) -> dict[str, Any]:
112
+ """Reclaim space. Default is dry-run (counts only); ``dry_run=False`` to
113
+ actually delete. ``purge_junk`` is opt-in because it targets a known-bad
114
+ pattern set — review with ``analyze`` first.
115
+
116
+ Returns counts of what was (or would be) removed, and the new total.
117
+ """
118
+ if "://" in str(db_path):
119
+ raise ValueError("verbatim cleanup is SQLite-only")
120
+ conn = _connect(db_path)
121
+ try:
122
+ if not _verbatim_present(conn):
123
+ return {"verbatim_present": False}
124
+ out: dict[str, Any] = {
125
+ "dry_run": bool(dry_run),
126
+ "dedup_deleted": 0,
127
+ "junk_deleted": 0,
128
+ "before_total": int(conn.execute("SELECT COUNT(*) FROM verbatim_memories").fetchone()[0]),
129
+ }
130
+ has_fts = _fts_present(conn)
131
+ deleted_ids: list[int] = []
132
+
133
+ if dedup:
134
+ # Identify ids to drop: for each (session_id, content) group, keep
135
+ # the smallest id (oldest), drop the rest.
136
+ rows = conn.execute(
137
+ """SELECT id FROM verbatim_memories WHERE id NOT IN (
138
+ SELECT MIN(id) FROM verbatim_memories GROUP BY session_id, content
139
+ )"""
140
+ ).fetchall()
141
+ ids = [int(r[0]) for r in rows]
142
+ out["dedup_deleted"] = len(ids)
143
+ deleted_ids.extend(ids)
144
+
145
+ if purge_junk:
146
+ for prefix in _JUNK_PREFIXES:
147
+ rows = conn.execute(
148
+ "SELECT id FROM verbatim_memories WHERE content LIKE ?",
149
+ (prefix + "%",),
150
+ ).fetchall()
151
+ ids = [int(r[0]) for r in rows]
152
+ out["junk_deleted"] += len(ids)
153
+ deleted_ids.extend(ids)
154
+
155
+ if not dry_run and deleted_ids:
156
+ # Chunked DELETE to keep SQLite parameter binding bounded.
157
+ BATCH = 500
158
+ for i in range(0, len(deleted_ids), BATCH):
159
+ chunk = deleted_ids[i : i + BATCH]
160
+ placeholders = ",".join("?" for _ in chunk)
161
+ conn.execute(
162
+ f"DELETE FROM verbatim_memories WHERE id IN ({placeholders})", chunk
163
+ )
164
+ if has_fts:
165
+ conn.execute(
166
+ f"DELETE FROM verbatim_fts WHERE rowid IN ({placeholders})", chunk
167
+ )
168
+ conn.commit()
169
+
170
+ out["after_total"] = int(
171
+ conn.execute("SELECT COUNT(*) FROM verbatim_memories").fetchone()[0]
172
+ )
173
+ return out
174
+ finally:
175
+ conn.close()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memorymaster
3
- Version: 3.22.0
3
+ Version: 3.23.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-2214-green.svg)]()
55
+ [![Tests](https://img.shields.io/badge/tests-2224-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/)
@@ -91,7 +91,8 @@ recent PR status, and sensitivity-filter invariants.
91
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
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
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
94
+ - **Semantic contradiction probe** (new in v3.22.0, wired as a steward phase in v3.23.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; in v3.23 the same probe runs inside `run-steward` and emits paste-ready `conflicted` proposals
95
+ - **Verbatim archive cleanup** (new in v3.23.0): `verbatim-cleanup` dedups the raw-transcript table and optionally purges pre-#128 junk rows, with a dry-run default and FTS5 mirror sync
95
96
  - **Steward governance**: multi-probe validators (filesystem, format, citation, semantic, tool) with proposal review
96
97
  - **Conflict resolution**: 5-tier auto (confidence > freshness > citations > LLM > manual)
97
98
  - **Auto-redaction** at ingest: JWT, GitHub tokens, Bearer, AWS keys, SSH keys, custom patterns