fallow 3.27.0 → 3.28.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.
package/capabilities.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fallow",
3
- "version": "3.27.0",
3
+ "version": "3.28.0",
4
4
  "manifest_version": "1",
5
5
  "description": "Codebase analyzer for TypeScript/JavaScript: unused code, circular dependencies, code duplication, complexity hotspots, and architecture boundary violations",
6
6
  "global_flags": [
@@ -7262,7 +7262,7 @@
7262
7262
  ]
7263
7263
  },
7264
7264
  "plugins": {
7265
- "count": 126,
7265
+ "count": 127,
7266
7266
  "note": "Built-in framework plugins, auto-activated when their enabler dependency is present; run fallow list --plugins for the set active in a specific project",
7267
7267
  "names": [
7268
7268
  "nextjs",
@@ -7314,6 +7314,7 @@
7314
7314
  "rolldown",
7315
7315
  "rspack",
7316
7316
  "rsbuild",
7317
+ "module-federation",
7317
7318
  "tsup",
7318
7319
  "tsdown",
7319
7320
  "pkg-utils",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fallow",
3
- "version": "3.27.0",
3
+ "version": "3.28.0",
4
4
  "mcpName": "io.github.fallow-rs/fallow",
5
5
  "description": "Codebase intelligence for TypeScript and JavaScript. Free static analysis of code and styles, optional paid runtime intelligence (Fallow Runtime). Quality, risk, architecture, dependencies, duplication, and design-system drift for humans, CI, and the agents writing your code. Zero-config framework support.",
6
6
  "license": "MIT",
@@ -88,14 +88,14 @@
88
88
  "@tanstack/intent": "0.4.0"
89
89
  },
90
90
  "optionalDependencies": {
91
- "@fallow-cli/darwin-arm64": "3.27.0",
92
- "@fallow-cli/darwin-x64": "3.27.0",
93
- "@fallow-cli/linux-x64-gnu": "3.27.0",
94
- "@fallow-cli/linux-arm64-gnu": "3.27.0",
95
- "@fallow-cli/linux-x64-musl": "3.27.0",
96
- "@fallow-cli/linux-arm64-musl": "3.27.0",
97
- "@fallow-cli/win32-arm64-msvc": "3.27.0",
98
- "@fallow-cli/win32-x64-msvc": "3.27.0",
99
- "fallow-type-aware": "3.27.0"
91
+ "@fallow-cli/darwin-arm64": "3.28.0",
92
+ "@fallow-cli/darwin-x64": "3.28.0",
93
+ "@fallow-cli/linux-x64-gnu": "3.28.0",
94
+ "@fallow-cli/linux-arm64-gnu": "3.28.0",
95
+ "@fallow-cli/linux-x64-musl": "3.28.0",
96
+ "@fallow-cli/linux-arm64-musl": "3.28.0",
97
+ "@fallow-cli/win32-arm64-msvc": "3.28.0",
98
+ "@fallow-cli/win32-x64-msvc": "3.28.0",
99
+ "fallow-type-aware": "3.28.0"
100
100
  }
101
101
  }
package/schema.json CHANGED
@@ -363,7 +363,7 @@
363
363
  "default": false
364
364
  },
365
365
  "autoImports": {
366
- "description": "When true, drops Nuxt convention-based entry-pattern fallbacks: component fallbacks are dropped unless nuxt.config declares components:, and composable/util fallbacks are dropped unless it declares imports:, so genuinely-unreferenced convention files surface as unused-file. Boolean, defaults to false; set it for a Nuxt project that has explicitly configured its auto-import directories. Synthesis of auto-import graph edges (resolving `<Card />` or `useUserStore()` to their convention files) happens regardless of this flag.",
366
+ "description": "When true, drops Nuxt convention-based entry-pattern fallbacks so genuinely-unreferenced convention files surface as unused-file. Component fallbacks are kept when nuxt.config customizes components: in a way fallow does not model, and composable/util fallbacks are kept when it customizes imports:; a config that scans no more than the Nuxt defaults (components: false, components: [], components: { dirs: [] }, components: true, imports: { scan: false }, imports: {}, imports: { dirs: [] }) is treated like the default and its fallbacks are dropped; imports: { autoImport: false } on its own is not, because it only switches the injection off while the same directories stay registered behind #imports. A nuxt.config that carries any top-level key fallow cannot read statically, a computed key, an accessor, or a spread such as { ...base, devtools: {} }, keeps both surfaces' fallbacks for that root, because the spread may hold the keys that decide. Each root is classified on its own config, so one custom nuxt.config in a monorepo does not keep every other workspace's fallbacks. Component names follow Nuxt: a component under components/global or components/islands is named after its own directory (components/global/Foo.vue is `<Foo>`), and .client, .server, .global and .island suffixes are stripped. A name imported or re-exported by hand from #components or #imports credits its convention file just like a template tag or a bare call; a namespace import names nothing and credits nothing. Boolean, defaults to false; set it for a Nuxt project that has explicitly configured its auto-import directories. Synthesis of auto-import graph edges (resolving `<Card />` or `useUserStore()` to their convention files) happens regardless of this flag.",
367
367
  "type": "boolean",
368
368
  "default": false
369
369
  },
