memleaf 0.2.68__tar.gz → 0.2.69__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.
- {memleaf-0.2.68 → memleaf-0.2.69}/CHANGELOG.md +8 -0
- {memleaf-0.2.68/src/memleaf.egg-info → memleaf-0.2.69}/PKG-INFO +2 -2
- {memleaf-0.2.68 → memleaf-0.2.69}/README.en.md +1 -1
- {memleaf-0.2.68 → memleaf-0.2.69}/README.md +1 -1
- {memleaf-0.2.68 → memleaf-0.2.69}/pyproject.toml +1 -1
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/__init__.py +1 -1
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/hermes_provider/plugin.yaml +1 -1
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/incremental_protocol.py +86 -16
- {memleaf-0.2.68 → memleaf-0.2.69/src/memleaf.egg-info}/PKG-INFO +2 -2
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_protocol.py +41 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_provider_compatibility.py +1 -1
- {memleaf-0.2.68 → memleaf-0.2.69}/LICENSE +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/MANIFEST.in +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/acceptance-evidence-guide.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/automatic-processing-route.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/body-compaction.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/candidate-reconciliation.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/capture-budget-design.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/config-migrations.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/core-refactor.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/development-closeout.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/evidence-retention.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/explicit-memory-update.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/extraction-latency.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/gate-evidence-boundary.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/general-processing.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/hermes-mcp-runtime.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/hermes-provider-compatibility.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/host-acceptance-runbook.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/immutable-release-assets.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/incremental-commit-contract.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/incremental-execution.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/incremental-native-comparison.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/incremental-partial-recovery.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/incremental-planner-preview.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/incremental-runtime-contract.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/incremental-scope-registration.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/incremental-selected-retention.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/installed-artifact-verification.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/memory-state-contract.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/migration-preflight.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/processing-quality-acceptance.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/public-query-integrity.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/query-performance.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/remember-incremental-route.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/runtime-state-retention.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/semantic-extraction-protocol.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/source-work-contract.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/docs/v0.2.26-processing-status.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/examples/README.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/examples/basic_usage.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/examples/incremental_acceptance.json +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/examples/mcp_stdio.ndjson +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/examples/query_benchmark.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/install.ps1 +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/install.sh +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/setup.cfg +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/__main__.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/acceptance.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/adapters/__init__.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/adapters/antigravity.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/adapters/base.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/adapters/codex.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/adapters/hermes.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/admission.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/batch_review.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/budget.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/capture.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/cli.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/compaction.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/config.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/create_coordinator.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/credentials.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/evidence_budget.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/evidence_policy.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/evidence_structure.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/evidence_syntax.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/explicit_text_source.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/extraction_budget.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/extraction_capability.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/extraction_work_state.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/frontmatter.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/hermes_provider/README.md +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/hermes_provider/__init__.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/hermes_provider/_mcp_client.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/hermes_provider/_provider.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/hermes_provider/_shared.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/hermes_provider/evidence_budget.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/hermes_provider/provider_compatibility.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/hermes_runtime.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/host_events.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/host_runtime.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/inbox.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/incremental_commit.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/incremental_dates.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/incremental_execution.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/incremental_journal.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/incremental_native.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/incremental_partial.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/incremental_preview.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/incremental_prompts.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/incremental_recovery.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/incremental_run_state.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/incremental_runtime.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/incremental_scopes.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/incremental_selection.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/index.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/inspection.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/installer.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/legacy_update_journal.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/llm/__init__.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/llm/base.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/llm/claude_compatible.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/llm/gemini.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/llm/openai_compatible.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/llm/router.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/llm/thinking.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/locking.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/mcp_server.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/memory_commit.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/memory_planner.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/memory_retraction.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/memory_update.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/memory_writer.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/migration.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/model_capabilities.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/model_discovery.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/model_execution.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/models.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/native_index.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/native_registration.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/parallel_model.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/planning_context.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/process_common.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/process_jobs.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/process_journal.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/process_owner.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/processing.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/processing_route.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/prompts.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/provenance.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/provider_compatibility.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/query_clock.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/query_progress.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/query_scan.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/receipt_codec.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/recording_policy.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/redaction.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/remember_route.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/retention.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/retrieval.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/retrieval_gate.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/runtime_retention.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/scope_maintenance.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/scope_state.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/semantic_maintenance.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/semantic_protocol.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/service.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/single_pass_memory_planner.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/single_pass_plan.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/source_policy.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/state_layout.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/subprocess_flags.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/summary_batch.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/target_reconciliation.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/turn_audit.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/turn_plan.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/update_coordinator.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/update_review.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/validation.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf/vault.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf.egg-info/SOURCES.txt +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf.egg-info/dependency_links.txt +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf.egg-info/entry_points.txt +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/src/memleaf.egg-info/top_level.txt +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/acceptance_oracles.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/candidate_update_fixtures.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/incremental_test_support.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/platform_smoke.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/release_assets.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/run_contracts.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_a083_regressions.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_a47_regressions.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_acceptance.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_acceptance_boundaries.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_artifact_verification.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_body_compaction.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_candidate_reconciliation.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_contract_reporter.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_control_absence_contract.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_create_memory_contract.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_development_closeout.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_explicit_memory_update.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_explicit_update_contract.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_g1_memory_contract.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_g2_source_contract.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_hermes_native_provision.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_hermes_source_metadata.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_commit.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_configured_runtime.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_dates.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_execution.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_execution_recovery.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_native.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_partial.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_partial_boundaries.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_partial_recovery.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_preview.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_recovery.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_remember.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_remember_recovery.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_scope_recovery.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_incremental_scopes.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_inspection_read_budget.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_mcp_retrieval_fallback.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_migration_backup.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_migration_runtime.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_native_read_stability.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_planner_provenance_contract.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_platform_contracts.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_processed_state_contract.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_processing_entrypoints.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_processing_route.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_processing_route_recovery.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_public_query_integrity.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_query_benchmark.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_query_clock.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_query_progress.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_query_scan_reuse.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_release_assets.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_remember_route.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_remember_route_recovery.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_revision_target_integrity.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_runtime_retention.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_runtime_retention_observation.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_runtime_retention_recovery.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_source_followup_contract.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/test_vault_binding_contract.py +0 -0
- {memleaf-0.2.68 → memleaf-0.2.69}/tests_public/verify_distribution.py +0 -0
|
@@ -4,6 +4,14 @@ All notable changes to memleaf are documented here.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.2.69 — 2026-09-21
|
|
8
|
+
|
|
9
|
+
### Incremental protocol compatibility hardening
|
|
10
|
+
|
|
11
|
+
- Advance the incremental semantic protocol to `incremental-turn-v5` and expose precise field-shape details for fail-closed rows.
|
|
12
|
+
- Normalize only unambiguous model serialization variants: null placeholders from other branches, a null CREATE wrapper, a single-item `scopes` alias, default `validity`, and flattened CREATE fields. Ambiguous aliases and unknown fields remain rejected.
|
|
13
|
+
- Keep the new normalization behavior covered by the public protocol and provider-compatibility tests.
|
|
14
|
+
|
|
7
15
|
## 0.2.68 — 2026-09-20
|
|
8
16
|
|
|
9
17
|
### Hermes live-path hardening
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memleaf
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.69
|
|
4
4
|
Summary: A local-first Markdown memory core for AI agents
|
|
5
5
|
Author: memleaf contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,7 +23,7 @@ Dynamic: license-file
|
|
|
23
23
|
|
|
24
24
|
[English](README.en.md) · [PyPI](https://pypi.org/project/memleaf/) · [GitHub](https://github.com/miffyblueboo/memleaf)
|
|
25
25
|
|
|
26
|
-
> **版本:0.2.
|
|
26
|
+
> **版本:0.2.69。**
|
|
27
27
|
> 自动提炼和复核共用同一条“未来记忆价值”标准:模型综合未来复用、信息增量、再次读取时的直接可用性和忘记成本,只保留对未来理解、判断或行动有实质影响的最小核心;没有明确价值的信息不提炼,不按具体业务场景硬编码排除。
|
|
28
28
|
> 提炼契约现在只有一份中文语义契约(旧版英文 B3 提示词已删除),首轮、主题选择、候选修复和 `semantic_maintenance` 使用同一套保留价值判断;同一件事只写一次。
|
|
29
29
|
> Core 不再因为归属、日期、任务依据或跨项目而丢弃整条候选:归属缺失会归一到证据里唯一被点名的项目、否则落 `global`;日期只接受能在本候选证据里找到出处、且能锚定成 ISO 的写法(`明天`、`周五`、`月底` 都会被解析),无法解析时只丢掉日期字段,记忆照常写入;未获授权的改归属沿用目标原归属。
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[中文](README.md) · [PyPI](https://pypi.org/project/memleaf/) · [GitHub](https://github.com/miffyblueboo/memleaf)
|
|
6
6
|
|
|
7
|
-
> **Version: 0.2.
|
|
7
|
+
> **Version: 0.2.69.**
|
|
8
8
|
> Automatic extraction and review share one future-value standard: the model weighs likely reuse, information gain, direct usability when read again, and the cost of forgetting. It keeps only the smallest core that can materially help future understanding, decisions, or actions; information without clear value is not extracted, and no business-specific exclusion rule is hard-coded.
|
|
9
9
|
> A single Chinese semantic contract is sent for extraction, topic selection, candidate repair, and `semantic_maintenance`; the older English B3 prompt has been deleted and B3 remains only as a compatibility read format. One topic is written once.
|
|
10
10
|
> Core no longer discards a whole candidate over ownership, dates, task basis or cross-project wording. A missing owner normalizes to the single project named by that candidate's own evidence, otherwise `global`. A deadline is accepted only when it appears in the candidate's own evidence and can be anchored to ISO (`明天`, `周五`, `月底` all resolve); when it cannot, only the date field is dropped and the memory is still written. An unauthorized ownership change keeps the target's existing scope.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[English](README.en.md) · [PyPI](https://pypi.org/project/memleaf/) · [GitHub](https://github.com/miffyblueboo/memleaf)
|
|
6
6
|
|
|
7
|
-
> **版本:0.2.
|
|
7
|
+
> **版本:0.2.69。**
|
|
8
8
|
> 自动提炼和复核共用同一条“未来记忆价值”标准:模型综合未来复用、信息增量、再次读取时的直接可用性和忘记成本,只保留对未来理解、判断或行动有实质影响的最小核心;没有明确价值的信息不提炼,不按具体业务场景硬编码排除。
|
|
9
9
|
> 提炼契约现在只有一份中文语义契约(旧版英文 B3 提示词已删除),首轮、主题选择、候选修复和 `semantic_maintenance` 使用同一套保留价值判断;同一件事只写一次。
|
|
10
10
|
> Core 不再因为归属、日期、任务依据或跨项目而丢弃整条候选:归属缺失会归一到证据里唯一被点名的项目、否则落 `global`;日期只接受能在本候选证据里找到出处、且能锚定成 ISO 的写法(`明天`、`周五`、`月底` 都会被解析),无法解析时只丢掉日期字段,记忆照常写入;未获授权的改归属沿用目标原归属。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Local-first Markdown memory core for AI agents."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.2.
|
|
3
|
+
__version__ = "0.2.69"
|
|
4
4
|
|
|
5
5
|
from .config import DEFAULT_CONFIG, default_config, load_config, save_config
|
|
6
6
|
from .frontmatter import FrontmatterError, dump_frontmatter, dump_yaml, load_yaml, parse_frontmatter
|
|
@@ -22,7 +22,7 @@ from .turn_plan import revision_digest
|
|
|
22
22
|
from .validation import ModelOutputError, parse_strict_json
|
|
23
23
|
|
|
24
24
|
PROTOCOL_VERSION = "incremental-items-v1"
|
|
25
|
-
SEMANTIC_PROTOCOL = "incremental-turn-
|
|
25
|
+
SEMANTIC_PROTOCOL = "incremental-turn-v5"
|
|
26
26
|
MAX_BYTES = 128 * 1024
|
|
27
27
|
MAX_ITEMS = 64
|
|
28
28
|
_TYPES = frozenset(("fact", "todo", "preference", "project", "event", "identity", "other"))
|
|
@@ -38,6 +38,64 @@ _BRANCHES = {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
_CREATE_ROW_META = frozenset(("action", "evidence", "at", "effective"))
|
|
41
|
+
_ROW_FIELDS = frozenset(
|
|
42
|
+
{"action", "evidence"}.union(*(required | optional for required, optional in _BRANCHES.values()))
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class _FieldShapeError(ValueError):
|
|
47
|
+
def __init__(self, detail: str):
|
|
48
|
+
super().__init__("invalid_fields")
|
|
49
|
+
self.detail = detail
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _normalize_create_fields(fields: Any) -> tuple[Any, list[str]]:
|
|
53
|
+
"""Normalize model serialization aliases that preserve one exact meaning."""
|
|
54
|
+
if not isinstance(fields, dict):
|
|
55
|
+
return fields, []
|
|
56
|
+
normalized = deepcopy(fields)
|
|
57
|
+
warnings = []
|
|
58
|
+
if "scope" not in normalized and isinstance(normalized.get("scopes"), list):
|
|
59
|
+
scopes = normalized["scopes"]
|
|
60
|
+
if len(scopes) == 1 and isinstance(scopes[0], str):
|
|
61
|
+
normalized["scope"] = scopes[0]
|
|
62
|
+
normalized.pop("scopes")
|
|
63
|
+
warnings.append("create_single_scope_alias_normalized")
|
|
64
|
+
if normalized.get("validity") == "valid":
|
|
65
|
+
normalized.pop("validity")
|
|
66
|
+
warnings.append("create_default_validity_normalized")
|
|
67
|
+
return normalized, warnings
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _normalize_model_row(row: Any) -> tuple[Any, list[str]]:
|
|
71
|
+
"""Remove only semantically empty branch placeholders and normalize CREATE aliases."""
|
|
72
|
+
if not isinstance(row, dict) or not isinstance(row.get("action"), str):
|
|
73
|
+
return row, []
|
|
74
|
+
action = row["action"].strip().upper()
|
|
75
|
+
if action not in _BRANCHES:
|
|
76
|
+
return row, []
|
|
77
|
+
normalized = deepcopy(row)
|
|
78
|
+
warnings = []
|
|
79
|
+
required, optional = _BRANCHES[action]
|
|
80
|
+
allowed = {"action", "evidence"} | required | optional
|
|
81
|
+
for key in sorted(_ROW_FIELDS - allowed):
|
|
82
|
+
if key in normalized and normalized[key] is None:
|
|
83
|
+
normalized.pop(key)
|
|
84
|
+
warnings.append("null_branch_placeholder_normalized")
|
|
85
|
+
if action == "CREATE":
|
|
86
|
+
if "memory" in normalized and normalized["memory"] is None:
|
|
87
|
+
normalized.pop("memory")
|
|
88
|
+
warnings.append("null_memory_wrapper_normalized")
|
|
89
|
+
if "memory" in normalized:
|
|
90
|
+
normalized["memory"], field_warnings = _normalize_create_fields(normalized["memory"])
|
|
91
|
+
warnings.extend(field_warnings)
|
|
92
|
+
else:
|
|
93
|
+
normalized, field_warnings = _normalize_create_fields(normalized)
|
|
94
|
+
warnings.extend(field_warnings)
|
|
95
|
+
normalized, flattened = _normalize_flat_create_row(normalized)
|
|
96
|
+
if flattened:
|
|
97
|
+
warnings.append("create_memory_wrapper_normalized")
|
|
98
|
+
return normalized, list(dict.fromkeys(warnings))
|
|
41
99
|
|
|
42
100
|
|
|
43
101
|
def _normalize_flat_create_row(row: Any) -> tuple[Any, bool]:
|
|
@@ -55,8 +113,6 @@ def _normalize_flat_create_row(row: Any) -> tuple[Any, bool]:
|
|
|
55
113
|
required = {"type", "scope", "title", "body"}
|
|
56
114
|
if not required <= memory_keys:
|
|
57
115
|
return row, False
|
|
58
|
-
if set(row) - (_CREATE_ROW_META | set(_CREATE)):
|
|
59
|
-
return row, False
|
|
60
116
|
normalized = {key: deepcopy(value) for key, value in row.items() if key not in _CREATE}
|
|
61
117
|
normalized["memory"] = {key: deepcopy(row[key]) for key in row if key in _CREATE}
|
|
62
118
|
return normalized, True
|
|
@@ -75,9 +131,18 @@ def _text(value: Any, maximum: int = 16384, *, empty: bool = False) -> str:
|
|
|
75
131
|
return value
|
|
76
132
|
|
|
77
133
|
|
|
78
|
-
def _keys(value: Any, required: set[str], allowed: set[str] | frozenset[str]) -> None:
|
|
79
|
-
if not isinstance(value, dict)
|
|
80
|
-
raise
|
|
134
|
+
def _keys(value: Any, required: set[str], allowed: set[str] | frozenset[str], *, path: str) -> None:
|
|
135
|
+
if not isinstance(value, dict):
|
|
136
|
+
raise _FieldShapeError(f"{path}:not_object")
|
|
137
|
+
missing = sorted(required - value.keys())
|
|
138
|
+
unexpected = sorted(value.keys() - allowed)
|
|
139
|
+
if missing or unexpected:
|
|
140
|
+
parts = []
|
|
141
|
+
if missing:
|
|
142
|
+
parts.append("missing=" + ",".join(missing))
|
|
143
|
+
if unexpected:
|
|
144
|
+
parts.append("unexpected=" + ",".join(unexpected))
|
|
145
|
+
raise _FieldShapeError(path + ":" + ";".join(parts))
|
|
81
146
|
|
|
82
147
|
|
|
83
148
|
def _ref(value: Any, table: Mapping[str, Any]) -> str:
|
|
@@ -305,7 +370,7 @@ def _selected(value: Any, evidence: Mapping[str, Any], refs: list[str], *, clear
|
|
|
305
370
|
if ref not in refs or evidence[ref]["use"] != "new":
|
|
306
371
|
raise ValueError("invalid_reference")
|
|
307
372
|
return {"clear": True, "anchor": source_basis(evidence[ref])}
|
|
308
|
-
_keys(value, {"ref", "text"}, {"ref", "text"})
|
|
373
|
+
_keys(value, {"ref", "text"}, {"ref", "text"}, path="selected_time")
|
|
309
374
|
ref = _ref(value["ref"], evidence)
|
|
310
375
|
if ref not in refs:
|
|
311
376
|
raise ValueError("invalid_reference")
|
|
@@ -369,7 +434,7 @@ def _parse_row(row: Any, state: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
369
434
|
required_keys = {"action"} | required
|
|
370
435
|
if action != "NO_MEMORY":
|
|
371
436
|
required_keys.add("evidence")
|
|
372
|
-
_keys(row, required_keys, {"action", "evidence"} | optional)
|
|
437
|
+
_keys(row, required_keys, {"action", "evidence"} | optional, path="row")
|
|
373
438
|
evidence = {e["ref"]: e for e in state["evidence"]}
|
|
374
439
|
if action == "NO_MEMORY" and state["request_kind"] == "explicit_remember":
|
|
375
440
|
raise ValueError("explicit_retention_required")
|
|
@@ -412,7 +477,8 @@ def _parse_row(row: Any, state: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
412
477
|
if action in {"CREATE", "UPDATE"}:
|
|
413
478
|
fields = deepcopy(row["memory"] if action == "CREATE" else row["patch"])
|
|
414
479
|
_keys(fields, {"type", "scope", "title", "body"} if action == "CREATE" else set(),
|
|
415
|
-
_CREATE if action == "CREATE" else _PATCH
|
|
480
|
+
_CREATE if action == "CREATE" else _PATCH,
|
|
481
|
+
path="memory" if action == "CREATE" else "patch")
|
|
416
482
|
if not fields:
|
|
417
483
|
raise ValueError("empty_patch")
|
|
418
484
|
if action == "CREATE":
|
|
@@ -564,26 +630,30 @@ def compile_incremental(raw: str, snapshot: PlanningSnapshot) -> dict[str, Any]:
|
|
|
564
630
|
value = parse_strict_json(raw)
|
|
565
631
|
except (ModelOutputError, RecursionError) as error:
|
|
566
632
|
raise ValueError("invalid_json") from error
|
|
567
|
-
_keys(value, {"items"}, {"items"})
|
|
633
|
+
_keys(value, {"items"}, {"items"}, path="root")
|
|
568
634
|
if not isinstance(value["items"], list) or len(value["items"]) > MAX_ITEMS:
|
|
569
635
|
raise ValueError("invalid_items")
|
|
570
636
|
state = snapshot.state()
|
|
571
637
|
known = {e["ref"] for e in state["evidence"]}
|
|
572
638
|
new = {e["ref"] for e in state["evidence"] if e["use"] == "new"}
|
|
573
639
|
parsed, issues, poisoned = [], [], set()
|
|
574
|
-
def problem(index: int | None, code: str, row: Any) -> None:
|
|
640
|
+
def problem(index: int | None, code: str, row: Any, detail: str | None = None) -> None:
|
|
575
641
|
refs = row.get("evidence", []) if isinstance(row, dict) else []
|
|
576
642
|
refs = [r.strip() for r in refs if isinstance(r, str) and r.strip() in known] if isinstance(refs, list) else []
|
|
577
|
-
|
|
643
|
+
issue = {"row": index, "code": code, "evidence": list(dict.fromkeys(refs))}
|
|
644
|
+
if detail:
|
|
645
|
+
issue["detail"] = detail
|
|
646
|
+
issues.append(issue)
|
|
578
647
|
for index, row in enumerate(value["items"]):
|
|
579
648
|
try:
|
|
580
|
-
normalized_row,
|
|
649
|
+
normalized_row, warnings = _normalize_model_row(row)
|
|
581
650
|
parsed_row = _parse_row(normalized_row, state)
|
|
582
|
-
if
|
|
583
|
-
parsed_row.setdefault("warnings", []).
|
|
651
|
+
if warnings:
|
|
652
|
+
parsed_row.setdefault("warnings", []).extend(warnings)
|
|
584
653
|
parsed.append((index, parsed_row))
|
|
585
654
|
except (ValueError, TypeError) as error:
|
|
586
|
-
problem(index, str(error) if isinstance(error, ValueError) else "invalid_fields", row
|
|
655
|
+
problem(index, str(error) if isinstance(error, ValueError) else "invalid_fields", row,
|
|
656
|
+
getattr(error, "detail", None))
|
|
587
657
|
if isinstance(row, dict) and isinstance(row.get("target"), str) and row["target"].strip() in state["targets"]:
|
|
588
658
|
poisoned.add(row["target"].strip())
|
|
589
659
|
groups: dict[str, list[tuple[int, dict[str, Any]]]] = defaultdict(list)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memleaf
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.69
|
|
4
4
|
Summary: A local-first Markdown memory core for AI agents
|
|
5
5
|
Author: memleaf contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,7 +23,7 @@ Dynamic: license-file
|
|
|
23
23
|
|
|
24
24
|
[English](README.en.md) · [PyPI](https://pypi.org/project/memleaf/) · [GitHub](https://github.com/miffyblueboo/memleaf)
|
|
25
25
|
|
|
26
|
-
> **版本:0.2.
|
|
26
|
+
> **版本:0.2.69。**
|
|
27
27
|
> 自动提炼和复核共用同一条“未来记忆价值”标准:模型综合未来复用、信息增量、再次读取时的直接可用性和忘记成本,只保留对未来理解、判断或行动有实质影响的最小核心;没有明确价值的信息不提炼,不按具体业务场景硬编码排除。
|
|
28
28
|
> 提炼契约现在只有一份中文语义契约(旧版英文 B3 提示词已删除),首轮、主题选择、候选修复和 `semantic_maintenance` 使用同一套保留价值判断;同一件事只写一次。
|
|
29
29
|
> Core 不再因为归属、日期、任务依据或跨项目而丢弃整条候选:归属缺失会归一到证据里唯一被点名的项目、否则落 `global`;日期只接受能在本候选证据里找到出处、且能锚定成 ISO 的写法(`明天`、`周五`、`月底` 都会被解析),无法解析时只丢掉日期字段,记忆照常写入;未获授权的改归属沿用目标原归属。
|
|
@@ -141,6 +141,47 @@ class IncrementalProtocolTests(unittest.TestCase):
|
|
|
141
141
|
out = self.run_rows(row)
|
|
142
142
|
self.assertFalse(out["operations"])
|
|
143
143
|
self.assertEqual(out["issues"][0]["code"], "invalid_fields")
|
|
144
|
+
self.assertEqual(out["issues"][0]["detail"], "row:unexpected=unexpected")
|
|
145
|
+
|
|
146
|
+
def test_null_fields_from_other_branches_are_ignored(self):
|
|
147
|
+
row = self.create()
|
|
148
|
+
row.update(target=None, patch=None, reason=None, need=None, reopen=None)
|
|
149
|
+
out = self.run_rows(row)
|
|
150
|
+
self.assertFalse(out["issues"])
|
|
151
|
+
self.assertEqual(len(out["operations"]), 1)
|
|
152
|
+
self.assertIn("null_branch_placeholder_normalized", out["operations"][0]["warnings"])
|
|
153
|
+
|
|
154
|
+
def test_flattened_create_with_null_memory_wrapper_is_normalized(self):
|
|
155
|
+
row = {
|
|
156
|
+
"action": "CREATE", "evidence": ["e1"], "memory": None,
|
|
157
|
+
"type": "fact", "scope": "s1",
|
|
158
|
+
"title": "Known requirement", "body": "Keep this requirement.",
|
|
159
|
+
}
|
|
160
|
+
out = self.run_rows(row)
|
|
161
|
+
self.assertFalse(out["issues"])
|
|
162
|
+
warnings = out["operations"][0]["warnings"]
|
|
163
|
+
self.assertIn("null_memory_wrapper_normalized", warnings)
|
|
164
|
+
self.assertIn("create_memory_wrapper_normalized", warnings)
|
|
165
|
+
|
|
166
|
+
def test_single_scopes_alias_and_default_validity_are_normalized(self):
|
|
167
|
+
row = self.create()
|
|
168
|
+
row["memory"].pop("scope")
|
|
169
|
+
row["memory"].update(scopes=["s1"], validity="valid")
|
|
170
|
+
out = self.run_rows(row)
|
|
171
|
+
self.assertFalse(out["issues"])
|
|
172
|
+
op = out["operations"][0]
|
|
173
|
+
self.assertEqual(op["memory"]["scopes"], ["project:Atlas"])
|
|
174
|
+
self.assertIn("create_single_scope_alias_normalized", op["warnings"])
|
|
175
|
+
self.assertIn("create_default_validity_normalized", op["warnings"])
|
|
176
|
+
|
|
177
|
+
def test_ambiguous_scopes_alias_stays_rejected_with_detail(self):
|
|
178
|
+
row = self.create()
|
|
179
|
+
row["memory"].pop("scope")
|
|
180
|
+
row["memory"]["scopes"] = ["s1", "s2"]
|
|
181
|
+
out = self.run_rows(row)
|
|
182
|
+
self.assertFalse(out["operations"])
|
|
183
|
+
self.assertEqual(out["issues"][0]["code"], "invalid_fields")
|
|
184
|
+
self.assertEqual(out["issues"][0]["detail"], "memory:missing=scope;unexpected=scopes")
|
|
144
185
|
|
|
145
186
|
def test_flattened_create_missing_required_field_stays_rejected(self):
|
|
146
187
|
row = {
|
|
@@ -283,7 +283,7 @@ class ServerCompatibilityTests(unittest.TestCase):
|
|
|
283
283
|
with patch.object(self.service,'stats',side_effect=AssertionError('must not scan')):
|
|
284
284
|
result=self.hello(self.good)['result']
|
|
285
285
|
self.assertEqual(result['_meta'][BUILD_META],self.good)
|
|
286
|
-
self.assertEqual(result['serverInfo']['version'],'0.2.
|
|
286
|
+
self.assertEqual(result['serverInfo']['version'],'0.2.69')
|
|
287
287
|
|
|
288
288
|
def test_compatible_bridge_dispatches_capture(self):
|
|
289
289
|
self.hello(self.good)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|