memleaf 0.2.66__tar.gz → 0.2.67__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. {memleaf-0.2.66 → memleaf-0.2.67}/CHANGELOG.md +13 -0
  2. {memleaf-0.2.66/src/memleaf.egg-info → memleaf-0.2.67}/PKG-INFO +1 -1
  3. {memleaf-0.2.66 → memleaf-0.2.67}/docs/acceptance-evidence-guide.md +11 -10
  4. {memleaf-0.2.66 → memleaf-0.2.67}/docs/automatic-processing-route.md +26 -26
  5. {memleaf-0.2.66 → memleaf-0.2.67}/docs/migration-preflight.md +6 -5
  6. {memleaf-0.2.66 → memleaf-0.2.67}/docs/remember-incremental-route.md +19 -19
  7. {memleaf-0.2.66 → memleaf-0.2.67}/pyproject.toml +1 -1
  8. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/__init__.py +1 -1
  9. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/cli.py +2 -2
  10. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/config.py +8 -6
  11. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/hermes_provider/plugin.yaml +1 -1
  12. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/installer.py +71 -6
  13. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/mcp_server.py +2 -2
  14. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/migration.py +12 -5
  15. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/process_jobs.py +18 -12
  16. memleaf-0.2.67/src/memleaf/processing.py +22 -0
  17. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/processing_route.py +31 -12
  18. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/service.py +17 -46
  19. {memleaf-0.2.66 → memleaf-0.2.67/src/memleaf.egg-info}/PKG-INFO +1 -1
  20. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_a083_regressions.py +73 -73
  21. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_a47_regressions.py +14 -8
  22. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_artifact_verification.py +35 -6
  23. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_control_absence_contract.py +12 -6
  24. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_execution.py +7 -3
  25. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_migration_backup.py +18 -1
  26. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_processed_state_contract.py +15 -9
  27. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_processing_entrypoints.py +23 -19
  28. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_processing_route.py +25 -17
  29. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_provider_compatibility.py +53 -2
  30. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_remember_route.py +24 -19
  31. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_source_followup_contract.py +60 -42
  32. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/verify_distribution.py +70 -0
  33. memleaf-0.2.66/src/memleaf/processing.py +0 -535
  34. {memleaf-0.2.66 → memleaf-0.2.67}/LICENSE +0 -0
  35. {memleaf-0.2.66 → memleaf-0.2.67}/MANIFEST.in +0 -0
  36. {memleaf-0.2.66 → memleaf-0.2.67}/README.en.md +0 -0
  37. {memleaf-0.2.66 → memleaf-0.2.67}/README.md +0 -0
  38. {memleaf-0.2.66 → memleaf-0.2.67}/docs/body-compaction.md +0 -0
  39. {memleaf-0.2.66 → memleaf-0.2.67}/docs/candidate-reconciliation.md +0 -0
  40. {memleaf-0.2.66 → memleaf-0.2.67}/docs/capture-budget-design.md +0 -0
  41. {memleaf-0.2.66 → memleaf-0.2.67}/docs/config-migrations.md +0 -0
  42. {memleaf-0.2.66 → memleaf-0.2.67}/docs/core-refactor.md +0 -0
  43. {memleaf-0.2.66 → memleaf-0.2.67}/docs/development-closeout.md +0 -0
  44. {memleaf-0.2.66 → memleaf-0.2.67}/docs/evidence-retention.md +0 -0
  45. {memleaf-0.2.66 → memleaf-0.2.67}/docs/explicit-memory-update.md +0 -0
  46. {memleaf-0.2.66 → memleaf-0.2.67}/docs/extraction-latency.md +0 -0
  47. {memleaf-0.2.66 → memleaf-0.2.67}/docs/gate-evidence-boundary.md +0 -0
  48. {memleaf-0.2.66 → memleaf-0.2.67}/docs/general-processing.md +0 -0
  49. {memleaf-0.2.66 → memleaf-0.2.67}/docs/hermes-mcp-runtime.md +0 -0
  50. {memleaf-0.2.66 → memleaf-0.2.67}/docs/hermes-provider-compatibility.md +0 -0
  51. {memleaf-0.2.66 → memleaf-0.2.67}/docs/host-acceptance-runbook.md +0 -0
  52. {memleaf-0.2.66 → memleaf-0.2.67}/docs/immutable-release-assets.md +0 -0
  53. {memleaf-0.2.66 → memleaf-0.2.67}/docs/incremental-commit-contract.md +0 -0
  54. {memleaf-0.2.66 → memleaf-0.2.67}/docs/incremental-execution.md +0 -0
  55. {memleaf-0.2.66 → memleaf-0.2.67}/docs/incremental-native-comparison.md +0 -0
  56. {memleaf-0.2.66 → memleaf-0.2.67}/docs/incremental-partial-recovery.md +0 -0
  57. {memleaf-0.2.66 → memleaf-0.2.67}/docs/incremental-planner-preview.md +0 -0
  58. {memleaf-0.2.66 → memleaf-0.2.67}/docs/incremental-runtime-contract.md +0 -0
  59. {memleaf-0.2.66 → memleaf-0.2.67}/docs/incremental-scope-registration.md +0 -0
  60. {memleaf-0.2.66 → memleaf-0.2.67}/docs/incremental-selected-retention.md +0 -0
  61. {memleaf-0.2.66 → memleaf-0.2.67}/docs/installed-artifact-verification.md +0 -0
  62. {memleaf-0.2.66 → memleaf-0.2.67}/docs/memory-state-contract.md +0 -0
  63. {memleaf-0.2.66 → memleaf-0.2.67}/docs/processing-quality-acceptance.md +0 -0
  64. {memleaf-0.2.66 → memleaf-0.2.67}/docs/public-query-integrity.md +0 -0
  65. {memleaf-0.2.66 → memleaf-0.2.67}/docs/query-performance.md +0 -0
  66. {memleaf-0.2.66 → memleaf-0.2.67}/docs/runtime-state-retention.md +0 -0
  67. {memleaf-0.2.66 → memleaf-0.2.67}/docs/semantic-extraction-protocol.md +0 -0
  68. {memleaf-0.2.66 → memleaf-0.2.67}/docs/source-work-contract.md +0 -0
  69. {memleaf-0.2.66 → memleaf-0.2.67}/docs/v0.2.26-processing-status.md +0 -0
  70. {memleaf-0.2.66 → memleaf-0.2.67}/examples/README.md +0 -0
  71. {memleaf-0.2.66 → memleaf-0.2.67}/examples/basic_usage.py +0 -0
  72. {memleaf-0.2.66 → memleaf-0.2.67}/examples/incremental_acceptance.json +0 -0
  73. {memleaf-0.2.66 → memleaf-0.2.67}/examples/mcp_stdio.ndjson +0 -0
  74. {memleaf-0.2.66 → memleaf-0.2.67}/examples/query_benchmark.py +0 -0
  75. {memleaf-0.2.66 → memleaf-0.2.67}/install.ps1 +0 -0
  76. {memleaf-0.2.66 → memleaf-0.2.67}/install.sh +0 -0
  77. {memleaf-0.2.66 → memleaf-0.2.67}/setup.cfg +0 -0
  78. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/__main__.py +0 -0
  79. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/acceptance.py +0 -0
  80. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/adapters/__init__.py +0 -0
  81. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/adapters/antigravity.py +0 -0
  82. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/adapters/base.py +0 -0
  83. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/adapters/codex.py +0 -0
  84. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/adapters/hermes.py +0 -0
  85. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/admission.py +0 -0
  86. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/batch_review.py +0 -0
  87. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/budget.py +0 -0
  88. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/capture.py +0 -0
  89. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/compaction.py +0 -0
  90. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/create_coordinator.py +0 -0
  91. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/credentials.py +0 -0
  92. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/evidence_budget.py +0 -0
  93. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/evidence_policy.py +0 -0
  94. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/evidence_structure.py +0 -0
  95. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/evidence_syntax.py +0 -0
  96. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/explicit_text_source.py +0 -0
  97. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/extraction_budget.py +0 -0
  98. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/extraction_capability.py +0 -0
  99. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/extraction_work_state.py +0 -0
  100. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/frontmatter.py +0 -0
  101. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/hermes_provider/README.md +0 -0
  102. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/hermes_provider/__init__.py +0 -0
  103. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/hermes_provider/_mcp_client.py +0 -0
  104. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/hermes_provider/_provider.py +0 -0
  105. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/hermes_provider/_shared.py +0 -0
  106. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/hermes_provider/evidence_budget.py +0 -0
  107. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/hermes_provider/provider_compatibility.py +0 -0
  108. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/hermes_runtime.py +0 -0
  109. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/host_events.py +0 -0
  110. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/host_runtime.py +0 -0
  111. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/inbox.py +0 -0
  112. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/incremental_commit.py +0 -0
  113. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/incremental_dates.py +0 -0
  114. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/incremental_execution.py +0 -0
  115. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/incremental_journal.py +0 -0
  116. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/incremental_native.py +0 -0
  117. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/incremental_partial.py +0 -0
  118. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/incremental_preview.py +0 -0
  119. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/incremental_prompts.py +0 -0
  120. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/incremental_protocol.py +0 -0
  121. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/incremental_recovery.py +0 -0
  122. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/incremental_run_state.py +0 -0
  123. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/incremental_runtime.py +0 -0
  124. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/incremental_scopes.py +0 -0
  125. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/incremental_selection.py +0 -0
  126. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/index.py +0 -0
  127. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/inspection.py +0 -0
  128. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/legacy_update_journal.py +0 -0
  129. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/llm/__init__.py +0 -0
  130. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/llm/base.py +0 -0
  131. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/llm/claude_compatible.py +0 -0
  132. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/llm/gemini.py +0 -0
  133. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/llm/openai_compatible.py +0 -0
  134. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/llm/router.py +0 -0
  135. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/llm/thinking.py +0 -0
  136. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/locking.py +0 -0
  137. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/memory_commit.py +0 -0
  138. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/memory_planner.py +0 -0
  139. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/memory_retraction.py +0 -0
  140. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/memory_update.py +0 -0
  141. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/memory_writer.py +0 -0
  142. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/model_capabilities.py +0 -0
  143. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/model_discovery.py +0 -0
  144. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/model_execution.py +0 -0
  145. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/models.py +0 -0
  146. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/native_index.py +0 -0
  147. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/native_registration.py +0 -0
  148. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/parallel_model.py +0 -0
  149. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/planning_context.py +0 -0
  150. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/process_common.py +0 -0
  151. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/process_journal.py +0 -0
  152. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/process_owner.py +0 -0
  153. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/prompts.py +0 -0
  154. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/provenance.py +0 -0
  155. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/provider_compatibility.py +0 -0
  156. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/query_clock.py +0 -0
  157. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/query_progress.py +0 -0
  158. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/query_scan.py +0 -0
  159. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/receipt_codec.py +0 -0
  160. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/recording_policy.py +0 -0
  161. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/redaction.py +0 -0
  162. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/remember_route.py +0 -0
  163. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/retention.py +0 -0
  164. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/retrieval.py +0 -0
  165. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/retrieval_gate.py +0 -0
  166. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/runtime_retention.py +0 -0
  167. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/scope_maintenance.py +0 -0
  168. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/scope_state.py +0 -0
  169. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/semantic_maintenance.py +0 -0
  170. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/semantic_protocol.py +0 -0
  171. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/single_pass_memory_planner.py +0 -0
  172. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/single_pass_plan.py +0 -0
  173. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/source_policy.py +0 -0
  174. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/state_layout.py +0 -0
  175. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/subprocess_flags.py +0 -0
  176. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/summary_batch.py +0 -0
  177. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/target_reconciliation.py +0 -0
  178. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/turn_audit.py +0 -0
  179. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/turn_plan.py +0 -0
  180. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/update_coordinator.py +0 -0
  181. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/update_review.py +0 -0
  182. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/validation.py +0 -0
  183. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf/vault.py +0 -0
  184. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf.egg-info/SOURCES.txt +0 -0
  185. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf.egg-info/dependency_links.txt +0 -0
  186. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf.egg-info/entry_points.txt +0 -0
  187. {memleaf-0.2.66 → memleaf-0.2.67}/src/memleaf.egg-info/top_level.txt +0 -0
  188. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/acceptance_oracles.py +0 -0
  189. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/candidate_update_fixtures.py +0 -0
  190. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/incremental_test_support.py +0 -0
  191. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/platform_smoke.py +0 -0
  192. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/release_assets.py +0 -0
  193. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/run_contracts.py +0 -0
  194. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_acceptance.py +0 -0
  195. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_acceptance_boundaries.py +0 -0
  196. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_body_compaction.py +0 -0
  197. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_candidate_reconciliation.py +0 -0
  198. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_contract_reporter.py +0 -0
  199. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_create_memory_contract.py +0 -0
  200. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_development_closeout.py +0 -0
  201. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_explicit_memory_update.py +0 -0
  202. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_explicit_update_contract.py +0 -0
  203. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_g1_memory_contract.py +0 -0
  204. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_g2_source_contract.py +0 -0
  205. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_hermes_native_provision.py +0 -0
  206. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_hermes_source_metadata.py +0 -0
  207. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_commit.py +0 -0
  208. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_configured_runtime.py +0 -0
  209. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_dates.py +0 -0
  210. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_execution_recovery.py +0 -0
  211. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_native.py +0 -0
  212. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_partial.py +0 -0
  213. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_partial_boundaries.py +0 -0
  214. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_partial_recovery.py +0 -0
  215. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_preview.py +0 -0
  216. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_protocol.py +0 -0
  217. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_recovery.py +0 -0
  218. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_remember.py +0 -0
  219. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_remember_recovery.py +0 -0
  220. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_scope_recovery.py +0 -0
  221. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_incremental_scopes.py +0 -0
  222. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_inspection_read_budget.py +0 -0
  223. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_mcp_retrieval_fallback.py +0 -0
  224. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_migration_runtime.py +0 -0
  225. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_native_read_stability.py +0 -0
  226. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_planner_provenance_contract.py +0 -0
  227. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_platform_contracts.py +0 -0
  228. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_processing_route_recovery.py +0 -0
  229. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_public_query_integrity.py +0 -0
  230. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_query_benchmark.py +0 -0
  231. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_query_clock.py +0 -0
  232. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_query_progress.py +0 -0
  233. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_query_scan_reuse.py +0 -0
  234. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_release_assets.py +0 -0
  235. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_remember_route_recovery.py +0 -0
  236. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_revision_target_integrity.py +0 -0
  237. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_runtime_retention.py +0 -0
  238. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_runtime_retention_observation.py +0 -0
  239. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_runtime_retention_recovery.py +0 -0
  240. {memleaf-0.2.66 → memleaf-0.2.67}/tests_public/test_vault_binding_contract.py +0 -0
