bantamkit 0.32.0__tar.gz → 0.32.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. {bantamkit-0.32.0 → bantamkit-0.32.1}/PKG-INFO +1 -1
  2. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/__init__.py +1 -1
  3. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/mcpserver.py +113 -0
  4. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/memory/component.py +51 -9
  5. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/memory/store.py +43 -2
  6. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_status_surface.py +18 -3
  7. {bantamkit-0.32.0 → bantamkit-0.32.1}/.gitignore +0 -0
  8. {bantamkit-0.32.0 → bantamkit-0.32.1}/README.md +0 -0
  9. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/contracts/default.yaml +0 -0
  10. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/manifest.yaml +0 -0
  11. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/HISTORY.md +0 -0
  12. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/README.md +0 -0
  13. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/docs/architecture.md +0 -0
  14. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/docs/runbook.md +0 -0
  15. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/issues/142-settlement-timeout.md +0 -0
  16. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/patches/0009-retry-budget.patch +0 -0
  17. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/src/ledger/__init__.py +0 -0
  18. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/src/ledger/config.py +0 -0
  19. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/src/ledger/errors.py +0 -0
  20. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/src/ledger/posting.py +0 -0
  21. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/src/ledger/registry.py +0 -0
  22. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/src/ledger/report.py +0 -0
  23. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/src/ledger/retry.py +0 -0
  24. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/src/ledger/settle.py +0 -0
  25. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/src/ledger/validate.py +0 -0
  26. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/tests/test_posting.py +0 -0
  27. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/repo/tests/test_settle.py +0 -0
  28. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/tasks/dt-error-contract.yaml +0 -0
  29. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/tasks/dt-handler-map.yaml +0 -0
  30. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/tasks/dt-patch-before-after.yaml +0 -0
  31. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/tasks/dt-retry-attempts.yaml +0 -0
  32. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/tasks/dt-settlement-config.yaml +0 -0
  33. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/tasks/dt-symbol-home.yaml +0 -0
  34. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/tasks/dt-trace-blame.yaml +0 -0
  35. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/devteam/tasks/dt-unread-key.yaml +0 -0
  36. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/document/tasks/doc-large-in-137.yaml +0 -0
  37. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/document/tasks/doc-large-in-359.yaml +0 -0
  38. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/document/tasks/doc-large-in-372.yaml +0 -0
  39. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/document/tasks/doc-large-out-11764.yaml +0 -0
  40. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/document/tasks/doc-large-out-4137.yaml +0 -0
  41. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/document/tasks/doc-large-out-8022.yaml +0 -0
  42. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/document/tasks/doc-small-137.yaml +0 -0
  43. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/document/tasks/doc-small-261.yaml +0 -0
  44. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/document/tasks/doc-small-388.yaml +0 -0
  45. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/fixtures/.gitkeep +0 -0
  46. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/fixtures/catalog.json +0 -0
  47. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/perturbations/task-completion.yaml +0 -0
  48. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/.gitkeep +0 -0
  49. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/extract-contact.yaml +0 -0
  50. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/extract-invoice.yaml +0 -0
  51. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/extract-order.yaml +0 -0
  52. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/extract-schedule.yaml +0 -0
  53. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/extract-versions.yaml +0 -0
  54. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/nav-prod-port.yaml +0 -0
  55. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/nav-release-bundle.yaml +0 -0
  56. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/recall-audit-retention.yaml +0 -0
  57. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/recall-cache-ttl.yaml +0 -0
  58. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/recall-db-port.yaml +0 -0
  59. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/recall-deploy.yaml +0 -0
  60. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/recall-env-endpoint.yaml +0 -0
  61. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/recall-oncall-rotation.yaml +0 -0
  62. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/recall-oncall.yaml +0 -0
  63. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/recall-org-quota.yaml +0 -0
  64. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/recall-owner.yaml +0 -0
  65. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/shop-basket-total.yaml +0 -0
  66. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/shop-cheapest.yaml +0 -0
  67. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/shop-compare.yaml +0 -0
  68. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/shop-gadget-value.yaml +0 -0
  69. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/shop-stock-total.yaml +0 -0
  70. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/evals/tasks/shop-total.yaml +0 -0
  71. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/pricing/default.json +0 -0
  72. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/profiles/default.yaml +0 -0
  73. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/profiles/patient.yaml +0 -0
  74. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/rubrics/.gitkeep +0 -0
  75. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/rubrics/code-quality.yaml +0 -0
  76. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/rubrics/grounded-completion.yaml +0 -0
  77. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/rubrics/task-completion.yaml +0 -0
  78. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/schemas/shiftwork-checkpoint.json +0 -0
  79. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/skills/.gitkeep +0 -0
  80. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/skills/file-graph.md +0 -0
  81. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/skills/memory.md +0 -0
  82. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/.gitkeep +0 -0
  83. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/bantamkit_read.json +0 -0
  84. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/bantamkit_status.json +0 -0
  85. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/build_identity.json +0 -0
  86. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/document_list.json +0 -0
  87. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/document_read.json +0 -0
  88. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/file_graph.json +0 -0
  89. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/memory_compact.json +0 -0
  90. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/memory_dream.json +0 -0
  91. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/memory_recall.json +0 -0
  92. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/memory_save.json +0 -0
  93. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/repo_map.json +0 -0
  94. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/shiftwork_clock_in.json +0 -0
  95. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/shiftwork_clock_out.json +0 -0
  96. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/shiftwork_status.json +0 -0
  97. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/skill_audit.json +0 -0
  98. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/token_ledger.json +0 -0
  99. {bantamkit-0.32.0 → bantamkit-0.32.1}/_assets/tools/validate_json.json +0 -0
  100. {bantamkit-0.32.0 → bantamkit-0.32.1}/hatch_build.py +0 -0
  101. {bantamkit-0.32.0 → bantamkit-0.32.1}/pyproject.toml +0 -0
  102. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/agent.py +0 -0
  103. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/assets.py +0 -0
  104. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/budget.py +0 -0
  105. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/client.py +0 -0
  106. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/contract.py +0 -0
  107. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/criticreplay.py +0 -0
  108. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/critique.py +0 -0
  109. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/docmanifest.py +0 -0
  110. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/docread.py +0 -0
  111. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/evalrun.py +0 -0
  112. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/eventlog.py +0 -0
  113. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/filegraph.py +0 -0
  114. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/hostinstall.py +0 -0
  115. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/loopguard.py +0 -0
  116. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/mcpreport.py +0 -0
  117. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/memory/__init__.py +0 -0
  118. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/memory/__main__.py +0 -0
  119. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/memory/divergence.py +0 -0
  120. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/memory/dream.py +0 -0
  121. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/memory/layers.py +0 -0
  122. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/pdfread.py +0 -0
  123. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/pricing.py +0 -0
  124. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/profile.py +0 -0
  125. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/repomap.py +0 -0
  126. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/selfupdate.py +0 -0
  127. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/shiftwork.py +0 -0
  128. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/skillaudit.py +0 -0
  129. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/statusline.py +0 -0
  130. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/structured.py +0 -0
  131. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/textutil.py +0 -0
  132. {bantamkit-0.32.0 → bantamkit-0.32.1}/src/bantamkit/tokenledger.py +0 -0
  133. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/cli_exit_status_probe.py +0 -0
  134. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/conftest.py +0 -0
  135. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/docread/bad-crc.docx +0 -0
  136. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/docread/charref-4301-digits.html +0 -0
  137. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/docread/charset-table.json +0 -0
  138. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/docread/compression-method-9.docx +0 -0
  139. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/docread/corrupt-deflate.docx +0 -0
  140. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/docread/encrypted-member.docx +0 -0
  141. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/docread/encrypted-mimetype.odt +0 -0
  142. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/docread/eszett-cell-ref.xlsx +0 -0
  143. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/docread/internal-dtd-entity.docx +0 -0
  144. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/docread/rfc2231-charset.eml +0 -0
  145. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/docread/rfc822-nested-twice.eml +0 -0
  146. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/docread/unicode-digit-shared-string.xlsx +0 -0
  147. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/docread/x-uuencode.eml +0 -0
  148. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/f8404ab-perturbation-baseline.json +0 -0
  149. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/platform-assumption-baseline.json +0 -0
  150. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/data/served-tool-surface.json +0 -0
  151. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/docread_fixtures.py +0 -0
  152. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/perturbation_baseline_harness.py +0 -0
  153. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/rbp16_effect_probe.py +0 -0
  154. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/rbp18_payload_probe.py +0 -0
  155. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_adapter.py +0 -0
  156. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_agent.py +0 -0
  157. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_amendguard.py +0 -0
  158. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_bantamkit_read_tool.py +0 -0
  159. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_budget.py +0 -0
  160. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_build_identity.py +0 -0
  161. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_client.py +0 -0
  162. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_compaction_corpus_survey.py +0 -0
  163. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_conformance.py +0 -0
  164. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_conformance_harness_resilience.py +0 -0
  165. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_conformance_suite_table_gate.py +0 -0
  166. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_contract_fanout.py +0 -0
  167. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_criticreplay.py +0 -0
  168. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_critique.py +0 -0
  169. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_doc_commands_gate.py +0 -0
  170. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_docread.py +0 -0
  171. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_docread_ceilings.py +0 -0
  172. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_document_manifest_parity.py +0 -0
  173. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_document_setup.py +0 -0
  174. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_document_tasks.py +0 -0
  175. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_document_tools.py +0 -0
  176. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_encoding_gate.py +0 -0
  177. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_evalrun.py +0 -0
  178. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_eventlog.py +0 -0
  179. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_field_program_gates.py +0 -0
  180. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_field_programs.py +0 -0
  181. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_filegraph.py +0 -0
  182. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_hostinstall.py +0 -0
  183. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_install_shape.py +0 -0
  184. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_ladder_statistics.py +0 -0
  185. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_launcher_which.py +0 -0
  186. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_layers.py +0 -0
  187. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_loopguard.py +0 -0
  188. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_mcp_endpoint.py +0 -0
  189. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_mcpdrift.py +0 -0
  190. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_mcpreport.py +0 -0
  191. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_mcpserver.py +0 -0
  192. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_memory.py +0 -0
  193. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_memory_compact_tool.py +0 -0
  194. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_memory_component.py +0 -0
  195. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_memory_divergence.py +0 -0
  196. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_memory_dream.py +0 -0
  197. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_memory_layers.py +0 -0
  198. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_memory_store_tripwire.py +0 -0
  199. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_mutmatrix.py +0 -0
  200. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_newline_gate.py +0 -0
  201. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_packaging.py +0 -0
  202. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_pdfread.py +0 -0
  203. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_pinharness_ledger.py +0 -0
  204. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_platform_assumption_gate.py +0 -0
  205. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_pricing.py +0 -0
  206. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_repo_map_tool.py +0 -0
  207. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_repomap.py +0 -0
  208. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_selfupdate.py +0 -0
  209. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_served_tool_count_records.py +0 -0
  210. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_shiftwork.py +0 -0
  211. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_skillaudit.py +0 -0
  212. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_statusline.py +0 -0
  213. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_structured.py +0 -0
  214. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_thread_exception_gate.py +0 -0
  215. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_tokenledger.py +0 -0
  216. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_tool_manifest.py +0 -0
  217. {bantamkit-0.32.0 → bantamkit-0.32.1}/tests/test_version_agreement.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: bantamkit
