[luv-88] feat: convention-based policy auto-discovery#89
Conversation
Add git-hooks-like auto-discovery: drop *policies.{js,mjs,ts} files into
.failproofai/policies/ at project or user level and they're loaded
automatically — no --custom flag or config changes needed.
- Add discoverPolicyFiles() and loadAllCustomHooks() to custom-hooks-loader
- Convention hooks registered with "convention/" prefix for telemetry
- PostHog telemetry: convention_policies_loaded event, is_convention_policy
flag on hook_policy_triggered
- listHooks() shows discovered convention policies
- CLI help updated with CONVENTION POLICIES section
- 6 new unit tests, 16 new e2e tests covering discovery, union loading,
fail-open, file filtering, coexistence with --custom
- Example files in examples/convention-policies/
- Mintlify docs and README updated
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 45 minutes and 29 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThis PR adds convention-based policy auto-discovery, enabling automatic loading of Changes
Sequence DiagramsequenceDiagram
participant User
participant FileSystem as Filesystem
participant Loader as Policy Loader
participant Registry as Hook Registry
participant Handler as Event Handler
participant Policy as Loaded Policy
User->>FileSystem: Places *policies.{js,mjs,ts} files<br/>in .failproofai/policies/
Handler->>Loader: loadAllCustomHooks(customPath)
Loader->>FileSystem: discoverPolicyFiles(project dir)
FileSystem-->>Loader: [sorted filenames]
Loader->>FileSystem: discoverPolicyFiles(user dir)
FileSystem-->>Loader: [sorted filenames]
loop For each discovered file
Loader->>FileSystem: read & import file
FileSystem-->>Policy: module loaded
Policy->>Registry: customPolicies.add(hook)
Registry-->>Loader: hook registered
Loader->>Loader: tag hook with __conventionSource
end
Loader-->>Handler: {hooks[], conventionSources[]}
Handler->>Registry: register all policies<br/>(custom/ & convention/)
Handler->>Handler: emit convention_policies_loaded<br/>telemetry
User->>Handler: trigger tool event
Handler->>Registry: query policies
Registry-->>Handler: matching policies
Handler->>Policy: execute (custom or convention)
Policy-->>Handler: allow/deny/instruct decision
Handler->>Handler: log is_convention_policy<br/>in telemetry
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly Related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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.
Actionable comments posted: 4
🧹 Nitpick comments (1)
examples/convention-policies/workflow-policies.mjs (1)
35-40: Consider defensive coding for theincludescheck.Line 36 has a subtle edge case: if
ctx.toolNameisnull(notundefined), the??operator won't trigger andincludes(null)would returnfalse(safe), but the intent is clearer with explicit null handling.Current code is functionally correct since
includes()coercesnullto"null"string for comparison. No action required, just noting for clarity.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/convention-policies/workflow-policies.mjs` around lines 35 - 40, The conditional in the async fn should defensively normalize ctx.toolName before calling includes: ensure the value used in ["Write","Edit"].includes is coerced to a string or explicitly nullish-fallbacked (e.g., convert ctx.toolName to a string or use an empty-string fallback) so that the includes check is unambiguous; update the fn's condition that references ctx.toolName and the includes call accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@CHANGELOG.md`:
- Line 6: Update the changelog entry that currently references PR `#88` to the
correct PR number `#89`; specifically edit the line mentioning "Convention-based
policy auto-discovery: ... (`#88`)" in CHANGELOG.md and change the trailing PR
reference to "(`#89`)" so the entry matches the actual PR.
In `@README.md`:
- Around line 223-230: The fenced code block in README.md is missing a language
identifier; update the triple-backtick opening fence for the directory example
to include a language (e.g., change "```" to "```text") so the static analyzer
and renderers treat it as plain text; locate the block containing
".failproofai/policies/security-policies.mjs" and
"~/.failproofai/policies/my-policies.mjs" and add the language specifier to the
opening fence.
In `@src/hooks/custom-hooks-loader.ts`:
- Around line 184-190: The code tags convention hooks by name using
conventionHookNames, which can mis-tag explicit hooks with the same name;
instead, during convention file loading collect the actual hook object
references into a Set (e.g., conventionHookSet) rather than names
(conventionSources -> collect hook objects), then iterate allHooks and mark only
those where conventionHookSet.has(hook) by setting (hook as CustomHook & {
__conventionSource?: boolean }).__conventionSource = true; update references
from conventionHookNames to the new Set to avoid name collisions.
In `@src/hooks/manager.ts`:
- Around line 670-679: Replace the fragile file.split("/") filename extraction
with Node's cross-platform basename: import basename from "node:path" alongside
the existing resolve/dirname imports, then change all occurrences where filename
is derived (currently const filename = file.split("/").pop() ?? file;) to use
const filename = basename(file); ensure this replacement is applied in the
success, failure and catch blocks around the hooks handling (the blocks
referencing filename and hooks.map(...)) so Windows backslashes are handled
correctly.
---
Nitpick comments:
In `@examples/convention-policies/workflow-policies.mjs`:
- Around line 35-40: The conditional in the async fn should defensively
normalize ctx.toolName before calling includes: ensure the value used in
["Write","Edit"].includes is coerced to a string or explicitly
nullish-fallbacked (e.g., convert ctx.toolName to a string or use an
empty-string fallback) so that the includes check is unambiguous; update the
fn's condition that references ctx.toolName and the includes call accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a19f4bc8-d844-4359-a3d3-59ed7aa118ef
📒 Files selected for processing (15)
CHANGELOG.mdREADME.md__tests__/e2e/helpers/fixture-env.ts__tests__/e2e/hooks/custom-hooks.e2e.test.ts__tests__/hooks/custom-hooks-loader.test.ts__tests__/hooks/handler.test.ts__tests__/hooks/manager.test.tsbin/failproofai.mjsdocs/configuration.mdxdocs/custom-policies.mdxexamples/convention-policies/security-policies.mjsexamples/convention-policies/workflow-policies.mjssrc/hooks/custom-hooks-loader.tssrc/hooks/handler.tssrc/hooks/manager.ts
- Fix PR number reference in CHANGELOG (#88 → #89) - Add `text` language identifier to fenced code block in README - Track convention hooks by object reference instead of name to prevent mis-tagging explicit hooks that share a convention hook name - Use `basename()` instead of `file.split("/")` for cross-platform paths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix PR number reference in CHANGELOG (#88 → #89) - Add `text` language identifier to fenced code block in README - Track convention hooks by object reference instead of name to prevent mis-tagging explicit hooks that share a convention hook name - Use `basename()` instead of `file.split("/")` for cross-platform paths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix PR number reference in CHANGELOG (#88 → #89) - Add `text` language identifier to fenced code block in README - Track convention hooks by object reference instead of name to prevent mis-tagging explicit hooks that share a convention hook name - Use `basename()` instead of `file.split("/")` for cross-platform paths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…olicyParams (#91) * feat: convention-based policy auto-discovery from .failproofai/policies/ Add git-hooks-like auto-discovery: drop *policies.{js,mjs,ts} files into .failproofai/policies/ at project or user level and they're loaded automatically — no --custom flag or config changes needed. - Add discoverPolicyFiles() and loadAllCustomHooks() to custom-hooks-loader - Convention hooks registered with "convention/" prefix for telemetry - PostHog telemetry: convention_policies_loaded event, is_convention_policy flag on hook_policy_triggered - listHooks() shows discovered convention policies - CLI help updated with CONVENTION POLICIES section - 6 new unit tests, 16 new e2e tests covering discovery, union loading, fail-open, file filtering, coexistence with --custom - Example files in examples/convention-policies/ - Mintlify docs and README updated Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address CodeRabbit review feedback - Fix PR number reference in CHANGELOG (#88 → #89) - Add `text` language identifier to fenced code block in README - Track convention hooks by object reference instead of name to prevent mis-tagging explicit hooks that share a convention hook name - Use `basename()` instead of `file.split("/")` for cross-platform paths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: configurable `hint` in policyParams for deny/instruct messages Allow users to append custom guidance to deny and instruct messages by adding a `hint` string to any policy's policyParams config. The hint is appended to the reason before it's sent back to Claude, giving actionable next steps without modifying the policy itself. Works with built-in, custom, and convention policies. Non-string and empty values are silently ignored for backward compatibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add missing vitest expect import in policy-params e2e test Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address CodeRabbit review feedback (round 2) - Remove duplicate policyParams hint test suite, merge unique sanitize-jwt test - Fix CHANGELOG PR references from #89/#90 to #91 - Extract appendHint() helper to trim/normalize hint concatenation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Summary
*policies.{js,mjs,ts}files into.failproofai/policies/at project or user level and they're loaded automatically — no--customflag or config changes neededconvention/prefix for PostHog telemetry (convention_policies_loadedevent,is_convention_policyflag)listHooks()displays discovered convention policiesexamples/convention-policies/Test plan
bun run test:run— 853 unit tests passbun run test:e2e— 198 e2e tests pass (16 new convention tests)bun run lint— no errorsbun run tsc --noEmit— no type errorsbun run build— builds successfully🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
.failproofai/policies/directories at project and user scope without requiring configuration or flags.Documentation