fallow 3.29.0 → 3.30.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 +128 -4
- package/issue-registry.json +30 -0
- package/package.json +10 -10
- package/schema.json +9 -2
- package/skills/fallow/SKILL.md +3 -2
- package/skills/fallow/references/cli-reference.md +35 -13
- package/skills/fallow/references/issue-types.md +1 -0
- package/skills/fallow/references/mcp.md +1 -1
- package/types/output-contract.d.ts +437 -0
package/capabilities.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fallow",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.30.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": [
|
|
@@ -1055,6 +1055,16 @@
|
|
|
1055
1055
|
"required": false,
|
|
1056
1056
|
"description": "Shortest import path between two modules, as two file paths (e.g. `--path src/app.ts src/db.ts`). Mutually exclusive with the symbol target and the call-chain flags"
|
|
1057
1057
|
},
|
|
1058
|
+
{
|
|
1059
|
+
"name": "--eager-only",
|
|
1060
|
+
"type": "bool",
|
|
1061
|
+
"required": false,
|
|
1062
|
+
"description": "With `--path`, follow only static value imports, so the route explains why TO loads before FROM runs",
|
|
1063
|
+
"possible_values": [
|
|
1064
|
+
"true",
|
|
1065
|
+
"false"
|
|
1066
|
+
]
|
|
1067
|
+
},
|
|
1058
1068
|
{
|
|
1059
1069
|
"name": "--callers",
|
|
1060
1070
|
"type": "bool",
|
|
@@ -1264,7 +1274,7 @@
|
|
|
1264
1274
|
},
|
|
1265
1275
|
{
|
|
1266
1276
|
"name": "list",
|
|
1267
|
-
"description": "List discovered entry points, files, plugins, boundaries, and
|
|
1277
|
+
"description": "List discovered entry points, files, plugins, boundaries, workspaces, and the startup import weight",
|
|
1268
1278
|
"flags": [
|
|
1269
1279
|
{
|
|
1270
1280
|
"name": "--entry-points",
|
|
@@ -1316,6 +1326,16 @@
|
|
|
1316
1326
|
"false"
|
|
1317
1327
|
]
|
|
1318
1328
|
},
|
|
1329
|
+
{
|
|
1330
|
+
"name": "--entry-weight",
|
|
1331
|
+
"type": "bool",
|
|
1332
|
+
"required": false,
|
|
1333
|
+
"description": "Show the startup import weight of each runtime entry point, in source bytes (not bundle size)",
|
|
1334
|
+
"possible_values": [
|
|
1335
|
+
"true",
|
|
1336
|
+
"false"
|
|
1337
|
+
]
|
|
1338
|
+
},
|
|
1319
1339
|
{
|
|
1320
1340
|
"name": "path",
|
|
1321
1341
|
"type": "string",
|
|
@@ -1722,6 +1742,77 @@
|
|
|
1722
1742
|
"type": "string",
|
|
1723
1743
|
"required": false,
|
|
1724
1744
|
"description": "Show only the top N flags"
|
|
1745
|
+
},
|
|
1746
|
+
{
|
|
1747
|
+
"name": "--retirement",
|
|
1748
|
+
"type": "bool",
|
|
1749
|
+
"required": false,
|
|
1750
|
+
"description": "Add a retirement report: one row per flag, with the reasons the flag can be retired. Advisory only; nothing is removed",
|
|
1751
|
+
"possible_values": [
|
|
1752
|
+
"true",
|
|
1753
|
+
"false"
|
|
1754
|
+
]
|
|
1755
|
+
},
|
|
1756
|
+
{
|
|
1757
|
+
"name": "--reason",
|
|
1758
|
+
"type": "string",
|
|
1759
|
+
"required": false,
|
|
1760
|
+
"description": "Keep only retirement rows with this reason (repeatable)",
|
|
1761
|
+
"possible_values": [
|
|
1762
|
+
"single-read-site",
|
|
1763
|
+
"test-only",
|
|
1764
|
+
"literal-constant",
|
|
1765
|
+
"identical-branches",
|
|
1766
|
+
"empty-branch",
|
|
1767
|
+
"guards-dead-code",
|
|
1768
|
+
"defined-never-read",
|
|
1769
|
+
"fully-rolled-out",
|
|
1770
|
+
"archived-in-vendor",
|
|
1771
|
+
"missing-in-vendor",
|
|
1772
|
+
"vendor-only"
|
|
1773
|
+
]
|
|
1774
|
+
},
|
|
1775
|
+
{
|
|
1776
|
+
"name": "--sort",
|
|
1777
|
+
"type": "string",
|
|
1778
|
+
"required": false,
|
|
1779
|
+
"description": "Order of the retirement rows",
|
|
1780
|
+
"default": "age",
|
|
1781
|
+
"possible_values": [
|
|
1782
|
+
"age",
|
|
1783
|
+
"sites",
|
|
1784
|
+
"name"
|
|
1785
|
+
]
|
|
1786
|
+
},
|
|
1787
|
+
{
|
|
1788
|
+
"name": "--flag-age",
|
|
1789
|
+
"type": "string",
|
|
1790
|
+
"required": false,
|
|
1791
|
+
"description": "How to measure flag age: blame (lower bound), pickaxe (first commit with the name, slower) or off",
|
|
1792
|
+
"default": "blame",
|
|
1793
|
+
"possible_values": [
|
|
1794
|
+
"blame",
|
|
1795
|
+
"pickaxe",
|
|
1796
|
+
"off"
|
|
1797
|
+
]
|
|
1798
|
+
},
|
|
1799
|
+
{
|
|
1800
|
+
"name": "--min-age",
|
|
1801
|
+
"type": "string",
|
|
1802
|
+
"required": false,
|
|
1803
|
+
"description": "Keep only retirement rows at least this many days old"
|
|
1804
|
+
},
|
|
1805
|
+
{
|
|
1806
|
+
"name": "--flag-state",
|
|
1807
|
+
"type": "string",
|
|
1808
|
+
"required": false,
|
|
1809
|
+
"description": "Vendor flag export (JSON, read offline) that adds the fully-rolled-out, archived-in-vendor, missing-in-vendor and vendor-only reasons"
|
|
1810
|
+
},
|
|
1811
|
+
{
|
|
1812
|
+
"name": "--max-flag-age",
|
|
1813
|
+
"type": "string",
|
|
1814
|
+
"required": false,
|
|
1815
|
+
"description": "Exit with code 1 when a flag in scope is older than this many days. Opt-in; needs a flag age"
|
|
1725
1816
|
}
|
|
1726
1817
|
]
|
|
1727
1818
|
},
|
|
@@ -4672,6 +4763,36 @@
|
|
|
4672
4763
|
"license_note": null,
|
|
4673
4764
|
"docs_url": "https://docs.fallow.tools/cli/flags"
|
|
4674
4765
|
},
|
|
4766
|
+
{
|
|
4767
|
+
"id": "flag-retirement-candidate",
|
|
4768
|
+
"rule_id": "fallow/flag-retirement-candidate",
|
|
4769
|
+
"command": "flags",
|
|
4770
|
+
"category": "Flags",
|
|
4771
|
+
"description": "Feature flag is a retirement candidate",
|
|
4772
|
+
"label": null,
|
|
4773
|
+
"config_key": null,
|
|
4774
|
+
"registry_index": null,
|
|
4775
|
+
"aliases": [],
|
|
4776
|
+
"lsp": false,
|
|
4777
|
+
"filter_flag": null,
|
|
4778
|
+
"result_key": null,
|
|
4779
|
+
"summary_label": null,
|
|
4780
|
+
"summary_docs_anchor": null,
|
|
4781
|
+
"sarif_rule_ids": null,
|
|
4782
|
+
"codeclimate_check_names": null,
|
|
4783
|
+
"ts_alias": null,
|
|
4784
|
+
"counts_in_total": false,
|
|
4785
|
+
"fixable": false,
|
|
4786
|
+
"suppressible": false,
|
|
4787
|
+
"suppress_comment": null,
|
|
4788
|
+
"default_severity": null,
|
|
4789
|
+
"opt_in": null,
|
|
4790
|
+
"frameworks": [],
|
|
4791
|
+
"note": null,
|
|
4792
|
+
"license": "free",
|
|
4793
|
+
"license_note": null,
|
|
4794
|
+
"docs_url": "https://docs.fallow.tools/cli/flags#retirement-report"
|
|
4795
|
+
},
|
|
4675
4796
|
{
|
|
4676
4797
|
"id": "tainted-sink",
|
|
4677
4798
|
"rule_id": "security/tainted-sink",
|
|
@@ -6201,8 +6322,10 @@
|
|
|
6201
6322
|
"FALLOW_TIMEOUT_SECS": "MCP server: per-tool-call CLI subprocess timeout in seconds (default 120). Raise for long runs like production coverage on large dumps.",
|
|
6202
6323
|
"FALLOW_DIFF_FILE": "MCP server: path to a unified diff that scopes all findings by changed line.",
|
|
6203
6324
|
"FALLOW_CHANGED_SINCE": "MCP server: git ref that scopes file discovery for analysis tools.",
|
|
6325
|
+
"FALLOW_MCP_WARM_SESSION": "MCP server: set to 0, false, off or no to stop typed tool calls from keeping parsed modules in memory between calls (default on).",
|
|
6204
6326
|
"FALLOW_INTEGRATION_SURFACE": "Telemetry integration_surface override for non-CLI surfaces (mcp/lsp/vscode/napi/programmatic). Set by the MCP server on the CLI it spawns.",
|
|
6205
|
-
"FALLOW_MCP_TOOL": "Telemetry mcp_tool dimension, validated against the MCP tool-name allowlist. Set by the MCP server alongside FALLOW_INTEGRATION_SURFACE=mcp."
|
|
6327
|
+
"FALLOW_MCP_TOOL": "Telemetry mcp_tool dimension, validated against the MCP tool-name allowlist. Set by the MCP server alongside FALLOW_INTEGRATION_SURFACE=mcp.",
|
|
6328
|
+
"FALLOW_LSP_REUSE_SESSION": "Language server: set to 0/false/off/no to load a new project session on each analysis run. By default the server keeps one session per project root between saves and parses only the changed files."
|
|
6206
6329
|
},
|
|
6207
6330
|
"severity_levels": [
|
|
6208
6331
|
"error",
|
|
@@ -7331,7 +7454,7 @@
|
|
|
7331
7454
|
]
|
|
7332
7455
|
},
|
|
7333
7456
|
"plugins": {
|
|
7334
|
-
"count":
|
|
7457
|
+
"count": 128,
|
|
7335
7458
|
"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",
|
|
7336
7459
|
"names": [
|
|
7337
7460
|
"nextjs",
|
|
@@ -7345,6 +7468,7 @@
|
|
|
7345
7468
|
"react-router",
|
|
7346
7469
|
"redwoodsdk",
|
|
7347
7470
|
"tanstack-router",
|
|
7471
|
+
"waku",
|
|
7348
7472
|
"react-native",
|
|
7349
7473
|
"expo",
|
|
7350
7474
|
"expo-router",
|
package/issue-registry.json
CHANGED
|
@@ -2402,6 +2402,36 @@
|
|
|
2402
2402
|
"license_note": null,
|
|
2403
2403
|
"docs_url": "https://docs.fallow.tools/cli/security"
|
|
2404
2404
|
},
|
|
2405
|
+
{
|
|
2406
|
+
"id": "flag-retirement-candidate",
|
|
2407
|
+
"rule_id": "fallow/flag-retirement-candidate",
|
|
2408
|
+
"command": "flags",
|
|
2409
|
+
"category": "Flags",
|
|
2410
|
+
"description": "Feature flag is a retirement candidate",
|
|
2411
|
+
"label": null,
|
|
2412
|
+
"config_key": null,
|
|
2413
|
+
"registry_index": null,
|
|
2414
|
+
"aliases": [],
|
|
2415
|
+
"lsp": false,
|
|
2416
|
+
"filter_flag": null,
|
|
2417
|
+
"result_key": null,
|
|
2418
|
+
"summary_label": null,
|
|
2419
|
+
"summary_docs_anchor": null,
|
|
2420
|
+
"sarif_rule_ids": null,
|
|
2421
|
+
"codeclimate_check_names": null,
|
|
2422
|
+
"ts_alias": null,
|
|
2423
|
+
"counts_in_total": false,
|
|
2424
|
+
"fixable": false,
|
|
2425
|
+
"suppressible": false,
|
|
2426
|
+
"suppress_comment": null,
|
|
2427
|
+
"default_severity": null,
|
|
2428
|
+
"opt_in": null,
|
|
2429
|
+
"frameworks": [],
|
|
2430
|
+
"note": null,
|
|
2431
|
+
"license": "free",
|
|
2432
|
+
"license_note": null,
|
|
2433
|
+
"docs_url": "https://docs.fallow.tools/cli/flags#retirement-report"
|
|
2434
|
+
},
|
|
2405
2435
|
{
|
|
2406
2436
|
"id": "hardcoded-secret",
|
|
2407
2437
|
"rule_id": "security/hardcoded-secret",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fallow",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.30.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.
|
|
92
|
-
"@fallow-cli/darwin-x64": "3.
|
|
93
|
-
"@fallow-cli/linux-x64-gnu": "3.
|
|
94
|
-
"@fallow-cli/linux-arm64-gnu": "3.
|
|
95
|
-
"@fallow-cli/linux-x64-musl": "3.
|
|
96
|
-
"@fallow-cli/linux-arm64-musl": "3.
|
|
97
|
-
"@fallow-cli/win32-arm64-msvc": "3.
|
|
98
|
-
"@fallow-cli/win32-x64-msvc": "3.
|
|
99
|
-
"fallow-type-aware": "3.
|
|
91
|
+
"@fallow-cli/darwin-arm64": "3.30.0",
|
|
92
|
+
"@fallow-cli/darwin-x64": "3.30.0",
|
|
93
|
+
"@fallow-cli/linux-x64-gnu": "3.30.0",
|
|
94
|
+
"@fallow-cli/linux-arm64-gnu": "3.30.0",
|
|
95
|
+
"@fallow-cli/linux-x64-musl": "3.30.0",
|
|
96
|
+
"@fallow-cli/linux-arm64-musl": "3.30.0",
|
|
97
|
+
"@fallow-cli/win32-arm64-msvc": "3.30.0",
|
|
98
|
+
"@fallow-cli/win32-x64-msvc": "3.30.0",
|
|
99
|
+
"fallow-type-aware": "3.30.0"
|
|
100
100
|
}
|
|
101
101
|
}
|
package/schema.json
CHANGED
|
@@ -265,7 +265,7 @@
|
|
|
265
265
|
}
|
|
266
266
|
},
|
|
267
267
|
"flags": {
|
|
268
|
-
"description": "Configures feature-flag detection: `sdkPatterns` (custom flag-evaluating call signatures, each `{ function, nameArg (zero-based arg index of the flag name, default 0), provider? }`, merged with built-ins for LaunchDarkly, Statsig, Unleash, GrowthBook, Split, PostHog, Vercel Flags, ConfigCat, Flagsmith, Optimizely, and Eppo), `envPrefixes` (env-var prefixes marking `process.env.*` accesses as flags, merged with built-ins), and `configObjectHeuristics` (default false; when true, property accesses on objects whose name contains `feature`/`flag`/`toggle` are reported as low-confidence flags). Set `sdkPatterns`/`envPrefixes` to teach fallow a proprietary flag SDK or naming convention, or enable `configObjectHeuristics` for projects that read flags off config objects (higher false-positive rate). Feature-flag findings surface only when the `feature-flags` rule is enabled (default `off`).",
|
|
268
|
+
"description": "Configures feature-flag detection: `sdkPatterns` (custom flag-evaluating call signatures, each `{ function, nameArg (zero-based arg index of the flag name, default 0), provider? }`, merged with built-ins for LaunchDarkly, Statsig, Unleash, GrowthBook, Split, PostHog, Vercel Flags, ConfigCat, Flagsmith, Optimizely, and Eppo), `envPrefixes` (env-var prefixes marking `process.env.*` and `import.meta.env.*` accesses as flags, merged with built-ins), and `configObjectHeuristics` (default false; when true, property accesses on objects whose name contains `feature`/`flag`/`toggle` are reported as low-confidence flags). Set `sdkPatterns`/`envPrefixes` to teach fallow a proprietary flag SDK or naming convention, or enable `configObjectHeuristics` for projects that read flags off config objects (higher false-positive rate). Feature-flag findings surface only when the `feature-flags` rule is enabled (default `off`).",
|
|
269
269
|
"$ref": "#/$defs/FlagsConfig",
|
|
270
270
|
"default": {
|
|
271
271
|
"configObjectHeuristics": false
|
|
@@ -1824,7 +1824,7 @@
|
|
|
1824
1824
|
}
|
|
1825
1825
|
},
|
|
1826
1826
|
"envPrefixes": {
|
|
1827
|
-
"description": "Environment variable prefixes that indicate feature flags.\nMerged with built-in prefixes. Only `process.env.*` accesses matching
|
|
1827
|
+
"description": "Environment variable prefixes that indicate feature flags.\nMerged with built-in prefixes. Only `process.env.*` and\n`import.meta.env.*` accesses matching these prefixes are reported as\nfeature flags.",
|
|
1828
1828
|
"type": "array",
|
|
1829
1829
|
"items": {
|
|
1830
1830
|
"type": "string"
|
|
@@ -1834,6 +1834,13 @@
|
|
|
1834
1834
|
"description": "Enable config object heuristic detection.\nWhen true, property accesses on objects whose name contains \"feature\",\n\"flag\", or \"toggle\" are reported as low-confidence feature flags.\nDefault: false (opt-in due to higher false positive rate).",
|
|
1835
1835
|
"type": "boolean",
|
|
1836
1836
|
"default": false
|
|
1837
|
+
},
|
|
1838
|
+
"vendorKeyPrefix": {
|
|
1839
|
+
"description": "Prefix to remove from each key of a `--flag-state` vendor export\nbefore the key is compared with the flag names in the code. For\nexample, `\"web.\"` makes the vendor key `web.new-checkout` match the\ncode flag `new-checkout`. A key without the prefix is compared as it\nis.",
|
|
1840
|
+
"type": [
|
|
1841
|
+
"string",
|
|
1842
|
+
"null"
|
|
1843
|
+
]
|
|
1837
1844
|
}
|
|
1838
1845
|
}
|
|
1839
1846
|
},
|
package/skills/fallow/SKILL.md
CHANGED
|
@@ -192,9 +192,10 @@ Reports unused exports in entry files (package.json `main`/`exports`, framework
|
|
|
192
192
|
```bash
|
|
193
193
|
fallow flags --format json --quiet
|
|
194
194
|
fallow flags --format json --quiet --top 20
|
|
195
|
+
fallow flags --retirement --format json --quiet
|
|
195
196
|
```
|
|
196
197
|
|
|
197
|
-
Reports environment-variable gates (`process.env.FEATURE_*`), SDK calls from common flag providers, and config-object patterns, with flag locations, detection confidence, and a cross-reference against dead code.
|
|
198
|
+
Reports environment-variable gates (`process.env.FEATURE_*`), SDK calls from common flag providers, and config-object patterns, with flag locations, detection confidence, and a cross-reference against dead code. `--top N` limits the list. `--retirement` adds a `retirement` object with one row per flag, the reasons it can be retired (`single-read-site`, `test-only`, `literal-constant`, `identical-branches`, `empty-branch`, `guards-dead-code`, `defined-never-read`), and its age from git (`--flag-age blame|pickaxe|off`; blame gives a lower bound). Filter with `--reason <CODE>` and `--min-age <DAYS>`, order with `--sort age|sites|name`. `--flag-state <FILE>` reads an offline vendor export in one vendor-neutral schema and adds `fully-rolled-out`, `archived-in-vendor`, `missing-in-vendor` and `vendor-only`. With `--retirement`, `--save-regression-baseline <PATH>` and `--fail-on-regression --regression-baseline <PATH>` gate on `distinct_flags` (plus each `--reason` count), and the opt-in `--max-flag-age <DAYS>` fails on old flags. Every format works: compact prints `flag-retire:<reason>:<path>:<line>:<name>`, SARIF adds the rule `fallow/flag-retirement-candidate`, CodeClimate adds `fallow/flag-retirement`. The report is advisory: every action has `auto_fixable: false`, and a person decides what to remove.
|
|
198
199
|
|
|
199
200
|
### Surface security candidates for verification
|
|
200
201
|
```bash
|
|
@@ -260,7 +261,7 @@ fallow dead-code --format json --quiet --save-baseline .fallow/snapshot.json
|
|
|
260
261
|
fallow dead-code --format json --quiet --baseline .fallow/snapshot.json
|
|
261
262
|
```
|
|
262
263
|
|
|
263
|
-
`--save-regression-baseline` / `--regression-baseline` / `--fail-on-regression` / `--tolerance` are count-based gates for `dead-code
|
|
264
|
+
`--save-regression-baseline` / `--regression-baseline` / `--fail-on-regression` / `--tolerance` are count-based gates for `dead-code`, bare combined mode, and `flags --retirement` (a flags baseline needs a PATH; without `--retirement` the options have no effect on `flags` and it warns). `--save-baseline` / `--baseline` are identity-based (track finding identity, fail on new). `audit` rejects the global baseline flags and uses `--dead-code-baseline` / `--health-baseline` / `--dupes-baseline` instead.
|
|
264
265
|
|
|
265
266
|
With no path, `--save-regression-baseline` updates `regression.baseline` in the discovered fallow config, or creates `.fallowrc.json` when none exists. Pass a path only when a standalone baseline file is preferred.
|
|
266
267
|
|
|
@@ -52,7 +52,7 @@ Every fallow command with its purpose and key flags. The table is regenerated fr
|
|
|
52
52
|
| `doctor` | Diagnose project readiness without analysis or mutation | |
|
|
53
53
|
| `similar-code` | Find semantically similar functions with a pinned local model (opt-in) | `--threshold`, `--min-lines`, `--top`, `--file` |
|
|
54
54
|
| `inspect` | Compose one evidence bundle for a file or exported symbol | `--file <path>`, `--symbol <file>:<export>` |
|
|
55
|
-
| `trace` | Trace a symbol's call chain (best-effort, syntactic; OFF the ranked path) | `symbol`, `--callers`, `--callees`, `--depth` |
|
|
55
|
+
| `trace` | Trace a symbol's call chain (best-effort, syntactic; OFF the ranked path) | `symbol`, `--callers`, `--callees`, `--depth`, `--path`, `--eager-only` |
|
|
56
56
|
| `trace-error` | Resolve a runtime stack trace's frames to the definitions they name (best-effort, syntactic; OFF the ranked path) | `trace_file` |
|
|
57
57
|
| `fix` | Auto-remove unused exports/deps | `--dry-run`, `--yes` (required in non-TTY) |
|
|
58
58
|
| `init` | Generate config file, AGENTS.md agent guide, or pre-commit hook | `--toml`, `--agents`, `--hooks`, `--branch` |
|
|
@@ -68,11 +68,11 @@ Every fallow command with its purpose and key flags. The table is regenerated fr
|
|
|
68
68
|
| `guard` | Show which architecture rules apply to files before changing them | `files` |
|
|
69
69
|
| `config` | Show the loaded config path and resolved config (verifies which `.fallowrc.json` is in effect) | `--path` |
|
|
70
70
|
| `recommend` | Recommend a project-tailored config for an agent to author | |
|
|
71
|
-
| `list` | Inspect project structure | `--files`, `--entry-points`, `--plugins`, `--boundaries`, `--workspaces` |
|
|
71
|
+
| `list` | Inspect project structure | `--files`, `--entry-points`, `--plugins`, `--boundaries`, `--workspaces`, `--entry-weight` |
|
|
72
72
|
| `workspaces` | Inspect monorepo workspaces + discovery diagnostics (shorthand for `list --workspaces`) | (no flags) |
|
|
73
73
|
| `dupes` | Code duplication detection | `--mode`, `--near`, `--threshold`, `--top`, `--changed-since`, `--workspace`, `--changed-workspaces`, `--skip-local`, `--cross-language`, `--ignore-imports`, `--explain-skipped`, `--fail-on-regression`, `--tolerance`, `--regression-baseline`, `--save-regression-baseline` |
|
|
74
74
|
| `health` | Function complexity analysis (also covers component templates as synthetic `<template>` findings: Angular external `.html` files via `templateUrl` AND inline `@Component({ template: \`...\` })` literals, plus Vue, Svelte and Astro single-file components; suppress an Angular external template with `<!-- fallow-ignore-file complexity -->` at the top of the `.html` file, an Angular inline template with `// fallow-ignore-next-line complexity` directly above the `@Component` decorator, and a `.svelte` / `.vue` / `.astro` template with `<!-- fallow-ignore-next-line complexity -->` on the line immediately above the reported line) | `--complexity`, `--max-cyclomatic`, `--max-cognitive`, `--max-crap`, `--top`, `--sort`, `--file-scores`, `--hotspots`, `--ownership`, `--ownership-emails`, `--targets`, `--effort`, `--score`, `--min-score`, `--since`, `--min-commits`, `--save-snapshot`, `--trend`, `--coverage-gaps`, `--coverage`, `--coverage-root`, `--runtime-coverage`, `--min-invocations-hot`, `--min-observation-volume`, `--low-traffic-threshold`, `--css`, `--complexity-breakdown`, `--min-severity`, `--report-only`, `--workspace`, `--changed-workspaces`, `--baseline`, `--save-baseline` |
|
|
75
|
-
| `flags` | Detect feature flag patterns (env vars, SDK calls, config objects) | `--top` |
|
|
75
|
+
| `flags` | Detect feature flag patterns (env vars, SDK calls, config objects) | `--top`, `--retirement`, `--reason`, `--min-age`, `--flag-state`, `--max-flag-age` |
|
|
76
76
|
| `suppressions` | List active fallow-ignore suppression markers (read-only inventory) | `--file` |
|
|
77
77
|
| `explain` | Explain one issue type without running analysis | `<issue-type>`, `--format json` |
|
|
78
78
|
| `audit` | Combined dead-code + complexity + duplication + styling for changed files, returns a verdict; `fallow review` is an alias for `fallow audit --brief` (advisory orientation brief, always exits 0) | `--base`, `--gate`, `--brief`, `--max-decisions`, `--walkthrough-guide`, `--walkthrough-file`, `--show-deprioritized`, `--production`, `--production-dead-code`, `--production-health`, `--production-dupes`, `--workspace`, `--changed-workspaces`, `--ci`, `--fail-on-issues`, `--explain`, `--explain-skipped`, `--dead-code-baseline`, `--health-baseline`, `--dupes-baseline`, `--max-crap`, `--coverage`, `--coverage-root`, `--no-css`, `--css-deep`, `--no-css-deep`, `--include-entry-exports` |
|
|
@@ -347,6 +347,7 @@ Inspect discovered files, entry points, detected frameworks, and architecture bo
|
|
|
347
347
|
| `--plugins` | `bool` | `false` | List active framework plugins |
|
|
348
348
|
| `--boundaries` | `bool` | `false` | Show architecture boundary zones, rules, per-zone file counts, and `logical_groups[]` for `autoDiscover` parents |
|
|
349
349
|
| `--workspaces` | `bool` | `false` | Show discovered monorepo workspaces plus any workspace-discovery diagnostics (malformed `package.json`, unreachable glob matches, missing tsconfig references). Available as the `fallow workspaces` alias too. |
|
|
350
|
+
| `--entry-weight` | `bool` | `false` | Show the startup import weight of each runtime entry point, in source bytes (not bundle size): eager, deferred and out-of-thread modules, eager packages, and the imports that keep the most bytes eager |
|
|
350
351
|
|
|
351
352
|
Common global flags for this command: [`--format`](#global-flags), [`--quiet`](#global-flags).
|
|
352
353
|
<!-- generated:flags:list:end -->
|
|
@@ -358,9 +359,14 @@ fallow list --entry-points --format json --quiet
|
|
|
358
359
|
fallow list --plugins --format json --quiet
|
|
359
360
|
fallow list --boundaries --format json --quiet
|
|
360
361
|
fallow list --workspaces --format json --quiet
|
|
362
|
+
fallow list --entry-weight --format json --quiet
|
|
361
363
|
fallow workspaces --format json --quiet # alias of `fallow list --workspaces`
|
|
362
364
|
```
|
|
363
365
|
|
|
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.
|
|
367
|
+
|
|
368
|
+
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
|
+
|
|
364
370
|
The `--workspaces` JSON output carries `workspaces[]` (name, project-root-relative path, `is_internal_dependency` bool) plus `workspace_diagnostics[]`. Each diagnostic has a `kind` discriminator (`undeclared-workspace`, `malformed-package-json`, `glob-matched-no-package-json`, `malformed-tsconfig`, `tsconfig-reference-dir-missing`, `malformed-pnpm-workspace-yaml`, `skipped-large-file`, `skipped-minified-file`, `skipped-source-dotdir`, `source-read-failure`, `bun-lockb-override-resolution-skipped`) with a typed payload (`error`, `pattern`, or none), and a `path` that is project-root-relative with forward slashes on every envelope that carries the array. The same `workspace_diagnostics[]` array is also surfaced on the `fallow dead-code --format json`, `fallow dupes --format json`, and `fallow health --format json` envelopes, at the top level of the bare combined `fallow --format json` envelope, on `fallow audit --format json` under `dead_code`, and on the `audit-brief` envelope shared by `fallow review --format json` and `fallow audit --brief --format json`, also under `dead_code` (omitted when empty). The combined carrier is the envelope root, not a section, so `--skip check`, `--only health`, and `--only dupes` all still report what their analyses recorded. The combined root is the union of what every analysis in the run recorded, deduplicated on the whole `kind` (typed payload included) plus `path`, so two overlapping globs still report the same package-less directory once per `pattern` (a declared glob's no-op `./` prefix is normalised away, so one glob written `"./apps/**"` in `package.json` and `apps/**` in `pnpm-workspace.yaml` stays one entry): a combined run walks the project once per analysis, and a per-analysis `production` mode (`production: { deadCode, health, dupes }`, `--production-health`) can give those walks different file sets, so only the union reports what the run as a whole saw. Each analysis contributes the workspace-discovery list its own config load produced, the same list `fallow list --workspaces` reports, so the combined root can carry an `undeclared-workspace` or `glob-matched-no-package-json` entry that the standalone `dead-code`, `check`, `health`, and `dupes` envelopes, which read the process diagnostics registry instead, do not. `fallow audit --format json` and the `audit-brief` envelope are on the same broad side: they fold the dead-code analysis's own list into their `dead_code.workspace_diagnostics[]`, so they too report an `undeclared-workspace` entry the standalone envelopes miss. The CLI and the programmatic route (MCP code mode, NAPI, embedders) agree on everything an analysis records: both folds close with the same process-registry read, which covers what an analysis records after its section captured its list (a `source-read-failure`, or the analysis-stage kinds a health run's own dead-code precompute records) and skips `skipped-large-file`, `skipped-minified-file`, and `skipped-source-dotdir`, since those reach an envelope only from the walk that recorded them. The two analysis-stage kinds (`malformed-pnpm-workspace-yaml`, `bun-lockb-override-resolution-skipped`) are recorded by the dead-code analyze pass, so they only appear on runs that include it: `fallow dupes --format json` and `fallow --only dupes` report the workspace-discovery and source-discovery kinds alone. A malformed ROOT `package.json` exits 2 at config load; everything else warns and continues.
|
|
365
371
|
|
|
366
372
|
The `--boundaries` JSON output carries `boundaries.logical_groups[]` alongside the existing `zones[]` / `rules[]` arrays. Each logical-group entry surfaces a user-authored `autoDiscover` parent zone (which expansion otherwise flattens into per-child zones like `features/auth` / `features/billing`): `name`, `children`, `auto_discover` (verbatim user strings), `status` (`ok` / `empty` / `invalid_path`), `source_zone_index`, summed `file_count`, optional `authored_rule` (the pre-expansion `{ allow, allowTypeOnly }` keyed on the parent), optional `fallback_zone` cross-reference when the parent also kept its own `patterns` (Bulletproof case), optional `merged_from` (parent zone indices when the user declared the same parent name twice; surfaces the duplicate in JSON instead of only in `tracing::warn!`), optional `original_zone_root` (echo of the parent's `root` subtree scope for monorepo patchers), and optional `child_source_indices` (parallel to `children`, attributing each child to a specific `auto_discover` entry when multiple paths were authored). The full shape is documented in `docs/output-schema.json` under `ListBoundariesOutput`.
|
|
@@ -447,7 +453,7 @@ Human output groups paths under "Shared with your team (commit these)" and "Loca
|
|
|
447
453
|
{
|
|
448
454
|
"kind": "agent-install",
|
|
449
455
|
"schema_version": 1,
|
|
450
|
-
"fallow_version": "3.
|
|
456
|
+
"fallow_version": "3.30.0",
|
|
451
457
|
"root": "/abs/path",
|
|
452
458
|
"mode": "install",
|
|
453
459
|
"dry_run": false,
|
|
@@ -651,7 +657,7 @@ fallow health --format json --quiet --trend
|
|
|
651
657
|
{
|
|
652
658
|
"kind": "health",
|
|
653
659
|
"schema_version": 7,
|
|
654
|
-
"version": "3.
|
|
660
|
+
"version": "3.30.0",
|
|
655
661
|
"elapsed_ms": 32,
|
|
656
662
|
"summary": {
|
|
657
663
|
"files_analyzed": 482,
|
|
@@ -1054,7 +1060,7 @@ fallow audit \
|
|
|
1054
1060
|
{
|
|
1055
1061
|
"kind": "audit",
|
|
1056
1062
|
"schema_version": 7,
|
|
1057
|
-
"version": "3.
|
|
1063
|
+
"version": "3.30.0",
|
|
1058
1064
|
"command": "audit",
|
|
1059
1065
|
"verdict": "fail",
|
|
1060
1066
|
"changed_files_count": 12,
|
|
@@ -1110,6 +1116,13 @@ Detects feature flag patterns in the codebase. Identifies environment variable f
|
|
|
1110
1116
|
| Flag | Type | Default | Description |
|
|
1111
1117
|
|---|---|---|---|
|
|
1112
1118
|
| `--top` | `string` | - | Show only the top N flags |
|
|
1119
|
+
| `--retirement` | `bool` | `false` | Add a retirement report: one row per flag, with the reasons the flag can be retired. Advisory only; nothing is removed |
|
|
1120
|
+
| `--reason` | `single-read-site\|test-only\|literal-constant\|identical-branches\|empty-branch\|guards-dead-code\|defined-never-read\|fully-rolled-out\|archived-in-vendor\|missing-in-vendor\|vendor-only` | - | Keep only retirement rows with this reason (repeatable) |
|
|
1121
|
+
| `--sort` | `age\|sites\|name` | `age` | Order of the retirement rows |
|
|
1122
|
+
| `--flag-age` | `blame\|pickaxe\|off` | `blame` | How to measure flag age: blame (lower bound), pickaxe (first commit with the name, slower) or off |
|
|
1123
|
+
| `--min-age` | `string` | - | Keep only retirement rows at least this many days old |
|
|
1124
|
+
| `--flag-state` | `string` | - | Vendor flag export (JSON, read offline) that adds the fully-rolled-out, archived-in-vendor, missing-in-vendor and vendor-only reasons |
|
|
1125
|
+
| `--max-flag-age` | `string` | - | Exit with code 1 when a flag in scope is older than this many days. Opt-in; needs a flag age |
|
|
1113
1126
|
|
|
1114
1127
|
Common global flags for this command: [`--format`](#global-flags), [`--quiet`](#global-flags), [`--changed-since`](#global-flags), [`--workspace`](#global-flags).
|
|
1115
1128
|
<!-- generated:flags:flags:end -->
|
|
@@ -1122,6 +1135,10 @@ fallow flags --format json --quiet
|
|
|
1122
1135
|
# Top 10 flags
|
|
1123
1136
|
fallow flags --format json --quiet --top 10
|
|
1124
1137
|
|
|
1138
|
+
# Flags at least 90 days old, oldest first. A row with an empty
|
|
1139
|
+
# `reasons` array is not a retirement candidate.
|
|
1140
|
+
fallow flags --retirement --min-age 90 --format json --quiet
|
|
1141
|
+
|
|
1125
1142
|
# Single workspace package
|
|
1126
1143
|
fallow flags --format json --quiet --workspace my-package
|
|
1127
1144
|
```
|
|
@@ -1131,7 +1148,7 @@ fallow flags --format json --quiet --workspace my-package
|
|
|
1131
1148
|
```json
|
|
1132
1149
|
{
|
|
1133
1150
|
"schema_version": 7,
|
|
1134
|
-
"version": "3.
|
|
1151
|
+
"version": "3.30.0",
|
|
1135
1152
|
"elapsed_ms": 116,
|
|
1136
1153
|
"feature_flags": [],
|
|
1137
1154
|
"total_flags": 0
|
|
@@ -1232,7 +1249,7 @@ fallow security --gate newly-reachable --changed-since origin/main
|
|
|
1232
1249
|
{
|
|
1233
1250
|
"kind": "security",
|
|
1234
1251
|
"schema_version": "4",
|
|
1235
|
-
"version": "3.
|
|
1252
|
+
"version": "3.30.0",
|
|
1236
1253
|
"elapsed_ms": 42,
|
|
1237
1254
|
"config": {
|
|
1238
1255
|
"rules": {
|
|
@@ -1261,7 +1278,7 @@ fallow security --gate newly-reachable --changed-since origin/main
|
|
|
1261
1278
|
{
|
|
1262
1279
|
"kind": "security",
|
|
1263
1280
|
"schema_version": "4",
|
|
1264
|
-
"version": "3.
|
|
1281
|
+
"version": "3.30.0",
|
|
1265
1282
|
"elapsed_ms": 42,
|
|
1266
1283
|
"config": {
|
|
1267
1284
|
"rules": {
|
|
@@ -1386,12 +1403,17 @@ The target is a positional argument, formatted as `FILE:SYMBOL` (for example `sr
|
|
|
1386
1403
|
```bash
|
|
1387
1404
|
fallow trace src/utils.ts:formatDate
|
|
1388
1405
|
fallow trace src/utils.ts:formatDate --callers --depth 3
|
|
1406
|
+
fallow trace --path src/main.ts src/chart.ts --format json --quiet
|
|
1407
|
+
fallow trace --path src/main.ts src/chart.ts --eager-only --format json --quiet
|
|
1389
1408
|
```
|
|
1390
1409
|
|
|
1410
|
+
Each `--path` hop carries `type_only` and `dynamic`. A `dynamic` hop loads its target only on demand (`import()`, a lazy glob) or on another thread (a worker, a fork). `--eager-only` follows static value imports only, so its route explains why a module is in the `--entry-weight` eager set of `fallow list`.
|
|
1411
|
+
|
|
1391
1412
|
<!-- generated:flags:trace:start -->
|
|
1392
1413
|
| Flag | Type | Default | Description |
|
|
1393
1414
|
|---|---|---|---|
|
|
1394
1415
|
| `--path` | `string` | - | Shortest import path between two modules, as two file paths (e.g. `--path src/app.ts src/db.ts`). Mutually exclusive with the symbol target and the call-chain flags |
|
|
1416
|
+
| `--eager-only` | `bool` | `false` | With `--path`, follow only static value imports, so the route explains why TO loads before FROM runs. `import()`, lazy globs, worker loads and `import type` do not qualify |
|
|
1395
1417
|
| `--callers` | `bool` | `false` | Walk UP to callers (modules that import the symbol). When neither `--callers` nor `--callees` is set, both directions are walked |
|
|
1396
1418
|
| `--callees` | `bool` | `false` | Walk DOWN to callees (the symbol's module's import-symbol edges plus unresolved call sites). When neither flag is set, both are walked |
|
|
1397
1419
|
| `--depth` | `string` | - | Chain depth bound for both directions (default 2). Symbol-level is best-effort, so a shallow bound keeps the trace legible |
|
|
@@ -2034,7 +2056,7 @@ The HTTP layer mirrors the bash `gh_api_retry` / `curl_retry` helpers: `FALLOW_A
|
|
|
2034
2056
|
{
|
|
2035
2057
|
"kind": "dead-code",
|
|
2036
2058
|
"schema_version": 7,
|
|
2037
|
-
"version": "3.
|
|
2059
|
+
"version": "3.30.0",
|
|
2038
2060
|
"elapsed_ms": 45,
|
|
2039
2061
|
"total_issues": 12,
|
|
2040
2062
|
"entry_points": {
|
|
@@ -2194,7 +2216,7 @@ When `--baseline` is used in combined output, the JSON includes a `baseline_delt
|
|
|
2194
2216
|
{
|
|
2195
2217
|
"kind": "dupes",
|
|
2196
2218
|
"schema_version": 7,
|
|
2197
|
-
"version": "3.
|
|
2219
|
+
"version": "3.30.0",
|
|
2198
2220
|
"elapsed_ms": 82,
|
|
2199
2221
|
"total_clones": 15,
|
|
2200
2222
|
"total_lines_duplicated": 230,
|
|
@@ -2238,11 +2260,11 @@ When running `fallow` with no subcommand (all analyses), the JSON output combine
|
|
|
2238
2260
|
{
|
|
2239
2261
|
"kind": "combined",
|
|
2240
2262
|
"schema_version": 7,
|
|
2241
|
-
"version": "3.
|
|
2263
|
+
"version": "3.30.0",
|
|
2242
2264
|
"elapsed_ms": 159,
|
|
2243
2265
|
"check": {
|
|
2244
2266
|
"schema_version": 7,
|
|
2245
|
-
"version": "3.
|
|
2267
|
+
"version": "3.30.0",
|
|
2246
2268
|
"elapsed_ms": 45,
|
|
2247
2269
|
"total_issues": 12,
|
|
2248
2270
|
"unused_files": [],
|
|
@@ -72,6 +72,7 @@ The `generated:issue-types` table below is regenerated from `fallow schema` by s
|
|
|
72
72
|
| `untested-export` | `--coverage-gaps` | - | `// fallow-ignore-file coverage-gaps` | Runtime-reachable export has no test dependency path |
|
|
73
73
|
| `code-duplication` | - | - | `// fallow-ignore-next-line code-duplication` | Duplicated code block; Reported by fallow dupes (and bare fallow / fallow audit) |
|
|
74
74
|
| `feature-flag` | - | - | `// fallow-ignore-next-line feature-flag` | Detected feature flag pattern; Reported by fallow flags |
|
|
75
|
+
| `flag-retirement-candidate` | - | - | - | Feature flag is a retirement candidate |
|
|
75
76
|
| `tainted-sink` | - | - | `// fallow-ignore-next-line security-sink` | Syntactic security sink candidates require verification |
|
|
76
77
|
| `client-server-leak` | - | - | `// fallow-ignore-file security-client-server-leak` | Client-bound code reaches a non-public env read |
|
|
77
78
|
| `hardcoded-secret` | - | - | `// fallow-ignore-next-line security-sink` | Provider-prefixed or contextual secret literals require verification; Include-required category: enable via security.categories.include |
|
|
@@ -36,7 +36,7 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
|
|
|
36
36
|
| `project_info` | introspection | free | `fallow list --files --entry-points --plugins --format json --quiet` | `entry_points`, `files`, `plugins`, `boundaries` | Project metadata. Set `entry_points`, `files`, `plugins`, or `boundaries` to `true` to request specific sections |
|
|
37
37
|
| `recommend` | introspection | free | `fallow recommend --format json --quiet` | `root` | Recommend a project-tailored config from framework/workspace/tooling detection: a loader-validated proposed_config and three-valued auto/default/taste decisions for cold-start onboarding |
|
|
38
38
|
| `list_boundaries` | introspection | free | `fallow list --boundaries --format json --quiet` | - | Architecture boundary zones, access rules, and pre-expansion `autoDiscover` `logical_groups[]` (user-authored parent name, verbatim paths, discovered children, `status` enum, summed `file_count`). Returns `{"configured": false}` if no boundaries configured |
|
|
39
|
-
| `feature_flags` | analysis | free | `fallow flags --format json --quiet` | `workspace`, `production` | Detect feature flag patterns (env vars, SDK calls, config objects). Set `top` to limit results |
|
|
39
|
+
| `feature_flags` | analysis | free | `fallow flags --format json --quiet` | `workspace`, `production` | Detect feature flag patterns (env vars, SDK calls, config objects). Set `top` to limit results. `retirement: true` adds the retirement rows; `flag_state` and `flag_age` tune them |
|
|
40
40
|
| `list_suppressions` | analysis | free | `fallow suppressions --format json --quiet` | `workspace`, `changed_since`, `file` | List active fallow-ignore suppression markers grouped per file (line, kind, level, reason, and a stale cross-reference); a read-only governance inventory that always exits 0 |
|
|
41
41
|
| `impact` | introspection | free | `fallow impact --format json --quiet` | `root` | Read the local, opt-in Fallow Impact value report (`fallow impact --format json`). Runs no analysis: current surfacing counts, trend since the last recorded run, pre-commit gate containment, and (on impact v1.5+) resolved/suppressed attribution. History is read from a per-project file in the user's private config dir (never inside the repo). Read-only and `root`-only; the mutating `enable` / `disable` / `default` lifecycle is not exposed. A never-enabled project returns a populated `{"enabled": false, ...}` report (never `{}`); branch on `enabled` and `enabled_source` (`project` / `user` / `default`) then `record_count`, recommending `fallow impact enable` only when `explicit_decision` is `false` (never asked) and staying silent when `true` (deliberately disabled here). Local-developer signal: fallow never records in CI, so empty there and not a CI metric |
|
|
42
42
|
| `impact_all` | introspection | free | `fallow impact --all --format json --quiet` | `sort`, `limit` | Roll every tracked fallow project on this machine into one cross-repo value report (hashed keys plus basename labels, never paths; local-dev only) |
|
|
@@ -731,6 +731,15 @@ reason: string
|
|
|
731
731
|
kind: "plugin-effect-not-modeled"
|
|
732
732
|
} | {
|
|
733
733
|
kind: "coverage-auto-detected"
|
|
734
|
+
} | {
|
|
735
|
+
kind: "flag-age-shallow-clone"
|
|
736
|
+
} | {
|
|
737
|
+
/**
|
|
738
|
+
* Why no history is available, as a kebab-case token:
|
|
739
|
+
* `not-a-repository` or `no-commits`. The set is open.
|
|
740
|
+
*/
|
|
741
|
+
cause: string
|
|
742
|
+
kind: "flag-age-unavailable"
|
|
734
743
|
})
|
|
735
744
|
/**
|
|
736
745
|
* Discriminant for [`CloneGroupAction::kind`]. Mirrors the action types
|
|
@@ -905,6 +914,11 @@ export type RuntimeCoverageVerdict = ("safe_to_delete" | "review_required" | "co
|
|
|
905
914
|
* Confidence level for a runtime coverage finding.
|
|
906
915
|
*/
|
|
907
916
|
export type RuntimeCoverageConfidence = ("very_high" | "high" | "medium" | "low" | "none" | "unknown")
|
|
917
|
+
/**
|
|
918
|
+
* The per-call cost that `optimization_target.cost_score` multiplies with
|
|
919
|
+
* `invocations`.
|
|
920
|
+
*/
|
|
921
|
+
export type RuntimeCoverageCostBasis = ("inner_iterations" | "cognitive")
|
|
908
922
|
/**
|
|
909
923
|
* Blast-radius risk band. The current thresholds are high at >=20 static
|
|
910
924
|
* callers or >=1,000,000 traffic-weighted caller reach; medium at >=5 callers
|
|
@@ -1301,6 +1315,30 @@ export type FeatureFlagConfidence = ("high" | "medium" | "low")
|
|
|
1301
1315
|
* Feature flag action discriminants.
|
|
1302
1316
|
*/
|
|
1303
1317
|
export type FeatureFlagActionType = ("investigate-flag" | "suppress-line")
|
|
1318
|
+
/**
|
|
1319
|
+
* How the report measures the age of a flag.
|
|
1320
|
+
*/
|
|
1321
|
+
export type FlagAgeMode = ("blame" | "pickaxe" | "off")
|
|
1322
|
+
/**
|
|
1323
|
+
* How a retirement row's flag was detected.
|
|
1324
|
+
*/
|
|
1325
|
+
export type RetirementFlagKind = ("environment_variable" | "sdk_call" | "config_object" | "constant" | "vendor_export")
|
|
1326
|
+
/**
|
|
1327
|
+
* What a site does with the flag.
|
|
1328
|
+
*/
|
|
1329
|
+
export type FlagSiteRole = ("read" | "definition")
|
|
1330
|
+
/**
|
|
1331
|
+
* Why a flag is a retirement candidate.
|
|
1332
|
+
*/
|
|
1333
|
+
export type RetirementReason = ("single-read-site" | "test-only" | "literal-constant" | "identical-branches" | "empty-branch" | "guards-dead-code" | "defined-never-read" | "fully-rolled-out" | "archived-in-vendor" | "missing-in-vendor" | "vendor-only")
|
|
1334
|
+
/**
|
|
1335
|
+
* Action discriminants for a retirement row.
|
|
1336
|
+
*/
|
|
1337
|
+
export type RetirementActionType = "review-retirement"
|
|
1338
|
+
/**
|
|
1339
|
+
* State of a flag in a `--flag-state` vendor export.
|
|
1340
|
+
*/
|
|
1341
|
+
export type VendorFlagState = ("on" | "off" | "rolled_out" | "archived" | "experiment")
|
|
1304
1342
|
/**
|
|
1305
1343
|
* Independently-versioned wire-version newtype for the brief envelope.
|
|
1306
1344
|
* Serializes as the integer `REVIEW_BRIEF_SCHEMA_VERSION`.
|
|
@@ -8362,6 +8400,48 @@ percentile: number
|
|
|
8362
8400
|
* when empty.
|
|
8363
8401
|
*/
|
|
8364
8402
|
actions?: RuntimeCoverageAction[]
|
|
8403
|
+
/**
|
|
8404
|
+
* Per-call cost inputs and the speed-work score for this hot function.
|
|
8405
|
+
* Omitted when the hot path has no `stable_id` or no static counterpart
|
|
8406
|
+
* in this checkout.
|
|
8407
|
+
*/
|
|
8408
|
+
optimization_target?: (RuntimeCoverageOptimizationTarget | null)
|
|
8409
|
+
}
|
|
8410
|
+
/**
|
|
8411
|
+
* Speed-work inputs for one hot function: how often it runs and how much
|
|
8412
|
+
* work each call does. `importance` ranks the risk of a change; this block
|
|
8413
|
+
* ranks where speed work gives the largest gain.
|
|
8414
|
+
*/
|
|
8415
|
+
export interface RuntimeCoverageOptimizationTarget {
|
|
8416
|
+
/**
|
|
8417
|
+
* `invocations` multiplied by the per-call cost that `cost_basis` names.
|
|
8418
|
+
* Uncapped integer. Compare it only between hot paths with the same
|
|
8419
|
+
* `cost_basis`: sort by `cost_basis` first, then by `cost_score`
|
|
8420
|
+
* descending. On the `cognitive` basis the per-call cost is at least 1.
|
|
8421
|
+
*/
|
|
8422
|
+
cost_score: number
|
|
8423
|
+
cost_basis: RuntimeCoverageCostBasis
|
|
8424
|
+
/**
|
|
8425
|
+
* Static cognitive complexity of the function. A static proxy for the
|
|
8426
|
+
* work per call, not a measurement.
|
|
8427
|
+
*/
|
|
8428
|
+
cognitive: number
|
|
8429
|
+
/**
|
|
8430
|
+
* Static cyclomatic complexity of the function.
|
|
8431
|
+
*/
|
|
8432
|
+
cyclomatic: number
|
|
8433
|
+
/**
|
|
8434
|
+
* Number of lines in the function body.
|
|
8435
|
+
*/
|
|
8436
|
+
line_count: number
|
|
8437
|
+
/**
|
|
8438
|
+
* Peak executions of one block inside the function per call, from V8
|
|
8439
|
+
* block coverage. `1.0` means no block ran more than once per call. A
|
|
8440
|
+
* loop body that runs 3 times per call gives `3.0`. Calls to other
|
|
8441
|
+
* functions do not change the value.
|
|
8442
|
+
* Omitted when the coverage input has no block counts for the function.
|
|
8443
|
+
*/
|
|
8444
|
+
inner_iterations_per_call?: (number | null)
|
|
8365
8445
|
}
|
|
8366
8446
|
/**
|
|
8367
8447
|
* One blast-radius entry in `runtime_coverage.blast_radius`: how far a
|
|
@@ -10925,6 +11005,13 @@ to: string
|
|
|
10925
11005
|
* chain is a real compile-time coupling.
|
|
10926
11006
|
*/
|
|
10927
11007
|
type_only: boolean
|
|
11008
|
+
/**
|
|
11009
|
+
* Whether the edge carries a runtime value but no static one: the target
|
|
11010
|
+
* loads only on demand (`import()`, a lazy glob or template pattern) or
|
|
11011
|
+
* on another thread (a worker URL, `child_process.fork`). False for a
|
|
11012
|
+
* static hop and for a type-only hop.
|
|
11013
|
+
*/
|
|
11014
|
+
dynamic: boolean
|
|
10928
11015
|
/**
|
|
10929
11016
|
* 1-based line in `from` of the imported binding that creates this edge:
|
|
10930
11017
|
* the first value-carrying symbol on the import, or the first symbol when
|
|
@@ -14063,6 +14150,15 @@ total_flags: number
|
|
|
14063
14150
|
* change.
|
|
14064
14151
|
*/
|
|
14065
14152
|
workspace_diagnostics?: WorkspaceDiagnostic[]
|
|
14153
|
+
/**
|
|
14154
|
+
* One row per flag with the reasons the flag can be retired.
|
|
14155
|
+
*
|
|
14156
|
+
* Present only with `--retirement`. Without that option the key is
|
|
14157
|
+
* omitted, so the envelope stays byte-identical and `schema_version`
|
|
14158
|
+
* does not move. The per-site `feature_flags[]` array is the same with
|
|
14159
|
+
* and without the option.
|
|
14160
|
+
*/
|
|
14161
|
+
retirement?: (FlagRetirementReport | null)
|
|
14066
14162
|
/**
|
|
14067
14163
|
* `_meta` block; see [`FeatureFlagsMeta`].
|
|
14068
14164
|
*/
|
|
@@ -14138,6 +14234,347 @@ dead_export_count: number
|
|
|
14138
14234
|
*/
|
|
14139
14235
|
dead_exports: string[]
|
|
14140
14236
|
}
|
|
14237
|
+
/**
|
|
14238
|
+
* The `retirement` block of `fallow flags --retirement --format json`.
|
|
14239
|
+
*/
|
|
14240
|
+
export interface FlagRetirementReport {
|
|
14241
|
+
/**
|
|
14242
|
+
* The analysis clock that ages count from, as an RFC 3339 UTC
|
|
14243
|
+
* timestamp. `null` when the age mode is `off`, and also when no git
|
|
14244
|
+
* history is available: outside a repository, on a branch without
|
|
14245
|
+
* commits, or in a shallow clone. A `workspace_diagnostics` entry then
|
|
14246
|
+
* gives the reason.
|
|
14247
|
+
*/
|
|
14248
|
+
generated_at_clock?: (string | null)
|
|
14249
|
+
age_mode: FlagAgeMode
|
|
14250
|
+
/**
|
|
14251
|
+
* The vendor export that the report read. Present only with
|
|
14252
|
+
* `--flag-state`.
|
|
14253
|
+
*/
|
|
14254
|
+
vendor_state?: (RetirementVendorState | null)
|
|
14255
|
+
summary: RetirementSummary
|
|
14256
|
+
/**
|
|
14257
|
+
* Verdict of `--fail-on-regression` against a flags regression
|
|
14258
|
+
* baseline. Present only when the gate ran.
|
|
14259
|
+
*/
|
|
14260
|
+
regression?: (FlagRegressionResult | null)
|
|
14261
|
+
/**
|
|
14262
|
+
* Verdict of `--max-flag-age`. Present only with that option.
|
|
14263
|
+
*/
|
|
14264
|
+
max_flag_age?: (FlagAgeGate | null)
|
|
14265
|
+
/**
|
|
14266
|
+
* One row per flag after the `--min-age`, `--reason`, `--sort` and
|
|
14267
|
+
* `--top` options. A row with an empty `reasons` array is not a
|
|
14268
|
+
* candidate.
|
|
14269
|
+
*/
|
|
14270
|
+
flags: RetirementFlag[]
|
|
14271
|
+
}
|
|
14272
|
+
/**
|
|
14273
|
+
* The `--flag-state` vendor export that the report read.
|
|
14274
|
+
*/
|
|
14275
|
+
export interface RetirementVendorState {
|
|
14276
|
+
/**
|
|
14277
|
+
* The vendor name from the export, for example `launchdarkly`.
|
|
14278
|
+
*/
|
|
14279
|
+
source: string
|
|
14280
|
+
/**
|
|
14281
|
+
* When the export was made, as the export gives it.
|
|
14282
|
+
*/
|
|
14283
|
+
exported_at: string
|
|
14284
|
+
/**
|
|
14285
|
+
* Days between `exported_at` and the analysis clock. `null` when the
|
|
14286
|
+
* date cannot be read.
|
|
14287
|
+
*/
|
|
14288
|
+
export_age_days?: (number | null)
|
|
14289
|
+
/**
|
|
14290
|
+
* Number of flags in the export.
|
|
14291
|
+
*/
|
|
14292
|
+
flags: number
|
|
14293
|
+
}
|
|
14294
|
+
/**
|
|
14295
|
+
* Totals of the retirement report.
|
|
14296
|
+
*/
|
|
14297
|
+
export interface RetirementSummary {
|
|
14298
|
+
/**
|
|
14299
|
+
* Distinct flags in the code in scope, before `--min-age` and
|
|
14300
|
+
* `--reason`. The `vendor-only` rows of a `--flag-state` export do not
|
|
14301
|
+
* count here, so the count does not change when a key is added in the
|
|
14302
|
+
* vendor only. `by_reason` counts them.
|
|
14303
|
+
*/
|
|
14304
|
+
distinct_flags: number
|
|
14305
|
+
/**
|
|
14306
|
+
* Rows in scope with at least one reason, `vendor-only` rows included.
|
|
14307
|
+
*/
|
|
14308
|
+
candidates: number
|
|
14309
|
+
/**
|
|
14310
|
+
* Number of rows in scope per reason.
|
|
14311
|
+
*/
|
|
14312
|
+
by_reason: {
|
|
14313
|
+
[k: string]: number
|
|
14314
|
+
}
|
|
14315
|
+
}
|
|
14316
|
+
/**
|
|
14317
|
+
* Verdict of the flags regression gate.
|
|
14318
|
+
*/
|
|
14319
|
+
export interface FlagRegressionResult {
|
|
14320
|
+
status: RegressionStatus
|
|
14321
|
+
/**
|
|
14322
|
+
* The `--tolerance` value. Absent when the status is `skipped`.
|
|
14323
|
+
*/
|
|
14324
|
+
tolerance?: (number | null)
|
|
14325
|
+
/**
|
|
14326
|
+
* How to read `tolerance`. Absent when the status is `skipped`.
|
|
14327
|
+
*/
|
|
14328
|
+
tolerance_kind?: (RegressionToleranceKind | null)
|
|
14329
|
+
/**
|
|
14330
|
+
* The compared counts: `distinct_flags` first, then each `--reason`
|
|
14331
|
+
* code. Empty when the status is `skipped`.
|
|
14332
|
+
*/
|
|
14333
|
+
metrics: FlagRegressionMetric[]
|
|
14334
|
+
/**
|
|
14335
|
+
* Whether one count grew more than the tolerance.
|
|
14336
|
+
*/
|
|
14337
|
+
exceeded: boolean
|
|
14338
|
+
/**
|
|
14339
|
+
* Why the gate did not run. Present only when the status is `skipped`.
|
|
14340
|
+
*/
|
|
14341
|
+
reason?: (string | null)
|
|
14342
|
+
}
|
|
14343
|
+
/**
|
|
14344
|
+
* One count that the flags regression gate compares.
|
|
14345
|
+
*/
|
|
14346
|
+
export interface FlagRegressionMetric {
|
|
14347
|
+
/**
|
|
14348
|
+
* `distinct_flags`, or a reason code from `--reason`.
|
|
14349
|
+
*/
|
|
14350
|
+
metric: string
|
|
14351
|
+
/**
|
|
14352
|
+
* The count in the baseline.
|
|
14353
|
+
*/
|
|
14354
|
+
baseline: number
|
|
14355
|
+
/**
|
|
14356
|
+
* The count in this run.
|
|
14357
|
+
*/
|
|
14358
|
+
current: number
|
|
14359
|
+
/**
|
|
14360
|
+
* `current - baseline`.
|
|
14361
|
+
*/
|
|
14362
|
+
delta: number
|
|
14363
|
+
/**
|
|
14364
|
+
* Whether the growth is more than the tolerance.
|
|
14365
|
+
*/
|
|
14366
|
+
exceeded: boolean
|
|
14367
|
+
}
|
|
14368
|
+
/**
|
|
14369
|
+
* Verdict of `--max-flag-age`.
|
|
14370
|
+
*/
|
|
14371
|
+
export interface FlagAgeGate {
|
|
14372
|
+
status: RegressionStatus
|
|
14373
|
+
/**
|
|
14374
|
+
* The `--max-flag-age` value in days.
|
|
14375
|
+
*/
|
|
14376
|
+
max_days: number
|
|
14377
|
+
/**
|
|
14378
|
+
* Whether one flag in scope is older than `max_days`.
|
|
14379
|
+
*/
|
|
14380
|
+
exceeded: boolean
|
|
14381
|
+
/**
|
|
14382
|
+
* Flags in the code in scope without a measured age. The gate cannot
|
|
14383
|
+
* check these flags.
|
|
14384
|
+
*/
|
|
14385
|
+
unmeasured: number
|
|
14386
|
+
/**
|
|
14387
|
+
* Why the gate did not run. Present only when the status is `skipped`.
|
|
14388
|
+
*/
|
|
14389
|
+
reason?: (string | null)
|
|
14390
|
+
/**
|
|
14391
|
+
* The flags in scope that are older than `max_days`, oldest first.
|
|
14392
|
+
* The `--reason`, `--min-age` and `--top` options do not change this
|
|
14393
|
+
* list.
|
|
14394
|
+
*/
|
|
14395
|
+
flags: FlagAgeGateEntry[]
|
|
14396
|
+
}
|
|
14397
|
+
/**
|
|
14398
|
+
* A flag that is older than `--max-flag-age`.
|
|
14399
|
+
*/
|
|
14400
|
+
export interface FlagAgeGateEntry {
|
|
14401
|
+
/**
|
|
14402
|
+
* Flag identifier.
|
|
14403
|
+
*/
|
|
14404
|
+
flag_name: string
|
|
14405
|
+
kind: RetirementFlagKind
|
|
14406
|
+
/**
|
|
14407
|
+
* Flag SDK, for SDK flags with a known provider.
|
|
14408
|
+
*/
|
|
14409
|
+
sdk_name?: (string | null)
|
|
14410
|
+
/**
|
|
14411
|
+
* Workspace root of the flag, in a project with workspaces.
|
|
14412
|
+
*/
|
|
14413
|
+
workspace?: (string | null)
|
|
14414
|
+
/**
|
|
14415
|
+
* Age of the flag in days.
|
|
14416
|
+
*/
|
|
14417
|
+
age_days: number
|
|
14418
|
+
}
|
|
14419
|
+
/**
|
|
14420
|
+
* One flag in the retirement report.
|
|
14421
|
+
*/
|
|
14422
|
+
export interface RetirementFlag {
|
|
14423
|
+
/**
|
|
14424
|
+
* Flag identifier.
|
|
14425
|
+
*/
|
|
14426
|
+
flag_name: string
|
|
14427
|
+
kind: RetirementFlagKind
|
|
14428
|
+
/**
|
|
14429
|
+
* Flag SDK, for SDK flags with a known provider.
|
|
14430
|
+
*/
|
|
14431
|
+
sdk_name?: (string | null)
|
|
14432
|
+
/**
|
|
14433
|
+
* Workspace root relative to the analysed root, when the project has
|
|
14434
|
+
* workspaces and the flag is inside one. Part of the flag identity.
|
|
14435
|
+
*/
|
|
14436
|
+
workspace?: (string | null)
|
|
14437
|
+
/**
|
|
14438
|
+
* Every site of the flag, sorted by path, line and column.
|
|
14439
|
+
*/
|
|
14440
|
+
sites: RetirementSite[]
|
|
14441
|
+
/**
|
|
14442
|
+
* Number of sites in this row that read the flag.
|
|
14443
|
+
*/
|
|
14444
|
+
read_sites: number
|
|
14445
|
+
/**
|
|
14446
|
+
* Whether every read site is in a test, story or mock file. Read sites
|
|
14447
|
+
* of the same flag in other workspaces count too.
|
|
14448
|
+
*/
|
|
14449
|
+
test_only: boolean
|
|
14450
|
+
/**
|
|
14451
|
+
* First commit that added the flag name. Set in `pickaxe` mode only.
|
|
14452
|
+
*/
|
|
14453
|
+
first_seen?: (FlagCommit | null)
|
|
14454
|
+
/**
|
|
14455
|
+
* Oldest commit among the lines that still hold the flag.
|
|
14456
|
+
*/
|
|
14457
|
+
oldest_surviving_site?: (FlagCommit | null)
|
|
14458
|
+
/**
|
|
14459
|
+
* Newest commit among the lines that still hold the flag.
|
|
14460
|
+
*/
|
|
14461
|
+
last_touched?: (FlagCommit | null)
|
|
14462
|
+
/**
|
|
14463
|
+
* Days between the flag's oldest known commit and the analysis clock.
|
|
14464
|
+
* In `blame` mode this is a lower bound.
|
|
14465
|
+
*/
|
|
14466
|
+
age_days?: (number | null)
|
|
14467
|
+
/**
|
|
14468
|
+
* Retirement reasons, in report order. Empty for a flag that is not a
|
|
14469
|
+
* candidate.
|
|
14470
|
+
*/
|
|
14471
|
+
reasons: RetirementReason[]
|
|
14472
|
+
/**
|
|
14473
|
+
* Evidence for each reason.
|
|
14474
|
+
*/
|
|
14475
|
+
evidence: RetirementEvidence[]
|
|
14476
|
+
/**
|
|
14477
|
+
* Follow-up actions. Empty for a flag that is not a candidate.
|
|
14478
|
+
*/
|
|
14479
|
+
actions: RetirementAction[]
|
|
14480
|
+
/**
|
|
14481
|
+
* The vendor state of the flag. Present only with `--flag-state`, for
|
|
14482
|
+
* a flag whose key is in the export.
|
|
14483
|
+
*/
|
|
14484
|
+
vendor?: (RetirementVendor | null)
|
|
14485
|
+
}
|
|
14486
|
+
/**
|
|
14487
|
+
* One site of a flag in the retirement report.
|
|
14488
|
+
*/
|
|
14489
|
+
export interface RetirementSite {
|
|
14490
|
+
/**
|
|
14491
|
+
* File path relative to the analysed root.
|
|
14492
|
+
*/
|
|
14493
|
+
path: string
|
|
14494
|
+
/**
|
|
14495
|
+
* 1-based line.
|
|
14496
|
+
*/
|
|
14497
|
+
line: number
|
|
14498
|
+
/**
|
|
14499
|
+
* 0-based byte column.
|
|
14500
|
+
*/
|
|
14501
|
+
col: number
|
|
14502
|
+
role: FlagSiteRole
|
|
14503
|
+
/**
|
|
14504
|
+
* Whether the file is a test, story or mock file.
|
|
14505
|
+
*/
|
|
14506
|
+
in_test: boolean
|
|
14507
|
+
}
|
|
14508
|
+
/**
|
|
14509
|
+
* A commit that git history links to a flag.
|
|
14510
|
+
*/
|
|
14511
|
+
export interface FlagCommit {
|
|
14512
|
+
/**
|
|
14513
|
+
* Abbreviated commit hash.
|
|
14514
|
+
*/
|
|
14515
|
+
commit: string
|
|
14516
|
+
/**
|
|
14517
|
+
* Commit date in UTC, as `YYYY-MM-DD`.
|
|
14518
|
+
*/
|
|
14519
|
+
date: string
|
|
14520
|
+
}
|
|
14521
|
+
/**
|
|
14522
|
+
* One piece of evidence for a retirement reason.
|
|
14523
|
+
*/
|
|
14524
|
+
export interface RetirementEvidence {
|
|
14525
|
+
reason: RetirementReason
|
|
14526
|
+
/**
|
|
14527
|
+
* File path relative to the analysed root. For `vendor-only`, the path
|
|
14528
|
+
* of the `--flag-state` file: relative to the root when the file is
|
|
14529
|
+
* inside it, else as given.
|
|
14530
|
+
*/
|
|
14531
|
+
path: string
|
|
14532
|
+
/**
|
|
14533
|
+
* 1-based line.
|
|
14534
|
+
*/
|
|
14535
|
+
line: number
|
|
14536
|
+
/**
|
|
14537
|
+
* What the evidence shows.
|
|
14538
|
+
*/
|
|
14539
|
+
detail: string
|
|
14540
|
+
}
|
|
14541
|
+
/**
|
|
14542
|
+
* A follow-up action for a retirement candidate.
|
|
14543
|
+
*/
|
|
14544
|
+
export interface RetirementAction {
|
|
14545
|
+
type: RetirementActionType
|
|
14546
|
+
/**
|
|
14547
|
+
* Always `false`: Fallow never removes a flag.
|
|
14548
|
+
*/
|
|
14549
|
+
auto_fixable: boolean
|
|
14550
|
+
/**
|
|
14551
|
+
* Human-readable action description.
|
|
14552
|
+
*/
|
|
14553
|
+
description: string
|
|
14554
|
+
}
|
|
14555
|
+
/**
|
|
14556
|
+
* The vendor state of one flag in the retirement report.
|
|
14557
|
+
*/
|
|
14558
|
+
export interface RetirementVendor {
|
|
14559
|
+
/**
|
|
14560
|
+
* The key in the vendor export, before `flags.vendorKeyPrefix` is
|
|
14561
|
+
* removed.
|
|
14562
|
+
*/
|
|
14563
|
+
key: string
|
|
14564
|
+
state: VendorFlagState
|
|
14565
|
+
/**
|
|
14566
|
+
* Whether the flag serves one variation, when the export says so.
|
|
14567
|
+
*/
|
|
14568
|
+
serves_single_variation?: (boolean | null)
|
|
14569
|
+
/**
|
|
14570
|
+
* When the vendor created the flag, as the export gives it.
|
|
14571
|
+
*/
|
|
14572
|
+
created_at?: (string | null)
|
|
14573
|
+
/**
|
|
14574
|
+
* When the vendor last evaluated the flag, as the export gives it.
|
|
14575
|
+
*/
|
|
14576
|
+
last_evaluated_at?: (string | null)
|
|
14577
|
+
}
|
|
14141
14578
|
/**
|
|
14142
14579
|
* Optional `_meta` block for [`FeatureFlagsOutput`]. Both fields are optional
|
|
14143
14580
|
* because the two contributors are independent: `feature_flags` details are
|