@@ -4,6 +4,19 @@ All notable changes to memleaf are documented here.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.2.67 — 2026-09-20
8
+
9
+ ### Incremental-only processing and release integrity
10
+
11
+ - Remove the executable legacy processing/remember engine. New Vaults use the
12
+ incremental runner for automatic processing, explicit remember, MCP/CLI and
13
+ detached jobs; retained legacy settings/jobs remain inspectable but fail closed.
14
+ - Fix the v0.2.66 Hermes Provider manifest drift by aligning Core and Provider at
15
+ 0.2.67 and rejecting an internally inconsistent package before host/Vault changes.
16
+ - Gate source and wheel/sdist verification on one version identity across package
17
+ metadata, Core `__version__`, Hermes `plugin.yaml` and sdist project metadata.
18
+
19
+
7
20
  ## 0.2.66 — 2026-09-20
8
21
 
9
22
  ### Reconcile divergent local candidates
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memleaf
3
- Version: 0.2.66
3
+ Version: 0.2.67
4
4
  Summary: A local-first Markdown memory core for AI agents
5
5
  Author: memleaf contributors
6
6
  License-Expression: MIT
@@ -24,16 +24,17 @@ the suite count or from module-name references.
24
24
 
25
25
  ## Current route semantics
26
26
 
27
- Both `process.automatic_pipeline` and `process.remember_pipeline` default to
28
- `legacy`. Selecting one does not switch the other. A successfully installed
29
- candidate is not proof that either new route is active. Use the exact installed
30
- configuration and returned route/state when recording acceptance; no instructions
31
- here modify production defaults or grant a production switch.
32
-
33
- After isolation and authorization, the supported opt-in values are `incremental`
34
- for both keys. They are test configuration choices, not edits to package defaults.
35
- Existing queued work retains its route/authority contract. Do not erase old work,
36
- failed receipts or request counters to get a clean-looking preflight result.
27
+ The v0.2.67 candidate has one processing engine: incremental. New Vaults default
28
+ both `process.automatic_pipeline` and `process.remember_pipeline` to
29
+ `incremental`. A retained `legacy` value from an older Vault is not executable;
30
+ migration preflight reports it as `legacy_pipeline_configuration`, and runtime
31
+ entry points reject it with `legacy_pipeline_removed`.
32
+
33
+ A successfully installed candidate is still not proof that production migration is
34
+ safe. Existing legacy queue/control records retain their identity for diagnosis and
35
+ must be settled or explicitly reviewed; they are not reinterpreted as incremental
36
+ work. Do not erase old work, failed receipts or request counters to obtain a clean
37
+ preflight result.
37
38
 
