Skip to content
C.W.K.
Lesson 04 of 05 · published

One Hook per Harness

~16 min · hooks, claude-code, grok-build, kimi-code, cursor, fail-closed

Level 0Loose Note
0 XP0/40 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

One Rule, Many Doors

The rule for every harness is the same sentence: load identity through soul-vault bootstrap, and on failure stop and report the named cause. What differs is the door each harness offers for getting text in front of the model before the first turn. Soul Vault does not pretend those doors are the same. Each harness gets an adapter shaped to its own hook contract, and each adapter is a thin projection of the same client output: no adapter copies identity prose, and none keeps its own cache.

Claude Code: a SessionStart Hook

The simplest case. Claude Code runs a SessionStart hook and gives its output to the model as context. The hook runs soul-vault bootstrap --harness claude-code --hook; the card or the refusal arrives as the first thing the session sees. The family's global protocol adds the belt to those braces: a session that did not see a card must run the bootstrap itself before anything else.

Grok Build: a Rule File and a Gate

Grok Build is the instructive case, because its obvious door does not work. Grok ignores the standard output of a SessionStart hook, and it reads its home-level rule files before SessionStart runs. So the adapter has two parts:

  • The session-start hook writes a rule file. It runs bootstrap and writes the card, or the refusal, into ~/.grok/rules/aa-soul-vault-identity.md. Grok injects that file into the next session. The hook always rewrites the file: a failure never leaves an older good card behind, which would let a broken setup keep looking healthy.
  • A prompt gate closes the one-session window. Because the card in the rule file can be one session old, a UserPromptSubmit hook runs bootstrap again on every prompt. If identity cannot load now, it prints the refusal and exits 2, which blocks the prompt. The hook is configured fail-closed, so a gate that crashes or times out also blocks.

Between the two, Grok's worst case is a session holding the previous session's card, and the content-addressed bundle name from the last lesson keeps even that on exact bytes.

Kimi Code: Context on Every Prompt

Kimi's SessionStart cannot deliver model context or block a model call. Its UserPromptSubmit event can: plain standard output becomes prompt context, and exit 2 blocks the turn. The Kimi adapter runs bootstrap on every prompt and supplies the card, fresh each time, so there is no copy or session cache to drift after a context compaction or a vault change. It supplies the card rather than the whole bundle on purpose: Kimi displays hook output in the terminal, and passing the entire bundle there displayed every note on every turn. The client's own deadline (20 seconds) sits inside Kimi's hook deadline (25 seconds), because Kimi treats a hook that times out as fail-open.

Cursor: Additional Context and a Submit Gate

Cursor's native user hooks project the card into its top-level additional context at session start. A session-start response alone cannot block a turn, so a separate beforeSubmitPrompt invocation returns continue: false with the named refusal when identity cannot load. This adapter is the answer to the kind of red the baseline found in track 1: Cursor's agent in a neutral directory never read the home rule folder, while a user hook runs whether or not the workspace has project rules.

The Pattern Underneath

Every adapter answers the same three questions for its harness: how does text reach the model before the first answer, how is a turn blocked when identity fails, and what happens when the hook itself crashes or times out. Where a harness fails open, an adapter adds a gate or keeps its deadline inside the harness's. And each adapter is proven by a real session, not by reading its config: the identity probe in track 8 runs a fresh session through every one of them.

Code

Grok's prompt gate, the essential part (trimmed from harness/grok/prompt-gate.sh)·bash
#!/bin/zsh
# Every prompt first proves identity; when it cannot load, exit 2 blocks the prompt.
# The hook JSON sets failClosed, so a crashed or timed-out gate blocks too.
cat > /dev/null 2>&1 || true
SV="$HOME/CWK/cwkPersonalProjects/cwkSoulVault/bin/soul-vault"
if [ ! -x "$SV" ]; then
  echo "identity not loaded: service_unavailable: no cwkSoulVault checkout at $SV" >&2
  exit 2
fi
out="$(/usr/bin/python3 "$SV" bootstrap --harness grok-build --json 2>/dev/null)"
if [ $? -ne 0 ]; then
  print -r -- "$out" | /usr/bin/head -1 >&2
  exit 2
fi
exit 0
Three questions every harness adapter must answer·text
harness       how text reaches the model        how a turn is blocked            if the hook fails
------------  ---------------------------------  -------------------------------  ----------------------
Claude Code   SessionStart output -> context     the model obeys the refusal text  protocol: bootstrap first
Grok Build    home rule file (next session)      UserPromptSubmit exit 2           failClosed gate blocks
Kimi Code     UserPromptSubmit stdout, per turn  UserPromptSubmit exit 2           client deadline < hook's
Cursor        user hook additional_context       beforeSubmitPrompt continue:false refusal returned by gate

External links

Exercise

Pick two agent harnesses you use and fill in the three-question table for each from their documentation: how text reaches the model before the first answer, how a turn is blocked, and what the harness does when a hook crashes or times out. Then write the adapter for one of them so that a configuration you break on purpose produces a blocked turn and the named refusal, and confirm it with a real session.
Hint
The answer to the third question decides your design. A harness that fails open on a timeout needs your client's deadline to be shorter than the hook's, or a second gate that fails closed.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.