Releases: fallow-rs/fallow
v2.86.0: fallow security candidate catalogue + JSON kind discriminator
Highlights
fallow security: a local security-candidate layer for agent verification
fallow security is an opt-in command that surfaces local, deterministic security candidates (not verified vulnerabilities) for a human or coding agent to verify. fallow ships the signal; your agent confirms and fixes.
This release expands it from one rule to a data-driven catalogue spanning 9 CWE categories:
- dangerous HTML (CWE-79), OS command injection (CWE-78), code injection (CWE-94), SQL injection (CWE-89), SSRF (CWE-918), path traversal (CWE-22), open redirect (CWE-601), runtime-selectable crypto algorithm (CWE-327), unsafe deserialization (CWE-502)
- Plus the original client-server-leak rule: a
"use client"file that transitively reaches a non-publicprocess.envsecret, with a structural import-hop trace.
Design choices that keep the signal honest:
- Conservative by default. A candidate fires only when the relevant argument is non-literal; a fully literal value (
el.innerHTML = "<b>x</b>",child_process.exec("ls")) is never flagged. fallow prefers false-negatives over false-positives. - Parameterized SQL is not flagged. Tagged-template
sql`${x}`and the object form.execute({ sql, args })bind safely and do not fire; only string concatenation, interpolated template literals into.query()/.execute(), andsql.raw(...)do. - Node sinks are provenance-gated to their import source (command injection to
node:child_process,vmcode injection tonode:vm, path traversal tonode:path, crypto tonode:crypto, deserialization tojs-yaml/node-serialize). - Build configs and test files are excluded from candidate generation.
- Opt-in and isolated. The rules default to
off, never appear under barefallowor theauditgate, andfallow securityis the only surface. Output is human, JSON, or SARIF (SARIF emits atnotelevel with per-category rule IDs and CWE tags for the GitHub Security tab). - Blind spots are counted in-band, so an empty result with a non-zero unresolved count is never a clean bill.
Adding a CWE category is a single catalogue entry with no code churn. Scope which categories run with security.categories include / exclude lists, and suppress a file with // fallow-ignore-file security-sink.
Changed
- JSON envelope outputs now carry a top-level
kinddiscriminator (dead-code,health,dupes,combined,audit,security, ...), so consumers identify the shape bykindinstead of field-presence heuristics.schema_versionis bumped;--legacy-envelopekeeps the previous root shape for one migration cycle. (Closes #413.)
Fixed
Detection and integration fixes across this cycle:
- A bare
"@"plugin alias no longer swallows@scope/*npm packages (#838). declareambient class properties are no longer reported as unused class members (#839).new URL('./dir', import.meta.url)directory targets no longer surface as unresolved imports (#840).- Quoted globs in
package.jsonscripts are registered as entry points (#841). - NestJS lifecycle and handler methods are credited as used (#843).
- A method called on an
instanceof-narrowed value is credited as a use of that class's member (#845). - Oxlint config packages referenced through
extendsare credited (#846). - Bun
bunfig.tomlpreload files are tracked, and the Bun plugin activates on@types/bun(#847). duplicate-exportno longer over-reports across unrelated packages (#848).- Pinia stores auto-imported by
@pinia/nuxtare tracked (#740). - Nuxt composables and utils referenced only through auto-imports are tracked (#739).
resolve.aliasshared via an imported identifier or built with spreads is no longer ignored. Thanks @michaljuris for the report (#811).- Svelte markup
<script src>tags no longer surface as unresolved imports. Thanks @codingthat for the report (#835). - The GitHub Action now uploads SARIF on public repositories. Thanks @cloud-walker for the detailed report (#817).
- The GitHub Action no longer logs a spurious
SARIF generation failedwarning on repos with issues (#813). - Two built-in plugins sharing a config file no longer emit an un-actionable collision warning (#808).
fallow healthsurfaces CRAP coverage-source consistency in JSON and adds a tunable secondary refactor band (#474).
Full Changelog: v2.85.0...v2.86.0
v2.85.0: health as a CI gate, broader framework detection, and fallow impact
v2.85.0: health as a CI gate, broader framework detection, and the new fallow impact report
A large release that lands the accumulated work since v2.84.0. The headline changes for everyday use: fallow health can now gate CI on a score, and framework/plugin detection got broader and more accurate. On top of that, a new local fallow impact report, opt-in telemetry, and a coverage-intelligence verdict for Fallow Runtime users.
fallow health as a CI gate
fallow health can now act as a real CI gate (#790):
--min-score Nis now authoritative: the build fails when the health score drops belowN, and complexity findings become informational rather than hard failures. (Previously--min-scoredid not take effect; if you were already passing it, it now does what it says.)--report-onlyruns the full health output and always exits 0, for teams that want the signal without gating yet.- No gate flag behaves as before (any finding fails), so existing pipelines are unchanged unless you opt into the flags above.
- The churn-hotspot window now shows in the metrics line and the markdown vital-signs section (#799).
fallow health --min-score 80 # fail CI below 80
fallow health --report-only # always exit 0, just reportNew and improved framework detection
The broadest free win this cycle is fewer false positives across more frameworks:
- New Velite plugin (#774).
- rspress: the
@themevirtual module is credited (#787). - SvelteKit: layout-reset route filenames (
+page@.svelte,+layout@named.svelte) are recognized as entry points (#797). - Nuxt:
@nuxt/contentcontent.config.tsis credited as a default-export entry (#801). - ESLint: meta-preset plugins pulled in via
peerDependencies(e.g.@antfu/eslint-config) are credited as used (#805). - oxlint: CLI tooling packages such as
oxlint-tsgolintare credited as used (#802). - React Compiler:
babel-plugin-react-compileris credited viareactCompilerPreset()(#764). - Danger: the Danger plugin activates from dangerfiles.
Plus correctness fixes that remove false "unused" reports:
- Class members used via typed destructure bindings are credited (#762).
- Workspace imports resolve when tsconfig
pathspoint at an unbuiltdist(#763). - A binary invoked as
bun --flag <bin>in a package script is credited (#761). - Workspace class members and SCSS include paths survive plugin-result merging (#783).
fallow impact (new, free, local)
fallow impact is an opt-in, local report that shows whether your codebase is trending cleaner over time. It is off by default, writes a single gitignored file at .fallow/impact.json, and never affects exit codes.
fallow impact enable # start recording (off by default)
fallow impact # show the reportIt surfaces three things:
- Now: how many findings the latest run reported (#788).
- Trend: the change in finding count versus the previous run (#788).
- Resolved: which findings you actually fixed between runs, distinguished from ones you suppressed or moved (#803).
- Pre-commit saves: how often a
fallow auditpre-commit gate blocked a bad change until it was fixed (#788). - Whole-project track: the report can be credited from full
fallowruns, including duplication (#812). - A read-only
impactMCP tool lets agents query the report without enabling or disabling it (#804).
The store is gitignored on enable and unsupported formats are rejected up front (#795).
Duplication
- CRLF-invariant clone fingerprints (#809): a clone's fingerprint is now stable across platforms regardless of line endings.
- Forward-slash path normalization in human output on Windows (#807).
- Trace a clone family by fingerprint: drill into a specific clone group via its fingerprint, with hardened deep-dive handles (#769).
CLI and source evidence
fallow upload-static-findings(#796): uploads findings for the source-evidence viewer.- Repo-relative source-map paths (#810): each source map's repo-relative path is uploaded so monorepo source resolution works.
Configuration
- Multiple-config warning (#780):
fallowwarns when more than one config file coexists in a single directory, so which config is active is never a surprise. - Configuration hint on empty
fallow flagsoutput (#785): an empty flags result now points you at how to configure flag detection.
Opt-in telemetry
Optional, coarse usage telemetry to help prioritize framework and feature work. It is off by default, never collects paths, names, source, or config, and has fleet-wide kill switches (#770):
fallow telemetry enable # opt in
fallow telemetry inspect # print the exact payload, send nothing
# DO_NOT_TRACK=1 or FALLOW_TELEMETRY_DISABLED=1 hard-disables itCoverage intelligence (Fallow Runtime)
For Fallow Runtime users with coverage data, fallow health gains a coverage-intelligence verdict that weighs static findings against runtime coverage evidence (#768). Requires Fallow Runtime and a coverage source.
Supply-chain and self-hardening
Internal hardening of fallow's own build and analysis path. No action needed for users:
- Analysis only ever shells out to git, nothing else (#773).
- Guards against hidden-unicode and agent-file poisoning, with a CI gate on baseline drift (#779, #794).
--ignore-scriptson dev-dependency and release-pipeline installs (#775, #782).- A documented signing-key rotation and compromise-response runbook (#781).
Thanks
Reporters who surfaced detection gaps fixed in this release:
- @codingthat for SvelteKit layout-reset entry points (#791).
- @Noktomezo for ESLint meta-preset plugins (#754) and
bunscript binaries (#755). - @kevinmichaelchen for
oxlint-tsgolint(#753). - @callstackincubator for rspress
@theme(#756). - @asciimoo for typed-destructure class members (#752).
Full Changelog: v2.84.0...v2.85.0
v2.84.0: @sanity/pkg-utils support
@sanity/pkg-utils support
Projects built with @sanity/pkg-utils no longer need to hand-list their build configs in entry.
A new pkg-utils plugin keeps package.config.{ts,js,mts,mjs,cts,cjs} and package.bundle.{ts,js,mts,mjs,cts,cjs} reachable automatically, at the repo root and in every workspace package. The tool discovers these files by filename rather than importing them from source, so they previously surfaced as unused-file and had to be listed manually.
The plugin activates only on an exact @sanity/pkg-utils dependency, so a plain @sanity/client consumer with a stray package.config.ts keeps reporting it. It also credits @sanity/pkg-utils as a tooling dependency. The credit is scoped to the two build-config filenames, so an unreferenced ordinary source file still reports.
pkg-utils monorepos can drop the package.config.ts / package.bundle.ts lines from their entry config entirely.
Full Changelog: v2.83.0...v2.84.0
v2.83.0: line-move-tolerant runtime-coverage baselines, Nuxt auto-imports, more framework plugins
Runtime coverage
- Cross-surface function identity. Runtime coverage now adopts the
fallow-cov-protocolv2FunctionIdentity(afallow:fn:<hash>join key over file, name, and start line). One value identifies a function across findings, hot paths, blast-radius, and importance entries, and across V8 / Istanbul / oxc producers (columns are excluded from the hash). Old artifacts without an identity keep working via the existing path/name/line fallback. (Closes #506) - Baselines survive line moves. Function identities can now carry a
source_hash: a content digest of the function body that excludes position. Runtime-coverage baselines match on it in addition tostable_idand the legacy id, so a function that moves to a different line but is otherwise unedited stays suppressed; editing the body re-surfaces it for review. (Closes #742)
Framework support
- Nuxt components referenced only by template tag are now tracked. A new
auto_importsplugin capability synthesizes a real graph edge from<MyComponent />-style tags to their component files (directory-prefixed PascalCase,<Lazy...>,.client/.server/.global). Additive by default; opt intoautoImports: trueto report genuinely-unreferenced convention components as unused. Thanks @Hal-Spidernight for the detailed proposal. (Closes #704) - New built-in plugins: Contentlayer, Obsidian, WXT, Varlock, OpenNext Cloudflare, and browser-extension (WebExtension / Chrome MV2+MV3 manifests) projects no longer report their config files, entrypoints, and manifest-declared runtime files as unused.
Fixes
ignoreUnresolvedImportsconfig field accepts import-specifier globs to suppress expectedunresolved-importfindings. Thanks @OmerGronich. (Closes #726)- tsdown
.mts/.ctsconfig files are treated as entry points. Thanks @eojoel. (Closes #744) fallow auditno longer emits spurious Nuxt/Astro prerequisite warnings on its base pass (#705).- Workspace packages without an
exportsmap resolve missing prebuild output back to source. (Closes #725) - File health scores surface high CRAP-risk files even when their Maintainability Index is not the lowest. (Closes #554)
- POSIX-rooted paths from Linux tooling stay absolute on Windows. (Closes #614)
- GitHub Action:
artifacts-dirwrites generated files outside the repository root. Thanks @Guria. (Closes #735)
Notes
stable_id values use the reconciled cross-producer recipe shared by the CLI, the fallow-cov sidecar, and cloud aggregation. Runtime-coverage baselines written on a previous version re-baseline once.
Full Changelog: v2.82.0...v2.83.0
v2.82.0: framework plugins, quieter output, safer fixes
Highlights
A broad framework-plugin release: ng-packagr Angular libraries, electron-vite, Playwright webServer, SvelteKit remote functions, k6, Supabase Edge Functions, Mintlify, and Iconify icon packages all stop producing false unused findings out of the box. Plus quieter JSON output, smarter fallow fix, and a binary-verification model that no longer relies on npm install scripts.
Features
Framework plugins
- Angular ng-packagr libraries:
lib.entryFilefromng-package.json/ng-package.prod.json(defaultsrc/public_api.ts) is now a package entry point, resolved relative to the config directory, so the library public API and everything reachable through its re-export chain stays alive instead of reporting as unused. Nested secondary-entry-point configs (packages/lib/client/ng-package.json) are scanned too, and the plugin also activates on ang-packagrdependency. (#606) - electron-vite: renderer, preload, and main entries declared in
electron.vite.config.*build.rollupOptions.input(string, array, and object forms, includingresolve(__dirname, ...)path helpers) are treated as entry points. (#600) - Playwright
webServer.command: CLI dependencies and local server scripts launched bywebServer.command(object and array forms) are credited and kept reachable, with paths resolved relative to the config directory. (#621) - Vitest
test.alias/resolve.alias: now recognized invite.config.*, intest.projects[*], and invitest.workspace.*files, covering mock and virtual-module aliases so they stop reporting as unresolved imports, unlisted dependencies, or unused exports. (#601) - SvelteKit remote functions:
src/**/*.remote.{ts,js}files and their exportedquery/command/form/prerenderfunctions are kept reachable. (#611) - k6: load-test scripts (
*.k6.{js,ts,...}) act as runtime entry surfaces, thek6CLI is credited, and thek6/k6/*runtime namespace is recognized. (#625) - Supabase Edge Functions: Deno
jsr:/npm:/ URL imports resolve correctly, andsupabase/functions/*/index.*files are runtime entry roots. (#624) - Mintlify:
docs.json/mint.json, themint/mintlifyCLI, and MDX content under the docs root are recognized via a new built-in plugin. (#626) - Iconify:
@iconify-json/<prefix>packages used only through static icon strings (<Icon name="jam:github" />) are credited as referenced. Thanks @nicolas-deyros for the report. (#608)
Bug fixes
- Vite path-helper entries:
build.rollupOptions.input/build.lib.entrywritten asresolve(__dirname, ...)/path.resolve(...)/join(...)/import.meta.dirnamecalls are evaluated to entry patterns; the shared extractor also benefits Webpack, Rspack, Rsbuild, Rolldown, Rollup, Vitest, and Drizzle. (#604) - Error subclass
name: anamemember on a class whose heritage reaches a native error constructor (Error,TypeError, ...) is treated as runtime-used rather than an unused class member. (#620) new Class().method()receivers: methods called on a freshly-constructed instance, directly or through a self-returning fluent chain, are credited. (#605)fallow fixlow-confidence exports: export removals are withheld for files under off-graph consumer directories (__mocks__,e2e,fixtures, ...) and files with unresolved imports, so a fix never turns an analysis false positive into a broken build. Askipped_low_confidence_exportscount and per-entryskip_reasonare added to JSON output. (#602)- Quieter JSON / CI output: repeated workspace and plugin diagnostics on stderr are aggregated into one summary line per glob pattern or framework, instead of one line per directory. The structured
workspace_diagnostics[]array is unchanged. (#637)
Changed
- Binary verification moved from
npm install'spostinstallhook to first-run insidefallow,fallow-lsp, andfallow-mcp. The Ed25519 signature + SHA-256 digest check is preserved bit-for-bit (same key, same fail-closed behavior); a sentinel caches the verified state per install dir + binary hash. This removes the dependency on npm install scripts ahead of npm RFC 868.fallow --versionnow prints averified: yesline;FALLOW_SKIP_BINARY_VERIFY=1warns once per invocation when active, andFALLOW_VERIFY_CACHE_DIRredirects the sentinel for read-only install dirs. --performanceannotations: theparse/extractline gains a(parallel: ~Nms CPU)suffix, reused health stages read(measured above), and both breakdowns gain an(other)row so stages reconcile withTOTAL. JSON output gains observationalparse_cpu_ms/shared_parsefields. (#481)
v2.81.0: OpenCode, RedwoodSDK, Wuchale, and Lexical plugins plus reliability hardening
Framework support
- OpenCode:
.opencode/plugins/*modules,@opencode-ai/plugin, and npm plugins declared inopencode.jsonare recognized as loaded, so they no longer report as unused files or dependencies. (Closes #629) - RedwoodSDK:
src/worker.{ts,tsx,js,jsx,mts,mjs}and files reached through therwsdk/viteworker stay reachable without broad dynamic-load suppressions. (Closes #632) - Wuchale:
wuchale.config.js, adapter packages imported only from that config, and static ViteconfigFilereferences are kept reachable in active Wuchale projects. (Closes #631) - Lexical: classes extending
DecoratorNode,ElementNode, orTextNodeno longer report their framework-invoked lifecycle methods (getType,clone,createDOM,updateDOM,decorate, and the serialization/DOM set) as unused class members. (Closes #628)
Detection fixes
node:sqliteand other mandatory-node:-prefix builtins (node:sea,node:test,node:test/reporters) are recognized as platform builtins when written with thenode:prefix, so they no longer surface as unlisted dependencies or unresolved imports. Bare forms remain ordinary npm packages. (Closes #627)- Scaffold template package assets such as
template-*,templates/**, andscaffolds/*published throughpackage.json#filesare treated as support entry points, including in monorepo workspaces. (Closes #635) - Oxlint
jsPluginspackages loaded only throughjsPlugins(for exampleeslint-plugin-testing-library,eslint-plugin-playwright) are credited as referenced dependencies instead of reporting as unused. Thanks @pasTa4667 for the patch. (Closes #607) - Re-export source edges now participate in resolver diagnostics: missing
export { x } from "./missing"sources report as unresolved imports, and package re-exports report their actual source line. Thanks @M-Hassan-Raza for the patch. (PR #666)
Reliability and integrations
fallow ci reconcile-reviewapplies stale review cleanup fail-fast through a typed plan, preflights provider targets before mutation, and reportsapply_hint,failed_fingerprints, andunapplied_fingerprints. (Closes #459)fallow watchdebounces directly and recovers when the watched root is moved, deleted, or recreated, applying the same source/config/ignore filters as discovery. (Closes #456)fallow coverage setupresumes safely after interruption, persisting step progress in.fallow/setup.json, guarding concurrent runs with.fallow/setup.lock, and writing state atomically. JSON planning mode stays read-only. (Closes #460)- Ownership-aware health hotspots emit a required
ownership_statediscriminator (active,unowned,declared_inactive,drifting), suppress vague drift only for matched active owners, and add collision-safe author handles. The new--ownership-emails anonymizedspelling is accepted alongsidehash. (Closes #478) - Programmatic and Node analyses keep per-call thread and diff scope:
diff_file(Rust) /diffFile(N-API) per call, each building its own Rayon pool. (Closes #469) - Fallow cloud API calls honor
Retry-Afterbackoff (capped at 60s), parse error envelopes consistently, accept a custom trust bundle viaFALLOW_CA_BUNDLE, and exit 7 when setup or transport failures prevent every upload. (Closes #464)
Install
npm install -D fallow@2.81.0
# or
cargo install fallow-cli@2.81.0Full Changelog: v2.80.0...v2.81.0
v2.80.0: optional review guidance, summary scope, Fumadocs plugin
Highlights
This release adds optional inline review guidance and a diff-scoped sticky summary mode for CI integrations, ships a built-in Fumadocs plugin, and lands a batch of false-positive fixes for Wrangler precedence, TanStack Start virtual modules, MDX code fences, Node script runners, Bun's bare runtime module, prebuild package maps, and React Router type imports.
Added
-
Inline review comments can include optional rule guidance. Set
FALLOW_REVIEW_GUIDANCE=true(or GitHub Actionreview-guidance: true) to append collapsed "What to do" blocks toreview-githubandreview-gitlabcomments using the existing rule guides fromfallow explain. Default stays off.fallow/unused-typereview comments also pick up the same one-line export-stripping suggestions asunused-export. Thanks @OmerGronich for the request. (Closes #659) -
Sticky PR/MR summary comments can scope project-level findings to the diff. Set
FALLOW_SUMMARY_SCOPE=diff(or GitHub Actionsummary-scope: diff) to apply the diff filter to dependency, catalog, and override findings inpr-comment-githubandpr-comment-gitlab. The defaultallpreserves the existing behavior where sticky summaries include those findings even when their fixedpackage.jsonor workspace-manifest anchor line is outside the diff. Inline review comments are unaffected. Thanks @OmerGronich for the request. (Closes #661)
Fixed
-
Fumadocs MDX projects no longer report configured docs content as unused. A built-in Fumadocs plugin activates from
fumadocs-mdx,fumadocs-core,fumadocs-ui, orsource.config.*, keepssource.config.*and.source/**/*.{ts,tsx,js,jsx,...}reachable, suppressesfumadocs-mdx:*virtual imports, credits packages imported by the source config, and extracts literaldirvalues fromdefineCollections,defineDocs, and directdefineConfig({ collections })entries as MDX content roots. (Closes #633) -
Wrangler config precedence now matches Wrangler's selected config file. Projects with multiple sibling
wrangler.*config files previously credited everymainvalue as an entry, so stale migration leftovers (e.g.wrangler.toml) could keep dead worker files alive. Fallow now only readsmainfrom the highest-precedence sibling:wrangler.json, thenwrangler.jsonc, thenwrangler.toml. All sibling configs themselves remain credited as used. (Closes #630) -
TanStack Start
:vvirtual modules no longer surface as unlisted dependencies. Imports such astanstack-start-manifest:vandtanstack-start-injected-head-scripts:vare now recognized as plugin-registered framework virtual modules, and skipped from unlisted-dependency and unresolved-import reporting when the TanStack plugin is active. (Closes #636) -
Node package-script and forked runner entrypoints no longer report as unused. Package scripts such as
node scripts/process-messagesresolve extensionless directory paths to theirindex.*files, and statically resolvable localchild_process.fork()targets from provennode:child_processimports are credited as dynamic entrypoints, including thefork(path.resolve(fileURLToPath(import.meta.url), '../runner.js'))shape. (Closes #638) -
MDX documentation code fences no longer create unresolved imports. Fenced TypeScript examples in
.mdxfiles were previously extracted like executable top-level statements, so docs snippets with virtual// file:boundaries reported falseunresolved-importfindings. Fenced code blocks are now skipped during MDX import/export extraction, while real top-level imports continue through the parser path. (Closes #639) -
Workspace and self package imports that point at missing prebuild output now resolve back to source. Packages such as Nitro and Redux Toolkit could previously report false
unresolved-imports,unlisted-dependencies,unused-dependencies, andunused-fileswhenpackage.jsonimportsorexportsselecteddisttargets before a build had run. Fallow now uses the nearest package manifest for#...imports and known root/workspace package manifests for self or workspace specifiers, maps project-relative output targets back to trackedsrccandidates, and preserves dependency usage metadata when those imports resolve to internal source files. (Closes #641) -
Bun's bare
bunruntime module is no longer reported as an unlisted dependency.import { SQL } from "bun"and type-only imports from"bun"are recognized as a Bun runtime builtin, alongside the existingbun:*builtin recognition. Real packages such asbun-types,@types/bun,bunyan, andbun/*subpaths remain normal dependencies. (Closes #642) -
React Router v7 and Remix generated
./+types/*route modules no longer surface as unresolved imports. Route modules usingimport type { Route } from "./+types/root"previously reported false-positive findings because those files are generated by the framework's typegen step and are often gitignored. The React Router and Remix plugins now declare./+types/as a generated type-import prefix; the suppression is plugin-gated and type-only, so runtime imports under the same prefix still report. (Closes #645) -
Windows CI is green again. The Fumadocs integration test now normalizes path separators before asserting on
unused_files, restoring the Windows leg ofci.yml. No user-visible behavior change.
Full Changelog: v2.79.0...v2.80.0
v2.79.0: Ember.js / Glimmer plugin, JSX + class-member + worker / loader false-positive fixes
Highlights
Ember.js / Glimmer / Embroider plugin. First-class support for strict-mode Ember apps and v2 addons: ember-source / ember-cli / @embroider/* packages stay credited, framework-invoked lifecycle methods on Component / Route / Controller / Service / Helper / Modifier / Application / Router subclasses are no longer flagged as unused class members, the @ember/* virtual-module paths exposed through the AMD loader and Embroider rewriter stop reporting as unresolved imports, and classic-layout filesystem conventions (app/components/**, app/routes/**, tests/**/*-test.{js,ts,gjs,gts}, ember-cli-build.js, etc.) are added as entry-point globs. .gts / .gjs single-file components are first-class: imports referenced inside <template>...</template> blocks (PascalCase tag invocation, mustache / triple-stash / sub-expression helpers, element modifiers, dotted refs) are credited; Handlebars built-ins (if, unless, each, let, yield), this.* chains, and @arg references are not. Thanks @mike-engel for the plugin (PR #369).
False-positive fixes across JSX, class members, Cloudflare Workers, Content Collections, Node loaders, and HTML scaffolding.
Added
- Ember.js / Glimmer / Embroider plugin with strict-mode
.gts/.gjstemplate-binding usage tracking. See above.
Fixed
-
JSX resource attributes no longer report as unresolved imports. Generic TSX metadata like
<link rel="stylesheet" href="style-a.css" />,<link rel="modulepreload" href="/vendor.js" />, and<script src="./script-a.js" />no longer emit synthetic side-effect imports; HTML files and barehtmltagged-template asset scanning are unchanged. (Closes #640.) -
Combined human summaries are less repetitive, and
fallow explainaccepts issue labels with spaces.fallow --summaryno longer prints both a section header and the summary renderer's own title; theloaded config:notice is deduped per config file;fallow explain unused filesandfallow explain code duplicationwork the same as the hyphenated spellings. -
Public class members exposed through non-private package entry points are no longer reported as removable internals. Library packages that re-export builder or database classes from
package.jsonentry points (main, rootexports, or subpathexports) had every uncalled public method reported asunused-class-member.find_unused_membersnow treats classes reached from non-private entry-point re-exports (including the transitiveexport *closure andsrc/**/index.*source subpath indexes in packages without anexportsmap) as public API and skips class member findings for those exports. Enum members, private app packages, and internal reachable classes are unchanged. (Closes #643.) -
Cloudflare Workers, Content Collections, and Node
module.register()loaders no longer surface as false positives. Cloudflare Workers projects with"main": "src/worker.tsx"(or anyenv.<name>.mainoverride) inwrangler.{toml,json,jsonc}keep that worker entry alive; the static glob widens tosrc/{index,worker}.{ts,tsx,js,jsx,mts,mjs}. Content Collections projects (@content-collections/{core,vite,next,solid-start,remix-vite,qwik,vinxi}) keep their root config alive and the integration packages stay credited. Nodemodule.register('./hooks/loader.ts', import.meta.url)calls now credit the loader file's hook exports (initialize/resolve/load/globalPreloadplus legacygetFormat/getSource/transformSource). Thanks @M-Hassan-Raza for the patch. (Closes #588, #589, #590.) -
Playwright
extend()fixture helpers are credited as used class members. Helper classes referenced only as Playwright fixtures (test.extend({ helper: async ({}, use) => use(new MyHelper(page)) })) no longer report every public method asunused-class-member. Playwright Page Object Model patterns no longer surface false-positive removable-internal findings. -
HTML asset scanner skips build-time template-placeholder specifiers.
<script src="{{rootURL}}assets/app.js">(Ember'sapp/index.html),<script src="###APPNAME###/...">(ember-cli blueprint scaffolds), and equivalent shapes from Handlebars / Mustache / Jinja2 / pre-compiled Vue or Angular templates are filtered at extraction time instead of being seeded as unresolvable specifiers.{{and###are never valid in a real<script src>/<link href>path, so the filter is generic across template engines.
Install
npm install -g fallow@2.79.0or via Homebrew, cargo install fallow-cli, or the install script. See INSTALL.md for the full matrix.
Full Changelog: v2.78.1...v2.79.0
v2.78.1: npm postinstall no longer hits GitHub rate limits
Patch release
Fixed
-
npm install fallowpostinstall no longer fails on shared-IP CI runners withdigest-unavailable. The postinstall verifier previously fetched each platform binary's expected SHA-256 from the unauthenticated GitHub release API. Pooled CI IPs (Buildkite, GitHub Actions shared runners, internal build clusters) routinely exceeded GitHub's 60 req/hr unauthenticated limit, andpnpm install --frozen-lockfileaborted withfallow: binary verification failed ... (digest-unavailable): GitHub release API returned HTTP 403: API rate limit exceeded. The release workflow'snpm-prepjob now computes the SHA-256 of every binary inside each@fallow-cli/<platform>package and writes it into the platform package'spackage.jsonunderfallowDigests.verify-binary.jsreads that embedded value first and only falls back to the GitHub API for older platform packages that lack the field, so steady-state installs perform zero network calls during digest verification. The Ed25519 signature layer and theFALLOW_SKIP_BINARY_VERIFYescape hatch are unchanged. (Closes #597. Thanks @drgnkpr for the report.) -
Windows clippy on
mainis green again. Replaced an unfulfilled#[expect(dead_code)]annotation onScopedChild::idwith#[allow]. The function ispubinside apub mod, so rustc never flags it as dead under-D warnings, and the previousexpectannotation brokeci.yml's Windows leg. No user-visible behavior change.
Upgrade
npm install -g fallow@2.78.1
Full Changelog: v2.78.0...v2.78.1
v2.78.0: production CodeClimate fix, --explain everywhere, Windows hardening
Highlights
Added
fallow flagscovers 5 more SDKs by default: PostHog, Vercel Flags, ConfigCat, Optimizely, and Eppo. No config required; user-authoredflags.sdkPatternsstill layer on top. (Closes #563)--explainnow works on every invocation shape, not just subcommand+JSON. Human output gains aDescription:line under each rule / metric. Combined-mode JSON gains a top-level_metablock aggregating the per-analysis metadata sofallow --explain --format jsonis a single-call discovery surface for rule docs. (Closes #559)- License clock-skew defense:
fallow-licensenow rejects JWTs whoseiatis more than 24h in the future (a forward-signed token, or a clock-behind-reality runner accepting a long-expired license). NewLicenseError::ClockSkewvariant, tolerance configurable viaFALLOW_LICENSE_SKEW_TOLERANCE_SECONDSenv var, default 24h matchingjsonwebtoken/pyjwt/jjwtconventions. (Closes #453)
Changed
- One persistent progress spinner instead of per-stage flicker. Before, each pipeline stage (discovery, extract, graph, analyze, dupes, health) had its own spinner; on fast machines none of them painted, so
fallowon a moderately-sized project appeared frozen between command issue and final summary. Now a single spinner spawns at the start of the run, updates the stage label in place, and respectsFALLOW_QUIET=1/--quiet/ non-TTY stderr. (Closes #560)
Fixed
fallow --scoreandfallow --trendnow actually render the health score and trend in the terminal in combined mode. Previously the values were computed and emitted to JSON / SARIF / CodeClimate but the human renderer silently skipped them. (Closes #557)fallow check --format codeclimate --productionno longer panics withinternal error: entered unreachable codewhen a--production-suppressed dep / export / member rule resolves toSeverity::Off. Generic CodeClimate helpers now map severity lazily inside the loop body instead of eagerly before iteration. (Closes #452)- Workspace-relative paths instead of bare basenames in the nudge line, refactoring-targets nudge, Angular rollup line, and CRAP-coverage
(inherited from ...)suffix. Nx / Angular / Rust monorepos with many same-named files (index.ts,mod.rs,*.component.ts,template.html) now show enough path context to identify which file fallow is pointing at. (Closes #547) - CSS Modules unused-export findings now report the real source line and column instead of
line: 1, col: 0. LSP "go to definition" jumps to the actual class declaration; SARIF regions anchor to the right line;--diff-fileline-overlap classification works correctly. (Closes #549) - CSS Modules nested cascade-layer sub-names (
@layer foo.bar { ... }) no longer report as unused exports. Thanks @BowlingX for the report. (Closes #540)
Windows CI hardening
The push-to-main Windows CI matrix (added in #447) surfaced a class of long-latent Windows-only path-shape bugs that had been invisible because PR runs were ubuntu-only:
--changed-sinceandfallow auditnow match findings on Windows instead of silently returning zero. Three sites moved fromstd::fs::canonicalize(which adds the\\?\verbatim prefix on Windows) todunce::canonicalizeso canonical paths agree withopts.rootshape. (Closes #561)fallow audit --gate new-onlyno longer reports every inherited finding as freshly introduced on Windows; BASE-vs-HEAD finding-key intersection now strips the\\?\prefix beforestrip_prefix. (Closes #561)- Diff-aware analyses (
--diff-file,--diff-stdin,--changed-since) and the--filefilter onfallow checknow classify POSIX-style absolute paths (/foo/bar) correctly on Windows. Newcrates/cli/src/path_util::is_absolute_path_any_platformrecognizes host-absolute, POSIX-root, and Windows-drive-prefix shapes regardless of host. (Closes #545) - Windows
fallowbinary now links with a 16 MiB main-thread stack (was Windows's 1 MiB default). Audit integration tests crashed withSTATUS_STACK_OVERFLOWon a recursive code path insidefallow auditthat exceeded 1 MiB but stayed under POSIX's 8 MiB default. (Closes #556) - The main Rust
Checkjob now runs on Windows in addition to Linux on every push tomain, catching this class of path-shape bug before the next release rather than at user-report time. PR runs stay ubuntu-only for fast feedback. (Closes #447)
Internal
- Narrowed an import in
crates/mcp/src/server/tests/run.rsso two Unix-only helpers are gated on#[cfg(unix)], silencing a Rust 1.95unused-importsclippy regression on thewindows-latestCI leg.
Install
npm install fallow@2.78.0
# or
cargo install fallow-cli --version 2.78.0
# or via Homebrew / direct binary downloadFull Changelog: v2.77.0...v2.78.0