@@ -446,7 +446,7 @@ Human output groups paths under "Shared with your team (commit these)" and "Loca
446
446
  {
447
447
  "kind": "agent-install",
448
448
  "schema_version": 1,
449
- "fallow_version": "3.27.0",
449
+ "fallow_version": "3.28.0",
450
450
  "root": "/abs/path",
451
451
  "mode": "install",
452
452
  "dry_run": false,
@@ -650,7 +650,7 @@ fallow health --format json --quiet --trend
650
650
  {
651
651
  "kind": "health",
652
652
  "schema_version": 7,
653
- "version": "3.27.0",
653
+ "version": "3.28.0",
654
654
  "elapsed_ms": 32,
655
655
  "summary": {
656
656
  "files_analyzed": 482,
@@ -1053,7 +1053,7 @@ fallow audit \
1053
1053
  {
1054
1054
  "kind": "audit",
1055
1055
  "schema_version": 7,
1056
- "version": "3.27.0",
1056
+ "version": "3.28.0",
1057
1057
  "command": "audit",
1058
1058
  "verdict": "fail",
1059
1059
  "changed_files_count": 12,
@@ -1130,7 +1130,7 @@ fallow flags --format json --quiet --workspace my-package
1130
1130
  ```json
1131
1131
  {
1132
1132
  "schema_version": 7,
1133
- "version": "3.27.0",
1133
+ "version": "3.28.0",
1134
1134
  "elapsed_ms": 116,
1135
1135
  "feature_flags": [],
1136
1136
  "total_flags": 0
@@ -1231,7 +1231,7 @@ fallow security --gate newly-reachable --changed-since origin/main
1231
1231
  {
1232
1232
  "kind": "security",
1233
1233
  "schema_version": "4",
1234
- "version": "3.27.0",
1234
+ "version": "3.28.0",
1235
1235
  "elapsed_ms": 42,
1236
1236
  "config": {
1237
1237
  "rules": {
@@ -1260,7 +1260,7 @@ fallow security --gate newly-reachable --changed-since origin/main
1260
1260
  {
1261
1261
  "kind": "security",
1262
1262
  "schema_version": "4",
1263
- "version": "3.27.0",
1263
+ "version": "3.28.0",
1264
1264
  "elapsed_ms": 42,
1265
1265
  "config": {
1266
1266
  "rules": {
@@ -2030,7 +2030,7 @@ The HTTP layer mirrors the bash `gh_api_retry` / `curl_retry` helpers: `FALLOW_A
2030
2030
  {
2031
2031
  "kind": "dead-code",
2032
2032
  "schema_version": 7,
2033
- "version": "3.27.0",
2033
+ "version": "3.28.0",
2034
2034
  "elapsed_ms": 45,
2035
2035
  "total_issues": 12,
2036
2036
  "entry_points": {
@@ -2190,7 +2190,7 @@ When `--baseline` is used in combined output, the JSON includes a `baseline_delt
2190
2190
  {
2191
2191
  "kind": "dupes",
2192
2192
  "schema_version": 7,
2193
- "version": "3.27.0",
2193
+ "version": "3.28.0",
2194
2194
  "elapsed_ms": 82,
2195
2195
  "total_clones": 15,
2196
2196
  "total_lines_duplicated": 230,
@@ -2234,11 +2234,11 @@ When running `fallow` with no subcommand (all analyses), the JSON output combine
2234
2234
  {
2235
2235
  "kind": "combined",
2236
2236
  "schema_version": 7,
2237
- "version": "3.27.0",
2237
+ "version": "3.28.0",
2238
2238
  "elapsed_ms": 159,
2239
2239
  "check": {
2240
2240
  "schema_version": 7,
2241
- "version": "3.27.0",
2241
+ "version": "3.28.0",
2242
2242
  "elapsed_ms": 45,
2243
2243
  "total_issues": 12,
2244
2244
  "unused_files": [],
@@ -2376,8 +2376,10 @@ Config files are searched in priority order: `.fallowrc.json` > `.fallowrc.jsonc
2376
2376
  // Resolve framework convention auto-imports (Nuxt components) as graph edges.
2377
2377
  // Edges for `<Card001 />`-style template tags are always synthesized; setting
2378
2378
  // this to true also drops the Nuxt component entry patterns so an
2379
- // unreferenced component is reported as unused-file. Kept conservative: a
2380
- // `components:` key in nuxt.config keeps the entry patterns. Default false.
2379
+ // unreferenced component is reported as unused-file. Kept conservative: an
2380
+ // unmodelled `components:` or `imports:` config keeps the entry patterns, but
2381
+ // a config that switches the scan off (`components: { dirs: [] }`,
2382
+ // `imports: { scan: false }`) is treated like the default. Default false.
2381
2383
  "autoImports": false,
2382
2384
 
2383
2385
  // Production mode
@@ -420,6 +420,22 @@ export type DependencyOverrideMisconfigReason = ("unparsable-key" | "empty-value
420
420
  * from counts.
421
421
  */
422
422
  export type BaselineStalenessAdvisory = ("none" | "zero-overlap" | "partial")
423
+ /**
424
+ * One channel that narrowed a run to part of the project.
425
+ *
426
+ * Serialized as kebab-case inside `scope_reasons` and published as an OPEN
427
+ * set, the same tolerate-unknown contract `gate_outcomes` keys carry: a name
428
+ * this build does not emit means "some narrowing", not an error.
429
+ *
430
+ * Which names a command can emit differs per command, because the three
431
+ * narrowing predicates see different state. `dead-code` reads the flags
432
+ * themselves and can name every channel. `dupes` and `health` see an already
433
+ * resolved changed-file set and report `changed-files`, because at that point
434
+ * the flag that produced it is gone. `health` reports `workspace` for both
435
+ * `--workspace` and `--changed-workspaces` for the same reason. A consumer
436
+ * must therefore not assume a given command emits a given name.
437
+ */
438
+ export type ScopeReason = ("diff" | "changed-since" | "changed-files" | "workspace" | "changed-workspaces" | "scope" | "file" | "issue-type-filter" | "production")
423
439
  /**
424
440
  * Status of a regression-check pass.
425
441
  */
@@ -428,6 +444,29 @@ export type RegressionStatus = ("pass" | "exceeded" | "skipped")
428
444
  * Interpretation of [`RegressionResult::tolerance`].
429
445
  */
430
446
  export type RegressionToleranceKind = ("absolute" | "percentage")
447
+ /**
448
+ * What became of one request on this run.
449
+ *
450
+ * Two-valued today. The value set is OPEN so a later `partial` needs no bump,
451
+ * and it is deliberately not added now: nothing emits it, and a permanently
452
+ * unused value reads as a measurement nobody takes.
453
+ */
454
+ export type RequestStatus = ("applied" | "not-applied")
455
+ /**
456
+ * What a request governs, and therefore what its failure means.
457
+ *
458
+ * Published on every entry so a consumer selects on the class rather than on
459
+ * a name list. Without it the one sentence a consumer can write for the whole
460
+ * object ("the report is wider than requested") is false for any request that
461
+ * does not narrow, which is how a failed `--sarif-file` write came to be
462
+ * reported as an unscoped run. A request name added later carries its own
463
+ * class, so a consumer written today keeps saying the right thing about it.
464
+ *
465
+ * The value set is OPEN, like the names and the statuses: read a class this
466
+ * build does not recognise as "some request", not as an error, and do not read
467
+ * it as `scope`.
468
+ */
469
+ export type RequestEffect = ("scope" | "artifact")
431
470
  /**
432
471
  * A diagnostic about a workspace-discovery candidate.
433
472
  *
@@ -570,6 +609,97 @@ kind: "excluded-by-default-ignore"
570
609
  */
571
610
  excluded_file_count: number
572
611
  kind: "no-source-files-analyzed"
612
+ } | {
613
+ /**
614
+ * Scoring error text.
615
+ */
616
+ error: string
617
+ kind: "file-scores-unavailable"
618
+ } | {
619
+ /**
620
+ * Which input stopped it, as a kebab-case token: `not-a-repository`,
621
+ * `invalid-since` or `churn-file-unreadable`. The set is open.
622
+ *
623
+ * The cause decides the remedy, which is why it is on the wire: a run
624
+ * outside a repository is fixed by running fallow inside one, a
625
+ * malformed `--since` by respelling the flag, and a churn file that
626
+ * changed under the run by rerunning it. A consumer reading only the
627
+ * kind would offer the first remedy for all three.
628
+ */
629
+ cause: string
630
+ kind: "hotspots-skipped"
631
+ } | {
632
+ /**
633
+ * `true` when the run also asked for ownership attribution, which a
634
+ * shallow clone skews further by inflating single-author dominance.
635
+ */
636
+ ownership_requested: boolean
637
+ kind: "shallow-clone"
638
+ } | {
639
+ kind: "unpinned-clock"
640
+ } | {
641
+ /**
642
+ * Which input failed, as a kebab-case token: `invalid-bot-pattern` or
643
+ * `codeowners-parse-failed`. The set is open.
644
+ */
645
+ cause: string
646
+ /**
647
+ * Underlying error text.
648
+ */
649
+ error: string
650
+ kind: "ownership-unavailable"
651
+ } | {
652
+ /**
653
+ * Filesystem or JSON error text.
654
+ */
655
+ error: string
656
+ kind: "trend-snapshot-unreadable"
657
+ } | {
658
+ /**
659
+ * The plugin that read the config, as it labels itself:
660
+ * `module-federation` for a standalone `module-federation.config.*`,
661
+ * or the bundler plugin (`webpack`, `rspack`, `rsbuild`, `vite`) that
662
+ * read the same options inline from its own config.
663
+ */
664
+ plugin: string
665
+ /**
666
+ * The config key that was present and not fully readable (`exposes`,
667
+ * `remotes`). The set is open.
668
+ */
669
+ key: string
670
+ /**
671
+ * Why it could not be read, as a kebab-case token:
672
+ * `not-object-literal`, `array-form`, `spread` or
673
+ * `unreadable-entries`. The set is open.
674
+ *
675
+ * The reason decides the remedy, which is why it is on the wire: a
676
+ * value that is not an object literal is fixed by writing one, while
677
+ * unreadable entries are fixed by naming those entries in the config
678
+ * option the message points at.
679
+ */
680
+ reason: string
681
+ kind: "plugin-config-unreadable"
682
+ } | {
683
+ /**
684
+ * The plugin that read the config, as it labels itself (`nuxt`).
685
+ */
686
+ plugin: string
687
+ /**
688
+ * The config key whose effect is not modeled (`components`,
689
+ * `imports`). The set is open.
690
+ */
691
+ key: string
692
+ /**
693
+ * Why the effect is not modeled, as a kebab-case token:
694
+ * `key-effect-not-modeled` when the key's own value is the reason,
695
+ * `config-property-unreadable` when a top-level property of the same
696
+ * config file could not be read statically, so no surface in it can
697
+ * be classified at all. The set is open.
698
+ */
699
+ reason: string
700
+ kind: "plugin-effect-not-modeled"
701
+ } | {
702
+ kind: "coverage-auto-detected"
573
703
  })
574
704
  /**
575
705
  * Discriminant for [`CloneGroupAction::kind`]. Mirrors the action types
@@ -2820,6 +2950,16 @@ regression?: (RegressionResult | null)
2820
2950
  * was asked for", never "nothing failed". See [`crate::GateOutcomes`].
2821
2951
  */
2822
2952
  gate_outcomes?: (GateOutcomes | null)
2953
+ /**
2954
+ * Every narrowing or shaping request this run RECEIVED, keyed by name,
2955
+ * absent when it was asked for nothing. An entry whose `status` is not
2956
+ * `applied` means the run could not do what it was asked and reported
2957
+ * something WIDER instead, so what follows is a valid report of a scope
2958
+ * nobody requested. Honoured requests are published too, with
2959
+ * `status: "applied"`, so an absent object means "nothing was asked for",
2960
+ * never "nothing failed". See [`crate::RequestOutcomes`].
2961
+ */
2962
+ request_outcomes?: (RequestOutcomes | null)
2823
2963
  /**
2824
2964
  * `_meta` block with docs and rule definitions, when `--explain` was
2825
2965
  * passed.
@@ -2838,10 +2978,12 @@ _meta?: (Meta | null)
2838
2978
  * `source-parse-degraded`;
2839
2979
  * - dead-code analysis, from the dependency-catalog and override
2840
2980
  * detectors: `malformed-pnpm-workspace-yaml`,
2841
- * `bun-lockb-override-resolution-skipped`.
2981
+ * `bun-lockb-override-resolution-skipped`;
2982
+ * - framework plugins, while they read their own build configs:
2983
+ * `plugin-config-unreadable`, `plugin-effect-not-modeled`.
2842
2984
  *
2843
- * Analysis-stage kinds therefore reach only the envelopes whose run
2844
- * includes a dead-code analyze pass, never a standalone
2985
+ * Analysis-stage and plugin-stage kinds therefore reach only the envelopes
2986
+ * whose run includes a dead-code analyze pass, never a standalone
2845
2987
  * `fallow dupes --format json`. `path` is project-root-relative with
2846
2988
  * forward slashes; the array is omitted when empty. The same list is
2847
2989
  * repeated on each top-level command's envelope so single-command
@@ -5289,7 +5431,8 @@ current_findings: number
5289
5431
  * differ per command and include a diff, a base ref, `--changed-since`,
5290
5432
  * `--workspace`, `--changed-workspaces`, `--scope`, `--file`, an
5291
5433
  * issue-type filter, and production mode. Both `stale` and `gate_trips`
5292
- * are false whenever this is true.
5434
+ * are false whenever this is true. `scope_reasons` names the channels
5435
+ * that fired.
5293
5436
  */
5294
5437
  change_scoped: boolean
5295
5438
  /**
@@ -5301,13 +5444,21 @@ change_scoped: boolean
5301
5444
  stale: boolean
5302
5445
  warning: BaselineStalenessAdvisory
5303
5446
  /**
5304
- * True exactly when
5305
- * `!change_scoped && baseline_entries > 0 && matched_entries < baseline_entries`,
5306
- * which is the rule `--fail-on-stale-baseline` applies. Deliberately
5307
- * stricter than `stale`: any unmatched entry counts. It describes the
5308
- * baseline, not the run's exit code: `health --report-only` is an explicit
5309
- * request never to fail, so that run exits 0 and says so on stderr while
5310
- * still reporting `gate_trips: true` here.
5447
+ * True exactly when `unrecognised_format` is true, or
5448
+ * `!change_scoped && baseline_entries > 0 && matched_entries < baseline_entries`.
5449
+ * That is the rule `--fail-on-stale-baseline` applies. Deliberately
5450
+ * stricter than `stale`: any unmatched entry counts, and so does a file
5451
+ * this command could not read as its own, which protects nothing at all.
5452
+ * The second half is suppressed by `change_scoped` and the first is not:
5453
+ * which command wrote a file does not depend on how much of the project
5454
+ * the run looked at.
5455
+ *
5456
+ * It describes the baseline, not the run's exit code: `health
5457
+ * --report-only` is an explicit request never to fail, so that run exits 0
5458
+ * and says so on stderr while still reporting `gate_trips: true` here, and
5459
+ * `fallow audit` never judges a baseline at all, so its
5460
+ * `gate_outcomes["stale-baseline"]` stands down beside a section that
5461
+ * reports `true`.
5311
5462
  */
5312
5463
  gate_trips: boolean
5313
5464
  /**
@@ -5317,6 +5468,37 @@ gate_trips: boolean
5317
5468
  * `0`. Always `0` in health's count mode too.
5318
5469
  */
5319
5470
  moved_entries: number
5471
+ /**
5472
+ * True when the loaded file is not a baseline of the command that read it:
5473
+ * it names another command in its top-level `kind`, or it names none and
5474
+ * carries no key this command's own format writes. Read this, not
5475
+ * `baseline_entries == 0`, before telling anyone their baseline is the
5476
+ * wrong file: a baseline saved from a project that had nothing to record
5477
+ * is legitimately empty and is not a mistake.
5478
+ *
5479
+ * Present only when true, so an envelope from a run that loaded its own
5480
+ * baseline is unchanged. All three commands set it, including `dead-code`,
5481
+ * which classifies the file before its required fields could reject it.
5482
+ * A file with no `kind` is the reading a baseline saved before that member
5483
+ * existed gets, which is why the keys remain the fallback.
5484
+ */
5485
+ unrecognised_format?: boolean
5486
+ /**
5487
+ * Which channels narrowed this run, present and non-empty exactly when
5488
+ * `change_scoped` is true. Both members are derived from one function, so
5489
+ * the boolean and the array cannot disagree.
5490
+ *
5491
+ * Read it to decide whether the narrowing is removable: a run narrowed
5492
+ * only by `diff`, `changed-since`, `changed-files`, `scope`, `file` or
5493
+ * `issue-type-filter` can be repeated unscoped to judge the baseline,
5494
+ * while `production`, `workspace` and `changed-workspaces` are the
5495
+ * caller's own choice about what to analyze and an unscoped repeat would
5496
+ * contradict it.
5497
+ *
5498
+ * The name set is OPEN and the names a command can emit differ per
5499
+ * command; see [`ScopeReason`].
5500
+ */
5501
+ scope_reasons?: ScopeReason[]
5320
5502
  }
5321
5503
  /**
5322
5504
  * Result of regression detection (`--fail-on-regression`). Compares current
@@ -5354,6 +5536,102 @@ exceeded: boolean
5354
5536
  */
5355
5537
  reason?: (string | null)
5356
5538
  }
5539
+ /**
5540
+ * Every narrowing or shaping request a run RECEIVED, keyed by name.
5541
+ *
5542
+ * Received, not failed: a request the run honoured is published with
5543
+ * `status: "applied"`, so a consumer can say "scoped to the change"
5544
+ * positively. Read an absent object as "nothing was asked for", never as
5545
+ * "nothing failed".
5546
+ *
5547
+ * Absent from an envelope whenever it is empty, so a run that was asked for
5548
+ * nothing is byte-identical to one produced before this object existed. An
5549
+ * empty object is never emitted: it would assert that something was asked and
5550
+ * all of it applied, which is a different and false claim.
5551
+ *
5552
+ * The names this build can emit are `changed-since`, `diff-filter` and
5553
+ * `sarif-file`. The reasons are `git-missing`, `not-a-repository`,
5554
+ * `git-failed` and `invalid-ref` for `changed-since`, `oversize`,
5555
+ * `unreadable`, `not-utf8`, `foreign-namespace` and `ambiguous-base` for
5556
+ * `diff-filter`, and `directory-create-failed`, `write-failed` and
5557
+ * `serialize-failed` for `sarif-file`. Every set is OPEN: a name a consumer
5558
+ * does not recognise means "some request", not an error.
5559
+ *
5560
+ * `sarif-file` reports a SECONDARY artifact rather than the scope of the
5561
+ * report it travels in, and it is in the same object for the same reason the
5562
+ * others are: the run was asked to do something and did something else, and
5563
+ * nothing in the primary report says so. Which of the two an entry is, every
5564
+ * entry says for itself: `affects` is `scope` for the narrowing requests and
5565
+ * `artifact` for this one. Select on it. A consumer that instead assumes the
5566
+ * whole object narrows the report tells its reader an unwritten SARIF file
5567
+ * widened the analysis, which is what `affects` exists to prevent.
5568
+ *
5569
+ * `scope_size` is emitted for `diff-filter` only today, in added lines. A
5570
+ * consumer reads the unit off the name, so a name that starts measuring its
5571
+ * own scope in a later release needs no change here.
5572
+ *
5573
+ * `invalid-ref` is reachable only through the programmatic API. The
5574
+ * `--changed-since` flag validates its value before a run starts and fails
5575
+ * with exit 2 and an error document, which is the right side to err on: a
5576
+ * malformed ref is invalid input rather than a report of the wrong scope.
5577
+ */
5578
+ export interface RequestOutcomes {
5579
+ [k: string]: RequestOutcome
5580
+ }
5581
+ /**
5582
+ * One request's fate on one run.
5583
+ *
5584
+ * `reason` and `message` are present exactly when `status` is not `applied`,
5585
+ * and absent otherwise, so a consumer that only wants to know whether a
5586
+ * report is scoped reads `status` alone.
5587
+ */
5588
+ export interface RequestOutcome {
5589
+ status: RequestStatus
5590
+ affects: RequestEffect
5591
+ /**
5592
+ * What was asked, as the user spelled it: the git ref for
5593
+ * `changed-since`, the diff source label (`--diff-file pr.diff`,
5594
+ * `--diff-stdin`, `$FALLOW_DIFF_FILE build/pr.diff`) for `diff-filter`,
5595
+ * the target path for `sarif-file`. Echoed rather than normalised, so a
5596
+ * consumer must not join it to the project root the way it joins every
5597
+ * other path-shaped field.
5598
+ */
5599
+ requested: string
5600
+ /**
5601
+ * How much this request left in scope, in the request's own unit, when the
5602
+ * run applied it AND measured that scope. Absent otherwise, including on
5603
+ * every unapplied entry: a request that stood down narrowed nothing, so a
5604
+ * number there would describe a scope nobody applied.
5605
+ *
5606
+ * The unit belongs to the name. `diff-filter` counts added lines, which is
5607
+ * what its filter keeps a finding for. Read the unit off the name the entry
5608
+ * is keyed under, never across names, and read an absent member as "not
5609
+ * measured" rather than as zero.
5610
+ *
5611
+ * The count is what the run INDEXED rather than the true total:
5612
+ * `diff-filter` indexes at most one million added lines and reports that
5613
+ * cap for a larger diff, so read any non-zero value as a lower bound.
5614
+ *
5615
+ * `0` is the case this member exists for: a request that applied over an
5616
+ * EMPTY scope. Every finding then filters out and the report reads clean,
5617
+ * so a consumer that sees no findings beside `scope_size: 0` learns that
5618
+ * nothing was analyzable rather than that the code is clean.
5619
+ */
5620
+ scope_size?: (number | null)
5621
+ /**
5622
+ * Why the request was not applied, as a kebab-case token. Present exactly
5623
+ * when `status` is not `applied`. The set is open per request name; the
5624
+ * names this build can emit are listed on [`RequestOutcomes`].
5625
+ */
5626
+ reason?: (string | null)
5627
+ /**
5628
+ * One sentence naming what was asked, what happened instead, and the next
5629
+ * step. Byte-identical to the stderr line for the same case, so a
5630
+ * consumer that renders this never contradicts a log a human read.
5631
+ * Present exactly when `status` is not `applied`.
5632
+ */
5633
+ message?: (string | null)
5634
+ }
5357
5635
  /**
5358
5636
  * A read-only follow-up command fallow surfaces from the current findings,
5359
5637
  * emitted as the top-level `next_steps` array on each command's JSON envelope.
@@ -11027,6 +11305,16 @@ groups?: (HealthGroup[] | null)
11027
11305
  * was asked for", never "nothing failed". See [`crate::GateOutcomes`].
11028
11306
  */
11029
11307
  gate_outcomes?: (GateOutcomes | null)
11308
+ /**
11309
+ * Every narrowing or shaping request this run RECEIVED, keyed by name,
11310
+ * absent when it was asked for nothing. An entry whose `status` is not
11311
+ * `applied` means the run could not do what it was asked and reported
11312
+ * something WIDER instead, so what follows is a valid report of a scope
11313
+ * nobody requested. Honoured requests are published too, with
11314
+ * `status: "applied"`, so an absent object means "nothing was asked for",
11315
+ * never "nothing failed". See [`crate::RequestOutcomes`].
11316
+ */
11317
+ request_outcomes?: (RequestOutcomes | null)
11030
11318
  /**
11031
11319
  * `_meta` block with metric definitions, when `--explain` was passed.
11032
11320
  */
@@ -11220,6 +11508,16 @@ baseline_staleness?: (BaselineStaleness | null)
11220
11508
  * was asked for", never "nothing failed". See [`crate::GateOutcomes`].
11221
11509
  */
11222
11510
  gate_outcomes?: (GateOutcomes | null)
11511
+ /**
11512
+ * Every narrowing or shaping request this run RECEIVED, keyed by name,
11513
+ * absent when it was asked for nothing. An entry whose `status` is not
11514
+ * `applied` means the run could not do what it was asked and reported
11515
+ * something WIDER instead, so what follows is a valid report of a scope
11516
+ * nobody requested. Honoured requests are published too, with
11517
+ * `status: "applied"`, so an absent object means "nothing was asked for",
11518
+ * never "nothing failed". See [`crate::RequestOutcomes`].
11519
+ */
11520
+ request_outcomes?: (RequestOutcomes | null)
11223
11521
  /**
11224
11522
  * `_meta` block with metric / rule definitions, emitted when `--explain`
11225
11523
  * is passed (always present in MCP responses).
@@ -11397,6 +11695,16 @@ baseline_staleness?: (BaselineStaleness | null)
11397
11695
  * was asked for", never "nothing failed". See [`crate::GateOutcomes`].
11398
11696
  */
11399
11697
  gate_outcomes?: (GateOutcomes | null)
11698
+ /**
11699
+ * Every narrowing or shaping request this run RECEIVED, keyed by name,
11700
+ * absent when it was asked for nothing. An entry whose `status` is not
11701
+ * `applied` means the run could not do what it was asked and reported
11702
+ * something WIDER instead, so what follows is a valid report of a scope
11703
+ * nobody requested. Honoured requests are published too, with
11704
+ * `status: "applied"`, so an absent object means "nothing was asked for",
11705
+ * never "nothing failed". See [`crate::RequestOutcomes`].
11706
+ */
11707
+ request_outcomes?: (RequestOutcomes | null)
11400
11708
  /**
11401
11709
  * `_meta` block with docs and rule definitions, when `--explain` was
11402
11710
  * passed.
@@ -12076,6 +12384,16 @@ config: SecurityOutputConfig
12076
12384
  * was asked for", never "nothing failed". See [`crate::GateOutcomes`].
12077
12385
  */
12078
12386
  gate_outcomes?: (GateOutcomes | null)
12387
+ /**
12388
+ * Every narrowing or shaping request this run RECEIVED, keyed by name,
12389
+ * absent when it was asked for nothing. An entry whose `status` is not
12390
+ * `applied` means the run could not do what it was asked and reported
12391
+ * something WIDER instead, so what follows is a valid report of a scope
12392
+ * nobody requested. Honoured requests are published too, with
12393
+ * `status: "applied"`, so an absent object means "nothing was asked for",
12394
+ * never "nothing failed". See [`crate::RequestOutcomes`].
12395
+ */
12396
+ request_outcomes?: (RequestOutcomes | null)
12079
12397
  /**
12080
12398
  * Security-specific rule and field metadata, emitted with `--explain`.
12081
12399
  */
@@ -12709,6 +13027,16 @@ config: SecurityOutputConfig
12709
13027
  * was asked for", never "nothing failed". See [`crate::GateOutcomes`].
12710
13028
  */
12711
13029
  gate_outcomes?: (GateOutcomes | null)
13030
+ /**
13031
+ * Every narrowing or shaping request this run RECEIVED, keyed by name,
13032
+ * absent when it was asked for nothing. An entry whose `status` is not
13033
+ * `applied` means the run could not do what it was asked and reported
13034
+ * something WIDER instead, so what follows is a valid report of a scope
13035
+ * nobody requested. Honoured requests are published too, with
13036
+ * `status: "applied"`, so an absent object means "nothing was asked for",
13037
+ * never "nothing failed". See [`crate::RequestOutcomes`].
13038
+ */
13039
+ request_outcomes?: (RequestOutcomes | null)
12712
13040
  /**
12713
13041
  * Security-specific rule and field metadata, emitted with `--explain`.
12714
13042
  */
@@ -13002,6 +13330,16 @@ elapsed_ms: ElapsedMs
13002
13330
  * was asked for", never "nothing failed". See [`crate::GateOutcomes`].
13003
13331
  */
13004
13332
  gate_outcomes?: (GateOutcomes | null)
13333
+ /**
13334
+ * Every narrowing or shaping request this run RECEIVED, keyed by name,
13335
+ * absent when it was asked for nothing. An entry whose `status` is not
13336
+ * `applied` means the run could not do what it was asked and reported
13337
+ * something WIDER instead, so what follows is a valid report of a scope
13338
+ * nobody requested. Honoured requests are published too, with
13339
+ * `status: "applied"`, so an absent object means "nothing was asked for",
13340
+ * never "nothing failed". See [`crate::RequestOutcomes`].
13341
+ */
13342
+ request_outcomes?: (RequestOutcomes | null)
13005
13343
  /**
13006
13344
  * Per-section `_meta` blocks, when `--explain` was passed.
13007
13345
  */
@@ -13061,6 +13399,22 @@ export interface FeatureFlagsOutput {
13061
13399
  schema_version: FeatureFlagsSchemaVersion
13062
13400
  version: ToolVersion
13063
13401
  elapsed_ms: ElapsedMs
13402
+ /**
13403
+ * What the run was asked to narrow and whether it did. See
13404
+ * [`crate::RequestOutcomes`] for the full contract.
13405
+ *
13406
+ * `fallow flags` accepts `--changed-since`, and an unresolvable ref widens
13407
+ * the scan to the whole project rather than failing the run. Until this
13408
+ * member existed the only account of that was a stderr line, which `--quiet`
13409
+ * removes, so a flag inventory read as scoped to the change could silently
13410
+ * be the whole project's (issue #2734).
13411
+ *
13412
+ * The command applies no diff filter, so the object carries the
13413
+ * `changed-since` entry only. Omitted when the run was asked for nothing,
13414
+ * which keeps a scan that passed no narrowing flag byte-identical and moves
13415
+ * no `schema_version`.
13416
+ */
13417
+ request_outcomes?: (RequestOutcomes | null)
13064
13418
  /**
13065
13419
  * Detected feature-flag findings.
13066
13420
  */
@@ -14397,6 +14751,22 @@ invalid_value?: (string | null)
14397
14751
  */
14398
14752
  export interface SuppressionInventoryOutput {
14399
14753
  schema_version: SuppressionInventorySchemaVersion
14754
+ /**
14755
+ * What the run was asked to narrow and whether it did. See
14756
+ * [`crate::RequestOutcomes`] for the full contract.
14757
+ *
14758
+ * `fallow suppressions` accepts `--changed-since`, and an unresolvable ref
14759
+ * widens the inventory to the whole project rather than failing the run.
14760
+ * Until this member existed the only account of that was a stderr line,
14761
+ * which `--quiet` removes, so an inventory read as scoped to the change
14762
+ * could silently be the whole project's (issue #2734).
14763
+ *
14764
+ * The command applies no diff filter, so the object carries the
14765
+ * `changed-since` entry only. Omitted when the run was asked for nothing,
14766
+ * which keeps an inventory that passed no narrowing flag byte-identical and
14767
+ * leaves `schema_version` at `1`.
14768
+ */
14769
+ request_outcomes?: (RequestOutcomes | null)
14400
14770
  summary: SuppressionInventorySummary
14401
14771
  /**
14402
14772
  * Per-file suppression listings, sorted by path then line.