3
- Version: 0.32.0
3
+ Version: 0.32.1
4
4
  Summary: bantamweight tooling — harness primitives that lift small-model agents
5
5
  License-Expression: MIT
6
6
  Requires-Python: >=3.11
@@ -29,4 +29,4 @@ from bantamkit.structured import StructuredOutputError, extract_json, structured
29
29
  # file as its dynamic version source, so the wheel's metadata and the string the MCP
30
30
  # server advertises are the same committed bytes, and neither is a function of when
31
31
  # someone last ran `pip`.
32
- __version__ = "0.32.0"
32
+ __version__ = "0.32.1"
@@ -8,6 +8,7 @@ import hashlib
8
8
  import json
9
9
  import os
10
10
  import platform
11
+ import stat as stat_module
11
12
  import sys
12
13
  from collections.abc import Callable, Iterable, Iterator
13
14
  from contextlib import contextmanager
@@ -2042,10 +2043,80 @@ def _build_memory(args: argparse.Namespace) -> Memory:
2042
2043
  if args.store is not None:
2043
2044
  if not args.store:
2044
2045
  raise SystemExit("--store requires a non-empty path")
2046
+ _check_store_flag(args.store)
2045
2047
  return Memory(store=args.store, k=args.k, index_budget=args.index_budget)
