PreToolUse hooks route through kei-capability check when orchestrator
registers a capability via KEI_CAPABILITY_NAME env var on agent spawn.
hooks/agent-capability-check.sh (22 LOC):
- Pass-through (exit 0) when KEI_CAPABILITY_NAME unset — no-op by default
- Fail-open (exit 0) when kei-capability binary missing — kit convention
- Sources _lib/gate.sh for KEI_DISABLED_HOOKS / KEI_HOOK_PROFILE respect
- exec kei-capability check "$CAP_NAME" when active
hooks/agent-capability-verify.sh (24 LOC):
- Orchestrator-driven, NOT a Claude Code native hook
- Carries env: AGENT_ID, TASK_TOML, WORKTREE_PATH, MAIN_REPO, RUN_MODE
- exec kei-capability verify "$CAP_NAME"
Registered in hooks/hooks.json + settings-snippet.json under both
PreToolUse:Bash and PreToolUse:Edit|Write matchers. Internal NotApplicable
returns exit 0 so non-matching tool calls cost nothing.
install.sh unchanged — hooks/*.sh glob picks up both new files.
tests/hook_wiring_integration.sh (64 LOC) — 3 contract assertions:
(1) pass-through on unset KEI_CAPABILITY_NAME
(2) deny+exit 2 on git-op pattern
(3) allow+exit 0 on cargo-check pattern
Multi-capability routing (for phase 5): KEI_CAPABILITY_NAME currently
holds ONE name. When a role requires N capabilities, orchestrator will
either iterate or kei-capability gains a compose subcommand. Design
note left for phase 5.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three post-E1/E2/E3-merge items:
1. Schema amendment A-1 (architect P0-a, non-breaking clarification):
input.schema and output.schema are REQUIRED for all atom kinds. The
shared kei-atom-discovery parses them as Option<PathBuf> only to allow
tolerant skip-on-missing (stderr warn), not to permit absent schemas.
Resolves Stream C / Stream D enforcement asymmetry documented in
critic finding #6.
2. Cross-stream integration test (architect P0-b): tests/substrate_integration.sh
builds release binaries, scaffolds a test atom corpus, runs
schema-lint + list-atoms + atoms-discover + invoke; asserts all four
streams agree on the same atom corpus and exit codes honour the
locked §Runtime contract. Previously missing — only manual smoke
checks existed.
3. Fix regression introduced by E1's jsonschema 0.18 upgrade:
"relative URL without a base" on compile when schema declared a
relative $id like "kei-task/atoms/schemas/create-input.json".
validate.rs now synthesises an absolute file:// $id from the
canonicalised schema path before compile. Internal $refs still
resolve relative to the schema file; LocalFileResolver still confines
to the schema's parent dir. Integration test catches this.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>