38
39
  ## Preparation with no paid calls
39
40
 
@@ -1,31 +1,29 @@
1
- # Automatic processing route integration (G4a, opt-in)
1
+ # Automatic processing on the incremental engine
2
2
 
3
- This increment routes the **existing** `Memleaf.process()`, CLI `process`, MCP
4
- `process` and detached job worker to the already implemented incremental runner.
5
- It does not add another planner, model review, job queue or writer. Existing
6
- `process_incremental` and selected `remember_incremental` remain single-turn
7
- facades on that same runner, budget and commit journal.
3
+ As of the v0.2.67 candidate, `Memleaf.process()`, CLI `process`, MCP `process`
4
+ and the detached job worker all execute through the existing incremental runner.
5
+ There is no executable legacy processing engine and no runtime fallback to it.
6
+ `process_incremental` and selected `remember_incremental` remain facades on the
7
+ same runner, request budget and commit journal.
8
8
 
9
- The default is still `legacy`. This is an integration for isolated evaluation,
10
- not live Flash/Hermes acceptance, a release, or permission to change a production
11
- Vault. No existing config, memory, model route or version is automatically migrated.
12
- Text `remember()` has a separate, default-legacy `process.remember_pipeline`
13
- selection; see `remember-incremental-route.md`. Switching automatic processing
14
- alone does not enable the text route. Both use the same incremental runner.
9
+ New Vaults default to incremental processing. Existing Vaults that still contain a
10
+ `legacy` pipeline setting remain readable for migration inspection, but processing
11
+ fails closed with `legacy_pipeline_removed` until the operator explicitly updates
12
+ the configuration after the required stop/backup/preflight procedure.
15
13
 