2046
2048
  return Memory.layered(start=args.start, k=args.k, index_budget=args.index_budget)
2047
2049
 
2048
2050
 
2051
+ def _check_store_flag(raw: str) -> None:
2052
+ """`--store` may name a store that is missing, never one that is not a store.
2053
+
2054
+ THIS CHECK IS RESTORED, NOT INVENTED, and the distinction is the point. Until the
2055
+ project layer was built lazily there WAS a `--store` validation, and it was entirely
2056
+ accidental: the constructor ran `mkdir(parents=True)`, so a `--store` pointing at a
2057
+ regular file or at a path under a directory that does not exist took the process down
2058
+ with an `OSError` traceback out of `pathlib`. `test_statusline.py::
2059
+ test_the_flag_returns_before_anything_a_server_would_touch` depends on it -- it arms
2060
+ `--store <a regular file>` as a trap and shows `--statusline` walking past it -- and a
2061
+ lazy layer disarms the trap by making that same argv exit 0. The honest way to keep
2062
+ that test true is to mean the refusal on purpose.
2063
+
2064
+ IT IS `layers._pinned_store`'s CHECK MINUS ONE ARM, and the missing arm is deliberate
2065
+ and measured. The pin refuses a path that is not there ("nothing was created"); this
2066
+ flag does NOT, and must not, for two reasons that are both already pinned elsewhere in
2067
+ this repository:
2068
+
2069
+ - The sibling CLI's identical flag is contracted to CREATE one. `tools/conformance/
2070
+ suites/memorycli.mjs` carries `status-creates-a-missing-store`, `--store {BED}/nowhere`
2071
+ over an empty bed, whose whole job is to say the two runtimes create the same two
2072
+ directories there. Refusing a missing `--store` here would put two flags of the same
2073
+ name, in two programs of the same product, in direct contradiction.
2074
+ - "Missing" is no longer a broken state anywhere in this program. The walk designates a
2075
+ project store without creating it, and the first save brings it into existence or
2076
+ refuses by name (`store._ensure_dirs`). A `--store` at a path that is not there is
2077
+ that same designated state, reached by being told instead of by searching. MEASURED:
2078
+ requiring existence here reddened 14 tests that have nothing to do with this defect
2079
+ -- `test_tool_manifest.py` ×7, `test_mcpserver.py` ×6, `test_mcp_endpoint.py` -- every
2080
+ one of them a fixture naming a store under `tmp_path` it never made, because that is
2081
+ what this flag has always meant.
2082
+
2083
+ What is left is the arm the accident actually covered and the only one it covered: a
2084
+ `--store` that names something which EXISTS and is NOT A DIRECTORY. That is not a store
2085
+ and never becomes one -- `mkdir` under it is ENOTDIR on both runtimes and on every
2086
+ platform, the one row of the errno table where they already agree -- so it is refused
2087
+ here, by name, before a transport exists.
2088
+
2089
+ `FileNotFoundError` and not an errno comparison: the absent case is selected by the
2090
+ exception CPython raises for it, which `runtime-ts`' `pyfs` shim raises under the same
2091
+ name. Every other `stat` failure -- a permission wall on the parent, a symlink loop --
2092
+ is a real fault about a path the operator named, and it keeps the pin's sentence.
2093
+
2094
+ `os.stat` rather than `Path.is_dir()`, and that too is `_pinned_store`'s reasoning:
2095
+ `is_dir()` swallows `PermissionError` and answers False, which would report an
2096
+ operator's real store as a typo. A single named path gets the accurate reason.
2097
+
2098
+ `SystemExit` and not `MemoryValidationError`, so that both halves of this flag's
2099
+ refusal family read the same way on the terminal: `--store requires a non-empty path`
2100
+ is already bare on stderr at exit 1, and the port spells both as `Refusal`.
2101
+ """
2102
+ # NOT `expanduser()`, unlike the pin. `Memory(store=...)` builds `Path(root)` from this
2103
+ # string verbatim, so expanding here would check one path and serve another: `--store
2104
+ # ~/x` left unexpanded by the shell would pass a check against the home directory and
2105
+ # then build a store in a directory literally named `~`. The check must stat the path
2106
+ # the store is going to be.
2107
+ store = Path(raw)
2108
+ try:
2109
+ info = os.stat(store)
2110
+ except FileNotFoundError:
2111
+ return # designated, not broken: the first save makes it or refuses by name
2112
+ except OSError as e:
2113
+ raise SystemExit(
2114
+ f"--store is unreachable: {store}: {e.strerror}; nothing was created"
2115
+ ) from e
2116
+ if not stat_module.S_ISDIR(info.st_mode):
2117
+ raise SystemExit(f"--store is not a directory: {store}")
2118
+
2119
+
2049
2120
  def _print_mcp_report(args: argparse.Namespace) -> None:
