Add optional filter command for second-stage rule applicability#2
Merged
Conversation
Globs decide applicability by file path; the new `filter` front-matter field adds a content/relationship-aware check that runs after a glob match. It runs once per rule with the matched paths appended as argv and decides via grep-style exit codes: 0 applies, 1 skips, anything else / timeout / missing command fails open and applies (errors must never silently suppress a rule). - types: AgentRule.filter, FilterResult/FilterExecutor, RunOptions knobs (runFilters, filterTimeoutMs, filterExecutor, cwd), ReviewResult.warnings - rule.ts: parse `filter:` (quote-stripped; whitespace-only => unset) - filter-exec.ts: default subprocess executor (stdin closed, recursion marker) - runner.ts: concurrent filter stage in discoverApplicableRules; warnings - cli.ts: --no-filters and --filter-timeout; warnings to stderr/JSON - tests: parsing, discovery (pass/reject/error/throw/disabled), exec mapping - docs/examples/smoke updated Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The image built and ran only the smoke script; the unit tests could not run because `test/` was never copied in and the entrypoint had no test command. - Dockerfile: COPY test ./test (vitest is already an installed devDependency) - entrypoint.sh: `test` (vitest) and `check` (unit tests + smoke) commands - docker/README.md: document the hermetic test commands Verified: `docker run --rm agent-rules check` => 50 tests + 9 smoke checks pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
e96bdb2 to
277f7bc
Compare
btresedder
approved these changes
Jun 29, 2026
browep
approved these changes
Jun 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an optional
filtercommand to rules — a second-stage applicability check that runs after a rule's globs match. Globs match file paths; afilterlets a rule also depend on file content or relationships between the changed files (e.g. "only apply when a matched file actually contains a SQL string").The filter runs once per rule with the glob-matched paths appended as argv, and decides applicability via grep-style exit codes:
01"<name> (filtered)")warningsFail-open is deliberate: a broken or missing filter must never silently suppress a rule.
Changes
Core
types.ts—AgentRule.filter,FilterResult/FilterExecutor,RunOptionsknobs (runFilters,filterTimeoutMs,filterExecutor,cwd),ReviewResult.warningsrule.ts— parsefilter:front-matter (quote-stripped; whitespace-only ⇒ unset)filter-exec.ts(new) — default subprocess executor: tokenizes the command, appends matched paths, stdin closed,AGENT_RULES_SUBPROCESS=1recursion marker; maps exit codes toFilterResultrunner.ts— concurrent filter stage indiscoverApplicableRules(only spawns for rules that passed the glob stage); threads options +warningsthroughrunReviewcli.ts—--no-filtersand--filter-timeout; warnings to stderr / JSON.--listhonors filtersindex.ts— new public exportsTrust model:
filterexecutes arbitrary commands with the user's privileges — the same trust level the rules directory already carries. For reviewing untrusted diffs (e.g. fork PRs that could edit afilter),--no-filtersdisables all filter execution.Tests (14 added, 50 total)
runFilters:false, executor called only for filtered rules with correct paths), real subprocess exit-code mapping, smoke (--no-filters)Docker
test/and the entrypoint gainstest(vitest) andcheck(unit tests + smoke) commands;docker/README.mdupdated. Previously the image could only runsmoke.Docs:
README.md,docs/agent-rules-requirements.md(R32–R38),CHANGELOG.md, plusexamples/rules/no-raw-sql.mdandexamples/filters/touches-public-api.sh.Test plan
All hermetic, verified locally and in Docker:
Also green:
yarn typecheck,yarn lint,yarn format:check.🤖 Generated with Claude Code