memorymaster 3.25.0__tar.gz → 3.26.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.
- {memorymaster-3.25.0/memorymaster.egg-info → memorymaster-3.26.0}/PKG-INFO +2 -2
- {memorymaster-3.25.0 → memorymaster-3.26.0}/README.md +1 -1
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/__init__.py +1 -1
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/contradiction_probe.py +70 -9
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/query_cache.py +25 -1
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/rule_miner.py +17 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/verbatim_cleanup.py +12 -3
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/verbatim_store.py +80 -56
- {memorymaster-3.25.0 → memorymaster-3.26.0/memorymaster.egg-info}/PKG-INFO +2 -2
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster.egg-info/SOURCES.txt +12 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/pyproject.toml +1 -1
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/bench_longmemeval.py +3 -2
- memorymaster-3.26.0/tests/test_bench_answer_prompt.py +75 -0
- memorymaster-3.26.0/tests/test_cli_handlers_extra.py +308 -0
- memorymaster-3.26.0/tests/test_context_hook_extra.py +643 -0
- memorymaster-3.26.0/tests/test_contradiction_probe.py +313 -0
- memorymaster-3.26.0/tests/test_db_merge_extra.py +237 -0
- memorymaster-3.26.0/tests/test_dream_bridge_extra.py +229 -0
- memorymaster-3.26.0/tests/test_entity_registry_extra.py +271 -0
- memorymaster-3.26.0/tests/test_llm_provider_extra.py +673 -0
- memorymaster-3.26.0/tests/test_observability_extra.py +335 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_query_cache.py +39 -0
- memorymaster-3.26.0/tests/test_retrieval_extra.py +320 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_rule_miner.py +51 -4
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_service_coverage.py +96 -1
- memorymaster-3.26.0/tests/test_storage_extra.py +154 -0
- memorymaster-3.26.0/tests/test_vault_linter_extra.py +290 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_verbatim_cleanup.py +80 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_verbatim_dedup.py +48 -0
- memorymaster-3.26.0/tests/test_wiki_engine_extra.py +410 -0
- memorymaster-3.25.0/tests/test_contradiction_probe.py +0 -153
- {memorymaster-3.25.0 → memorymaster-3.26.0}/LICENSE +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/artifacts/bm25-per-field-eval-harness.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/benchmarks/longmemeval_runner.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/benchmarks/longmemeval_vector_runner.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/benchmarks/perf_smoke.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/__main__.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/_storage_lifecycle.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/_storage_read.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/_storage_schema.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/_storage_shared.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/_storage_sources.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/_storage_write_claims.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/access_control.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/action_exporters.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/action_extractor.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/atlas_claim_extractor.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/atlas_contract.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/auto_extractor.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/auto_resolver.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/candidate_dedupe.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/claim_edges.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/claim_verifier.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/cli.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/cli_handlers_basic.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/cli_handlers_curation.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/cli_helpers.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/closets.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/config.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/config_templates/claude-md-append.md +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/config_templates/codex-agents-md-append.md +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-auto-ingest.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-classify.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-dream-sync.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-precompact.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-recall.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-session-start.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-steward-cycle.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/config_templates/hooks/memorymaster-validate-wiki.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/conflict_resolver.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/connectors/__init__.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/connectors/whatsapp.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/context_hook.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/context_optimizer.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/daily_notes.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/dashboard.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/dashboard_auth.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/db_merge.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/delta_sync.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/dream_bridge.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/embeddings.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/entity_extractor.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/entity_graph.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/entity_registry.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/federated_graphify.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/feedback.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/graph_store.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/hook_log.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/jobs/__init__.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/jobs/calibration.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/jobs/compact_summaries.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/jobs/compactor.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/jobs/daydream_ingest.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/jobs/decay.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/jobs/dedup.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/jobs/deterministic.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/jobs/entity_graph_export.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/jobs/extractor.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/jobs/staleness.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/jobs/validator.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/key_rotator.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/lifecycle.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/llm_budget.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/llm_provider.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/llm_rerank.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/llm_steward.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/mcp_path_policy.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/mcp_server.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/mcp_usage.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/media_processing.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/media_providers.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/metrics_exporter.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/migrations/0001_initial.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/migrations/0002_miner_state.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/migrations/0003_contradiction_verdicts.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/migrations/0004_query_cache.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/migrations/__init__.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/migrations/runner.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/models.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/observability.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/operator.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/operator_queue.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/plugins.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/policy.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/postgres_store.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/qdrant_backend.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/qdrant_recall_fallback.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/qmd_bridge.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/query_classifier.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/query_expansion.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/recall_fusion.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/recall_tokenizer.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/retrieval.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/retry.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/review.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/rl_trainer.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/rules.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/scheduler.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/schema.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/schema.sql +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/schema_postgres.sql +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/scope_utils.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/security.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/service.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/session_tracker.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/setup_hooks.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/skill_evolver.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/snapshot.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/steward.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/steward_classifier.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/steward_features.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/storage.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/store_factory.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/transcript_miner.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/turn_schema.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/vault_bases.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/vault_curator.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/vault_exporter.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/vault_linter.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/vault_log.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/vault_query_capture.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/vault_synthesis.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/verbatim_recall.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/webhook.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/wiki_engine.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/wiki_freshness.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/wiki_similarity.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/wiki_suggest.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster/wiki_validate.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster.egg-info/dependency_links.txt +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster.egg-info/entry_points.txt +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster.egg-info/requires.txt +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/memorymaster.egg-info/top_level.txt +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/agg_recall_latency.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/alert_operator_metrics.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/audit_dedupe_precision.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/autoresearch_daemon.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/backfill_entity_extraction.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/backfill_graph_store.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/backfill_stop_hook_citations.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/backtest_steward_classifier.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/build_steward_training_set.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/check_hook_template_drift.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/claude_to_turns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/codex_live_to_turns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/compaction_edge_cases.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/compaction_trace_report.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/compaction_trace_validate.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/confusion_matrix_eval.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/conversation_importer.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/conversation_to_turns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/e2e_operator.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/email_live_to_turns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/eval_bm25_sweep.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/eval_classify_f1.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/eval_memorymaster.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/eval_recall_precision_at_5.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/eval_recall_quality.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/eval_steward_pareto.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/eval_verbatim_recall.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/expand_recall_eval.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/generate_drill_signoff.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/git_to_turns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/github_live_to_turns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/gitnexus_to_claims.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/grid_recall_weights.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/index_claims_to_qdrant.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/ingest_planning_docs.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/jira_live_to_turns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/label_prompts_with_judge.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/llm_benchmark.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/measure_dedupe_thresholds.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/merge_scope_variants.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/messages_to_turns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/operator_metrics.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/precompute_candidates.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/recurring_incident_drill.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/release_readiness.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/run_codex_autologger.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/run_incident_drill.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/scheduled_ingest.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/setup-hooks.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/slack_live_to_turns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/sync_hook_templates.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/tickets_to_turns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/train_steward_classifier.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/scripts/webhook_to_turns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/setup.cfg +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/conftest.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/integration/test_extract_llm_ollama_live.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_access_control.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_action_exporters.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_action_extractor.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_atlas_claim_extractor.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_atlas_contract.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_atlas_source_schema.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_auto_extractor.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_auto_ingest_hook_citations.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_auto_ingest_hook_schema.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_auto_resolver.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_auto_validate.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_backend_parity.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_bm25_per_field.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_calibration.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_calibration_priors_applied.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_candidate_dedupe.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_claim_edges.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_claim_links.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_claim_type_ranking.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_classify_hook_f1.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_classify_hook_latency.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_claude_to_turns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_cli_dry_run.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_cli_json_flag.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_cli_ready.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_cli_review_queue.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_cli_subcommands.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_closets.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_closets_recall_integration.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_compact_summaries.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_compact_summaries_sensitivity.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_compaction_trace.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_compactor_artifact_order.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_config.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_conflict_resolver.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_confusion_matrix_eval.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_connection_retry.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_connectors.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_context_hook.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_context_optimizer.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_context_optimizer_provider.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_conversation_to_turns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_dashboard.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_dashboard_auth.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_dashboard_coverage.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_dashboard_latency.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_dashboard_lineage.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_dashboard_review_queue.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_daydream_ingest.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_db_merge_confidence_conflict.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_db_merge_coverage_v2.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_decay_coverage.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_decay_respects_pinned.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_dedup.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_dedup_cli.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_dedup_conflict_disambiguation.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_delta_sync.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_deterministic_predicates.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_dream_bridge_coverage_v2.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_dream_bridge_sensitivity.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_embeddings_coverage.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_entity_extractor.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_entity_extractor_llm.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_entity_graph.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_entity_graph_export.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_entity_new_kinds.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_entity_regex_v3.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_entity_registry.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_eval_harness.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_events_schema.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_extract_llm_ollama.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_federated_graphify_mcp.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_federated_query_safety.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_feedback.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_floor_gate.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_fts5_search.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_graph_distance.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_graph_store.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_handler_regressions.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_hook_env_isolation.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_human_id.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_incident_drill_runner.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_integration_workflows.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_key_rotator.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_lifecycle.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_lifecycle_supersede_invariant.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_llm_budget.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_llm_fallback.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_llm_provider_claude_cli.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_llm_provider_key_rotation.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_llm_steward_coverage.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_llm_steward_key_rotation.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_mcp_filter_bypass.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_mcp_helpers.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_mcp_path_policy.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_mcp_rate_limit.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_mcp_server_validation.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_mcp_usage.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_media_processing.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_meta_decisions.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_metrics_exporter.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_migrations.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_observability.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_obsidian_mind_patterns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_operator.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_operator_queue.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_perf_smoke_config.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_plugins.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_policy_coverage.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_policy_mode_env.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_postgres_parity.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_qdrant_backend.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_qmd_bridge.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_qrels_regression.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_query_classifier.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_query_expansion.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_recall_entity_fanout.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_recall_fusion.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_recall_latency.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_recall_precision_at_5.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_recall_tokenizer.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_recall_vector_fallback.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_reliability_hardening.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_resolvers_concurrent_supersede.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_retrieval_profile.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_retrieval_profiles.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_retrieval_rrf_tiebreaker.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_retrieval_weights.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_review.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_rl_trainer.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_rrf_auto_gate.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_rule_claims.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_scheduler.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_schema.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_scope_boost.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_scope_utils.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_security_access.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_security_patterns.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_sensitivity_filter_adversarial.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_sensitivity_filter_adversarial_v2.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_sensitivity_filter_t07.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_session_tracker.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_snapshot.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_snapshot_roundtrip.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_sqlite_core.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_staleness.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_stealth_mode.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_steward.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_steward_classifier.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_steward_contradiction_phase.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_steward_daydream_hook.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_steward_features.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_steward_features_v3.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_steward_resolution_parity.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_storage_parity.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_store_factory.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_tenant_isolation.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_turn_schema.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_two_pass_recall.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_v311_fixes.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_v313_e2e.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_v313_run_cycle_dedupe.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_v390_e2e.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_v391_strict_warnings.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_vault_exporter.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_vault_linter_orphan.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_vector_search.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_verbatim_recall.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_verbatim_store.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_verbatim_store_qdrant.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_webhook.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_webhook_hmac.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_whatsapp_importer.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_wiki_autopromote.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_wiki_binding.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_wiki_engine_idempotency.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_wiki_explored_and_contradictions.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_wiki_freshness.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_wiki_similarity_multiscope.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.0}/tests/test_wiki_suggest.py +0 -0
- {memorymaster-3.25.0 → memorymaster-3.26.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.
|
|
3
|
+
Version: 3.26.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)
|
|
54
54
|
[](https://www.python.org/downloads/)
|
|
55
|
-
[]()
|
|
56
56
|
[]()
|
|
57
57
|
[]()
|
|
58
58
|
[](https://pypi.org/project/memorymaster/)
|
|
@@ -6,7 +6,7 @@ Lifecycle-managed claims with citations, conflict detection, steward governance,
|
|
|
6
6
|
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
[](https://www.python.org/downloads/)
|
|
9
|
-
[]()
|
|
10
10
|
[]()
|
|
11
11
|
[]()
|
|
12
12
|
[](https://pypi.org/project/memorymaster/)
|
|
@@ -77,9 +77,23 @@ def _canonical_pair(a_id: int, b_id: int) -> tuple[int, int]:
|
|
|
77
77
|
return (a_id, b_id) if a_id <= b_id else (b_id, a_id)
|
|
78
78
|
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
_ensured_verdict_dbs: set[str] = set()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _ensure_verdict_table(conn: sqlite3.Connection, *, db_key: str | None = None) -> None:
|
|
84
|
+
"""Create the verdict cache table if absent.
|
|
85
|
+
|
|
86
|
+
``db_key`` lets per-claim callers skip the redundant ``CREATE TABLE IF NOT
|
|
87
|
+
EXISTS`` + ``commit`` on every claim in a steward cycle: once a given DB has
|
|
88
|
+
been ensured in this process we never re-issue the DDL. ``run_probe`` (one
|
|
89
|
+
DDL per whole run) passes no key and always ensures, preserving its semantics.
|
|
90
|
+
"""
|
|
91
|
+
if db_key is not None and db_key in _ensured_verdict_dbs:
|
|
92
|
+
return
|
|
81
93
|
conn.execute(_VERDICT_DDL)
|
|
82
94
|
conn.commit()
|
|
95
|
+
if db_key is not None:
|
|
96
|
+
_ensured_verdict_dbs.add(db_key)
|
|
83
97
|
|
|
84
98
|
|
|
85
99
|
def _cache_get(conn: sqlite3.Connection, a_id: int, b_id: int, model: str) -> dict | None:
|
|
@@ -164,13 +178,23 @@ def sample_candidate_pairs(
|
|
|
164
178
|
return []
|
|
165
179
|
embeddings = [provider.embed(_embed_text(c)) for c in usable]
|
|
166
180
|
pairs: list[tuple[Claim, Claim, float]] = []
|
|
181
|
+
# The cosine sweep is O(n^2). Once we have already collected ``limit`` in-band
|
|
182
|
+
# pairs there is no point paying for the rest of the quadratic scan — the
|
|
183
|
+
# caller only ever consumes the first ``limit`` after the sort below, so break
|
|
184
|
+
# early instead of building (then discarding) thousands of extra pairs.
|
|
185
|
+
done = False
|
|
167
186
|
for i in range(len(usable)):
|
|
187
|
+
if done:
|
|
188
|
+
break
|
|
168
189
|
for j in range(i + 1, len(usable)):
|
|
169
190
|
if _prefiltered(usable[i], usable[j]):
|
|
170
191
|
continue
|
|
171
192
|
sim = cosine_similarity(embeddings[i], embeddings[j])
|
|
172
193
|
if sim_low <= sim < sim_high:
|
|
173
194
|
pairs.append((usable[i], usable[j], round(sim, 4)))
|
|
195
|
+
if limit is not None and len(pairs) >= limit:
|
|
196
|
+
done = True
|
|
197
|
+
break
|
|
174
198
|
pairs.sort(key=lambda p: -p[2])
|
|
175
199
|
if limit is not None:
|
|
176
200
|
pairs = pairs[:limit]
|
|
@@ -193,7 +217,11 @@ def _judge_llm(a: Claim, b: Claim) -> dict | None:
|
|
|
193
217
|
if isinstance(item, dict) and "contradicts" in item:
|
|
194
218
|
return {
|
|
195
219
|
"contradicts": bool(item.get("contradicts")),
|
|
196
|
-
|
|
220
|
+
# Floor empty/off-vocabulary severity to "medium" at the source
|
|
221
|
+
# (the JSON contract is advisory). Defaulting to "low" here would
|
|
222
|
+
# silently bury a real contradiction in the least-actionable tier
|
|
223
|
+
# and made the "medium" default downstream dead code.
|
|
224
|
+
"severity": _coerce_severity(item.get("severity")),
|
|
197
225
|
"reason": (item.get("reason") or "").strip(),
|
|
198
226
|
"cached": False,
|
|
199
227
|
}
|
|
@@ -206,6 +234,36 @@ def _model_key() -> str:
|
|
|
206
234
|
return f"{provider}:{model}"
|
|
207
235
|
|
|
208
236
|
|
|
237
|
+
_VALID_SEVERITIES = {"low", "medium", "high"}
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _coerce_severity(value: Any) -> str:
|
|
241
|
+
"""Normalize a verdict severity to a canonical bucket.
|
|
242
|
+
|
|
243
|
+
The LLM judge and verdict cache can both yield an empty or off-vocabulary
|
|
244
|
+
severity (the JSON contract is advisory, not enforced). When that happens we
|
|
245
|
+
floor to ``"medium"`` rather than ``"low"`` so a contradiction surfaced by
|
|
246
|
+
the probe is never silently downgraded to the least-actionable tier.
|
|
247
|
+
"""
|
|
248
|
+
sev = str(value or "").strip().lower()
|
|
249
|
+
return sev if sev in _VALID_SEVERITIES else "medium"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _safe_judge_reason(reason: Any) -> str | None:
|
|
253
|
+
"""Return the raw judge reason unless it leaks sensitive content.
|
|
254
|
+
|
|
255
|
+
Both ``probe_for_claim`` and ``run_probe(apply=True)`` persist/surface the
|
|
256
|
+
LLM judge reason. Routing both through this guard ensures a reason that the
|
|
257
|
+
sensitivity filter flags is dropped (returns ``None``) on EVERY path — the
|
|
258
|
+
events table must never receive an unredacted secret.
|
|
259
|
+
"""
|
|
260
|
+
text = str(reason or "")
|
|
261
|
+
_, leaks = redact_text(text)
|
|
262
|
+
if leaks:
|
|
263
|
+
return None
|
|
264
|
+
return text
|
|
265
|
+
|
|
266
|
+
|
|
209
267
|
# ---------------------------------------------------------------------------
|
|
210
268
|
# Wilson confidence interval
|
|
211
269
|
# ---------------------------------------------------------------------------
|
|
@@ -303,15 +361,19 @@ def run_probe(
|
|
|
303
361
|
if verdict["contradicts"]:
|
|
304
362
|
stats["contradictions"] += 1
|
|
305
363
|
loser, winner = (a, b) if a.confidence <= b.confidence else (b, a)
|
|
364
|
+
# Same sensitivity guard the per-claim path uses: never let an
|
|
365
|
+
# unredacted judge reason reach the events table on apply.
|
|
366
|
+
safe_reason = _safe_judge_reason(verdict.get("reason"))
|
|
306
367
|
stats["found"].append({
|
|
307
368
|
"claim_a_id": a.id, "claim_b_id": b.id, "similarity": sim,
|
|
308
|
-
"severity": verdict["severity"], "reason": verdict
|
|
369
|
+
"severity": verdict["severity"], "reason": verdict.get("reason", ""),
|
|
309
370
|
"flag_candidate_id": loser.id,
|
|
310
371
|
})
|
|
311
372
|
if apply:
|
|
373
|
+
detail = f" ({safe_reason})" if safe_reason else ""
|
|
312
374
|
transition_claim(
|
|
313
375
|
service.store, loser.id, "conflicted",
|
|
314
|
-
reason=f"contradiction_probe: contradicts claim {winner.id}
|
|
376
|
+
reason=f"contradiction_probe: contradicts claim {winner.id}{detail}",
|
|
315
377
|
event_type="transition",
|
|
316
378
|
)
|
|
317
379
|
stats["flagged_conflicted"] += 1
|
|
@@ -415,7 +477,7 @@ def probe_for_claim(
|
|
|
415
477
|
|
|
416
478
|
conn = sqlite3.connect(str(db_path))
|
|
417
479
|
try:
|
|
418
|
-
_ensure_verdict_table(conn)
|
|
480
|
+
_ensure_verdict_table(conn, db_key=str(db_path))
|
|
419
481
|
model = _model_key()
|
|
420
482
|
for peer, sim in candidates:
|
|
421
483
|
metrics["pairs_checked"] += 1
|
|
@@ -439,14 +501,13 @@ def probe_for_claim(
|
|
|
439
501
|
_cache_put(conn, claim.id, peer.id, model, verdict)
|
|
440
502
|
if not verdict.get("contradicts"):
|
|
441
503
|
continue
|
|
442
|
-
verdict_reason = verdict.get("reason"
|
|
443
|
-
|
|
444
|
-
if leaks:
|
|
504
|
+
verdict_reason = _safe_judge_reason(verdict.get("reason"))
|
|
505
|
+
if verdict_reason is None:
|
|
445
506
|
continue # drop rather than surface a sensitive judge-reason
|
|
446
507
|
metrics["contradictions"] += 1
|
|
447
508
|
reasons.append({
|
|
448
509
|
"code": "contradiction_probe.semantic_pair",
|
|
449
|
-
"severity": verdict.get("severity"
|
|
510
|
+
"severity": _coerce_severity(verdict.get("severity")),
|
|
450
511
|
"detail": f"Semantic contradiction with claim {peer.id}: {verdict_reason}",
|
|
451
512
|
"evidence": {
|
|
452
513
|
"peer_claim_id": int(peer.id),
|
|
@@ -116,7 +116,14 @@ def read(db_path: str, cache_key: str) -> list[dict] | None:
|
|
|
116
116
|
"SELECT result_json, generation FROM query_cache WHERE cache_key = ?",
|
|
117
117
|
(cache_key,),
|
|
118
118
|
).fetchone()
|
|
119
|
-
if row is None
|
|
119
|
+
if row is None:
|
|
120
|
+
return None
|
|
121
|
+
if int(row["generation"]) != gen:
|
|
122
|
+
try:
|
|
123
|
+
conn.execute("DELETE FROM query_cache WHERE cache_key = ?", (cache_key,))
|
|
124
|
+
conn.commit()
|
|
125
|
+
except sqlite3.Error:
|
|
126
|
+
pass
|
|
120
127
|
return None
|
|
121
128
|
return json.loads(row["result_json"])
|
|
122
129
|
except (sqlite3.Error, json.JSONDecodeError, ValueError) as exc:
|
|
@@ -126,6 +133,23 @@ def read(db_path: str, cache_key: str) -> list[dict] | None:
|
|
|
126
133
|
conn.close()
|
|
127
134
|
|
|
128
135
|
|
|
136
|
+
def evict_stale(db_path: str) -> None:
|
|
137
|
+
"""Delete query cache rows older than the current corpus generation."""
|
|
138
|
+
try:
|
|
139
|
+
conn = _connect(db_path)
|
|
140
|
+
except sqlite3.Error as exc:
|
|
141
|
+
logger.warning("query_cache.evict_stale connect failed: %s", exc)
|
|
142
|
+
return
|
|
143
|
+
try:
|
|
144
|
+
gen = current_generation(conn)
|
|
145
|
+
conn.execute("DELETE FROM query_cache WHERE generation < ?", (gen,))
|
|
146
|
+
conn.commit()
|
|
147
|
+
except (sqlite3.Error, ValueError) as exc:
|
|
148
|
+
logger.warning("query_cache.evict_stale failed: %s", exc)
|
|
149
|
+
finally:
|
|
150
|
+
conn.close()
|
|
151
|
+
|
|
152
|
+
|
|
129
153
|
def write(db_path: str, cache_key: str, stub_rows: list[dict], generation: int) -> None:
|
|
130
154
|
"""Store result stubs tagged with ``generation``. Best-effort.
|
|
131
155
|
|
|
@@ -48,6 +48,11 @@ _CORRECTION_KEYWORDS = (
|
|
|
48
48
|
"wrong", "not what", "that's not", "thats not", "revert", "undo",
|
|
49
49
|
"should have", "shouldn't", "why did you", "no need", "stop ",
|
|
50
50
|
)
|
|
51
|
+
_CORRECTION_FTS_MATCH = (
|
|
52
|
+
'"no" OR "don" OR "do" OR "dont" OR "instead" OR "actually" OR '
|
|
53
|
+
'"wrong" OR "not" OR "revert" OR "undo" OR "should" OR "shouldn" OR '
|
|
54
|
+
'"why" OR "stop"'
|
|
55
|
+
)
|
|
51
56
|
|
|
52
57
|
_MINER_STATE_DDL = """
|
|
53
58
|
CREATE TABLE IF NOT EXISTS miner_state (
|
|
@@ -132,6 +137,18 @@ def _candidate_batch(
|
|
|
132
137
|
params: list[Any] = [since_id]
|
|
133
138
|
params.extend(f"%{kw}%" for kw in _CORRECTION_KEYWORDS)
|
|
134
139
|
params.append(batch_size)
|
|
140
|
+
try:
|
|
141
|
+
return conn.execute(
|
|
142
|
+
f"""SELECT v.id, v.session_id, v.content, v.scope FROM verbatim_fts f
|
|
143
|
+
JOIN verbatim_memories v ON v.id = f.rowid
|
|
144
|
+
WHERE verbatim_fts MATCH ?
|
|
145
|
+
AND v.role = 'user' AND v.id > ? AND ({like_clause})
|
|
146
|
+
ORDER BY v.id ASC LIMIT ?""",
|
|
147
|
+
[_CORRECTION_FTS_MATCH, *params],
|
|
148
|
+
).fetchall()
|
|
149
|
+
except sqlite3.Error as exc:
|
|
150
|
+
logger.debug("rule_miner: verbatim_fts pre-filter failed, using LIKE scan: %s", exc)
|
|
151
|
+
|
|
135
152
|
return conn.execute(
|
|
136
153
|
f"""SELECT id, session_id, content, scope FROM verbatim_memories
|
|
137
154
|
WHERE role = 'user' AND id > ? AND ({like_clause})
|
|
@@ -132,10 +132,19 @@ def cleanup(
|
|
|
132
132
|
|
|
133
133
|
if dedup:
|
|
134
134
|
# Identify ids to drop: for each (session_id, content) group, keep
|
|
135
|
-
# the smallest id (oldest), drop the rest.
|
|
135
|
+
# the smallest id (oldest), drop the rest. NOT EXISTS against a
|
|
136
|
+
# correlated "is there an older twin?" probe avoids the O(n^2)
|
|
137
|
+
# full-table anti-join that `id NOT IN (SELECT MIN(id) ...)` forces
|
|
138
|
+
# SQLite into on the cold CLI path. Results are identical: a row is
|
|
139
|
+
# dropped iff another row in the same (session_id, content) group has
|
|
140
|
+
# a strictly smaller id.
|
|
136
141
|
rows = conn.execute(
|
|
137
|
-
"""SELECT id FROM verbatim_memories
|
|
138
|
-
|
|
142
|
+
"""SELECT id FROM verbatim_memories AS v
|
|
143
|
+
WHERE EXISTS (
|
|
144
|
+
SELECT 1 FROM verbatim_memories AS older
|
|
145
|
+
WHERE older.session_id IS v.session_id
|
|
146
|
+
AND older.content = v.content
|
|
147
|
+
AND older.id < v.id
|
|
139
148
|
)"""
|
|
140
149
|
).fetchall()
|
|
141
150
|
ids = [int(r[0]) for r in rows]
|
|
@@ -22,11 +22,11 @@ from datetime import datetime, timezone
|
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
from typing import Any
|
|
24
24
|
|
|
25
|
+
# Credential detection delegated to the canonical filter in memorymaster.security.
|
|
26
|
+
from memorymaster.security import redact_text as _redact_text
|
|
27
|
+
|
|
25
28
|
logger = logging.getLogger(__name__)
|
|
26
29
|
|
|
27
|
-
# Credential detection delegated to the canonical filter in
|
|
28
|
-
# memorymaster.security — single source of truth.
|
|
29
|
-
from memorymaster.security import redact_text as _redact_text
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def _contains_sensitive(text: str) -> bool:
|
|
@@ -76,6 +76,33 @@ def store_verbatim(
|
|
|
76
76
|
timestamp: str | None = None,
|
|
77
77
|
) -> int | None:
|
|
78
78
|
"""Store a verbatim conversation turn. Returns row ID or None if filtered."""
|
|
79
|
+
# closing() guarantees the connection (and its WAL write lock) is released
|
|
80
|
+
# even if an INSERT/commit raises (e.g. "database is locked" under
|
|
81
|
+
# concurrent MCP/Stop-hook writers) - this is the hottest write path.
|
|
82
|
+
with closing(_connect(db_path)) as conn:
|
|
83
|
+
row_id = _store_verbatim_conn(
|
|
84
|
+
conn,
|
|
85
|
+
session_id,
|
|
86
|
+
role,
|
|
87
|
+
content,
|
|
88
|
+
scope,
|
|
89
|
+
source_agent,
|
|
90
|
+
timestamp,
|
|
91
|
+
)
|
|
92
|
+
conn.commit()
|
|
93
|
+
return row_id
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _store_verbatim_conn(
|
|
97
|
+
conn: sqlite3.Connection,
|
|
98
|
+
session_id: str,
|
|
99
|
+
role: str,
|
|
100
|
+
content: str,
|
|
101
|
+
scope: str = "project",
|
|
102
|
+
source_agent: str = "",
|
|
103
|
+
timestamp: str | None = None,
|
|
104
|
+
) -> int | None:
|
|
105
|
+
"""Store one turn using an existing connection without committing."""
|
|
79
106
|
if not content or len(content) < 20:
|
|
80
107
|
return None
|
|
81
108
|
if _row_has_sensitive_field(role or "", source_agent or "", content):
|
|
@@ -83,37 +110,32 @@ def store_verbatim(
|
|
|
83
110
|
|
|
84
111
|
now = timestamp or datetime.now(timezone.utc).isoformat()
|
|
85
112
|
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
(session_id, content),
|
|
99
|
-
).fetchone()
|
|
100
|
-
if existing:
|
|
101
|
-
return None
|
|
102
|
-
|
|
103
|
-
cur = conn.execute(
|
|
104
|
-
"""INSERT INTO verbatim_memories (session_id, role, content, scope, timestamp, source_agent)
|
|
105
|
-
VALUES (?, ?, ?, ?, ?, ?)""",
|
|
106
|
-
(session_id, role, content, scope, now, source_agent),
|
|
107
|
-
)
|
|
108
|
-
row_id = cur.lastrowid
|
|
113
|
+
# Dedup by exact content within the same session. Uses idx_verbatim_session
|
|
114
|
+
# so the lookup is O(rows-in-session), not table-scan. The previous FTS5-based
|
|
115
|
+
# dedup query passed a sha256 hex prefix to MATCH which never resolves -
|
|
116
|
+
# the FTS5 index stores the content text, not its hash. Result: 9M+ rows
|
|
117
|
+
# accumulated for orchestrator sessions because every Stop event re-inserted
|
|
118
|
+
# every message. Fixed 2026-05-03; see mm-0c43.
|
|
119
|
+
existing = conn.execute(
|
|
120
|
+
"SELECT id FROM verbatim_memories WHERE session_id = ? AND content = ? LIMIT 1",
|
|
121
|
+
(session_id, content),
|
|
122
|
+
).fetchone()
|
|
123
|
+
if existing:
|
|
124
|
+
return None
|
|
109
125
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
126
|
+
cur = conn.execute(
|
|
127
|
+
"""INSERT INTO verbatim_memories (session_id, role, content, scope, timestamp, source_agent)
|
|
128
|
+
VALUES (?, ?, ?, ?, ?, ?)""",
|
|
129
|
+
(session_id, role, content, scope, now, source_agent),
|
|
130
|
+
)
|
|
131
|
+
row_id = cur.lastrowid
|
|
132
|
+
|
|
133
|
+
# Update FTS
|
|
134
|
+
conn.execute(
|
|
135
|
+
"INSERT INTO verbatim_fts(rowid, content) VALUES (?, ?)",
|
|
136
|
+
(row_id, content),
|
|
137
|
+
)
|
|
138
|
+
return row_id
|
|
117
139
|
|
|
118
140
|
|
|
119
141
|
def _extract_role_content(entry: dict) -> tuple[str, str]:
|
|
@@ -164,29 +186,31 @@ def store_transcript(
|
|
|
164
186
|
stats = {"stored": 0, "skipped": 0}
|
|
165
187
|
session_id = path.stem # Use filename as session ID
|
|
166
188
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
189
|
+
with closing(_connect(db_path)) as conn:
|
|
190
|
+
for line in path.read_text(encoding="utf-8", errors="replace").splitlines():
|
|
191
|
+
if not line.strip():
|
|
192
|
+
continue
|
|
193
|
+
try:
|
|
194
|
+
entry = json.loads(line)
|
|
195
|
+
except json.JSONDecodeError:
|
|
196
|
+
continue
|
|
197
|
+
if not isinstance(entry, dict):
|
|
198
|
+
continue
|
|
199
|
+
|
|
200
|
+
role, content = _extract_role_content(entry)
|
|
201
|
+
if role not in ("user", "assistant"):
|
|
202
|
+
stats["skipped"] += 1
|
|
203
|
+
continue
|
|
204
|
+
if not content or len(content) < 20:
|
|
205
|
+
stats["skipped"] += 1
|
|
206
|
+
continue
|
|
207
|
+
|
|
208
|
+
row_id = _store_verbatim_conn(conn, session_id, role, content, scope, source_agent)
|
|
209
|
+
if row_id:
|
|
210
|
+
stats["stored"] += 1
|
|
211
|
+
else:
|
|
212
|
+
stats["skipped"] += 1
|
|
213
|
+
conn.commit()
|
|
190
214
|
|
|
191
215
|
return stats
|
|
192
216
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memorymaster
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.26.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)
|
|
54
54
|
[](https://www.python.org/downloads/)
|
|
55
|
-
[]()
|
|
56
56
|
[]()
|
|
57
57
|
[]()
|
|
58
58
|
[](https://pypi.org/project/memorymaster/)
|
|
@@ -218,6 +218,7 @@ tests/test_auto_ingest_hook_schema.py
|
|
|
218
218
|
tests/test_auto_resolver.py
|
|
219
219
|
tests/test_auto_validate.py
|
|
220
220
|
tests/test_backend_parity.py
|
|
221
|
+
tests/test_bench_answer_prompt.py
|
|
221
222
|
tests/test_bm25_per_field.py
|
|
222
223
|
tests/test_calibration.py
|
|
223
224
|
tests/test_calibration_priors_applied.py
|
|
@@ -229,6 +230,7 @@ tests/test_classify_hook_f1.py
|
|
|
229
230
|
tests/test_classify_hook_latency.py
|
|
230
231
|
tests/test_claude_to_turns.py
|
|
231
232
|
tests/test_cli_dry_run.py
|
|
233
|
+
tests/test_cli_handlers_extra.py
|
|
232
234
|
tests/test_cli_json_flag.py
|
|
233
235
|
tests/test_cli_ready.py
|
|
234
236
|
tests/test_cli_review_queue.py
|
|
@@ -245,6 +247,7 @@ tests/test_confusion_matrix_eval.py
|
|
|
245
247
|
tests/test_connection_retry.py
|
|
246
248
|
tests/test_connectors.py
|
|
247
249
|
tests/test_context_hook.py
|
|
250
|
+
tests/test_context_hook_extra.py
|
|
248
251
|
tests/test_context_optimizer.py
|
|
249
252
|
tests/test_context_optimizer_provider.py
|
|
250
253
|
tests/test_contradiction_probe.py
|
|
@@ -258,6 +261,7 @@ tests/test_dashboard_review_queue.py
|
|
|
258
261
|
tests/test_daydream_ingest.py
|
|
259
262
|
tests/test_db_merge_confidence_conflict.py
|
|
260
263
|
tests/test_db_merge_coverage_v2.py
|
|
264
|
+
tests/test_db_merge_extra.py
|
|
261
265
|
tests/test_decay_coverage.py
|
|
262
266
|
tests/test_decay_respects_pinned.py
|
|
263
267
|
tests/test_dedup.py
|
|
@@ -266,6 +270,7 @@ tests/test_dedup_conflict_disambiguation.py
|
|
|
266
270
|
tests/test_delta_sync.py
|
|
267
271
|
tests/test_deterministic_predicates.py
|
|
268
272
|
tests/test_dream_bridge_coverage_v2.py
|
|
273
|
+
tests/test_dream_bridge_extra.py
|
|
269
274
|
tests/test_dream_bridge_sensitivity.py
|
|
270
275
|
tests/test_embeddings_coverage.py
|
|
271
276
|
tests/test_entity_extractor.py
|
|
@@ -275,6 +280,7 @@ tests/test_entity_graph_export.py
|
|
|
275
280
|
tests/test_entity_new_kinds.py
|
|
276
281
|
tests/test_entity_regex_v3.py
|
|
277
282
|
tests/test_entity_registry.py
|
|
283
|
+
tests/test_entity_registry_extra.py
|
|
278
284
|
tests/test_eval_harness.py
|
|
279
285
|
tests/test_events_schema.py
|
|
280
286
|
tests/test_extract_llm_ollama.py
|
|
@@ -296,6 +302,7 @@ tests/test_lifecycle_supersede_invariant.py
|
|
|
296
302
|
tests/test_llm_budget.py
|
|
297
303
|
tests/test_llm_fallback.py
|
|
298
304
|
tests/test_llm_provider_claude_cli.py
|
|
305
|
+
tests/test_llm_provider_extra.py
|
|
299
306
|
tests/test_llm_provider_key_rotation.py
|
|
300
307
|
tests/test_llm_steward_coverage.py
|
|
301
308
|
tests/test_llm_steward_key_rotation.py
|
|
@@ -310,6 +317,7 @@ tests/test_meta_decisions.py
|
|
|
310
317
|
tests/test_metrics_exporter.py
|
|
311
318
|
tests/test_migrations.py
|
|
312
319
|
tests/test_observability.py
|
|
320
|
+
tests/test_observability_extra.py
|
|
313
321
|
tests/test_obsidian_mind_patterns.py
|
|
314
322
|
tests/test_operator.py
|
|
315
323
|
tests/test_operator_queue.py
|
|
@@ -332,6 +340,7 @@ tests/test_recall_tokenizer.py
|
|
|
332
340
|
tests/test_recall_vector_fallback.py
|
|
333
341
|
tests/test_reliability_hardening.py
|
|
334
342
|
tests/test_resolvers_concurrent_supersede.py
|
|
343
|
+
tests/test_retrieval_extra.py
|
|
335
344
|
tests/test_retrieval_profile.py
|
|
336
345
|
tests/test_retrieval_profiles.py
|
|
337
346
|
tests/test_retrieval_rrf_tiebreaker.py
|
|
@@ -364,6 +373,7 @@ tests/test_steward_daydream_hook.py
|
|
|
364
373
|
tests/test_steward_features.py
|
|
365
374
|
tests/test_steward_features_v3.py
|
|
366
375
|
tests/test_steward_resolution_parity.py
|
|
376
|
+
tests/test_storage_extra.py
|
|
367
377
|
tests/test_storage_parity.py
|
|
368
378
|
tests/test_store_factory.py
|
|
369
379
|
tests/test_tenant_isolation.py
|
|
@@ -375,6 +385,7 @@ tests/test_v313_run_cycle_dedupe.py
|
|
|
375
385
|
tests/test_v390_e2e.py
|
|
376
386
|
tests/test_v391_strict_warnings.py
|
|
377
387
|
tests/test_vault_exporter.py
|
|
388
|
+
tests/test_vault_linter_extra.py
|
|
378
389
|
tests/test_vault_linter_orphan.py
|
|
379
390
|
tests/test_vector_search.py
|
|
380
391
|
tests/test_verbatim_cleanup.py
|
|
@@ -387,6 +398,7 @@ tests/test_webhook_hmac.py
|
|
|
387
398
|
tests/test_whatsapp_importer.py
|
|
388
399
|
tests/test_wiki_autopromote.py
|
|
389
400
|
tests/test_wiki_binding.py
|
|
401
|
+
tests/test_wiki_engine_extra.py
|
|
390
402
|
tests/test_wiki_engine_idempotency.py
|
|
391
403
|
tests/test_wiki_explored_and_contradictions.py
|
|
392
404
|
tests/test_wiki_freshness.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "memorymaster"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.26.0"
|
|
8
8
|
description = "Production-grade memory reliability system for AI coding agents. Lifecycle-managed claims with citations, conflict detection, steward governance, and MCP integration."
|
|
9
9
|
license = {text = "MIT"}
|
|
10
10
|
authors = [{name = "wolverin0"}]
|
|
@@ -696,8 +696,9 @@ def call_claude_cli_judge(
|
|
|
696
696
|
def answer_question(question: str, contexts: list[str], judge: JudgeClient) -> str:
|
|
697
697
|
prompt = "\n\n".join(
|
|
698
698
|
[
|
|
699
|
-
"Answer the question using
|
|
700
|
-
"
|
|
699
|
+
"Answer the question using the retrieved conversation context. "
|
|
700
|
+
"Answer with the most specific relevant fact from the retrieved context; "
|
|
701
|
+
"only say I don't know if the answer is genuinely absent.",
|
|
701
702
|
"Keep the answer concise.",
|
|
702
703
|
"Retrieved context:",
|
|
703
704
|
"\n\n---\n\n".join(contexts),
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Guards the LongMemEval answerer prompt against over-refusal regressions.
|
|
2
|
+
|
|
3
|
+
WHY: The bench's `answer_question` prompt drives the QA-accuracy metric. An
|
|
4
|
+
earlier wording ("If the context does not contain the answer, say I don't
|
|
5
|
+
know.") biased the answerer toward refusing whenever the fact was not stated
|
|
6
|
+
verbatim, depressing QA accuracy on questions whose answer WAS present but
|
|
7
|
+
phrased differently. The contract for this prompt is now: extract the most
|
|
8
|
+
specific relevant fact from the retrieved context, and only abstain when the
|
|
9
|
+
answer is genuinely absent. These tests anchor on that requirement (intent),
|
|
10
|
+
not on the exact prose, so they stay meaningful if the wording is polished
|
|
11
|
+
further — what must hold is that the prompt instructs specificity and scopes
|
|
12
|
+
the abstention to genuine absence rather than mere non-verbatim phrasing.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import importlib.util
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
_SPEC = importlib.util.spec_from_file_location(
|
|
22
|
+
"bench_longmemeval",
|
|
23
|
+
Path(__file__).resolve().parent / "bench_longmemeval.py",
|
|
24
|
+
)
|
|
25
|
+
assert _SPEC and _SPEC.loader
|
|
26
|
+
bench = importlib.util.module_from_spec(_SPEC)
|
|
27
|
+
# Register before exec so the module's frozen @dataclass decorators can resolve
|
|
28
|
+
# their own __module__ via sys.modules during class creation.
|
|
29
|
+
sys.modules[_SPEC.name] = bench
|
|
30
|
+
_SPEC.loader.exec_module(bench)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class _PromptCapturingJudge:
|
|
34
|
+
"""Captures the prompt instead of calling any provider."""
|
|
35
|
+
|
|
36
|
+
def __init__(self) -> None:
|
|
37
|
+
self.prompt: str | None = None
|
|
38
|
+
|
|
39
|
+
def complete(self, prompt, *, max_tokens, temperature=0.0):
|
|
40
|
+
self.prompt = prompt
|
|
41
|
+
return bench.LLMResponse(text="captured", model="test", provider="test")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _build_prompt() -> str:
|
|
45
|
+
judge = _PromptCapturingJudge()
|
|
46
|
+
bench.answer_question("What city did the user move to?", ["ctx-a", "ctx-b"], judge)
|
|
47
|
+
assert judge.prompt is not None
|
|
48
|
+
return judge.prompt
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_prompt_requests_specific_fact() -> None:
|
|
52
|
+
# Intent: the answerer must be told to surface the most specific relevant
|
|
53
|
+
# fact, not just "answer the question" generically.
|
|
54
|
+
prompt = _build_prompt().lower()
|
|
55
|
+
assert "most specific" in prompt
|
|
56
|
+
assert "relevant fact" in prompt
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_abstention_is_scoped_to_genuine_absence() -> None:
|
|
60
|
+
# Intent: abstention ("I don't know") must be gated on the answer being
|
|
61
|
+
# genuinely absent — NOT the blanket "if the context does not contain the
|
|
62
|
+
# answer" wording that triggered over-refusal on non-verbatim matches.
|
|
63
|
+
prompt = _build_prompt().lower()
|
|
64
|
+
assert "genuinely absent" in prompt
|
|
65
|
+
assert "if the context does not contain the answer" not in prompt
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_prompt_still_includes_question_and_contexts() -> None:
|
|
69
|
+
# Guard the structural contract so the softening did not drop inputs.
|
|
70
|
+
judge = _PromptCapturingJudge()
|
|
71
|
+
bench.answer_question("Q?", ["alpha-ctx", "beta-ctx"], judge)
|
|
72
|
+
assert judge.prompt is not None
|
|
73
|
+
assert "Q?" in judge.prompt
|
|
74
|
+
assert "alpha-ctx" in judge.prompt
|
|
75
|
+
assert "beta-ctx" in judge.prompt
|