2050
2121
  """`--mcp-report`: the joined report on stdout, then return. No transport, no server.
2051
2122
 
@@ -2184,9 +2255,43 @@ def _refuse_update(sentence: str) -> NoReturn:
2184
2255
 
2185
2256
 
2186
2257
  def main() -> None:
2258
+ """Dispatch one invocation, and hand a refusal to the operator as a SENTENCE.
2259
+
2260
+ THIS ARM IS A PARITY DEFECT BEING CLOSED, not a nicety. `runtime-ts/src/cli.ts` has
2261
+ caught `BantamError` at its top level and written `bantamkit-mcp: <message>` at exit 1
2262
+ since it was written, and `bantamkit/memory/__main__.py` was handed the same defect and
2263
+ fixed the same way for the operator memory CLI. This CLI never got it, so the identical
2264
+ refusal -- `BANTAMKIT_MEMORY_DIR=/nope/pinned`, one sentence that already names the
2265
+ directory and says nothing was created -- was one clean line on Node and a two-stage
2266
+ CPython traceback on Python, carrying this repository's absolute paths and line numbers
2267
+ in place of the store the operator asked about. Nothing covered it, so the suite was
2268
+ green over it.
2269
+
2270
+ `BantamError` AND NOTHING WIDER, in `__main__.py`'s own words: a bug in bantamkit is
2271
+ still a traceback, because that one IS a report for a maintainer. What is caught is the
2272
+ class of failures that are ABOUT the operator's store, and every one of them already
2273
+ carries a sentence that names the directory and says what the consequence would have
2274
+ been -- including `_ensure_dirs`' new one, which is the whole reason a cwd of `/` is now
2275
+ a refused write rather than a dead process.
2276
+
2277
+ `SystemExit` is deliberately not caught: argparse's usage errors are exit 2, and this
2278
+ file's own flag refusals are already their own sentence on stderr.
2279
+
2280
+ Written through `sys.stderr.buffer`, like every other refusal in this file, so the line
2281
+ ends LF on Windows too and cannot drift from the port's `process.stderr.write`.
2282
+ """
2187
2283
  if MCPServer is None:
2188
2284
  raise SystemExit(_INSTALL_HINT)
2189
2285
  args = _parse_args()
2286
+ try:
2287
+ _dispatch(args)
2288
+ except BantamError as e:
2289
+ sys.stderr.buffer.write(f"bantamkit-mcp: {e}\n".encode())
2290
+ sys.stderr.buffer.flush()
2291
+ raise SystemExit(1) from e
2292
+
2293
+
2294
+ def _dispatch(args: argparse.Namespace) -> None:
2190
2295
  # Before `_build_memory`, which touches the filesystem, and before the server exists at
2191
2296
  # all -- the Node arm returns from `main` here too, ahead of `new RawStdioTransport()`.
2192
2297
  if args.assets_root:
@@ -2215,6 +2320,14 @@ def main() -> None:
2215
2320
  # `.bantamkit/memory` directory in whatever cwd they were standing in, and the flags
2216
2321
  # above return before a transport for the same class of reason.
2217
2322
  #
2323
+ # AMENDMENT 2026-09-12 (job48, J48-1). "which is what creates a store" is no longer
2324
+ # true of the sentence's own words: the project layer is built `create=False`, so
2325
+ # `_build_memory` designates a path and creates nothing until a save. The guard stays
2326
+ # exactly where it is, for what remains of the reason -- a person typing a command to
2327
+ # see what it does has not asked for a walk up their filesystem either -- and it stays
2328
+ # because the ORDER is the contract the port shares, not because it is the last thing
2329
+ # standing between a bare invocation and a mkdir.
2330
+ #
2218
2331
  # STDOUT AND EXIT 0, i.e. byte-for-byte what `-h` does on this platform, because the
2219
2332
  # request was "ให้แสดงเหมือน --help" -- show it the way `--help` shows it. `print_help`
2220
2333
  # is the same call argparse's own `-h` action makes, so the two cannot diverge: fix
@@ -184,14 +184,38 @@ class Memory:
184
184
  index_budget: int = DEFAULT_INDEX_BUDGET,
185
185
  binding: StoreBinding | None = None,
186
186
  ):
187
- self.store = MemoryStore(store, index_budget=index_budget, k=k)
187
+ # `create=False`, AND IT IS THE WHOLE OF THE cwd-`/` FIX. This was the last eager
188
+ # layer -- grants and the profile store have always been lazy -- so a cwd that no
189
+ # directory can be created under was a STARTUP CRASH: every GUI MCP host launches
190
+ # its child with cwd `/`, measured live on this machine (`lsof -p <pid> -a -d cwd`
191
+ # on Claude Desktop and all four of its `bantamkit-mcp` children returns `/`), and
192
+ # the client saw only CONNECTION_CLOSED. Refusing at startup with a sentence was the
193
+ # other candidate and it is REFUTED by measurement: it leaves those same four
194
+ # processes dead, which is strictly worse than today. Built lazily, the server
195
+ # starts from `/`, binds `['project', 'profile']`, and answers out of the profile
196
+ # layer. The sentence still exists -- it is what `MemoryStore.save`/`compact` say
197
+ # when a write actually needs the directory (`store._ensure_dirs`).
198
+ #
199
+ # It is legal because every READ is already defined over an absent directory:
200
+ # `recall() -> []`, `archived() -> []`, `index_text() -> ''`. `_facts`' docstring
201
+ # says so outright, and `create=False` is one of the three callers it names.
202
+ #
203
+ # THE PRICE, stated rather than discovered: a bare server start in a perfectly good
204
+ # cwd no longer scatters `.bantamkit/memory/{facts,archive}` there before anything
205
+ # is saved. That moves TOWARD a stance this file already holds -- see the binding
206
+ # comment below, and `mcpserver.main`'s bare-TTY guard.
207
+ self.store = MemoryStore(store, index_budget=index_budget, k=k, create=False)
188
208
  self.k = k
189
209
  self._layers: list[tuple[str, MemoryStore, bool]] = [("project", self.store, True)]
190
210
  self._show_layers = False
191
- # How this store came to be the store, captured once. It cannot be re-derived
192
- # later: `MemoryStore(create=True)` above has already made the directory, so a
193
- # store that was only DESIGNATED a moment ago is indistinguishable on disk from
194
- # one that was found empty. `None` means the caller named the path outright --
211
+ # How this store came to be the store, captured once. It is still captured here and
212
+ # not re-derived later, but the reason has changed with the line above and the old
213
+ # one is recorded rather than deleted: it WAS that `MemoryStore(create=True)` had
214
+ # already made the directory, so a store that was only DESIGNATED a moment ago was
215
+ # indistinguishable on disk from one that was found empty. That complaint is now
216
+ # discharged -- nothing is created -- and the binding is kept because it also
217
+ # carries WHY this path (`origin`, `searched_from`), which no amount of looking at
218
+ # the disk recovers. `None` means the caller named the path outright --
195
219
  # `Memory(store=...)` — and no resolution happened to report.
196
220
  self._binding = binding
197
221
 
@@ -207,8 +231,15 @@ class Memory:
207
231
  `resolve_project_store` rather than `discover_project_store`: the path both
208
232
  return is the same path (`test_resolve_path_never_disagrees_with_discover`),
209
233
  but only the binding carries WHY it is that path and whether it holds
210
- anything — and `recall` cannot recover either fact afterwards, because
211
- constructing the store creates the directory.
234
+ anything — and `recall` cannot recover either fact afterwards.
235
+
236
+ AMENDMENT 2026-09-12 (job48, J48-1), correcting the reason the clause above used
237
+ to give and not the clause itself. WAS: "because constructing the store creates
238
+ the directory." NOW that is false -- the project layer is built `create=False`
239
+ (see `__init__`) and constructing it creates nothing. The binding is still the
240
+ only carrier of WHY, because `origin` and `searched_from` were never on the disk
241
+ to begin with; what the disk has stopped being able to answer is merely "was this
242
+ empty or brand new", which it now answers correctly for the first time.
212
243
  """
