fallow 3.30.0 → 3.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -113,6 +113,7 @@ Analyzes the project for unused files, exports, dependencies, types, members, an
113
113
  | `--symbol-impact` | `string` | - | Compute exact-symbol consumers, affected files, and targeted tests |
114
114
  | `--top` | `string` | - | Show only the top N items per category |
115
115
  | `--file` | `string` | - | Scope output to specific files. Only issues in the specified files are reported. Project-wide dependency issues are suppressed. Warns on non-existent paths. Useful for lint-staged |
116
+ | `--finding-id` | `string` | - | Only report the findings with these `finding_id` values. Repeat the flag or pass a comma-separated list. Ids stay the same under every filter. The JSON output adds `finding_id_query`: a missing id means "resolved" only when `conclusive` is true |
116
117
 
117
118
  Common global flags for this command: [`--format`](#global-flags), [`--quiet`](#global-flags), [`--output-file`](#global-flags), [`--changed-since`](#global-flags), [`--max-file-size`](#global-flags), [`--production`](#global-flags), [`--no-production`](#global-flags), [`--production-dead-code`](#global-flags), [`--baseline`](#global-flags), [`--save-baseline`](#global-flags), [`--workspace`](#global-flags), [`--changed-workspaces`](#global-flags), [`--include-entry-exports`](#global-flags).
118
119
  <!-- generated:flags:dead-code:end -->
@@ -144,6 +145,7 @@ Common global flags for this command: [`--format`](#global-flags), [`--quiet`](#
144
145
  | `--duplicate-exports` | Duplicate exports |
145
146
  | `--circular-deps` | Circular dependencies |
146
147
  | `--re-export-cycles` | Re-export cycles (`kind: multi-node` for barrel files re-exporting from each other in a loop, `kind: self-loop` for a barrel re-exporting from itself). File-scoped finding; chain propagation through the loop is a no-op so imports may silently come up empty. Distinct from `--circular-deps` (runtime cycles). |
148
+ | `--package-cycles` | Two or more workspace packages import each other in a loop |
147
149
  | `--boundary-violations` | Boundary violations (imports crossing architecture zone boundaries, unzoned source files when `boundaries.coverage.requireAllFiles` is set, and forbidden calls from `boundaries.calls.forbidden`; suppression token `boundary-violation`, with `boundary-call-violation` and `boundary-call-violations` accepted as aliases for the whole family) |
148
150
  | `--policy-violations` | Rule-pack policy violations (banned calls, imports, and catalogue-derived effects declared via the `rulePacks` config key) |
149
151
  | `--stale-suppressions` | Stale suppression comments or `@expected-unused` JSDoc tags |
@@ -363,7 +365,7 @@ fallow list --entry-weight --format json --quiet
363
365
  fallow workspaces --format json --quiet # alias of `fallow list --workspaces`
364
366
  ```
365
367
 
366
- The `--entry-weight` JSON output carries `entry_weight.entries[]`, one row per runtime entry point, heaviest first. Each row has `eager_modules` and `eager_bytes` (the project modules and source bytes that load before the entry runs; `import type` and a declaration file do not count), `eager_css_bytes`, `deferred_modules` and `deferred_bytes` (reached only through `import()` or a lazy glob), `out_of_thread_modules` and `out_of_thread_bytes` (reached only through a `new URL(..., import.meta.url)` reference such as a worker URL, `child_process.fork`, a pino transport or a `module.register` hook), `eager_packages[]` with the specifiers as written, and `dominating_imports[]`. A dominating import is one import that alone keeps `exclusive_bytes` on the startup path. The unit is `source_bytes`: types and comments count, and tree shaking does not apply, so the value is not a bundle size. An import without the `type` keyword counts as eager, even when it brings in only types that TypeScript removes, so `eager_bytes` can be too high. Treat a dominating import as evidence for a review, not as a fix: a lazy load of code that the first screen needs can make startup slower.
368
+ The `--entry-weight` JSON output carries `entry_weight.entries[]`, one row per runtime entry point, heaviest first. Each row has `eager_modules` and `eager_bytes` (the project modules and source bytes that load before the entry runs; `import type` and a declaration file do not count), `eager_css_bytes`, `deferred_modules` and `deferred_bytes` (reached only through `import()` or a lazy glob), `out_of_thread_modules` and `out_of_thread_bytes` (reached only through a `new URL(..., import.meta.url)` reference such as a worker URL, a webpack worker loader request such as `worker-loader!./work.js`, `child_process.fork`, a pino transport or a `module.register` hook), `eager_packages[]` with the specifiers as written, and `dominating_imports[]`. A dominating import is one import that alone keeps `exclusive_bytes` on the startup path. The unit is `source_bytes`: types and comments count, and tree shaking does not apply, so the value is not a bundle size. An import without the `type` keyword counts as eager, even when it brings in only types that TypeScript removes, so `eager_bytes` can be too high. Treat a dominating import as evidence for a review, not as a fix: a lazy load of code that the first screen needs can make startup slower.
367
369
 
368
370
  To gate eager growth in CI, save a baseline file on the main branch with `fallow list --entry-weight --save-regression-baseline <PATH>`. A later run with `--regression-baseline <PATH>` adds `entry_weight.regression`: per-entry `baseline_eager_bytes`, `current_eager_bytes`, `new_eager_packages` and `exceeded`. The comparison is report-only until you add `--fail-on-regression`; then an entry that grew more than `--tolerance` (bytes, or a percentage such as `5%`) exits 1. A new entry never fails the gate.
369
371
 
@@ -453,7 +455,7 @@ Human output groups paths under "Shared with your team (commit these)" and "Loca
453
455
  {
454
456
  "kind": "agent-install",
455
457
  "schema_version": 1,
456
- "fallow_version": "3.30.0",
458
+ "fallow_version": "3.31.0",
457
459
  "root": "/abs/path",
458
460
  "mode": "install",
459
461
  "dry_run": false,
@@ -657,7 +659,7 @@ fallow health --format json --quiet --trend
657
659
  {
658
660
  "kind": "health",
659
661
  "schema_version": 7,
660
- "version": "3.30.0",
662
+ "version": "3.31.0",
661
663
  "elapsed_ms": 32,
662
664
  "summary": {
663
665
  "files_analyzed": 482,
@@ -1060,7 +1062,7 @@ fallow audit \
1060
1062
  {
1061
1063
  "kind": "audit",
1062
1064
  "schema_version": 7,
1063
- "version": "3.30.0",
1065
+ "version": "3.31.0",
1064
1066
  "command": "audit",
1065
1067
  "verdict": "fail",
1066
1068
  "changed_files_count": 12,
@@ -1148,7 +1150,7 @@ fallow flags --format json --quiet --workspace my-package
1148
1150
  ```json
1149
1151
  {
1150
1152
  "schema_version": 7,
1151
- "version": "3.30.0",
1153
+ "version": "3.31.0",
1152
1154
  "elapsed_ms": 116,
1153
1155
  "feature_flags": [],
1154
1156
  "total_flags": 0
@@ -1249,7 +1251,7 @@ fallow security --gate newly-reachable --changed-since origin/main
1249
1251
  {
1250
1252
  "kind": "security",
1251
1253
  "schema_version": "4",
1252
- "version": "3.30.0",
1254
+ "version": "3.31.0",
1253
1255
  "elapsed_ms": 42,
1254
1256
  "config": {
1255
1257
  "rules": {
@@ -1278,7 +1280,7 @@ fallow security --gate newly-reachable --changed-since origin/main
1278
1280
  {
1279
1281
  "kind": "security",
1280
1282
  "schema_version": "4",
1281
- "version": "3.30.0",
1283
+ "version": "3.31.0",
1282
1284
  "elapsed_ms": 42,
1283
1285
  "config": {
1284
1286
  "rules": {
@@ -1539,7 +1541,7 @@ Pack files can also reference the published schema directly: `"$schema": "https:
1539
1541
 
1540
1542
  ---
1541
1543
 
1542
- ## `license`: Manage Continuous Runtime License
1544
+ ## `license`: Manage the Fallow Cloud License
1543
1545
 
1544
1546
  Manage the local JWT used to unlock continuous/cloud runtime monitoring. Single-capture local runtime analysis does not require a license. Verification is fully offline against an Ed25519 public key compiled into the binary. Only `--trial` and `refresh` hit the network (`api.fallow.cloud`, 5s connect / 10s total timeout).
1545
1547
 
@@ -1678,7 +1680,9 @@ Helper subcommand for runtime coverage setup, focused analysis, and cloud invent
1678
1680
 
1679
1681
  - `coverage setup` - resumable state machine that wires sidecar installation, framework-aware coverage recipe writing, optional license activation for continuous monitoring, and automatic handoff into `fallow health --runtime-coverage`.
1680
1682
  - `coverage analyze` - focused runtime coverage analysis. Local mode reads `--runtime-coverage <path>`; cloud mode requires explicit `--cloud`, `--runtime-coverage-cloud`, or `FALLOW_RUNTIME_COVERAGE_SOURCE=cloud` and never triggers from `FALLOW_API_KEY` alone.
1681
- - `coverage upload-inventory` - push a static function inventory to fallow cloud so the dashboard can surface `untracked` functions (those in the codebase but never called at runtime).
1683
+ - `coverage upload-inventory` - push a static function inventory to Fallow Cloud so the dashboard can surface `untracked` functions (those in the codebase but never called at runtime).
1684
+ - `coverage review-packet` - read the production facts of changed files or functions from Fallow Cloud as JSON, with no local analysis and no full runtime-context pull. `--file <path>` and `--function <file>:<name>[:<line>]` select the scope; with neither, the source files changed against `--base` (resolved like `fallow audit`) are sent.
1685
+ - `coverage deployment-changes` - read the Fallow Cloud deployment change report for `--sha` (default `HEAD`) against `--base` (default: the previous deployment with production runtime) as JSON.
1682
1686
 
1683
1687
  ```bash
1684
1688
  fallow coverage setup # interactive
@@ -1690,6 +1694,10 @@ fallow coverage setup --yes --json --explain # add _meta field docs, enums, war
1690
1694
  fallow coverage analyze --runtime-coverage ./coverage --format json
1691
1695
  fallow coverage analyze --cloud --repo owner/repo --format json
1692
1696
 
1697
+ fallow coverage review-packet --repo owner/repo # files changed against the merge-base
1698
+ fallow coverage review-packet --repo owner/repo --file src/api.ts
1699
+ fallow coverage deployment-changes --repo owner/repo --sha <sha> --base <sha>
1700
+
1693
1701
  fallow coverage upload-inventory # infers project-id, git-sha, API key
1694
1702
  fallow coverage upload-inventory --dry-run # print what would be uploaded, exit 0
1695
1703
 
@@ -1712,7 +1720,7 @@ fallow coverage upload-source-maps --dry-run # print maps and fileNam
1712
1720
  |------|------|---------|-------------|
1713
1721
  | `--runtime-coverage <PATH>` | path | none | Local V8 directory, V8 JSON file, or Istanbul coverage map. Mutually exclusive with cloud mode. |
1714
1722
  | `--cloud`, `--runtime-coverage-cloud` | bool | false | Explicitly fetch cloud runtime facts from `/v1/coverage/:repo/runtime-context`. |
1715
- | `--api-key <KEY>` | string | `$FALLOW_API_KEY` | Fallow cloud bearer token, used only after explicit cloud opt-in. |
1723
+ | `--api-key <KEY>` | string | `$FALLOW_API_KEY` | Fallow Cloud bearer token, used only after explicit cloud opt-in. |
1716
1724
  | `--api-endpoint <URL>` | string | `$FALLOW_API_URL` or `https://api.fallow.cloud` | Override for staging / on-prem. |
1717
1725
  | `--repo <OWNER/REPO>` | string | `$FALLOW_REPO`, then parsed git origin | Repository whose latest cloud runtime facts should be pulled. Slashes are percent-encoded as one route segment. |
1718
1726
  | `--coverage-period <DAYS>` | integer | 30 | Cloud observation window, 1 through 90 days. |
@@ -1738,7 +1746,7 @@ Under `--production` the evidence block also carries `test_only_reference`. It i
1738
1746
 
1739
1747
  | Flag | Type | Default | Description |
1740
1748
  |------|------|---------|-------------|
1741
- | `--api-key <KEY>` | string | `$FALLOW_API_KEY` | Fallow cloud bearer token. Generate at `https://fallow.cloud/settings#api-keys`. **Prefer `$FALLOW_API_KEY` on shared CI runners**: `--api-key` on the command line may be visible to other processes via `ps`. |
1749
+ | `--api-key <KEY>` | string | `$FALLOW_API_KEY` | Fallow Cloud bearer token. Generate at `https://fallow.cloud/settings#api-keys`. **Prefer `$FALLOW_API_KEY` on shared CI runners**: `--api-key` on the command line may be visible to other processes via `ps`. |
1742
1750
  | `--api-endpoint <URL>` | string | `$FALLOW_API_URL` or `https://api.fallow.cloud` | Override for staging / on-prem. |
1743
1751
  | `--project-id <OWNER/REPO>` | string | `$GITHUB_REPOSITORY` → `$CI_PROJECT_PATH` → `git remote get-url origin` | Project identifier. |
1744
1752
  | `--git-sha <SHA>` | string | `git rev-parse HEAD` | Commit SHA this inventory is keyed to. Max 64 chars; `[A-Za-z0-9._-]` only. |
@@ -1753,7 +1761,7 @@ Only plain JS/TS/JSX/TSX sources are walked. Declaration files (`*.d.ts`, `*.d.m
1753
1761
  ### Environment
1754
1762
 
1755
1763
  - `FALLOW_COV_BIN` - explicit override for the sidecar binary (for `setup`). Wins over all other discovery paths. Must point to an existing file.
1756
- - `FALLOW_API_KEY` - fallow cloud bearer token (for `upload-inventory` and `upload-source-maps`). Overridden by `--api-key` for `upload-inventory`; `upload-source-maps` reads only the env var so secrets stay out of argv.
1764
+ - `FALLOW_API_KEY` - Fallow Cloud bearer token (for `upload-inventory` and `upload-source-maps`). Overridden by `--api-key` for `upload-inventory`; `upload-source-maps` reads only the env var so secrets stay out of argv.
1757
1765
  - `FALLOW_API_URL` - base URL for cloud calls. Overridden by `--api-endpoint`.
1758
1766
  - `FALLOW_CA_BUNDLE` - PEM certificate bundle for cloud calls. Relative paths resolve from the process cwd. The bundle replaces default WebPKI roots, so private-CA runners should pass a complete bundle that includes public roots plus the private CA.
1759
1767
 
@@ -1841,6 +1849,7 @@ Available on all commands:
1841
1849
  | `--no-cache` | `bool` | `false` | Disable incremental caching |
1842
1850
  | `--threads` | `string` | - | Number of parser threads |
1843
1851
  | `--changed-since` | `string` | - | Only report issues in files changed since this git ref (e.g., main, HEAD~5) |
1852
+ | `--no-package-baselines` | `bool` | `false` | Ignore the per-package refs of `workspaces.changedSince` for this run |
1844
1853
  | `--diff-file` | `string` | - | Unified diff for line-level scoping. Use `-` to read from stdin. Project-level findings still bypass this filter. When both this and `--changed-since` are set, the diff filter wins for finding scope while `--changed-since` still drives file discovery |
1845
1854
  | `--diff-stdin` | `bool` | `false` | Read the unified diff from stdin. Equivalent to `--diff-file -` |
1846
1855
  | `--churn-file` | `string` | - | Import change history from a `fallow-churn/v1` JSON file instead of `git log`, powering hotspots, ownership, and bus-factor on projects with no git repository (Yandex Arc, Mercurial, Perforce). A small wrapper translates your VCS log into the contract. Resolved relative to `--root`. Affects `health --hotspots` / `--ownership` / `--targets` only; `audit`, `impact`, and `--changed-since` still require git |
@@ -1868,6 +1877,8 @@ Available on all commands:
1868
1877
  | `--report-path-prefix` | `string` | - | Prefix prepended to every path in the CI-facing formats (`github-annotations`, `github-summary`, `codeclimate`, `review-github`, `review-gitlab`). CI platforms address files by repository-root-relative path, so when the analyzed project lives in a subdirectory (e.g. `packages/app/`), paths need that offset. fallow detects the offset via the git toplevel automatically; this flag overrides the detection. Pass an empty string to disable rebasing and emit paths relative to `--root` |
1869
1878
  | `--fail-on-regression` | `bool` | `false` | Fail if issue count increased beyond tolerance vs a regression baseline |
1870
1879
  | `--fail-on-stale-baseline` | `bool` | `false` | Exit with code 1 if a loaded --baseline has entries that match nothing in this run |
1880
+ | `--fail-on-baseline-growth` | `bool` | `false` | Exit with code 1 if a loaded baseline has a key that the same file at the base ref does not have |
1881
+ | `--baseline-base` | `string` | - | The git ref that --fail-on-baseline-growth compares the baseline with |
1871
1882
  | `--fail-on-parse-error` | `bool` | `false` | Exit with code 1 if fallow could not parse a source file cleanly |
1872
1883
  | `--tolerance` | `string` | `0` | Allowed increase: `"2%"` (percentage) or `"5"` (absolute). Default: `"0"` |
1873
1884
  | `--regression-baseline` | `string` | - | Path to a standalone regression baseline file. Without it, fallow uses `regression.baseline` from the config |
@@ -1948,6 +1959,7 @@ These are global flags with behavior specific to bare `fallow` combined mode.
1948
1959
  | `FALLOW_EXTENDS_TIMEOUT_SECS` | Timeout for fetching remote config inheritance in seconds (default: `5`). Do not raise this for untrusted sources. |
1949
1960
  | `FALLOW_CACHE_DIR` | Override the persistent extraction cache directory. Wins over `cache.dir`. Useful for read-only checkouts or CI cache volumes. `--no-cache` disables this knob. |
1950
1961
  | `FALLOW_CACHE_MAX_SIZE` | Maximum on-disk extraction cache (`.fallow/cache.bin`) size in megabytes (default: `256`). Triggers LRU eviction when crossed. Wins over `cache.maxSizeMb` config field. Intended for CI runners with disk quotas. `--no-cache` short-circuits this knob. |
1962
+ | `FALLOW_PACKAGE_BASELINES` | Set to `false`, `0`, `no` or `off` to ignore `workspaces.changedSince` for every run of the process, like `--no-package-baselines`. Other values keep the map. |
1951
1963
  | `FALLOW_COVERAGE` | Path to Istanbul or raw V8 coverage data for exact CRAP scoring in `health`, `audit`, and bare `fallow`. |
1952
1964
  | `FALLOW_COVERAGE_ROOT` | Absolute coverage-data prefix to strip before matching Istanbul paths in `health`, `audit`, and bare `fallow`. |
1953
1965
  | `FALLOW_TYPE_AWARE` | Enable or disable TypeScript semantic (type-aware) analysis for the run. Accepts `true`/`false`/`1`/`0`/`yes`/`no`/`on`/`off`; any other value is a hard error. Sits mid-chain in the precedence: the `--type-aware`/`--no-type-aware` CLI flags win over it, and it wins over the `audit.typeAware` config field, which wins over `typeAware.enabled`. |
@@ -2056,7 +2068,7 @@ The HTTP layer mirrors the bash `gh_api_retry` / `curl_retry` helpers: `FALLOW_A
2056
2068
  {
2057
2069
  "kind": "dead-code",
2058
2070
  "schema_version": 7,
2059
- "version": "3.30.0",
2071
+ "version": "3.31.0",
2060
2072
  "elapsed_ms": 45,
2061
2073
  "total_issues": 12,
2062
2074
  "entry_points": {
@@ -2216,7 +2228,7 @@ When `--baseline` is used in combined output, the JSON includes a `baseline_delt
2216
2228
  {
2217
2229
  "kind": "dupes",
2218
2230
  "schema_version": 7,
2219
- "version": "3.30.0",
2231
+ "version": "3.31.0",
2220
2232
  "elapsed_ms": 82,
2221
2233
  "total_clones": 15,
2222
2234
  "total_lines_duplicated": 230,
@@ -2260,11 +2272,11 @@ When running `fallow` with no subcommand (all analyses), the JSON output combine
2260
2272
  {
2261
2273
  "kind": "combined",
2262
2274
  "schema_version": 7,
2263
- "version": "3.30.0",
2275
+ "version": "3.31.0",
2264
2276
  "elapsed_ms": 159,
2265
2277
  "check": {
2266
2278
  "schema_version": 7,
2267
- "version": "3.30.0",
2279
+ "version": "3.31.0",
2268
2280
  "elapsed_ms": 45,
2269
2281
  "total_issues": 12,
2270
2282
  "unused_files": [],
@@ -2476,9 +2488,11 @@ preset = "bulletproof"
2476
2488
  - `dynamicallyLoaded`: glob patterns for files loaded at runtime (plugin dirs, locale files); treated as always-used
2477
2489
  - `cache.dir`: override the persistent extraction cache directory. `FALLOW_CACHE_DIR` wins over this config field, and `--no-cache` disables caching entirely
2478
2490
  - `cache.maxSizeMb`: cap the serialized extraction cache size in megabytes. `FALLOW_CACHE_MAX_SIZE` wins over this config field
2491
+ - `workspaces.changedSince`: map exact workspace roots (as `fallow list --workspaces` prints them) to Git refs. `check`, `dead-code` and `dupes` then report the findings of a mapped package only for files changed since its ref; unlisted packages and root files stay in full scope. A global `--changed-since` replaces the map for one run; `--no-package-baselines` or `FALLOW_PACKAGE_BASELINES=false` turns it off. A key that names no workspace, or a ref Git cannot resolve, leaves every package in full scope with a warning and `request_outcomes["package-baselines"]` as `not-applied`. Save a whole-project baseline with `--no-package-baselines`.
2479
2492
  - `usedClassMembers`: class method/property names that extend the built-in Angular/React lifecycle allowlist with framework-invoked names. Each entry is a plain string (global suppression) or a scoped object `{ extends?, implements?, members }` matching only classes with the given heritage. Strings can be exact names (`"agInit"`) or glob patterns (`"*"` matches every member, `"enter*"` prefix, `"*Handler"` suffix, `"on*Event"` combined). Use scoped rules for common names like `refresh` or `execute` to avoid false negatives on unrelated classes; global strings for unique names like `agInit`. Example: `["agInit", { "implements": "ICellRendererAngularComp", "members": ["refresh"] }, { "extends": "BaseCommand", "members": ["execute"] }, { "extends": "GrammarBaseListener", "members": ["enter*", "exit*"] }]`. Glob patterns that match zero members emit a `WARN` so dead allowlist entries surface. An unconstrained scoped rule (no `extends` or `implements`) is rejected at load time. Use plugin-level `usedClassMembers` in a `.fallow/plugins/*.jsonc` file for library-specific allowlists
2480
2493
  - `resolve.conditions`: additional package.json `exports` / `imports` condition names to honor during module resolution. Baseline conditions (`development`, `import`, `require`, `default`, `types`, `node`, plus `react-native` / `browser` under RN/Expo) are always included; user entries prepend ahead of them. Use for community conditions like `worker`, `edge-light`, `deno`, or custom bundler conditions. Example: `{ "resolve": { "conditions": ["worker", "edge-light"] } }`
2481
2494
  - `unusedComponentProps.ignorePattern`: opt-in regex that exempts a component prop from `unused-component-props` when the prop's LOCAL destructure binding name matches (the leading-underscore "accepted-but-intentionally-unused" convention, mirroring TS `noUnusedParameters` + ESLint `varsIgnorePattern` / `argsIgnorePattern`). Applies to Vue, Svelte, Astro, and React/Preact props. The match is on the local alias (`_stage` in `let { stage: _stage } = $props()`), not the public prop name the finding reports (`stage`); matching is unanchored like ESLint's `RegExp.test`, so anchor with `^_`. An invalid regex fails config load. Example: `{ "unusedComponentProps": { "ignorePattern": "^_" } }`
2495
+ - `circularDependencies.ignoreLazyImports`: opt-in boolean, default `false`. When `true`, `circular-dependencies` skips an import edge on which every import loads its target on demand or on another thread: an `import()` inside a function, a template `import()`, a lazy `import.meta.glob`, a worker URL, or a webpack worker loader request such as `worker-loader!./work.js`. A top-level `await import()`, `require()`, an eager `import.meta.glob`, and an edge that also has a static import stay. The filter runs before cycles are counted, so lazy cycles do not use the per-group cycle limit. Example: `{ "circularDependencies": { "ignoreLazyImports": true } }`
2482
2496
 
2483
2497
  ---
2484
2498
 
@@ -436,6 +436,30 @@ Fallow treats `Config` and `Result` in `./types.ts` as used. Works with `@param`
436
436
 
437
437
  ---
438
438
 
439
+ ## Command File Arguments Are Entry Points
440
+
441
+ A file that a command names in a `package.json` script, a CI file (GitHub Actions, GitLab CI), a Dockerfile, a Procfile, or `fly.toml` becomes an entry point: `node scripts/seed.ts` keeps `scripts/seed.ts` and its imports reachable.
442
+
443
+ Formatters, linters, and checkers are the exception. They read their file arguments but do not run them, so `eslint src/a.ts`, `prettier --check "**/*.ts"`, `oxlint src/`, `biome check`, `stylelint`, `textlint`, and similar tools make no entry points. This applies to the common package-manager and wrapper forms (`npx`, `pnpm exec`, `pnpm --filter web exec`, `pnpm -r exec`, `yarn run`, `cross-env`, `dotenv -e .env --`, `varlock run --`), and to a call of a script that runs the tool (`npm run lint -- src/a.ts`, `npm run lint src/a.ts`, `yarn lint src/a.ts`). The tool stays a used dependency, its `--config` file stays tracked, and a module that it loads through a flag (`eslint -f ./fmt.js`, `prettier --plugin=./plugin.mjs`) stays reachable.
444
+
445
+ A command in a workspace package that the command selects resolves its file arguments against the directory of that package. `yarn workspace web node scripts/a.ts`, `pnpm --filter web exec tsx scripts/a.ts`, `npm exec -w web -- tsx scripts/a.ts`, and a call of a script of that package (`npm run -w web gen -- scripts/a.ts`) make `scripts/a.ts` of the `web` package an entry point. A pnpm filter can be a name, a name glob (`'@acme/*'`), a directory (`./packages/*`, `{packages/web}`), or an exclusion (`'!web'`). A selection of several packages resolves the file in each package where the file exists. This includes every package: `pnpm -r exec tsx scripts/a.ts`, `yarn workspaces foreach -A exec tsx scripts/a.ts` (narrowed by `--include` and `--exclude`), `yarn workspaces run gen scripts/a.ts`, and `npm --workspaces run gen -- scripts/a.ts`. `yarn workspaces foreach -A` also runs in the root package, as yarn berry does, and its `--include` and `--exclude` match a workspace name or directory (`.` is the root). `pnpm -w` also selects the root package. `--include-workspace-root` adds the root package: in pnpm to `-r` and to a filter that only excludes packages (`--filter '!web'`), and in npm to every workspace selection (`-w web`, `--workspaces`). Without it, `pnpm -r`, `yarn workspaces run`, and `npm --workspaces` leave out the root package. From a workspace package, `npm --workspaces` selects only that package. A `start` script that calls a script in selected packages (`pnpm -r run serve`, `pnpm -C packages/web run serve`) makes that script a runtime script of each package. A script call in the directory of a workspace package (`pnpm -C packages/web run gen scripts/a.ts`, `npm --prefix packages/web run gen -- scripts/a.ts`, `yarn --cwd packages/web gen scripts/a.ts`) runs the script of that package with the forwarded arguments. The formatter and linter rule above still applies in each selected package.
446
+
447
+ A command that runs in workspace packages that Fallow cannot resolve makes no entry points, because those packages resolve the paths against their own directories. This covers the pnpm dependency and changed-package filters (`web...`, `[origin/main]`), the other `yarn workspaces foreach` selections (`--since`, `--recursive`, `--from`, `--worktree`, `--no-private`), and task runners (`turbo run lint -- src/a.ts`, `nx`, `lerna`). The binary stays a used dependency. A command in another directory (`pnpm -C docs exec tsx scripts/a.ts`, `npm --prefix`, `yarn --cwd`) resolves its file arguments against that directory. `yarn node <file>` runs the file with Node.js, also after `yarn --cwd <dir>` and `yarn workspace <name>`.
448
+
449
+ A declared script with the name of a tool runs instead of the tool. With `"eslint": "node tools/check.js"`, `yarn eslint src/a.ts` keeps `src/a.ts` as an entry point.
450
+
451
+ For another command whose file arguments are data, list it in `ignoreCommandEntries`:
452
+
453
+ ```jsonc
454
+ {
455
+ "ignoreCommandEntries": ["my-codegen"]
456
+ }
457
+ ```
458
+
459
+ `["*"]` turns off entry points from all commands, including modules that a linter loads through a flag (`eslint -f ./fmt.js`); declare the real entries in `entry` instead.
460
+
461
+ ---
462
+
439
463
  ## JSX `<script src>` and `<link href>` Are Asset References
440
464
 
441
465
  Inside JSX/TSX files, lowercase intrinsic `<script src="...">` and `<link rel="stylesheet|modulepreload" href="...">` are tracked as asset references, same as in plain HTML files. This is needed for SSR frameworks like Hono where layout components emit HTML via JSX.
@@ -30,7 +30,8 @@ The `generated:issue-types` table below is regenerated from `fallow schema` by s
30
30
  | `duplicate-export` | `--duplicate-exports` | - | `// fallow-ignore-file duplicate-export` | Same symbol exported from multiple modules |
31
31
  | `circular-dependency` | `--circular-deps` | - | `// fallow-ignore-next-line circular-dependency` | Import cycles in the module graph |
32
32
  | `re-export-cycle` | `--re-export-cycles` | - | `// fallow-ignore-file re-export-cycle` | Barrel files re-exporting from each other in a loop (`kind: "multi-node"`) or a barrel re-exporting from itself (`kind: "self-loop"`). Chain propagation through the loop is a structural no-op so imports through any member may silently come up empty. Default `warn`. Distinct from `circular-dependencies` (runtime cycles, sometimes intentional). File-scoped suppression only: `// fallow-ignore-file re-export-cycle` on any member breaks the cycle. |
33
- | `boundary-violation` | `--boundary-violations` | - | `// fallow-ignore-next-line boundary-violation` | Imports crossing architecture zone boundaries. Presets: `layered`, `hexagonal`, `feature-sliced`, `bulletproof`; `autoDiscover` can create one zone per feature directory; per-rule `allowTypeOnly: [zones]` admits `import type` / `export type` crossings while still blocking value imports. Optional sections: `boundaries.coverage.requireAllFiles` reports unzoned source files (`allowUnmatched` globs exempt intentional ones), and `boundaries.calls.forbidden` bans callee patterns per zone (segment-aware and import-resolved, so `child_process.*` covers `node:child_process` named/namespace/default imports; direct callees only, zoned files only). The whole family shares the `boundary-violation` rule and suppression token (`boundary-call-violation` and `boundary-call-violations` accepted as aliases); start the rule at `warn` for a staged rollout |
33
+ | `package-cycle` | `--package-cycles` | - | `// fallow-ignore-next-line package-cycle` | Two or more workspace packages import each other in a loop, so they cannot be built in dependency order. Edges are resolved imports, not declared `package.json` dependencies. Imports from test, spec, story, fixture and tooling config files do not count. Type-only imports count, and each hop in `edges` has a `type_only` flag. Default `warn`. Requires a workspace with two or more packages. A suppression removes one import; the cycle goes away when every import on one hop is suppressed. `group_truncated: true` means the package group has more cycles than listed. `--changed-since` and diff scope use the example import of each hop (`edges[].path`) |
34
+ | `boundary-violation` | `--boundary-violations` | - | `// fallow-ignore-next-line boundary-violation` | Imports crossing architecture zone boundaries. Presets: `layered`, `hexagonal`, `feature-sliced`, `bulletproof`; `autoDiscover` can create one zone per feature directory; per-rule `allowTypeOnly: [zones]` admits `import type` / `export type` crossings while still blocking value imports. Named and default imports through a barrel are judged against the zone of the origin module (`to_path`), and `via_path` names the barrel. Optional sections: `boundaries.coverage.requireAllFiles` reports unzoned source files (`allowUnmatched` globs exempt intentional ones), and `boundaries.calls.forbidden` bans callee patterns per zone (segment-aware and import-resolved, so `child_process.*` covers `node:child_process` named/namespace/default imports; direct callees only, zoned files only). The whole family shares the `boundary-violation` rule and suppression token (`boundary-call-violation` and `boundary-call-violations` accepted as aliases); start the rule at `warn` for a staged rollout |
34
35
  | `boundary-coverage` | `--boundary-violations` | - | `// fallow-ignore-file boundary-violation` | Source file matches no configured architecture boundary zone; Requires boundaries.coverage.requireAllFiles |
35
36
  | `boundary-call-violation` | `--boundary-violations` | - | `// fallow-ignore-next-line boundary-call-violation` | Zoned file calls a callee its zone forbids; Requires boundaries.calls.forbidden patterns |
36
37
  | `policy-violation` | `--policy-violations` | - | `// fallow-ignore-next-line policy-violation` | Calls, imports, or catalogue-derived effects banned by a declarative rule pack (`rulePacks` config key lists standalone JSON/JSONC files of `banned-call`, `banned-import`, and `banned-effect` rules; pure data, no project code executes). Findings identified as `<pack>/<rule-id>`. Default `warn` master; per-rule `severity` overrides per finding and the exit gate reads the effective severity. Invalid or missing packs fail config load with exit 2. `fallow rule-pack-schema` prints the pack JSON Schema. Use the scoped token to suppress one rule; bare `policy-violation` still covers every pack rule on the line or file. |
@@ -12,7 +12,7 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
12
12
  | Tool | Kind | License | CLI fallback | Key params | Description |
13
13
  |---|---|---|---|---|---|
14
14
  | `code_execute` | composition | free | - | `code`, `timeout_ms`, `max_output_bytes` | Bounded read-only Code Mode for composing multiple fallow analysis calls in one JavaScript snippet. The snippet receives `{ fallow, root }`, returns JSON-serializable data, and can call read-only helpers such as `fallow.projectInfo`, `fallow.audit`, `fallow.checkHealth`, and `fallow.run(tool, params)` for the same allowlist. `fallow.all(requests)` fans out independent calls in one go: pass `[{ tool, params }, ...]` and get back a positionally aligned array of `{ ok: true, value }` or `{ ok: false, error }`, so one failing element never hides the rest. Host calls are memoized for the duration of one snippet, so repeating the same tool with the same params (key order does not matter) is served from cache, spends no `max_host_calls` slot and no output budget, and is reported in `calls[]` with `cache_hit: true`; a call refused before dispatch (unknown tool, malformed params) spends no slot either, and `limits.max_rejected_host_calls` bounds how many of those the response records. Similar-code is excluded because Code Mode is capped at 30 seconds; use standalone `find_similar_code` and `inspect_similar_code`, which have dedicated 15-minute timeouts. Mutating fix tools are not exposed, and a host call that passes `save_baseline`, `save_regression_baseline` or `save_snapshot` is refused with the name of the standalone tool to call. The sandbox has no filesystem, network, imports, `process`, `require`, `Deno`, `Bun`, or shell access, and no dynamic code compilation: `eval`, `Function`, and the async and generator function constructors are removed, including the `constructor` route reachable through function prototypes. Params: `code`, optional `root`, `timeout_ms` (capped at 30000), and `max_output_bytes` (capped at 4000000). `max_output_bytes` bounds two separate things: the total fallow JSON host calls read, shared across a `fallow.all` fan-out rather than granted per element, and the serialized snippet result. An oversized result is refused with `ok:false`, `truncated:true`, `result_bytes`, and a short `result_preview` in place of the value, never returned whole, so return a projection rather than a whole report. |
15
- | `analyze` | analysis | free | `fallow dead-code --format json --quiet` | `issue_types`, `production`, `workspace`, `baseline`, `group_by`, `file` | Full dead code analysis (unused files/exports/types/dependencies/members + circular dependencies + re-export cycles (barrel files that form a structural loop, silently breaking re-exports) + boundary violations + rule-pack policy violations (banned calls, imports, and catalogue-derived effects declared via the `rulePacks` config key) + stale suppressions). Private type leaks are an opt-in API hygiene check via `issue_types: ["private-type-leaks"]`. Deprecated exports that still have consumers are an opt-in migration sweep via `issue_types: ["deprecated-exports-in-use"]`. Set `boundary_violations: true` as a convenience alias for `issue_types: ["boundary-violations"]`. Set `group_by` to `"owner"`, `"directory"`, `"package"`, or `"section"` to partition results. The `section` mode reads GitLab CODEOWNERS `[Section]` headers and emits `owners` metadata per group |
15
+ | `analyze` | analysis | free | `fallow dead-code --format json --quiet` | `issue_types`, `production`, `workspace`, `baseline`, `group_by`, `file` | Full dead code analysis (unused files/exports/types/dependencies/members + circular dependencies + re-export cycles (barrel files that form a structural loop, silently breaking re-exports) + package cycles (workspace packages that import each other in a loop) + boundary violations + rule-pack policy violations (banned calls, imports, and catalogue-derived effects declared via the `rulePacks` config key) + stale suppressions). Private type leaks are an opt-in API hygiene check via `issue_types: ["private-type-leaks"]`. Deprecated exports that still have consumers are an opt-in migration sweep via `issue_types: ["deprecated-exports-in-use"]`. Set `boundary_violations: true` as a convenience alias for `issue_types: ["boundary-violations"]`. Set `group_by` to `"owner"`, `"directory"`, `"package"`, or `"section"` to partition results. The `section` mode reads GitLab CODEOWNERS `[Section]` headers and emits `owners` metadata per group |
16
16
  | `check_changed` | analysis | free | `fallow dead-code --changed-since <ref> --format json --quiet` | `since`, `baseline`, `fail_on_regression` | Incremental analysis of files changed since a git ref |
17
17
  | `security_candidates` | analysis | free | `fallow security --format json --quiet` | `gate`, `surface`, `changed_since`, `paths` | Unverified local security candidates, not confirmed vulnerabilities (`fallow security --format json`). Read `security_findings[]` for category, CWE, severity, evidence, trace, optional `reachability`, blind-spot counters, and optional `unresolved_callee_diagnostics` samples for dynamic callee follow-up. `severity` is a review-priority tier, not a verified vulnerability verdict. Each finding also carries an agent-actionable `candidate` (`source_kind`/`sink`/`boundary`), where URL-category sinks may include `url_shape` (`fixed-origin-dynamic-path` or `dynamic-origin`), an optional `taint_flow` source-to-sink triple, and a stable `finding_id` (equal to the SARIF fingerprint) for cross-run correlation; there is no `impact` field (deciding exploitability is the agent's job). Set `surface: true` to include top-level `attack_surface[]` entries with defensive-boundary prompts for a verifier. Set `gate` to `new` for changed-line candidates or `newly-reachable` for candidates that became reachable from entry points; `newly-reachable` requires `changed_since`. `reachability.untrusted_source_trace` is module-level import context only and does not prove value flow; `reachability.taint_confidence` tiers each reachable candidate as `arg-level` (sink argument traces to a same-module source read, strong) or `module-level` (only the module is import-reachable from a source, weak), so tier from this field instead of the evidence text. Verify trace, reachability context, severity, and evidence before editing code. Supports `root`, `config`, `workspace`, `paths`, `changed_since`, `changed_workspaces`, `surface`, `gate`, `no_cache`, and `threads`; `paths` forwards repeated `fallow security --file` filters for finding anchors, trace hops, untrusted-source reachability trace hops, and unresolved-callee diagnostics. See <https://docs.fallow.tools/cli/security-agent-verification> for the verifier packet and verdict recipe. Inherits `FALLOW_DIFF_FILE` from the server environment for line-level diff scoping; raise `FALLOW_TIMEOUT_SECS` for large repos. |
18
18
  | `find_similar_code` | analysis | free | `fallow similar-code --format json --quiet` | `threshold`, `min_lines`, `top`, `changed_since`, `paths` | Find unverified semantically similar function candidates with the exact pinned local model. Discovery is read-only and never authorizes or performs model setup. Scoped output materializes the exact admitted files once in `generation.scope.paths` as provenance. Ask the user to run `fallow similar-code setup --local` when setup is missing. Cold local inference has a dedicated 15-minute subprocess window. |
@@ -27,6 +27,8 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
27
27
  | `get_importance` | runtime-coverage | freemium | `fallow health --runtime-coverage <path> --format json --quiet` | `coverage`, `group_by` | Runtime-context slice for production-importance review. Same params as `check_runtime_coverage`; read `runtime_coverage.importance` for stable `fallow:importance:<hash>` IDs, invocations, cyclomatic complexity, owner count, 0-100 score, and templated reason. |
28
28
  | `get_cleanup_candidates` | runtime-coverage | freemium | `fallow health --runtime-coverage <path> --format json --quiet` | `coverage`, `group_by` | Runtime-context slice for cleanup review. Same params as `check_runtime_coverage`; read `runtime_coverage.findings` for `safe_to_delete`, `review_required`, `low_traffic`, and `coverage_unavailable`. |
29
29
  | `get_cloud_runtime_context` | runtime-coverage | freemium | `fallow coverage analyze --cloud --repo <owner/repo> --format json --quiet` | `repo`, `period_days`, `environment`, `commit_sha`, `top` | Cloud-backed runtime-context slice, and the only MCP tool that makes a network call. Required `repo` (`owner/repo`); `project_id`, `period_days` (1-90, default 30), `environment`, and `commit_sha` narrow the cloud selection, while `production`, `top`, and `min_invocations_hot` behave as on `check_runtime_coverage`. The key is `FALLOW_API_KEY` in the server environment and never a param: without it the call is refused with `code: "cloud_api_key_missing"` before anything runs. Returns the same `runtime_coverage` block as the local tools, joined against the checkout at `root`, so a `root` on a different revision quietly empties `findings` and raises a `cloud_functions_unmatched` warning. Confirm `runtime_coverage.summary.data_source` is `cloud`, and read the source-map confidence table below before acting on file-level signals. |
30
+ | `get_cloud_review_packet` | runtime-coverage | freemium | `fallow coverage review-packet --repo <owner/repo> --file <path> --format json --quiet` | `repo`, `files`, `functions`, `period_days`, `project_id` | Production facts of a few changed files or functions, read from fallow cloud without the full runtime-context pull |
31
+ | `get_cloud_deployment_changes` | runtime-coverage | freemium | `fallow coverage deployment-changes --repo <owner/repo> --sha <sha> --format json --quiet` | `repo`, `sha`, `base`, `change` | How production behavior changed between two deployments, read from the fallow cloud change report |
30
32
  | `get_token_blast_radius` | analysis | free | `fallow health --css --format json --quiet` | - | Design-token blast radius for Tailwind v4 @theme tokens and CSS-in-JS token definitions (StyleX, vanilla-extract, PandaCSS): per token, a consumer_count (static lower bound) and a capped located consumers[] sample tagged theme-var/css-var/utility/apply (Tailwind), js-member (member access), or js-call (StyleX theme-group and Panda token calls); descriptive context for sizing a token change, never a deletion gate |
31
33
  | `audit` | analysis | free | `fallow audit --format json --quiet` | `gate`, `base`, `css_deep`, `max_crap`, `coverage`, `runtime_coverage` | Combined dead-code + complexity + duplication + styling for changed files, returns verdict. Styling analytics are enabled by default; CSS and CSS-in-JS evidence can add `styling_findings`, `css_analytics`, and `styling_health` under the health sub-result. Set `gate` to `"new-only"` or `"all"`. Set `css_deep: false` to skip project-wide styling reachability while keeping local styling checks, or `css_deep: true` to force it back on when config disables it. Optional `runtime_coverage` (V8 dir / V8 JSON / Istanbul JSON) folds runtime findings into the same call; `min_invocations_hot` tunes the hot-path threshold (default 100). Runtime evidence appears under the audit `complexity` sub-result, including `coverage_intelligence` when combined evidence yields actionable recommendations. |
32
34
  | `decision_surface` | analysis | free | `fallow decision-surface --format json --quiet` | `base`, `max_decisions`, `workspace` | Surface the few consequential structural decisions a change embeds (coupling, public API, dependency), each as a judgment question with the routed expert; ranked, capped, and signal_id-anchored |
@@ -95,6 +97,34 @@ ambiguity or reachable references.
95
97
 
96
98
  `trace_export` never carries a `semantic` block: it is API-backed in-process and answers from the graph alone.
97
99
 
100
+ ## Scoped cloud reads
101
+
102
+ Use these reads when the project sends production coverage to Fallow Cloud. The CLI reads the key from `--api-key` or `FALLOW_API_KEY`; the MCP tools read `FALLOW_API_KEY` from the server environment only. Prefer the two scoped reads: they return only the functions or the deploy you ask about. `get_cloud_runtime_context` downloads the evidence of every function and runs a full local analysis first, so keep it for whole-project questions.
103
+
104
+ | Question | CLI | MCP tool |
105
+ |:---------|:----|:---------|
106
+ | Is a changed function hot, cold, or tested? | `fallow coverage review-packet --repo <owner/repo>` | `get_cloud_review_packet` |
107
+ | Did the last deploy change what runs? | `fallow coverage deployment-changes --repo <owner/repo>` | `get_cloud_deployment_changes` |
108
+
109
+ **Before an edit.** Without `--file` or `--function`, `review-packet` sends the source files changed against the base (`--base`, then `FALLOW_AUDIT_BASE`, then the merge-base). Narrow it with `--file <path>` or `--function <file>:<name>[:<line>]`, both repeatable; over MCP pass `files[]` or `functions[{file, name, line?}]`. Per function, read:
110
+
111
+ - `hit_count`: the production call count. Rank hot functions by it, not by `prod_hit_count`, which counts tagged traffic only.
112
+ - `tracking_state`: `called`, `never_called`, or `untracked` in the current deployment.
113
+ - `covered_by_test`: `true` or `null`. `null` means no test evidence, not "no test". A hot function with `covered_by_test: null` is a high-risk edit.
114
+ - `blast_radius.caller_count` and `blast_radius.caller_sites`: callers when the data exists; `null` is unknown, not zero.
115
+
116
+ An entry in `not_found` has no cloud data. Absence is not evidence that the code is cold.
117
+
118
+ **Before a delete.** Require both static and runtime evidence:
119
+
120
+ 1. `fallow dead-code --trace <file>:<export>` (MCP `trace_export`) confirms the static side.
121
+ 2. `period_tracking_state` is `never_called`. It covers the whole period, while `tracking_state` covers only the current deployment. A function with `period_tracking_state: "called"` never gets a `safe_to_delete` verdict.
122
+ 3. `evidence_window.observed_hours` is large enough for the traffic of that code. An admin page, a yearly job, an error handler, or a flagged feature can stay unvisited for a long time: "never called" there means "not visited", not "dead". Ask the owner or keep the code.
123
+
124
+ **After a deploy.** `deployment-changes` compares `--sha` (default: git HEAD) with `--base` (default: the previous deployment with production runtime). Each function gets one kind: `stopped`, `new_not_called`, `heated_up`, `cooled_down`, `new_called`, or `unchanged`. Filter with `--change <kind>`, page with `--limit` (1 to 200) and `--cursor`. When `comparable` is `false`, report `reason` and claim no stop and no rate change: `head_warming_up`, `head_short_window`, and `head_insufficient_runtime` mean "try again later"; `no_base_deployment` means there is nothing to compare; `runtime_surfaces_differ`, `runtime_surfaces_unknown`, and `function_set_differs` mean the two deployments do not measure the same code. The report is context and never proves that a function is dead.
125
+
126
+ **Paths.** Open and edit files by `repo_path`. `file_path` is the path the runtime reported (for example `/app/src/x.ts` in a container) and often does not exist in the checkout.
127
+
98
128
  ## Runtime source-map confidence for cloud runtime tools
99
129
 
100
130
  | Values | Meaning | Agent action |