memleaf 0.2.69__tar.gz → 0.2.71__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 (241) hide show
  1. {memleaf-0.2.69 → memleaf-0.2.71}/CHANGELOG.md +21 -0
  2. {memleaf-0.2.69 → memleaf-0.2.71}/MANIFEST.in +0 -1
  3. {memleaf-0.2.69/src/memleaf.egg-info → memleaf-0.2.71}/PKG-INFO +2 -2
  4. {memleaf-0.2.69 → memleaf-0.2.71}/README.en.md +1 -1
  5. {memleaf-0.2.69 → memleaf-0.2.71}/README.md +1 -1
  6. {memleaf-0.2.69 → memleaf-0.2.71}/docs/immutable-release-assets.md +6 -6
  7. {memleaf-0.2.69 → memleaf-0.2.71}/docs/incremental-planner-preview.md +4 -11
  8. memleaf-0.2.71/docs/installed-artifact-verification.md +57 -0
  9. {memleaf-0.2.69 → memleaf-0.2.71}/docs/memory-state-contract.md +3 -6
  10. {memleaf-0.2.69 → memleaf-0.2.71}/docs/processing-quality-acceptance.md +6 -7
  11. {memleaf-0.2.69 → memleaf-0.2.71}/pyproject.toml +1 -1
  12. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/__init__.py +1 -1
  13. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/hermes_provider/_provider.py +12 -2
  14. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/hermes_provider/plugin.yaml +1 -1
  15. memleaf-0.2.71/src/memleaf/incremental_dates.py +146 -0
  16. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/incremental_protocol.py +13 -3
  17. {memleaf-0.2.69 → memleaf-0.2.71/src/memleaf.egg-info}/PKG-INFO +2 -2
  18. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf.egg-info/SOURCES.txt +1 -67
  19. memleaf-0.2.69/docs/installed-artifact-verification.md +0 -131
  20. memleaf-0.2.69/src/memleaf/incremental_dates.py +0 -75
  21. memleaf-0.2.69/tests_public/acceptance_oracles.py +0 -2
  22. memleaf-0.2.69/tests_public/candidate_update_fixtures.py +0 -8
  23. memleaf-0.2.69/tests_public/incremental_test_support.py +0 -62
  24. memleaf-0.2.69/tests_public/platform_smoke.py +0 -70
  25. memleaf-0.2.69/tests_public/release_assets.py +0 -176
  26. memleaf-0.2.69/tests_public/run_contracts.py +0 -102
  27. memleaf-0.2.69/tests_public/test_a083_regressions.py +0 -370
  28. memleaf-0.2.69/tests_public/test_a47_regressions.py +0 -440
  29. memleaf-0.2.69/tests_public/test_acceptance.py +0 -250
  30. memleaf-0.2.69/tests_public/test_acceptance_boundaries.py +0 -239
  31. memleaf-0.2.69/tests_public/test_artifact_verification.py +0 -178
  32. memleaf-0.2.69/tests_public/test_body_compaction.py +0 -287
  33. memleaf-0.2.69/tests_public/test_candidate_reconciliation.py +0 -161
  34. memleaf-0.2.69/tests_public/test_contract_reporter.py +0 -66
  35. memleaf-0.2.69/tests_public/test_control_absence_contract.py +0 -91
  36. memleaf-0.2.69/tests_public/test_create_memory_contract.py +0 -207
  37. memleaf-0.2.69/tests_public/test_development_closeout.py +0 -193
  38. memleaf-0.2.69/tests_public/test_explicit_memory_update.py +0 -553
  39. memleaf-0.2.69/tests_public/test_explicit_update_contract.py +0 -205
  40. memleaf-0.2.69/tests_public/test_g1_memory_contract.py +0 -171
  41. memleaf-0.2.69/tests_public/test_g2_source_contract.py +0 -402
  42. memleaf-0.2.69/tests_public/test_hermes_native_provision.py +0 -91
  43. memleaf-0.2.69/tests_public/test_hermes_source_metadata.py +0 -188
  44. memleaf-0.2.69/tests_public/test_incremental_commit.py +0 -194
  45. memleaf-0.2.69/tests_public/test_incremental_configured_runtime.py +0 -110
  46. memleaf-0.2.69/tests_public/test_incremental_dates.py +0 -119
  47. memleaf-0.2.69/tests_public/test_incremental_execution.py +0 -367
  48. memleaf-0.2.69/tests_public/test_incremental_execution_recovery.py +0 -302
  49. memleaf-0.2.69/tests_public/test_incremental_native.py +0 -428
  50. memleaf-0.2.69/tests_public/test_incremental_partial.py +0 -177
  51. memleaf-0.2.69/tests_public/test_incremental_partial_boundaries.py +0 -180
  52. memleaf-0.2.69/tests_public/test_incremental_partial_recovery.py +0 -277
  53. memleaf-0.2.69/tests_public/test_incremental_preview.py +0 -190
  54. memleaf-0.2.69/tests_public/test_incremental_protocol.py +0 -459
  55. memleaf-0.2.69/tests_public/test_incremental_recovery.py +0 -263
  56. memleaf-0.2.69/tests_public/test_incremental_remember.py +0 -231
  57. memleaf-0.2.69/tests_public/test_incremental_remember_recovery.py +0 -238
  58. memleaf-0.2.69/tests_public/test_incremental_scope_recovery.py +0 -332
  59. memleaf-0.2.69/tests_public/test_incremental_scopes.py +0 -203
  60. memleaf-0.2.69/tests_public/test_inspection_read_budget.py +0 -171
  61. memleaf-0.2.69/tests_public/test_mcp_retrieval_fallback.py +0 -140
  62. memleaf-0.2.69/tests_public/test_migration_backup.py +0 -374
  63. memleaf-0.2.69/tests_public/test_migration_runtime.py +0 -111
  64. memleaf-0.2.69/tests_public/test_native_read_stability.py +0 -196
  65. memleaf-0.2.69/tests_public/test_planner_provenance_contract.py +0 -117
  66. memleaf-0.2.69/tests_public/test_platform_contracts.py +0 -103
  67. memleaf-0.2.69/tests_public/test_processed_state_contract.py +0 -98
  68. memleaf-0.2.69/tests_public/test_processing_entrypoints.py +0 -266
  69. memleaf-0.2.69/tests_public/test_processing_route.py +0 -278
  70. memleaf-0.2.69/tests_public/test_processing_route_recovery.py +0 -159
  71. memleaf-0.2.69/tests_public/test_provider_compatibility.py +0 -567
  72. memleaf-0.2.69/tests_public/test_public_query_integrity.py +0 -267
  73. memleaf-0.2.69/tests_public/test_query_benchmark.py +0 -68
  74. memleaf-0.2.69/tests_public/test_query_clock.py +0 -89
  75. memleaf-0.2.69/tests_public/test_query_progress.py +0 -140
  76. memleaf-0.2.69/tests_public/test_query_scan_reuse.py +0 -234
  77. memleaf-0.2.69/tests_public/test_release_assets.py +0 -283
  78. memleaf-0.2.69/tests_public/test_remember_route.py +0 -274
  79. memleaf-0.2.69/tests_public/test_remember_route_recovery.py +0 -232
  80. memleaf-0.2.69/tests_public/test_revision_target_integrity.py +0 -262
  81. memleaf-0.2.69/tests_public/test_runtime_retention.py +0 -329
  82. memleaf-0.2.69/tests_public/test_runtime_retention_observation.py +0 -174
  83. memleaf-0.2.69/tests_public/test_runtime_retention_recovery.py +0 -230
  84. memleaf-0.2.69/tests_public/test_source_followup_contract.py +0 -160
  85. memleaf-0.2.69/tests_public/test_vault_binding_contract.py +0 -97
  86. memleaf-0.2.69/tests_public/verify_distribution.py +0 -363
  87. {memleaf-0.2.69 → memleaf-0.2.71}/LICENSE +0 -0
  88. {memleaf-0.2.69 → memleaf-0.2.71}/docs/acceptance-evidence-guide.md +0 -0
  89. {memleaf-0.2.69 → memleaf-0.2.71}/docs/automatic-processing-route.md +0 -0
  90. {memleaf-0.2.69 → memleaf-0.2.71}/docs/body-compaction.md +0 -0
  91. {memleaf-0.2.69 → memleaf-0.2.71}/docs/candidate-reconciliation.md +0 -0
  92. {memleaf-0.2.69 → memleaf-0.2.71}/docs/capture-budget-design.md +0 -0
  93. {memleaf-0.2.69 → memleaf-0.2.71}/docs/config-migrations.md +0 -0
  94. {memleaf-0.2.69 → memleaf-0.2.71}/docs/core-refactor.md +0 -0
  95. {memleaf-0.2.69 → memleaf-0.2.71}/docs/development-closeout.md +0 -0
  96. {memleaf-0.2.69 → memleaf-0.2.71}/docs/evidence-retention.md +0 -0
  97. {memleaf-0.2.69 → memleaf-0.2.71}/docs/explicit-memory-update.md +0 -0
  98. {memleaf-0.2.69 → memleaf-0.2.71}/docs/extraction-latency.md +0 -0
  99. {memleaf-0.2.69 → memleaf-0.2.71}/docs/gate-evidence-boundary.md +0 -0
  100. {memleaf-0.2.69 → memleaf-0.2.71}/docs/general-processing.md +0 -0
  101. {memleaf-0.2.69 → memleaf-0.2.71}/docs/hermes-mcp-runtime.md +0 -0
  102. {memleaf-0.2.69 → memleaf-0.2.71}/docs/hermes-provider-compatibility.md +0 -0
  103. {memleaf-0.2.69 → memleaf-0.2.71}/docs/host-acceptance-runbook.md +0 -0
  104. {memleaf-0.2.69 → memleaf-0.2.71}/docs/incremental-commit-contract.md +0 -0
  105. {memleaf-0.2.69 → memleaf-0.2.71}/docs/incremental-execution.md +0 -0
  106. {memleaf-0.2.69 → memleaf-0.2.71}/docs/incremental-native-comparison.md +0 -0
  107. {memleaf-0.2.69 → memleaf-0.2.71}/docs/incremental-partial-recovery.md +0 -0
  108. {memleaf-0.2.69 → memleaf-0.2.71}/docs/incremental-runtime-contract.md +0 -0
  109. {memleaf-0.2.69 → memleaf-0.2.71}/docs/incremental-scope-registration.md +0 -0
  110. {memleaf-0.2.69 → memleaf-0.2.71}/docs/incremental-selected-retention.md +0 -0
  111. {memleaf-0.2.69 → memleaf-0.2.71}/docs/migration-preflight.md +0 -0
  112. {memleaf-0.2.69 → memleaf-0.2.71}/docs/public-query-integrity.md +0 -0
  113. {memleaf-0.2.69 → memleaf-0.2.71}/docs/query-performance.md +0 -0
  114. {memleaf-0.2.69 → memleaf-0.2.71}/docs/remember-incremental-route.md +0 -0
  115. {memleaf-0.2.69 → memleaf-0.2.71}/docs/runtime-state-retention.md +0 -0
  116. {memleaf-0.2.69 → memleaf-0.2.71}/docs/semantic-extraction-protocol.md +0 -0
  117. {memleaf-0.2.69 → memleaf-0.2.71}/docs/source-work-contract.md +0 -0
  118. {memleaf-0.2.69 → memleaf-0.2.71}/docs/v0.2.26-processing-status.md +0 -0
  119. {memleaf-0.2.69 → memleaf-0.2.71}/examples/README.md +0 -0
  120. {memleaf-0.2.69 → memleaf-0.2.71}/examples/basic_usage.py +0 -0
  121. {memleaf-0.2.69 → memleaf-0.2.71}/examples/incremental_acceptance.json +0 -0
  122. {memleaf-0.2.69 → memleaf-0.2.71}/examples/mcp_stdio.ndjson +0 -0
  123. {memleaf-0.2.69 → memleaf-0.2.71}/examples/query_benchmark.py +0 -0
  124. {memleaf-0.2.69 → memleaf-0.2.71}/install.ps1 +0 -0
  125. {memleaf-0.2.69 → memleaf-0.2.71}/install.sh +0 -0
  126. {memleaf-0.2.69 → memleaf-0.2.71}/setup.cfg +0 -0
  127. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/__main__.py +0 -0
  128. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/acceptance.py +0 -0
  129. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/adapters/__init__.py +0 -0
  130. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/adapters/antigravity.py +0 -0
  131. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/adapters/base.py +0 -0
  132. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/adapters/codex.py +0 -0
  133. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/adapters/hermes.py +0 -0
  134. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/admission.py +0 -0
  135. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/batch_review.py +0 -0
  136. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/budget.py +0 -0
  137. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/capture.py +0 -0
  138. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/cli.py +0 -0
  139. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/compaction.py +0 -0
  140. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/config.py +0 -0
  141. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/create_coordinator.py +0 -0
  142. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/credentials.py +0 -0
  143. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/evidence_budget.py +0 -0
  144. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/evidence_policy.py +0 -0
  145. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/evidence_structure.py +0 -0
  146. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/evidence_syntax.py +0 -0
  147. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/explicit_text_source.py +0 -0
  148. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/extraction_budget.py +0 -0
  149. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/extraction_capability.py +0 -0
  150. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/extraction_work_state.py +0 -0
  151. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/frontmatter.py +0 -0
  152. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/hermes_provider/README.md +0 -0
  153. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/hermes_provider/__init__.py +0 -0
  154. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/hermes_provider/_mcp_client.py +0 -0
  155. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/hermes_provider/_shared.py +0 -0
  156. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/hermes_provider/evidence_budget.py +0 -0
  157. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/hermes_provider/provider_compatibility.py +0 -0
  158. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/hermes_runtime.py +0 -0
  159. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/host_events.py +0 -0
  160. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/host_runtime.py +0 -0
  161. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/inbox.py +0 -0
  162. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/incremental_commit.py +0 -0
  163. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/incremental_execution.py +0 -0
  164. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/incremental_journal.py +0 -0
  165. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/incremental_native.py +0 -0
  166. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/incremental_partial.py +0 -0
  167. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/incremental_preview.py +0 -0
  168. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/incremental_prompts.py +0 -0
  169. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/incremental_recovery.py +0 -0
  170. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/incremental_run_state.py +0 -0
  171. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/incremental_runtime.py +0 -0
  172. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/incremental_scopes.py +0 -0
  173. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/incremental_selection.py +0 -0
  174. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/index.py +0 -0
  175. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/inspection.py +0 -0
  176. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/installer.py +0 -0
  177. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/legacy_update_journal.py +0 -0
  178. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/llm/__init__.py +0 -0
  179. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/llm/base.py +0 -0
  180. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/llm/claude_compatible.py +0 -0
  181. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/llm/gemini.py +0 -0
  182. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/llm/openai_compatible.py +0 -0
  183. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/llm/router.py +0 -0
  184. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/llm/thinking.py +0 -0
  185. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/locking.py +0 -0
  186. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/mcp_server.py +0 -0
  187. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/memory_commit.py +0 -0
  188. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/memory_planner.py +0 -0
  189. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/memory_retraction.py +0 -0
  190. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/memory_update.py +0 -0
  191. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/memory_writer.py +0 -0
  192. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/migration.py +0 -0
  193. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/model_capabilities.py +0 -0
  194. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/model_discovery.py +0 -0
  195. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/model_execution.py +0 -0
  196. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/models.py +0 -0
  197. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/native_index.py +0 -0
  198. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/native_registration.py +0 -0
  199. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/parallel_model.py +0 -0
  200. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/planning_context.py +0 -0
  201. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/process_common.py +0 -0
  202. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/process_jobs.py +0 -0
  203. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/process_journal.py +0 -0
  204. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/process_owner.py +0 -0
  205. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/processing.py +0 -0
  206. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/processing_route.py +0 -0
  207. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/prompts.py +0 -0
  208. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/provenance.py +0 -0
  209. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/provider_compatibility.py +0 -0
  210. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/query_clock.py +0 -0
  211. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/query_progress.py +0 -0
  212. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/query_scan.py +0 -0
  213. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/receipt_codec.py +0 -0
  214. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/recording_policy.py +0 -0
  215. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/redaction.py +0 -0
  216. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/remember_route.py +0 -0
  217. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/retention.py +0 -0
  218. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/retrieval.py +0 -0
  219. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/retrieval_gate.py +0 -0
  220. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/runtime_retention.py +0 -0
  221. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/scope_maintenance.py +0 -0
  222. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/scope_state.py +0 -0
  223. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/semantic_maintenance.py +0 -0
  224. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/semantic_protocol.py +0 -0
  225. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/service.py +0 -0
  226. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/single_pass_memory_planner.py +0 -0
  227. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/single_pass_plan.py +0 -0
  228. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/source_policy.py +0 -0
  229. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/state_layout.py +0 -0
  230. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/subprocess_flags.py +0 -0
  231. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/summary_batch.py +0 -0
  232. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/target_reconciliation.py +0 -0
  233. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/turn_audit.py +0 -0
  234. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/turn_plan.py +0 -0
  235. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/update_coordinator.py +0 -0
  236. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/update_review.py +0 -0
  237. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/validation.py +0 -0
  238. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf/vault.py +0 -0
  239. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf.egg-info/dependency_links.txt +0 -0
  240. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf.egg-info/entry_points.txt +0 -0
  241. {memleaf-0.2.69 → memleaf-0.2.71}/src/memleaf.egg-info/top_level.txt +0 -0