213
244
  binding = resolve_project_store(start)
214
245
  project_root = binding.path
@@ -553,9 +584,20 @@ class Memory:
553
584
  if binding is None:
554
585
  where = f"The project store {root} is empty."
555
586
  elif binding.state == "designated":
587
+ # AMENDED 2026-09-12 (job48, J48-1) because the sentence became FALSE, not
588
+ # because it read badly. WAS: "No memory store existed at or above <from>, so
589
+ # the empty <root> was created for this session." Under a lazily-built project
590
+ # layer nothing is created -- the path is designated and stays absent until a
591
+ # save needs it -- so the old sentence told an operator to go look for a
592
+ # directory that is not there. The new one borrows the pin's own closing
593
+ # clause, "nothing was created", because it is the same claim about the same
594
+ # thing, and it reuses the binding's existing `designated` vocabulary rather
595
+ # than minting a word for it. The remedy that follows -- "otherwise save a
596
+ # memory to start this one" -- is now literally true: the save is what brings
597
+ # the directory into existence.
556
598
  where = (
557
- f"No memory store existed at or above {binding.searched_from}, so the "
558
- f"empty {root} was created for this session."
599
+ f"No memory store existed at or above {binding.searched_from}, so {root} "
600
+ f"was designated for this session; nothing was created there."
559
601
  )
