zikaron 0.3.1__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.
- {zikaron-0.3.1 → zikaron-0.3.2}/PKG-INFO +58 -32
- {zikaron-0.3.1 → zikaron-0.3.2}/README.md +57 -31
- {zikaron-0.3.1 → zikaron-0.3.2}/pyproject.toml +7 -9
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_guard_claude_live.py +65 -12
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_guard_main.py +54 -13
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_guard_malformed.py +5 -3
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_guard_nudge.py +2 -2
- zikaron-0.3.2/tests/test_guard_properties.py +104 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_guard_rule_table.py +6 -4
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_guard_script_file.py +1 -1
- zikaron-0.3.2/tests/test_guard_selfmatch.py +327 -0
- zikaron-0.3.2/tests/test_guard_selfmatch_premise.py +99 -0
- zikaron-0.3.2/tests/test_guard_selfmatch_table.py +90 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_guard_texts.py +36 -14
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_harness_table.py +6 -6
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_components.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_connect.py +76 -17
- zikaron-0.3.2/tests/test_hook_connect_edge_cases.py +273 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_connect_integration.py +77 -25
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_connect_race.py +59 -47
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_connect_real_service_integration.py +71 -46
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_failure.py +13 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_push.py +261 -43
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_rpc.py +120 -10
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_stdlib_only.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_indexing_acquisition.py +4 -4
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_install_components.py +24 -7
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_install_limits.py +20 -3
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_project_scope.py +18 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_dispatch.py +35 -3
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_encoder_failure_stop.py +20 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_lifecycle.py +144 -2
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_lifecycle_integration.py +16 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_main.py +214 -3
- zikaron-0.3.2/tests/test_service_ownership.py +123 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_server.py +25 -5
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/indexing/acquisition.py +3 -5
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/indexing/encoder.py +5 -0
- zikaron-0.3.2/zikaron/guard/__init__.py +1 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/chars.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/decision.py +34 -29
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/expression.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/forms.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/grammar.py +1 -1
- zikaron-0.3.2/zikaron/guard/grep_filter.py +190 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/heredoc.py +9 -2
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/inplace.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/interpreter.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/lexer.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/limits.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/literal_text.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/main.py +25 -16
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/messages.py +55 -10
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/nudge.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/override.py +1 -1
- zikaron-0.3.2/zikaron/guard/patterns.py +47 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/position.py +5 -5
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/programs.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/quoting.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/redirects.py +19 -2
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/scratch.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/script_file.py +2 -2
- zikaron-0.3.2/zikaron/guard/selfmatch.py +278 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/start_text.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/guard/writes.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/harness/spec.py +11 -10
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/hook/components.py +2 -2
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/hook/connect.py +248 -128
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/hook/failure.py +30 -22
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/hook/limits.py +4 -4
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/hook/main.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/hook/push.py +94 -67
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/hook/rpc.py +55 -4
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/hook/warm_helper.py +2 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/install/entries.py +3 -3
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/install/main.py +8 -8
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/install/targets.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/install/writer.py +1 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/knowledge/client.py +2 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/mcp/connection.py +5 -4
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/mcp/errors.py +4 -4
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/project/initialize.py +20 -14
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/asyncio_compat.py +3 -3
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/context.py +8 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/dispatch.py +13 -4
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/lifecycle.py +104 -56
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/log.py +17 -3
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/main.py +87 -49
- zikaron-0.3.2/zikaron/service/ownership.py +109 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/paths.py +7 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/server.py +30 -14
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron.egg-info/PKG-INFO +58 -32
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron.egg-info/SOURCES.txt +8 -0
- zikaron-0.3.1/tests/test_guard_properties.py +0 -78
- zikaron-0.3.1/tests/test_hook_connect_edge_cases.py +0 -104
- zikaron-0.3.1/zikaron/guard/__init__.py +0 -1
- {zikaron-0.3.1 → zikaron-0.3.2}/LICENSE +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/setup.cfg +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_access_log.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_agent_scan.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_asyncio_compat.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_background_encoder.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_check_gate.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_ci_workflow.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_cli_front_door.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_clock.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_config_keys.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_config_resolution.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_consolidation_grouping.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_consolidation_invariants.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_consolidation_observability.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_consolidation_planning.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_consolidation_refusals.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_consolidation_serving.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_consolidation_snapshot_planning.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_consolidation_verbs.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_ddl.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_definition_of_done_sites.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_design_pointers_resolve.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_design_tables.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_distribution.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_doctor.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_embedder.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_error_codes.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_event_kinds.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_guard_docstring_examples.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_harness_detect.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_harness_store_scope.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_harness_tier_guard.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_envelope.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_main.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_spawn_warm.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_subagent_policy.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_tripwire.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_warm_helper.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_warm_helper_integration.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_hook_write_policy.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_indexing_chunking.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_indexing_encoder.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_indexing_integration.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_indexing_model_cache.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_indexing_writes.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_injected_prose_log.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_install_assets.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_install_claude_live.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_install_e2e.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_install_harness.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_install_main.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_install_takeover.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_install_targets.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_install_writer.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_build_invariants.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_build_log.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_build_row_ordering.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_builds.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_chunking.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_cli.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_cli_integration.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_counters.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_database.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_ddl.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_detach.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_files.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_git.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_groups.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_indexer.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_integration.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_invariants.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_lifecycle.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_lock.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_lock_race.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_meta.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_registry.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_repair.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_reporting.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_roots.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_scan.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_scan_git.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_search.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_state.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_text.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_vectors.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_walk.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_knowledge_writes.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_markdown_renders_as_written.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_mcp_connection_ambiguous_failure.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_mcp_connection_cancellation.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_mcp_connection_concurrency.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_mcp_connection_integration.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_mcp_connection_labels.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_mcp_consolidator_bridge.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_mcp_errors.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_mcp_main.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_mcp_server.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_mcp_spill.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_mcp_tool_descriptions.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_mcp_wire_contracts.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_meta.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_permissions.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_publication_hygiene.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_quoted_design_prose_is_verbatim.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_readme_config_table.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_readme_internal_links.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_records_memory.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_records_receipts.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_records_supersession.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_retrieval_arms.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_retrieval_eligibility.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_retrieval_integration.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_retrieval_query.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_retrieval_ranking.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_retrieval_reads.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_context.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_context_assemble.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_diagnostics.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_diagnostics_integration.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_dispatch_consolidation.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_dispatch_knowledge.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_envelope.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_knowledge_management.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_log.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_params.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_paths.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_read_contention.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_rpc.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_security.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_surface_deadline.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_write_contention.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_service_writer.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_signals_contention.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_signals_dedup.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_signals_horizon.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_signals_repair.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_signals_retirement.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_signals_sessions.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_signals_writes.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_socket_path_budget.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_store.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_store_connection.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_store_migration.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_store_pool.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_transaction_begin_sites.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_version_seam.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_write_dedup.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/tests/test_write_tools.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/cli/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/cli/main.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/clock.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/config/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/config/keys.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/config/resolution.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/consolidation/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/consolidation/authorization.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/consolidation/candidates.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/consolidation/context.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/consolidation/grouping.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/consolidation/groups.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/consolidation/payload.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/consolidation/planning.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/consolidation/rowstate.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/consolidation/runs.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/consolidation/serving.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/consolidation/verbs.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/errors.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/events.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/indexing/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/indexing/chunking.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/indexing/lexical.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/indexing/model_cache.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/indexing/model_pin.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/indexing/vectors.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/indexing/writes.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/arms.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/builds.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/candidates.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/changes.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/chunking.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/counters.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/database.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/ddl.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/disposal.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/errors.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/files.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/git.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/groups.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/lexical.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/lifecycle.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/lock.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/meta.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/paths.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/pending.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/registry.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/repair.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/reporting.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/roots.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/scan.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/search.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/state.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/text.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/vectors.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/walk.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/knowledge/writes.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/records/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/records/memory.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/records/receipts.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/records/supersession.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/retrieval/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/retrieval/arms.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/retrieval/block.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/retrieval/eligibility.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/retrieval/query.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/retrieval/ranking.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/retrieval/reads.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/retrieval/retrieve.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/retrieval/similarity.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/signals/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/signals/contention.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/signals/dedup.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/signals/horizon.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/signals/repair.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/signals/retirement.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/signals/sessions.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/signals/writes.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/store/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/store/connection.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/store/ddl.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/store/deadline.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/store/embedder.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/store/holders.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/store/meta.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/store/migration.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/store/permissions.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/store/pool.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/store/store.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/store/transactions.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/write/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/write/dedup.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/core/write/tools.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/doctor/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/doctor/checks.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/doctor/main.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/harness/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/harness/detect.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/hook/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/hook/envelope.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/hook/spawn_warm.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/hook/subagent_policy.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/hook/tripwire.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/hook/write_policy.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/install/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/install/__main__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/install/agent_scan.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/install/assets.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/install/harness.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/install/ownership.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/knowledge/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/knowledge/__main__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/knowledge/indexer/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/knowledge/indexer/__main__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/knowledge/indexer/build_log.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/knowledge/indexer/detach.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/knowledge/indexer/main.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/knowledge/main.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/knowledge/scope.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/mcp/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/mcp/consolidator.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/mcp/main.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/mcp/primary.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/mcp/server.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/mcp/spill.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/mcp/tool_names.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/project/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/project/__main__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/project/report.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/project/resolve.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/py.typed +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/__init__.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/access_log.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/diagnostics.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/dispatch_consolidation.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/dispatch_knowledge.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/envelope.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/methods.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/params.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/rpc.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/security.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/serialize.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron/service/serialize_knowledge.py +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron.egg-info/dependency_links.txt +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron.egg-info/entry_points.txt +0 -0
- {zikaron-0.3.1 → zikaron-0.3.2}/zikaron.egg-info/requires.txt +0 -0
- {zikaron-0.3.1 → 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.
|
|
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
|
|
@@ -61,11 +61,12 @@ 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,
|
|
65
|
-
find-replace edit to an authored file (`sed -i`, an inline
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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.
|
|
69
70
|
|
|
70
71
|
It runs entirely on your machine: SQLite databases under `.zikaron/`, a local embedding model, and a
|
|
71
72
|
small background service on a Unix socket. **Zikaron reaches the network for exactly one thing** —
|
|
@@ -130,7 +131,7 @@ reads which one it is addressing before it reads a description. Consolidation ne
|
|
|
130
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. |
|
|
131
132
|
| **MCP server** | Translates the agent's tool calls into requests to the service. One process per agent instance; loads no model. |
|
|
132
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. |
|
|
133
|
-
| **guard** | Claude Code only, installed on request: a single-shot executable the harness runs before each Bash 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. |
|
|
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. |
|
|
134
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. |
|
|
135
136
|
|
|
136
137
|
The service exists for one measured reason: loading the embedding model costs about **780 ms**, and
|
|
@@ -242,7 +243,7 @@ cd /path/to/your/project
|
|
|
242
243
|
zikaron install --project . --harness claude-code # or: --harness kiro
|
|
243
244
|
```
|
|
244
245
|
|
|
245
|
-
Under Claude Code, `--components` adds the [
|
|
246
|
+
Under Claude Code, `--components` adds the [guards](#guards-claude-code-on-request), or
|
|
246
247
|
installs them alone; without it you get the memory store only, exactly as before:
|
|
247
248
|
|
|
248
249
|
```bash
|
|
@@ -353,8 +354,8 @@ and under **Claude Code**:
|
|
|
353
354
|
|
|
354
355
|
With `--components guards` the install writes `.claude/settings.local.json` alone, and in it only
|
|
355
356
|
hooks: the session-start and per-subagent hooks, which carry a short note about the guards, and the
|
|
356
|
-
two guard hooks — before each Bash call, after each edit. No server, no approval
|
|
357
|
-
consolidator. With `--components both` you get all of the above together, and the start hooks carry
|
|
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
|
|
358
359
|
the write policy and the guards' note.
|
|
359
360
|
|
|
360
361
|
**`settings.local.json`, not `settings.json`**, and it matters if you commit your settings: the hook
|
|
@@ -474,10 +475,10 @@ guard, the receipts and the lease are untouched — but an unauthorized consolid
|
|
|
474
475
|
tokens and could write a poorly-judged long-term record. `permissions.deny` is **not** the fix: it is
|
|
475
476
|
global and unregisters the tool, after which the consolidator itself refuses to start.
|
|
476
477
|
|
|
477
|
-
###
|
|
478
|
+
### Guards (Claude Code, on request)
|
|
478
479
|
|
|
479
|
-
`--components guards` (or `both`) installs two deterministic hooks that hold an agent to
|
|
480
|
-
with no model and no store behind them (`design/
|
|
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`):
|
|
481
482
|
|
|
482
483
|
- **A find-replace edit to an authored file is refused** — `sed -i`, `perl -pi`, an inline Python
|
|
483
484
|
or node script that writes a file, a heredoc or `echo` redirected into one, and a script under
|
|
@@ -489,6 +490,15 @@ with no model and no store behind them (`design/edit-guards.md`):
|
|
|
489
490
|
re-read of the edited material once that file's editing is done. A Bash command gets no such
|
|
490
491
|
reminder, so when the agent overrides a refusal, the guard's acknowledgement names what the
|
|
491
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`.
|
|
492
502
|
|
|
493
503
|
They are not a security boundary: a script the guard reads is one run from a scratch directory,
|
|
494
504
|
and a script anywhere else — one of the project's own programs — is never read, by design. A guard
|
|
@@ -518,7 +528,7 @@ usually shows something else: told the rule when its session starts, it reaches
|
|
|
518
528
|
| `--model <id>` | the consolidator's model (default: `claude-sonnet-5` under kiro, `sonnet` under Claude Code) |
|
|
519
529
|
| `--format {object,array}` | **kiro only.** Which hook format to write when the target config has none yet |
|
|
520
530
|
| `--no-trust-tools` | do not pre-approve Zikaron's own tools, so every Zikaron tool call asks permission |
|
|
521
|
-
| `--components {memory,guards,both}` | what to install: the memory store (the default), the
|
|
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 |
|
|
522
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 |
|
|
523
533
|
|
|
524
534
|
Both hook formats kiro accepts are supported, and a config that already uses one keeps it: kiro
|
|
@@ -565,7 +575,7 @@ write policy goes into the model's context, not to your terminal. Then, from the
|
|
|
565
575
|
# a service should be running for this project after your first message
|
|
566
576
|
pgrep -af zikaron.service.main
|
|
567
577
|
|
|
568
|
-
# 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
|
|
569
579
|
cat .zikaron/hook.log
|
|
570
580
|
```
|
|
571
581
|
|
|
@@ -756,9 +766,11 @@ it is `0600` or `0700`. The **installer** also writes harness config — the fil
|
|
|
756
766
|
config.toml your per-project overrides, if you wrote any
|
|
757
767
|
service.log the background service: startup, resolved config, errors, why it stopped,
|
|
758
768
|
any request running longer than 30 s, and the stack dumps below
|
|
759
|
-
hook.log one line per hook failure, write-policy-override note, or
|
|
760
|
-
mismatch. Absent means none happened. Not every line is a
|
|
761
|
-
|
|
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
|
|
762
774
|
warmup.log the small helper that warms the service when a session starts
|
|
763
775
|
write-policy.md your policy override, if you wrote one
|
|
764
776
|
```
|
|
@@ -771,19 +783,23 @@ shared by every project you use Zikaron in, under `$XDG_CACHE_HOME/zikaron/model
|
|
|
771
783
|
A copy inside each project would duplicate it for bytes that are identical by construction, since the
|
|
772
784
|
version fetched is pinned. Removing a project does not remove it; [Uninstall](#uninstall) says how.
|
|
773
785
|
|
|
774
|
-
The second is the service's Unix socket
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
[Secrets](#secrets) removes it by hand.
|
|
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.
|
|
783
796
|
|
|
784
797
|
**What `service.log` tells you about a service that stopped, or stopped answering.** A clean stop
|
|
785
|
-
writes one `stopping: reason=…` line: `idle`, `store_replaced`, `
|
|
786
|
-
|
|
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
|
|
787
803
|
`long request: method=… session=… age=…` on every poll until it ends — `session=unresolved` if it
|
|
788
804
|
never got that far. Two signals write a dump without stopping the service: `kill -USR2 <pid>` writes
|
|
789
805
|
the requests in flight and every asyncio task's stack, and `kill -USR1 <pid>` writes every thread's
|
|
@@ -792,8 +808,8 @@ raw text, without the timestamp and `pid=` every other line carries.
|
|
|
792
808
|
|
|
793
809
|
Two notes on reading the rest. `memory.db` can look implausibly small while `memory.db-wal` is large —
|
|
794
810
|
that is normal for a database held open by the service, since recent writes live in the log until a
|
|
795
|
-
checkpoint; the files together are the store. And `hook.log` records only
|
|
796
|
-
|
|
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.
|
|
797
813
|
|
|
798
814
|
There is one more log that is not a file: an event table inside `memory.db`. It records what happened
|
|
799
815
|
on every **memory** read and write — used to measure whether the write policy is working, and kept in
|
|
@@ -813,6 +829,16 @@ Zikaron is built to fail quietly rather than get in your way. A hook that cannot
|
|
|
813
829
|
prints a short note asking the agent to tell you, writes one line to `hook.log`, and exits cleanly. It
|
|
814
830
|
never blocks your message, and it never reads the store directly.
|
|
815
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
|
+
|
|
816
842
|
**Run `zikaron doctor` first for anything that smells environmental** — no search results at all, a
|
|
817
843
|
service that will not start, an MCP server that dies on contact. It checks the things about this
|
|
818
844
|
machine that can stop Zikaron working and names what to change for each. The table below is for
|
|
@@ -921,7 +947,7 @@ than deleted, because both hold settings that are not Zikaron's. **All three set
|
|
|
921
947
|
the install writes three** — leaving `enabledMcpjsonServers` behind names two servers that no longer
|
|
922
948
|
exist, which loads nothing and breaks nothing but is not an uninstall.
|
|
923
949
|
|
|
924
|
-
**The
|
|
950
|
+
**The guards** live only in `.claude/settings.local.json`: remove the `PreToolUse` and
|
|
925
951
|
`PostToolUse` hook groups whose command is `zikaron-guard`, and the `SessionStart` and
|
|
926
952
|
`SubagentStart` groups whose command is `zikaron-hook`. To keep the memory store and drop only the
|
|
927
953
|
guards, re-running the installer will not do it — an install never removes a selection — so delete
|
|
@@ -32,11 +32,12 @@ yes, it is out of scope. That store is for what cost somebody time to discover,
|
|
|
32
32
|
be discovered again by the next agent, at full price. A knowledge base has no such test — it is
|
|
33
33
|
whatever corpus you point it at.
|
|
34
34
|
|
|
35
|
-
**And, on request,
|
|
36
|
-
find-replace edit to an authored file (`sed -i`, an inline
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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.
|
|
40
41
|
|
|
41
42
|
It runs entirely on your machine: SQLite databases under `.zikaron/`, a local embedding model, and a
|
|
42
43
|
small background service on a Unix socket. **Zikaron reaches the network for exactly one thing** —
|
|
@@ -101,7 +102,7 @@ reads which one it is addressing before it reads a description. Consolidation ne
|
|
|
101
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. |
|
|
102
103
|
| **MCP server** | Translates the agent's tool calls into requests to the service. One process per agent instance; loads no model. |
|
|
103
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. |
|
|
104
|
-
| **guard** | Claude Code only, installed on request: a single-shot executable the harness runs before each Bash 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. |
|
|
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. |
|
|
105
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. |
|
|
106
107
|
|
|
107
108
|
The service exists for one measured reason: loading the embedding model costs about **780 ms**, and
|
|
@@ -213,7 +214,7 @@ cd /path/to/your/project
|
|
|
213
214
|
zikaron install --project . --harness claude-code # or: --harness kiro
|
|
214
215
|
```
|
|
215
216
|
|
|
216
|
-
Under Claude Code, `--components` adds the [
|
|
217
|
+
Under Claude Code, `--components` adds the [guards](#guards-claude-code-on-request), or
|
|
217
218
|
installs them alone; without it you get the memory store only, exactly as before:
|
|
218
219
|
|
|
219
220
|
```bash
|
|
@@ -324,8 +325,8 @@ and under **Claude Code**:
|
|
|
324
325
|
|
|
325
326
|
With `--components guards` the install writes `.claude/settings.local.json` alone, and in it only
|
|
326
327
|
hooks: the session-start and per-subagent hooks, which carry a short note about the guards, and the
|
|
327
|
-
two guard hooks — before each Bash call, after each edit. No server, no approval
|
|
328
|
-
consolidator. With `--components both` you get all of the above together, and the start hooks carry
|
|
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
|
|
329
330
|
the write policy and the guards' note.
|
|
330
331
|
|
|
331
332
|
**`settings.local.json`, not `settings.json`**, and it matters if you commit your settings: the hook
|
|
@@ -445,10 +446,10 @@ guard, the receipts and the lease are untouched — but an unauthorized consolid
|
|
|
445
446
|
tokens and could write a poorly-judged long-term record. `permissions.deny` is **not** the fix: it is
|
|
446
447
|
global and unregisters the tool, after which the consolidator itself refuses to start.
|
|
447
448
|
|
|
448
|
-
###
|
|
449
|
+
### Guards (Claude Code, on request)
|
|
449
450
|
|
|
450
|
-
`--components guards` (or `both`) installs two deterministic hooks that hold an agent to
|
|
451
|
-
with no model and no store behind them (`design/
|
|
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`):
|
|
452
453
|
|
|
453
454
|
- **A find-replace edit to an authored file is refused** — `sed -i`, `perl -pi`, an inline Python
|
|
454
455
|
or node script that writes a file, a heredoc or `echo` redirected into one, and a script under
|
|
@@ -460,6 +461,15 @@ with no model and no store behind them (`design/edit-guards.md`):
|
|
|
460
461
|
re-read of the edited material once that file's editing is done. A Bash command gets no such
|
|
461
462
|
reminder, so when the agent overrides a refusal, the guard's acknowledgement names what the
|
|
462
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`.
|
|
463
473
|
|
|
464
474
|
They are not a security boundary: a script the guard reads is one run from a scratch directory,
|
|
465
475
|
and a script anywhere else — one of the project's own programs — is never read, by design. A guard
|
|
@@ -489,7 +499,7 @@ usually shows something else: told the rule when its session starts, it reaches
|
|
|
489
499
|
| `--model <id>` | the consolidator's model (default: `claude-sonnet-5` under kiro, `sonnet` under Claude Code) |
|
|
490
500
|
| `--format {object,array}` | **kiro only.** Which hook format to write when the target config has none yet |
|
|
491
501
|
| `--no-trust-tools` | do not pre-approve Zikaron's own tools, so every Zikaron tool call asks permission |
|
|
492
|
-
| `--components {memory,guards,both}` | what to install: the memory store (the default), the
|
|
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 |
|
|
493
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 |
|
|
494
504
|
|
|
495
505
|
Both hook formats kiro accepts are supported, and a config that already uses one keeps it: kiro
|
|
@@ -536,7 +546,7 @@ write policy goes into the model's context, not to your terminal. Then, from the
|
|
|
536
546
|
# a service should be running for this project after your first message
|
|
537
547
|
pgrep -af zikaron.service.main
|
|
538
548
|
|
|
539
|
-
# 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
|
|
540
550
|
cat .zikaron/hook.log
|
|
541
551
|
```
|
|
542
552
|
|
|
@@ -727,9 +737,11 @@ it is `0600` or `0700`. The **installer** also writes harness config — the fil
|
|
|
727
737
|
config.toml your per-project overrides, if you wrote any
|
|
728
738
|
service.log the background service: startup, resolved config, errors, why it stopped,
|
|
729
739
|
any request running longer than 30 s, and the stack dumps below
|
|
730
|
-
hook.log one line per hook failure, write-policy-override note, or
|
|
731
|
-
mismatch. Absent means none happened. Not every line is a
|
|
732
|
-
|
|
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
|
|
733
745
|
warmup.log the small helper that warms the service when a session starts
|
|
734
746
|
write-policy.md your policy override, if you wrote one
|
|
735
747
|
```
|
|
@@ -742,19 +754,23 @@ shared by every project you use Zikaron in, under `$XDG_CACHE_HOME/zikaron/model
|
|
|
742
754
|
A copy inside each project would duplicate it for bytes that are identical by construction, since the
|
|
743
755
|
version fetched is pinned. Removing a project does not remove it; [Uninstall](#uninstall) says how.
|
|
744
756
|
|
|
745
|
-
The second is the service's Unix socket
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
[Secrets](#secrets) removes it by hand.
|
|
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.
|
|
754
767
|
|
|
755
768
|
**What `service.log` tells you about a service that stopped, or stopped answering.** A clean stop
|
|
756
|
-
writes one `stopping: reason=…` line: `idle`, `store_replaced`, `
|
|
757
|
-
|
|
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
|
|
758
774
|
`long request: method=… session=… age=…` on every poll until it ends — `session=unresolved` if it
|
|
759
775
|
never got that far. Two signals write a dump without stopping the service: `kill -USR2 <pid>` writes
|
|
760
776
|
the requests in flight and every asyncio task's stack, and `kill -USR1 <pid>` writes every thread's
|
|
@@ -763,8 +779,8 @@ raw text, without the timestamp and `pid=` every other line carries.
|
|
|
763
779
|
|
|
764
780
|
Two notes on reading the rest. `memory.db` can look implausibly small while `memory.db-wal` is large —
|
|
765
781
|
that is normal for a database held open by the service, since recent writes live in the log until a
|
|
766
|
-
checkpoint; the files together are the store. And `hook.log` records only
|
|
767
|
-
|
|
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.
|
|
768
784
|
|
|
769
785
|
There is one more log that is not a file: an event table inside `memory.db`. It records what happened
|
|
770
786
|
on every **memory** read and write — used to measure whether the write policy is working, and kept in
|
|
@@ -784,6 +800,16 @@ Zikaron is built to fail quietly rather than get in your way. A hook that cannot
|
|
|
784
800
|
prints a short note asking the agent to tell you, writes one line to `hook.log`, and exits cleanly. It
|
|
785
801
|
never blocks your message, and it never reads the store directly.
|
|
786
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
|
+
|
|
787
813
|
**Run `zikaron doctor` first for anything that smells environmental** — no search results at all, a
|
|
788
814
|
service that will not start, an MCP server that dies on contact. It checks the things about this
|
|
789
815
|
machine that can stop Zikaron working and names what to change for each. The table below is for
|
|
@@ -892,7 +918,7 @@ than deleted, because both hold settings that are not Zikaron's. **All three set
|
|
|
892
918
|
the install writes three** — leaving `enabledMcpjsonServers` behind names two servers that no longer
|
|
893
919
|
exist, which loads nothing and breaks nothing but is not an uninstall.
|
|
894
920
|
|
|
895
|
-
**The
|
|
921
|
+
**The guards** live only in `.claude/settings.local.json`: remove the `PreToolUse` and
|
|
896
922
|
`PostToolUse` hook groups whose command is `zikaron-guard`, and the `SessionStart` and
|
|
897
923
|
`SubagentStart` groups whose command is `zikaron-hook`. To keep the memory store and drop only the
|
|
898
924
|
guards, re-running the installer will not do it — an install never removes a selection — so delete
|
|
@@ -4,16 +4,14 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "zikaron"
|
|
7
|
-
# Semver,
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
# `design/distribution.md` §"The version scheme" is normative.
|
|
13
|
-
version = "0.3.1"
|
|
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
|
|
@@ -65,7 +63,7 @@ Repository = "https://github.com/nathan-shapiro/Zikaron"
|
|
|
65
63
|
Issues = "https://github.com/nathan-shapiro/Zikaron/issues"
|
|
66
64
|
|
|
67
65
|
# The commands a shipped harness config names — `zikaron-hook` and `zikaron-mcp` for either harness,
|
|
68
|
-
# `zikaron-guard` for Claude Code's
|
|
66
|
+
# `zikaron-guard` for Claude Code's guards — and the reason they are console scripts rather than
|
|
69
67
|
# `python -m` command lines: a hook entry's `command` and an `mcpServers` entry's `command` each need
|
|
70
68
|
# one absolute path, and a console script's shebang pins the interpreter Zikaron is installed into —
|
|
71
69
|
# which is also the interpreter start-if-absent will spawn the service with, via `sys.executable`.
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
"""The
|
|
1
|
+
"""The guards in a real Claude Code session (`design/build-plan.md` §M37, "A live test", and
|
|
2
|
+
§M38's done-when 3).
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
neither the edit nor the override is the model's choice
|
|
5
|
-
form with a valid override, and an `Edit
|
|
6
|
-
the
|
|
4
|
+
Each headless session under `--permission-mode bypassPermissions` is given exact commands, so that
|
|
5
|
+
neither the edit nor the override is the model's choice. The first runs a `sed -i` the guard must
|
|
6
|
+
deny, the same form with a valid override, and an `Edit`; the second a self-matching `pkill -f`,
|
|
7
|
+
the same command with `-A`, and a self-matching `pgrep -f` through `Monitor`. What is asserted is
|
|
8
|
+
what the harness recorded, never what the model says:
|
|
7
9
|
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
+
- each deny is its call's `tool_result` in the `stream-json` output, and the file is unchanged by
|
|
11
|
+
the denied `sed` — under `bypassPermissions`, which is itself one of §2's rows;
|
|
10
12
|
- the override's command ran (the file changed), and its acknowledgement reached the model, which is
|
|
11
13
|
recorded only in the session's transcript, as an attachment;
|
|
12
|
-
- the `Edit` was followed by the nudge, also an attachment
|
|
14
|
+
- the `Edit` was followed by the nudge, also an attachment;
|
|
15
|
+
- the `-A` call ran rather than being denied.
|
|
13
16
|
|
|
14
17
|
**The project lives under `~/.cache/zikaron-live-tests/`, never `tmp_path`**: pytest's temporary
|
|
15
18
|
directories lie under `/tmp` or `$TMPDIR`, where the guard exempts the commands and suppresses the
|
|
@@ -31,6 +34,7 @@ from typing import Any, Final
|
|
|
31
34
|
|
|
32
35
|
import pytest
|
|
33
36
|
|
|
37
|
+
from zikaron.guard import messages
|
|
34
38
|
from zikaron.harness.spec import CLAUDE_CODE
|
|
35
39
|
from zikaron.install import harness
|
|
36
40
|
from zikaron.install import main as install_main
|
|
@@ -49,6 +53,22 @@ _PROMPT: Final = (
|
|
|
49
53
|
"Step 3: use the Edit tool to replace the word alpha with ALPHA in target.txt. Then reply with "
|
|
50
54
|
"the single word: done."
|
|
51
55
|
)
|
|
56
|
+
#: A pattern no process carries but the command running it, so only the shell can match it.
|
|
57
|
+
_NONCE: Final = f"zikaron_live_selfmatch_{uuid.uuid4().hex[:12]}"
|
|
58
|
+
_SELF_MATCHING: Final = f"pkill -f {_NONCE}"
|
|
59
|
+
_ANCESTORS_IGNORED: Final = f"pkill -A -f {_NONCE}"
|
|
60
|
+
_MONITORED: Final = f"pgrep -f {_NONCE}"
|
|
61
|
+
#: Measured: told only to run the steps, a small model declines a `pkill` it has no reason for, so
|
|
62
|
+
#: the prompt says what the commands are for and why they are safe.
|
|
63
|
+
_SELF_MATCH_PROMPT: Final = (
|
|
64
|
+
"This is a test of a hook installed in this throwaway project. Its pattern is a random string "
|
|
65
|
+
"that no process on this machine carries, so none of these commands can stop anything. "
|
|
66
|
+
"Do these three steps in order, each exactly as written, and carry on to the next even if one "
|
|
67
|
+
f"fails. Step 1: run this Bash command, character for character: `{_SELF_MATCHING}`. Step 2: "
|
|
68
|
+
f"run this Bash command, character for character: `{_ANCESTORS_IGNORED}`. Step 3: use the "
|
|
69
|
+
f"Monitor tool to run this command, character for character: `{_MONITORED}`. Then reply with "
|
|
70
|
+
"the single word: done."
|
|
71
|
+
)
|
|
52
72
|
|
|
53
73
|
|
|
54
74
|
@pytest.fixture
|
|
@@ -62,7 +82,7 @@ def project() -> Iterator[Path]:
|
|
|
62
82
|
shutil.rmtree(root)
|
|
63
83
|
|
|
64
84
|
|
|
65
|
-
def _session(project: Path) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
|
85
|
+
def _session(project: Path, prompt: str) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
|
66
86
|
"""One headless turn: the `stream-json` records, and the session transcript's records."""
|
|
67
87
|
assert harness.binary_is_available(CLAUDE_CODE.harness_binary), (
|
|
68
88
|
f"{CLAUDE_CODE.harness_binary!r} is not on PATH: do not run -m integration_claude here, "
|
|
@@ -72,7 +92,7 @@ def _session(project: Path) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]
|
|
|
72
92
|
[
|
|
73
93
|
CLAUDE_CODE.harness_binary,
|
|
74
94
|
"-p",
|
|
75
|
-
|
|
95
|
+
prompt,
|
|
76
96
|
"--model",
|
|
77
97
|
"haiku",
|
|
78
98
|
"--permission-mode",
|
|
@@ -108,12 +128,33 @@ def _tool_results(stream: list[dict[str, Any]]) -> list[str]:
|
|
|
108
128
|
return results
|
|
109
129
|
|
|
110
130
|
|
|
111
|
-
def
|
|
131
|
+
def _results_by_command(stream: list[dict[str, Any]]) -> dict[str, str]:
|
|
132
|
+
"""Each `Bash` or `Monitor` call's command, mapped to its `tool_result`."""
|
|
133
|
+
commands: dict[str, str] = {}
|
|
134
|
+
results: dict[str, str] = {}
|
|
135
|
+
for record in stream:
|
|
136
|
+
for block in (record.get("message") or {}).get("content") or []:
|
|
137
|
+
if not isinstance(block, dict):
|
|
138
|
+
continue
|
|
139
|
+
if block.get("type") == "tool_use" and block.get("name") in ("Bash", "Monitor"):
|
|
140
|
+
commands[block["id"]] = block["input"].get("command", "")
|
|
141
|
+
elif block.get("type") == "tool_result":
|
|
142
|
+
content = block.get("content")
|
|
143
|
+
text = content if isinstance(content, str) else json.dumps(content)
|
|
144
|
+
results[block["tool_use_id"]] = text
|
|
145
|
+
return {command: results.get(use_id, "") for use_id, command in commands.items()}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _install_guards(project: Path) -> None:
|
|
112
149
|
status = install_main.main(
|
|
113
150
|
["--project", str(project), "--harness", "claude-code", "--components", "guards"]
|
|
114
151
|
)
|
|
115
152
|
assert status == 0
|
|
116
|
-
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_the_guards_deny_acknowledge_and_nudge_in_a_real_session(project: Path) -> None:
|
|
156
|
+
_install_guards(project)
|
|
157
|
+
stream, transcript = _session(project, _PROMPT)
|
|
117
158
|
text = (project / "target.txt").read_text(encoding="utf-8")
|
|
118
159
|
|
|
119
160
|
denials = [r for r in _tool_results(stream) if "PreToolUse:Bash hook error" in r]
|
|
@@ -126,3 +167,15 @@ def test_the_guards_deny_acknowledge_and_nudge_in_a_real_session(project: Path)
|
|
|
126
167
|
assert "#ZIKARON-FORCE accepted by Zikaron's find-replace guard" in recorded
|
|
127
168
|
assert "PostToolUse:Edit hook additional context" in recorded
|
|
128
169
|
assert "target.txt` was edited" in recorded
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def test_the_self_match_rule_denies_on_bash_and_monitor_in_a_real_session(project: Path) -> None:
|
|
173
|
+
_install_guards(project)
|
|
174
|
+
results = _results_by_command(_session(project, _SELF_MATCH_PROMPT)[0])
|
|
175
|
+
refused = messages.SELF_MATCH_TEMPLATE.split("{", 1)[0]
|
|
176
|
+
|
|
177
|
+
assert "PreToolUse:Bash hook error" in results[_SELF_MATCHING], results
|
|
178
|
+
assert refused in results[_SELF_MATCHING], results
|
|
179
|
+
assert "hook error" not in results[_ANCESTORS_IGNORED], results
|
|
180
|
+
assert "PreToolUse:Monitor hook error" in results[_MONITORED], results
|
|
181
|
+
assert refused in results[_MONITORED], results
|