zikaron 0.3.0__tar.gz → 0.3.2__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 (393) hide show
  1. {zikaron-0.3.0/zikaron.egg-info → zikaron-0.3.2}/PKG-INFO +112 -23
  2. zikaron-0.3.0/PKG-INFO → zikaron-0.3.2/README.md +106 -46
  3. {zikaron-0.3.0 → zikaron-0.3.2}/pyproject.toml +19 -19
  4. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_check_gate.py +2 -2
  5. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_cli_front_door.py +12 -4
  6. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_distribution.py +5 -2
  7. zikaron-0.3.2/tests/test_guard_claude_live.py +181 -0
  8. zikaron-0.3.2/tests/test_guard_docstring_examples.py +22 -0
  9. zikaron-0.3.2/tests/test_guard_main.py +289 -0
  10. zikaron-0.3.2/tests/test_guard_malformed.py +71 -0
  11. zikaron-0.3.2/tests/test_guard_nudge.py +82 -0
  12. zikaron-0.3.2/tests/test_guard_properties.py +104 -0
  13. zikaron-0.3.2/tests/test_guard_rule_table.py +134 -0
  14. zikaron-0.3.2/tests/test_guard_script_file.py +49 -0
  15. zikaron-0.3.2/tests/test_guard_selfmatch.py +327 -0
  16. zikaron-0.3.2/tests/test_guard_selfmatch_premise.py +99 -0
  17. zikaron-0.3.2/tests/test_guard_selfmatch_table.py +90 -0
  18. zikaron-0.3.2/tests/test_guard_texts.py +143 -0
  19. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_harness_table.py +17 -0
  20. zikaron-0.3.2/tests/test_hook_components.py +157 -0
  21. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_connect.py +76 -17
  22. zikaron-0.3.2/tests/test_hook_connect_edge_cases.py +273 -0
  23. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_connect_integration.py +77 -25
  24. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_connect_race.py +59 -47
  25. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_connect_real_service_integration.py +71 -46
  26. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_failure.py +13 -0
  27. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_push.py +261 -43
  28. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_rpc.py +120 -10
  29. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_stdlib_only.py +7 -6
  30. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_indexing_acquisition.py +4 -4
  31. zikaron-0.3.2/tests/test_install_components.py +379 -0
  32. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_install_limits.py +20 -3
  33. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_project_scope.py +18 -1
  34. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_diagnostics.py +16 -9
  35. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_diagnostics_integration.py +14 -6
  36. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_dispatch.py +35 -3
  37. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_encoder_failure_stop.py +20 -0
  38. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_lifecycle.py +144 -2
  39. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_lifecycle_integration.py +16 -1
  40. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_main.py +214 -3
  41. zikaron-0.3.2/tests/test_service_ownership.py +123 -0
  42. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_server.py +25 -5
  43. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/indexing/acquisition.py +3 -5
  44. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/indexing/encoder.py +5 -0
  45. zikaron-0.3.2/zikaron/guard/__init__.py +1 -0
  46. zikaron-0.3.2/zikaron/guard/chars.py +60 -0
  47. zikaron-0.3.2/zikaron/guard/decision.py +140 -0
  48. zikaron-0.3.2/zikaron/guard/expression.py +416 -0
  49. zikaron-0.3.2/zikaron/guard/forms.py +161 -0
  50. zikaron-0.3.2/zikaron/guard/grammar.py +292 -0
  51. zikaron-0.3.2/zikaron/guard/grep_filter.py +190 -0
  52. zikaron-0.3.2/zikaron/guard/heredoc.py +216 -0
  53. zikaron-0.3.2/zikaron/guard/inplace.py +136 -0
  54. zikaron-0.3.2/zikaron/guard/interpreter.py +124 -0
  55. zikaron-0.3.2/zikaron/guard/lexer.py +141 -0
  56. zikaron-0.3.2/zikaron/guard/limits.py +8 -0
  57. zikaron-0.3.2/zikaron/guard/literal_text.py +74 -0
  58. zikaron-0.3.2/zikaron/guard/main.py +135 -0
  59. zikaron-0.3.2/zikaron/guard/messages.py +150 -0
  60. zikaron-0.3.2/zikaron/guard/nudge.py +69 -0
  61. zikaron-0.3.2/zikaron/guard/override.py +84 -0
  62. zikaron-0.3.2/zikaron/guard/patterns.py +47 -0
  63. zikaron-0.3.2/zikaron/guard/position.py +166 -0
  64. zikaron-0.3.2/zikaron/guard/programs.py +39 -0
  65. zikaron-0.3.2/zikaron/guard/quoting.py +83 -0
  66. zikaron-0.3.2/zikaron/guard/redirects.py +89 -0
  67. zikaron-0.3.2/zikaron/guard/scratch.py +155 -0
  68. zikaron-0.3.2/zikaron/guard/script_file.py +238 -0
  69. zikaron-0.3.2/zikaron/guard/selfmatch.py +278 -0
  70. zikaron-0.3.2/zikaron/guard/start_text.py +19 -0
  71. zikaron-0.3.2/zikaron/guard/writes.py +236 -0
  72. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/harness/spec.py +34 -0
  73. zikaron-0.3.2/zikaron/hook/components.py +60 -0
  74. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/hook/connect.py +248 -128
  75. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/hook/failure.py +30 -22
  76. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/hook/limits.py +4 -4
  77. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/hook/main.py +58 -25
  78. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/hook/push.py +94 -67
  79. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/hook/rpc.py +55 -4
  80. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/hook/subagent_policy.py +6 -1
  81. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/hook/warm_helper.py +2 -1
  82. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/install/entries.py +68 -31
  83. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/install/main.py +39 -3
  84. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/install/targets.py +173 -37
  85. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/install/writer.py +6 -3
  86. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/knowledge/client.py +2 -1
  87. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/mcp/connection.py +5 -4
  88. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/mcp/errors.py +4 -4
  89. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/project/initialize.py +20 -14
  90. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/asyncio_compat.py +3 -3
  91. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/context.py +8 -0
  92. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/diagnostics.py +21 -5
  93. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/dispatch.py +13 -4
  94. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/lifecycle.py +104 -56
  95. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/log.py +17 -3
  96. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/main.py +87 -49
  97. zikaron-0.3.2/zikaron/service/ownership.py +109 -0
  98. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/paths.py +7 -1
  99. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/server.py +30 -14
  100. zikaron-0.3.0/README.md → zikaron-0.3.2/zikaron.egg-info/PKG-INFO +135 -17
  101. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron.egg-info/SOURCES.txt +44 -0
  102. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron.egg-info/entry_points.txt +1 -0
  103. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron.egg-info/requires.txt +5 -5
  104. zikaron-0.3.0/tests/test_hook_connect_edge_cases.py +0 -104
  105. {zikaron-0.3.0 → zikaron-0.3.2}/LICENSE +0 -0
  106. {zikaron-0.3.0 → zikaron-0.3.2}/setup.cfg +0 -0
  107. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_access_log.py +0 -0
  108. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_agent_scan.py +0 -0
  109. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_asyncio_compat.py +0 -0
  110. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_background_encoder.py +0 -0
  111. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_ci_workflow.py +0 -0
  112. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_clock.py +0 -0
  113. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_config_keys.py +0 -0
  114. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_config_resolution.py +0 -0
  115. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_consolidation_grouping.py +0 -0
  116. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_consolidation_invariants.py +0 -0
  117. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_consolidation_observability.py +0 -0
  118. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_consolidation_planning.py +0 -0
  119. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_consolidation_refusals.py +0 -0
  120. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_consolidation_serving.py +0 -0
  121. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_consolidation_snapshot_planning.py +0 -0
  122. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_consolidation_verbs.py +0 -0
  123. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_ddl.py +0 -0
  124. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_definition_of_done_sites.py +0 -0
  125. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_design_pointers_resolve.py +0 -0
  126. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_design_tables.py +0 -0
  127. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_doctor.py +0 -0
  128. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_embedder.py +0 -0
  129. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_error_codes.py +0 -0
  130. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_event_kinds.py +0 -0
  131. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_harness_detect.py +0 -0
  132. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_harness_store_scope.py +0 -0
  133. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_harness_tier_guard.py +0 -0
  134. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_envelope.py +0 -0
  135. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_main.py +0 -0
  136. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_spawn_warm.py +0 -0
  137. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_subagent_policy.py +0 -0
  138. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_tripwire.py +0 -0
  139. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_warm_helper.py +0 -0
  140. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_warm_helper_integration.py +0 -0
  141. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_hook_write_policy.py +0 -0
  142. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_indexing_chunking.py +0 -0
  143. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_indexing_encoder.py +0 -0
  144. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_indexing_integration.py +0 -0
  145. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_indexing_model_cache.py +0 -0
  146. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_indexing_writes.py +0 -0
  147. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_injected_prose_log.py +0 -0
  148. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_install_assets.py +0 -0
  149. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_install_claude_live.py +0 -0
  150. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_install_e2e.py +0 -0
  151. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_install_harness.py +0 -0
  152. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_install_main.py +0 -0
  153. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_install_takeover.py +0 -0
  154. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_install_targets.py +0 -0
  155. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_install_writer.py +0 -0
  156. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_build_invariants.py +0 -0
  157. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_build_log.py +0 -0
  158. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_build_row_ordering.py +0 -0
  159. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_builds.py +0 -0
  160. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_chunking.py +0 -0
  161. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_cli.py +0 -0
  162. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_cli_integration.py +0 -0
  163. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_counters.py +0 -0
  164. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_database.py +0 -0
  165. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_ddl.py +0 -0
  166. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_detach.py +0 -0
  167. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_files.py +0 -0
  168. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_git.py +0 -0
  169. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_groups.py +0 -0
  170. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_indexer.py +0 -0
  171. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_integration.py +0 -0
  172. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_invariants.py +0 -0
  173. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_lifecycle.py +0 -0
  174. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_lock.py +0 -0
  175. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_lock_race.py +0 -0
  176. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_meta.py +0 -0
  177. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_registry.py +0 -0
  178. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_repair.py +0 -0
  179. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_reporting.py +0 -0
  180. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_roots.py +0 -0
  181. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_scan.py +0 -0
  182. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_scan_git.py +0 -0
  183. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_search.py +0 -0
  184. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_state.py +0 -0
  185. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_text.py +0 -0
  186. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_vectors.py +0 -0
  187. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_walk.py +0 -0
  188. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_knowledge_writes.py +0 -0
  189. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_markdown_renders_as_written.py +0 -0
  190. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_mcp_connection_ambiguous_failure.py +0 -0
  191. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_mcp_connection_cancellation.py +0 -0
  192. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_mcp_connection_concurrency.py +0 -0
  193. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_mcp_connection_integration.py +0 -0
  194. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_mcp_connection_labels.py +0 -0
  195. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_mcp_consolidator_bridge.py +0 -0
  196. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_mcp_errors.py +0 -0
  197. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_mcp_main.py +0 -0
  198. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_mcp_server.py +0 -0
  199. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_mcp_spill.py +0 -0
  200. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_mcp_tool_descriptions.py +0 -0
  201. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_mcp_wire_contracts.py +0 -0
  202. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_meta.py +0 -0
  203. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_permissions.py +0 -0
  204. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_publication_hygiene.py +0 -0
  205. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_quoted_design_prose_is_verbatim.py +0 -0
  206. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_readme_config_table.py +0 -0
  207. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_readme_internal_links.py +0 -0
  208. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_records_memory.py +0 -0
  209. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_records_receipts.py +0 -0
  210. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_records_supersession.py +0 -0
  211. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_retrieval_arms.py +0 -0
  212. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_retrieval_eligibility.py +0 -0
  213. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_retrieval_integration.py +0 -0
  214. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_retrieval_query.py +0 -0
  215. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_retrieval_ranking.py +0 -0
  216. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_retrieval_reads.py +0 -0
  217. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_context.py +0 -0
  218. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_context_assemble.py +0 -0
  219. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_dispatch_consolidation.py +0 -0
  220. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_dispatch_knowledge.py +0 -0
  221. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_envelope.py +0 -0
  222. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_knowledge_management.py +0 -0
  223. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_log.py +0 -0
  224. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_params.py +0 -0
  225. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_paths.py +0 -0
  226. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_read_contention.py +0 -0
  227. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_rpc.py +0 -0
  228. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_security.py +0 -0
  229. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_surface_deadline.py +0 -0
  230. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_write_contention.py +0 -0
  231. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_service_writer.py +0 -0
  232. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_signals_contention.py +0 -0
  233. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_signals_dedup.py +0 -0
  234. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_signals_horizon.py +0 -0
  235. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_signals_repair.py +0 -0
  236. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_signals_retirement.py +0 -0
  237. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_signals_sessions.py +0 -0
  238. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_signals_writes.py +0 -0
  239. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_socket_path_budget.py +0 -0
  240. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_store.py +0 -0
  241. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_store_connection.py +0 -0
  242. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_store_migration.py +0 -0
  243. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_store_pool.py +0 -0
  244. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_transaction_begin_sites.py +0 -0
  245. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_version_seam.py +0 -0
  246. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_write_dedup.py +0 -0
  247. {zikaron-0.3.0 → zikaron-0.3.2}/tests/test_write_tools.py +0 -0
  248. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/__init__.py +0 -0
  249. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/cli/__init__.py +0 -0
  250. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/cli/main.py +0 -0
  251. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/__init__.py +0 -0
  252. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/clock.py +0 -0
  253. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/config/__init__.py +0 -0
  254. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/config/keys.py +0 -0
  255. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/config/resolution.py +0 -0
  256. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/consolidation/__init__.py +0 -0
  257. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/consolidation/authorization.py +0 -0
  258. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/consolidation/candidates.py +0 -0
  259. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/consolidation/context.py +0 -0
  260. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/consolidation/grouping.py +0 -0
  261. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/consolidation/groups.py +0 -0
  262. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/consolidation/payload.py +0 -0
  263. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/consolidation/planning.py +0 -0
  264. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/consolidation/rowstate.py +0 -0
  265. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/consolidation/runs.py +0 -0
  266. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/consolidation/serving.py +0 -0
  267. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/consolidation/verbs.py +0 -0
  268. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/errors.py +0 -0
  269. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/events.py +0 -0
  270. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/indexing/__init__.py +0 -0
  271. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/indexing/chunking.py +0 -0
  272. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/indexing/lexical.py +0 -0
  273. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/indexing/model_cache.py +0 -0
  274. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/indexing/model_pin.py +0 -0
  275. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/indexing/vectors.py +0 -0
  276. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/indexing/writes.py +0 -0
  277. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/__init__.py +0 -0
  278. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/arms.py +0 -0
  279. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/builds.py +0 -0
  280. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/candidates.py +0 -0
  281. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/changes.py +0 -0
  282. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/chunking.py +0 -0
  283. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/counters.py +0 -0
  284. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/database.py +0 -0
  285. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/ddl.py +0 -0
  286. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/disposal.py +0 -0
  287. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/errors.py +0 -0
  288. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/files.py +0 -0
  289. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/git.py +0 -0
  290. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/groups.py +0 -0
  291. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/lexical.py +0 -0
  292. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/lifecycle.py +0 -0
  293. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/lock.py +0 -0
  294. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/meta.py +0 -0
  295. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/paths.py +0 -0
  296. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/pending.py +0 -0
  297. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/registry.py +0 -0
  298. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/repair.py +0 -0
  299. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/reporting.py +0 -0
  300. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/roots.py +0 -0
  301. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/scan.py +0 -0
  302. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/search.py +0 -0
  303. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/state.py +0 -0
  304. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/text.py +0 -0
  305. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/vectors.py +0 -0
  306. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/walk.py +0 -0
  307. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/knowledge/writes.py +0 -0
  308. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/records/__init__.py +0 -0
  309. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/records/memory.py +0 -0
  310. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/records/receipts.py +0 -0
  311. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/records/supersession.py +0 -0
  312. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/retrieval/__init__.py +0 -0
  313. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/retrieval/arms.py +0 -0
  314. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/retrieval/block.py +0 -0
  315. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/retrieval/eligibility.py +0 -0
  316. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/retrieval/query.py +0 -0
  317. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/retrieval/ranking.py +0 -0
  318. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/retrieval/reads.py +0 -0
  319. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/retrieval/retrieve.py +0 -0
  320. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/retrieval/similarity.py +0 -0
  321. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/signals/__init__.py +0 -0
  322. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/signals/contention.py +0 -0
  323. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/signals/dedup.py +0 -0
  324. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/signals/horizon.py +0 -0
  325. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/signals/repair.py +0 -0
  326. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/signals/retirement.py +0 -0
  327. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/signals/sessions.py +0 -0
  328. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/signals/writes.py +0 -0
  329. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/store/__init__.py +0 -0
  330. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/store/connection.py +0 -0
  331. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/store/ddl.py +0 -0
  332. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/store/deadline.py +0 -0
  333. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/store/embedder.py +0 -0
  334. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/store/holders.py +0 -0
  335. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/store/meta.py +0 -0
  336. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/store/migration.py +0 -0
  337. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/store/permissions.py +0 -0
  338. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/store/pool.py +0 -0
  339. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/store/store.py +0 -0
  340. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/store/transactions.py +0 -0
  341. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/write/__init__.py +0 -0
  342. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/write/dedup.py +0 -0
  343. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/core/write/tools.py +0 -0
  344. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/doctor/__init__.py +0 -0
  345. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/doctor/checks.py +0 -0
  346. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/doctor/main.py +0 -0
  347. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/harness/__init__.py +0 -0
  348. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/harness/detect.py +0 -0
  349. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/hook/__init__.py +0 -0
  350. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/hook/envelope.py +0 -0
  351. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/hook/spawn_warm.py +0 -0
  352. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/hook/tripwire.py +0 -0
  353. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/hook/write_policy.py +0 -0
  354. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/install/__init__.py +0 -0
  355. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/install/__main__.py +0 -0
  356. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/install/agent_scan.py +0 -0
  357. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/install/assets.py +0 -0
  358. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/install/harness.py +0 -0
  359. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/install/ownership.py +0 -0
  360. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/knowledge/__init__.py +0 -0
  361. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/knowledge/__main__.py +0 -0
  362. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/knowledge/indexer/__init__.py +0 -0
  363. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/knowledge/indexer/__main__.py +0 -0
  364. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/knowledge/indexer/build_log.py +0 -0
  365. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/knowledge/indexer/detach.py +0 -0
  366. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/knowledge/indexer/main.py +0 -0
  367. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/knowledge/main.py +0 -0
  368. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/knowledge/scope.py +0 -0
  369. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/mcp/__init__.py +0 -0
  370. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/mcp/consolidator.py +0 -0
  371. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/mcp/main.py +0 -0
  372. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/mcp/primary.py +0 -0
  373. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/mcp/server.py +0 -0
  374. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/mcp/spill.py +0 -0
  375. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/mcp/tool_names.py +0 -0
  376. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/project/__init__.py +0 -0
  377. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/project/__main__.py +0 -0
  378. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/project/report.py +0 -0
  379. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/project/resolve.py +0 -0
  380. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/py.typed +0 -0
  381. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/__init__.py +0 -0
  382. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/access_log.py +0 -0
  383. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/dispatch_consolidation.py +0 -0
  384. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/dispatch_knowledge.py +0 -0
  385. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/envelope.py +0 -0
  386. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/methods.py +0 -0
  387. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/params.py +0 -0
  388. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/rpc.py +0 -0
  389. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/security.py +0 -0
  390. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/serialize.py +0 -0
  391. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron/service/serialize_knowledge.py +0 -0
  392. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron.egg-info/dependency_links.txt +0 -0
  393. {zikaron-0.3.0 → zikaron-0.3.2}/zikaron.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zikaron
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Memory and searchable project knowledge for coding agents
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/nathan-shapiro/Zikaron
@@ -11,16 +11,16 @@ Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
12
  Requires-Dist: aiosqlite==0.22.1
13
13
  Requires-Dist: sqlite-vec==0.1.9
14
- Requires-Dist: fastembed==0.8.0
15
- Requires-Dist: huggingface_hub==1.26.0
16
- Requires-Dist: fastmcp==3.4.7
14
+ Requires-Dist: fastembed==0.8.1
15
+ Requires-Dist: huggingface_hub==1.33.0
16
+ Requires-Dist: fastmcp==4.0.10
17
17
  Provides-Extra: dev
18
- Requires-Dist: ruff==0.16.7; extra == "dev"
18
+ Requires-Dist: ruff==0.16.9; extra == "dev"
19
19
  Requires-Dist: mypy==2.3.1; extra == "dev"
20
20
  Requires-Dist: pytest==9.1.1; extra == "dev"
21
21
  Requires-Dist: pytest-cov==7.1.0; extra == "dev"
22
22
  Requires-Dist: pytest-asyncio==1.4.0; extra == "dev"
23
- Requires-Dist: hypothesis==6.168.0; extra == "dev"
23
+ Requires-Dist: hypothesis==6.168.3; extra == "dev"
24
24
  Requires-Dist: pyyaml==6.0.3; extra == "dev"
25
25
  Requires-Dist: types-PyYAML==6.0.12.20260906; extra == "dev"
