Skip to content

Hand the cached kb-guard over to the code clone - #54

Merged
gabe4coding merged 3 commits into
mainfrom
claude/eloquent-aryabhata-8f4a35
Oct 13, 2026
Merged

gabe4coding merged 3 commits into
mainfrom
claude/eloquent-aryabhata-8f4a35

Conversation

@gabe4coding

@gabe4coding gabe4coding commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Intent

Make the cached bin/kb-guard run the code clone's guard, like bin/kb does. Then a guard fix reaches users with kb update, and does not wait for a new plugin version.

Drift

  • Added: drive.py hook --cached. Before, /verify loaded the plugin from the checkout, so it could not reach the new hand-over. Now it loads a copy from <sandbox>/plugins/cache/.
  • Added: the version is now 0.11.4. main went to 0.11.3 in Raise the plugin version so the guard fix of #49 reaches the cache #53 while this branch was open.
  • Fixed in the merge of main: both sides added a CACHED constant to tests/test_plugin.py. The second one (a path string) replaced the list of the version test. The cache path is now CACHE_DIR.

Decisions

  • The hand-over runs before INPUT=$(cat), so the event on stdin goes to the clone's guard unchanged.
  • The guard uses its own logic when: no config, no "code" key, no bin/kb-guard in the clone, or a "code" under */plugins/cache/*. The last case prevents an exec loop.
  • exec /bin/sh, not exec sh: if sh is not on PATH, the exec fails with 127 and the guard does not block the command.
  • Known trade-off, the same as bin/kb: if the clone is older than the cache, the older guard runs. kb update fixes it.
  • Door: two-way. Revert the 12 lines at the top of bin/kb-guard.

Evidence

The same fake code clone for both runs. Its bin/kb-guard only prints a deny with the reason FAKE CLONE GUARD ran. Each guard runs from a cache-shaped path, with the event of echo hello:

Before (main, cached 0.11.3): the cached guard uses its own logic and lets echo hello go through.

== before/plugins/cache/retroagent/retroagent/0.11.3/bin/kb-guard
exit 0

After (this branch, cached 0.11.4): the cached guard runs the clone's guard.

== after/plugins/cache/retroagent/retroagent/0.11.4/bin/kb-guard
{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "FAKE CLONE GUARD ran"}}
exit 0

Live headless Claude Code (Haiku) with drive.py hook --cached, plugin loaded from the sandbox cache:

  • "code" = the fake clone, echo hello → PreToolUse:Bash hook error: FAKE CLONE GUARD ran.
  • "code" = this checkout, kb decide accept s-000002 --yes → denied with "retroagent: only the user answers kb decide questions…". Then echo allowed-by-guard → allowed-by-guard.
  • pluginLog: PreToolUse:Bash [".../ra-verify-xp5c9o_g/plugins/cache/retroagent/retroagent/0.11.3/bin/kb-guard"] (plugin retroagent@inline) finished with status 0 (171ms), then (114ms). The same hook from the checkout: 96 ms and 101 ms. The hook timeout is 5 s. (This live run was before the merge of main, so the path shows 0.11.3.)

Reading map

Tests changed or deleted:

  • tests/test_plugin.py: _assert_guard gets a guard= parameter; 5 new tests (hand-over, no config, no key, missing clone, cache loop); CACHED → CACHE_DIR for the path.

Config:

  • .claude-plugin/plugin.json, .codex-plugin/plugin.json: 0.11.3 → 0.11.4.

Error handling:

  • bin/kb-guard:15-26: the new exec path, and its fallback to the local logic.

Stack

Step 1/2. Previous: none. Next: #55 (stacked on this PR; merge this one first, then set the base of #55 to main).

🤖 Generated with Claude Code

gabrypavanello and others added 3 commits October 13, 2026 17:48
Claude Code and Codex run the hooks from their per-version plugin cache. bin/kb already hands over to the code clone ("code" in the config) when it runs from a */plugins/cache/* path. bin/kb-guard had no hand-over, so a guard fix reached users only with a new version.

Now the cached bin/kb-guard runs the clone's bin/kb-guard with /bin/sh, before it reads stdin, so the event passes through. With no config, no "code" key, no bin/kb-guard in the clone, or a "code" that is itself under */plugins/cache/* (an exec loop), it uses its own logic.

drive.py hook gets --cached: it loads a copy of the plugin from <dir>/plugins/cache/, so /verify can drive the hand-over.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hata-8f4a35

# Conflicts:
#	docs/development.mdx
main is at 0.11.3 now (#53), and its test wants a new version when bin/ changes. The merge also gave tests/test_plugin.py two CACHED constants: the second one, a path string, replaced the list of the version test. The cache path is now CACHE_DIR. The bump rule in CODING_STANDARDS.md and the test comment now say that bin/kb and bin/kb-guard hand over to the code clone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gabe4coding
gabe4coding merged commit ee4bb05 into main Oct 13, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants