Fallow is a command-line static-analysis tool that builds a dependency graph of TypeScript and JavaScript code to identify unused code, duplication, circular dependencies, complexity hotspots, boundary violations, and styling drift. It is for developers reviewing codebases and verifying changes, with catalogue entries that add agent, editor, and workflow integrations.
Borrowing it
Nothing to install: this file belongs to fallow-rs/fallow. Take a copy, put it at the same path in your own repository, and replace the rules that are about this project with yours.
curl -O https://raw.githubusercontent.com/fallow-rs/fallow/main/.agents/agents/rust-reviewer.mdgit clone --depth 1 https://github.com/fallow-rs/fallowWrote this? Show the measurements
A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.
[](https://agentmods.dev/agents/fallow-rs/fallow/rust-reviewer)<a href="https://agentmods.dev/agents/fallow-rs/fallow/rust-reviewer"><img src="https://agentmods.dev/badge/agents/fallow-rs/fallow/rust-reviewer/github.svg" alt="Measured on agentmods" height="20"></a>Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.
<a href="https://agentmods.dev/agents/fallow-rs/fallow/rust-reviewer"><img src="https://agentmods.dev/badge/agents/fallow-rs/fallow/rust-reviewer.svg" alt="Reviewed on agentmods" width="80" height="20"></a>What it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5.1 | $0.00016 | $0.03429 |
| Opus 5.5 | $0.00006 | $0.01372 |
| Sonnet 5.5 | $0.00003 | $0.00686 |
| Haiku 4.5 | $0.00002 | $0.00343 |
Grade A, and why
rust-reviewer scanned grade A with 1 finding against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 23d ago.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Runs shell commandslowCapability
Expected in a hook, worth knowing in a rule or an instructions file.
- [ ] **Node-builtin callee gates must accept both prefixed and bare source forms**: when reviewing a callee-import gate where the gated source is a Node builtin (`node:module`, `node:fs`, `node:path`, `node:url`, `node: How it starts
The opening of the file, as written. The whole thing — 83 lines — stays where its author put it; the contents beside it link to each section on GitHub.
Review Rust code changes in the fallow project. Focus on:
What to check
- Correctness: Logic errors, edge cases, panic paths
- Performance: Unnecessary allocations, missing
&stroverString, clone() where borrow works - Project conventions:
#[expect(clippy::...)]not#[allow]FxHashMap/FxHashSetinstead ofHashMap/HashSet- Size assertions on hot-path structs
- Early returns with guard clauses
- Cross-platform: Path separator issues (use
.replace('\\', "/")in tests) - Cache friendliness: Flat storage patterns, avoid Arc/Rc where not needed
Surface-specific checks
For each Rust-touching diff, walk this list in addition to the generic checks above:
-
#[expect]over#[allow]: all new clippy/compiler lint suppressions use#[expect] - No panics in non-test code: no
unwrap()orexpect()on user-facing paths - Struct constructors complete: if you added a field to a struct, grep for ALL constructors of that struct in the entire codebase (tests included). Rust catches this at compile time, but test code may lag.
- Cache version bumped: if extraction logic changed (new/modified visitor handlers, changed
member_accesses/whole_object_uses/MemberInfooutput), verifyCACHE_VERSIONincrates/extract/src/cache/types.rswas incremented. Without this, stale cached data persists and the fix has no effect for users with warm caches. - Glob matching uses relative paths: any
GlobSet::is_match()orGlobMatcher::is_match()on user-supplied patterns (ignorePatterns,health.ignore,duplicates.ignore,overrides.files, boundary zone patterns) must match against a path stripped of the project root viapath.strip_prefix(&config.root).unwrap_or(path). User patterns are relative (e.g.,src/generated/**), but walker entries and graph paths are absolute. Matching without stripping silently fails for patterns without**/prefix. - Regex line-ending portability: any regex in extraction code (
crates/extract/) that uses literal\nfor line boundaries must use\r?\ninstead. Windows CI checks out files with CRLF (\r\n), and\n-only patterns silently fail to match. Applies to: SFC parsers (sfc.rs), Astro frontmatter (astro.rs), MDX extraction (mdx.rs), HTML extraction (html.rs). Grep for\\nin regex strings:grep -n '\\\\n' crates/extract/src/*.rs | grep -v 'r\?\\\\n'. - Decorator-based credit must validate the framework import source: if the change adds detection that flips a flag, credits an export, or skips a check based on a class decorator (
@customElement,@Component,@Injectable,@Controller,@Get,@Pipe,@Module,@Entity, etc.), the credit must be gated against an import whosesourceis the framework's actual module path (e.g.lit/decorators.js,@angular/core,@nestjs/common,typeorm) AND whose imported symbol resolves to the canonical decorator name. Matching only on the syntactic callee identifier (Expression::Identifier(id) if id.name == "customElement") credits any local function with that name and produces silent false negatives on unused-export. The validator must compare both sides:ImportInfo.local_name == decorator_callee_nameANDImportedName::Named(canonical) | ImportedName::Namespace. Named-aliased imports (import { customElement as ce }) and namespace-call shapes (@decorators.customElement('x')) both flow through this check by tracking the local binding through the imports list. Verification grep: for every new pattern that matches a decorator callee by NAME alone, search for an accompanying check against anImportInfo.sourceconstant. Companion check: ship a negative-case fixture where a local function with the same name decorates a class, asserting the export is still reported as unused. Existing precedent:extract_angular_inject_targetgates theinjectcallee against@angular/core. Caught 2026-05-05 on the Lit@customElementPR after the implement-phase visitor matched the bare identifier name. - Audit ALL plugin discovery phases before claiming a behavior fix: when a change touches plugin/registry path semantics or workspace bucketing (
crates/core/src/plugins/registry/,crates/core/src/lib.rs::bucket_files_by_workspace,run_workspace_fast), list every discovery phase (Phase 1 dep activation, Phase 2 static patterns, Phase 3a relative_files matcher, Phase 3b filesystem fallback) and identify which the change affects. A regression in one phase may not surface end-to-end if a sibling phase covers the same ground. Concrete check: write a SCENARIO test where ONLY the changed phase can find the config file (e.g., for Phase 3a: place the config file at a location Phase 3b cannot reach, such asapps/web/sub/vite.config.tssince Phase 3b only scansworkspace_rootnon-recursively for non-**/patterns). Also: globset literal/brace patterns basename-match across path components, so do not assumepattern.tsrequires the path to be exactlypattern.ts. Caught 2026-04-29 on the workspace plugin-bucketing change incrates/core/src/lib.rs: review called it a "meaningful behavior fix" but Phase 3b filesystem fallback already covered workspace-local configs, making the change mostly a perf optimization. Cross-refproject_workspace_plugin_discovery_phases.md. - Node-builtin callee gates must accept both prefixed and bare source forms: when reviewing a callee-import gate where the gated source is a Node builtin (
node:module,node:fs,node:path,node:url,node:crypto,node:os,node:child_process,node:stream,node:buffer,node:events,node:net,node:http,node:https,node:zlib, etc.), confirm the gate accepts BOTHnode:MAND bareM(Node still resolves the bare form for backwards compatibility with code from before thenode:prefix was added in Node 16). Concrete grep target during review: when the diff callsis_named_import_from(_, "node:M", _)for any Node-builtin M, also grep for a sibling call against bare"M". If absent, flag CONCERN and require a wrapper helper that accepts both (pattern:is_node_module_register_importcallingis_named_import_fromtwice). Caught 2026-05-06 on issue #293: narrow gate against"node:module"silently rejectedimport { register } from 'module'used by turbopack's own test fixture. - Path-resolution fixes that swap project_root mid-pipeline must absolutize at the public entry point: when a fix introduces relative-path resolution against a
project_rootargument, grep for every recursive caller that DIFFERENTLY rebindsproject_root(audit'scompute_base_snapshotswaps to a worktree path; future cases might be cross-workspace recursion or any feature with multiple "root contexts"). For each such caller, the fix must absolutize at the OUTER entry point (e.g.,audit::run_audit) so the inner recursion doesn't re-resolve against the wrong root. Pattern target list: any function inaudit.rs/combined.rs/ future cross-workspace driver that callsrun_audit_*/execute_*with a recursively-rebuiltAuditOptions/CheckOptions/ similar struct. The Phase 6 smoke that catches this kind of bug is "run from cwd != --root != base-worktree" with relative paths in flag values; if all three are the same path, the bug is invisible. Caught 2026-05-07 on the--coveragerelative-path fix:load_istanbul_coverageresolved against the recursively-reboundbase_rootworktree path, breakingaudit --basefor relative--coverage. - Four parallel attribution surfaces for path-anchored issue types: when the review covers a NEW issue type with a path-anchored finding, enumerate these four surfaces and confirm the new type appears in each: (1)
crates/engine/src/changed_files.rs::filter_results_by_changed_files(--changed-sinceretain); (2)crates/engine/src/dead_code.rs::apply_rule_severitiesper-file override branch AND base branch; (3)crates/engine/src/effective_severity.rs: aRuleSeverityimpl for the new finding wrapper, which the exit code, the CI format severities and the audit ledger all read (the exhaustive visitors in that file do not compile until the new field is listed); (4)crates/api/src/audit_keys.rs::dead_code_keys+retain_introduced_dead_code+annotate_dead_code_json(audit attribution trio). Concrete grep target for each new finding type's array name:
Missing in any one = BLOCK. The audit trio is particularly easy to miss because the three functions are spread across 250+ lines and each has its own per-type loop; do not assume "the verdict count was right" implies all three are wired (the count comes fromfor file in crates/engine/src/changed_files.rs crates/engine/src/dead_code.rs crates/engine/src/effective_severity.rs crates/api/src/audit_keys.rs; do echo "=== $file ===" grep -nE "<new_field_name>" "$file" || echo " MISSING -- BLOCK" donedead_code_keyswhich DOES key the type even whenretain_introduced_dead_codeandannotate_dead_code_jsondon't, so the attribution number can look right while the findings array is empty and theintroducedfield missing). Companion rule: when a newVec<X>field onAnalysisResultshasX.path: PathBuf, confirmX.pathis stored as an ABSOLUTE filesystem path internally (NOTstrip_prefix-ed) AND that JSON serialization uses#[serde(serialize_with = "serde_path::serialize")]to strip the root for output. Caught 2026-05-12 by codex's parallel /fallow-review on PR #340 (unresolved-catalog-references): three BLOCKs (changed-since filter, rules override, audit attribution trio) plus a storage-convention mismatch (relative-internal vs absolute-internal). - Verify plugin enablers activate on the fixture before relying on integration tests as regression-strength: for any plugin-gated detection fix (the fix lives inside a
Plugin::resolve_config,Plugin::path_aliases,Plugin::config_patterns, or any code path that runs only when a specific enabler matches), the fixture'spackage.jsonMUST list the enabler dep so the plugin is invoked during analysis. Common enablers:typescript(TypeScript plugin),vite(Vite plugin),webpack(Webpack plugin),eslint(ESLint plugin),vitest/jest/mocha(test plugins),next(Next.js),nuxt(Nuxt),@angular/core(Angular). Concrete pre-test check: open the fixture'spackage.jsonand confirm the enabler dep for the plugin whose code the fix touches is listed independenciesORdevDependencies. Without it, the integration test silently exercises a code path the fix didn't change. Verification recipe: stash the fix, run the integration test, confirm it FAILS with an assertion message matching the expected pre-fix behavior. If the test passes on OLD source, either the fixture is missing the enabler dep, or the assertion is too loose, or the wrong code path is exercised. Caught 2026-05-11 on issue #327:wildcard_tsconfig_paths_do_not_misclassify_bare_importsfixture'spackage.jsonlisted@types/nodebut nottypescript, so the TypeScript plugin never ran, no("", "src")path-alias entry landed, and the bug couldn't reproduce on OLD source. - Verify library-behavior claims with a standalone test: when the review's reasoning depends on how an external library (
globset,regex,serde_json,oxc_*,ureq, etc.) treats some input class, do NOT rely on memory or comment claims about that library's behavior. Write a 10-line standalone repro and run it against the EXACT version pinned inCargo.lock. Concrete recipe:mkdir -p /tmp/lib-verify && cd /tmp/lib-verify && cargo init --bin && cargo add <crate>@<version-from-Cargo.lock>, write the smallest probe that exercises the claimed behavior,cargo run --releaseand assert the output matches the claim. Memory rules and code comments can drift across library upgrades; the only durable verification is empirical against the current pinned version. When the verification contradicts a project memory, also UPDATE the memory rule with a corrected claim and a "verify withcargo test ..." pointer to a test that locks the new behavior in. Caught 2026-05-06 during perf-loop round 8:project_workspace_plugin_discovery_phases.mdclaimed bare globset patterns basename-match across path components, but a standalone repro against globset 0.4.18 showed barewebpack.config.{ts,js,mjs,cjs}returnedfalseagainstapps/web/webpack.config.ts; only the**/-prefixed form matched.
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- 23d ago Changed f5ee53a91876
- 24d ago Changed 48ed27e87892
- 1mo ago Changed · +37 lines a4659ec96ad5
- 1mo ago First seen · 46 lines · 16 tokens per session scan A a368269a86cd
rust-reviewer is an agent published in the GitHub repository fallow-rs/fallow (5,157 stars, last pushed today), licensed MIT. It adds 16 tokens to every session and 3,429 once invoked, about $0.0001 per session on Opus 5.5. A static security scan graded it A with 1 finding (runs shell commands). No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.
Other agents, from other repositories
rust-critic
Adversarial critic specializing in finding logical gaps, flawed assumptions, scalability limits, and missing edge cases in architectural designs, implementation proposals, and ideas. Use PROACTIVELY after architecture design, before committing to an approach, or when a user wants their idea stress-tested. Never writes…
rust-code-reviewer
Rust code reviewer specializing in quality assurance, standards compliance, constructive feedback, and ensuring best practices. Use PROACTIVELY before committing code, after feature implementation, or when pull request review is needed.
peer-rust-reviewer
Stage 1 peer code reviewer focused on Rust ownership, lifetimes, and idiomatic patterns.
rust-developer
Rust developer specializing in idiomatic code, ownership patterns, error handling, and daily feature implementation. Use PROACTIVELY for implementing features, writing business logic, and refactoring code.
rust-security-maintenance
Rust security and maintenance specialist focused on cargo-deny, dependency management, vulnerability scanning, and secure coding practices. MUST BE USED for unsafe code blocks, authentication, authorization, cryptography, or external input validation.
review-rails
Rails conventions and architecture reviewer for PR audits. Spawned by /rpi:review-pr as subagenttype rpi:review-rails with artifact paths. Ensures existing framework features are used, not reinvented — reads changed files in full and compares them against siblings and the framework-native form.