26
26
  Requires-Dist: build==1.3.0; extra == "dev"
@@ -61,6 +61,13 @@ yes, it is out of scope. That store is for what cost somebody time to discover,
61
61
  be discovered again by the next agent, at full price. A knowledge base has no such test — it is
62
62
  whatever corpus you point it at.
63
63
 
64
+ **And, on request, guards for Claude Code** — two deterministic hooks, independent of both stores.
65
+ One refuses two kinds of command: a find-replace edit to an authored file (`sed -i`, an inline
66
+ script, a heredoc into the file), pointing the agent at `Read` and `Edit`, and a `pgrep -f` or
67
+ `pkill -f` whose pattern matches the shell running it. The other reminds the agent, after each
68
+ edit, to re-read what it changed. See [Guards](#guards-claude-code-on-request); a project may install the memory store, the
69
+ guards, or both.
70
+
64
71
  It runs entirely on your machine: SQLite databases under `.zikaron/`, a local embedding model, and a
65
72
  small background service on a Unix socket. **Zikaron reaches the network for exactly one thing** —
66
73
  the embedding-model download described under [Install](#install), pinned to one revision and
@@ -124,6 +131,7 @@ reads which one it is addressing before it reads a description. Consolidation ne
124
131
  | **service** | A long-running process per project. Holds the embedding model in memory and the databases open, and answers requests over a Unix socket. Starts itself when needed and stops itself when idle. |
125
132
  | **MCP server** | Translates the agent's tool calls into requests to the service. One process per agent instance; loads no model. |
126
133
  | **hook** | A single-shot executable your agent runs on start, before each message, and — under Claude Code — when it spawns a subagent. Deliberately tiny; loads no model and never touches the databases directly. |
134
+ | **guard** | Claude Code only, installed on request: a single-shot executable the harness runs before each `Bash` or `Monitor` call and after each edit. Regular expressions over the call, plus one bounded read of a script the call runs from a scratch directory — no model, no service, no store. |
127
135
  | **indexer** | A detached process started per knowledge-base build — by the service on every `add`/`refresh`, whether an agent asked or you did, or by you running the foreground command a build result prints. It outlives the call that started it, loads a model, saturates a core for a minute or more, and exits when the build finishes. If you see one in `ps`, that is a corpus building, not a runaway. |
128
136
 
129
137
  The service exists for one measured reason: loading the embedding model costs about **780 ms**, and
@@ -235,6 +243,16 @@ cd /path/to/your/project
235
243
  zikaron install --project . --harness claude-code # or: --harness kiro
236
244
  ```
237
245
 
246
+ Under Claude Code, `--components` adds the [guards](#guards-claude-code-on-request), or
247
+ installs them alone; without it you get the memory store only, exactly as before:
248
+
249
+ ```bash
250
+ zikaron install --project . --harness claude-code --components both # memory and guards
251
+ zikaron install --project . --harness claude-code --components guards # guards only
252
+ ```
253
+
254
+ Installing one later keeps the other: an install touches only what it was asked for.
255
+
238
256
  **Name it on a first install, rather than leaving it to be detected.** Detection looks for a
239
257
  `.kiro/` or `.claude/` directory in the project and for `CLAUDECODE` in the environment, and it
240
258
  refuses rather than guessing when neither says. Running from an ordinary terminal, a first install
@@ -334,6 +352,12 @@ and under **Claude Code**:
334
352
  | `.claude/settings.local.json` | three hooks — session start, per-message, and per-subagent — plus both approval keys, `enabledMcpjsonServers` and `permissions.allow` |
335
353
  | `.mcp.json` | both servers: `zikaron` for your own tools, `zikaron-consolidator` for the consolidation verbs — each marked `alwaysLoad` so their descriptions are in context from the start rather than fetched per tool |
336
354
 
355
+ With `--components guards` the install writes `.claude/settings.local.json` alone, and in it only
356
+ hooks: the session-start and per-subagent hooks, which carry a short note about the guards, and the
357
+ two guard hooks — before each `Bash` or `Monitor` call, after each edit. No server, no approval
358
+ key, no consolidator. With `--components both` you get all of the above together, and the start hooks carry
359
+ the write policy and the guards' note.
360
+
337
361
  **`settings.local.json`, not `settings.json`**, and it matters if you commit your settings: the hook
338
362
  entries name absolute paths inside *your* virtualenv, so they are meaningless in anyone else's clone.
339
363
  The third hook is the one with no kiro counterpart — it hands the write policy to each subagent you
@@ -451,6 +475,48 @@ guard, the receipts and the lease are untouched — but an unauthorized consolid
451
475
  tokens and could write a poorly-judged long-term record. `permissions.deny` is **not** the fix: it is
452
476
  global and unregisters the tool, after which the consolidator itself refuses to start.
453
477
 
478
+ ### Guards (Claude Code, on request)
479
+
480
+ `--components guards` (or `both`) installs two deterministic hooks that hold an agent to three
481
+ rules, with no model and no store behind them (`design/guards.md`):
482
+
483
+ - **A find-replace edit to an authored file is refused** — `sed -i`, `perl -pi`, an inline Python
484
+ or node script that writes a file, a heredoc or `echo` redirected into one, and a script under
485
+ `/tmp` or `$TMPDIR` that the command runs and that does any of these. The refusal reaches the
486
+ agent as the tool's result and tells it to use `Read` and `Edit`, or `Write` for a new file.
487
+ Paths under `/tmp`, `/var/tmp` and `$TMPDIR` are exempt, and so is a command carrying the
488
+ override the refusal itself describes, for the files whose bytes a program owns.
489
+ - **After each edit, the agent is reminded** that the file has not been re-read, and asked for one
490
+ re-read of the edited material once that file's editing is done. A Bash command gets no such
491
+ reminder, so when the agent overrides a refusal, the guard's acknowledgement names what the
492
+ command changes and asks for that re-read instead.
493
+ - **A `pgrep -f` or `pkill -f` whose pattern matches its own command is refused.** Claude Code
494
+ runs each command inside a shell whose command line carries the command's text, so on Linux such
495
+ a pattern matches that shell: `pkill` kills it, and `pgrep` always finds it, so a check for a
496
+ process that has exited never sees it gone. The refusal shows where the pattern matched and gives
497
+ the fix: `-A`, where `pgrep` has it, or a bracketed character (`[m]onth_monitor.py`). On macOS,
498
+ whose `pgrep` already leaves out the shell, only `-a` brings it back.
499
+
500
+ Both refusals judge `Bash` and `Monitor` commands alike. An install made by an earlier version
501
+ judges `Bash` alone; re-running it with the same `--components` adds `Monitor`.
502
+
503
+ They are not a security boundary: a script the guard reads is one run from a scratch directory,
504
+ and a script anywhere else — one of the project's own programs — is never read, by design. A guard
505
+ that fails for any reason allows the call.
506
+
507
+ They write no log and no store record: a refusal, an override and its reason are in the session's
508
+ own transcript. `zikaron doctor` does not report on them. To see the refusal without a model in
509
+ the loop, hand the guard the payload Claude Code would send it, from the project directory — run
510
+ from a scratch directory such as `/tmp`, the same command is exempt and prints nothing:
511
+
512
+ ```bash
513
+ printf '%s' '{"hook_event_name":"PreToolUse","tool_name":"Bash","cwd":"'"$PWD"'","tool_input":{"command":"sed -i s/a/b/ README.md"}}' | zikaron-guard
514
+ ```
515
+
516
+ It prints the deny the agent would read as the command's result. Asking an agent to run `sed -i`
517
+ usually shows something else: told the rule when its session starts, it reaches for `Read` and
518
+ `Edit` without trying.
519
+
454
520
  ### Options
455
521
 
456
522
  | Flag | Effect |
@@ -462,6 +528,7 @@ global and unregisters the tool, after which the consolidator itself refuses to
462
528
  | `--model <id>` | the consolidator's model (default: `claude-sonnet-5` under kiro, `sonnet` under Claude Code) |
463
529
  | `--format {object,array}` | **kiro only.** Which hook format to write when the target config has none yet |
464
530
  | `--no-trust-tools` | do not pre-approve Zikaron's own tools, so every Zikaron tool call asks permission |
531
+ | `--components {memory,guards,both}` | what to install: the memory store (the default), the guards below, or both. **Claude Code only** for `guards` and `both`; `--model` is refused with `guards`, which ships no consolidator |
465
532
  | `--force` | replace a symlink at a shipped path, and overwrite entries wired to a different Zikaron install that would otherwise be refused. It also stops merging: a server entry — `.mcp.json`'s, or kiro's `mcpServers` — is replaced **whole**, so a key you added to Zikaron's own entry goes, and under Claude Code a Zikaron hook group is replaced whole, so a hook of your own inside it goes too. Both are named in the output |
466
533
 
467
534
  Both hook formats kiro accepts are supported, and a config that already uses one keeps it: kiro
@@ -508,7 +575,7 @@ write policy goes into the model's context, not to your terminal. Then, from the
508
575
  # a service should be running for this project after your first message
509
576
  pgrep -af zikaron.service.main
510
577
 
511
- # one line per hook failure, write-policy-override note, or session/environment mismatch; absent means none happened
578
+ # one line per hook failure, missed cold start, write-policy-override note, or session/environment mismatch; absent means none happened
512
579
  cat .zikaron/hook.log
513
580
  ```
514
581
 
@@ -699,9 +766,11 @@ it is `0600` or `0700`. The **installer** also writes harness config — the fil
699
766
  config.toml your per-project overrides, if you wrote any
700
767
  service.log the background service: startup, resolved config, errors, why it stopped,
701
768
  any request running longer than 30 s, and the stack dumps below
702
- hook.log one line per hook failure, write-policy-override note, or session/environment
703
- mismatch. Absent means none happened. Not every line is a failure: an override
704
- that is used but exceeds the injection budget is printed anyway and logged
769
+ hook.log one line per hook failure, missed cold start, write-policy-override note, or
770
+ session/environment mismatch. Absent means none happened. Not every line is a
771
+ failure: `starting` is a message that arrived while the service was still
772
+ starting, and an override that is used but exceeds the injection budget is
773
+ printed anyway and logged
705
774
  warmup.log the small helper that warms the service when a session starts
706
775
  write-policy.md your policy override, if you wrote one
707
776
  ```
@@ -714,19 +783,23 @@ shared by every project you use Zikaron in, under `$XDG_CACHE_HOME/zikaron/model
714
783
  A copy inside each project would duplicate it for bytes that are identical by construction, since the
715
784
  version fetched is pinned. Removing a project does not remove it; [Uninstall](#uninstall) says how.
716
785
 
717
- The second is the service's Unix socket, which with its lock file sits in
718
- `$XDG_RUNTIME_DIR/zikaron/` — or `/tmp/zikaron-<uid>/` where that variable is unset, which is every
719
- Mac — named by a hash of the store's path, `0600` inside a `0700` directory. A socket belongs on a
720
- runtime filesystem rather than in your project. **Under Claude
721
- Code**, an over-large consolidator result also spills to a file beside them, carrying that group's
722
- record prose verbatim and named by the same store hash; it is deleted when the consolidator asks for
723
- its next group — or, if that process is killed first, by the next one that starts. It is the one
724
- copy of memory text that lives outside `.zikaron/`, which is why the erasure procedure under
725
- [Secrets](#secrets) removes it by hand. Nothing spills under kiro.
786
+ The second is the service's Unix socket. It sits in `$XDG_RUNTIME_DIR/zikaron/` — or
787
+ `/tmp/zikaron-<uid>/` where that variable is unset, which is every Mac — named by a hash of the
788
+ store's path, `0600` inside a `0700` directory, beside two lock files: one clients take while they
789
+ start the service, and one the service holds while it has the store open. A socket belongs on a
790
+ runtime filesystem rather than in your project. **Under Claude Code**, an over-large consolidator
791
+ result also spills to a file beside them, carrying that group's record prose verbatim and named by
792
+ the same store hash; it is deleted when the consolidator asks for its next group — or, if that
793
+ process is killed first, by the next one that starts. It is the one copy of memory text that lives
794
+ outside `.zikaron/`, which is why the erasure procedure under [Secrets](#secrets) removes it by hand.
795
+ Nothing spills under kiro.
726
796
 
727
797
  **What `service.log` tells you about a service that stopped, or stopped answering.** A clean stop
728
- writes one `stopping: reason=…` line: `idle`, `store_replaced`, `encoder_failed`, or `sigterm` /
729
- `sigint` when it was asked to stop. A request still running after the 30 s idle poll is logged as
798
+ writes one `stopping: reason=…` line: `idle`, `store_replaced`, `socket_lost` (its socket file was
799
+ removed, so nothing could reach it), `encoder_failed`, or `sigterm` / `sigint` when it was asked to
800
+ stop. A start that found another service already holding the store writes `not starting:
801
+ reason=another_service` and exits, leaving that one to serve it. A request still running after the
802
+ 30 s idle poll is logged as
730
803
  `long request: method=… session=… age=…` on every poll until it ends — `session=unresolved` if it
731
804
  never got that far. Two signals write a dump without stopping the service: `kill -USR2 <pid>` writes
732
805
  the requests in flight and every asyncio task's stack, and `kill -USR1 <pid>` writes every thread's
@@ -735,8 +808,8 @@ raw text, without the timestamp and `pid=` every other line carries.
735
808
 
736
809
  Two notes on reading the rest. `memory.db` can look implausibly small while `memory.db-wal` is large —
737
810
  that is normal for a database held open by the service, since recent writes live in the log until a
738
- checkpoint; the files together are the store. And `hook.log` records only a fixed failure label and an
739
- error code, never your prompt or a memory's text, so it cannot hold a leaked secret.
811
+ checkpoint; the files together are the store. And `hook.log` records only fixed labels and an error
812
+ code, never your prompt or a memory's text, so it cannot hold a leaked secret.
740
813
 
741
814
  There is one more log that is not a file: an event table inside `memory.db`. It records what happened
742
815
  on every **memory** read and write — used to measure whether the write policy is working, and kept in
@@ -756,6 +829,16 @@ Zikaron is built to fail quietly rather than get in your way. A hook that cannot
756
829
  prints a short note asking the agent to tell you, writes one line to `hook.log`, and exits cleanly. It
757
830
  never blocks your message, and it never reads the store directly.
758
831
 
832
+ The service stops itself after a stretch of idleness, and the next message starts it again. That
833
+ message waits a few seconds longer — the hook allows a start more time than a running service — and
834
+ if the start still is not finished, the message goes without memories and the agent is told so, without
835
+ being asked to report it. `hook.log` records that as `starting`, followed by what the hook was still
836
+ waiting on — `lock` or `poll` before the service answered, or the lateness of its answer after — and
837
+ memories return once it has started, usually with the next message. A run of `starting` lines means
838
+ starts are slower than the hook allows, not that the service is broken; a start that never finishes is
839
+ reported as `transport` once it has gone a minute without a sign of life — which a first run still
840
+ fetching its model over a slow link can also do, until it finishes.
841
+
759
842
  **Run `zikaron doctor` first for anything that smells environmental** — no search results at all, a
760
843
  service that will not start, an MCP server that dies on contact. It checks the things about this
761
844
  machine that can stop Zikaron working and names what to change for each. The table below is for
@@ -864,6 +947,12 @@ than deleted, because both hold settings that are not Zikaron's. **All three set
864
947
  the install writes three** — leaving `enabledMcpjsonServers` behind names two servers that no longer
865
948
  exist, which loads nothing and breaks nothing but is not an uninstall.
866
949
 
950
+ **The guards** live only in `.claude/settings.local.json`: remove the `PreToolUse` and
951
+ `PostToolUse` hook groups whose command is `zikaron-guard`, and the `SessionStart` and
952
+ `SubagentStart` groups whose command is `zikaron-hook`. To keep the memory store and drop only the
953
+ guards, re-running the installer will not do it — an install never removes a selection — so delete
954
+ the two guard groups and the `--components …` arguments from the two start groups' command.
955
+
867
956
  Note that `rm -rf .zikaron` takes the knowledge bases with it. They are rebuildable — an index is a
868
957
  view onto files you still have — but rebuilding one takes minutes per corpus.
869
958
 
@@ -1,32 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: zikaron
3
- Version: 0.3.0
4
- Summary: Memory and searchable project knowledge for coding agents
5
- License-Expression: MIT
6
- Project-URL: Homepage, https://github.com/nathan-shapiro/Zikaron
7
- Project-URL: Repository, https://github.com/nathan-shapiro/Zikaron
8
- Project-URL: Issues, https://github.com/nathan-shapiro/Zikaron/issues
9
- Requires-Python: >=3.12
10
- Description-Content-Type: text/markdown
11
- License-File: LICENSE
12
- Requires-Dist: aiosqlite==0.22.1
13
- Requires-Dist: sqlite-vec==0.1.9
14
- Requires-Dist: fastembed==0.8.0
15
- Requires-Dist: huggingface_hub==1.26.0
16
- Requires-Dist: fastmcp==3.4.7
17
- Provides-Extra: dev
18
- Requires-Dist: ruff==0.16.7; extra == "dev"
19
- Requires-Dist: mypy==2.3.1; extra == "dev"
20
- Requires-Dist: pytest==9.1.1; extra == "dev"
21
- Requires-Dist: pytest-cov==7.1.0; extra == "dev"
22
- Requires-Dist: pytest-asyncio==1.4.0; extra == "dev"
23
- Requires-Dist: hypothesis==6.168.0; extra == "dev"
24
- Requires-Dist: pyyaml==6.0.3; extra == "dev"
25
- Requires-Dist: types-PyYAML==6.0.12.20260906; extra == "dev"
26
- Requires-Dist: build==1.3.0; extra == "dev"
27
- Requires-Dist: setuptools==84.0.0; extra == "dev"
28
- Dynamic: license-file
29
-
30
1
  # Zikaron
31
2
 
32
3
  [![PyPI](https://img.shields.io/pypi/v/zikaron)](https://pypi.org/project/zikaron/)
@@ -61,6 +32,13 @@ yes, it is out of scope. That store is for what cost somebody time to discover,
61
32
  be discovered again by the next agent, at full price. A knowledge base has no such test — it is
62
33
  whatever corpus you point it at.
63
34
 
35
+ **And, on request, guards for Claude Code** — two deterministic hooks, independent of both stores.
36
+ One refuses two kinds of command: a find-replace edit to an authored file (`sed -i`, an inline
37
+ script, a heredoc into the file), pointing the agent at `Read` and `Edit`, and a `pgrep -f` or
38
+ `pkill -f` whose pattern matches the shell running it. The other reminds the agent, after each
39
+ edit, to re-read what it changed. See [Guards](#guards-claude-code-on-request); a project may install the memory store, the
40
+ guards, or both.
41
+
64
42
  It runs entirely on your machine: SQLite databases under `.zikaron/`, a local embedding model, and a
65
43
  small background service on a Unix socket. **Zikaron reaches the network for exactly one thing** —
66
44
  the embedding-model download described under [Install](#install), pinned to one revision and
@@ -124,6 +102,7 @@ reads which one it is addressing before it reads a description. Consolidation ne
124
102
  | **service** | A long-running process per project. Holds the embedding model in memory and the databases open, and answers requests over a Unix socket. Starts itself when needed and stops itself when idle. |
125
103
  | **MCP server** | Translates the agent's tool calls into requests to the service. One process per agent instance; loads no model. |
126
104
  | **hook** | A single-shot executable your agent runs on start, before each message, and — under Claude Code — when it spawns a subagent. Deliberately tiny; loads no model and never touches the databases directly. |
105
+ | **guard** | Claude Code only, installed on request: a single-shot executable the harness runs before each `Bash` or `Monitor` call and after each edit. Regular expressions over the call, plus one bounded read of a script the call runs from a scratch directory — no model, no service, no store. |
127
106
  | **indexer** | A detached process started per knowledge-base build — by the service on every `add`/`refresh`, whether an agent asked or you did, or by you running the foreground command a build result prints. It outlives the call that started it, loads a model, saturates a core for a minute or more, and exits when the build finishes. If you see one in `ps`, that is a corpus building, not a runaway. |
128
107
 
129
108
  The service exists for one measured reason: loading the embedding model costs about **780 ms**, and
@@ -235,6 +214,16 @@ cd /path/to/your/project
235
214
  zikaron install --project . --harness claude-code # or: --harness kiro
236
215
  ```
237
216
 
217
+ Under Claude Code, `--components` adds the [guards](#guards-claude-code-on-request), or
218
+ installs them alone; without it you get the memory store only, exactly as before:
219
+
220
+ ```bash
221
+ zikaron install --project . --harness claude-code --components both # memory and guards
222
+ zikaron install --project . --harness claude-code --components guards # guards only
223
+ ```
224
+
225
+ Installing one later keeps the other: an install touches only what it was asked for.
226
+
238
227
  **Name it on a first install, rather than leaving it to be detected.** Detection looks for a
239
228
  `.kiro/` or `.claude/` directory in the project and for `CLAUDECODE` in the environment, and it
240
229
  refuses rather than guessing when neither says. Running from an ordinary terminal, a first install
@@ -334,6 +323,12 @@ and under **Claude Code**:
334
323
  | `.claude/settings.local.json` | three hooks — session start, per-message, and per-subagent — plus both approval keys, `enabledMcpjsonServers` and `permissions.allow` |
335
324
  | `.mcp.json` | both servers: `zikaron` for your own tools, `zikaron-consolidator` for the consolidation verbs — each marked `alwaysLoad` so their descriptions are in context from the start rather than fetched per tool |
336
325
 
326
+ With `--components guards` the install writes `.claude/settings.local.json` alone, and in it only
327
+ hooks: the session-start and per-subagent hooks, which carry a short note about the guards, and the
328
+ two guard hooks — before each `Bash` or `Monitor` call, after each edit. No server, no approval
329
+ key, no consolidator. With `--components both` you get all of the above together, and the start hooks carry
330
+ the write policy and the guards' note.
331
+
337
332
  **`settings.local.json`, not `settings.json`**, and it matters if you commit your settings: the hook
338
333
  entries name absolute paths inside *your* virtualenv, so they are meaningless in anyone else's clone.
339
334
  The third hook is the one with no kiro counterpart — it hands the write policy to each subagent you
@@ -451,6 +446,48 @@ guard, the receipts and the lease are untouched — but an unauthorized consolid
451
446
  tokens and could write a poorly-judged long-term record. `permissions.deny` is **not** the fix: it is
452
447
  global and unregisters the tool, after which the consolidator itself refuses to start.
453
448
 
449
+ ### Guards (Claude Code, on request)
450
+
451
+ `--components guards` (or `both`) installs two deterministic hooks that hold an agent to three
452
+ rules, with no model and no store behind them (`design/guards.md`):
453
+
454
+ - **A find-replace edit to an authored file is refused** — `sed -i`, `perl -pi`, an inline Python
455
+ or node script that writes a file, a heredoc or `echo` redirected into one, and a script under
456
+ `/tmp` or `$TMPDIR` that the command runs and that does any of these. The refusal reaches the
457
+ agent as the tool's result and tells it to use `Read` and `Edit`, or `Write` for a new file.
458
+ Paths under `/tmp`, `/var/tmp` and `$TMPDIR` are exempt, and so is a command carrying the
459
+ override the refusal itself describes, for the files whose bytes a program owns.
460
+ - **After each edit, the agent is reminded** that the file has not been re-read, and asked for one
461
+ re-read of the edited material once that file's editing is done. A Bash command gets no such
462
+ reminder, so when the agent overrides a refusal, the guard's acknowledgement names what the
463
+ command changes and asks for that re-read instead.
464
+ - **A `pgrep -f` or `pkill -f` whose pattern matches its own command is refused.** Claude Code
465
+ runs each command inside a shell whose command line carries the command's text, so on Linux such
466
+ a pattern matches that shell: `pkill` kills it, and `pgrep` always finds it, so a check for a
467
+ process that has exited never sees it gone. The refusal shows where the pattern matched and gives
468
+ the fix: `-A`, where `pgrep` has it, or a bracketed character (`[m]onth_monitor.py`). On macOS,
469
+ whose `pgrep` already leaves out the shell, only `-a` brings it back.
470
+
471
+ Both refusals judge `Bash` and `Monitor` commands alike. An install made by an earlier version
472
+ judges `Bash` alone; re-running it with the same `--components` adds `Monitor`.
473
+
474
+ They are not a security boundary: a script the guard reads is one run from a scratch directory,
475
+ and a script anywhere else — one of the project's own programs — is never read, by design. A guard
476
+ that fails for any reason allows the call.
477
+
478
+ They write no log and no store record: a refusal, an override and its reason are in the session's
479
+ own transcript. `zikaron doctor` does not report on them. To see the refusal without a model in
480
+ the loop, hand the guard the payload Claude Code would send it, from the project directory — run
481
+ from a scratch directory such as `/tmp`, the same command is exempt and prints nothing:
482
+
483
+ ```bash
484
+ printf '%s' '{"hook_event_name":"PreToolUse","tool_name":"Bash","cwd":"'"$PWD"'","tool_input":{"command":"sed -i s/a/b/ README.md"}}' | zikaron-guard
485
+ ```
486
+
487
+ It prints the deny the agent would read as the command's result. Asking an agent to run `sed -i`
488
+ usually shows something else: told the rule when its session starts, it reaches for `Read` and
489
+ `Edit` without trying.
490
+
454
491
  ### Options
455
492
 
456
493
  | Flag | Effect |
@@ -462,6 +499,7 @@ global and unregisters the tool, after which the consolidator itself refuses to
462
499
  | `--model <id>` | the consolidator's model (default: `claude-sonnet-5` under kiro, `sonnet` under Claude Code) |
463
500
  | `--format {object,array}` | **kiro only.** Which hook format to write when the target config has none yet |
464
501
  | `--no-trust-tools` | do not pre-approve Zikaron's own tools, so every Zikaron tool call asks permission |
502
+ | `--components {memory,guards,both}` | what to install: the memory store (the default), the guards below, or both. **Claude Code only** for `guards` and `both`; `--model` is refused with `guards`, which ships no consolidator |
465
503
  | `--force` | replace a symlink at a shipped path, and overwrite entries wired to a different Zikaron install that would otherwise be refused. It also stops merging: a server entry — `.mcp.json`'s, or kiro's `mcpServers` — is replaced **whole**, so a key you added to Zikaron's own entry goes, and under Claude Code a Zikaron hook group is replaced whole, so a hook of your own inside it goes too. Both are named in the output |
466
504
 
467
505
  Both hook formats kiro accepts are supported, and a config that already uses one keeps it: kiro
@@ -508,7 +546,7 @@ write policy goes into the model's context, not to your terminal. Then, from the
508
546
  # a service should be running for this project after your first message
509
547
  pgrep -af zikaron.service.main
510
548
 
511
- # one line per hook failure, write-policy-override note, or session/environment mismatch; absent means none happened
549
+ # one line per hook failure, missed cold start, write-policy-override note, or session/environment mismatch; absent means none happened
512
550
  cat .zikaron/hook.log
513
551
  ```
514
552
 
@@ -699,9 +737,11 @@ it is `0600` or `0700`. The **installer** also writes harness config — the fil
699
737
  config.toml your per-project overrides, if you wrote any
700
738
  service.log the background service: startup, resolved config, errors, why it stopped,
701
739
  any request running longer than 30 s, and the stack dumps below
702
- hook.log one line per hook failure, write-policy-override note, or session/environment
703
- mismatch. Absent means none happened. Not every line is a failure: an override
704
- that is used but exceeds the injection budget is printed anyway and logged
740
+ hook.log one line per hook failure, missed cold start, write-policy-override note, or
741
+ session/environment mismatch. Absent means none happened. Not every line is a
742
+ failure: `starting` is a message that arrived while the service was still
743
+ starting, and an override that is used but exceeds the injection budget is
744
+ printed anyway and logged
705
745
  warmup.log the small helper that warms the service when a session starts
706
746
  write-policy.md your policy override, if you wrote one
707
747
  ```
@@ -714,19 +754,23 @@ shared by every project you use Zikaron in, under `$XDG_CACHE_HOME/zikaron/model
714
754
  A copy inside each project would duplicate it for bytes that are identical by construction, since the
715
755
  version fetched is pinned. Removing a project does not remove it; [Uninstall](#uninstall) says how.
716
756
 
717
- The second is the service's Unix socket, which with its lock file sits in
718
- `$XDG_RUNTIME_DIR/zikaron/` — or `/tmp/zikaron-<uid>/` where that variable is unset, which is every
719
- Mac — named by a hash of the store's path, `0600` inside a `0700` directory. A socket belongs on a
720
- runtime filesystem rather than in your project. **Under Claude
721
- Code**, an over-large consolidator result also spills to a file beside them, carrying that group's
722
- record prose verbatim and named by the same store hash; it is deleted when the consolidator asks for
723
- its next group — or, if that process is killed first, by the next one that starts. It is the one
724
- copy of memory text that lives outside `.zikaron/`, which is why the erasure procedure under
725
- [Secrets](#secrets) removes it by hand. Nothing spills under kiro.
757
+ The second is the service's Unix socket. It sits in `$XDG_RUNTIME_DIR/zikaron/` — or
758
+ `/tmp/zikaron-<uid>/` where that variable is unset, which is every Mac — named by a hash of the
759
+ store's path, `0600` inside a `0700` directory, beside two lock files: one clients take while they
760
+ start the service, and one the service holds while it has the store open. A socket belongs on a
761
+ runtime filesystem rather than in your project. **Under Claude Code**, an over-large consolidator
762
+ result also spills to a file beside them, carrying that group's record prose verbatim and named by
763
+ the same store hash; it is deleted when the consolidator asks for its next group — or, if that
764
+ process is killed first, by the next one that starts. It is the one copy of memory text that lives
765
+ outside `.zikaron/`, which is why the erasure procedure under [Secrets](#secrets) removes it by hand.
766
+ Nothing spills under kiro.
726
767
 
727
768
  **What `service.log` tells you about a service that stopped, or stopped answering.** A clean stop
728
- writes one `stopping: reason=…` line: `idle`, `store_replaced`, `encoder_failed`, or `sigterm` /
729
- `sigint` when it was asked to stop. A request still running after the 30 s idle poll is logged as
769
+ writes one `stopping: reason=…` line: `idle`, `store_replaced`, `socket_lost` (its socket file was
770
+ removed, so nothing could reach it), `encoder_failed`, or `sigterm` / `sigint` when it was asked to
771
+ stop. A start that found another service already holding the store writes `not starting:
772
+ reason=another_service` and exits, leaving that one to serve it. A request still running after the
773
+ 30 s idle poll is logged as
730
774
  `long request: method=… session=… age=…` on every poll until it ends — `session=unresolved` if it
731
775
  never got that far. Two signals write a dump without stopping the service: `kill -USR2 <pid>` writes
732
776
  the requests in flight and every asyncio task's stack, and `kill -USR1 <pid>` writes every thread's
@@ -735,8 +779,8 @@ raw text, without the timestamp and `pid=` every other line carries.
735
779
 
736
780
  Two notes on reading the rest. `memory.db` can look implausibly small while `memory.db-wal` is large —
737
781
  that is normal for a database held open by the service, since recent writes live in the log until a
738
- checkpoint; the files together are the store. And `hook.log` records only a fixed failure label and an
739
- error code, never your prompt or a memory's text, so it cannot hold a leaked secret.
782
+ checkpoint; the files together are the store. And `hook.log` records only fixed labels and an error
783
+ code, never your prompt or a memory's text, so it cannot hold a leaked secret.
740
784
 
741
785
  There is one more log that is not a file: an event table inside `memory.db`. It records what happened
742
786
  on every **memory** read and write — used to measure whether the write policy is working, and kept in
@@ -756,6 +800,16 @@ Zikaron is built to fail quietly rather than get in your way. A hook that cannot
756
800
  prints a short note asking the agent to tell you, writes one line to `hook.log`, and exits cleanly. It
757
801
  never blocks your message, and it never reads the store directly.
758
802
 
803
+ The service stops itself after a stretch of idleness, and the next message starts it again. That
804
+ message waits a few seconds longer — the hook allows a start more time than a running service — and
805
+ if the start still is not finished, the message goes without memories and the agent is told so, without
806
+ being asked to report it. `hook.log` records that as `starting`, followed by what the hook was still
807
+ waiting on — `lock` or `poll` before the service answered, or the lateness of its answer after — and
808
+ memories return once it has started, usually with the next message. A run of `starting` lines means
809
+ starts are slower than the hook allows, not that the service is broken; a start that never finishes is
810
+ reported as `transport` once it has gone a minute without a sign of life — which a first run still
811
+ fetching its model over a slow link can also do, until it finishes.
812
+
759
813
  **Run `zikaron doctor` first for anything that smells environmental** — no search results at all, a
760
814
  service that will not start, an MCP server that dies on contact. It checks the things about this
761
815
  machine that can stop Zikaron working and names what to change for each. The table below is for
@@ -864,6 +918,12 @@ than deleted, because both hold settings that are not Zikaron's. **All three set
864
918
  the install writes three** — leaving `enabledMcpjsonServers` behind names two servers that no longer
865
919
  exist, which loads nothing and breaks nothing but is not an uninstall.
866
920
 
921
+ **The guards** live only in `.claude/settings.local.json`: remove the `PreToolUse` and
922
+ `PostToolUse` hook groups whose command is `zikaron-guard`, and the `SessionStart` and
923
+ `SubagentStart` groups whose command is `zikaron-hook`. To keep the memory store and drop only the
924
+ guards, re-running the installer will not do it — an install never removes a selection — so delete
925
+ the two guard groups and the `--components …` arguments from the two start groups' command.
926
+
867
927
  Note that `rm -rf .zikaron` takes the knowledge bases with it. They are rebuildable — an index is a
868
928
  view onto files you still have — but rebuilding one takes minutes per corpus.
869
929
 
@@ -4,16 +4,14 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "zikaron"
7
- # Semver, and `0.x` for as long as the *install contract* may still change — the shapes and
8
- # locations of the artefacts the installer writes into a harness's config. The one rule that
9
- # matters for an upgrade: **a release whose installed artefacts differ in shape from the previous
10
- # one is a minor bump, never a patch**, because those paths are written into harness config that an
11
- # upgrade has to be able to refresh, and a patch bump tells a reader nothing needs refreshing.
12
- # `design/distribution.md` §"The version scheme" is normative.
13
- version = "0.3.0"
7
+ # Semver, `0.x`. **A minor bump means one thing: `meta.schema_version` moved**, so a release that
8
+ # cannot open a store an earlier one wrote says so in its number. Every other change is a patch, a
9
+ # changed installed artifact included; a release that needs an existing install refreshed says so
10
+ # in its release note. `design/distribution.md` §"The version scheme" is normative.
11
+ version = "0.3.2"
14
12
  # What a package index renders as the one-line summary, so it must name both stores: the memory
15
13
  # store, for what was learned by working here, and the knowledge index, for what the project already
16
- # wrote down.
14
+ # wrote down. The guards, which ship on request, are the README's to describe.
17
15
  description = "Memory and searchable project knowledge for coding agents"
18
16
  readme = "README.md"
19
17
  # An SPDX expression plus `license-files`, which is the PEP 639 shape. **No
@@ -32,11 +30,11 @@ requires-python = ">=3.12"
32
30
  dependencies = [
33
31
  "aiosqlite==0.22.1",
34
32
  "sqlite-vec==0.1.9",
35
- "fastembed==0.8.0",
33
+ "fastembed==0.8.1",
36
34
  # Direct, not transitive: `core/indexing/acquisition.py` calls `snapshot_download` itself, so
37
35
  # that the revision and the digests are Zikaron's rather than whatever upstream head fastembed
38
36
  # would resolve. §6 pins direct dependencies exactly.
39
- "huggingface_hub==1.26.0",
37
+ "huggingface_hub==1.33.0",
40
38
  # `zikaron-mcp` only. Deliberately not stdlib-only, unlike `zikaron-hook` — see
41
39
  # coding-standards.md §6 for the cost-model reasoning (once per agent spawn, not once per
42
40
  # message) and architecture.md §Components for the measured import cost.
@@ -45,7 +43,7 @@ dependencies = [
45
43
  # changes how a client's connection — and therefore the session label this client adopts and
46
44
  # reuses across calls — is carried. That is a transport change worth its own measurement, so
47
45
  # the bump is deliberate rather than absorbed into an ordinary dependency refresh.
48
- "fastmcp==3.4.7",
46
+ "fastmcp==4.0.10",
49
47
  ]
50
48
 
51
49
  # Absent until now, which is why the PyPI page M30 will publish would have rendered blank. The
@@ -64,12 +62,13 @@ Homepage = "https://github.com/nathan-shapiro/Zikaron"
64
62
  Repository = "https://github.com/nathan-shapiro/Zikaron"
65
63
  Issues = "https://github.com/nathan-shapiro/Zikaron/issues"
66
64
 
67
- # The two commands a shipped harness config names — either harness — and the reason they are console
68
- # scripts rather than `python -m` command lines: a hook entry's `command` and an `mcpServers` entry's
69
- # `command` each need one absolute path, and a console script's shebang pins the interpreter Zikaron
70
- # is installed into — which is also the interpreter start-if-absent will spawn the service with, via
71
- # `sys.executable`. Measured, so the choice costs nothing: the two forms differ by about 1.4 ms
72
- # median over a full hook process (architecture.md §"The install contract").
65
+ # The commands a shipped harness config names — `zikaron-hook` and `zikaron-mcp` for either harness,
66
+ # `zikaron-guard` for Claude Code's guards — and the reason they are console scripts rather than
67
+ # `python -m` command lines: a hook entry's `command` and an `mcpServers` entry's `command` each need
68
+ # one absolute path, and a console script's shebang pins the interpreter Zikaron is installed into —
69
+ # which is also the interpreter start-if-absent will spawn the service with, via `sys.executable`.
70
+ # Measured, so the choice costs nothing: the two forms differ by about 1.4 ms median over a full hook
71
+ # process (architecture.md §"The install contract").
73
72
  #
74
73
  # `zikaron-service` is deliberately *not* here. No config names it: it is spawned by
75
74
  # `sys.executable -m zikaron.service.main` from whichever client finds the socket absent, which is
@@ -84,17 +83,18 @@ Issues = "https://github.com/nathan-shapiro/Zikaron/issues"
84
83
  zikaron = "zikaron.cli.main:main"
85
84
  zikaron-hook = "zikaron.hook.main:main"
86
85
  zikaron-mcp = "zikaron.mcp.main:main"
86
+ zikaron-guard = "zikaron.guard.main:main"
87
87
 
88
88
  [project.optional-dependencies]
89
89
  dev = [
90
- "ruff==0.16.7",
90
+ "ruff==0.16.9",
91
91
  "mypy==2.3.1",
92
92
  "pytest==9.1.1",
93
93
  "pytest-cov==7.1.0",
94
94
  "pytest-asyncio==1.4.0",
95
95
  # Property-based tests, for the places the design states a property rather than a case:
96
96
  # chunking never loses content, and no chunk exceeds the budget it was cut to.
97
- "hypothesis==6.168.0",
97
+ "hypothesis==6.168.3",
98
98
  # Tests only, and never imported by shipped code. Two of Claude Code's four installed
99
99
  # artefacts are YAML-frontmatter Markdown, and the installer emits that frontmatter by string
100
100
  # formatting — so the only way to assert it is *valid* is to parse it with something that did
@@ -140,7 +140,7 @@ def test_the_project_table_still_holds_the_keys_a_later_table_can_swallow() -> N
140
140
  still *in `[project]`*, which is the property an inserted table destroys.
141
141
  """
142
142
  project = tomllib.loads((_ROOT / "pyproject.toml").read_text(encoding="utf-8"))["project"]
143
- for key in ("name", "version", "readme", "license", "requires-python", "dependencies"):
143
+ keys = ("name", "version", "readme", "license", "requires-python", "dependencies", "scripts")
144
+ for key in keys:
144
145
  assert key in project, f"`{key}` has left [project] — a table header was inserted above it"
145
146
  assert len(project["dependencies"]) == 5, "the runtime dependency list changed size"
146
- assert set(project["scripts"]) == {"zikaron", "zikaron-hook", "zikaron-mcp"}
@@ -25,10 +25,18 @@ _ROOT: Final = Path(__file__).resolve().parent.parent
25
25
  _SUBCOMMANDS: Final = ("init", "install", "knowledge", "doctor")
26
26
 
27
27
 
28
- def test_the_console_script_resolves_to_something_callable() -> None:
29
- declared = tomllib.loads((_ROOT / "pyproject.toml").read_text(encoding="utf-8"))
30
- target = declared["project"]["scripts"][NAME]
31
- module_path, _, attribute = target.partition(":")
28
+ _SCRIPTS: Final[dict[str, str]] = tomllib.loads(
29
+ (_ROOT / "pyproject.toml").read_text(encoding="utf-8")
30
+ )["project"]["scripts"]
31
+
32
+
33
+ def test_the_front_door_is_a_declared_console_script() -> None:
34
+ assert NAME in _SCRIPTS
35
+
36
+
37
+ @pytest.mark.parametrize("script", sorted(_SCRIPTS))
38
+ def test_every_console_script_resolves_to_something_callable(script: str) -> None:
39
+ module_path, _, attribute = _SCRIPTS[script].partition(":")
32
40
  entry = getattr(importlib.import_module(module_path), attribute)
33
41
  assert callable(entry)
34
42
 
@@ -143,8 +143,11 @@ def test_the_wheel_declares_every_console_script(built: tuple[Path, Path]) -> No
143
143
  with zipfile.ZipFile(wheel) as archive:
144
144
  (entry_points,) = [n for n in archive.namelist() if n.endswith("entry_points.txt")]
145
145
  declared = archive.read(entry_points).decode()
146
- for script in ("zikaron =", "zikaron-hook =", "zikaron-mcp ="):
147
- assert script in declared, f"{script.rstrip(' =')} is not in the built wheel"
146
+ scripts = tomllib.loads((_ROOT / "pyproject.toml").read_text(encoding="utf-8"))["project"][
147
+ "scripts"
148
+ ]
149
+ for script in scripts:
150
+ assert f"{script} =" in declared, f"{script} is not in the built wheel"
148
151
 
149
152
 
150
153
  def test_nothing_published_names_this_machine(built: tuple[Path, Path]) -> None: