fallow 3.15.0 → 3.17.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 +5 -5
- package/package.json +10 -10
- package/schema.json +36 -6
- package/scripts/run-binary.js +30 -10
- package/scripts/run-binary.test.js +22 -1
- package/skills/fallow/references/cli-reference.md +12 -12
- package/types/output-contract.d.ts +88 -15
package/capabilities.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fallow",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.17.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": [
|
|
@@ -302,7 +302,7 @@
|
|
|
302
302
|
"name": "--report-path-prefix",
|
|
303
303
|
"type": "string",
|
|
304
304
|
"required": false,
|
|
305
|
-
"description": "Prefix prepended to every path in the CI-facing formats (`github-annotations`, `github-summary`, `codeclimate`, `review-github`, `review-gitlab`). CI platforms address files by repository-root-relative path, so when the analyzed project lives in a subdirectory (e.g. `packages/app/`), paths need that offset. fallow detects the offset via the git toplevel automatically; this flag overrides the detection. Pass an empty string to disable rebasing and emit paths relative to `--root`"
|
|
305
|
+
"description": "Prefix prepended to every path in the CI-facing formats (`github-annotations`, `github-summary`, `codeclimate`, `pr-comment-github`, `pr-comment-gitlab`, `review-github`, `review-gitlab`). CI platforms address files by repository-root-relative path, so when the analyzed project lives in a subdirectory (e.g. `packages/app/`), paths need that offset. fallow detects the offset via the git toplevel automatically; this flag overrides the detection. Pass an empty string to disable rebasing and emit paths relative to `--root`"
|
|
306
306
|
},
|
|
307
307
|
{
|
|
308
308
|
"name": "--fail-on-regression",
|
|
@@ -1897,7 +1897,7 @@
|
|
|
1897
1897
|
},
|
|
1898
1898
|
{
|
|
1899
1899
|
"name": "report",
|
|
1900
|
-
"description": "Render a saved `--format json` results file in another format without re-running analysis (analyze once, render
|
|
1900
|
+
"description": "Render a saved `--format json` results file in another format without re-running analysis (analyze once, then render every CI surface from the same file). Supports GitHub annotations/summary, CodeClimate, SARIF, and GitHub/GitLab PR-comment and review formats",
|
|
1901
1901
|
"flags": [
|
|
1902
1902
|
{
|
|
1903
1903
|
"name": "--from",
|
|
@@ -6006,9 +6006,9 @@
|
|
|
6006
6006
|
"rule_pack_schema_command": "fallow rule-pack-schema",
|
|
6007
6007
|
"rule_pack_schema_note": "JSON Schema for a declarative rule pack referenced from rulePacks.",
|
|
6008
6008
|
"plugin_schema_command": "fallow plugin-schema",
|
|
6009
|
-
"plugin_schema_note": "JSON Schema for a user-authored external plugin (fallow-plugin-*.jsonc). Teach fallow about an unsupported framework declaratively: detection, entryPoints, alwaysUsed, usedExports, usedClassMembers, and manifestEntries (derive entry points from per-package manifest files).",
|
|
6009
|
+
"plugin_schema_note": "JSON Schema for a user-authored external plugin (fallow-plugin-*.jsonc). Teach fallow about an unsupported framework declaratively: detection, entryPoints, alwaysUsed, usedExports, usedClassMembers, and manifestEntries (derive entry points from per-package manifest files, including typed [*] traversal and exists predicates).",
|
|
6010
6010
|
"plugin_check_command": "fallow plugin-check",
|
|
6011
|
-
"plugin_check_note": "Read-only dry-run of your external plugins: reports which activated, which manifests each manifestEntries rule matched, what it seeded (with path-exists), and typed warnings (manifests-matched-none, when-excluded-all, field-path-unresolved, entries-empty, manifest-parse-failed, entry-outside-root, seeded-paths-missing). Run it after authoring a fallow-plugin-*.jsonc to verify it before a full analysis.",
|
|
6011
|
+
"plugin_check_note": "Read-only dry-run of your external plugins: reports which activated, which manifests each manifestEntries rule matched, what it seeded (with path-exists), and typed warnings (manifests-matched-none, when-excluded-all, field-path-unresolved, entries-empty, manifest-parse-failed, field-values-limit-exceeded, entry-expansion-limit-exceeded, entry-outside-root, seeded-paths-missing). Run it after authoring a fallow-plugin-*.jsonc to verify it before a full analysis.",
|
|
6012
6012
|
"config_files": [
|
|
6013
6013
|
".fallowrc.json",
|
|
6014
6014
|
".fallowrc.jsonc",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fallow",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.17.0",
|
|
4
4
|
"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.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -87,14 +87,14 @@
|
|
|
87
87
|
"@tanstack/intent": "0.3.6"
|
|
88
88
|
},
|
|
89
89
|
"optionalDependencies": {
|
|
90
|
-
"@fallow-cli/darwin-arm64": "3.
|
|
91
|
-
"@fallow-cli/darwin-x64": "3.
|
|
92
|
-
"@fallow-cli/linux-x64-gnu": "3.
|
|
93
|
-
"@fallow-cli/linux-arm64-gnu": "3.
|
|
94
|
-
"@fallow-cli/linux-x64-musl": "3.
|
|
95
|
-
"@fallow-cli/linux-arm64-musl": "3.
|
|
96
|
-
"@fallow-cli/win32-arm64-msvc": "3.
|
|
97
|
-
"@fallow-cli/win32-x64-msvc": "3.
|
|
98
|
-
"fallow-type-aware": "3.
|
|
90
|
+
"@fallow-cli/darwin-arm64": "3.17.0",
|
|
91
|
+
"@fallow-cli/darwin-x64": "3.17.0",
|
|
92
|
+
"@fallow-cli/linux-x64-gnu": "3.17.0",
|
|
93
|
+
"@fallow-cli/linux-arm64-gnu": "3.17.0",
|
|
94
|
+
"@fallow-cli/linux-x64-musl": "3.17.0",
|
|
95
|
+
"@fallow-cli/linux-arm64-musl": "3.17.0",
|
|
96
|
+
"@fallow-cli/win32-arm64-msvc": "3.17.0",
|
|
97
|
+
"@fallow-cli/win32-x64-msvc": "3.17.0",
|
|
98
|
+
"fallow-type-aware": "3.17.0"
|
|
99
99
|
}
|
|
100
100
|
}
|
package/schema.json
CHANGED
|
@@ -569,9 +569,11 @@
|
|
|
569
569
|
"default": "jsonc"
|
|
570
570
|
},
|
|
571
571
|
"when": {
|
|
572
|
-
"description": "Manifest-level gate: a map of
|
|
572
|
+
"description": "Manifest-level gate: a map of field path to an equality expectation or\nan explicit `exists` predicate. Paths use dotted object keys and may\ntraverse arrays with `[*]`. ALL entries must match; a wildcard equality\nmatches when any yielded value equals the expectation. An empty map\nmatches every manifest.",
|
|
573
573
|
"type": "object",
|
|
574
|
-
"additionalProperties":
|
|
574
|
+
"additionalProperties": {
|
|
575
|
+
"$ref": "#/$defs/ManifestCondition"
|
|
576
|
+
},
|
|
575
577
|
"default": {}
|
|
576
578
|
},
|
|
577
579
|
"entries": {
|
|
@@ -602,18 +604,46 @@
|
|
|
602
604
|
}
|
|
603
605
|
]
|
|
604
606
|
},
|
|
607
|
+
"ManifestCondition": {
|
|
608
|
+
"description": "A typed condition used by a manifest entry gate.\n\nPlain JSON values retain strict equality semantics. The reserved\n`{ \"exists\": bool }` object tests field presence without truthiness.",
|
|
609
|
+
"anyOf": [
|
|
610
|
+
{
|
|
611
|
+
"description": "Require the field path to resolve (`true`) or not resolve (`false`).",
|
|
612
|
+
"$ref": "#/$defs/ManifestExistsPredicate"
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
"description": "Require at least one yielded value to equal this JSON value exactly."
|
|
616
|
+
}
|
|
617
|
+
]
|
|
618
|
+
},
|
|
619
|
+
"ManifestExistsPredicate": {
|
|
620
|
+
"description": "The explicit field-presence predicate accepted by [`ManifestCondition`].",
|
|
621
|
+
"type": "object",
|
|
622
|
+
"properties": {
|
|
623
|
+
"exists": {
|
|
624
|
+
"description": "Whether the field path must resolve to at least one value.",
|
|
625
|
+
"type": "boolean"
|
|
626
|
+
}
|
|
627
|
+
},
|
|
628
|
+
"additionalProperties": false,
|
|
629
|
+
"required": [
|
|
630
|
+
"exists"
|
|
631
|
+
]
|
|
632
|
+
},
|
|
605
633
|
"ManifestSeedRule": {
|
|
606
634
|
"description": "A single entry seeded by a [`ManifestEntryRule`], resolved relative to the\nmanifest's directory.",
|
|
607
635
|
"type": "object",
|
|
608
636
|
"properties": {
|
|
609
637
|
"path": {
|
|
610
|
-
"description": "Entry glob relative to the manifest directory. May contain\n`${dotted.field}` interpolation that fans out over string / array\nmanifest field values
|
|
638
|
+
"description": "Entry glob relative to the manifest directory. May contain\n`${dotted.field}` interpolation that fans out over string / array\nmanifest field values; `[*]` traverses object arrays. A missing or empty\nfield seeds nothing. The glob must encode its own extension, such as\n`public/index.{ts,tsx}`. Glob entry patterns are matched literally\nagainst discovered files without\nsource-extension probing.",
|
|
611
639
|
"type": "string"
|
|
612
640
|
},
|
|
613
641
|
"when": {
|
|
614
|
-
"description": "Per-entry gate
|
|
642
|
+
"description": "Per-entry gate, using the same strict equality and `[*]` traversal as the\nmanifest-level gate. An empty map always passes.",
|
|
615
643
|
"type": "object",
|
|
616
|
-
"additionalProperties":
|
|
644
|
+
"additionalProperties": {
|
|
645
|
+
"$ref": "#/$defs/ManifestCondition"
|
|
646
|
+
},
|
|
617
647
|
"default": {}
|
|
618
648
|
}
|
|
619
649
|
},
|
|
@@ -977,7 +1007,7 @@
|
|
|
977
1007
|
"default": 15
|
|
978
1008
|
},
|
|
979
1009
|
"maxCrap": {
|
|
980
|
-
"description": "Maximum allowed CRAP (Change Risk Anti-Patterns) score per function\n(default: 30.0). CRAP combines cyclomatic complexity with test\ncoverage: high complexity plus low coverage produces a high CRAP\nscore. Functions meeting or exceeding this threshold are reported.\nUse `--coverage` with Istanbul data for accurate per-function CRAP;\notherwise fallow estimates coverage from the module graph. Governs\nfindings
|
|
1010
|
+
"description": "Maximum allowed CRAP (Change Risk Anti-Patterns) score per function\n(default: 30.0). CRAP combines cyclomatic complexity with test\ncoverage: high complexity plus low coverage produces a high CRAP\nscore. Functions meeting or exceeding this threshold are reported.\nUse `--coverage` with Istanbul data for accurate per-function CRAP;\notherwise fallow estimates coverage from the module graph. Governs\nfindings and the threshold-relative file-score signals\n(`crap_above_threshold`, the `risk` triage tag, and the\n`add_test_coverage` refactoring target); measured values such as\n`crap_max` and the overall health score never move with it. Set to\n`0` to disable CRAP enforcement entirely: no findings, nothing counts\nabove threshold, and file-score rows disclose baseline breaches as\nexempt instead.",
|
|
981
1011
|
"type": "number",
|
|
982
1012
|
"format": "double",
|
|
983
1013
|
"default": 30.0
|
package/scripts/run-binary.js
CHANGED
|
@@ -145,18 +145,38 @@ function resolveTypeAwareCompanion(
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
function
|
|
148
|
+
function typeAwareCommand(
|
|
149
|
+
companion,
|
|
150
|
+
{ platform = process.platform, execPath = process.execPath } = {},
|
|
151
|
+
) {
|
|
152
|
+
return platform === "win32"
|
|
153
|
+
? { binary: execPath, script: companion }
|
|
154
|
+
: { binary: companion, script: undefined };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function childEnvironment(
|
|
158
|
+
resolvedVersion,
|
|
159
|
+
resolveCompanion = resolveTypeAwareCompanion,
|
|
160
|
+
commandOptions,
|
|
161
|
+
) {
|
|
149
162
|
if (process.env.FALLOW_TYPE_AWARE_BIN) return process.env;
|
|
150
163
|
const companion = resolveCompanion(resolvedVersion);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
164
|
+
if (companion === undefined) return process.env;
|
|
165
|
+
|
|
166
|
+
const command = typeAwareCommand(companion, commandOptions);
|
|
167
|
+
const environment = {
|
|
168
|
+
...process.env,
|
|
169
|
+
FALLOW_TYPE_AWARE_BIN: command.binary,
|
|
170
|
+
// Marks the wiring as launcher-provided node_modules resolution so
|
|
171
|
+
// `type-aware status` does not report it as a user-set override.
|
|
172
|
+
FALLOW_TYPE_AWARE_BIN_SOURCE: "npm-wrapper",
|
|
173
|
+
};
|
|
174
|
+
if (command.script) {
|
|
175
|
+
environment.FALLOW_TYPE_AWARE_SCRIPT = command.script;
|
|
176
|
+
} else {
|
|
177
|
+
delete environment.FALLOW_TYPE_AWARE_SCRIPT;
|
|
178
|
+
}
|
|
179
|
+
return environment;
|
|
160
180
|
}
|
|
161
181
|
|
|
162
182
|
// Swallow EPIPE on stdout. When fallow's output is piped into a reader that
|
|
@@ -162,16 +162,32 @@ test("resolveTypeAwareCompanion accepts only an exact matching package", (t) =>
|
|
|
162
162
|
test("childEnvironment marks launcher-wired companions as npm-wrapper", (t) => {
|
|
163
163
|
const { childEnvironment } = require(RUN_BINARY);
|
|
164
164
|
const previousBin = process.env.FALLOW_TYPE_AWARE_BIN;
|
|
165
|
+
const previousScript = process.env.FALLOW_TYPE_AWARE_SCRIPT;
|
|
165
166
|
delete process.env.FALLOW_TYPE_AWARE_BIN;
|
|
167
|
+
delete process.env.FALLOW_TYPE_AWARE_SCRIPT;
|
|
166
168
|
t.after(() => {
|
|
167
169
|
if (previousBin === undefined) delete process.env.FALLOW_TYPE_AWARE_BIN;
|
|
168
170
|
else process.env.FALLOW_TYPE_AWARE_BIN = previousBin;
|
|
171
|
+
if (previousScript === undefined) delete process.env.FALLOW_TYPE_AWARE_SCRIPT;
|
|
172
|
+
else process.env.FALLOW_TYPE_AWARE_SCRIPT = previousScript;
|
|
169
173
|
});
|
|
170
174
|
|
|
171
|
-
const env = childEnvironment("3.8.0", () => "/tmp/fallow-type-aware.mjs"
|
|
175
|
+
const env = childEnvironment("3.8.0", () => "/tmp/fallow-type-aware.mjs", {
|
|
176
|
+
platform: "linux",
|
|
177
|
+
execPath: "/usr/bin/node",
|
|
178
|
+
});
|
|
172
179
|
assert.equal(env.FALLOW_TYPE_AWARE_BIN, "/tmp/fallow-type-aware.mjs");
|
|
180
|
+
assert.equal(env.FALLOW_TYPE_AWARE_SCRIPT, undefined);
|
|
173
181
|
assert.equal(env.FALLOW_TYPE_AWARE_BIN_SOURCE, "npm-wrapper");
|
|
174
182
|
|
|
183
|
+
const windowsEnv = childEnvironment("3.8.0", () => "C:\\pkg\\fallow-type-aware.mjs", {
|
|
184
|
+
platform: "win32",
|
|
185
|
+
execPath: "C:\\Program Files\\nodejs\\node.exe",
|
|
186
|
+
});
|
|
187
|
+
assert.equal(windowsEnv.FALLOW_TYPE_AWARE_BIN, "C:\\Program Files\\nodejs\\node.exe");
|
|
188
|
+
assert.equal(windowsEnv.FALLOW_TYPE_AWARE_SCRIPT, "C:\\pkg\\fallow-type-aware.mjs");
|
|
189
|
+
assert.equal(windowsEnv.FALLOW_TYPE_AWARE_BIN_SOURCE, "npm-wrapper");
|
|
190
|
+
|
|
175
191
|
// No resolvable companion: the environment passes through untouched.
|
|
176
192
|
const untouched = childEnvironment("3.8.0", () => undefined);
|
|
177
193
|
assert.equal(untouched, process.env);
|
|
@@ -181,14 +197,19 @@ test("childEnvironment marks launcher-wired companions as npm-wrapper", (t) => {
|
|
|
181
197
|
test("childEnvironment leaves a user-set override unmarked", (t) => {
|
|
182
198
|
const { childEnvironment } = require(RUN_BINARY);
|
|
183
199
|
const previousBin = process.env.FALLOW_TYPE_AWARE_BIN;
|
|
200
|
+
const previousScript = process.env.FALLOW_TYPE_AWARE_SCRIPT;
|
|
184
201
|
process.env.FALLOW_TYPE_AWARE_BIN = "/opt/custom-sidecar";
|
|
202
|
+
process.env.FALLOW_TYPE_AWARE_SCRIPT = "/opt/custom-sidecar.mjs";
|
|
185
203
|
t.after(() => {
|
|
186
204
|
if (previousBin === undefined) delete process.env.FALLOW_TYPE_AWARE_BIN;
|
|
187
205
|
else process.env.FALLOW_TYPE_AWARE_BIN = previousBin;
|
|
206
|
+
if (previousScript === undefined) delete process.env.FALLOW_TYPE_AWARE_SCRIPT;
|
|
207
|
+
else process.env.FALLOW_TYPE_AWARE_SCRIPT = previousScript;
|
|
188
208
|
});
|
|
189
209
|
|
|
190
210
|
const env = childEnvironment("3.8.0", () => "/tmp/fallow-type-aware.mjs");
|
|
191
211
|
assert.equal(env, process.env);
|
|
212
|
+
assert.equal(env.FALLOW_TYPE_AWARE_SCRIPT, "/opt/custom-sidecar.mjs");
|
|
192
213
|
assert.equal(env.FALLOW_TYPE_AWARE_BIN_SOURCE, undefined);
|
|
193
214
|
});
|
|
194
215
|
|
|
@@ -521,7 +521,7 @@ fallow health --format json --quiet --trend
|
|
|
521
521
|
{
|
|
522
522
|
"kind": "health",
|
|
523
523
|
"schema_version": 7,
|
|
524
|
-
"version": "3.
|
|
524
|
+
"version": "3.16.0",
|
|
525
525
|
"elapsed_ms": 32,
|
|
526
526
|
"summary": {
|
|
527
527
|
"files_analyzed": 482,
|
|
@@ -595,7 +595,7 @@ With `--file-scores`, the JSON output also includes `file_scores` array and `sum
|
|
|
595
595
|
|
|
596
596
|
The `file_scores` array is sorted by risk-aware triage concern: the larger of low-MI concern and CRAP risk. This keeps files with very high untested complexity near the top even when their Maintainability Index is not the lowest.
|
|
597
597
|
|
|
598
|
-
The `crap_max` field is the highest CRAP (Change Risk Anti-Patterns) score among functions in the file, using the canonical formula `CC^2 * (1 - cov/100)^3 + CC`. The default model (`static_estimated`) estimates per-function coverage from export references: directly test-referenced = 85%, indirectly test-reachable = 40%, untested = 0%. Provide `--coverage <path>` with Istanbul-format `coverage-final.json` for exact scores (`istanbul` model). The `crap_above_threshold` field counts functions
|
|
598
|
+
The `crap_max` field is the highest CRAP (Change Risk Anti-Patterns) score among functions in the file, using the canonical formula `CC^2 * (1 - cov/100)^3 + CC`. It is always the raw measured value. The default model (`static_estimated`) estimates per-function coverage from export references: directly test-referenced = 85%, indirectly test-reachable = 40%, untested = 0%. Provide `--coverage <path>` with Istanbul-format `coverage-final.json` for exact scores (`istanbul` model). The `crap_above_threshold` field counts functions whose rounded CRAP meets or exceeds their effective ceiling, resolved from `health.thresholdOverrides` over the global `maxCrap` / `--max-crap` value (default 30); it is 0 when CRAP enforcement is disabled (`maxCrap: 0`). Rows whose breaches were let through by configuration carry two additional fields: `crap_exempted` (functions at or above the canonical 30 baseline but below their effective ceiling; omitted when 0) and `crap_effective_threshold` (the lowest effective ceiling among the file's functions, present only when it differs from `summary.max_crap_threshold`). When `--file-scores` is active, `summary.coverage_model` indicates the model used (`"static_estimated"` or `"istanbul"`). When CRAP findings carry `coverage_source`, `summary.coverage_source_consistency` is `uniform` or `mixed`; grouped health JSON mirrors this as `groups[].coverage_source_consistency`.
|
|
599
599
|
|
|
600
600
|
Maintainability index formula: `100 - (complexity_density × 30) - (dead_code_ratio × 20) - min(ln(fan_out+1) × 4, 15)`, clamped to 0–100. Higher is better. Type-only exports are excluded from dead_code_ratio. Zero-function files (barrels) are excluded by default.
|
|
601
601
|
|
|
@@ -668,7 +668,7 @@ With `--targets`, the JSON output includes a `targets` array with ranked refacto
|
|
|
668
668
|
|
|
669
669
|
Targets are sorted by `efficiency` (priority / effort_numeric) descending, surfacing quick wins first. The `target_thresholds` object exposes the adaptive percentile-based thresholds used for scoring. Priority formula: `min(complexity_density, 1) x 30 + hotspot_boost x 25 + dead_code_ratio x 20 + fan_in_norm x 15 + fan_out_norm x 10`, clamped to 0-100. Fan-in and fan-out normalization uses the project's p95 values (with floors). Categories: `urgent_churn_complexity`, `break_circular_dependency`, `split_high_impact`, `remove_dead_code`, `extract_complex_functions`, `extract_dependencies`, `add_test_coverage`. Each target includes `efficiency`, `effort` (low/medium/high), `confidence` (high/medium/low, data source reliability), and contributing `factors`.
|
|
670
670
|
|
|
671
|
-
The `add_test_coverage` category fires when a file has 2+ functions
|
|
671
|
+
The `add_test_coverage` category fires when a file has 2+ functions whose rounded CRAP meets or exceeds their effective ceiling (`health.thresholdOverrides` over the global `maxCrap` / `--max-crap`, default 30) and complexity density > 0.3. A file whose breaching functions are all exempted by configuration produces no target. The `crap_max` metric appears in contributing factors for these targets, with `threshold` set to the file's lowest effective ceiling (the run global when no override applies).
|
|
672
672
|
|
|
673
673
|
### Vital Signs
|
|
674
674
|
|
|
@@ -919,7 +919,7 @@ fallow audit \
|
|
|
919
919
|
{
|
|
920
920
|
"kind": "audit",
|
|
921
921
|
"schema_version": 7,
|
|
922
|
-
"version": "3.
|
|
922
|
+
"version": "3.16.0",
|
|
923
923
|
"command": "audit",
|
|
924
924
|
"verdict": "fail",
|
|
925
925
|
"changed_files_count": 12,
|
|
@@ -996,7 +996,7 @@ fallow flags --format json --quiet --workspace my-package
|
|
|
996
996
|
```json
|
|
997
997
|
{
|
|
998
998
|
"schema_version": 7,
|
|
999
|
-
"version": "3.
|
|
999
|
+
"version": "3.16.0",
|
|
1000
1000
|
"elapsed_ms": 116,
|
|
1001
1001
|
"feature_flags": [],
|
|
1002
1002
|
"total_flags": 0
|
|
@@ -1097,7 +1097,7 @@ fallow security --gate newly-reachable --changed-since origin/main
|
|
|
1097
1097
|
{
|
|
1098
1098
|
"kind": "security",
|
|
1099
1099
|
"schema_version": "4",
|
|
1100
|
-
"version": "3.
|
|
1100
|
+
"version": "3.16.0",
|
|
1101
1101
|
"elapsed_ms": 42,
|
|
1102
1102
|
"config": {
|
|
1103
1103
|
"rules": {
|
|
@@ -1126,7 +1126,7 @@ fallow security --gate newly-reachable --changed-since origin/main
|
|
|
1126
1126
|
{
|
|
1127
1127
|
"kind": "security",
|
|
1128
1128
|
"schema_version": "4",
|
|
1129
|
-
"version": "3.
|
|
1129
|
+
"version": "3.16.0",
|
|
1130
1130
|
"elapsed_ms": 42,
|
|
1131
1131
|
"config": {
|
|
1132
1132
|
"rules": {
|
|
@@ -1361,7 +1361,7 @@ fallow plugin-schema > plugin-schema.json
|
|
|
1361
1361
|
|
|
1362
1362
|
## `plugin-check`: Verify external plugins
|
|
1363
1363
|
|
|
1364
|
-
Read-only dry-run of your external plugins. Reports, per plugin, whether it activated (with the unmet `detection`/`enabler` requirement when inactive), and for `manifestEntries` rules which manifests each matched, what it seeded (with `path_exists`), and typed warnings (`manifests-matched-none`, `when-excluded-all`, `field-path-unresolved`, `entries-empty`, `manifest-parse-failed`, `entry-outside-root`, `seeded-paths-missing`). Run it after authoring a `fallow-plugin-*.jsonc` to verify it before a full analysis. Deterministic output; always exits 0 (advisory, never a gate).
|
|
1364
|
+
Read-only dry-run of your external plugins. Reports, per plugin, whether it activated (with the unmet `detection`/`enabler` requirement when inactive), and for `manifestEntries` rules which manifests each matched, what it seeded (with `path_exists`), and typed warnings (`manifests-matched-none`, `when-excluded-all`, `field-path-unresolved`, `entries-empty`, `manifest-parse-failed`, `field-values-limit-exceeded`, `entry-expansion-limit-exceeded`, `entry-outside-root`, `seeded-paths-missing`). Run it after authoring a `fallow-plugin-*.jsonc` to verify it before a full analysis. Deterministic output; always exits 0 (advisory, never a gate).
|
|
1365
1365
|
|
|
1366
1366
|
```bash
|
|
1367
1367
|
fallow plugin-check --format json
|
|
@@ -1891,7 +1891,7 @@ The HTTP layer mirrors the bash `gh_api_retry` / `curl_retry` helpers: `FALLOW_A
|
|
|
1891
1891
|
{
|
|
1892
1892
|
"kind": "dead-code",
|
|
1893
1893
|
"schema_version": 7,
|
|
1894
|
-
"version": "3.
|
|
1894
|
+
"version": "3.16.0",
|
|
1895
1895
|
"elapsed_ms": 45,
|
|
1896
1896
|
"total_issues": 12,
|
|
1897
1897
|
"entry_points": {
|
|
@@ -2051,7 +2051,7 @@ When `--baseline` is used in combined output, the JSON includes a `baseline_delt
|
|
|
2051
2051
|
{
|
|
2052
2052
|
"kind": "dupes",
|
|
2053
2053
|
"schema_version": 7,
|
|
2054
|
-
"version": "3.
|
|
2054
|
+
"version": "3.16.0",
|
|
2055
2055
|
"elapsed_ms": 82,
|
|
2056
2056
|
"total_clones": 15,
|
|
2057
2057
|
"total_lines_duplicated": 230,
|
|
@@ -2095,11 +2095,11 @@ When running `fallow` with no subcommand (all analyses), the JSON output combine
|
|
|
2095
2095
|
{
|
|
2096
2096
|
"kind": "combined",
|
|
2097
2097
|
"schema_version": 7,
|
|
2098
|
-
"version": "3.
|
|
2098
|
+
"version": "3.16.0",
|
|
2099
2099
|
"elapsed_ms": 159,
|
|
2100
2100
|
"check": {
|
|
2101
2101
|
"schema_version": 7,
|
|
2102
|
-
"version": "3.
|
|
2102
|
+
"version": "3.16.0",
|
|
2103
2103
|
"elapsed_ms": 45,
|
|
2104
2104
|
"total_issues": 12,
|
|
2105
2105
|
"unused_files": [],
|
|
@@ -102,7 +102,7 @@ kind: "type-aware-status"
|
|
|
102
102
|
/**
|
|
103
103
|
* Schema projection for the audit envelope's exact version.
|
|
104
104
|
*/
|
|
105
|
-
export type AuditSchemaVersion =
|
|
105
|
+
export type AuditSchemaVersion = 10
|
|
106
106
|
/**
|
|
107
107
|
* Fallow CLI version that produced this envelope. Renders to the JSON wire as
|
|
108
108
|
* a bare string (e.g. `"2.74.0"`).
|
|
@@ -144,7 +144,7 @@ export type SemanticCompletenessRequirement = ("best-effort" | "complete")
|
|
|
144
144
|
/**
|
|
145
145
|
* Stable reason why semantic evidence is partial or unavailable.
|
|
146
146
|
*/
|
|
147
|
-
export type SemanticGapReason = ("no-project" | "ambiguous-project" | "blocking-diagnostics" | "unknown-symbol" | "unknown-entry-point" | "evidence-limit" | "dynamic-behavior" | "virtual-dispatch" | "dynamic-member-access" | "decorated-declaration" | "optional-contract" | "accessor-pair" | "overload-set" | "attached-comment" | "abstract-declaration" | "incomplete-project-coverage" | "framework-contract-provenance" | "capacity" | "unsupported-syntax")
|
|
147
|
+
export type SemanticGapReason = ("no-project" | "ambiguous-project" | "blocking-diagnostics" | "svelte-virtual-module-exports" | "unknown-symbol" | "unknown-entry-point" | "evidence-limit" | "dynamic-behavior" | "virtual-dispatch" | "dynamic-member-access" | "decorated-declaration" | "optional-contract" | "accessor-pair" | "overload-set" | "attached-comment" | "abstract-declaration" | "incomplete-project-coverage" | "framework-contract-provenance" | "capacity" | "unsupported-syntax")
|
|
148
148
|
/**
|
|
149
149
|
* Value or type namespace for one exact declaration or reference.
|
|
150
150
|
*/
|
|
@@ -184,7 +184,7 @@ export type TypeAwareAbstentionReason = ("no-project" | "ambiguous-project" | "b
|
|
|
184
184
|
/**
|
|
185
185
|
* Schema projection for the dead-code envelope's exact version.
|
|
186
186
|
*/
|
|
187
|
-
export type CheckSchemaVersion =
|
|
187
|
+
export type CheckSchemaVersion = 9
|
|
188
188
|
/**
|
|
189
189
|
* A suggested action attached to a finding in the JSON output. Each finding
|
|
190
190
|
* carries an `actions` array; consumers (agents, IDE clients, CI bots) can
|
|
@@ -273,7 +273,9 @@ export type AddToConfigValue = (string | IgnoreExportsRule[] | {
|
|
|
273
273
|
* means it was inherited. Duplication findings carry one carve-out: a clone
|
|
274
274
|
* group whose structural key is new but whose instances contain no added line
|
|
275
275
|
* from the diff (a group re-shaped by removing duplication elsewhere) is
|
|
276
|
-
* demoted to inherited and serializes `false` (issue #2164).
|
|
276
|
+
* demoted to inherited and serializes `false` (issue #2164). Such demoted
|
|
277
|
+
* groups additionally carry a `demotion_reason` field naming the rule, and
|
|
278
|
+
* are counted in `attribution.duplication_demoted` (issue #2220).
|
|
277
279
|
*
|
|
278
280
|
* Outside of audit sub-results the field is omitted, so call sites typically
|
|
279
281
|
* hold `Option<AuditIntroduced>`. Renders to the JSON wire as a bare boolean.
|
|
@@ -445,6 +447,15 @@ kind: "source-read-failure"
|
|
|
445
447
|
* emitted by the legacy `build_clone_group_actions` walker.
|
|
446
448
|
*/
|
|
447
449
|
export type CloneGroupActionType = ("extract-shared" | "suppress-line")
|
|
450
|
+
/**
|
|
451
|
+
* Why the audit new-only gate demoted an introduced clone group to
|
|
452
|
+
* inherited. Serializes as a kebab-case string on the wire (for example
|
|
453
|
+
* `"no-added-lines"`).
|
|
454
|
+
*
|
|
455
|
+
* Further variants may be added in later releases; consumers should treat an
|
|
456
|
+
* unknown value as "some demotion reason" rather than failing.
|
|
457
|
+
*/
|
|
458
|
+
export type CloneDemotionReason = "no-added-lines"
|
|
448
459
|
/**
|
|
449
460
|
* The kind of refactoring suggested for a clone family.
|
|
450
461
|
*/
|
|
@@ -771,7 +782,7 @@ export type GitLabReviewPositionType = "text"
|
|
|
771
782
|
/**
|
|
772
783
|
* Schema-version discriminator for the review envelope.
|
|
773
784
|
*/
|
|
774
|
-
export type ReviewEnvelopeSchema = ("fallow-review-envelope/v1" | "fallow-review-envelope/v2")
|
|
785
|
+
export type ReviewEnvelopeSchema = ("fallow-review-envelope/v1" | "fallow-review-envelope/v2" | "fallow-review-envelope/v3")
|
|
775
786
|
/**
|
|
776
787
|
* Review-envelope provider tag.
|
|
777
788
|
*/
|
|
@@ -810,7 +821,7 @@ export type CoverageSetupRuntimeTarget = ("node" | "browser")
|
|
|
810
821
|
/**
|
|
811
822
|
* Schema-version discriminator for [`CoverageAnalyzeOutput`].
|
|
812
823
|
*/
|
|
813
|
-
export type CoverageAnalyzeSchemaVersion = "1"
|
|
824
|
+
export type CoverageAnalyzeSchemaVersion = ("1" | "2")
|
|
814
825
|
/**
|
|
815
826
|
* Discovery outcome for a [`LogicalGroup`].
|
|
816
827
|
*/
|
|
@@ -818,7 +829,7 @@ export type LogicalGroupStatus = ("ok" | "empty" | "invalid_path")
|
|
|
818
829
|
/**
|
|
819
830
|
* Exact schema version for [`HealthOutput`].
|
|
820
831
|
*/
|
|
821
|
-
export type HealthSchemaVersion =
|
|
832
|
+
export type HealthSchemaVersion = 11
|
|
822
833
|
/**
|
|
823
834
|
* Resolver mode label for grouped envelopes (dead-code, dupes, health).
|
|
824
835
|
*
|
|
@@ -831,7 +842,7 @@ export type GroupByMode = ("owner" | "directory" | "package" | "section")
|
|
|
831
842
|
* Schema projection for the duplication envelope's CLI and programmatic
|
|
832
843
|
* version lineages.
|
|
833
844
|
*/
|
|
834
|
-
export type DupesSchemaVersion = (
|
|
845
|
+
export type DupesSchemaVersion = (3 | 9)
|
|
835
846
|
/**
|
|
836
847
|
* Wire-version discriminator for [`ImpactReport`]. Independent from the global
|
|
837
848
|
* `SchemaVersion` (the impact report versions on its own cadence) and from the
|
|
@@ -840,7 +851,7 @@ export type DupesSchemaVersion = (2 | 8)
|
|
|
840
851
|
* it, matching the other independently-versioned envelopes (e.g.
|
|
841
852
|
* `CoverageAnalyzeSchemaVersion`).
|
|
842
853
|
*/
|
|
843
|
-
export type ImpactReportSchemaVersion = "1"
|
|
854
|
+
export type ImpactReportSchemaVersion = ("1" | "2")
|
|
844
855
|
/**
|
|
845
856
|
* Why Impact tracking is (or is not) active for a project. `Project` = an
|
|
846
857
|
* explicit per-repo `enable`; `User` = the user-global default with no per-repo
|
|
@@ -856,12 +867,12 @@ export type ImpactTrendDirection = ("improving" | "declining" | "stable")
|
|
|
856
867
|
* versions separately from the per-project `ImpactReportSchemaVersion` and the
|
|
857
868
|
* on-disk `STORE_SCHEMA_VERSION`).
|
|
858
869
|
*/
|
|
859
|
-
export type CrossRepoImpactSchemaVersion = "1"
|
|
870
|
+
export type CrossRepoImpactSchemaVersion = ("1" | "2")
|
|
860
871
|
/**
|
|
861
872
|
* The `fallow security --format json` schema version. Independently versioned
|
|
862
873
|
* from the main contract, mirroring `ImpactReportSchemaVersion`.
|
|
863
874
|
*/
|
|
864
|
-
export type SecuritySchemaVersion = ("1" | "2" | "3" | "4" | "5" | "6" | "7")
|
|
875
|
+
export type SecuritySchemaVersion = ("1" | "2" | "3" | "4" | "5" | "6" | "7" | "8")
|
|
865
876
|
/**
|
|
866
877
|
* Severity level for rules.
|
|
867
878
|
*
|
|
@@ -943,7 +954,7 @@ export type SecurityBlindSpotsSchemaVersion = "1"
|
|
|
943
954
|
/**
|
|
944
955
|
* Schema projection for the combined envelope's exact version.
|
|
945
956
|
*/
|
|
946
|
-
export type CombinedSchemaVersion =
|
|
957
|
+
export type CombinedSchemaVersion = 11
|
|
947
958
|
/**
|
|
948
959
|
* Schema projection for the feature-flags envelope's exact version.
|
|
949
960
|
*/
|
|
@@ -964,7 +975,7 @@ export type FeatureFlagActionType = ("investigate-flag" | "suppress-line")
|
|
|
964
975
|
* Independently-versioned wire-version newtype for the brief envelope.
|
|
965
976
|
* Serializes as the integer `REVIEW_BRIEF_SCHEMA_VERSION`.
|
|
966
977
|
*/
|
|
967
|
-
export type ReviewBriefSchemaVersion =
|
|
978
|
+
export type ReviewBriefSchemaVersion = 7
|
|
968
979
|
/**
|
|
969
980
|
* The exactly-three shippable decision categories (the SOLID-3). No cut category
|
|
970
981
|
* (abstraction / deletion / convention / irreversibility) is representable: this
|
|
@@ -1157,6 +1168,7 @@ duplication_introduced: number
|
|
|
1157
1168
|
duplication_inherited: number
|
|
1158
1169
|
styling_introduced: number
|
|
1159
1170
|
styling_inherited: number
|
|
1171
|
+
duplication_demoted: number
|
|
1160
1172
|
}
|
|
1161
1173
|
/**
|
|
1162
1174
|
* Metric and rule definitions emitted under `_meta` when `--explain` is
|
|
@@ -1928,6 +1940,11 @@ ambiguous_project: number
|
|
|
1928
1940
|
* Candidates retained because structural diagnostics block scanning.
|
|
1929
1941
|
*/
|
|
1930
1942
|
blocking_diagnostics: number
|
|
1943
|
+
/**
|
|
1944
|
+
* Candidates retained because the raw TypeScript-Go host cannot expose
|
|
1945
|
+
* named exports from Svelte virtual modules.
|
|
1946
|
+
*/
|
|
1947
|
+
svelte_virtual_module_exports: number
|
|
1931
1948
|
/**
|
|
1932
1949
|
* Candidates whose exact declaration identity could not be resolved.
|
|
1933
1950
|
*/
|
|
@@ -4891,6 +4908,13 @@ actions: CloneGroupAction[]
|
|
|
4891
4908
|
* to the merge-base. `None` when serialized directly from Rust.
|
|
4892
4909
|
*/
|
|
4893
4910
|
introduced?: (AuditIntroduced | null)
|
|
4911
|
+
/**
|
|
4912
|
+
* Set only by `fallow audit` under `--gate new-only`, on groups whose
|
|
4913
|
+
* `introduced` flag the gate demoted to `false`: why the demotion
|
|
4914
|
+
* happened. `None` everywhere else, including `fallow dupes
|
|
4915
|
+
* --format json` (issue #2220).
|
|
4916
|
+
*/
|
|
4917
|
+
demotion_reason?: (CloneDemotionReason | null)
|
|
4894
4918
|
}
|
|
4895
4919
|
/**
|
|
4896
4920
|
* A single instance of duplicated code at a specific location.
|
|
@@ -5759,6 +5783,12 @@ cognitive: number
|
|
|
5759
5783
|
* Current CRAP score, when coverage data exists.
|
|
5760
5784
|
*/
|
|
5761
5785
|
crap?: (number | null)
|
|
5786
|
+
/**
|
|
5787
|
+
* Measured line count of the matched unit. Present on complexity rows,
|
|
5788
|
+
* where `maxUnitSize` participates in the dimension; absent on CRAP rows
|
|
5789
|
+
* and `<component>` rollup rows, which are never scored on unit size.
|
|
5790
|
+
*/
|
|
5791
|
+
line_count?: (number | null)
|
|
5762
5792
|
}
|
|
5763
5793
|
/**
|
|
5764
5794
|
* Project-wide vital signs: a fixed set of metrics for trend tracking.
|
|
@@ -6134,13 +6164,31 @@ function_count: number
|
|
|
6134
6164
|
*/
|
|
6135
6165
|
lines: number
|
|
6136
6166
|
/**
|
|
6137
|
-
* Highest CRAP score among the file's functions.
|
|
6167
|
+
* Highest CRAP score among the file's functions. Always the raw measured
|
|
6168
|
+
* value; threshold overrides never rewrite it.
|
|
6138
6169
|
*/
|
|
6139
6170
|
crap_max: number
|
|
6140
6171
|
/**
|
|
6141
|
-
* Functions whose CRAP score exceeds
|
|
6172
|
+
* Functions whose rounded CRAP score meets or exceeds their effective
|
|
6173
|
+
* ceiling, resolved from `health.thresholdOverrides` over the global
|
|
6174
|
+
* `maxCrap` / `--max-crap` value. Zero when CRAP enforcement is disabled
|
|
6175
|
+
* (global ceiling `0`).
|
|
6142
6176
|
*/
|
|
6143
6177
|
crap_above_threshold: number
|
|
6178
|
+
/**
|
|
6179
|
+
* Functions whose rounded CRAP score is at or above the canonical 30.0
|
|
6180
|
+
* baseline but below their effective ceiling: the count the configuration
|
|
6181
|
+
* let through. Stays `0` when the effective ceiling is stricter than 30.
|
|
6182
|
+
* When CRAP enforcement is disabled (global ceiling `0`), counts every
|
|
6183
|
+
* function at or above the canonical baseline. Omitted when zero.
|
|
6184
|
+
*/
|
|
6185
|
+
crap_exempted?: number
|
|
6186
|
+
/**
|
|
6187
|
+
* Lowest effective CRAP ceiling among the file's functions, present only
|
|
6188
|
+
* when it differs from the run global (`summary.max_crap_threshold`).
|
|
6189
|
+
* Consumers fall back to `summary.max_crap_threshold` when absent.
|
|
6190
|
+
*/
|
|
6191
|
+
crap_effective_threshold?: (number | null)
|
|
6144
6192
|
}
|
|
6145
6193
|
/**
|
|
6146
6194
|
* Static test coverage gaps derived from the module graph. Shows runtime files
|
|
@@ -8971,6 +9019,7 @@ file: string
|
|
|
8971
9019
|
* The export name being traced.
|
|
8972
9020
|
*/
|
|
8973
9021
|
export_name: string
|
|
9022
|
+
namespace?: SemanticNamespace
|
|
8974
9023
|
/**
|
|
8975
9024
|
* Whether the file is reachable from an entry point.
|
|
8976
9025
|
*/
|
|
@@ -9322,6 +9371,14 @@ callees?: (ChainHop[] | null)
|
|
|
9322
9371
|
* Present only when `--callees` was requested.
|
|
9323
9372
|
*/
|
|
9324
9373
|
unresolved_callees?: (UnresolvedCallee[] | null)
|
|
9374
|
+
/**
|
|
9375
|
+
* Set when the name is unresolvable because two different `export *`
|
|
9376
|
+
* sources of this file supply it. `symbol_found` is `false` in that case
|
|
9377
|
+
* for the same reason it is false for an unknown name (the file exports
|
|
9378
|
+
* nothing under it per ECMA-262 ResolveExport), so this field is the only
|
|
9379
|
+
* thing that separates a barrel mistake from a typo.
|
|
9380
|
+
*/
|
|
9381
|
+
star_export_ambiguity?: (StarExportAmbiguity | null)
|
|
9325
9382
|
/**
|
|
9326
9383
|
* A human-readable summary of the trace outcome.
|
|
9327
9384
|
*/
|
|
@@ -9366,6 +9423,22 @@ export interface UnresolvedCallee {
|
|
|
9366
9423
|
callee: string
|
|
9367
9424
|
reason: UnresolvedReason
|
|
9368
9425
|
}
|
|
9426
|
+
/**
|
|
9427
|
+
* The `export *` collision that keeps a name from being exported.
|
|
9428
|
+
*/
|
|
9429
|
+
export interface StarExportAmbiguity {
|
|
9430
|
+
/**
|
|
9431
|
+
* Files that each declare a colliding declaration under the traced name
|
|
9432
|
+
* (project-root-relative), sorted. These are the origins to fix: keep one,
|
|
9433
|
+
* rename or explicitly re-export the rest.
|
|
9434
|
+
*/
|
|
9435
|
+
sources: string[]
|
|
9436
|
+
/**
|
|
9437
|
+
* The namespaces the collision occurs in, type before value. A name can
|
|
9438
|
+
* collide in type space, value space, or both.
|
|
9439
|
+
*/
|
|
9440
|
+
namespaces: SemanticNamespace[]
|
|
9441
|
+
}
|
|
9369
9442
|
/**
|
|
9370
9443
|
* Envelope emitted by `fallow --format review-github` / `review-gitlab`.
|
|
9371
9444
|
*/
|