560
602
  elif binding.origin == "pin":
561
603
  where = f"The project store {root} is empty; {MEMORY_DIR_ENV} pinned it."
@@ -315,8 +315,40 @@ class MemoryStore:
315
315
  self._snapshot = previous
316
316
 
317
317
  def _ensure_dirs(self) -> None:
318
- (self.root / "facts").mkdir(parents=True, exist_ok=True)
319
- (self.root / "archive").mkdir(parents=True, exist_ok=True)
318
+ """Bring the store's directories into existence, or refuse the write that needs them.
319
+
320
+ THE PROPERTY: a store that cannot be brought into existence cannot be written to,
321
+ and the refusal names which directory and why. It is a refusal and not a crash
322
+ because the only callers are the two WRITES (`save`, `compact`); every read is
323
+ already defined over an absent directory -- `_facts`' own docstring rules it, "AN
324
+ ABSENT DIRECTORY IS `[]`, NOT AN ERROR" -- which is what makes a lazily-built
325
+ project layer legal at all.
326
+
327
+ THE PREDICATE IS THE OUTCOME AND NEVER AN ERRNO, and that is measured rather than
328
+ tidy. One cwd, `/`, gives CPython EROFS(30) at `/.bantamkit` and Node ENOENT(-2) at
329
+ `/.bantamkit/memory/facts`, because Node's recursive mkdir does not pass EROFS
330
+ through and stats the missing path instead; a `chmod 555` directory gives EACCES(13)
331
+ on both; and on Linux `/` is a writable root owned by root, so the same cwd gives
332
+ EACCES there. Three platforms, three numbers, one fact -- the directory could not be
333
+ made -- so the `except` is the whole of `OSError` and the sentence carries no errno.
334
+ A fix keyed on the number would have been green on Linux CI and wrong on the machine
335
+ the bug was reported from.
336
+
337
+ THE SENTENCE NAMES `self.root` AND NOTHING DEEPER, for the same reason. The two
338
+ runtimes fail at different components of the same path (`/.bantamkit` against
339
+ `/.bantamkit/memory/facts`) and report different strerrors for it, so a sentence
340
+ carrying the failing leaf, or that strerror, would be a divergence manufactured by
341
+ mkdir's internals. `self.root` is the path the caller named and both sides agree on
342
+ it, which is what lets one conformance case pin this line byte for byte.
343
+ """
344
+ try:
345
+ (self.root / "facts").mkdir(parents=True, exist_ok=True)
346
+ (self.root / "archive").mkdir(parents=True, exist_ok=True)
347
+ except OSError as e:
348
+ raise MemoryValidationError(
349
+ f"memory store could not be created: {self.root}; the directory is not"
350
+ " there and this filesystem would not make it, so nothing was written"
351
+ ) from e
320
352
 
