feat: plugin hooks no-op outside a bstack workspace — scope-safe global plugin (BRO-1926 Phase 2 prereq)#95
Conversation
…al plugin (BRO-1926) The plugin loads at personal scope (~/.claude/skills/bstack), so once a workspace enables bstack@skills-dir its hooks fire in every session, including non-bstack repos. leverage-sensor.py/leverage-ship-sensor.py makedirs+write <ws>/.control/*.json and l3-stability-pretool logs to <ws>/.control/audit/ — so a global plugin would litter .control/ into unrelated repos (e.g. a client repo) every session. New hooks/bstack-hook-guard.sh gates each hook on the workspace being bstack-governed (has .control/); if not, no-op (PreToolUse still emits approve). hooks/hooks.json routes the 5 workspace hooks through the guard; bstack-autoupdate stays unguarded (workspace- agnostic). Sibling $0/dirname resolution preserved (guard execs the real script by full path). tests/hook-guard.test.sh covers 4 cases. No behavior change in a bstack workspace. Prerequisite for BRO-1926 Phase 2 (adopting the plugin over hand-wired settings.json hooks). Bump 0.35.0 -> 0.36.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 46 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThe plugin adds a workspace guard that skips governed hooks outside ChangesHook scope safety
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant ClaudeCode
participant hooks.json
participant bstack-hook-guard.sh
participant WrappedHook
ClaudeCode->>hooks.json: Trigger workspace hook
hooks.json->>bstack-hook-guard.sh: Invoke guard with hook command
bstack-hook-guard.sh->>bstack-hook-guard.sh: Resolve workspace and check .control/
alt Governed workspace
bstack-hook-guard.sh->>WrappedHook: exec command and arguments
WrappedHook-->>ClaudeCode: Hook output
else Non-governed pretool hook
bstack-hook-guard.sh-->>ClaudeCode: Return approve decision
else Non-governed generic hook
bstack-hook-guard.sh-->>ClaudeCode: No-op
end
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/hook-guard.test.sh (1)
66-66: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAppend failure context to the assertion message.
For consistency with the other test cases in this file, consider appending a brief explanation of the failure state to the failure message.
💡 Proposed change
- assert_fail "governed workspace: sibling \$0/dirname resolution preserved" + assert_fail "governed workspace: sibling \$0/dirname resolution preserved (it did not)"🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/hook-guard.test.sh` at line 66, Update the governed workspace assertion using assert_fail to append a brief explanation of the expected failure state, matching the contextual failure-message style used by the other test cases in tests/hook-guard.test.sh.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/hook-guard.test.sh`:
- Line 66: Update the governed workspace assertion using assert_fail to append a
brief explanation of the expected failure state, matching the contextual
failure-message style used by the other test cases in tests/hook-guard.test.sh.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b0e15a4b-43cb-410c-ba89-0cd7efb633cd
📒 Files selected for processing (5)
CHANGELOG.mdVERSIONhooks/bstack-hook-guard.shhooks/hooks.jsontests/hook-guard.test.sh
…it nothing (P20)
P20 review (7/10, 1 MAJOR) found that wrapping the session-scoped, self-guarding,
non-polluting arc-continuation + autonomous-posture hooks in a cwd-.control gate
FALSE-NEGATIVES the loop-stall/posture logic when cwd is a nested non-governed git
repo (work/stimulus, core/autoany confirmed present) — for zero pollution benefit.
Redesign, matched to actual hook behavior:
- Guard wraps ONLY the two workspace-.control writers: knowledge-wakeup (SessionStart,
runs leverage-ship-sensor) + leverage-sensor (Stop). Once-per-event → negligible git cost.
- arc-continuation + autonomous-posture run UNWRAPPED (session-scoped, self-guarding,
write nothing to <ws>/.control). Fixes the MAJOR false-negative.
- l3-stability source-guarded internally (approve + no .control/audit when ungoverned);
reached only for L3-file edits, so NO per-edit git cost (MINOR fix).
- Guard emits nothing (removed the {"decision":"approve"} case) — no PreToolUse hook
goes through it anymore, and exit-0-no-output is the correct neutral (MINOR fix).
- Guard exec's the real script by full path: exit codes + stdout + $0/dirname pass through.
Test grows 4→7 cases: adds exit-code propagation, stdout passthrough, and l3 source-guard
both directions. CHANGELOG rewritten to match + fix the leverage-ship-sensor wording.
Round-2 of BRO-1926 Phase 2 prereq PR.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
Makes the bstack plugin (v0.35.0) a good citizen at personal scope, so it can be adopted in place of hand-wired
settings.jsonhooks without polluting non-bstack repos.hooks/bstack-hook-guard.sh— gates each hook on the workspace being bstack-governed (has a.control/dir). Non-governed → no-op (PreToolUse still emits{"decision":"approve"}so tools aren't left undecided).hooks/hooks.json— routes the 5 workspace hooks (knowledge-wakeup, arc-continuation, leverage-sensor, autonomous-posture, l3-stability) through the guard.bstack-autoupdatestays unguarded (keeps the install fresh, workspace-agnostic).tests/hook-guard.test.sh— 4 cases (no-op outside / approve for PreToolUse / runs inside / sibling$0resolution preserved).Why
The plugin loads at personal scope (
~/.claude/skills/bstack), so once enabled its hooks fire in every session, including non-bstack repos.leverage-sensor.py/leverage-ship-sensor.pydoos.makedirs(<ws>/.control, exist_ok=True)+ write, andl3-stability-pretoollogs to<ws>/.control/audit/. Unguarded, a global plugin litters.control/into unrelated repos (e.g. a client repo) every session. The guard fixes all current + future writers in one place.Design notes
$0/dirnameresolution is preserved — the guardexecs the real script by full path (unit-tested)..control/is always present there).Validation
shellcheck hooks/bstack-hook-guard.sh tests/hook-guard.test.sh: clean (one intentional SC2016 disabled)bash tests/hook-guard.test.sh: 4/4 passjq -e .hooks.json +claude plugin validate: passTicket: BRO-1926 Phase 2 prerequisite. (Phase 2 = local adoption; Phase 3 =
bstack bootstrapprefers the plugin.)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Tests
Chores