@@ -4,6 +4,27 @@ All notable changes to memleaf are documented here.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.2.71 — 2026-09-21
8
+
9
+ ### Source-time and calendar grounding
10
+
11
+ - Normalize native Hermes Unix timestamps using the host-local day while
12
+ rejecting non-finite and out-of-range numeric values.
13
+ - Provide source-local calendar hints to incremental planning without using the
14
+ processing clock or making an additional model request.
15
+ - Reject generated content dates that conflict with cited source dates or
16
+ same-clock relative-date evidence, while preserving existing target wording.
17
+
18
+ ## 0.2.70 — 2026-09-21
19
+
20
+ ### Public repository and artifact hygiene
21
+
22
+ - Remove test files from the public `main` tree and keep them ignored locally.
23
+ - Stop including test files in source distributions and keep CI focused on
24
+ source syntax, isolated package installation and release-asset integrity.
25
+ - Preserve the runtime package and public API while ensuring new artifacts do
26
+ not contain the former test fixtures.
27
+
7
28
  ## 0.2.69 — 2026-09-21
8
29
 
9
30
  ### Incremental protocol compatibility hardening
@@ -7,7 +7,6 @@ include README.en.md
7
7
  recursive-include examples *.md *.ndjson *.py
8
8
  recursive-exclude examples *_acceptance.py