321
353
  # ---- ops ----
322
354
 
@@ -542,6 +574,15 @@ class MemoryStore:
542
574
  archived by staleness and this loop still lands at or below `target`.
543
575
  """
544
576
  facts = self._facts()
577
+ # SECOND, NOT FIRST, and the order is the paragraph above kept intact: the listing
578
+ # still refuses an unreadable store before anything here moves a file. What this
579
+ # line adds is the other half of the same stance -- this method WRITES (every
580
+ # `replace` below lands in `archive/`, and `_rebuild_index` writes `index.md` into
581
+ # the root whether or not a single fact was archived), so a root that does not
582
+ # exist and cannot be made is refused here, by name, instead of surfacing as a
583
+ # `FileNotFoundError` out of the final write. `exist_ok=True` makes it free for
584
+ # every store that is already there.
585
+ self._ensure_dirs()
545
586
  sizes = {fact.name: len(self._index_line(fact).encode()) for fact in facts}
546
587
  if reserve is None:
547
588
  # The default reserve is measured from the WARNING LINE, not from the budget.
@@ -54,7 +54,20 @@ def run(coro):
54
54
 
55
55
 
56
56
  def make_memory(tmp_path, **kwargs) -> Memory:
57
- return Memory(store=tmp_path / "store", **kwargs)
57
+ """A `Memory` whose store is ON DISK, because every test below breaks one by hand.
58
+
59
+ THE `mkdir` IS THE FIXTURE'S OWN AS OF job48, and it is a fixture change and not a
60
+ weakened assertion. The project layer is built `create=False` -- a cwd that nothing can
61
+ be created under must be a refused write and not a startup crash -- so constructing a
62
+ `Memory` no longer brings `facts/` and `archive/` into existence. The tests here all
63
+ start from a store that is already there (`_break_the_project_layer` rmtree's `facts/`;
64
+ the budget helper saves into it), so the directories they were relying on the
65
+ constructor for are made here instead. Not one assertion below moves.
66
+ """
67
+ root = tmp_path / "store"
68
+ (root / "facts").mkdir(parents=True, exist_ok=True)
69
+ (root / "archive").mkdir(parents=True, exist_ok=True)
70
+ return Memory(store=root, **kwargs)
58
71
 
59
72
 
60
73
  def status_of(server) -> str:
@@ -311,8 +324,10 @@ def test_an_unreadable_memory_layer_is_observed_and_named_by_kind_not_by_name(tm
311
324
  def test_a_granted_layer_is_reported_by_its_kind_and_never_by_its_grant_name(tmp_path):
312
325
  """The sharp case for the metadata rule: `extra:<name>` carries the operator's words."""
313
326
  grant = tmp_path / "teamdocs" / ".bantamkit" / "memory"
314
- Memory(store=grant)
315
- (grant / "facts").rmdir()
327
+ # Built here rather than by `Memory(store=grant)` for the reason `make_memory` gives:
328
+ # since job48 constructing a store creates nothing. The on-disk state this test
329
+ # actually needs is unchanged -- `archive/` a directory, `facts/` a regular file.
330
+ (grant / "archive").mkdir(parents=True)
316
331
  (grant / "facts").write_text("not a directory", encoding="utf-8")
317
332
  project_root = tmp_path / "proj"
318
333
  (project_root / ".bantamkit" / "memory").mkdir(parents=True)
File without changes
File without changes
File without changes
File without changes