16
- ## Select the route, not a second pipeline
14
+ ## One engine, compatibility selector only
17
15
 
18
- The optional configuration field is:
16
+ The configuration field is retained for migration visibility:
19
17
 
20
18
  ```yaml
21
19
  process:
22
- automatic_pipeline: legacy # legacy | incremental; default remains legacy
20
+ automatic_pipeline: incremental
23
21
  ```
24
22
 
25
- A missing field preserves legacy behavior. Unknown names, nonstrings and malformed
26
- configuration fail explicitly; they do not fall back to a different model pipeline.
27
- After operator-controlled preparation on an isolated copy, `incremental` may be
28
- selected in that copy's configuration. No command in this batch switches it for you.
23
+ A missing field resolves to incremental. `incremental` is the only executable
24
+ value. A retained `legacy` value is a migration blocker; unknown names, nonstrings
25
+ and malformed configuration fail explicitly. Nothing silently reinterprets an old
26
+ legacy queue or configuration as new incremental work.
29
27
 
30
28
  The optional per-call override affects that call only:
31
29
 
@@ -134,9 +132,11 @@ concurrent planners and stale responses.
134
132
 
135
133
  ## Queue binding and cutover
136
134
 
137
- An accepted job binds effective route, configured default, normalized scope and
138
- recover flag. A same-session request with different controls cannot silently
139
- coalesce into it. Legacy queue records missing these fields keep legacy semantics.
135
+ An accepted job binds the incremental engine, normalized scope and recover flag.
136
+ A same-session request with different controls cannot silently coalesce into it.
137
+ Historical queue records that are missing the incremental route, or explicitly
138
+ name `legacy`, are retained for diagnosis but blocked with
139
+ `legacy_pipeline_removed`; they are never executed by a compatibility engine.
140
140
  Malformed stored controls fail validation without resetting the queue.
141
141
 
142
142
  A waiting job refuses execution when the configured default has changed since its
@@ -185,8 +185,8 @@ model configuration absent. Queue errors do not become successful NO_MEMORY.
185
185
 
186
186
  ## Validation and outstanding work
187
187
 
188
- Public tests cover legacy defaults, route overrides, exact scope, source ordering,
189
- batch caps, redacted ID selection, CLI/MCP/queue controls, captured-route conflicts,
188
+ Public tests cover incremental defaults, explicit rejection of legacy execution,
189
+ exact scope, source ordering, batch caps, redacted ID selection, CLI/MCP/queue controls,
190
190
  old queue compatibility, safe summaries, native NO_CHANGE, index/cleanup failures,
191
191
  source/target edits, Forget, concurrent dispatch, and a real child process exiting
192
192
  after its saved response. All Vaults and model responses in tests are synthetic.
@@ -207,8 +207,8 @@ Native installed-artifact results must come from the exact candidate's CI
207
207
  reports, not from a phase document or a prior green build. A native package test
208
208
  with a host-interface stub is not actual host acceptance.
209
209
 
210
- The automatic and text-remember routes both remain default-legacy and are
211
- selected independently. `--dry-run` may still call the configured model;
210
+ Automatic processing and text remember both use the incremental engine by default.
211
+ `--dry-run` may still call the configured model;
212
212
  acceptance plan-only mode is the no-model preparation path. See
213
213
  [the acceptance evidence guide](acceptance-evidence-guide.md) for the remaining
214
214
  gates. The fixed 1,861-codepoint/3,587-byte main prompt is unchanged.
@@ -1,10 +1,11 @@
1
1
  # Migration preflight and private local backup (G4e)
2
2
 
3
- This increment implements the preparation portion of the controlled migration
4
- contract (FS178-179, FS188, FS192 and FS196). It neither switches a pipeline nor
5
- stops processes, rewrites business records, resets budgets or restores a Vault.
6
- A verified backup is not permission to activate the new routes. The fixed model
7
- prompt, request allowance, `legacy` defaults and package version are unchanged.
3
+ This command implements the preparation portion of the controlled migration
4
+ contract (FS178-179, FS188, FS192 and FS196). It does not stop processes, rewrite
5
+ business records, reset budgets, restore a Vault, or rewrite pipeline settings.
6
+ The v0.2.67 candidate itself has only the incremental engine; a retained
7
+ `legacy` setting is reported as `legacy_pipeline_configuration` and must be
8
+ handled explicitly after writers are stopped and a verified backup exists.
8
9
 
9
10
  ## Inspect before changing anything
10
11
 
@@ -1,10 +1,10 @@
1
- # Existing text remember route (G4b, opt-in)
1
+ # Text remember on the incremental engine
2
2
 
3
- The existing `Memleaf.remember()` and MCP `remember` tool can explicitly select
4
- incremental execution. This is an adapter to the same selected-retention runner,
5
- budget, native comparator, compiler and shared MemoryWriter; not another planner.
6
- The already implemented automatic route is independent. Neither route is enabled
7
- on an existing Vault by this change. Package version and fixed prompts are unchanged.
3
+ As of the v0.2.67 candidate, `Memleaf.remember()` and the MCP `remember` tool use
4
+ incremental execution as their only engine. This remains an adapter to the same
5
+ selected-retention runner, budget, native comparator, compiler and shared
6
+ MemoryWriter; it does not create a second planner. There is no executable legacy
7
+ remember path and no fallback to it.
8
8
 
9
9
  ## Selection and compatibility
10
10
 
@@ -26,13 +26,13 @@ if result["retry_available"]:
26
26
  )
27
27
  ```
28
28
 
29
- The optional `process.remember_pipeline` is `legacy` by default and accepts only
30
- `legacy` or `incremental`. The per-call `pipeline` overrides it. Selecting
31
- `process.automatic_pipeline=incremental` alone does not change text remember.
32
- Unknown values fail explicitly, not by falling back to another semantic pipeline.
33
- The MCP tool adds only the same optional `pipeline`, `recover` and `source_time`
34
- fields. There is no new MCP tool, CLI command, daemon or automatic live migration.
35
- A supplied model/router still must meet the existing single-dispatch contract.
29
+ The retained `process.remember_pipeline` field defaults to `incremental`.
30
+ `incremental` is the only executable value; an old `legacy` value is preserved
31
+ for migration inspection but is rejected with `legacy_pipeline_removed`. The
32
+ per-call `pipeline` argument is now only a compatibility selector for
33
+ `incremental`. Unknown values fail explicitly. The MCP tool exposes only
34
+ `incremental` plus the existing `recover` and `source_time` controls. A
35
+ supplied model/router still must meet the existing single-dispatch contract.
36
36
 
37
37
  `content` and its `text` alias contain the actual submitted user material. Supplying
38
38
  both with different values is rejected on the incremental route. One text request
@@ -145,12 +145,12 @@ consent or report a damaged state as NO_MEMORY.
145
145
 
146
146
  ## Cutover and validation
147
147
 
148
- Keep legacy defaults until controlled stop/backup/upgrade/restart and the planned
149
- live acceptance. Older code does not understand the standalone marker and cannot
150
- be expected to enforce its cleanup semantics. Changing a config key does not kill
151
- an old process or grant safe mixed-version writes. No default activation, production
152
- Vault mutation, release, real Flash calls or native Hermes/Windows/macOS acceptance
153
- was performed by this increment.
148
+ Production migration still requires controlled stop/backup/upgrade/restart and
149
+ live acceptance. Older binaries must be stopped before changing a retained legacy
150
+ configuration; changing a config key does not kill an old process or make
151
+ mixed-version writes safe. This candidate changes the package default to
152
+ incremental but does not itself mutate a production Vault, run a real model, or
153
+ prove native Hermes lifecycle acceptance.
154
154
 
155
155
  Deterministic tests cover one-user projection, origin provenance, same/new intents,
156
156
  legacy-route collisions, explicit and configured selection, unchanged originating
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "memleaf"
7
- version = "0.2.66"
7
+ version = "0.2.67"
8
8
  description = "A local-first Markdown memory core for AI agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,6 +1,6 @@
1
1
  """Local-first Markdown memory core for AI agents."""
2
2
 
3
- __version__ = "0.2.66"
3
+ __version__ = "0.2.67"
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
@@ -94,8 +94,8 @@ def build_parser() -> argparse.ArgumentParser:
94
94
  process.add_argument("--source", default=None)
95
95
  process.add_argument("--session-id", default=None)
96
96
  process.add_argument("--scope", default=None)
97
- process.add_argument("--pipeline", choices=("legacy", "incremental"), default=None,
98
- help="explicit automatic pipeline; otherwise use Vault configuration")
97
+ process.add_argument("--pipeline", choices=("incremental",), default=None,
98
+ help="compatibility override; incremental is the only processing engine")
99
99
  process.add_argument("--recover", action="store_true",
100
100
  help="allow remaining incremental transport recovery, not partial replan")
101
101
  process.add_argument("--dry-run", action="store_true", help="no source Vault writes; may call the configured model")
@@ -89,8 +89,8 @@ DEFAULT_CONFIG: dict[str, Any] = {
89
89
  "memory_compact_threshold_tokens": 100000,
90
90
  "memory_compact_candidate_ratio": 0.30,
91
91
  "inbox_cleanup_hours": 24,
92
- "automatic_pipeline": "legacy",
93
- "remember_pipeline": "legacy",
92
+ "automatic_pipeline": "incremental",
93
+ "remember_pipeline": "incremental",
94
94
  # Compatibility key: closed todo heads remain current identities.
95
95
  "closed_todo_retention_days": 30,
96
96
  "model_concurrency": DEFAULT_MODEL_CONCURRENCY,
@@ -225,10 +225,12 @@ def load_config(path: Path | str, *, vault: Path | str | None = None) -> dict[st
225
225
  cleanup_hours = process.get("inbox_cleanup_hours") if isinstance(process, Mapping) else None
226
226
  if type(cleanup_hours) is not int or cleanup_hours < 0:
227
227
  raise ValueError("invalid memleaf process.inbox_cleanup_hours")
228
- automatic_pipeline = process.get("automatic_pipeline", "legacy")
228
+ # Legacy values remain parseable only so old Vaults can be inspected and migrated.
229
+ # Runtime selection rejects them; new/default configurations are incremental-only.
230
+ automatic_pipeline = process.get("automatic_pipeline", "incremental")
229
231
  if not isinstance(automatic_pipeline, str) or automatic_pipeline not in {"legacy", "incremental"}:
230
232
  raise ValueError("invalid memleaf process.automatic_pipeline")
231
- remember_pipeline = process.get("remember_pipeline", "legacy")
233
+ remember_pipeline = process.get("remember_pipeline", "incremental")
232
234
  if not isinstance(remember_pipeline, str) or remember_pipeline not in {"legacy", "incremental"}:
233
235
  raise ValueError("invalid memleaf process.remember_pipeline")
234
236
  closed_todo_days = process.get("closed_todo_retention_days") if isinstance(process, Mapping) else None
@@ -296,11 +298,11 @@ def save_config(path: Path | str, config: Mapping[str, Any]) -> None:
296
298
  if not isinstance(process, Mapping):
297
299
  raise ValueError("invalid memleaf process settings")
298
300
  normalized_process = dict(process)
299
- pipeline = normalized_process.get("automatic_pipeline", "legacy")
301
+ pipeline = normalized_process.get("automatic_pipeline", "incremental")
300
302
  if not isinstance(pipeline, str) or pipeline not in {"legacy", "incremental"}:
301
303
  raise ValueError("invalid memleaf process.automatic_pipeline")
302
304
  normalized_process["automatic_pipeline"] = pipeline
303
- remember_pipeline = normalized_process.get("remember_pipeline", "legacy")
305
+ remember_pipeline = normalized_process.get("remember_pipeline", "incremental")
304
306
  if not isinstance(remember_pipeline, str) or remember_pipeline not in {"legacy", "incremental"}:
305
307
  raise ValueError("invalid memleaf process.remember_pipeline")
306
308
  normalized_process["remember_pipeline"] = remember_pipeline
@@ -1,5 +1,5 @@
1
1
  name: memleaf
2
- version: 0.2.65
2
+ version: 0.2.67
3
3
  description: "Hermes-native external MemoryProvider for local-first Markdown memory shared with the memleaf MCP server."
4
4
  hooks:
5
5
  - prefetch
@@ -902,6 +902,44 @@ _MODEL_ROUTE_INCOMPATIBLE_ACTION = (
902
902
  )
903
903
 
904
904
 
905
+ _PIPELINE_MIGRATION_REQUIRED_ACTION = (
906
+ "This Vault still selects the removed legacy processing engine. Stop all supported "
907
+ "writers, run migration-check, create and verify a migration backup, then set both "
908
+ "process.automatic_pipeline and process.remember_pipeline to incremental before "
909
+ "relying on automatic extraction or remember."
910
+ )
911
+
912
+ _PIPELINE_CONFIGURATION_INVALID_ACTION = (
913
+ "Repair the Vault process configuration before relying on memory processing. "
914
+ "v0.2.67 supports only the incremental execution engine."
915
+ )
916
+
917
+
918
+ def _pipeline_route_outcome(vault: Any) -> tuple[bool, str, str | None]:
919
+ """Report whether this Vault can execute the sole incremental engine."""
920
+ try:
921
+ config = vault.config()
922
+ except Exception:
923
+ return False, "pipeline_configuration_invalid", _PIPELINE_CONFIGURATION_INVALID_ACTION
924
+ process = config.get("process") if isinstance(config, Mapping) else None
925
+ if not isinstance(process, Mapping):
926
+ return False, "pipeline_configuration_invalid", _PIPELINE_CONFIGURATION_INVALID_ACTION
927
+ values = (
928
+ process.get("automatic_pipeline", "incremental"),
929
+ process.get("remember_pipeline", "incremental"),
930
+ )
931
+ if any(value == "legacy" for value in values):
932
+ return False, "pipeline_migration_required", _PIPELINE_MIGRATION_REQUIRED_ACTION
933
+ if any(value != "incremental" for value in values):
934
+ return False, "pipeline_configuration_invalid", _PIPELINE_CONFIGURATION_INVALID_ACTION
935
+ return True, "ready", None
936
+
937
+
938
+ def _join_user_actions(*actions: str | None) -> str | None:
939
+ values = [action.strip() for action in actions if isinstance(action, str) and action.strip()]
940
+ return " ".join(values) if values else None
941
+
942
+
905
943
  def _route_can_extract(vault: Any) -> bool:
906
944
  """Ask the production router whether this Vault's route can speak B3.
907
945
 
@@ -965,6 +1003,22 @@ def install_hermes(
965
1003
  if mcp_runtime not in _MCP_RUNTIME_POLICIES:
966
1004
  raise ValueError(f"unsupported Hermes MCP runtime policy: {mcp_runtime}")
967
1005
 
1006
+ # Reject an internally inconsistent package before reading or changing any
1007
+ # Hermes/Vault state. The post-copy check below remains as defense in depth.
1008
+ packaged_provider = Path(__file__).with_name("hermes_provider")
1009
+ packaged_provider_version = _provider_manifest_version(packaged_provider)
1010
+ if packaged_provider_version != core_version:
1011
+ return _failure_result(
1012
+ stage="provider_version",
1013
+ reason=(
1014
+ "packaged Hermes provider version mismatch: "
1015
+ f"core={core_version}, provider={packaged_provider_version or 'unknown'}"
1016
+ ),
1017
+ core_version=core_version,
1018
+ provider_version=packaged_provider_version,
1019
+ vault=None,
1020
+ )
1021
+
968
1022
  home = _home_from_environment()
969
1023
  hermes_home = _hermes_home(home)
970
1024
  try:
@@ -1082,11 +1136,16 @@ def install_hermes(
1082
1136
  # here used to leave core and provider versions out of step. A *present*
1083
1137
  # route that cannot speak B3 is reported separately, because advertising
1084
1138
  # ``ready`` for it would promise extraction that can never run.
1085
- model_ready, processing_status, model_action = _model_route_outcome(
1139
+ model_ready, model_processing_status, model_action = _model_route_outcome(
1086
1140
  model, can_extract=_route_can_extract(vault)
1087
1141
  )
1088
- # ``model_ready`` is reported through ``processing_status`` below; the host
1089
- # integration itself is configured either way.
1142
+ pipeline_ready, pipeline_processing_status, pipeline_action = _pipeline_route_outcome(vault)
1143
+ processing_status = (
1144
+ model_processing_status if pipeline_ready else pipeline_processing_status
1145
+ )
1146
+ install_action = _join_user_actions(pipeline_action, model_action)
1147
+ # Host integration is configured even when processing still needs an explicit
1148
+ # model route or a controlled legacy->incremental Vault migration.
1090
1149
 
1091
1150
  provider_target = hermes_home / "plugins" / "memleaf"
1092
1151
  provider_config = hermes_home / "memleaf.json"
@@ -1298,8 +1357,8 @@ def install_hermes(
1298
1357
  "native_sources": native_registration,
1299
1358
  "capture": capture_policy_status(vault.config()),
1300
1359
  "processing_status": processing_status,
1301
- "user_action_required": bool(model_action),
1302
- "user_action": model_action,
1360
+ "user_action_required": bool(install_action),
1361
+ "user_action": install_action,
1303
1362
  }
1304
1363
 
1305
1364
 
@@ -1357,9 +1416,12 @@ def install_codex(*, vault_path: Path | None = None) -> dict[str, Any]:
1357
1416
  "codex": configured.to_dict(),
1358
1417
  }
1359
1418
  model_ready = model.get("status") in {"configured", "already_configured"}
1419
+ pipeline_ready, pipeline_processing_status, pipeline_action = _pipeline_route_outcome(vault)
1360
1420
  actions: list[str] = []
1361
1421
  if configured.user_action_required and configured.user_action:
1362
1422
  actions.append(configured.user_action)
1423
+ if pipeline_action:
1424
+ actions.append(pipeline_action)
1363
1425
  if not model_ready:
1364
1426
  actions.append(
1365
1427
  "Configure an independent memleaf Model Route for this Vault before relying on "
@@ -1372,7 +1434,10 @@ def install_codex(*, vault_path: Path | None = None) -> dict[str, Any]:
1372
1434
  "vault": str(vault.root),
1373
1435
  "vault_source": vault_source,
1374
1436
  "model": model,
1375
- "processing_status": "ready" if model_ready else "model_route_required",
1437
+ "processing_status": (
1438
+ ("ready" if model_ready else "model_route_required")
1439
+ if pipeline_ready else pipeline_processing_status
1440
+ ),
1376
1441
  "codex": configured.to_dict(),
1377
1442
  "user_action_required": bool(actions),
1378
1443
  "user_action": " ".join(actions) if actions else None,
@@ -303,7 +303,7 @@ _TOOLS: tuple[dict[str, Any], ...] = (
303
303
  "session_id": {"type": "string"},
304
304
  "scope": _text_or_texts_schema(),
305
305
  "background": {"type": "boolean"},
306
- "pipeline": {"type": "string", "enum": ["legacy", "incremental"]},
306
+ "pipeline": {"type": "string", "enum": ["incremental"]},
307
307
  "recover": {"type": "boolean"},
308
308
  }
309
309
  ),
@@ -332,7 +332,7 @@ _TOOLS: tuple[dict[str, Any], ...] = (
332
332
  "event_id": {"type": "string"},
333
333
  "intent_id": {"type": "string"},
334
334
  "scopes": _text_or_texts_schema(),
335
- "pipeline": {"type": "string", "enum": ["legacy", "incremental"]},
335
+ "pipeline": {"type": "string", "enum": ["incremental"]},
336
336
  "recover": {"type": "boolean"},
337
337
  "source_time": {"type": "string"},
338
338
  },
@@ -12,7 +12,7 @@ import os
12
12
  from pathlib import Path
13
13
  import platform
14
14
  import re
15
- from typing import Any
15
+ from typing import Any, Mapping
16
16
 
17
17
  from . import __version__
18
18
  from .inspection import _checked_snapshot, _snapshot, _fingerprint, existing_root
@@ -94,6 +94,7 @@ def _checks(service: Any, snapshot: dict[str, bytes]) -> dict:
94
94
  warnings: set[str] = set()
95
95
  counts: dict[str, int] = {}
96
96
  config = None
97
+ configured: dict[str, Any] = {}
97
98
  binding = {"status": "unknown"}
98
99
  try:
99
100
  binding = service.vault.identity_status()
@@ -106,6 +107,14 @@ def _checks(service: Any, snapshot: dict[str, bytes]) -> dict:
106
107
  except (OSError, ValueError, UnicodeError, TypeError):
107
108
  blockers.add("invalid_configuration")
108
109
  if config is not None:
110
+ process_config = config.get("process", {}) if isinstance(config, Mapping) else {}
111
+ configured = {
112
+ key: process_config.get(key, "incremental") if isinstance(process_config, Mapping) else None
113
+ for key in ("automatic_pipeline", "remember_pipeline")
114
+ }
115
+ counts["legacy_pipeline_settings"] = sum(value == "legacy" for value in configured.values())
116
+ if counts["legacy_pipeline_settings"]:
117
+ blockers.add("legacy_pipeline_configuration")
109
118
  native_unavailable, native_blockers = _native_source_readiness(service, config)
110
119
  counts["unavailable_native_sources"] = native_unavailable
111
120
  blockers.update(native_blockers)
@@ -187,10 +196,8 @@ def _checks(service: Any, snapshot: dict[str, bytes]) -> dict:
187
196
  elif progress["status"] != "current":
188
197
  blockers.add("unsettled_sources_require_review")
189
198
  warnings.update(_EXTERNAL_CHECKS)
190
- # Even a disabled stage does not prove that an older installed binary will
191
- # never run. Report facts; never automatically flip either route.
192
- configured = {k: config["process"].get(k, "legacy") for k in
193
- ("automatic_pipeline", "remember_pipeline")} if config else {}
199
+ # Historical route settings are reported as migration facts. They are not
200
+ # executable by this release and are never silently rewritten here.
194
201
  return {"local_status": "blocked" if blockers else "clear",
195
202
  "blockers": sorted(blockers),
196
203
  "backup_blockers": sorted(blockers & {"processing_owner_live", "queue_not_quiescent"}),
@@ -141,8 +141,9 @@ def _valid_state(value: Any) -> dict[str, Any]:
141
141
  if not _valid_job_id(job_id) or not isinstance(raw, Mapping):
142
142
  raise ProcessJobStateError("invalid process job record")
143
143
  from .processing_route import PIPELINES
144
+ persisted_pipelines = PIPELINES | {"legacy"}
144
145
  for field in ("pipeline", "configured_pipeline"):
145
- if field in raw and (not isinstance(raw[field], str) or raw[field] not in PIPELINES):
146
+ if field in raw and (not isinstance(raw[field], str) or raw[field] not in persisted_pipelines):
146
147
  raise ProcessJobStateError("invalid process job pipeline")
147
148
  if "recover" in raw and type(raw["recover"]) is not bool:
148
149
  raise ProcessJobStateError("invalid process job recovery")
@@ -523,8 +524,8 @@ def _aggregate_attempt_results(attempts: list[Any]) -> dict[str, Any]:
523
524
  if type(value) is int and value >= 0:
524
525
  aggregate["pending_inbox_turns"] = value
525
526
  break
526
- # Route-change refusals can also originate from an old legacy job. Keep
527
- # its safe reason in the top-level result, not only in attempt history.
527
+ # Historical legacy jobs can still produce a blocked migration result. Keep
528
+ # that safe reason in the top-level result, not only in attempt history.
528
529
  for attempt in reversed(attempts):
529
530
  value = attempt.get("result", {}) if isinstance(attempt, Mapping) else {}
530
531
  if not isinstance(value, Mapping):
@@ -691,8 +692,7 @@ def enqueue(vault_path: Path | str, *, source: str, session_id: str, scope: Any
691
692
  with vault.lock():
692
693
  config = vault.config()
693
694
  chosen = select_pipeline(config, pipeline)
694
- configured = select_pipeline(config)
695
- if type(recover) is not bool or (chosen == "legacy" and recover):
695
+ if type(recover) is not bool:
696
696
  raise ValueError("invalid_process_recovery")
697
697
  state = _read_state(vault)
698
698
  recovered = _recover_dead_active(state)
@@ -707,8 +707,7 @@ def enqueue(vault_path: Path | str, *, source: str, session_id: str, scope: Any
707
707
  if job.get("status") in _ACTIVE | {"pending"}:
708
708
  old_scope = normalize_scopes(job.get("scope")) if job.get("scope") is not None else None
709
709
  if (job.get("pipeline", "legacy") != chosen or old_scope != scope
710
- or job.get("recover", False) != recover
711
- or job.get("configured_pipeline", "legacy") != configured):
710
+ or job.get("recover", False) != recover):
712
711
  return {"accepted": False, "completed": False, "status": "blocked", "job_id": job_id,
713
712
  "reason": "process_arguments_changed"}
714
713
  if job.get("status") in _ACTIVE:
@@ -728,7 +727,7 @@ def enqueue(vault_path: Path | str, *, source: str, session_id: str, scope: Any
728
727
  job_id = f"job-{uuid.uuid4().hex}"
729
728
  job = {
730
729
  "job_id": job_id, "source": source, "session_id": session_id,
731
- "scope": scope, "pipeline": chosen, "configured_pipeline": configured, "recover": recover,
730
+ "scope": scope, "pipeline": chosen, "configured_pipeline": "incremental", "recover": recover,
732
731
  "status": "pending", "accepted_at": _now(),
733
732
  "updated_at": _now(), "owner_pid": None, "rerun_requested": False,
734
733
  }
@@ -815,13 +814,20 @@ def run_worker(vault_path: Path | str, job_id: str) -> int:
815
814
  try:
816
815
  service = Memleaf(vault)
817
816
  from .processing_route import select_pipeline
818
- if select_pipeline(vault.config()) != job.get("configured_pipeline", "legacy"):
819
- result = {"pipeline": job.get("pipeline", "legacy"), "execution_status": "blocked",
817
+ stored_pipeline = job.get("pipeline")
818
+ try:
819
+ select_pipeline(vault.config())
820
+ configured_code = None
821
+ except ValueError as error:
822
+ configured_code = str(error)
823
+ if stored_pipeline != "incremental" or configured_code is not None:
824
+ result = {"pipeline": "incremental", "execution_status": "blocked",
820
825
  "coverage_status": "partial", "deferred_inbox_turns": 1,
821
- "results": [{"execution_status": "blocked", "code": "processing_pipeline_changed"}]}
826
+ "results": [{"execution_status": "blocked",
827
+ "code": configured_code or "legacy_pipeline_removed"}]}
822
828
  else:
823
829
  result = service.process(source=job["source"], session_id=job["session_id"], scope=job.get("scope"),
824
- pipeline=job.get("pipeline", "legacy"), recover=job.get("recover", False))
830
+ pipeline="incremental", recover=job.get("recover", False))
825
831
  terminal = _result_status(result if isinstance(result, Mapping) else {})
826
832
  rerun = _finish(vault, job_id, status_value=terminal, result=result)
827
833
  except Exception as error:
@@ -0,0 +1,22 @@
1
+ """Compatibility tombstone for the removed legacy processing engine.
2
+
3
+ memleaf v0.2.67 and later execute memory extraction only through the
4
+ incremental runtime. This module remains importable so older integrations fail
5
+ with an explicit migration error instead of a ModuleNotFoundError, but it no
6
+ longer contains or executes the legacy engine.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from typing import Any
11
+
12
+ from .process_common import ProcessingError
13
+
14
+
15
+ class Processor:
16
+ """Removed legacy processor compatibility surface."""
17
+
18
+ def __init__(self, service: Any):
19
+ raise ProcessingError("legacy_pipeline_removed")
20
+
21
+
22
+ __all__ = ["Processor", "ProcessingError"]