9
9
  prune tests
10
- recursive-include tests_public *.py
11
10
 
12
11
  include docs/*.md
13
12
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memleaf
3
- Version: 0.2.69
3
+ Version: 0.2.71
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.69。**
26
+ > **版本:0.2.71。**
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.69.**
7
+ > **Version: 0.2.71.**
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.69。**
7
+ > **版本:0.2.71。**
8
8
  > 自动提炼和复核共用同一条“未来记忆价值”标准:模型综合未来复用、信息增量、再次读取时的直接可用性和忘记成本,只保留对未来理解、判断或行动有实质影响的最小核心;没有明确价值的信息不提炼,不按具体业务场景硬编码排除。
9
9
  > 提炼契约现在只有一份中文语义契约(旧版英文 B3 提示词已删除),首轮、主题选择、候选修复和 `semantic_maintenance` 使用同一套保留价值判断;同一件事只写一次。
10
10
  > Core 不再因为归属、日期、任务依据或跨项目而丢弃整条候选:归属缺失会归一到证据里唯一被点名的项目、否则落 `global`;日期只接受能在本候选证据里找到出处、且能锚定成 ISO 的写法(`明天`、`周五`、`月底` 都会被解析),无法解析时只丢掉日期字段,记忆照常写入;未获授权的改归属沿用目标原归属。
@@ -14,14 +14,14 @@ that the bytes were unchanged: rebuilding an archive can produce different
14
14
  compressed bytes from equivalent source. A retry must use the **original verified
15
15
  artifacts**, not a rebuild presented as the same published package.
16
16
 
17
- `tests_public/release_assets.py` checks the tag's resolved commit and release ID,
18
- then downloads all existing expected assets by their asset IDs. Sizes and SHA-256
19
- must match the local wheel, source archive and deterministic `SHA256SUMS` before
20
- any missing attachment is uploaded. A mismatch fails without deleting, renaming,
17
+ The release workflow checks the tag's resolved commit and release metadata, then
18
+ downloads all existing expected assets by their asset IDs. Sizes and SHA-256 must
19
+ match the local wheel, source archive and deterministic `SHA256SUMS` before any
20
+ missing attachment is uploaded. A mismatch fails without deleting, renaming,
21
21
  overwriting or repairing the published asset. Unrelated release attachments stay
22
22
  untouched. Duplicate expected names, unfinished uploads and malformed metadata
23
- fail visibly. This helper is CI-only and is included with the public tests, not
24
- imported by the Memleaf runtime.
23
+ fail visibly. The check is kept inline in the workflow; no test helper is stored
24
+ in the public repository.
25
25
 
26
26
  Uploads are create-only and bounded to one attempt per missing name. When another
27
27
  uploader wins, the helper rereads and verifies the resulting bytes; a failed
@@ -150,17 +150,10 @@ bytes, LF newlines and a trailing LF; fences and dynamic inputs are excluded.
150
150
  Size is checked by the public tests against half of the v3.0 baseline. The old
151
151
  production prompts and model settings are not replaced.
152
152
 
153
- Run the source contract tests with:
154
-
155
- ```sh
156
- PYTHONPATH=src python -m unittest discover -s tests_public -p 'test_*.py'
157
- ```
158
-
159
- These tests cover the parser/compiler, calendar conversion, immutable snapshots,
160
- read-only service boundary and old 110 regressions. They are not live semantic
161
- or native Hermes/Windows/macOS acceptance. Before G3 production activation, finish
162
- commit/recovery integration and bounded dispatch, test the installed artifacts,
163
- and run the agreed isolated multi-turn Flash acceptance set.
153
+ The public repository intentionally does not store or execute test files. Before
154
+ G3 production activation, finish commit/recovery integration and bounded dispatch,
155
+ verify the installed artifacts through CI, and run the agreed isolated multi-turn
156
+ Flash acceptance set from its separate acceptance environment.
164
157
 
165
158
  ## Development closeout: bounded identity and provenance
166
159
 
@@ -0,0 +1,57 @@
1
+ # Installed artifact and native-platform verification
2
+
3
+ This is a deterministic packaging gate, not a runtime subsystem or live-model
4
+ acceptance gate. The public repository intentionally contains no test files.
5
+ CI therefore validates the source and the built artifacts without checking out
6
+ or executing an in-repository test suite.
7
+
8
+ ## Build once, install the same bytes
9
+
10
+ The build job:
11
+
12
+ 1. compiles `src/` with Python's standard-library compiler;
13
+ 2. builds one wheel and one source distribution;
14
+ 3. uploads only those two distributions as the workflow artifact.
15
+
16
+ The `verify-artifacts` job downloads those exact bytes and installs each artifact
17
+ in its own fresh virtual environment. It imports `memleaf` and checks the
18
+ reported package version. It does not read a source-tree checkout, run a model,
19
+ access a production Vault or create a release.
20
+
21
+ The matrix is:
22
+
23
+ | Native runner | Python |
24
+ |---|---|
25
+ | Ubuntu | 3.11, 3.12, 3.13 |
26
+ | Windows | 3.12 |
27
+ | macOS | 3.12 |
28
+
29
+ The source distribution is installed with its declared build backend. The
30
+ wheel and source distribution are checked separately so a successful wheel
31
+ install cannot hide a broken source package. `fail-fast: false` lets every
32
+ native cell report its own result; it does not relax the aggregate dependency.
33
+
34
+ ## Release dependency
35
+
36
+ The release job still requires the build and every installed-artifact cell.
37
+ Only an explicitly authorized `release: v<version>` commit on `main` can create
38
+ a GitHub Release. Existing release assets are checked against the local bytes
39
+ before a missing asset is uploaded; existing assets are never replaced.
40
+
41
+ Ordinary commits do not create tags, GitHub Releases or PyPI uploads. These
42
+ checks do not establish live provider behavior, Hermes host installation,
43
+ Windows process semantics, migration safety or semantic quality.
44
+
45
+ ## Local reproduction
46
+
47
+ From a clean checkout with the declared build backend available:
48
+
49
+ ```sh
50
+ python -m compileall -q src
51
+ python -m build --wheel --sdist
52
+ ```
53
+
54
+ For an isolated local import check, create a temporary virtual environment and
55
+ install the wheel and source archive separately with `--no-deps`. Do not add
56
+ local test files to this repository; they are intentionally ignored and are not
57
+ part of the release artifacts.
@@ -134,12 +134,9 @@ use the controlled stop-write upgrade procedure before resuming pending work.
134
134
  Malformed validity types raise controlled validation errors. Existing scanning
135
135
  can skip the invalid record and keep unrelated records usable; the public integrity envelope reports malformed and conflicting records with
136
136
  bounded snapshot validation. Explicit writes need a provably unique target and
137
- can conservatively block where ordinary reads continue with incomplete diagnostics. Public tests are included in the source distribution and use temporary
138
- Vaults only, with no model or network access:
139
-
140
- ```bash
141
- PYTHONPATH=src python -m unittest discover -s tests_public -p 'test_*.py' -v
142
- ```
137
+ can conservatively block where ordinary reads continue with incomplete diagnostics.
138
+ The public repository does not store test files; package installation and source
139
+ syntax are checked by CI, while semantic acceptance runs in a separate environment.
143
140
 
144
141
  ## Current revision-target integrity (unreleased)
145
142
 
@@ -5,9 +5,9 @@ The online candidate path uses one incremental planner and at most two durable
5
5
  request reservations per source/authorization work. It does not retain the old
6
6
  Gate -> summary -> semantic-review tail as an acceptance requirement. Legacy
7
7
  routes remain available for compatibility and deliberately stay the default
8
- until separately authorized migration. This document replaces the obsolete
9
- statement that deterministic tests are local-only: `tests_public/` is committed,
10
- in the sdist, and exercised by CI. A green CI is not live-model acceptance.
8
+ until separately authorized migration. The public repository keeps deterministic
9
+ test inputs and test code outside the tracked source tree. CI validates source
10
+ syntax and installed package artifacts; a green CI is not live-model acceptance.
11
11
 
12
12
  ## What runs, and what does not
13
13
 
@@ -157,10 +157,9 @@ Its OS matrix and explicit import/skip checks strengthen gate 1, not gates 2 or
157
157
  A workflow definition alone is not a passed native run; retain the per-commit
158
158
  reports and keep actual Hermes installation and live semantics separate.
159
159
 
160
- 1. **Deterministic contracts and artifact consistency.** Run `tests_public/` on
161
- source, sdist and an actually installed wheel, verifying import paths. Fixed
162
- oracle responses test the harness/runtime, not the model. Source and artifact
163
- implementation fingerprints must match before comparing results.
160
+ 1. **Artifact consistency.** Build the wheel and sdist once, then install both
161
+ exact artifacts in isolated native environments and verify package imports.
162
+ This does not replace semantic review or live host acceptance.
164
163
  2. **Live semantic judgement.** A structurally passing live run still records
165
164
  `semantic_status:not_reviewed`. Review each repetition against its own source
166
165
  and checklist: future value, non-invention, correct subject/scope, independent
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "memleaf"
7
- version = "0.2.69"
7
+ version = "0.2.71"
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.69"
3
+ __version__ = "0.2.71"
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
@@ -37,6 +37,7 @@ def _sync_source_metadata(messages: Any, user_content: str, assistant_content: s
37
37
  Strings supplied to sync_turn remain the only captured conversation bodies.
38
38
  """
39
39
  from datetime import datetime
40
+ from math import isfinite
40
41
 
41
42
  if not isinstance(messages, list) or not messages:
42
43
  return {}
@@ -55,8 +56,17 @@ def _sync_source_metadata(messages: Any, user_content: str, assistant_content: s
55
56
  value = item.get(name, item.get("id") if name == "message_id" else None)
56
57
  if isinstance(value, str) and 0 < len(value) <= 800 and not any(c in value for c in "\x00\r\n"):
57
58
  row[name] = value
58
- timestamp = item.get("source_time", item.get("timestamp"))
59
- if isinstance(timestamp, str) and len(timestamp) <= 80:
59
+ timestamp = item.get("source_time")
60
+ if timestamp is None:
61
+ timestamp = item.get("timestamp")
62
+ if type(timestamp) in (int, float) and isfinite(timestamp) and 0 <= timestamp < 253402300800:
63
+ # Hermes persists Unix seconds (not an ISO string). Keep the host's
64
+ # local offset: a UTC rendering can shift "today" across midnight.
65
+ try:
66
+ row["source_time"] = datetime.fromtimestamp(timestamp).astimezone().isoformat()
67
+ except (OverflowError, OSError, ValueError):
68
+ pass
69
+ elif isinstance(timestamp, str) and len(timestamp) <= 80:
60
70
  try:
61
71
  parsed = datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
62
72
  if parsed.tzinfo is not None and parsed.utcoffset() is not None:
@@ -1,5 +1,5 @@
1
1
  name: memleaf
2
- version: 0.2.69
2
+ version: 0.2.71
3
3
  description: "Hermes-native external MemoryProvider for local-first Markdown memory shared with the memleaf MCP server."
4
4
  hooks:
5
5
  - prefetch
@@ -0,0 +1,146 @@
1
+ """Calendar conversion for *model-selected* expressions, never deadline inference.
2
+
3
+ The incremental compiler supplies an exact supported expression. This module
4
+ retains it even outside the bounded calendar grammar; it never scans the whole
5
+ message to guess which date is an action deadline.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import re
10
+ from datetime import datetime
11
+ from typing import Any, Mapping
12
+
13
+ from .validation import calendar_tokens, normalize_relative_calendar_text, _RELATIVE_CALENDAR_EXPRESSION
14
+
15
+ _CLOCK_AFTER_DATE = re.compile(r"\s*(?:(?:日|号|上午|下午|晚上|中午|凌晨|约|在|at|T)\s*){0,2}(\d{1,2}:[0-5]\d)(?!\d)", re.IGNORECASE)
16
+
17
+
18
+ def _dated_clocks(text: str) -> set[tuple[str, str]]:
19
+ pairs = set()
20
+ for token in calendar_tokens(text):
21
+ clock = _CLOCK_AFTER_DATE.match(text, token.end)
22
+ if token.canonical and clock:
23
+ pairs.add((token.canonical, clock.group(1)))
24
+ return pairs
25
+
26
+
27
+ def parse_source_time(value: Any) -> datetime | None:
28
+ if value is None:
29
+ return None
30
+ if not isinstance(value, str):
31
+ raise ValueError("invalid_source_time")
32
+ try:
33
+ result = datetime.fromisoformat(value.replace("Z", "+00:00"))
34
+ except ValueError:
35
+ raise ValueError("invalid_source_time") from None
36
+ if result.tzinfo is None or result.utcoffset() is None:
37
+ raise ValueError("invalid_source_time")
38
+ return result
39
+
40
+
41
+ def reading_text(value: str) -> str:
42
+ # Presentation-only normalization. Keep parentheses, numbers and qualifiers.
43
+ return re.sub(r"\*\*|__|`", "", value)
44
+
45
+
46
+ def source_basis(evidence: Mapping[str, Any]) -> dict[str, Any]:
47
+ value = {key: evidence[key] for key in (
48
+ "source", "session_id", "event_key", "message_id", "message_revision",
49
+ "source_time", "source_sequence",
50
+ ) if key in evidence and evidence[key] is not None}
51
+ if evidence.get("explicit_input") is not None:
52
+ from .explicit_text_source import validate_origin
53
+ origin = validate_origin(evidence["explicit_input"])
54
+ value.update(input_kind="explicit_text", origin_session_id=origin["session_id"],
55
+ origin_turn_key=origin["turn_key"])
56
+ return value
57
+
58
+
59
+ def calendar_hints(evidence: Mapping[str, Any]) -> list[dict[str, str]]:
60
+ """Small, source-local calendar facts for the same model call (no processing clock)."""
61
+ anchor = parse_source_time(evidence.get("source_time"))
62
+ if anchor is None:
63
+ return []
64
+ text = reading_text(str(evidence["text"]))
65
+ result: list[dict[str, str]] = []
66
+ for match in _RELATIVE_CALENDAR_EXPRESSION.finditer(text):
67
+ resolved = normalize_relative_calendar_text(match.group(), anchor.date())
68
+ if resolved and resolved != match.group():
69
+ hint = {"text": match.group(), "date": resolved}
70
+ if hint not in result:
71
+ result.append(hint)
72
+ return result[:20]
73
+
74
+
75
+ def invalid_content_dates(fields: Mapping[str, Any], events: list[Mapping[str, Any]],
76
+ preserved: Mapping[str, Any] | None = None) -> bool:
77
+ """Reject unsupported generated dates, including misdated relative clock pairs.
78
+
79
+ Only cited sources and the selected update target may ground new wording.
80
+ Existing memory wording is preserved without a new source claim.
81
+ """
82
+ from .process_common import _summary_date_grounding_violations
83
+
84
+ source_texts = [reading_text(str(event["text"])) for event in events]
85
+ grounded: set[str] = set()
86
+ clock_dates: dict[str, set[str]] = {}
87
+ for event, source in zip(events, source_texts):
88
+ anchor = parse_source_time(event.get("source_time"))
89
+ local_day = anchor.date() if anchor is not None else None
90
+ normalized = normalize_relative_calendar_text(source, local_day) if local_day else None
91
+ grounded.update(token.canonical for token in calendar_tokens(source, local_day) if token.canonical)
92
+ if normalized is not None:
93
+ grounded.update(token.canonical for token in calendar_tokens(normalized, local_day) if token.canonical)
94
+ for match in _RELATIVE_CALENDAR_EXPRESSION.finditer(source):
95
+ clock = _CLOCK_AFTER_DATE.match(source, match.end())
96
+ if clock:
97
+ resolved = normalize_relative_calendar_text(match.group(), local_day)
98
+ if resolved:
99
+ clock_dates.setdefault(clock.group(1), set()).add(resolved)
100
+ # An explicit source date at the same clock makes a relative binding
101
+ # ambiguous; in that case the guard must not claim which event it is.
102
+ for date_value, clock_value in _dated_clocks(source):
103
+ clock_dates.setdefault(clock_value, set()).add(date_value)
104
+ prior = preserved or {}
105
+ texts = {key: fields[key] for key in ("title", "body") if key in fields}
106
+ if _summary_date_grounding_violations(texts, grounded_dates=grounded, source_texts=source_texts,
107
+ preserved_texts=[prior.get("title"), prior.get("body")]):
108
+ return True
109
+ preserved_pairs = set().union(*(_dated_clocks(text) for text in (prior.get("title"), prior.get("body"))
110
+ if isinstance(text, str)))
111
+ for value in texts.values():
112
+ for date_value, clock_value in _dated_clocks(value):
113
+ expected = clock_dates.get(clock_value, set())
114
+ if len(expected) == 1 and date_value not in expected and (date_value, clock_value) not in preserved_pairs:
115
+ return True
116
+ return False
117
+
118
+
119
+ def selected_calendar(text: str, evidence: Mapping[str, Any]) -> dict[str, Any]:
120
+ """Return a lossless text/anchor projection plus an optional calendar date.
121
+
122
+ Pass a local *date* to the legacy calendar grammar, which otherwise converts
123
+ datetime anchors to UTC. A midnight-offset message must keep its source day.
124
+ No configured processing clock or capture time is used as a substitute.
125
+ """
126
+ if not isinstance(text, str) or not text.strip() or len(text) > 512:
127
+ raise ValueError("invalid_time_expression")
128
+ if reading_text(text) not in reading_text(str(evidence.get("text", ""))):
129
+ raise ValueError("unsupported_time_quote")
130
+ anchor = parse_source_time(evidence.get("source_time"))
131
+ local_day = anchor.date() if anchor is not None else None
132
+ view = reading_text(text)
133
+ converted = normalize_relative_calendar_text(view, local_day)
134
+ if local_day is None and not _RELATIVE_CALENDAR_EXPRESSION.search(view):
135
+ converted = view
136
+ tokens = calendar_tokens(converted if converted is not None else view, local_day)
137
+ dates = {token.canonical for token in tokens}
138
+ # Relative expressions which cannot be resolved must not borrow another date.
139
+ resolved = (converted is not None and bool(tokens)
140
+ and None not in dates and len(dates) == 1)
141
+ return {
142
+ "date": next(iter(dates)) if resolved else None,
143
+ "text": text,
144
+ "anchor": source_basis(evidence),
145
+ "status": "resolved" if resolved else "unresolved",
146
+ }
@@ -15,7 +15,8 @@ from copy import deepcopy
15
15
  from dataclasses import dataclass
16
16
  from typing import Any, Mapping
17
17
 
18
- from .incremental_dates import parse_source_time, reading_text, selected_calendar, source_basis
18
+ from .incremental_dates import (calendar_hints, invalid_content_dates, parse_source_time,
19
+ reading_text, selected_calendar, source_basis)
19
20
  from .models import Memory
20
21
  from .scope_state import validate_scope_key
21
22
  from .turn_plan import revision_digest
@@ -352,6 +353,13 @@ class PlanningSnapshot:
352
353
  fields["observed"] = {}
353
354
  # No real memory IDs, revisions, local paths, source lists or journals.
354
355
  memories.append(fields)
356
+ projected_evidence = []
357
+ for event in state["evidence"]:
358
+ projection = {key: event[key] for key in ("ref", "use", "role", "text", "source_time", "source_sequence") if key in event}
359
+ hints = calendar_hints(event)
360
+ if hints:
361
+ projection["calendar_hints"] = hints
362
+ projected_evidence.append(projection)
355
363
  return {
356
364
  "protocol_version": PROTOCOL_VERSION, "request_kind": state["request_kind"],
357
365
  **({"retention_request": state["retention_request"]} if "retention_request" in state else {}),
@@ -359,8 +367,7 @@ class PlanningSnapshot:
359
367
  **({"scope_aliases": {reverse_scope[s]: a for s, a in state["scope_aliases"].items()}}
360
368
  if state.get("scope_aliases") else {}),
361
369
  "allow_new_scopes": state["allow_new_scopes"], "context_complete": state["context_complete"],
362
- "evidence": [{key: e[key] for key in ("ref", "use", "role", "text", "source_time", "source_sequence") if key in e}
363
- for e in state["evidence"]], "memories": memories,
370
+ "evidence": projected_evidence, "memories": memories,
364
371
  }
365
372
 
366
373
 
@@ -495,6 +502,9 @@ def _parse_row(row: Any, state: Mapping[str, Any]) -> dict[str, Any]:
495
502
  if key in fields:
496
503
  _text(fields[key], 256 if key == "title" else 16384,
497
504
  empty=key == "body" and fields.get("validity") == "retracted")
505
+ if invalid_content_dates(fields, [evidence[ref] for ref in refs],
506
+ state["targets"][result["target_ref"]]["memory"] if action == "UPDATE" else None):
507
+ raise ValueError("ungrounded_content_date")
498
508
  if "status" in fields and fields["status"] not in ("active", "completed", "cancelled"):
499
509
  raise ValueError("invalid_status")
500
510
  if "validity" in fields and fields["validity"] not in ("valid", "retracted"):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memleaf
3
- Version: 0.2.69
3
+ Version: 0.2.71
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.69。**
26
+ > **版本:0.2.71。**
27
27
  > 自动提炼和复核共用同一条“未来记忆价值”标准:模型综合未来复用、信息增量、再次读取时的直接可用性和忘记成本,只保留对未来理解、判断或行动有实质影响的最小核心;没有明确价值的信息不提炼,不按具体业务场景硬编码排除。
28
28
  > 提炼契约现在只有一份中文语义契约(旧版英文 B3 提示词已删除),首轮、主题选择、候选修复和 `semantic_maintenance` 使用同一套保留价值判断;同一件事只写一次。
29
29
  > Core 不再因为归属、日期、任务依据或跨项目而丢弃整条候选:归属缺失会归一到证据里唯一被点名的项目、否则落 `global`;日期只接受能在本候选证据里找到出处、且能锚定成 ISO 的写法(`明天`、`周五`、`月底` 都会被解析),无法解析时只丢掉日期字段,记忆照常写入;未获授权的改归属沿用目标原归属。
@@ -168,70 +168,4 @@ src/memleaf/llm/claude_compatible.py
168
168
  src/memleaf/llm/gemini.py
169
169
  src/memleaf/llm/openai_compatible.py
170
170
  src/memleaf/llm/router.py
171
- src/memleaf/llm/thinking.py
172
- tests_public/acceptance_oracles.py
173
- tests_public/candidate_update_fixtures.py
174
- tests_public/incremental_test_support.py
175
- tests_public/platform_smoke.py
176
- tests_public/release_assets.py
177
- tests_public/run_contracts.py
178
- tests_public/test_a083_regressions.py
179
- tests_public/test_a47_regressions.py
180
- tests_public/test_acceptance.py
181
- tests_public/test_acceptance_boundaries.py
182
- tests_public/test_artifact_verification.py
183
- tests_public/test_body_compaction.py
184
- tests_public/test_candidate_reconciliation.py
185
- tests_public/test_contract_reporter.py
186
- tests_public/test_control_absence_contract.py
187
- tests_public/test_create_memory_contract.py
188
- tests_public/test_development_closeout.py
189
- tests_public/test_explicit_memory_update.py
190
- tests_public/test_explicit_update_contract.py
191
- tests_public/test_g1_memory_contract.py
192
- tests_public/test_g2_source_contract.py
193
- tests_public/test_hermes_native_provision.py
194
- tests_public/test_hermes_source_metadata.py
195
- tests_public/test_incremental_commit.py
196
- tests_public/test_incremental_configured_runtime.py
197
- tests_public/test_incremental_dates.py
198
- tests_public/test_incremental_execution.py
199
- tests_public/test_incremental_execution_recovery.py
200
- tests_public/test_incremental_native.py
201
- tests_public/test_incremental_partial.py
202
- tests_public/test_incremental_partial_boundaries.py
203
- tests_public/test_incremental_partial_recovery.py
204
- tests_public/test_incremental_preview.py
205
- tests_public/test_incremental_protocol.py
206
- tests_public/test_incremental_recovery.py
207
- tests_public/test_incremental_remember.py
208
- tests_public/test_incremental_remember_recovery.py
209
- tests_public/test_incremental_scope_recovery.py
210
- tests_public/test_incremental_scopes.py
211
- tests_public/test_inspection_read_budget.py
212
- tests_public/test_mcp_retrieval_fallback.py
213
- tests_public/test_migration_backup.py
214
- tests_public/test_migration_runtime.py
215
- tests_public/test_native_read_stability.py
216
- tests_public/test_planner_provenance_contract.py
217
- tests_public/test_platform_contracts.py
218
- tests_public/test_processed_state_contract.py
219
- tests_public/test_processing_entrypoints.py
220
- tests_public/test_processing_route.py
221
- tests_public/test_processing_route_recovery.py
222
- tests_public/test_provider_compatibility.py
223
- tests_public/test_public_query_integrity.py
224
- tests_public/test_query_benchmark.py
225
- tests_public/test_query_clock.py
226
- tests_public/test_query_progress.py
227
- tests_public/test_query_scan_reuse.py
228
- tests_public/test_release_assets.py
229
- tests_public/test_remember_route.py
230
- tests_public/test_remember_route_recovery.py
231
- tests_public/test_revision_target_integrity.py
232
- tests_public/test_runtime_retention.py
233
- tests_public/test_runtime_retention_observation.py
234
- tests_public/test_runtime_retention_recovery.py
235
- tests_public/test_source_followup_contract.py
236
- tests_public/test_vault_binding_contract.py
237
- tests_public/verify_distribution.py
171
+ src/memleaf/llm/thinking.py