memorymaster 3.26.0__tar.gz → 3.26.1__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.26.0/memorymaster.egg-info → memorymaster-3.26.1}/PKG-INFO +2 -2
- {memorymaster-3.26.0 → memorymaster-3.26.1}/README.md +1 -1
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/__init__.py +1 -1
- {memorymaster-3.26.0 → memorymaster-3.26.1/memorymaster.egg-info}/PKG-INFO +2 -2
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster.egg-info/SOURCES.txt +3 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/pyproject.toml +1 -1
- memorymaster-3.26.1/tests/test_conflict_resolver_extra.py +415 -0
- memorymaster-3.26.1/tests/test_delta_sync_extra.py +392 -0
- memorymaster-3.26.1/tests/test_lifecycle_extra.py +314 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/LICENSE +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/artifacts/bm25-per-field-eval-harness.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/benchmarks/longmemeval_runner.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/benchmarks/longmemeval_vector_runner.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/benchmarks/perf_smoke.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/__main__.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/_storage_lifecycle.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/_storage_read.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/_storage_schema.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/_storage_shared.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/_storage_sources.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/_storage_write_claims.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/access_control.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/action_exporters.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/action_extractor.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/atlas_claim_extractor.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/atlas_contract.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/auto_extractor.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/auto_resolver.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/candidate_dedupe.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/claim_edges.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/claim_verifier.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/cli.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/cli_handlers_basic.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/cli_handlers_curation.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/cli_helpers.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/closets.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/config.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/config_templates/claude-md-append.md +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/config_templates/codex-agents-md-append.md +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/config_templates/hooks/memorymaster-auto-ingest.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/config_templates/hooks/memorymaster-classify.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/config_templates/hooks/memorymaster-dream-sync.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/config_templates/hooks/memorymaster-precompact.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/config_templates/hooks/memorymaster-recall.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/config_templates/hooks/memorymaster-session-start.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/config_templates/hooks/memorymaster-steward-cycle.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/config_templates/hooks/memorymaster-validate-wiki.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/conflict_resolver.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/connectors/__init__.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/connectors/whatsapp.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/context_hook.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/context_optimizer.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/contradiction_probe.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/daily_notes.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/dashboard.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/dashboard_auth.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/db_merge.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/delta_sync.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/dream_bridge.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/embeddings.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/entity_extractor.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/entity_graph.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/entity_registry.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/federated_graphify.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/feedback.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/graph_store.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/hook_log.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/jobs/__init__.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/jobs/calibration.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/jobs/compact_summaries.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/jobs/compactor.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/jobs/daydream_ingest.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/jobs/decay.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/jobs/dedup.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/jobs/deterministic.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/jobs/entity_graph_export.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/jobs/extractor.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/jobs/staleness.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/jobs/validator.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/key_rotator.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/lifecycle.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/llm_budget.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/llm_provider.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/llm_rerank.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/llm_steward.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/mcp_path_policy.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/mcp_server.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/mcp_usage.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/media_processing.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/media_providers.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/metrics_exporter.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/migrations/0001_initial.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/migrations/0002_miner_state.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/migrations/0003_contradiction_verdicts.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/migrations/0004_query_cache.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/migrations/__init__.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/migrations/runner.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/models.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/observability.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/operator.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/operator_queue.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/plugins.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/policy.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/postgres_store.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/qdrant_backend.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/qdrant_recall_fallback.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/qmd_bridge.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/query_cache.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/query_classifier.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/query_expansion.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/recall_fusion.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/recall_tokenizer.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/retrieval.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/retry.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/review.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/rl_trainer.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/rule_miner.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/rules.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/scheduler.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/schema.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/schema.sql +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/schema_postgres.sql +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/scope_utils.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/security.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/service.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/session_tracker.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/setup_hooks.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/skill_evolver.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/snapshot.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/steward.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/steward_classifier.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/steward_features.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/storage.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/store_factory.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/transcript_miner.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/turn_schema.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/vault_bases.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/vault_curator.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/vault_exporter.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/vault_linter.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/vault_log.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/vault_query_capture.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/vault_synthesis.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/verbatim_cleanup.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/verbatim_recall.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/verbatim_store.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/webhook.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/wiki_engine.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/wiki_freshness.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/wiki_similarity.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/wiki_suggest.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster/wiki_validate.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster.egg-info/dependency_links.txt +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster.egg-info/entry_points.txt +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster.egg-info/requires.txt +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/memorymaster.egg-info/top_level.txt +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/agg_recall_latency.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/alert_operator_metrics.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/audit_dedupe_precision.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/autoresearch_daemon.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/backfill_entity_extraction.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/backfill_graph_store.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/backfill_stop_hook_citations.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/backtest_steward_classifier.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/build_steward_training_set.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/check_hook_template_drift.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/claude_to_turns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/codex_live_to_turns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/compaction_edge_cases.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/compaction_trace_report.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/compaction_trace_validate.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/confusion_matrix_eval.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/conversation_importer.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/conversation_to_turns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/e2e_operator.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/email_live_to_turns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/eval_bm25_sweep.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/eval_classify_f1.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/eval_memorymaster.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/eval_recall_precision_at_5.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/eval_recall_quality.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/eval_steward_pareto.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/eval_verbatim_recall.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/expand_recall_eval.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/generate_drill_signoff.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/git_to_turns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/github_live_to_turns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/gitnexus_to_claims.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/grid_recall_weights.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/index_claims_to_qdrant.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/ingest_planning_docs.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/jira_live_to_turns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/label_prompts_with_judge.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/llm_benchmark.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/measure_dedupe_thresholds.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/merge_scope_variants.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/messages_to_turns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/operator_metrics.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/precompute_candidates.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/recurring_incident_drill.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/release_readiness.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/run_codex_autologger.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/run_incident_drill.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/scheduled_ingest.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/setup-hooks.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/slack_live_to_turns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/sync_hook_templates.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/tickets_to_turns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/train_steward_classifier.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/scripts/webhook_to_turns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/setup.cfg +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/bench_longmemeval.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/conftest.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/integration/test_extract_llm_ollama_live.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_access_control.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_action_exporters.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_action_extractor.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_atlas_claim_extractor.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_atlas_contract.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_atlas_source_schema.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_auto_extractor.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_auto_ingest_hook_citations.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_auto_ingest_hook_schema.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_auto_resolver.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_auto_validate.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_backend_parity.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_bench_answer_prompt.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_bm25_per_field.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_calibration.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_calibration_priors_applied.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_candidate_dedupe.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_claim_edges.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_claim_links.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_claim_type_ranking.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_classify_hook_f1.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_classify_hook_latency.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_claude_to_turns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_cli_dry_run.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_cli_handlers_extra.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_cli_json_flag.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_cli_ready.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_cli_review_queue.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_cli_subcommands.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_closets.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_closets_recall_integration.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_compact_summaries.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_compact_summaries_sensitivity.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_compaction_trace.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_compactor_artifact_order.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_config.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_conflict_resolver.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_confusion_matrix_eval.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_connection_retry.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_connectors.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_context_hook.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_context_hook_extra.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_context_optimizer.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_context_optimizer_provider.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_contradiction_probe.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_conversation_to_turns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_dashboard.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_dashboard_auth.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_dashboard_coverage.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_dashboard_latency.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_dashboard_lineage.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_dashboard_review_queue.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_daydream_ingest.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_db_merge_confidence_conflict.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_db_merge_coverage_v2.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_db_merge_extra.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_decay_coverage.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_decay_respects_pinned.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_dedup.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_dedup_cli.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_dedup_conflict_disambiguation.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_delta_sync.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_deterministic_predicates.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_dream_bridge_coverage_v2.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_dream_bridge_extra.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_dream_bridge_sensitivity.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_embeddings_coverage.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_entity_extractor.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_entity_extractor_llm.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_entity_graph.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_entity_graph_export.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_entity_new_kinds.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_entity_regex_v3.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_entity_registry.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_entity_registry_extra.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_eval_harness.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_events_schema.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_extract_llm_ollama.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_federated_graphify_mcp.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_federated_query_safety.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_feedback.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_floor_gate.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_fts5_search.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_graph_distance.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_graph_store.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_handler_regressions.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_hook_env_isolation.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_human_id.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_incident_drill_runner.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_integration_workflows.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_key_rotator.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_lifecycle.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_lifecycle_supersede_invariant.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_llm_budget.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_llm_fallback.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_llm_provider_claude_cli.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_llm_provider_extra.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_llm_provider_key_rotation.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_llm_steward_coverage.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_llm_steward_key_rotation.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_mcp_filter_bypass.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_mcp_helpers.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_mcp_path_policy.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_mcp_rate_limit.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_mcp_server_validation.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_mcp_usage.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_media_processing.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_meta_decisions.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_metrics_exporter.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_migrations.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_observability.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_observability_extra.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_obsidian_mind_patterns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_operator.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_operator_queue.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_perf_smoke_config.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_plugins.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_policy_coverage.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_policy_mode_env.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_postgres_parity.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_qdrant_backend.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_qmd_bridge.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_qrels_regression.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_query_cache.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_query_classifier.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_query_expansion.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_recall_entity_fanout.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_recall_fusion.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_recall_latency.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_recall_precision_at_5.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_recall_tokenizer.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_recall_vector_fallback.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_reliability_hardening.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_resolvers_concurrent_supersede.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_retrieval_extra.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_retrieval_profile.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_retrieval_profiles.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_retrieval_rrf_tiebreaker.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_retrieval_weights.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_review.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_rl_trainer.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_rrf_auto_gate.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_rule_claims.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_rule_miner.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_scheduler.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_schema.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_scope_boost.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_scope_utils.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_security_access.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_security_patterns.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_sensitivity_filter_adversarial.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_sensitivity_filter_adversarial_v2.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_sensitivity_filter_t07.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_service_coverage.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_session_tracker.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_snapshot.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_snapshot_roundtrip.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_sqlite_core.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_staleness.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_stealth_mode.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_steward.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_steward_classifier.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_steward_contradiction_phase.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_steward_daydream_hook.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_steward_features.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_steward_features_v3.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_steward_resolution_parity.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_storage_extra.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_storage_parity.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_store_factory.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_tenant_isolation.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_turn_schema.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_two_pass_recall.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_v311_fixes.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_v313_e2e.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_v313_run_cycle_dedupe.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_v390_e2e.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_v391_strict_warnings.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_vault_exporter.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_vault_linter_extra.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_vault_linter_orphan.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_vector_search.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_verbatim_cleanup.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_verbatim_dedup.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_verbatim_recall.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_verbatim_store.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_verbatim_store_qdrant.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_webhook.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_webhook_hmac.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_whatsapp_importer.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_wiki_autopromote.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_wiki_binding.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_wiki_engine_extra.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_wiki_engine_idempotency.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_wiki_explored_and_contradictions.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_wiki_freshness.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_wiki_similarity_multiscope.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/tests/test_wiki_suggest.py +0 -0
- {memorymaster-3.26.0 → memorymaster-3.26.1}/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.26.
|
|
3
|
+
Version: 3.26.1
|
|
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/)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memorymaster
|
|
3
|
-
Version: 3.26.
|
|
3
|
+
Version: 3.26.1
|
|
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/)
|
|
@@ -243,6 +243,7 @@ tests/test_compaction_trace.py
|
|
|
243
243
|
tests/test_compactor_artifact_order.py
|
|
244
244
|
tests/test_config.py
|
|
245
245
|
tests/test_conflict_resolver.py
|
|
246
|
+
tests/test_conflict_resolver_extra.py
|
|
246
247
|
tests/test_confusion_matrix_eval.py
|
|
247
248
|
tests/test_connection_retry.py
|
|
248
249
|
tests/test_connectors.py
|
|
@@ -268,6 +269,7 @@ tests/test_dedup.py
|
|
|
268
269
|
tests/test_dedup_cli.py
|
|
269
270
|
tests/test_dedup_conflict_disambiguation.py
|
|
270
271
|
tests/test_delta_sync.py
|
|
272
|
+
tests/test_delta_sync_extra.py
|
|
271
273
|
tests/test_deterministic_predicates.py
|
|
272
274
|
tests/test_dream_bridge_coverage_v2.py
|
|
273
275
|
tests/test_dream_bridge_extra.py
|
|
@@ -298,6 +300,7 @@ tests/test_incident_drill_runner.py
|
|
|
298
300
|
tests/test_integration_workflows.py
|
|
299
301
|
tests/test_key_rotator.py
|
|
300
302
|
tests/test_lifecycle.py
|
|
303
|
+
tests/test_lifecycle_extra.py
|
|
301
304
|
tests/test_lifecycle_supersede_invariant.py
|
|
302
305
|
tests/test_llm_budget.py
|
|
303
306
|
tests/test_llm_fallback.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "memorymaster"
|
|
7
|
-
version = "3.26.
|
|
7
|
+
version = "3.26.1"
|
|
8
8
|
description = "Production-grade memory reliability system for AI coding agents. Lifecycle-managed claims with citations, conflict detection, steward governance, and MCP integration."
|
|
9
9
|
license = {text = "MIT"}
|
|
10
10
|
authors = [{name = "wolverin0"}]
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
"""Extra coverage for conflict resolution (deterministic + LLM-driven).
|
|
2
|
+
|
|
3
|
+
Covers three contracts that the rest of the system relies on:
|
|
4
|
+
|
|
5
|
+
1. Winner selection between two conflicting claims (conflict_resolver._pick_winner)
|
|
6
|
+
follows the documented priority chain: pinned > confidence > recency >
|
|
7
|
+
citations > id. The *reason* string is the public contract — these tests
|
|
8
|
+
anchor on the rule that fired, not on which integer id happens to win.
|
|
9
|
+
|
|
10
|
+
2. Supersession wiring is bidirectional: after resolution the winner carries
|
|
11
|
+
supersedes_claim_id pointing at the loser, and the loser carries
|
|
12
|
+
replaced_by_claim_id pointing at the winner AND status == 'superseded'.
|
|
13
|
+
A half-wired pair silently breaks the wiki, so both legs are asserted.
|
|
14
|
+
|
|
15
|
+
3. The LLM-driven path (auto_resolver.resolve_conflict_pair) honours the
|
|
16
|
+
model's verdict for BOTH the chosen-winner branch and the
|
|
17
|
+
no-clear-winner / abstain branch — and never deletes the loser, only
|
|
18
|
+
supersedes it. The LLM call is monkeypatched; no real provider is hit.
|
|
19
|
+
|
|
20
|
+
All DBs are built fresh per-test from the project schema via MemoryService.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
from memorymaster.models import Claim, Citation, CitationInput
|
|
28
|
+
from memorymaster.service import MemoryService
|
|
29
|
+
from memorymaster.lifecycle import transition_claim
|
|
30
|
+
from memorymaster.conflict_resolver import (
|
|
31
|
+
_pick_winner,
|
|
32
|
+
supersede_claim,
|
|
33
|
+
resolve_conflicts,
|
|
34
|
+
SupersessionRaceLost,
|
|
35
|
+
)
|
|
36
|
+
from memorymaster import auto_resolver
|
|
37
|
+
from memorymaster.auto_resolver import resolve_conflict_pair, auto_resolve_conflicts
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# --------------------------------------------------------------------------- #
|
|
41
|
+
# Helpers
|
|
42
|
+
# --------------------------------------------------------------------------- #
|
|
43
|
+
def _make_claim(
|
|
44
|
+
*,
|
|
45
|
+
id: int = 1,
|
|
46
|
+
subject: str | None = "server",
|
|
47
|
+
predicate: str | None = "port",
|
|
48
|
+
object_value: str | None = "8080",
|
|
49
|
+
scope: str = "project",
|
|
50
|
+
status: str = "confirmed",
|
|
51
|
+
confidence: float = 0.7,
|
|
52
|
+
pinned: bool = False,
|
|
53
|
+
updated_at: str = "2026-01-01T00:00:00+00:00",
|
|
54
|
+
citations: list | None = None,
|
|
55
|
+
) -> Claim:
|
|
56
|
+
return Claim(
|
|
57
|
+
id=id,
|
|
58
|
+
text=f"{subject} {predicate} is {object_value}",
|
|
59
|
+
idempotency_key=None,
|
|
60
|
+
normalized_text=None,
|
|
61
|
+
claim_type=None,
|
|
62
|
+
subject=subject,
|
|
63
|
+
predicate=predicate,
|
|
64
|
+
object_value=object_value,
|
|
65
|
+
scope=scope,
|
|
66
|
+
volatility="medium",
|
|
67
|
+
status=status,
|
|
68
|
+
confidence=confidence,
|
|
69
|
+
pinned=pinned,
|
|
70
|
+
supersedes_claim_id=None,
|
|
71
|
+
replaced_by_claim_id=None,
|
|
72
|
+
created_at="2026-01-01T00:00:00+00:00",
|
|
73
|
+
updated_at=updated_at,
|
|
74
|
+
last_validated_at=None,
|
|
75
|
+
archived_at=None,
|
|
76
|
+
citations=citations or [],
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _fresh_service(tmp_path: Path) -> MemoryService:
|
|
81
|
+
svc = MemoryService(str(tmp_path / "test.db"))
|
|
82
|
+
svc.init_db()
|
|
83
|
+
return svc
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _cite() -> Citation:
|
|
87
|
+
return Citation(
|
|
88
|
+
id=1, claim_id=1, source="doc", locator=None,
|
|
89
|
+
excerpt=None, created_at="2026-01-01T00:00:00+00:00",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# --------------------------------------------------------------------------- #
|
|
94
|
+
# 1. Winner selection — anchored on the rule that fired, not the id
|
|
95
|
+
# --------------------------------------------------------------------------- #
|
|
96
|
+
class TestWinnerSelectionRules:
|
|
97
|
+
def test_pinned_overrides_a_higher_confidence_rival(self):
|
|
98
|
+
"""Contract: a pinned claim must win even against a more-confident
|
|
99
|
+
unpinned rival. If this regresses, a manually-protected fact gets
|
|
100
|
+
auto-superseded by noise."""
|
|
101
|
+
pinned_low = _make_claim(id=1, confidence=0.10, pinned=True)
|
|
102
|
+
unpinned_high = _make_claim(id=2, confidence=0.99, pinned=False)
|
|
103
|
+
pair = _pick_winner(pinned_low, unpinned_high)
|
|
104
|
+
assert pair.reason == "pinned_wins"
|
|
105
|
+
assert pair.winner is pinned_low
|
|
106
|
+
assert pair.loser is unpinned_high
|
|
107
|
+
|
|
108
|
+
def test_confidence_decides_when_pin_state_is_equal(self):
|
|
109
|
+
"""Contract: with pin state equal, the higher-confidence claim wins.
|
|
110
|
+
Confidence is the primary evidence signal."""
|
|
111
|
+
weak = _make_claim(id=10, confidence=0.40)
|
|
112
|
+
strong = _make_claim(id=11, confidence=0.80)
|
|
113
|
+
pair = _pick_winner(weak, strong)
|
|
114
|
+
assert pair.reason == "higher_confidence"
|
|
115
|
+
assert pair.winner is strong
|
|
116
|
+
|
|
117
|
+
def test_recency_breaks_a_confidence_tie(self):
|
|
118
|
+
"""Contract: equal confidence -> the freshest updated_at wins. Newer
|
|
119
|
+
information supersedes older information of equal strength."""
|
|
120
|
+
old = _make_claim(id=20, confidence=0.7, updated_at="2026-01-01T00:00:00+00:00")
|
|
121
|
+
new = _make_claim(id=21, confidence=0.7, updated_at="2026-03-01T00:00:00+00:00")
|
|
122
|
+
pair = _pick_winner(old, new)
|
|
123
|
+
assert pair.reason == "more_recent"
|
|
124
|
+
assert pair.winner is new
|
|
125
|
+
|
|
126
|
+
def test_citation_count_breaks_a_confidence_and_recency_tie(self):
|
|
127
|
+
"""Contract: equal confidence and equal timestamp -> more citations
|
|
128
|
+
wins. Better-evidenced claims are preferred."""
|
|
129
|
+
c = _cite()
|
|
130
|
+
few = _make_claim(id=30, confidence=0.7, citations=[c])
|
|
131
|
+
many = _make_claim(id=31, confidence=0.7, citations=[c, c, c])
|
|
132
|
+
pair = _pick_winner(few, many)
|
|
133
|
+
assert pair.reason == "more_citations"
|
|
134
|
+
assert pair.winner is many
|
|
135
|
+
|
|
136
|
+
def test_full_tie_falls_back_to_deterministic_id(self):
|
|
137
|
+
"""Contract: when every signal ties, resolution must still be
|
|
138
|
+
deterministic (higher id = most recently created wins) so repeated
|
|
139
|
+
runs never flip the winner."""
|
|
140
|
+
a = _make_claim(id=40, confidence=0.7)
|
|
141
|
+
b = _make_claim(id=41, confidence=0.7)
|
|
142
|
+
first = _pick_winner(a, b)
|
|
143
|
+
# Argument order must not change the outcome — determinism.
|
|
144
|
+
second = _pick_winner(b, a)
|
|
145
|
+
assert first.reason == "higher_id_tiebreaker"
|
|
146
|
+
assert first.winner.id == 41
|
|
147
|
+
assert second.winner.id == 41
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# --------------------------------------------------------------------------- #
|
|
151
|
+
# 2. Supersession wiring (deterministic resolve_conflicts, real DB)
|
|
152
|
+
# --------------------------------------------------------------------------- #
|
|
153
|
+
class TestSupersessionWiring:
|
|
154
|
+
def _two_conflicting(self, svc: MemoryService):
|
|
155
|
+
loser = svc.ingest(
|
|
156
|
+
text="server port is 8080",
|
|
157
|
+
citations=[CitationInput(source="doc1")],
|
|
158
|
+
subject="server", predicate="port", object_value="8080",
|
|
159
|
+
confidence=0.40,
|
|
160
|
+
)
|
|
161
|
+
winner = svc.ingest(
|
|
162
|
+
text="server port is 3000",
|
|
163
|
+
citations=[CitationInput(source="doc2")],
|
|
164
|
+
subject="server", predicate="port", object_value="3000",
|
|
165
|
+
confidence=0.95,
|
|
166
|
+
)
|
|
167
|
+
return loser, winner
|
|
168
|
+
|
|
169
|
+
def test_both_sides_of_the_link_are_set(self, tmp_path):
|
|
170
|
+
"""Contract: a resolved conflict must wire BOTH legs — loser.replaced_by
|
|
171
|
+
-> winner and winner.supersedes -> loser — and flip the loser to
|
|
172
|
+
'superseded'. A one-legged link corrupts the wiki's supersession graph."""
|
|
173
|
+
svc = _fresh_service(tmp_path)
|
|
174
|
+
loser, winner = self._two_conflicting(svc)
|
|
175
|
+
|
|
176
|
+
result = resolve_conflicts(svc, dry_run=False, statuses=["candidate"])
|
|
177
|
+
assert result.pairs_resolved >= 1
|
|
178
|
+
|
|
179
|
+
loser_fresh = svc.store.get_claim(loser.id)
|
|
180
|
+
winner_fresh = svc.store.get_claim(winner.id)
|
|
181
|
+
|
|
182
|
+
# Loser leg
|
|
183
|
+
assert loser_fresh.status == "superseded"
|
|
184
|
+
assert loser_fresh.replaced_by_claim_id == winner.id
|
|
185
|
+
# Winner leg
|
|
186
|
+
assert winner_fresh.supersedes_claim_id == loser.id
|
|
187
|
+
# Winner is NOT mutated into a terminal state
|
|
188
|
+
assert winner_fresh.status == "candidate"
|
|
189
|
+
|
|
190
|
+
def test_higher_confidence_claim_is_the_survivor(self, tmp_path):
|
|
191
|
+
"""Contract: the survivor of an auto-resolution is the higher-confidence
|
|
192
|
+
claim, never the weaker one. Asserts on which *value* survived, not on
|
|
193
|
+
ids, so it stays valid if id assignment changes."""
|
|
194
|
+
svc = _fresh_service(tmp_path)
|
|
195
|
+
loser, winner = self._two_conflicting(svc)
|
|
196
|
+
|
|
197
|
+
resolve_conflicts(svc, dry_run=False, statuses=["candidate"])
|
|
198
|
+
|
|
199
|
+
loser_fresh = svc.store.get_claim(loser.id)
|
|
200
|
+
winner_fresh = svc.store.get_claim(winner.id)
|
|
201
|
+
assert winner_fresh.object_value == "3000" # the 0.95-confidence value
|
|
202
|
+
assert loser_fresh.object_value == "8080" # the 0.40-confidence value
|
|
203
|
+
assert loser_fresh.status == "superseded"
|
|
204
|
+
assert winner_fresh.status != "superseded"
|
|
205
|
+
|
|
206
|
+
def test_supersede_claim_is_idempotent_against_double_resolve(self, tmp_path):
|
|
207
|
+
"""Contract: re-running resolution must not re-supersede an already
|
|
208
|
+
superseded loser — the second pass resolves nothing. Protects against
|
|
209
|
+
duplicate audit trails / link churn."""
|
|
210
|
+
svc = _fresh_service(tmp_path)
|
|
211
|
+
self._two_conflicting(svc)
|
|
212
|
+
|
|
213
|
+
first = resolve_conflicts(svc, dry_run=False, statuses=["candidate"])
|
|
214
|
+
assert first.pairs_resolved >= 1
|
|
215
|
+
second = resolve_conflicts(
|
|
216
|
+
svc, dry_run=False, statuses=["candidate", "superseded"]
|
|
217
|
+
)
|
|
218
|
+
assert second.pairs_resolved == 0
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class TestSupersedeClaimPrimitive:
|
|
222
|
+
def test_supersede_claim_links_and_transitions_loser(self, tmp_path):
|
|
223
|
+
"""Contract: supersede_claim() is the atomic primitive — it flips the
|
|
224
|
+
old claim to 'superseded' and stamps replaced_by, while the new claim
|
|
225
|
+
gains supersedes_claim_id. Directly exercises the SQLite atomic path."""
|
|
226
|
+
svc = _fresh_service(tmp_path)
|
|
227
|
+
old = svc.ingest(
|
|
228
|
+
text="old fact", citations=[CitationInput(source="d1")],
|
|
229
|
+
subject="x", predicate="y", object_value="a",
|
|
230
|
+
)
|
|
231
|
+
new = svc.ingest(
|
|
232
|
+
text="new fact", citations=[CitationInput(source="d2")],
|
|
233
|
+
subject="x", predicate="y", object_value="b",
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
updated_old = supersede_claim(
|
|
237
|
+
svc.store, old_claim_id=old.id, new_claim_id=new.id,
|
|
238
|
+
reason="test_supersede",
|
|
239
|
+
)
|
|
240
|
+
assert updated_old.status == "superseded"
|
|
241
|
+
assert updated_old.replaced_by_claim_id == new.id
|
|
242
|
+
new_fresh = svc.store.get_claim(new.id)
|
|
243
|
+
assert new_fresh.supersedes_claim_id == old.id
|
|
244
|
+
|
|
245
|
+
def test_supersede_claim_rejects_a_lost_race(self, tmp_path):
|
|
246
|
+
"""Contract: if the old claim was already superseded by someone else,
|
|
247
|
+
supersede_claim must raise SupersessionRaceLost rather than silently
|
|
248
|
+
overwriting the existing replacement pointer."""
|
|
249
|
+
svc = _fresh_service(tmp_path)
|
|
250
|
+
old = svc.ingest(
|
|
251
|
+
text="old", citations=[CitationInput(source="d1")],
|
|
252
|
+
subject="x", predicate="y", object_value="a",
|
|
253
|
+
)
|
|
254
|
+
first = svc.ingest(
|
|
255
|
+
text="first replacement", citations=[CitationInput(source="d2")],
|
|
256
|
+
subject="x", predicate="y", object_value="b",
|
|
257
|
+
)
|
|
258
|
+
second = svc.ingest(
|
|
259
|
+
text="second replacement", citations=[CitationInput(source="d3")],
|
|
260
|
+
subject="x", predicate="y", object_value="c",
|
|
261
|
+
)
|
|
262
|
+
supersede_claim(svc.store, old_claim_id=old.id, new_claim_id=first.id, reason="r1")
|
|
263
|
+
|
|
264
|
+
raised = False
|
|
265
|
+
try:
|
|
266
|
+
supersede_claim(svc.store, old_claim_id=old.id, new_claim_id=second.id, reason="r2")
|
|
267
|
+
except SupersessionRaceLost as exc:
|
|
268
|
+
raised = True
|
|
269
|
+
assert exc.current_replacement_id == first.id
|
|
270
|
+
assert raised, "expected SupersessionRaceLost on a second supersede of the same claim"
|
|
271
|
+
# The original winner pointer must be untouched.
|
|
272
|
+
assert svc.store.get_claim(old.id).replaced_by_claim_id == first.id
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# --------------------------------------------------------------------------- #
|
|
276
|
+
# 3. LLM-resolution path (auto_resolver) — monkeypatched, never real infra
|
|
277
|
+
# --------------------------------------------------------------------------- #
|
|
278
|
+
def _conflicted_pair(svc: MemoryService):
|
|
279
|
+
"""Ingest two contradicting claims and move both into 'conflicted'."""
|
|
280
|
+
a = svc.ingest(
|
|
281
|
+
text="region is us-east", citations=[CitationInput(source="d1")],
|
|
282
|
+
subject="db", predicate="region", object_value="us-east", confidence=0.5,
|
|
283
|
+
)
|
|
284
|
+
b = svc.ingest(
|
|
285
|
+
text="region is eu-west", citations=[CitationInput(source="d2")],
|
|
286
|
+
subject="db", predicate="region", object_value="eu-west", confidence=0.5,
|
|
287
|
+
)
|
|
288
|
+
transition_claim(svc.store, claim_id=a.id, to_status="conflicted",
|
|
289
|
+
reason="seed", event_type="validator")
|
|
290
|
+
transition_claim(svc.store, claim_id=b.id, to_status="conflicted",
|
|
291
|
+
reason="seed", event_type="validator")
|
|
292
|
+
return (
|
|
293
|
+
svc.store.get_claim(a.id, include_citations=True),
|
|
294
|
+
svc.store.get_claim(b.id, include_citations=True),
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class TestLlmResolutionPath:
|
|
299
|
+
def test_llm_picks_a_and_supersedes_b(self, tmp_path, monkeypatch):
|
|
300
|
+
"""Contract: when the model votes 'A', claim A survives and claim B is
|
|
301
|
+
superseded with A as its replacement — the loser is preserved (audit
|
|
302
|
+
trail), never deleted. LLM call is stubbed deterministically."""
|
|
303
|
+
svc = _fresh_service(tmp_path)
|
|
304
|
+
a, b = _conflicted_pair(svc)
|
|
305
|
+
|
|
306
|
+
monkeypatch.setattr(
|
|
307
|
+
auto_resolver, "call_llm",
|
|
308
|
+
lambda prompt, _: '{"winner": "A", "reason": "stronger evidence"}',
|
|
309
|
+
)
|
|
310
|
+
result = resolve_conflict_pair(svc.store, a, b)
|
|
311
|
+
|
|
312
|
+
assert result["resolved"] is True
|
|
313
|
+
assert result["winner_id"] == a.id
|
|
314
|
+
assert result["loser_id"] == b.id
|
|
315
|
+
|
|
316
|
+
loser = svc.store.get_claim(b.id)
|
|
317
|
+
winner = svc.store.get_claim(a.id)
|
|
318
|
+
assert loser.status == "superseded" # preserved, not deleted
|
|
319
|
+
assert loser.replaced_by_claim_id == a.id
|
|
320
|
+
assert winner.status != "superseded"
|
|
321
|
+
|
|
322
|
+
def test_llm_picks_b_supersedes_a(self, tmp_path, monkeypatch):
|
|
323
|
+
"""Contract: the survivor must follow the model's verdict, not argument
|
|
324
|
+
order. A 'B' vote supersedes claim A — the mirror of the 'A' case."""
|
|
325
|
+
svc = _fresh_service(tmp_path)
|
|
326
|
+
a, b = _conflicted_pair(svc)
|
|
327
|
+
|
|
328
|
+
monkeypatch.setattr(
|
|
329
|
+
auto_resolver, "call_llm",
|
|
330
|
+
lambda prompt, _: '{"winner": "B", "reason": "more specific"}',
|
|
331
|
+
)
|
|
332
|
+
result = resolve_conflict_pair(svc.store, a, b)
|
|
333
|
+
|
|
334
|
+
assert result["resolved"] is True
|
|
335
|
+
assert result["winner_id"] == b.id
|
|
336
|
+
assert result["loser_id"] == a.id
|
|
337
|
+
assert svc.store.get_claim(a.id).status == "superseded"
|
|
338
|
+
assert svc.store.get_claim(b.id).status != "superseded"
|
|
339
|
+
|
|
340
|
+
def test_llm_abstains_leaves_both_intact(self, tmp_path, monkeypatch):
|
|
341
|
+
"""Contract: when the model returns no clear winner (verdict outside
|
|
342
|
+
{A,B}), NOTHING is superseded — both claims survive for human review.
|
|
343
|
+
Auto-resolution must never guess when the evaluator abstained."""
|
|
344
|
+
svc = _fresh_service(tmp_path)
|
|
345
|
+
a, b = _conflicted_pair(svc)
|
|
346
|
+
|
|
347
|
+
monkeypatch.setattr(
|
|
348
|
+
auto_resolver, "call_llm",
|
|
349
|
+
lambda prompt, _: '{"winner": "neither", "reason": "ambiguous"}',
|
|
350
|
+
)
|
|
351
|
+
result = resolve_conflict_pair(svc.store, a, b)
|
|
352
|
+
|
|
353
|
+
assert result["resolved"] is False
|
|
354
|
+
assert result["reason"] == "llm_undecided"
|
|
355
|
+
# Neither claim was touched.
|
|
356
|
+
assert svc.store.get_claim(a.id).status == "conflicted"
|
|
357
|
+
assert svc.store.get_claim(b.id).status == "conflicted"
|
|
358
|
+
assert svc.store.get_claim(a.id).replaced_by_claim_id is None
|
|
359
|
+
assert svc.store.get_claim(b.id).replaced_by_claim_id is None
|
|
360
|
+
|
|
361
|
+
def test_invalid_llm_json_is_treated_as_abstain(self, tmp_path, monkeypatch):
|
|
362
|
+
"""Contract: a malformed LLM response degrades to abstain (resolved
|
|
363
|
+
False), not to a crash or an arbitrary supersession. Robustness against
|
|
364
|
+
a flaky model is part of the contract."""
|
|
365
|
+
svc = _fresh_service(tmp_path)
|
|
366
|
+
a, b = _conflicted_pair(svc)
|
|
367
|
+
|
|
368
|
+
monkeypatch.setattr(
|
|
369
|
+
auto_resolver, "call_llm",
|
|
370
|
+
lambda prompt, _: "this is not json at all",
|
|
371
|
+
)
|
|
372
|
+
result = resolve_conflict_pair(svc.store, a, b)
|
|
373
|
+
|
|
374
|
+
assert result["resolved"] is False
|
|
375
|
+
assert svc.store.get_claim(a.id).status == "conflicted"
|
|
376
|
+
assert svc.store.get_claim(b.id).status == "conflicted"
|
|
377
|
+
|
|
378
|
+
def test_auto_resolve_conflicts_groups_and_supersedes_one(self, tmp_path, monkeypatch):
|
|
379
|
+
"""Contract: the batch entrypoint groups conflicted claims by
|
|
380
|
+
(subject, predicate, scope) and resolves within each group, reporting
|
|
381
|
+
an accurate resolved count. End-to-end LLM-driven supersession."""
|
|
382
|
+
svc = _fresh_service(tmp_path)
|
|
383
|
+
a, b = _conflicted_pair(svc)
|
|
384
|
+
|
|
385
|
+
monkeypatch.setattr(
|
|
386
|
+
auto_resolver, "call_llm",
|
|
387
|
+
lambda prompt, _: '{"winner": "A", "reason": "kept A"}',
|
|
388
|
+
)
|
|
389
|
+
counts = auto_resolve_conflicts(svc.store, limit=10)
|
|
390
|
+
|
|
391
|
+
assert counts["pairs_evaluated"] >= 1
|
|
392
|
+
assert counts["resolved"] >= 1
|
|
393
|
+
# Exactly one of the two ends up superseded.
|
|
394
|
+
statuses = {
|
|
395
|
+
svc.store.get_claim(a.id).status,
|
|
396
|
+
svc.store.get_claim(b.id).status,
|
|
397
|
+
}
|
|
398
|
+
assert "superseded" in statuses
|
|
399
|
+
|
|
400
|
+
def test_auto_resolve_no_conflicted_is_a_noop(self, tmp_path, monkeypatch):
|
|
401
|
+
"""Contract: with no conflicted claims, the batch path returns zeroed
|
|
402
|
+
counts WITHOUT invoking the LLM — no wasted provider calls."""
|
|
403
|
+
svc = _fresh_service(tmp_path)
|
|
404
|
+
svc.ingest(
|
|
405
|
+
text="lonely fact", citations=[CitationInput(source="d1")],
|
|
406
|
+
subject="db", predicate="region", object_value="us-east",
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
def _boom(prompt, _): # pragma: no cover - must never run
|
|
410
|
+
raise AssertionError("LLM must not be called when nothing is conflicted")
|
|
411
|
+
|
|
412
|
+
monkeypatch.setattr(auto_resolver, "call_llm", _boom)
|
|
413
|
+
counts = auto_resolve_conflicts(svc.store, limit=10)
|
|
414
|
+
|
|
415
|
+
assert counts == {"pairs_evaluated": 0, "resolved": 0, "failed": 0}
|