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.
- {memorymaster-3.22.0/memorymaster.egg-info → memorymaster-3.23.0}/PKG-INFO +4 -3
- {memorymaster-3.22.0 → memorymaster-3.23.0}/README.md +3 -2
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/__init__.py +1 -1
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/cli.py +7 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/cli_handlers_basic.py +1 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/cli_handlers_curation.py +37 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/contradiction_probe.py +129 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/steward.py +75 -8
- memorymaster-3.23.0/memorymaster/verbatim_cleanup.py +175 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0/memorymaster.egg-info}/PKG-INFO +4 -3
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster.egg-info/SOURCES.txt +3 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/pyproject.toml +1 -1
- memorymaster-3.23.0/tests/test_steward_contradiction_phase.py +181 -0
- memorymaster-3.23.0/tests/test_verbatim_cleanup.py +152 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/LICENSE +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/artifacts/bm25-per-field-eval-harness.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/benchmarks/longmemeval_runner.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/benchmarks/longmemeval_vector_runner.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/benchmarks/perf_smoke.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/__main__.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/_storage_lifecycle.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/_storage_read.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/_storage_schema.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/_storage_shared.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/_storage_sources.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/_storage_write_claims.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/access_control.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/action_exporters.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/action_extractor.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/atlas_claim_extractor.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/atlas_contract.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/auto_extractor.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/auto_resolver.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/candidate_dedupe.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/claim_edges.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/claim_verifier.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/cli_helpers.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/closets.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/claude-md-append.md +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/codex-agents-md-append.md +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-auto-ingest.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-classify.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-dream-sync.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-precompact.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-recall.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-session-start.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-steward-cycle.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/config_templates/hooks/memorymaster-validate-wiki.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/conflict_resolver.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/connectors/__init__.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/connectors/whatsapp.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/context_hook.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/context_optimizer.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/daily_notes.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/dashboard.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/dashboard_auth.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/db_merge.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/delta_sync.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/dream_bridge.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/embeddings.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/entity_extractor.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/entity_graph.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/entity_registry.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/federated_graphify.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/feedback.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/graph_store.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/hook_log.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/__init__.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/calibration.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/compact_summaries.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/compactor.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/daydream_ingest.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/decay.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/dedup.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/deterministic.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/entity_graph_export.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/extractor.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/staleness.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/jobs/validator.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/key_rotator.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/lifecycle.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/llm_budget.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/llm_provider.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/llm_rerank.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/llm_steward.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/mcp_path_policy.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/mcp_server.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/mcp_usage.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/media_processing.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/media_providers.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/metrics_exporter.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/migrations/0001_initial.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/migrations/0002_miner_state.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/migrations/0003_contradiction_verdicts.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/migrations/0004_query_cache.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/migrations/__init__.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/migrations/runner.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/models.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/observability.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/operator.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/operator_queue.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/plugins.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/policy.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/postgres_store.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/qdrant_backend.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/qdrant_recall_fallback.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/qmd_bridge.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/query_cache.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/query_classifier.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/query_expansion.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/recall_fusion.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/recall_tokenizer.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/retrieval.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/retry.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/review.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/rl_trainer.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/rule_miner.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/rules.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/scheduler.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/schema.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/schema.sql +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/schema_postgres.sql +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/scope_utils.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/security.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/service.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/session_tracker.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/setup_hooks.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/skill_evolver.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/snapshot.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/steward_classifier.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/steward_features.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/storage.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/store_factory.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/transcript_miner.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/turn_schema.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/vault_bases.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/vault_curator.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/vault_exporter.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/vault_linter.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/vault_log.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/vault_query_capture.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/vault_synthesis.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/verbatim_recall.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/verbatim_store.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/webhook.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/wiki_engine.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/wiki_freshness.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/wiki_similarity.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/wiki_suggest.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster/wiki_validate.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster.egg-info/dependency_links.txt +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster.egg-info/entry_points.txt +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster.egg-info/requires.txt +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/memorymaster.egg-info/top_level.txt +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/agg_recall_latency.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/alert_operator_metrics.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/audit_dedupe_precision.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/autoresearch_daemon.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/backfill_entity_extraction.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/backfill_graph_store.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/backfill_stop_hook_citations.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/backtest_steward_classifier.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/build_steward_training_set.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/check_hook_template_drift.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/claude_to_turns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/codex_live_to_turns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/compaction_edge_cases.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/compaction_trace_report.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/compaction_trace_validate.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/confusion_matrix_eval.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/conversation_importer.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/conversation_to_turns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/e2e_operator.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/email_live_to_turns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/eval_bm25_sweep.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/eval_classify_f1.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/eval_memorymaster.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/eval_recall_precision_at_5.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/eval_recall_quality.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/eval_steward_pareto.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/eval_verbatim_recall.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/expand_recall_eval.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/generate_drill_signoff.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/git_to_turns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/github_live_to_turns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/gitnexus_to_claims.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/grid_recall_weights.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/index_claims_to_qdrant.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/ingest_planning_docs.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/jira_live_to_turns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/label_prompts_with_judge.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/llm_benchmark.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/measure_dedupe_thresholds.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/merge_scope_variants.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/messages_to_turns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/operator_metrics.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/precompute_candidates.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/recurring_incident_drill.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/release_readiness.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/run_codex_autologger.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/run_incident_drill.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/scheduled_ingest.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/setup-hooks.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/slack_live_to_turns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/sync_hook_templates.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/tickets_to_turns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/train_steward_classifier.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/scripts/webhook_to_turns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/setup.cfg +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/bench_longmemeval.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/conftest.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/integration/test_extract_llm_ollama_live.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_access_control.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_action_exporters.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_action_extractor.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_atlas_claim_extractor.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_atlas_contract.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_atlas_source_schema.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_auto_extractor.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_auto_ingest_hook_citations.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_auto_ingest_hook_schema.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_auto_resolver.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_auto_validate.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_backend_parity.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_bm25_per_field.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_calibration.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_calibration_priors_applied.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_candidate_dedupe.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_claim_edges.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_claim_links.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_claim_type_ranking.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_classify_hook_f1.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_classify_hook_latency.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_claude_to_turns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_cli_dry_run.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_cli_json_flag.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_cli_ready.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_cli_review_queue.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_cli_subcommands.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_closets.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_closets_recall_integration.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_compact_summaries.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_compact_summaries_sensitivity.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_compaction_trace.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_compactor_artifact_order.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_config.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_conflict_resolver.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_confusion_matrix_eval.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_connection_retry.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_connectors.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_context_hook.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_context_optimizer.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_context_optimizer_provider.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_contradiction_probe.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_conversation_to_turns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dashboard.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dashboard_auth.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dashboard_coverage.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dashboard_latency.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dashboard_lineage.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dashboard_review_queue.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_daydream_ingest.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_db_merge_confidence_conflict.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_db_merge_coverage_v2.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_decay_coverage.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_decay_respects_pinned.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dedup.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dedup_cli.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dedup_conflict_disambiguation.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_delta_sync.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_deterministic_predicates.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dream_bridge_coverage_v2.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_dream_bridge_sensitivity.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_embeddings_coverage.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_entity_extractor.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_entity_extractor_llm.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_entity_graph.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_entity_graph_export.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_entity_new_kinds.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_entity_regex_v3.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_entity_registry.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_eval_harness.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_events_schema.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_extract_llm_ollama.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_federated_graphify_mcp.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_federated_query_safety.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_feedback.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_floor_gate.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_fts5_search.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_graph_distance.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_graph_store.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_handler_regressions.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_hook_env_isolation.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_human_id.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_incident_drill_runner.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_integration_workflows.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_key_rotator.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_lifecycle.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_lifecycle_supersede_invariant.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_llm_budget.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_llm_fallback.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_llm_provider_claude_cli.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_llm_provider_key_rotation.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_llm_steward_coverage.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_llm_steward_key_rotation.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_mcp_filter_bypass.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_mcp_helpers.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_mcp_path_policy.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_mcp_rate_limit.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_mcp_server_validation.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_mcp_usage.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_media_processing.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_meta_decisions.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_metrics_exporter.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_migrations.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_observability.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_obsidian_mind_patterns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_operator.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_operator_queue.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_perf_smoke_config.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_plugins.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_policy_coverage.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_policy_mode_env.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_postgres_parity.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_qdrant_backend.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_qmd_bridge.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_qrels_regression.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_query_cache.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_query_classifier.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_query_expansion.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_recall_entity_fanout.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_recall_fusion.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_recall_latency.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_recall_precision_at_5.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_recall_tokenizer.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_recall_vector_fallback.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_reliability_hardening.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_resolvers_concurrent_supersede.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_retrieval_profile.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_retrieval_profiles.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_retrieval_rrf_tiebreaker.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_retrieval_weights.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_review.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_rl_trainer.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_rrf_auto_gate.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_rule_claims.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_rule_miner.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_scheduler.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_schema.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_scope_boost.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_scope_utils.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_security_access.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_security_patterns.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_sensitivity_filter_adversarial.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_sensitivity_filter_adversarial_v2.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_sensitivity_filter_t07.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_service_coverage.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_session_tracker.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_snapshot.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_snapshot_roundtrip.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_sqlite_core.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_staleness.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_stealth_mode.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_steward.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_steward_classifier.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_steward_daydream_hook.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_steward_features.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_steward_features_v3.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_steward_resolution_parity.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_storage_parity.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_store_factory.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_tenant_isolation.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_turn_schema.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_two_pass_recall.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_v311_fixes.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_v313_e2e.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_v313_run_cycle_dedupe.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_v390_e2e.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_v391_strict_warnings.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_vault_exporter.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_vault_linter_orphan.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_vector_search.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_verbatim_dedup.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_verbatim_recall.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_verbatim_store.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_verbatim_store_qdrant.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_webhook.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_webhook_hmac.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_whatsapp_importer.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_wiki_autopromote.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_wiki_binding.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_wiki_engine_idempotency.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_wiki_explored_and_contradictions.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_wiki_freshness.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_wiki_similarity_multiscope.py +0 -0
- {memorymaster-3.22.0 → memorymaster-3.23.0}/tests/test_wiki_suggest.py +0 -0
- {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.
|
|
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)
|
|
54
54
|
[](https://www.python.org/downloads/)
|
|
55
|
-
[]()
|
|
56
56
|
[]()
|
|
57
57
|
[]()
|
|
58
58
|
[](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)
|
|
8
8
|
[](https://www.python.org/downloads/)
|
|
9
|
-
[]()
|
|
10
10
|
[]()
|
|
11
11
|
[]()
|
|
12
12
|
[](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
|
|
@@ -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
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
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.
|
|
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)
|
|
54
54
|
[](https://www.python.org/downloads/)
|
|
55
|
-
[]()
|
|
56
56
|
[]()
|
|
57
57
|
[]()
|
|
58
58
|
[](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
|