fallow 3.4.2 → 3.5.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/README.md +1 -1
- package/bin/fallow-lsp +4 -1
- package/bin/fallow-mcp +4 -1
- package/capabilities.json +13 -2
- package/package.json +9 -9
- package/scripts/lazy-verify.js +4 -1
- package/scripts/lazy-verify.test.js +6 -11
- package/scripts/run-binary.js +10 -2
- package/scripts/verify-binary.js +9 -5
- package/scripts/verify-binary.test.js +16 -22
- package/skills/fallow/references/cli-reference.md +15 -12
- package/skills/fallow/references/mcp.md +1 -1
- package/skills/fallow/references/patterns.md +6 -6
- package/types/output-contract.d.ts +246 -138
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ Static analysis is open source. An optional runtime layer adds production execut
|
|
|
20
20
|
npm install --save-dev fallow # or: pnpm add -D fallow / yarn add -D fallow / bun add -d fallow
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
Installs the `fallow` CLI
|
|
23
|
+
Installs the `fallow` CLI in your project, along with the `fallow-lsp` and `fallow-mcp` launchers that start the LSP and MCP servers from the same binary.
|
|
24
24
|
|
|
25
25
|
The package also ships a version-matched Agent Skill under `skills/fallow`.
|
|
26
26
|
For tools that need CLI and issue-surface metadata without spawning the binary,
|
package/bin/fallow-lsp
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// Launcher shim: the platform packages ship a single multicall `fallow`
|
|
4
|
+
// binary, so `fallow-lsp` spawns `fallow lsp-server` with the same stdio,
|
|
5
|
+
// signal, and exit-code forwarding as a bare `fallow` invocation.
|
|
3
6
|
const { runBinary } = require('../scripts/run-binary');
|
|
4
|
-
runBinary('fallow-
|
|
7
|
+
runBinary('fallow', { prependArgs: ['lsp-server'] });
|
package/bin/fallow-mcp
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// Launcher shim: the platform packages ship a single multicall `fallow`
|
|
4
|
+
// binary, so `fallow-mcp` spawns `fallow mcp-server` with the same stdio,
|
|
5
|
+
// signal, and exit-code forwarding as a bare `fallow` invocation.
|
|
3
6
|
const { runBinary } = require('../scripts/run-binary');
|
|
4
|
-
runBinary('fallow-
|
|
7
|
+
runBinary('fallow', { prependArgs: ['mcp-server'] });
|
package/capabilities.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fallow",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.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": [
|
|
@@ -854,6 +854,16 @@
|
|
|
854
854
|
"true",
|
|
855
855
|
"false"
|
|
856
856
|
]
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
"name": "--churn",
|
|
860
|
+
"type": "bool",
|
|
861
|
+
"required": false,
|
|
862
|
+
"description": "OPT-IN: attach target-level git churn evidence from the health hotspot subsystem. Default off to avoid git-history latency",
|
|
863
|
+
"possible_values": [
|
|
864
|
+
"true",
|
|
865
|
+
"false"
|
|
866
|
+
]
|
|
857
867
|
}
|
|
858
868
|
]
|
|
859
869
|
},
|
|
@@ -6380,7 +6390,8 @@
|
|
|
6380
6390
|
"cli_command": "fallow inspect --format json --quiet",
|
|
6381
6391
|
"key_params": [
|
|
6382
6392
|
"target",
|
|
6383
|
-
"production"
|
|
6393
|
+
"production",
|
|
6394
|
+
"include_churn"
|
|
6384
6395
|
],
|
|
6385
6396
|
"license": "free",
|
|
6386
6397
|
"license_note": null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fallow",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.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,13 +87,13 @@
|
|
|
87
87
|
"@tanstack/intent": "0.3.2"
|
|
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.
|
|
90
|
+
"@fallow-cli/darwin-arm64": "3.5.0",
|
|
91
|
+
"@fallow-cli/darwin-x64": "3.5.0",
|
|
92
|
+
"@fallow-cli/linux-x64-gnu": "3.5.0",
|
|
93
|
+
"@fallow-cli/linux-arm64-gnu": "3.5.0",
|
|
94
|
+
"@fallow-cli/linux-x64-musl": "3.5.0",
|
|
95
|
+
"@fallow-cli/linux-arm64-musl": "3.5.0",
|
|
96
|
+
"@fallow-cli/win32-arm64-msvc": "3.5.0",
|
|
97
|
+
"@fallow-cli/win32-x64-msvc": "3.5.0"
|
|
98
98
|
}
|
|
99
99
|
}
|
package/scripts/lazy-verify.js
CHANGED
|
@@ -66,8 +66,11 @@ function emitVerifyLog(env, payload) {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
function binaryTargetsForPlatform(platform) {
|
|
69
|
+
// Platform packages ship one binary: the multicall `fallow`. The bundled
|
|
70
|
+
// lsp/mcp launchers spawn `fallow lsp-server` / `fallow mcp-server`, so there
|
|
71
|
+
// is only one binary to sentinel-track and verify.
|
|
69
72
|
const ext = platform === "win32" ? ".exe" : "";
|
|
70
|
-
return [`fallow${ext}
|
|
73
|
+
return [`fallow${ext}`];
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
function statMtimeMs(absPath) {
|
|
@@ -28,7 +28,8 @@ function ext() {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function binaryNames() {
|
|
31
|
-
|
|
31
|
+
// Platform packages ship a single multicall `fallow` binary.
|
|
32
|
+
return [`fallow${ext()}`];
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
function computeDigestsForDir(dir) {
|
|
@@ -116,7 +117,7 @@ test("ensureVerified verifies on cache miss and writes the sentinel", (t) => {
|
|
|
116
117
|
assert.equal(sentinel.schemaVersion, SENTINEL_SCHEMA_VERSION);
|
|
117
118
|
assert.equal(sentinel.packageVersion, "2.81.0");
|
|
118
119
|
assert.equal(sentinel.packageName, "@fallow-cli/test-platform");
|
|
119
|
-
assert.equal(Object.keys(sentinel.binaries).length,
|
|
120
|
+
assert.equal(Object.keys(sentinel.binaries).length, 1);
|
|
120
121
|
});
|
|
121
122
|
|
|
122
123
|
test("ensureVerified returns cached:true on a valid sentinel", (t) => {
|
|
@@ -164,7 +165,7 @@ test("ensureVerified invalidates sentinel on mtime drift", (t) => {
|
|
|
164
165
|
);
|
|
165
166
|
assert.equal(result.ok, true);
|
|
166
167
|
assert.equal(result.cached, false);
|
|
167
|
-
assert.equal(verifyCallCount,
|
|
168
|
+
assert.equal(verifyCallCount, 1, "verify should rerun for the single binary");
|
|
168
169
|
});
|
|
169
170
|
|
|
170
171
|
test("ensureVerified invalidates sentinel on packageVersion drift", (t) => {
|
|
@@ -215,12 +216,6 @@ test("ensureVerified invalidates sentinel on packageName drift", (t) => {
|
|
|
215
216
|
packageName: "@fallow-cli/wrong-name",
|
|
216
217
|
binaries: {
|
|
217
218
|
[`fallow${ext()}`]: { mtimeMs: fs.statSync(path.join(dir, `fallow${ext()}`)).mtimeMs },
|
|
218
|
-
[`fallow-lsp${ext()}`]: {
|
|
219
|
-
mtimeMs: fs.statSync(path.join(dir, `fallow-lsp${ext()}`)).mtimeMs,
|
|
220
|
-
},
|
|
221
|
-
[`fallow-mcp${ext()}`]: {
|
|
222
|
-
mtimeMs: fs.statSync(path.join(dir, `fallow-mcp${ext()}`)).mtimeMs,
|
|
223
|
-
},
|
|
224
219
|
},
|
|
225
220
|
}),
|
|
226
221
|
);
|
|
@@ -266,13 +261,13 @@ test("ensureVerified invalidates sentinel on schemaVersion drift", (t) => {
|
|
|
266
261
|
test("ensureVerified returns sig-invalid on a tampered signature", (t) => {
|
|
267
262
|
_resetWarningState();
|
|
268
263
|
const { privateKey, rawPub } = makeKeypair();
|
|
269
|
-
const dir = mkPlatformDir(privateKey, { corruptSigFor: `fallow
|
|
264
|
+
const dir = mkPlatformDir(privateKey, { corruptSigFor: `fallow${ext()}` });
|
|
270
265
|
t.after(() => cleanup(dir));
|
|
271
266
|
|
|
272
267
|
const result = ensureVerified(baseInput(dir, (p) => _verifyWithKey(p, rawPub)));
|
|
273
268
|
assert.equal(result.ok, false);
|
|
274
269
|
assert.equal(result.code, "sig-invalid");
|
|
275
|
-
assert.match(result.binary, /fallow
|
|
270
|
+
assert.match(result.binary, /fallow/);
|
|
276
271
|
// Sentinel must NOT have been written on failure
|
|
277
272
|
assert.equal(fs.existsSync(path.join(dir, SENTINEL_FILENAME)), false);
|
|
278
273
|
});
|
package/scripts/run-binary.js
CHANGED
|
@@ -146,7 +146,15 @@ function guardBrokenStdout() {
|
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
// Run the resolved platform binary. `options.prependArgs` (default none) is a
|
|
150
|
+
// list of leading arguments inserted before the process argv, used by the
|
|
151
|
+
// `fallow-lsp` / `fallow-mcp` launcher shims to spawn the multicall binary as
|
|
152
|
+
// `fallow lsp-server` / `fallow mcp-server`. Because the platform packages ship
|
|
153
|
+
// a single `fallow` binary, those shims pass `binaryBaseName = "fallow"` and
|
|
154
|
+
// the subcommand via `prependArgs`; signal, exit-code, and version-line
|
|
155
|
+
// handling stay identical to a bare `fallow` invocation.
|
|
156
|
+
function runBinary(binaryBaseName, options = {}) {
|
|
157
|
+
const prependArgs = Array.isArray(options.prependArgs) ? options.prependArgs : [];
|
|
150
158
|
guardBrokenStdout();
|
|
151
159
|
const { pkg, manifestPath, platformPkgDir } = resolvePlatformPaths();
|
|
152
160
|
const resolvedVersion = readResolvedVersion(manifestPath);
|
|
@@ -166,7 +174,7 @@ function runBinary(binaryBaseName) {
|
|
|
166
174
|
}
|
|
167
175
|
|
|
168
176
|
try {
|
|
169
|
-
execFileSync(binaryPath, process.argv.slice(2), { stdio: "inherit" });
|
|
177
|
+
execFileSync(binaryPath, [...prependArgs, ...process.argv.slice(2)], { stdio: "inherit" });
|
|
170
178
|
} catch (e) {
|
|
171
179
|
if (e.status === undefined) throw e;
|
|
172
180
|
if (e.status === null) {
|
package/scripts/verify-binary.js
CHANGED
|
@@ -293,11 +293,15 @@ function binaryTargetsForPlatform(platformId) {
|
|
|
293
293
|
// and tests can synthesize a Windows verify without running on Windows.
|
|
294
294
|
const isWindows = typeof platformId === "string" && platformId.startsWith("win32");
|
|
295
295
|
const ext = isWindows ? ".exe" : "";
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
296
|
+
// Platform packages ship ONE binary now: the multicall `fallow` (CLI + LSP +
|
|
297
|
+
// MCP in one engine). The bundled `fallow-lsp` / `fallow-mcp` launchers live
|
|
298
|
+
// in the wrapper and spawn `fallow lsp-server` / `fallow mcp-server`. The
|
|
299
|
+
// authoritative digest is the `fallowDigests.fallow` field the release
|
|
300
|
+
// pipeline embeds for every bundled package; the `asset` below is only the
|
|
301
|
+
// legacy GitHub-release fallback for pre-embedded-digest packages, which
|
|
302
|
+
// never shipped this bundled binary, so it is inert for bundled installs and
|
|
303
|
+
// fails closed (digest-mismatch) in the impossible case it is ever reached.
|
|
304
|
+
return [{ binary: `fallow${ext}`, asset: `fallow-${platformId}${ext}` }];
|
|
301
305
|
}
|
|
302
306
|
|
|
303
307
|
function isSkipRequested() {
|
|
@@ -243,7 +243,7 @@ function makePlatformDir(privateKey, options) {
|
|
|
243
243
|
const opts = options || {};
|
|
244
244
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "fallow-vbtest-"));
|
|
245
245
|
const ext = process.platform === "win32" ? ".exe" : "";
|
|
246
|
-
for (const base of ["fallow"
|
|
246
|
+
for (const base of ["fallow"]) {
|
|
247
247
|
const binaryPath = path.join(dir, `${base}${ext}`);
|
|
248
248
|
const content = Buffer.from(`mock ${base} contents`);
|
|
249
249
|
fs.writeFileSync(binaryPath, content);
|
|
@@ -349,7 +349,7 @@ test("verifyInstalled resolves a global npm install from the fallow package dire
|
|
|
349
349
|
);
|
|
350
350
|
|
|
351
351
|
const ext = process.platform === "win32" ? ".exe" : "";
|
|
352
|
-
for (const base of ["fallow"
|
|
352
|
+
for (const base of ["fallow"]) {
|
|
353
353
|
const binaryPath = path.join(platformDir, `${base}${ext}`);
|
|
354
354
|
const content = Buffer.from(`global install ${base}`);
|
|
355
355
|
fs.writeFileSync(binaryPath, content);
|
|
@@ -367,9 +367,9 @@ test("verifyInstalled resolves a global npm install from the fallow package dire
|
|
|
367
367
|
assert.equal(result.version, "9.9.9");
|
|
368
368
|
});
|
|
369
369
|
|
|
370
|
-
test("verifyInstalled with dirOverride fails
|
|
370
|
+
test("verifyInstalled with dirOverride fails on a bad signature", async (t) => {
|
|
371
371
|
const { privateKey, rawPub } = makeKeypair();
|
|
372
|
-
const dir = makePlatformDir(privateKey, { corruptSigFor: "fallow
|
|
372
|
+
const dir = makePlatformDir(privateKey, { corruptSigFor: "fallow" });
|
|
373
373
|
t.after(() => cleanup(dir));
|
|
374
374
|
const result = await verifyInstalled({
|
|
375
375
|
dirOverride: dir,
|
|
@@ -378,12 +378,12 @@ test("verifyInstalled with dirOverride fails fast on the first bad signature", a
|
|
|
378
378
|
});
|
|
379
379
|
assert.equal(result.ok, false);
|
|
380
380
|
assert.equal(result.code, "sig-invalid");
|
|
381
|
-
assert.match(result.binary, /fallow
|
|
381
|
+
assert.match(result.binary, /fallow/);
|
|
382
382
|
});
|
|
383
383
|
|
|
384
384
|
test("verifyInstalled with dirOverride reports sig-missing when a .sig is absent", async (t) => {
|
|
385
385
|
const { privateKey, rawPub } = makeKeypair();
|
|
386
|
-
const dir = makePlatformDir(privateKey, { skipSigFor: "fallow
|
|
386
|
+
const dir = makePlatformDir(privateKey, { skipSigFor: "fallow" });
|
|
387
387
|
t.after(() => cleanup(dir));
|
|
388
388
|
const result = await verifyInstalled({
|
|
389
389
|
dirOverride: dir,
|
|
@@ -392,13 +392,13 @@ test("verifyInstalled with dirOverride reports sig-missing when a .sig is absent
|
|
|
392
392
|
});
|
|
393
393
|
assert.equal(result.ok, false);
|
|
394
394
|
assert.equal(result.code, "sig-missing");
|
|
395
|
-
assert.match(result.binary, /fallow
|
|
395
|
+
assert.match(result.binary, /fallow/);
|
|
396
396
|
});
|
|
397
397
|
|
|
398
398
|
test("verifyInstalled threads the resolved version into the sig-missing message", async (t) => {
|
|
399
399
|
const { privateKey, rawPub } = makeKeypair();
|
|
400
|
-
const preDir = makePlatformDir(privateKey, { skipSigFor: "fallow
|
|
401
|
-
const eraDir = makePlatformDir(privateKey, { skipSigFor: "fallow
|
|
400
|
+
const preDir = makePlatformDir(privateKey, { skipSigFor: "fallow" });
|
|
401
|
+
const eraDir = makePlatformDir(privateKey, { skipSigFor: "fallow" });
|
|
402
402
|
t.after(() => {
|
|
403
403
|
cleanup(preDir);
|
|
404
404
|
cleanup(eraDir);
|
|
@@ -463,7 +463,7 @@ test("verifyInstalled honors FALLOW_SKIP_BINARY_VERIFY", async (t) => {
|
|
|
463
463
|
function computeDigestsForDir(dir) {
|
|
464
464
|
const ext = process.platform === "win32" ? ".exe" : "";
|
|
465
465
|
const out = {};
|
|
466
|
-
for (const base of ["fallow"
|
|
466
|
+
for (const base of ["fallow"]) {
|
|
467
467
|
const fileName = `${base}${ext}`;
|
|
468
468
|
const full = path.join(dir, fileName);
|
|
469
469
|
out[fileName] =
|
|
@@ -549,7 +549,7 @@ test("verifyInstalled falls back to the provider when the embedded digest is mis
|
|
|
549
549
|
},
|
|
550
550
|
});
|
|
551
551
|
assert.equal(result.ok, true);
|
|
552
|
-
assert.equal(providerCalls,
|
|
552
|
+
assert.equal(providerCalls, 1);
|
|
553
553
|
});
|
|
554
554
|
|
|
555
555
|
test("verifyInstalled falls back to the provider when fallowDigests is partial / malformed", async (t) => {
|
|
@@ -573,8 +573,8 @@ test("verifyInstalled falls back to the provider when fallowDigests is partial /
|
|
|
573
573
|
},
|
|
574
574
|
});
|
|
575
575
|
assert.equal(result.ok, true);
|
|
576
|
-
// One call
|
|
577
|
-
assert.equal(providerCalls,
|
|
576
|
+
// One call for the single shipped binary because its entry fails to normalize.
|
|
577
|
+
assert.equal(providerCalls, 1);
|
|
578
578
|
});
|
|
579
579
|
|
|
580
580
|
test("verifyInstalled returns digest-mismatch when the embedded digest disagrees with the binary", async (t) => {
|
|
@@ -587,8 +587,6 @@ test("verifyInstalled returns digest-mismatch when the embedded digest disagrees
|
|
|
587
587
|
version: "1.0.0",
|
|
588
588
|
fallowDigests: {
|
|
589
589
|
[`fallow${ext}`]: "sha256:" + "a".repeat(64),
|
|
590
|
-
[`fallow-lsp${ext}`]: "sha256:" + "a".repeat(64),
|
|
591
|
-
[`fallow-mcp${ext}`]: "sha256:" + "a".repeat(64),
|
|
592
590
|
},
|
|
593
591
|
});
|
|
594
592
|
const result = await verifyInstalled({
|
|
@@ -644,9 +642,9 @@ test("verifyInstalledSync with embedded digests returns ok end-to-end", (t) => {
|
|
|
644
642
|
assert.equal(result.package, "<override>");
|
|
645
643
|
});
|
|
646
644
|
|
|
647
|
-
test("verifyInstalledSync fails
|
|
645
|
+
test("verifyInstalledSync fails on a bad signature", (t) => {
|
|
648
646
|
const { privateKey, rawPub } = makeKeypair();
|
|
649
|
-
const dir = makePlatformDir(privateKey, { corruptSigFor: "fallow
|
|
647
|
+
const dir = makePlatformDir(privateKey, { corruptSigFor: "fallow" });
|
|
650
648
|
t.after(() => cleanup(dir));
|
|
651
649
|
writeManifest(dir, {
|
|
652
650
|
name: "@fallow-cli/x",
|
|
@@ -659,7 +657,7 @@ test("verifyInstalledSync fails fast on first bad signature", (t) => {
|
|
|
659
657
|
});
|
|
660
658
|
assert.equal(result.ok, false);
|
|
661
659
|
assert.equal(result.code, "sig-invalid");
|
|
662
|
-
assert.match(result.binary, /fallow
|
|
660
|
+
assert.match(result.binary, /fallow/);
|
|
663
661
|
});
|
|
664
662
|
|
|
665
663
|
test("verifyInstalledSync reports digest-mismatch when bytes diverge from embedded digest", (t) => {
|
|
@@ -671,11 +669,7 @@ test("verifyInstalledSync reports digest-mismatch when bytes diverge from embedd
|
|
|
671
669
|
version: "9.9.9",
|
|
672
670
|
fallowDigests: {
|
|
673
671
|
fallow: "sha256:" + "a".repeat(64),
|
|
674
|
-
"fallow-lsp": "sha256:" + "a".repeat(64),
|
|
675
|
-
"fallow-mcp": "sha256:" + "a".repeat(64),
|
|
676
672
|
"fallow.exe": "sha256:" + "a".repeat(64),
|
|
677
|
-
"fallow-lsp.exe": "sha256:" + "a".repeat(64),
|
|
678
|
-
"fallow-mcp.exe": "sha256:" + "a".repeat(64),
|
|
679
673
|
},
|
|
680
674
|
});
|
|
681
675
|
const result = verifyInstalledSync({
|
|
@@ -509,7 +509,7 @@ fallow health --format json --quiet --trend
|
|
|
509
509
|
{
|
|
510
510
|
"kind": "health",
|
|
511
511
|
"schema_version": 7,
|
|
512
|
-
"version": "3.
|
|
512
|
+
"version": "3.5.0",
|
|
513
513
|
"elapsed_ms": 32,
|
|
514
514
|
"summary": {
|
|
515
515
|
"files_analyzed": 482,
|
|
@@ -907,7 +907,7 @@ fallow audit \
|
|
|
907
907
|
{
|
|
908
908
|
"kind": "audit",
|
|
909
909
|
"schema_version": 7,
|
|
910
|
-
"version": "3.
|
|
910
|
+
"version": "3.5.0",
|
|
911
911
|
"command": "audit",
|
|
912
912
|
"verdict": "fail",
|
|
913
913
|
"changed_files_count": 12,
|
|
@@ -982,7 +982,7 @@ fallow flags --format json --quiet --workspace my-package
|
|
|
982
982
|
```json
|
|
983
983
|
{
|
|
984
984
|
"schema_version": 7,
|
|
985
|
-
"version": "3.
|
|
985
|
+
"version": "3.5.0",
|
|
986
986
|
"elapsed_ms": 116,
|
|
987
987
|
"feature_flags": [],
|
|
988
988
|
"total_flags": 0
|
|
@@ -1083,7 +1083,7 @@ fallow security --gate newly-reachable --changed-since origin/main
|
|
|
1083
1083
|
{
|
|
1084
1084
|
"kind": "security",
|
|
1085
1085
|
"schema_version": "4",
|
|
1086
|
-
"version": "3.
|
|
1086
|
+
"version": "3.5.0",
|
|
1087
1087
|
"elapsed_ms": 42,
|
|
1088
1088
|
"config": {
|
|
1089
1089
|
"rules": {
|
|
@@ -1112,7 +1112,7 @@ fallow security --gate newly-reachable --changed-since origin/main
|
|
|
1112
1112
|
{
|
|
1113
1113
|
"kind": "security",
|
|
1114
1114
|
"schema_version": "4",
|
|
1115
|
-
"version": "3.
|
|
1115
|
+
"version": "3.5.0",
|
|
1116
1116
|
"elapsed_ms": 42,
|
|
1117
1117
|
"config": {
|
|
1118
1118
|
"rules": {
|
|
@@ -1185,6 +1185,7 @@ Compose one evidence bundle before editing a file or exported symbol. This is th
|
|
|
1185
1185
|
```bash
|
|
1186
1186
|
fallow inspect --file src/api.ts --format json --quiet
|
|
1187
1187
|
fallow inspect --symbol src/api.ts:fetchUser --format json --quiet
|
|
1188
|
+
fallow inspect --file src/api.ts --churn --format json --quiet
|
|
1188
1189
|
```
|
|
1189
1190
|
|
|
1190
1191
|
### Target Flags
|
|
@@ -1193,6 +1194,7 @@ fallow inspect --symbol src/api.ts:fetchUser --format json --quiet
|
|
|
1193
1194
|
|------|-------------|
|
|
1194
1195
|
| `--file <PATH>` | Inspect one project-relative file |
|
|
1195
1196
|
| `--symbol <FILE:EXPORT>` | Inspect one exported symbol. Supporting dead-code, duplication, complexity, and security evidence is file-scoped in the first version |
|
|
1197
|
+
| `--churn` | Add target-level git churn evidence. Off by default; missing git history is reported as `unavailable` |
|
|
1196
1198
|
|
|
1197
1199
|
Common global flags: `--format`, `--quiet`, `--root`, `--config`, `--workspace`, `--production`, `--no-cache`, `--threads`.
|
|
1198
1200
|
|
|
@@ -1215,13 +1217,14 @@ Common global flags: `--format`, `--quiet`, `--root`, `--config`, `--workspace`,
|
|
|
1215
1217
|
"dead_code": { "status": "ok", "scope": "file", "data": {} },
|
|
1216
1218
|
"duplication": { "status": "ok", "scope": "project_filtered_to_file", "data": {} },
|
|
1217
1219
|
"complexity": { "status": "ok", "scope": "project_filtered_to_file", "data": {} },
|
|
1218
|
-
"security": { "status": "ok", "scope": "file", "data": {} }
|
|
1220
|
+
"security": { "status": "ok", "scope": "file", "data": {} },
|
|
1221
|
+
"churn": { "status": "ok", "scope": "project_filtered_to_file", "data": {} }
|
|
1219
1222
|
},
|
|
1220
1223
|
"warnings": []
|
|
1221
1224
|
}
|
|
1222
1225
|
```
|
|
1223
1226
|
|
|
1224
|
-
Each evidence section carries `status` and `scope`. Non-fatal
|
|
1227
|
+
Each evidence section carries `status` and `scope`. The optional churn section can report `ok`, `unavailable`, or `error`. Non-fatal analysis failures become section-level errors and warnings, so callers can still use the remaining evidence.
|
|
1225
1228
|
|
|
1226
1229
|
---
|
|
1227
1230
|
|
|
@@ -1798,7 +1801,7 @@ The HTTP layer mirrors the bash `gh_api_retry` / `curl_retry` helpers: `FALLOW_A
|
|
|
1798
1801
|
|
|
1799
1802
|
## CI Integration
|
|
1800
1803
|
|
|
1801
|
-
- **GitHub Actions**: `uses: fallow-rs/fallow@
|
|
1804
|
+
- **GitHub Actions**: `uses: fallow-rs/fallow@v3` - supports SARIF upload to Code Scanning, inline PR annotations (`annotations: true`), PR comments, all commands. Annotations use workflow commands (no Advanced Security required); limit with `max-annotations` (default 50). Set `score: true` to compute health score and enable the health delta header in PR comments
|
|
1802
1805
|
- **GitLab CI**: include `ci/gitlab-ci.yml` template and extend `.fallow` - generates Code Quality reports via `--format codeclimate` / `--format gitlab-codequality` (inline MR annotations), rich MR comments, code review comments, all commands. Use `fallow ci-template gitlab --vendor` when runners cannot reach `raw.githubusercontent.com`; commit the generated `ci/` and `action/` files and use GitLab's local include syntax. Variables use `FALLOW_` prefix (e.g., `FALLOW_COMMAND`, `FALLOW_FAIL_ON_ISSUES`). Set `FALLOW_SCORE: "true"` to compute health score; `FALLOW_TREND: "true"` to compare against saved snapshots
|
|
1803
1806
|
- **Any CI**: `npx fallow --ci` - equivalent to `--format sarif --fail-on-issues --quiet`
|
|
1804
1807
|
|
|
@@ -1833,7 +1836,7 @@ The HTTP layer mirrors the bash `gh_api_retry` / `curl_retry` helpers: `FALLOW_A
|
|
|
1833
1836
|
{
|
|
1834
1837
|
"kind": "dead-code",
|
|
1835
1838
|
"schema_version": 7,
|
|
1836
|
-
"version": "3.
|
|
1839
|
+
"version": "3.5.0",
|
|
1837
1840
|
"elapsed_ms": 45,
|
|
1838
1841
|
"total_issues": 12,
|
|
1839
1842
|
"entry_points": {
|
|
@@ -1993,7 +1996,7 @@ When `--baseline` is used in combined output, the JSON includes a `baseline_delt
|
|
|
1993
1996
|
{
|
|
1994
1997
|
"kind": "dupes",
|
|
1995
1998
|
"schema_version": 7,
|
|
1996
|
-
"version": "3.
|
|
1999
|
+
"version": "3.5.0",
|
|
1997
2000
|
"elapsed_ms": 82,
|
|
1998
2001
|
"total_clones": 15,
|
|
1999
2002
|
"total_lines_duplicated": 230,
|
|
@@ -2037,11 +2040,11 @@ When running `fallow` with no subcommand (all analyses), the JSON output combine
|
|
|
2037
2040
|
{
|
|
2038
2041
|
"kind": "combined",
|
|
2039
2042
|
"schema_version": 7,
|
|
2040
|
-
"version": "3.
|
|
2043
|
+
"version": "3.5.0",
|
|
2041
2044
|
"elapsed_ms": 159,
|
|
2042
2045
|
"check": {
|
|
2043
2046
|
"schema_version": 7,
|
|
2044
|
-
"version": "3.
|
|
2047
|
+
"version": "3.5.0",
|
|
2045
2048
|
"elapsed_ms": 45,
|
|
2046
2049
|
"total_issues": 12,
|
|
2047
2050
|
"unused_files": [],
|
|
@@ -15,7 +15,7 @@ When using fallow via MCP (`fallow-mcp`), the following tools are available:
|
|
|
15
15
|
| `analyze` | analysis | free | `fallow dead-code --format json --quiet` | `issue_types`, `production`, `workspace`, `baseline`, `group_by`, `file` | Full dead code analysis (unused files/exports/types/dependencies/members + circular dependencies + re-export cycles (barrel files that form a structural loop, silently breaking re-exports) + boundary violations + rule-pack policy violations (banned calls, imports, and catalogue-derived effects declared via the `rulePacks` config key) + stale suppressions). Private type leaks are an opt-in API hygiene check via `issue_types: ["private-type-leaks"]`. Set `boundary_violations: true` as a convenience alias for `issue_types: ["boundary-violations"]`. Set `group_by` to `"owner"`, `"directory"`, `"package"`, or `"section"` to partition results. The `section` mode reads GitLab CODEOWNERS `[Section]` headers and emits `owners` metadata per group |
|
|
16
16
|
| `check_changed` | analysis | free | `fallow dead-code --changed-since <ref> --format json --quiet` | `since`, `baseline`, `fail_on_regression` | Incremental analysis of files changed since a git ref |
|
|
17
17
|
| `security_candidates` | analysis | free | `fallow security --format json --quiet` | `gate`, `surface`, `changed_since`, `paths` | Unverified local security candidates, not confirmed vulnerabilities (`fallow security --format json`). Read `security_findings[]` for category, CWE, severity, evidence, trace, optional `reachability`, blind-spot counters, and optional `unresolved_callee_diagnostics` samples for dynamic callee follow-up. `severity` is a review-priority tier, not a verified vulnerability verdict. Each finding also carries an agent-actionable `candidate` (`source_kind`/`sink`/`boundary`), where URL-category sinks may include `url_shape` (`fixed-origin-dynamic-path` or `dynamic-origin`), an optional `taint_flow` source-to-sink triple, and a stable `finding_id` (equal to the SARIF fingerprint) for cross-run correlation; there is no `impact` field (deciding exploitability is the agent's job). Set `surface: true` to include top-level `attack_surface[]` entries with defensive-boundary prompts for a verifier. Set `gate` to `new` for changed-line candidates or `newly-reachable` for candidates that became reachable from entry points; `newly-reachable` requires `changed_since`. `reachability.untrusted_source_trace` is module-level import context only and does not prove value flow; `reachability.taint_confidence` tiers each reachable candidate as `arg-level` (sink argument traces to a same-module source read, strong) or `module-level` (only the module is import-reachable from a source, weak), so tier from this field instead of the evidence text. Verify trace, reachability context, severity, and evidence before editing code. Supports `root`, `config`, `workspace`, `paths`, `changed_since`, `changed_workspaces`, `surface`, `gate`, `no_cache`, and `threads`; `paths` forwards repeated `fallow security --file` filters for finding anchors, trace hops, untrusted-source reachability trace hops, and unresolved-callee diagnostics. See <https://docs.fallow.tools/cli/security-agent-verification> for the verifier packet and verdict recipe. Inherits `FALLOW_DIFF_FILE` from the server environment for line-level diff scoping; raise `FALLOW_TIMEOUT_SECS` for large repos. |
|
|
18
|
-
| `inspect_target` | analysis | free | `fallow inspect --format json --quiet` | `target`, `production` | Compose one evidence bundle for a file or exported symbol. File targets use `target: { type: "file", file }`; symbol targets use `target: { type: "symbol", file, export_name }`. Returns `kind: "inspect_target"`, normalized target identity, `trace_file`, optional `trace_export`, file-scoped dead-code actions, duplication groups filtered to the file, complexity findings filtered to the file, and security candidates scoped to the file. Evidence sections carry `status` and `scope`; symbol targets warn when supporting evidence is file-scoped. Supports `root`, `config`, `production`, `workspace`, `no_cache`, and `threads`; `production` applies to trace, dead-code, and health evidence only. Raise `FALLOW_TIMEOUT_SECS` for large repos. |
|
|
18
|
+
| `inspect_target` | analysis | free | `fallow inspect --format json --quiet` | `target`, `production`, `include_churn` | Compose one evidence bundle for a file or exported symbol. File targets use `target: { type: "file", file }`; symbol targets use `target: { type: "symbol", file, export_name }`. Returns `kind: "inspect_target"`, normalized target identity, `trace_file`, optional `trace_export`, file-scoped dead-code actions, duplication groups filtered to the file, complexity findings filtered to the file, and security candidates scoped to the file. Set `include_churn: true` to add target-level git churn from the health hotspot subsystem. Churn is off by default, and unavailable git history or analysis failures remain explicit section states. Evidence sections carry `status` and `scope`; symbol targets warn when supporting evidence is file-scoped. Supports `root`, `config`, `production`, `workspace`, `include_churn`, `no_cache`, and `threads`; `production` applies to trace, dead-code, and health evidence only. Raise `FALLOW_TIMEOUT_SECS` for large repos. |
|
|
19
19
|
| `guard` | introspection | free | `fallow guard <file> --format json --quiet` | `files` | Report the architecture rules that apply to given files before editing them: boundary zone, allowed import zones, forbidden calls, and rule-pack policies |
|
|
20
20
|
| `find_dupes` | analysis | free | `fallow dupes --format json --quiet` | `mode`, `min_tokens`, `min_occurrences`, `top`, `threshold` | Code duplication detection. Set `changed_since` to scope to changed files since a git ref. Set `min_occurrences` (≥ 2, default 2) to hide pair-only clones and focus on widespread copy-paste; JSON gains `stats.clone_groups_below_min_occurrences` when the filter hides anything. Each `clone_groups[]` entry carries a stable `fingerprint`, usually `dup:<8hex>` and widened only on rare report collisions; pass it to `trace_clone` to deep-dive that group |
|
|
21
21
|
| `check_health` | analysis | free | `fallow health --format json --quiet` | `score`, `css`, `file_scores`, `hotspots`, `targets`, `coverage`, `runtime_coverage`, `max_crap`, `group_by` | Complexity metrics, health scores, hotspots, and refactoring targets. Set `complexity_breakdown: true` to add a per-decision-point `contributions[]` array to each complexity finding (each `else-if`, nested `if`, boolean operator, loop, `case`, etc. with its source line and cyclomatic/cognitive weight) so you can explain WHY a function scored high and pinpoint refactor targets. Optional `runtime_coverage` merges a V8 or Istanbul dump; tune it with `min_invocations_hot` (default 100), `min_observation_volume` (default 5000), and `low_traffic_threshold` (default 0.001). When runtime evidence combines with static usage, test coverage, CRAP/complexity, ownership, or change scope, read `coverage_intelligence` for stable `fallow:coverage-intel:<hash>` recommendations. Set `group_by` to `owner`, `directory`, `package`, or `section` for per-group `vital_signs` / `health_score`; SARIF results gain `properties.group`, CodeClimate issues gain a top-level `group` field |
|
|
@@ -111,7 +111,7 @@ Parse the JSON to list specific files and exports that became unused.
|
|
|
111
111
|
### GitHub Actions: Using the Official Action
|
|
112
112
|
|
|
113
113
|
```yaml
|
|
114
|
-
- uses: fallow-rs/fallow@
|
|
114
|
+
- uses: fallow-rs/fallow@v3
|
|
115
115
|
with:
|
|
116
116
|
command: dead-code
|
|
117
117
|
fail-on-issues: true
|
|
@@ -123,7 +123,7 @@ Parse the JSON to list specific files and exports that became unused.
|
|
|
123
123
|
Fail a PR only when it introduces new security candidates (or makes existing ones newly reachable). Gated failures exit with code 8; the `issues` output counts only matching gate candidates. PR comment and review renderers skip security envelopes.
|
|
124
124
|
|
|
125
125
|
```yaml
|
|
126
|
-
- uses: fallow-rs/fallow@
|
|
126
|
+
- uses: fallow-rs/fallow@v3
|
|
127
127
|
with:
|
|
128
128
|
command: security
|
|
129
129
|
security-gate: new # or newly-reachable (needs a base ref via changed-since or PR auto-scoping)
|
|
@@ -134,7 +134,7 @@ GitLab equivalent: `FALLOW_COMMAND: "security"` with `FALLOW_SECURITY_GATE: "new
|
|
|
134
134
|
### GitHub Actions: With Health Score
|
|
135
135
|
|
|
136
136
|
```yaml
|
|
137
|
-
- uses: fallow-rs/fallow@
|
|
137
|
+
- uses: fallow-rs/fallow@v3
|
|
138
138
|
with:
|
|
139
139
|
score: true
|
|
140
140
|
changed-since: main
|
|
@@ -145,7 +145,7 @@ Computes a health score (0-100 with letter grade) in combined mode and enables t
|
|
|
145
145
|
### GitHub Actions: Severity-Aware PR Quality Gate (Audit)
|
|
146
146
|
|
|
147
147
|
```yaml
|
|
148
|
-
- uses: fallow-rs/fallow@
|
|
148
|
+
- uses: fallow-rs/fallow@v3
|
|
149
149
|
with:
|
|
150
150
|
command: audit
|
|
151
151
|
gate: new-only # default; fails only on findings introduced by this PR
|
|
@@ -157,7 +157,7 @@ Runs `fallow audit` to combine dead-code + complexity + duplication scoped to ch
|
|
|
157
157
|
The action exposes `outputs.verdict` (`pass`/`warn`/`fail`) and `outputs.gate` for downstream conditionals; `outputs.issues` holds the introduced count under `gate: new-only` and the total count under `gate: all`.
|
|
158
158
|
|
|
159
159
|
```yaml
|
|
160
|
-
- uses: fallow-rs/fallow@
|
|
160
|
+
- uses: fallow-rs/fallow@v3
|
|
161
161
|
id: fallow
|
|
162
162
|
with:
|
|
163
163
|
command: audit
|
|
@@ -174,7 +174,7 @@ Three additional outputs surface silent failures in the action's PR comment / re
|
|
|
174
174
|
The official action supports inline PR annotations via GitHub workflow commands. This does not require Advanced Security (unlike SARIF upload) and works on any GitHub plan.
|
|
175
175
|
|
|
176
176
|
```yaml
|
|
177
|
-
- uses: fallow-rs/fallow@
|
|
177
|
+
- uses: fallow-rs/fallow@v3
|
|
178
178
|
with:
|
|
179
179
|
command: dead-code
|
|
180
180
|
changed-since: main
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Schemas for the JSON output of fallow commands. Object-shaped envelopes covered by the `FallowOutput` contract carry a top-level `kind` discriminator. Current kind values: `audit`, `explain`, `inspect_target`, `trace`, `review-envelope`, `review-reconcile`, `coverage-setup`, `coverage-analyze`, `list-boundaries`, `list-workspaces`, `health`, `dupes`, `dead-code-grouped`, `impact`, `impact-cross-repo`, `security`, `security-survivors`, `security-blind-spots`, `dead-code`, `combined`, `feature-flags`, `audit-brief`, `decision-surface`, `review-walkthrough-guide`, `review-walkthrough-validation`, `suppression-inventory`. Consumers should branch on `kind` instead of probing for unique field presence. `CodeClimateOutput` is a bare JSON array (per the Code Climate / GitLab Code Quality spec) and stays a sibling root branch discriminated by checking whether the document root is an array.
|
|
27
|
+
* Schemas for the JSON output of fallow commands. Object-shaped envelopes covered by the `FallowOutput` contract carry a top-level `kind` discriminator. Current kind values: `audit`, `explain`, `inspect_target`, `trace`, `review-envelope`, `review-reconcile`, `coverage-setup`, `coverage-analyze`, `list-boundaries`, `list-workspaces`, `health`, `dupes`, `dead-code-grouped`, `impact`, `impact-cross-repo`, `security`, `security-survivors`, `security-blind-spots`, `dead-code`, `combined`, `feature-flags`, `audit-brief`, `decision-surface`, `review-walkthrough-guide`, `review-walkthrough-validation`, `suppression-inventory`. Consumers should branch on `kind` instead of probing for unique field presence. `CodeClimateOutput` is a bare JSON array (per the Code Climate / GitLab Code Quality spec) and stays a sibling root branch discriminated by checking whether the document root is an array. `ErrorOutput` is the `--format json` failure document, emitted on stdout with a non-zero exit; it carries no `kind` and is discriminated by the `error: true` field.
|
|
28
28
|
*/
|
|
29
|
-
export type FallowJsonOutput = (FallowOutput | CodeClimateOutput)
|
|
29
|
+
export type FallowJsonOutput = (FallowOutput | CodeClimateOutput | ErrorOutput)
|
|
30
30
|
/**
|
|
31
31
|
* Typed root of every fallow JSON envelope shape that serializes as a JSON
|
|
32
32
|
* object and participates in the documented `FallowOutput` contract. The
|
|
@@ -86,7 +86,7 @@ kind: "dead-code"
|
|
|
86
86
|
kind: "combined"
|
|
87
87
|
}) | (FeatureFlagsOutput & {
|
|
88
88
|
kind: "feature-flags"
|
|
89
|
-
}) | (
|
|
89
|
+
}) | (ReviewBriefWireOutput & {
|
|
90
90
|
kind: "audit-brief"
|
|
91
91
|
}) | (DecisionSurfaceOutput & {
|
|
92
92
|
kind: "decision-surface"
|
|
@@ -651,7 +651,7 @@ export_name: string
|
|
|
651
651
|
type: "symbol"
|
|
652
652
|
})
|
|
653
653
|
export type InspectIdentity = (InspectFileIdentity | InspectSymbolIdentity)
|
|
654
|
-
export type InspectSectionStatus = ("ok" | "error")
|
|
654
|
+
export type InspectSectionStatus = ("ok" | "unavailable" | "error")
|
|
655
655
|
export type InspectEvidenceScope = ("symbol" | "file" | "project_filtered_to_file")
|
|
656
656
|
/**
|
|
657
657
|
* Best-effort classification of why a callee did not resolve to an edge.
|
|
@@ -835,6 +835,12 @@ export type FeatureFlagActionType = ("investigate-flag" | "suppress-line")
|
|
|
835
835
|
* Serializes as the integer `REVIEW_BRIEF_SCHEMA_VERSION`.
|
|
836
836
|
*/
|
|
837
837
|
export type ReviewBriefSchemaVersion = number
|
|
838
|
+
/**
|
|
839
|
+
* The exactly-three shippable decision categories (the SOLID-3). No cut category
|
|
840
|
+
* (abstraction / deletion / convention / irreversibility) is representable: this
|
|
841
|
+
* enum is the structural guarantee that confirmed-noise categories never ship.
|
|
842
|
+
*/
|
|
843
|
+
export type DecisionCategory = ("coupling-boundary" | "public-api-contract" | "dependency")
|
|
838
844
|
/**
|
|
839
845
|
* Coarse risk classification for a changeset, a pure function of the change
|
|
840
846
|
* size (file count plus, once threaded, net lines).
|
|
@@ -864,12 +870,6 @@ export type ConfidenceFlag = ("dynamic-dispatch" | "re-export-indirection")
|
|
|
864
870
|
* The category of a single weakening signal.
|
|
865
871
|
*/
|
|
866
872
|
export type WeakeningKind = ("test-weakened" | "threshold-lowered" | "suppression-added" | "security-check-removed")
|
|
867
|
-
/**
|
|
868
|
-
* The exactly-three shippable decision categories (the SOLID-3). No cut category
|
|
869
|
-
* (abstraction / deletion / convention / irreversibility) is representable: this
|
|
870
|
-
* enum is the structural guarantee that confirmed-noise categories never ship.
|
|
871
|
-
*/
|
|
872
|
-
export type DecisionCategory = ("coupling-boundary" | "public-api-contract" | "dependency")
|
|
873
873
|
/**
|
|
874
874
|
* Independently-versioned wire-version newtype. Serializes as the integer
|
|
875
875
|
* [`DECISION_SURFACE_SCHEMA_VERSION`].
|
|
@@ -7206,6 +7206,11 @@ duplication: InspectEvidenceSection
|
|
|
7206
7206
|
complexity: InspectEvidenceSection
|
|
7207
7207
|
security: InspectEvidenceSection
|
|
7208
7208
|
impact_closure: InspectEvidenceSection
|
|
7209
|
+
/**
|
|
7210
|
+
* OPT-IN target-level git churn. Omitted unless historical evidence was
|
|
7211
|
+
* explicitly requested by the caller.
|
|
7212
|
+
*/
|
|
7213
|
+
churn?: (InspectEvidenceSection | null)
|
|
7209
7214
|
/**
|
|
7210
7215
|
* OPT-IN symbol-level call chain. Present only when `--symbol-chain` was
|
|
7211
7216
|
* requested AND the target is a SYMBOL (best-effort, syntactic, OFF the
|
|
@@ -9347,10 +9352,22 @@ dead_export_count: number
|
|
|
9347
9352
|
dead_exports: string[]
|
|
9348
9353
|
}
|
|
9349
9354
|
/**
|
|
9350
|
-
* `_meta
|
|
9355
|
+
* Optional `_meta` block for [`FeatureFlagsOutput`]. Both fields are optional
|
|
9356
|
+
* because the two contributors are independent: `feature_flags` details are
|
|
9357
|
+
* present only with `--explain`, and `telemetry` is injected post-pass by
|
|
9358
|
+
* [`attach_telemetry_meta`] whenever an analysis run id is available (which is
|
|
9359
|
+
* the default path). Mirrors `Meta` / `CombinedMeta`, which also model
|
|
9360
|
+
* `telemetry` as an optional, never-required property.
|
|
9351
9361
|
*/
|
|
9352
9362
|
export interface FeatureFlagsMeta {
|
|
9353
|
-
|
|
9363
|
+
/**
|
|
9364
|
+
* Feature-flag detection explanations, emitted only with `--explain`.
|
|
9365
|
+
*/
|
|
9366
|
+
feature_flags?: (FeatureFlagsMetaDetails | null)
|
|
9367
|
+
/**
|
|
9368
|
+
* Local telemetry correlation metadata for agent follow-up runs.
|
|
9369
|
+
*/
|
|
9370
|
+
telemetry?: (TelemetryMeta | null)
|
|
9354
9371
|
}
|
|
9355
9372
|
/**
|
|
9356
9373
|
* Feature flag explanatory metadata.
|
|
@@ -9378,21 +9395,48 @@ medium: string
|
|
|
9378
9395
|
low: string
|
|
9379
9396
|
}
|
|
9380
9397
|
/**
|
|
9381
|
-
*
|
|
9382
|
-
*
|
|
9383
|
-
*
|
|
9384
|
-
*
|
|
9398
|
+
* Complete `fallow audit --brief --format json` wire envelope.
|
|
9399
|
+
*
|
|
9400
|
+
* This is distinct from [`ReviewBriefOutput`], which is the reusable review
|
|
9401
|
+
* digest embedded in walkthrough output. The wire envelope also carries audit
|
|
9402
|
+
* metadata, optional telemetry, and the subtract-style analysis subreports.
|
|
9385
9403
|
*/
|
|
9386
|
-
export interface
|
|
9404
|
+
export interface ReviewBriefWireOutput {
|
|
9387
9405
|
schema_version: ReviewBriefSchemaVersion
|
|
9388
|
-
|
|
9389
|
-
* Fallow CLI version that produced this output.
|
|
9390
|
-
*/
|
|
9391
|
-
version: string
|
|
9406
|
+
version: ToolVersion
|
|
9392
9407
|
/**
|
|
9393
9408
|
* Command discriminator singleton: always `"audit-brief"`.
|
|
9394
9409
|
*/
|
|
9395
9410
|
command: string
|
|
9411
|
+
verdict: AuditVerdict
|
|
9412
|
+
/**
|
|
9413
|
+
* Number of changed files in the audit scope.
|
|
9414
|
+
*/
|
|
9415
|
+
changed_files_count: number
|
|
9416
|
+
/**
|
|
9417
|
+
* Base ref used to determine the changeset.
|
|
9418
|
+
*/
|
|
9419
|
+
base_ref: string
|
|
9420
|
+
/**
|
|
9421
|
+
* Human-readable description of the resolved base, when available.
|
|
9422
|
+
*/
|
|
9423
|
+
base_description?: (string | null)
|
|
9424
|
+
/**
|
|
9425
|
+
* Head commit SHA, when available.
|
|
9426
|
+
*/
|
|
9427
|
+
head_sha?: (string | null)
|
|
9428
|
+
elapsed_ms: ElapsedMs
|
|
9429
|
+
/**
|
|
9430
|
+
* Whether base-snapshot analysis was skipped for this run.
|
|
9431
|
+
*/
|
|
9432
|
+
base_snapshot_skipped?: (boolean | null)
|
|
9433
|
+
summary: AuditSummary
|
|
9434
|
+
attribution: AuditAttribution
|
|
9435
|
+
/**
|
|
9436
|
+
* Optional metric definitions and local telemetry correlation metadata.
|
|
9437
|
+
*/
|
|
9438
|
+
_meta?: (Meta | null)
|
|
9439
|
+
decisions: DecisionSurface
|
|
9396
9440
|
triage: DiffTriage
|
|
9397
9441
|
graph_facts: GraphFacts
|
|
9398
9442
|
partition: PartitionFacts
|
|
@@ -9400,20 +9444,130 @@ impact_closure: ImpactClosureFacts
|
|
|
9400
9444
|
focus: FocusMap
|
|
9401
9445
|
deltas: ReviewDeltas
|
|
9402
9446
|
/**
|
|
9403
|
-
*
|
|
9404
|
-
* removed/skipped, thresholds lowered, suppressions added, security steps
|
|
9405
|
-
* removed). Advisory, never gates, never auto-posted.
|
|
9447
|
+
* Reviewer-private weakening signals.
|
|
9406
9448
|
*/
|
|
9407
9449
|
weakening: WeakeningSignal[]
|
|
9408
9450
|
routing: RoutingFacts
|
|
9409
|
-
|
|
9451
|
+
/**
|
|
9452
|
+
* Dead-code findings scoped to the audit changeset.
|
|
9453
|
+
*/
|
|
9454
|
+
dead_code?: (CheckOutput | null)
|
|
9455
|
+
/**
|
|
9456
|
+
* Duplication findings scoped to the audit changeset.
|
|
9457
|
+
*/
|
|
9458
|
+
duplication?: (DupesReportPayload | null)
|
|
9459
|
+
/**
|
|
9460
|
+
* Complexity findings scoped to the audit changeset.
|
|
9461
|
+
*/
|
|
9462
|
+
complexity?: (HealthReport | null)
|
|
9463
|
+
}
|
|
9464
|
+
/**
|
|
9465
|
+
* The ranked, capped decision surface plus the set of signal_ids the
|
|
9466
|
+
* deterministic layer emitted (the anti-hallucination allowlist).
|
|
9467
|
+
*/
|
|
9468
|
+
export interface DecisionSurface {
|
|
9469
|
+
/**
|
|
9470
|
+
* Ranked decisions, highest consequence first.
|
|
9471
|
+
*/
|
|
9472
|
+
decisions: Decision[]
|
|
9473
|
+
/**
|
|
9474
|
+
* Present when more than the cap were extracted.
|
|
9475
|
+
*/
|
|
9476
|
+
truncated?: (TruncationNote | null)
|
|
9477
|
+
/**
|
|
9478
|
+
* Every signal_id the deterministic layer emitted, INCLUDING those whose
|
|
9479
|
+
* decision was collapsed below the cap or suppressed. The anti-hallucination
|
|
9480
|
+
* allowlist: an agent decision whose id is absent is rejected.
|
|
9481
|
+
*/
|
|
9482
|
+
emitted_signal_ids: string[]
|
|
9483
|
+
}
|
|
9484
|
+
/**
|
|
9485
|
+
* One consequential structural decision, framed as a judgment question for a
|
|
9486
|
+
* human with taste, anchored to a fallow-emitted signal.
|
|
9487
|
+
*/
|
|
9488
|
+
export interface Decision {
|
|
9489
|
+
/**
|
|
9490
|
+
* Deterministic anchor to the fallow-emitted candidate this decision frames.
|
|
9491
|
+
* `accept_signal_id` rejects any id not in the emitted set.
|
|
9492
|
+
*/
|
|
9493
|
+
signal_id: string
|
|
9494
|
+
category: DecisionCategory
|
|
9495
|
+
/**
|
|
9496
|
+
* The decision framed as a judgment question for the human.
|
|
9497
|
+
*/
|
|
9498
|
+
question: string
|
|
9499
|
+
/**
|
|
9500
|
+
* Root-relative file the decision is anchored at.
|
|
9501
|
+
*/
|
|
9502
|
+
anchor_file: string
|
|
9503
|
+
/**
|
|
9504
|
+
* 1-based anchor line, when the underlying signal carries one (0 = file head).
|
|
9505
|
+
*/
|
|
9506
|
+
anchor_line: number
|
|
9507
|
+
/**
|
|
9508
|
+
* The raw fallow-emitted candidate key the `signal_id` hashes.
|
|
9509
|
+
*/
|
|
9510
|
+
signal_key: string
|
|
9511
|
+
/**
|
|
9512
|
+
* The `signal_id` this decision WOULD have had before any rename in this
|
|
9513
|
+
* change (the anchor file's pre-rename path). Present only when the anchor was
|
|
9514
|
+
* renamed. A review-memory layer carries a dismissal across a `git mv`: if
|
|
9515
|
+
* `previous_signal_id` was dismissed in an earlier PR, treat this decision as
|
|
9516
|
+
* dismissed too. Keeps `signal_id` itself exact + deterministic.
|
|
9517
|
+
*/
|
|
9518
|
+
previous_signal_id?: (string | null)
|
|
9519
|
+
/**
|
|
9520
|
+
* Blast radius: count of modules affected beyond the diff by this decision.
|
|
9521
|
+
*/
|
|
9522
|
+
blast: number
|
|
9523
|
+
/**
|
|
9524
|
+
* `blast * reversibility_weight`: the rank key (sorted descending).
|
|
9525
|
+
*/
|
|
9526
|
+
consequence: number
|
|
9527
|
+
/**
|
|
9528
|
+
* The routed expert(s) to ask, from ownership routing. Empty when no
|
|
9529
|
+
* ownership signal is available for the anchor file.
|
|
9530
|
+
*/
|
|
9531
|
+
expert: string[]
|
|
9532
|
+
/**
|
|
9533
|
+
* Whether the anchor file's only qualified owner is one person.
|
|
9534
|
+
*/
|
|
9535
|
+
bus_factor_one?: boolean
|
|
9536
|
+
/**
|
|
9537
|
+
* Honest per-decision count: in-repo modules OUTSIDE the diff that already
|
|
9538
|
+
* depend on this decision's anchor. This is the DISPLAY number (taste
|
|
9539
|
+
* ownership: the human reads reversibility from the count itself), distinct
|
|
9540
|
+
* from `blast` (the project-wide proxy used only for ranking). Never a door
|
|
9541
|
+
* label. Internal-only by construction, so it cannot see a published library's
|
|
9542
|
+
* external consumers; the public-API trade-off clause names that risk in prose.
|
|
9543
|
+
*/
|
|
9544
|
+
internal_consumer_count: number
|
|
9545
|
+
/**
|
|
9546
|
+
* The named structural sacrifice this change makes, stated as a fact, never a
|
|
9547
|
+
* recommendation (e.g. "Couples `app` to `infra`; 4 in-repo modules already
|
|
9548
|
+
* depend on this anchor."). A sibling fact to `question`; it never tells the
|
|
9549
|
+
* human what to choose.
|
|
9550
|
+
*/
|
|
9551
|
+
tradeoff: string
|
|
9552
|
+
}
|
|
9553
|
+
/**
|
|
9554
|
+
* A note for decisions collapsed below the cap.
|
|
9555
|
+
*/
|
|
9556
|
+
export interface TruncationNote {
|
|
9557
|
+
/**
|
|
9558
|
+
* How many decisions were collapsed below the cap.
|
|
9559
|
+
*/
|
|
9560
|
+
collapsed: number
|
|
9561
|
+
/**
|
|
9562
|
+
* Human-readable collapse reason.
|
|
9563
|
+
*/
|
|
9564
|
+
reason: string
|
|
9410
9565
|
}
|
|
9411
9566
|
/**
|
|
9412
9567
|
* Stage 0 of the brief: triage facts derived purely from the diff size.
|
|
9413
9568
|
*
|
|
9414
|
-
* `hunks` and `net_lines` are
|
|
9415
|
-
*
|
|
9416
|
-
* on `--diff-file` / `--diff-stdin`, without a schema bump.
|
|
9569
|
+
* `hunks` and `net_lines` are populated when the caller supplies parsed diff
|
|
9570
|
+
* evidence. They remain absent when no diff is available.
|
|
9417
9571
|
*/
|
|
9418
9572
|
export interface DiffTriage {
|
|
9419
9573
|
/**
|
|
@@ -9421,11 +9575,11 @@ export interface DiffTriage {
|
|
|
9421
9575
|
*/
|
|
9422
9576
|
files: number
|
|
9423
9577
|
/**
|
|
9424
|
-
* Number of diff hunks
|
|
9578
|
+
* Number of diff hunks, or `None` when no diff evidence was supplied.
|
|
9425
9579
|
*/
|
|
9426
9580
|
hunks?: (number | null)
|
|
9427
9581
|
/**
|
|
9428
|
-
* Net added-minus-removed lines
|
|
9582
|
+
* Net added-minus-removed lines, or `None` without diff evidence.
|
|
9429
9583
|
*/
|
|
9430
9584
|
net_lines?: (number | null)
|
|
9431
9585
|
risk_class: RiskClass
|
|
@@ -9437,18 +9591,20 @@ review_effort: ReviewEffort
|
|
|
9437
9591
|
* `boundaries_touched` is derived from the run's boundary-violation zones;
|
|
9438
9592
|
* `reachable_from` is populated by the impact closure (the affected-not-shown
|
|
9439
9593
|
* set: modules the changed code is reachable from / affects, none in the diff).
|
|
9440
|
-
* `exports_added`
|
|
9441
|
-
*
|
|
9442
|
-
*
|
|
9594
|
+
* `exports_added` and `api_width_delta` both report the exports-aware public API
|
|
9595
|
+
* widening count. Removed exports are not represented in this widening-only
|
|
9596
|
+
* signal.
|
|
9443
9597
|
*/
|
|
9444
9598
|
export interface GraphFacts {
|
|
9445
9599
|
/**
|
|
9446
|
-
* Number of exports added by the changeset.
|
|
9600
|
+
* Number of public API exports added by the changeset. Zero means the
|
|
9601
|
+
* changeset adds no public API exports.
|
|
9447
9602
|
*/
|
|
9448
9603
|
exports_added: number
|
|
9449
9604
|
/**
|
|
9450
|
-
*
|
|
9451
|
-
*
|
|
9605
|
+
* Widening-only public API delta, currently equal to `exports_added`.
|
|
9606
|
+
* Removed exports are not represented, so zero means no public API exports
|
|
9607
|
+
* were added.
|
|
9452
9608
|
*/
|
|
9453
9609
|
api_width_delta: number
|
|
9454
9610
|
/**
|
|
@@ -9696,108 +9852,6 @@ expert: string[]
|
|
|
9696
9852
|
*/
|
|
9697
9853
|
bus_factor_one?: boolean
|
|
9698
9854
|
}
|
|
9699
|
-
/**
|
|
9700
|
-
* The ranked, capped decision surface plus the set of signal_ids the
|
|
9701
|
-
* deterministic layer emitted (the anti-hallucination allowlist).
|
|
9702
|
-
*/
|
|
9703
|
-
export interface DecisionSurface {
|
|
9704
|
-
/**
|
|
9705
|
-
* Ranked decisions, highest consequence first.
|
|
9706
|
-
*/
|
|
9707
|
-
decisions: Decision[]
|
|
9708
|
-
/**
|
|
9709
|
-
* Present when more than the cap were extracted.
|
|
9710
|
-
*/
|
|
9711
|
-
truncated?: (TruncationNote | null)
|
|
9712
|
-
/**
|
|
9713
|
-
* Every signal_id the deterministic layer emitted, INCLUDING those whose
|
|
9714
|
-
* decision was collapsed below the cap or suppressed. The anti-hallucination
|
|
9715
|
-
* allowlist: an agent decision whose id is absent is rejected.
|
|
9716
|
-
*/
|
|
9717
|
-
emitted_signal_ids: string[]
|
|
9718
|
-
}
|
|
9719
|
-
/**
|
|
9720
|
-
* One consequential structural decision, framed as a judgment question for a
|
|
9721
|
-
* human with taste, anchored to a fallow-emitted signal.
|
|
9722
|
-
*/
|
|
9723
|
-
export interface Decision {
|
|
9724
|
-
/**
|
|
9725
|
-
* Deterministic anchor to the fallow-emitted candidate this decision frames.
|
|
9726
|
-
* `accept_signal_id` rejects any id not in the emitted set.
|
|
9727
|
-
*/
|
|
9728
|
-
signal_id: string
|
|
9729
|
-
category: DecisionCategory
|
|
9730
|
-
/**
|
|
9731
|
-
* The decision framed as a judgment question for the human.
|
|
9732
|
-
*/
|
|
9733
|
-
question: string
|
|
9734
|
-
/**
|
|
9735
|
-
* Root-relative file the decision is anchored at.
|
|
9736
|
-
*/
|
|
9737
|
-
anchor_file: string
|
|
9738
|
-
/**
|
|
9739
|
-
* 1-based anchor line, when the underlying signal carries one (0 = file head).
|
|
9740
|
-
*/
|
|
9741
|
-
anchor_line: number
|
|
9742
|
-
/**
|
|
9743
|
-
* The raw fallow-emitted candidate key the `signal_id` hashes.
|
|
9744
|
-
*/
|
|
9745
|
-
signal_key: string
|
|
9746
|
-
/**
|
|
9747
|
-
* The `signal_id` this decision WOULD have had before any rename in this
|
|
9748
|
-
* change (the anchor file's pre-rename path). Present only when the anchor was
|
|
9749
|
-
* renamed. A review-memory layer carries a dismissal across a `git mv`: if
|
|
9750
|
-
* `previous_signal_id` was dismissed in an earlier PR, treat this decision as
|
|
9751
|
-
* dismissed too. Keeps `signal_id` itself exact + deterministic.
|
|
9752
|
-
*/
|
|
9753
|
-
previous_signal_id?: (string | null)
|
|
9754
|
-
/**
|
|
9755
|
-
* Blast radius: count of modules affected beyond the diff by this decision.
|
|
9756
|
-
*/
|
|
9757
|
-
blast: number
|
|
9758
|
-
/**
|
|
9759
|
-
* `blast * reversibility_weight`: the rank key (sorted descending).
|
|
9760
|
-
*/
|
|
9761
|
-
consequence: number
|
|
9762
|
-
/**
|
|
9763
|
-
* The routed expert(s) to ask, from ownership routing. Empty when no
|
|
9764
|
-
* ownership signal is available for the anchor file.
|
|
9765
|
-
*/
|
|
9766
|
-
expert: string[]
|
|
9767
|
-
/**
|
|
9768
|
-
* Whether the anchor file's only qualified owner is one person.
|
|
9769
|
-
*/
|
|
9770
|
-
bus_factor_one?: boolean
|
|
9771
|
-
/**
|
|
9772
|
-
* Honest per-decision count: in-repo modules OUTSIDE the diff that already
|
|
9773
|
-
* depend on this decision's anchor. This is the DISPLAY number (taste
|
|
9774
|
-
* ownership: the human reads reversibility from the count itself), distinct
|
|
9775
|
-
* from `blast` (the project-wide proxy used only for ranking). Never a door
|
|
9776
|
-
* label. Internal-only by construction, so it cannot see a published library's
|
|
9777
|
-
* external consumers; the public-API trade-off clause names that risk in prose.
|
|
9778
|
-
*/
|
|
9779
|
-
internal_consumer_count: number
|
|
9780
|
-
/**
|
|
9781
|
-
* The named structural sacrifice this change makes, stated as a fact, never a
|
|
9782
|
-
* recommendation (e.g. "Couples `app` to `infra`; 4 in-repo modules already
|
|
9783
|
-
* depend on this anchor."). A sibling fact to `question`; it never tells the
|
|
9784
|
-
* human what to choose.
|
|
9785
|
-
*/
|
|
9786
|
-
tradeoff: string
|
|
9787
|
-
}
|
|
9788
|
-
/**
|
|
9789
|
-
* A note for decisions collapsed below the cap.
|
|
9790
|
-
*/
|
|
9791
|
-
export interface TruncationNote {
|
|
9792
|
-
/**
|
|
9793
|
-
* How many decisions were collapsed below the cap.
|
|
9794
|
-
*/
|
|
9795
|
-
collapsed: number
|
|
9796
|
-
/**
|
|
9797
|
-
* Human-readable collapse reason.
|
|
9798
|
-
*/
|
|
9799
|
-
reason: string
|
|
9800
|
-
}
|
|
9801
9855
|
/**
|
|
9802
9856
|
* The separable `decision-surface` envelope: the single call that puts taste-
|
|
9803
9857
|
* decisions in front of a human, callable WITHOUT the full pipeline (the
|
|
@@ -9954,6 +10008,37 @@ agent_schema: AgentSchema
|
|
|
9954
10008
|
*/
|
|
9955
10009
|
injection_note: string
|
|
9956
10010
|
}
|
|
10011
|
+
/**
|
|
10012
|
+
* The full `fallow audit --brief --format json` envelope. Carries the
|
|
10013
|
+
* informational verdict, the triage and graph-facts orientation stages, plus
|
|
10014
|
+
* the reused "subtract" section (the same dead-code / duplication / complexity
|
|
10015
|
+
* payload `fallow audit --format json` emits).
|
|
10016
|
+
*/
|
|
10017
|
+
export interface ReviewBriefOutput {
|
|
10018
|
+
schema_version: ReviewBriefSchemaVersion
|
|
10019
|
+
/**
|
|
10020
|
+
* Fallow CLI version that produced this output.
|
|
10021
|
+
*/
|
|
10022
|
+
version: string
|
|
10023
|
+
/**
|
|
10024
|
+
* Command discriminator singleton: always `"audit-brief"`.
|
|
10025
|
+
*/
|
|
10026
|
+
command: string
|
|
10027
|
+
triage: DiffTriage
|
|
10028
|
+
graph_facts: GraphFacts
|
|
10029
|
+
partition: PartitionFacts
|
|
10030
|
+
impact_closure: ImpactClosureFacts
|
|
10031
|
+
focus: FocusMap
|
|
10032
|
+
deltas: ReviewDeltas
|
|
10033
|
+
/**
|
|
10034
|
+
* 6.F, headline: reviewer-private weakening signals (tests
|
|
10035
|
+
* removed/skipped, thresholds lowered, suppressions added, security steps
|
|
10036
|
+
* removed). Advisory, never gates, never auto-posted.
|
|
10037
|
+
*/
|
|
10038
|
+
weakening: WeakeningSignal[]
|
|
10039
|
+
routing: RoutingFacts
|
|
10040
|
+
decisions: DecisionSurface
|
|
10041
|
+
}
|
|
9957
10042
|
/**
|
|
9958
10043
|
* The review direction artifact: the order to review in, the coherent units,
|
|
9959
10044
|
* and per-unit concern lens + out-of-diff + expert. A minimal projection of the
|
|
@@ -10301,6 +10386,29 @@ export interface CodeClimateLines {
|
|
|
10301
10386
|
*/
|
|
10302
10387
|
begin: number
|
|
10303
10388
|
}
|
|
10389
|
+
/**
|
|
10390
|
+
* Structured JSON error emitted on stdout when `--format json` is active and a
|
|
10391
|
+
* command fails. It carries no `kind` discriminator: it is distinguished from
|
|
10392
|
+
* the kind-tagged success envelopes by the required `error: true` field, and is
|
|
10393
|
+
* a document-root branch alongside `FallowOutput` and `CodeClimateOutput` in
|
|
10394
|
+
* `docs/output-schema.json`. Agents that pass `--format json` and observe a
|
|
10395
|
+
* non-zero exit code parse this shape from stdout.
|
|
10396
|
+
*/
|
|
10397
|
+
export interface ErrorOutput {
|
|
10398
|
+
/**
|
|
10399
|
+
* Always `true`. The discriminator that separates an error document from a
|
|
10400
|
+
* success envelope (which instead carries a `kind`).
|
|
10401
|
+
*/
|
|
10402
|
+
error: boolean
|
|
10403
|
+
/**
|
|
10404
|
+
* Human-readable error message.
|
|
10405
|
+
*/
|
|
10406
|
+
message: string
|
|
10407
|
+
/**
|
|
10408
|
+
* The process exit code the CLI returns alongside this document.
|
|
10409
|
+
*/
|
|
10410
|
+
exit_code: number
|
|
10411
|
+
}
|
|
10304
10412
|
|
|
10305
10413
|
/**
|
|
10306
10414
|
* Inner complexity-violation payload, flattened into `HealthFinding`
|