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>
Sibling SSoT to SUBSTRATE-SCHEMA.md (atom substrate). This one decomposes
agent invocations rather than code primitives.
Core contribution — the capability TRIPLET, not just text:
- text.md — what agent reads (prompt fragment)
- gate.sh — PreToolUse hook (runtime enforcement)
- verify.sh — on-return predicate run from main repo (not worktree)
Motivation from substrate v1 orchestration audit:
- 40% prompt boilerplate across 7 spawns (git-ban + constructor-pattern +
report format etc. copy-pasted each time)
- Self-reported green tests broke at integration (E1 jsonschema
regression — agent claimed PASS from worktree but main workspace
failed; caught only by integration test)
- Scope violations (E1 touched invoke.rs when E3 was supposed to own it;
surfaced only at merge)
Triplet closes all three gaps: capabilities aren't promises agents make
in prose, they're enforced by gate hooks pre-exec and verified by
predicates on return from main branch clean state.
Schema specifies:
- Capability atom layout: _capabilities/<category>/<slug>/
- capability.toml frontmatter shape
- text.md / gate.sh / verify.sh contracts
- Role = bundle of capabilities (5 roles: read-only, explorer, edit-local,
edit-shared, git-ops)
- task.toml shape (orchestrator-written per spawn; parameterizes roles)
- kei-agent-runtime crate contract: compose + spawn + verify + run
- Initial 10-capability inventory for phase 1
- 6-question decision log with defaults
- 5-phase parallel build plan (phases 1-4 parallel, ~5-7 days wall time)
Open questions flagged at bottom for review before AGENT-SCHEMA-LOCKED.md.
Once locked: sibling SSoTs (atoms + agents) evolve symmetrically — agents
compose atoms, atoms compose agents (ultimate goal).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>