fallow 2.85.0 → 2.87.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/package.json +9 -9
- package/schema.json +119 -118
- package/skills/fallow/SKILL.md +16 -13
- package/skills/fallow/references/cli-reference.md +89 -22
- package/skills/fallow/references/gotchas.md +1 -1
- package/skills/fallow/references/patterns.md +1 -1
- package/types/output-contract.d.ts +221 -1219
|
@@ -25,33 +25,20 @@
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
* Schemas for the JSON output of fallow commands.
|
|
28
|
+
* Schemas for the JSON output of fallow commands. Object-shaped envelopes covered by the `FallowOutput` contract carry a top-level `kind` discriminator (for example `dead-code`, `dead-code-grouped`, `health`, `dupes`, `combined`, `audit`, `explain`, `impact`, `security`, `coverage-setup`, `coverage-analyze`, `list-boundaries`, `review-envelope`, and `review-reconcile`). Consumers should branch on `kind` instead of probing for unique field presence. `--legacy-envelope` removes only the document-root `kind` for one compatibility cycle. `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.
|
|
29
29
|
*/
|
|
30
30
|
export type FallowJsonOutput = (FallowOutput | CodeClimateOutput)
|
|
31
31
|
/**
|
|
32
|
-
* Typed root of every fallow
|
|
33
|
-
*
|
|
34
|
-
* the document-root `oneOf` in
|
|
35
|
-
*
|
|
32
|
+
* Typed root of every fallow JSON envelope shape that serializes as a JSON
|
|
33
|
+
* object and participates in the documented `FallowOutput` contract. The
|
|
34
|
+
* schema derived from this enum drives the document-root `oneOf` in
|
|
35
|
+
* `docs/output-schema.json`.
|
|
36
36
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* [`DupesOutput`] flatten their inner body (`HealthReport` /
|
|
43
|
-
* `DuplicationReport`) into top-level fields, so the actual
|
|
44
|
-
* discriminators are nested-body keys such as `health_score` (health) and
|
|
45
|
-
* `clone_groups` (dupes), NOT `report` or `groups`.
|
|
46
|
-
*
|
|
47
|
-
* Variant order is **most-specific first**. Schemars 1 preserves
|
|
48
|
-
* declaration order in the emitted `oneOf`, and validators that enforce
|
|
49
|
-
* strict `oneOf` (and any future migration that adds `Deserialize`) will
|
|
50
|
-
* try branches top-to-bottom. The required-field sets shrink as we move
|
|
51
|
-
* down the list, with [`CombinedOutput`] last because its three required
|
|
52
|
-
* fields (`schema_version`, `version`, `elapsed_ms`) are a strict subset
|
|
53
|
-
* of every other variant's required set; placing it earlier would let a
|
|
54
|
-
* `CheckOutput` payload silently match `CombinedOutput` first.
|
|
37
|
+
* The default wire shape now carries a top-level `kind` discriminator so
|
|
38
|
+
* agents and schema-validating clients can select the variant in O(1) instead
|
|
39
|
+
* of probing for unique field presence. `--legacy-envelope` is a one-cycle
|
|
40
|
+
* compatibility flag that removes only this document-root `kind` field from
|
|
41
|
+
* CLI JSON output; nested report objects are not rewritten.
|
|
55
42
|
*
|
|
56
43
|
* One envelope is intentionally NOT in this enum:
|
|
57
44
|
* - `CodeClimateOutput` serializes as a bare JSON array
|
|
@@ -59,13 +46,36 @@ export type FallowJsonOutput = (FallowOutput | CodeClimateOutput)
|
|
|
59
46
|
* spec; `#[serde(tag = ...)]` cannot internally tag a non-object
|
|
60
47
|
* variant and wrapping the array would break the spec. The root schema
|
|
61
48
|
* carries it as a sibling `oneOf` branch alongside `FallowOutput`.
|
|
62
|
-
*
|
|
63
|
-
* A future major release plans to switch this to
|
|
64
|
-
* `#[serde(tag = "kind")]` for true O(1) discriminability on AI / agent
|
|
65
|
-
* consumers, paired with a one-cycle `--legacy-envelope` opt-out flag.
|
|
66
|
-
* Tracked under issue #384.
|
|
67
49
|
*/
|
|
68
|
-
export type FallowOutput = (AuditOutput
|
|
50
|
+
export type FallowOutput = ((AuditOutput & {
|
|
51
|
+
kind: "audit"
|
|
52
|
+
}) | (ExplainOutput & {
|
|
53
|
+
kind: "explain"
|
|
54
|
+
}) | (ReviewEnvelopeOutput & {
|
|
55
|
+
kind: "review-envelope"
|
|
56
|
+
}) | (ReviewReconcileOutput & {
|
|
57
|
+
kind: "review-reconcile"
|
|
58
|
+
}) | (CoverageSetupOutput & {
|
|
59
|
+
kind: "coverage-setup"
|
|
60
|
+
}) | (CoverageAnalyzeOutput & {
|
|
61
|
+
kind: "coverage-analyze"
|
|
62
|
+
}) | (ListBoundariesOutput & {
|
|
63
|
+
kind: "list-boundaries"
|
|
64
|
+
}) | (HealthOutput & {
|
|
65
|
+
kind: "health"
|
|
66
|
+
}) | (DupesOutput & {
|
|
67
|
+
kind: "dupes"
|
|
68
|
+
}) | (CheckGroupedOutput & {
|
|
69
|
+
kind: "dead-code-grouped"
|
|
70
|
+
}) | (ImpactReport & {
|
|
71
|
+
kind: "impact"
|
|
72
|
+
}) | (SecurityOutput & {
|
|
73
|
+
kind: "security"
|
|
74
|
+
}) | (CheckOutput & {
|
|
75
|
+
kind: "dead-code"
|
|
76
|
+
}) | (CombinedOutput & {
|
|
77
|
+
kind: "combined"
|
|
78
|
+
}))
|
|
69
79
|
/**
|
|
70
80
|
* Schema version for this output format (independent of tool version). Bump
|
|
71
81
|
* policy: ADDITIVE changes (new optional top-level fields, new optional struct
|
|
@@ -87,15 +97,12 @@ export type FallowOutput = (AuditOutput | ExplainOutput | ReviewEnvelopeOutput |
|
|
|
87
97
|
* additive examples: dupes --group-by adds optional grouped_by, total_issues,
|
|
88
98
|
* groups fields without bumping.
|
|
89
99
|
*/
|
|
90
|
-
export type SchemaVersion =
|
|
100
|
+
export type SchemaVersion = 7
|
|
91
101
|
/**
|
|
92
102
|
* Fallow CLI version that produced this envelope. Renders to the JSON wire as
|
|
93
103
|
* a bare string (e.g. `"2.74.0"`).
|
|
94
104
|
*/
|
|
95
105
|
export type ToolVersion = string
|
|
96
|
-
/**
|
|
97
|
-
* Singleton `command` discriminator for [`AuditOutput`].
|
|
98
|
-
*/
|
|
99
106
|
export type AuditCommand = "audit"
|
|
100
107
|
/**
|
|
101
108
|
* Verdict for the audit command.
|
|
@@ -106,9 +113,6 @@ export type AuditVerdict = ("pass" | "warn" | "fail")
|
|
|
106
113
|
* integer.
|
|
107
114
|
*/
|
|
108
115
|
export type ElapsedMs = number
|
|
109
|
-
/**
|
|
110
|
-
* Gating mode for `fallow audit`.
|
|
111
|
-
*/
|
|
112
116
|
export type AuditGate = ("new-only" | "all")
|
|
113
117
|
/**
|
|
114
118
|
* A suggested action attached to a finding in the JSON output. Each finding
|
|
@@ -220,17 +224,6 @@ export type AuditIntroduced = boolean
|
|
|
220
224
|
export type DependencyLocation = ("dependencies" | "devDependencies" | "optionalDependencies")
|
|
221
225
|
/**
|
|
222
226
|
* The kind of member.
|
|
223
|
-
*
|
|
224
|
-
* # Examples
|
|
225
|
-
*
|
|
226
|
-
* ```
|
|
227
|
-
* use fallow_types::extract::MemberKind;
|
|
228
|
-
*
|
|
229
|
-
* let kind = MemberKind::EnumMember;
|
|
230
|
-
* assert_eq!(kind, MemberKind::EnumMember);
|
|
231
|
-
* assert_ne!(kind, MemberKind::ClassMethod);
|
|
232
|
-
* assert_ne!(MemberKind::ClassMethod, MemberKind::ClassProperty);
|
|
233
|
-
* ```
|
|
234
227
|
*/
|
|
235
228
|
export type MemberKind = ("enum_member" | "class_method" | "class_property" | "namespace_member")
|
|
236
229
|
/**
|
|
@@ -354,43 +347,10 @@ export type ExceededThreshold = ("cyclomatic" | "cognitive" | "both" | "crap" |
|
|
|
354
347
|
export type FindingSeverity = ("moderate" | "high" | "critical")
|
|
355
348
|
/**
|
|
356
349
|
* Coverage tier classification for CRAP findings.
|
|
357
|
-
*
|
|
358
|
-
* Bucketed coverage signal that lets action consumers (AI agents, IDE
|
|
359
|
-
* extensions, CI integrations) pick the right remediation without knowing
|
|
360
|
-
* the underlying coverage values:
|
|
361
|
-
* - `None`: file has no test reachability (estimated model 0% band) or
|
|
362
|
-
* Istanbul data shows 0% statement coverage. The right action is
|
|
363
|
-
* "add tests from scratch."
|
|
364
|
-
* - `Partial`: some coverage exists (estimated model 40% band, or
|
|
365
|
-
* Istanbul shows >0% but below the high watermark). The right
|
|
366
|
-
* action is "increase coverage on uncovered branches."
|
|
367
|
-
* - `High`: coverage is at or above the high watermark (estimated model
|
|
368
|
-
* 85% band, or Istanbul shows >= 70%). Action selection still checks
|
|
369
|
-
* the CRAP formula before deciding whether coverage or refactoring is
|
|
370
|
-
* the better remediation.
|
|
371
|
-
*
|
|
372
|
-
* The high watermark default is 70 (matches Istanbul `lines: 70`).
|
|
373
|
-
* Partial is anything in `(0, 70)`. None is `<= 0`.
|
|
374
350
|
*/
|
|
375
351
|
export type CoverageTier = ("none" | "partial" | "high")
|
|
376
352
|
/**
|
|
377
353
|
* Provenance of a CRAP finding's coverage signal.
|
|
378
|
-
*
|
|
379
|
-
* Discriminates whether the `coverage_tier` and `crap` score were derived
|
|
380
|
-
* from real Istanbul data, the graph-based estimated model evaluated against
|
|
381
|
-
* the finding's own file, or the graph-based estimated model evaluated
|
|
382
|
-
* against a different file (today: an Angular component `.ts` reached via
|
|
383
|
-
* the inverse `templateUrl` edge from a synthetic `<template>` finding on
|
|
384
|
-
* the component's `.html` template).
|
|
385
|
-
*
|
|
386
|
-
* Consumers reading this field:
|
|
387
|
-
* - AI agents picking remediation actions ("the score is inherited, the fix
|
|
388
|
-
* may need to land on the component file, not the template").
|
|
389
|
-
* - Dashboards plotting CRAP trends ("the discriminator changed shape;
|
|
390
|
-
* absorb the rollout rather than flagging a step change").
|
|
391
|
-
* - Future tier 2 (AOT source-map back-mapping) will introduce
|
|
392
|
-
* `MeasuredAotSourceMap` so consumers can distinguish measured-AOT from
|
|
393
|
-
* inherited-JIT without parsing the score itself.
|
|
394
354
|
*/
|
|
395
355
|
export type CoverageSource = ("istanbul" | "estimated" | "estimated_component_inherited")
|
|
396
356
|
/**
|
|
@@ -406,6 +366,10 @@ export type HealthFindingActionType = ("refactor-function" | "add-tests" | "incr
|
|
|
406
366
|
* Coverage model used for CRAP score computation.
|
|
407
367
|
*/
|
|
408
368
|
export type CoverageModel = ("static_binary" | "static_estimated" | "istanbul")
|
|
369
|
+
/**
|
|
370
|
+
* Whether CRAP findings in the report used one coverage-source kind or a mix.
|
|
371
|
+
*/
|
|
372
|
+
export type CoverageSourceConsistency = ("uniform" | "mixed")
|
|
409
373
|
/**
|
|
410
374
|
* Discriminant for [`UntestedFileAction::kind`]. Mirrors the action types
|
|
411
375
|
* emitted by `build_untested_file_actions`.
|
|
@@ -420,13 +384,7 @@ export type UntestedExportActionType = ("add-test-import" | "suppress-file")
|
|
|
420
384
|
* Churn trend indicator based on comparing recent vs older halves of the analysis period.
|
|
421
385
|
*/
|
|
422
386
|
export type ChurnTrend = ("accelerating" | "stable" | "cooling")
|
|
423
|
-
/**
|
|
424
|
-
* Format discriminator for [`ContributorEntry::identifier`].
|
|
425
|
-
*/
|
|
426
387
|
export type ContributorIdentifierFormat = ("raw" | "handle" | "anonymized" | "hash")
|
|
427
|
-
/**
|
|
428
|
-
* Machine-readable ownership state for a hotspot.
|
|
429
|
-
*/
|
|
430
388
|
export type OwnershipState = ("active" | "unowned" | "declared_inactive" | "drifting")
|
|
431
389
|
/**
|
|
432
390
|
* Discriminant for [`HotspotAction::kind`].
|
|
@@ -594,34 +552,13 @@ export type ReviewCheckConclusion = ("success" | "neutral" | "failure")
|
|
|
594
552
|
* Schema-version discriminator for the review reconcile envelope.
|
|
595
553
|
*/
|
|
596
554
|
export type ReviewReconcileSchema = "fallow-review-reconcile/v1"
|
|
597
|
-
/**
|
|
598
|
-
* Singleton schema-version discriminator for [`CoverageSetupOutput`].
|
|
599
|
-
*/
|
|
600
555
|
export type CoverageSetupSchemaVersion = "1"
|
|
601
|
-
/**
|
|
602
|
-
* Framework label inside coverage setup output.
|
|
603
|
-
*/
|
|
604
556
|
export type CoverageSetupFramework = ("nextjs" | "nestjs" | "nuxt" | "sveltekit" | "astro" | "remix" | "vite" | "plain_node" | "unknown")
|
|
605
|
-
/**
|
|
606
|
-
* Package manager label inside coverage setup output.
|
|
607
|
-
*/
|
|
608
557
|
export type CoverageSetupPackageManager = ("npm" | "pnpm" | "yarn" | "bun")
|
|
609
|
-
/**
|
|
610
|
-
* Runtime target inside coverage setup output.
|
|
611
|
-
*/
|
|
612
558
|
export type CoverageSetupRuntimeTarget = ("node" | "browser")
|
|
613
|
-
/**
|
|
614
|
-
* Singleton schema-version discriminator for [`CoverageAnalyzeOutput`].
|
|
615
|
-
* Independent from the global [`SchemaVersion`] because the runtime
|
|
616
|
-
* coverage envelope versions independently from the rest of the
|
|
617
|
-
* JSON contract.
|
|
618
|
-
*/
|
|
619
559
|
export type CoverageAnalyzeSchemaVersion = "1"
|
|
620
560
|
/**
|
|
621
|
-
* Discovery outcome for a [`LogicalGroup`].
|
|
622
|
-
* "the directory exists and is empty" versus "at least one `autoDiscover`
|
|
623
|
-
* path was invalid or unreadable", so consumers can render an actionable
|
|
624
|
-
* hint instead of "0 children, mystery".
|
|
561
|
+
* Discovery outcome for a [`LogicalGroup`].
|
|
625
562
|
*/
|
|
626
563
|
export type LogicalGroupStatus = ("ok" | "empty" | "invalid_path")
|
|
627
564
|
/**
|
|
@@ -645,6 +582,20 @@ export type ImpactReportSchemaVersion = "1"
|
|
|
645
582
|
* Direction of a count trend between two recorded runs.
|
|
646
583
|
*/
|
|
647
584
|
export type ImpactTrendDirection = ("improving" | "declining" | "stable")
|
|
585
|
+
/**
|
|
586
|
+
* The `fallow security --format json` schema version. Independently versioned
|
|
587
|
+
* from the main contract, mirroring `ImpactReportSchemaVersion`.
|
|
588
|
+
*/
|
|
589
|
+
export type SecuritySchemaVersion = "1"
|
|
590
|
+
/**
|
|
591
|
+
* The kind of security candidate. Findings are CANDIDATES for downstream agent
|
|
592
|
+
* verification, NOT verified vulnerabilities.
|
|
593
|
+
*/
|
|
594
|
+
export type SecurityFindingKind = ("client-server-leak" | "tainted-sink")
|
|
595
|
+
/**
|
|
596
|
+
* The role a hop plays in a security finding's structural import trace.
|
|
597
|
+
*/
|
|
598
|
+
export type TraceHopRole = ("client-boundary" | "intermediate" | "secret-source" | "sink")
|
|
648
599
|
/**
|
|
649
600
|
* Discriminator value for [`CodeClimateIssue::kind`].
|
|
650
601
|
*/
|
|
@@ -661,69 +612,22 @@ export type CodeClimateSeverity = ("info" | "minor" | "major" | "critical" | "bl
|
|
|
661
612
|
export type CodeClimateOutput = CodeClimateIssue[]
|
|
662
613
|
|
|
663
614
|
/**
|
|
664
|
-
*
|
|
665
|
-
* complexity, and duplication scoped to changed files with a verdict
|
|
666
|
-
* (`pass` / `warn` / `fail`), a per-category summary, optional
|
|
667
|
-
* new-vs-inherited attribution, and full sub-results.
|
|
668
|
-
*
|
|
669
|
-
* Like [`CombinedOutput`], `audit`'s `duplication` and `complexity`
|
|
670
|
-
* sub-keys hold body shapes rather than per-command envelopes:
|
|
671
|
-
* `duplication` is [`DupesReportPayload`] (the typed wrapper payload
|
|
672
|
-
* emitted via `crate::output_dupes::DupesReportPayload::from_report`),
|
|
673
|
-
* `complexity` is [`HealthReport`]. `dead_code` is the full
|
|
674
|
-
* [`CheckOutput`] envelope. The committed schema points `duplication`
|
|
675
|
-
* at `#/definitions/DupesReportPayload` and `complexity` at
|
|
676
|
-
* `#/definitions/HealthReport` so the documented shape matches the
|
|
677
|
-
* wire; the `committed_property_refs_match_derived_property_refs`
|
|
678
|
-
* drift test enforces the alignment.
|
|
615
|
+
* `fallow audit --format json` envelope.
|
|
679
616
|
*/
|
|
680
617
|
export interface AuditOutput {
|
|
681
618
|
schema_version: SchemaVersion
|
|
682
619
|
version: ToolVersion
|
|
683
620
|
command: AuditCommand
|
|
684
621
|
verdict: AuditVerdict
|
|
685
|
-
/**
|
|
686
|
-
* Number of files changed between base ref and HEAD.
|
|
687
|
-
*/
|
|
688
622
|
changed_files_count: number
|
|
689
|
-
/**
|
|
690
|
-
* Git ref used as comparison base (explicit or auto-detected).
|
|
691
|
-
*/
|
|
692
623
|
base_ref: string
|
|
693
|
-
/**
|
|
694
|
-
* Short SHA of HEAD. Omitted when git is unavailable.
|
|
695
|
-
*/
|
|
696
624
|
head_sha?: (string | null)
|
|
697
625
|
elapsed_ms: ElapsedMs
|
|
698
|
-
/**
|
|
699
|
-
* Only emitted when --performance is set. true means audit reused the
|
|
700
|
-
* current run's keys as the base snapshot because every changed file was
|
|
701
|
-
* either a non-behavioral doc or token-equivalent at the base ref (the
|
|
702
|
-
* docs-only-diff fast path); false means the regular base worktree
|
|
703
|
-
* analysis ran.
|
|
704
|
-
*/
|
|
705
626
|
base_snapshot_skipped?: (boolean | null)
|
|
706
627
|
summary: AuditSummary
|
|
707
628
|
attribution: AuditAttribution
|
|
708
|
-
/**
|
|
709
|
-
* Full dead code results (omitted if no changed files). Issue objects
|
|
710
|
-
* include introduced: true/false when audit can compare against the base
|
|
711
|
-
* ref.
|
|
712
|
-
*/
|
|
713
629
|
dead_code?: (CheckOutput | null)
|
|
714
|
-
/**
|
|
715
|
-
* Full duplication results (omitted if no changed files). Clone groups
|
|
716
|
-
* include introduced: true/false when audit can compare against the base
|
|
717
|
-
* ref. Carries typed [`crate::output_dupes::CloneGroupFinding`] and
|
|
718
|
-
* [`crate::output_dupes::CloneFamilyFinding`] wrappers (matches what
|
|
719
|
-
* `crates/cli/src/audit.rs` emits via
|
|
720
|
-
* `crate::output_dupes::DupesReportPayload::from_report`).
|
|
721
|
-
*/
|
|
722
630
|
duplication?: (DupesReportPayload | null)
|
|
723
|
-
/**
|
|
724
|
-
* Full complexity results (omitted if no changed files). Findings include
|
|
725
|
-
* introduced: true/false when audit can compare against the base ref.
|
|
726
|
-
*/
|
|
727
631
|
complexity?: (HealthReport | null)
|
|
728
632
|
}
|
|
729
633
|
/**
|
|
@@ -763,14 +667,7 @@ export interface CheckOutput {
|
|
|
763
667
|
schema_version: SchemaVersion
|
|
764
668
|
version: ToolVersion
|
|
765
669
|
elapsed_ms: ElapsedMs
|
|
766
|
-
/**
|
|
767
|
-
* Total number of issues found across all categories.
|
|
768
|
-
*/
|
|
769
670
|
total_issues: number
|
|
770
|
-
/**
|
|
771
|
-
* Entry-point detection summary. Present when the analysis populated
|
|
772
|
-
* the metadata block; absent in synthesised fixtures.
|
|
773
|
-
*/
|
|
774
671
|
entry_points?: (EntryPoints | null)
|
|
775
672
|
summary: CheckSummary
|
|
776
673
|
/**
|
|
@@ -925,34 +822,10 @@ unused_dependency_overrides?: UnusedDependencyOverrideFinding[]
|
|
|
925
822
|
* error. Wrapped in [`MisconfiguredDependencyOverrideFinding`].
|
|
926
823
|
*/
|
|
927
824
|
misconfigured_dependency_overrides?: MisconfiguredDependencyOverrideFinding[]
|
|
928
|
-
/**
|
|
929
|
-
* Per-category delta comparison against a saved baseline. Only present
|
|
930
|
-
* when `--baseline` is used (today only via the combined invocation).
|
|
931
|
-
*/
|
|
932
825
|
baseline_deltas?: (BaselineDeltas | null)
|
|
933
|
-
/**
|
|
934
|
-
* Baseline match statistics. Only present when `--baseline` is used.
|
|
935
|
-
*/
|
|
936
826
|
baseline?: (BaselineMatch | null)
|
|
937
|
-
/**
|
|
938
|
-
* Regression check result. Only present when `--fail-on-regression` is
|
|
939
|
-
* used.
|
|
940
|
-
*/
|
|
941
827
|
regression?: (RegressionResult | null)
|
|
942
|
-
/**
|
|
943
|
-
* `_meta` block with metric / rule definitions, emitted when `--explain`
|
|
944
|
-
* is passed (always present in MCP responses).
|
|
945
|
-
*/
|
|
946
828
|
_meta?: (Meta | null)
|
|
947
|
-
/**
|
|
948
|
-
* Workspace-discovery diagnostics surfaced by
|
|
949
|
-
* `discover_workspaces_with_diagnostics` (issue #473): malformed
|
|
950
|
-
* declared-workspace `package.json`, glob matches with no `package.json`,
|
|
951
|
-
* malformed `tsconfig.json`, missing tsconfig reference paths. Omitted
|
|
952
|
-
* when empty so consumers on monorepos without discovery noise see no
|
|
953
|
-
* new field. Pairing of `#[serde(default, skip_serializing_if = ...)]`
|
|
954
|
-
* is required for schemars to mark the field non-required.
|
|
955
|
-
*/
|
|
956
829
|
workspace_diagnostics?: WorkspaceDiagnostic[]
|
|
957
830
|
}
|
|
958
831
|
/**
|
|
@@ -2643,177 +2516,41 @@ actions_meta?: (HealthActionsMeta | null)
|
|
|
2643
2516
|
}
|
|
2644
2517
|
/**
|
|
2645
2518
|
* Wire envelope for a single complexity finding.
|
|
2646
|
-
*
|
|
2647
|
-
* Flattens [`ComplexityViolation`] for wire continuity and adds the typed
|
|
2648
|
-
* `actions` list plus the audit-mode `introduced` flag. The
|
|
2649
|
-
* `#[serde(flatten)]` keeps each `findings[]` item byte-identical to the
|
|
2650
|
-
* pre-wrapper shape: inner fields (`path`, `name`, `line`, `cyclomatic`,
|
|
2651
|
-
* ...) sit at the top level alongside `actions` and optional `introduced`.
|
|
2652
|
-
*
|
|
2653
|
-
* Construct via [`HealthFinding::with_actions`] in the typical health
|
|
2654
|
-
* pipeline (the wrapper computes its own `actions` from a
|
|
2655
|
-
* [`HealthActionContext`]) or via [`HealthFinding::new`] when the caller
|
|
2656
|
-
* already has the action list (e.g., tests, audit cross-attribution).
|
|
2657
2519
|
*/
|
|
2658
2520
|
export interface HealthFinding {
|
|
2659
|
-
/**
|
|
2660
|
-
* Absolute file path.
|
|
2661
|
-
*/
|
|
2662
2521
|
path: string
|
|
2663
|
-
/**
|
|
2664
|
-
* Function name, `"<anonymous>"` for unnamed functions/arrows, or
|
|
2665
|
-
* `"<template>"` for synthetic Angular template findings.
|
|
2666
|
-
*/
|
|
2667
2522
|
name: string
|
|
2668
|
-
/**
|
|
2669
|
-
* 1-based line number.
|
|
2670
|
-
*/
|
|
2671
2523
|
line: number
|
|
2672
|
-
/**
|
|
2673
|
-
* 0-based column.
|
|
2674
|
-
*/
|
|
2675
2524
|
col: number
|
|
2676
|
-
/**
|
|
2677
|
-
* Cyclomatic complexity.
|
|
2678
|
-
*/
|
|
2679
2525
|
cyclomatic: number
|
|
2680
|
-
/**
|
|
2681
|
-
* SonarSource cognitive complexity (structural + nesting penalty).
|
|
2682
|
-
*/
|
|
2683
2526
|
cognitive: number
|
|
2684
|
-
/**
|
|
2685
|
-
* Number of lines in the function.
|
|
2686
|
-
*/
|
|
2687
2527
|
line_count: number
|
|
2688
|
-
/**
|
|
2689
|
-
* Number of parameters (excluding TypeScript's this parameter).
|
|
2690
|
-
*/
|
|
2691
2528
|
param_count: number
|
|
2692
2529
|
exceeded: ExceededThreshold
|
|
2693
2530
|
severity: FindingSeverity
|
|
2694
|
-
/**
|
|
2695
|
-
* CRAP score (`CC^2 * (1 - cov/100)^3 + CC`), rounded to one decimal.
|
|
2696
|
-
* Present when the function also exceeded `--max-crap`, otherwise absent.
|
|
2697
|
-
*/
|
|
2698
2531
|
crap?: (number | null)
|
|
2699
|
-
/**
|
|
2700
|
-
* Per-function statement coverage percentage (0.0 to 100.0) used to
|
|
2701
|
-
* derive `crap`. Present when Istanbul data matched the function,
|
|
2702
|
-
* otherwise absent (estimated model or unmatched functions).
|
|
2703
|
-
*/
|
|
2704
2532
|
coverage_pct?: (number | null)
|
|
2705
|
-
/**
|
|
2706
|
-
* Bucketed coverage tier used to drive action selection. Present whenever
|
|
2707
|
-
* CRAP triggered the finding (Istanbul or estimated), absent otherwise.
|
|
2708
|
-
* `none` = coverage is at most 0% (file not test-reachable, or Istanbul
|
|
2709
|
-
* reports 0); `partial` = coverage is in `(0, 70)`; `high` = coverage is
|
|
2710
|
-
* at or above the high watermark (default `>= 70`, or the estimated 85%
|
|
2711
|
-
* band).
|
|
2712
|
-
*/
|
|
2713
2533
|
coverage_tier?: (CoverageTier | null)
|
|
2714
|
-
/**
|
|
2715
|
-
* Provenance of the coverage signal. Present whenever CRAP triggered the
|
|
2716
|
-
* finding. `istanbul` = direct fnMap match; `estimated` = graph-based
|
|
2717
|
-
* estimate against the finding's own file; `estimated_component_inherited`
|
|
2718
|
-
* = graph-based estimate inherited from an Angular component `.ts`
|
|
2719
|
-
* reached via the inverse `templateUrl` edge (synthetic `<template>`
|
|
2720
|
-
* findings on `.html` files only).
|
|
2721
|
-
*/
|
|
2722
2534
|
coverage_source?: (CoverageSource | null)
|
|
2723
|
-
/**
|
|
2724
|
-
* Owning component file that contributed reachability when
|
|
2725
|
-
* `coverage_source == "estimated_component_inherited"`. Always paired
|
|
2726
|
-
* with that variant of `coverage_source` and absent otherwise. The
|
|
2727
|
-
* value is the `.ts` file fallow walked to via the inverse `templateUrl`
|
|
2728
|
-
* edge (e.g. `permissions.component.ts`); the JSON serializer strips it
|
|
2729
|
-
* to project-relative form just like other path fields. Lets human and
|
|
2730
|
-
* AI consumers explain "the template scored partial because the
|
|
2731
|
-
* component it belongs to is tested" without re-deriving the link.
|
|
2732
|
-
*/
|
|
2733
2535
|
inherited_from?: (string | null)
|
|
2734
|
-
/**
|
|
2735
|
-
* Breakdown of a synthetic `<component>` rollup finding into its
|
|
2736
|
-
* worst-class-function and template contributions. Present only on
|
|
2737
|
-
* findings whose [`name`](Self::name) is the literal string
|
|
2738
|
-
* `"<component>"` (Angular components whose class AND template both
|
|
2739
|
-
* contributed to a per-component complexity rollup); absent on every
|
|
2740
|
-
* other finding kind.
|
|
2741
|
-
*
|
|
2742
|
-
* The owning [`HealthFinding`](crate::health_types::HealthFinding)'s
|
|
2743
|
-
* [`cyclomatic`](Self::cyclomatic) / [`cognitive`](Self::cognitive)
|
|
2744
|
-
* totals are `class_worst_function + template`, so consumers ranking
|
|
2745
|
-
* by complexity see the component as one unit. The breakdown carries
|
|
2746
|
-
* the pre-summation numbers plus the worst class function's name so
|
|
2747
|
-
* consumers can explain "this component ranked high because the
|
|
2748
|
-
* template added 6 cyclomatic on top of the worst class function's 3".
|
|
2749
|
-
*/
|
|
2750
2536
|
component_rollup?: (ComponentRollup | null)
|
|
2751
2537
|
/**
|
|
2752
|
-
* Machine-actionable fix and suppress hints.
|
|
2753
|
-
* empty in the typical pipeline (the action selector emits at least
|
|
2754
|
-
* `suppress-line` or `suppress-file` unless suppressed by the
|
|
2755
|
-
* context).
|
|
2538
|
+
* Machine-actionable fix and suppress hints.
|
|
2756
2539
|
*/
|
|
2757
2540
|
actions: HealthFindingAction[]
|
|
2758
2541
|
/**
|
|
2759
|
-
* Audit-mode flag indicating whether the finding is new versus the
|
|
2760
|
-
*
|
|
2761
|
-
* `Some(false)` when present in both snapshots, `None` outside audit
|
|
2762
|
-
* mode (the field is skipped from the wire).
|
|
2542
|
+
* Audit-mode flag indicating whether the finding is new versus the base
|
|
2543
|
+
* snapshot.
|
|
2763
2544
|
*/
|
|
2764
2545
|
introduced?: (boolean | null)
|
|
2765
2546
|
}
|
|
2766
|
-
/**
|
|
2767
|
-
* Per-component breakdown attached to a synthetic `<component>`
|
|
2768
|
-
* [`HealthFinding`](crate::health_types::HealthFinding). See
|
|
2769
|
-
* [`ComplexityViolation::component_rollup`] for the owning-finding
|
|
2770
|
-
* contract; the wrapper flattens the inner type's
|
|
2771
|
-
* [`component_rollup`](ComplexityViolation::component_rollup) field
|
|
2772
|
-
* onto its own wire shape.
|
|
2773
|
-
*/
|
|
2774
2547
|
export interface ComponentRollup {
|
|
2775
|
-
/**
|
|
2776
|
-
* Angular component class name (e.g. `"HostGameComponent"`). Derived
|
|
2777
|
-
* from the worst class function's `ClassName.methodName` identifier.
|
|
2778
|
-
*/
|
|
2779
2548
|
component: string
|
|
2780
|
-
/**
|
|
2781
|
-
* Name of the worst class function/method whose individual cyclomatic
|
|
2782
|
-
* is the largest among the component's class findings (e.g.
|
|
2783
|
-
* `"ngOnInit"`). When two methods tie on cyclomatic the first by
|
|
2784
|
-
* iteration order wins; consumers should treat the choice as
|
|
2785
|
-
* representative, not authoritative.
|
|
2786
|
-
*/
|
|
2787
2549
|
class_worst_function: string
|
|
2788
|
-
/**
|
|
2789
|
-
* Cyclomatic complexity of the worst class function alone (the
|
|
2790
|
-
* `class_worst_function`).
|
|
2791
|
-
*/
|
|
2792
2550
|
class_cyclomatic: number
|
|
2793
|
-
/**
|
|
2794
|
-
* Cognitive complexity of the worst class function alone.
|
|
2795
|
-
*/
|
|
2796
2551
|
class_cognitive: number
|
|
2797
|
-
/**
|
|
2798
|
-
* Path of the Angular template that contributed to the rollup.
|
|
2799
|
-
* External-template components use the `.html` template file path;
|
|
2800
|
-
* inline-template components use the owning `.ts` itself (since the
|
|
2801
|
-
* `<template>` finding for inline templates is anchored at the
|
|
2802
|
-
* component's `@Component` decorator on the same file). Stored
|
|
2803
|
-
* absolute internally; the JSON output strips it to project-relative
|
|
2804
|
-
* form via the global `strip_root_prefix` post-pass (as with every
|
|
2805
|
-
* other `PathBuf` field in this crate).
|
|
2806
|
-
*/
|
|
2807
2552
|
template_path: string
|
|
2808
|
-
/**
|
|
2809
|
-
* Cyclomatic complexity contributed by the template alone (control
|
|
2810
|
-
* flow on `*ngIf` / `*ngFor` / `@if` / `@for` / `@switch` etc.).
|
|
2811
|
-
*/
|
|
2812
2553
|
template_cyclomatic: number
|
|
2813
|
-
/**
|
|
2814
|
-
* Cognitive complexity contributed by the template alone (nesting +
|
|
2815
|
-
* branching penalty on the same constructs as `template_cyclomatic`).
|
|
2816
|
-
*/
|
|
2817
2554
|
template_cognitive: number
|
|
2818
2555
|
}
|
|
2819
2556
|
/**
|
|
@@ -2882,77 +2619,20 @@ target_path?: (string | null)
|
|
|
2882
2619
|
* Summary statistics for the health report.
|
|
2883
2620
|
*/
|
|
2884
2621
|
export interface HealthSummary {
|
|
2885
|
-
/**
|
|
2886
|
-
* Number of files analyzed.
|
|
2887
|
-
*/
|
|
2888
2622
|
files_analyzed: number
|
|
2889
|
-
/**
|
|
2890
|
-
* Total number of functions found.
|
|
2891
|
-
*/
|
|
2892
2623
|
functions_analyzed: number
|
|
2893
|
-
/**
|
|
2894
|
-
* Number of functions exceeding at least one threshold (before --top
|
|
2895
|
-
* truncation).
|
|
2896
|
-
*/
|
|
2897
2624
|
functions_above_threshold: number
|
|
2898
|
-
/**
|
|
2899
|
-
* Configured cyclomatic threshold.
|
|
2900
|
-
*/
|
|
2901
2625
|
max_cyclomatic_threshold: number
|
|
2902
|
-
/**
|
|
2903
|
-
* Configured cognitive threshold.
|
|
2904
|
-
*/
|
|
2905
2626
|
max_cognitive_threshold: number
|
|
2906
|
-
/**
|
|
2907
|
-
* Configured CRAP (Change Risk Anti-Patterns) score threshold. Functions
|
|
2908
|
-
* meeting or exceeding this score appear as findings with the `crap` and
|
|
2909
|
-
* optional `coverage_pct` fields populated.
|
|
2910
|
-
*/
|
|
2911
2627
|
max_crap_threshold: number
|
|
2912
|
-
/**
|
|
2913
|
-
* Number of files with health scores. Only present when --file-scores is
|
|
2914
|
-
* used. 0 indicates the flag was set but scoring failed.
|
|
2915
|
-
*/
|
|
2916
2628
|
files_scored?: (number | null)
|
|
2917
|
-
/**
|
|
2918
|
-
* Average maintainability index across all scored files (before --top
|
|
2919
|
-
* truncation). Only present when --file-scores is used and at least one
|
|
2920
|
-
* file was scored.
|
|
2921
|
-
*/
|
|
2922
2629
|
average_maintainability?: (number | null)
|
|
2923
|
-
/**
|
|
2924
|
-
* Coverage model used for CRAP score computation. 'static_estimated'
|
|
2925
|
-
* (default) uses per-function graph-based estimation from export
|
|
2926
|
-
* references: directly test-referenced = 85%, indirectly reachable = 40%,
|
|
2927
|
-
* untested = 0%. 'istanbul' uses real per-function statement coverage from
|
|
2928
|
-
* a coverage-final.json file (--coverage flag or auto-detected).
|
|
2929
|
-
* 'static_binary' is the legacy binary model. Only present when file
|
|
2930
|
-
* scores are computed.
|
|
2931
|
-
*/
|
|
2932
2630
|
coverage_model?: (CoverageModel | null)
|
|
2933
|
-
|
|
2934
|
-
* Number of functions matched against Istanbul coverage data.
|
|
2935
|
-
* Only present when `coverage_model` is `istanbul`.
|
|
2936
|
-
*/
|
|
2631
|
+
coverage_source_consistency?: (CoverageSourceConsistency | null)
|
|
2937
2632
|
istanbul_matched?: (number | null)
|
|
2938
|
-
/**
|
|
2939
|
-
* Total functions that could potentially be matched.
|
|
2940
|
-
* Only present when `coverage_model` is `istanbul`.
|
|
2941
|
-
*/
|
|
2942
2633
|
istanbul_total?: (number | null)
|
|
2943
|
-
/**
|
|
2944
|
-
* Number of findings with critical severity (cognitive >= 40 or cyclomatic
|
|
2945
|
-
* >= 50).
|
|
2946
|
-
*/
|
|
2947
2634
|
severity_critical_count: number
|
|
2948
|
-
/**
|
|
2949
|
-
* Number of findings with high severity (cognitive 25-39 or cyclomatic
|
|
2950
|
-
* 30-49).
|
|
2951
|
-
*/
|
|
2952
2635
|
severity_high_count: number
|
|
2953
|
-
/**
|
|
2954
|
-
* Number of findings with moderate severity.
|
|
2955
|
-
*/
|
|
2956
2636
|
severity_moderate_count: number
|
|
2957
2637
|
}
|
|
2958
2638
|
/**
|
|
@@ -3103,173 +2783,43 @@ high_risk: number
|
|
|
3103
2783
|
*/
|
|
3104
2784
|
very_high_risk: number
|
|
3105
2785
|
}
|
|
3106
|
-
/**
|
|
3107
|
-
* Project-level health score. Score = 100 minus available penalties from dead
|
|
3108
|
-
* code, complexity, maintainability, hotspots, unused deps, circular deps,
|
|
3109
|
-
* unit size, coupling, and duplication. Missing metrics do not penalize;
|
|
3110
|
-
* --score computes the score and duplication penalty, while churn-backed
|
|
3111
|
-
* hotspot penalties require hotspot analysis (--hotspots, or --targets with
|
|
3112
|
-
* --score).
|
|
3113
|
-
*/
|
|
3114
2786
|
export interface HealthScore {
|
|
3115
|
-
/**
|
|
3116
|
-
* Health score formula version. Version 2 uses scale-invariant
|
|
3117
|
-
* density/tail metrics for monorepo-safe scoring.
|
|
3118
|
-
*/
|
|
3119
2787
|
formula_version: number
|
|
3120
|
-
/**
|
|
3121
|
-
* Overall score (0-100, higher is better). Reproducible: 100 -
|
|
3122
|
-
* sum(penalties) == score.
|
|
3123
|
-
*/
|
|
3124
2788
|
score: number
|
|
3125
|
-
/**
|
|
3126
|
-
* Letter grade. A: score >= 85, B: 70-84, C: 55-69, D: 40-54, F: below 40.
|
|
3127
|
-
*/
|
|
3128
2789
|
grade: string
|
|
3129
2790
|
penalties: HealthScorePenalties
|
|
3130
2791
|
}
|
|
3131
2792
|
/**
|
|
3132
2793
|
* Per-component penalty breakdown for the health score.
|
|
3133
|
-
*
|
|
3134
|
-
* Each field shows how many points were subtracted for that component.
|
|
3135
|
-
* `None` means the metric was not available (pipeline didn't run).
|
|
3136
2794
|
*/
|
|
3137
2795
|
export interface HealthScorePenalties {
|
|
3138
|
-
/**
|
|
3139
|
-
* Points lost from dead files (max 15). Null if dead code pipeline not
|
|
3140
|
-
* run.
|
|
3141
|
-
*/
|
|
3142
2796
|
dead_files?: (number | null)
|
|
3143
|
-
/**
|
|
3144
|
-
* Points lost from dead exports (max 15). Null if dead code pipeline not
|
|
3145
|
-
* run.
|
|
3146
|
-
*/
|
|
3147
2797
|
dead_exports?: (number | null)
|
|
3148
|
-
/**
|
|
3149
|
-
* Points lost from critical-complexity density (max 20). Older snapshots
|
|
3150
|
-
* without density fields fall back to average cyclomatic complexity above
|
|
3151
|
-
* 1.5.
|
|
3152
|
-
*/
|
|
3153
2798
|
complexity: number
|
|
3154
|
-
/**
|
|
3155
|
-
* Points lost from legacy p90 cyclomatic complexity above 10. Current
|
|
3156
|
-
* scale-invariant runs report 0 because tail complexity is folded into
|
|
3157
|
-
* complexity.
|
|
3158
|
-
*/
|
|
3159
2799
|
p90_complexity: number
|
|
3160
|
-
/**
|
|
3161
|
-
* Points lost from low maintainability index density (max 15).
|
|
3162
|
-
*/
|
|
3163
2800
|
maintainability?: (number | null)
|
|
3164
|
-
/**
|
|
3165
|
-
* Points lost from top-percentile hotspot density (max 10). Null if
|
|
3166
|
-
* hotspots not computed.
|
|
3167
|
-
*/
|
|
3168
2801
|
hotspots?: (number | null)
|
|
3169
|
-
/**
|
|
3170
|
-
* Points lost from unused dependency density (max 25). Null if dead code
|
|
3171
|
-
* pipeline not run.
|
|
3172
|
-
*/
|
|
3173
2802
|
unused_deps?: (number | null)
|
|
3174
|
-
/**
|
|
3175
|
-
* Points lost from circular dependency density (max 25). Null if dead code
|
|
3176
|
-
* pipeline not run.
|
|
3177
|
-
*/
|
|
3178
2803
|
circular_deps?: (number | null)
|
|
3179
|
-
/**
|
|
3180
|
-
* Points lost from oversized-function density (max 10). Null if no
|
|
3181
|
-
* functions analyzed.
|
|
3182
|
-
*/
|
|
3183
2804
|
unit_size?: (number | null)
|
|
3184
|
-
/**
|
|
3185
|
-
* Points lost from coupling concentration density (max 5). Null if file
|
|
3186
|
-
* scores not computed.
|
|
3187
|
-
*/
|
|
3188
2805
|
coupling?: (number | null)
|
|
3189
|
-
/**
|
|
3190
|
-
* Points lost from code duplication (max 10). Penalty = min(max(0,
|
|
3191
|
-
* duplication_pct - 5) * 1, 10). Null if duplication pipeline not run.
|
|
3192
|
-
*/
|
|
3193
2806
|
duplication?: (number | null)
|
|
3194
2807
|
}
|
|
3195
2808
|
/**
|
|
3196
2809
|
* Per-file health score combining complexity, coupling, and dead code metrics.
|
|
3197
|
-
*
|
|
3198
|
-
* Files with zero functions (barrel files, re-export files) are excluded by default.
|
|
3199
|
-
*
|
|
3200
|
-
* ## Maintainability Index Formula
|
|
3201
|
-
*
|
|
3202
|
-
* ```text
|
|
3203
|
-
* dampening = min(lines / 50, 1.0)
|
|
3204
|
-
* fan_out_penalty = min(ln(fan_out + 1) × 4, 15)
|
|
3205
|
-
* maintainability = 100
|
|
3206
|
-
* - (complexity_density × 30 × dampening)
|
|
3207
|
-
* - (dead_code_ratio × 20)
|
|
3208
|
-
* - fan_out_penalty
|
|
3209
|
-
* ```
|
|
3210
|
-
*
|
|
3211
|
-
* Clamped to \[0, 100\]. Higher is better. The dampening factor prevents
|
|
3212
|
-
* complexity density from dominating the score on small files (< 50 lines).
|
|
3213
|
-
*
|
|
3214
|
-
* - **complexity_density**: total cyclomatic complexity / lines of code
|
|
3215
|
-
* - **dead_code_ratio**: fraction of value exports (excluding type-only exports) with zero references (0.0–1.0)
|
|
3216
|
-
* - **fan_out_penalty**: logarithmic scaling with cap at 15 points; reflects diminishing marginal risk of additional imports
|
|
3217
2810
|
*/
|
|
3218
2811
|
export interface FileHealthScore {
|
|
3219
|
-
/**
|
|
3220
|
-
* File path (absolute; stripped to relative in output).
|
|
3221
|
-
*/
|
|
3222
2812
|
path: string
|
|
3223
|
-
/**
|
|
3224
|
-
* Number of files that import this file.
|
|
3225
|
-
*/
|
|
3226
2813
|
fan_in: number
|
|
3227
|
-
/**
|
|
3228
|
-
* Number of files this file imports.
|
|
3229
|
-
*/
|
|
3230
2814
|
fan_out: number
|
|
3231
|
-
/**
|
|
3232
|
-
* Fraction of value exports with zero references (0.0–1.0). Files with no value exports get 0.0.
|
|
3233
|
-
* Type-only exports (interfaces, type aliases) are excluded from both numerator and denominator
|
|
3234
|
-
* to avoid inflating the ratio for well-typed codebases that export props types alongside components.
|
|
3235
|
-
*/
|
|
3236
2815
|
dead_code_ratio: number
|
|
3237
|
-
/**
|
|
3238
|
-
* Total cyclomatic complexity / lines of code.
|
|
3239
|
-
*/
|
|
3240
2816
|
complexity_density: number
|
|
3241
|
-
/**
|
|
3242
|
-
* Weighted composite score (0–100, higher is better).
|
|
3243
|
-
*/
|
|
3244
2817
|
maintainability_index: number
|
|
3245
|
-
/**
|
|
3246
|
-
* Sum of cyclomatic complexity across all functions.
|
|
3247
|
-
*/
|
|
3248
2818
|
total_cyclomatic: number
|
|
3249
|
-
/**
|
|
3250
|
-
* Sum of cognitive complexity across all functions.
|
|
3251
|
-
*/
|
|
3252
2819
|
total_cognitive: number
|
|
3253
|
-
/**
|
|
3254
|
-
* Number of functions in this file.
|
|
3255
|
-
*/
|
|
3256
2820
|
function_count: number
|
|
3257
|
-
/**
|
|
3258
|
-
* Total lines of code (from line_offsets).
|
|
3259
|
-
*/
|
|
3260
2821
|
lines: number
|
|
3261
|
-
/**
|
|
3262
|
-
* Maximum CRAP score among functions in this file. Computed via the active
|
|
3263
|
-
* `coverage_model` per the canonical formula CC^2 * (1 - cov/100)^3 + CC
|
|
3264
|
-
* (Savoia & Evans, 2007). Coverage source: `static_estimated` (default,
|
|
3265
|
-
* graph-based per-function estimate), `istanbul` (real per-function
|
|
3266
|
-
* statement coverage from --coverage), or the legacy `static_binary`
|
|
3267
|
-
* (whole-file 0%/100%, retained for compatibility).
|
|
3268
|
-
*/
|
|
3269
2822
|
crap_max: number
|
|
3270
|
-
/**
|
|
3271
|
-
* Count of functions with CRAP >= 30 (CC >= 5 without test path).
|
|
3272
|
-
*/
|
|
3273
2823
|
crap_above_threshold: number
|
|
3274
2824
|
}
|
|
3275
2825
|
/**
|
|
@@ -3453,51 +3003,16 @@ comment?: (string | null)
|
|
|
3453
3003
|
* test code.
|
|
3454
3004
|
*/
|
|
3455
3005
|
export interface HotspotFinding {
|
|
3456
|
-
/**
|
|
3457
|
-
* File path (absolute; stripped to relative in output).
|
|
3458
|
-
*/
|
|
3459
3006
|
path: string
|
|
3460
|
-
/**
|
|
3461
|
-
* Hotspot score (0–100). Higher means more risk.
|
|
3462
|
-
*/
|
|
3463
3007
|
score: number
|
|
3464
|
-
/**
|
|
3465
|
-
* Number of commits in the analysis window.
|
|
3466
|
-
*/
|
|
3467
3008
|
commits: number
|
|
3468
|
-
/**
|
|
3469
|
-
* Recency-weighted commit count (exponential decay, half-life 90 days).
|
|
3470
|
-
*/
|
|
3471
3009
|
weighted_commits: number
|
|
3472
|
-
/**
|
|
3473
|
-
* Total lines added across all commits.
|
|
3474
|
-
*/
|
|
3475
3010
|
lines_added: number
|
|
3476
|
-
/**
|
|
3477
|
-
* Total lines deleted across all commits.
|
|
3478
|
-
*/
|
|
3479
3011
|
lines_deleted: number
|
|
3480
|
-
/**
|
|
3481
|
-
* Cyclomatic complexity / lines of code.
|
|
3482
|
-
*/
|
|
3483
3012
|
complexity_density: number
|
|
3484
|
-
/**
|
|
3485
|
-
* Number of files that import this file (blast radius).
|
|
3486
|
-
*/
|
|
3487
3013
|
fan_in: number
|
|
3488
3014
|
trend: ChurnTrend
|
|
3489
|
-
/**
|
|
3490
|
-
* Ownership signals (bus factor, contributors, declared owner, drift).
|
|
3491
|
-
* Populated only when `--ownership` is requested.
|
|
3492
|
-
*/
|
|
3493
3015
|
ownership?: (OwnershipMetrics | null)
|
|
3494
|
-
/**
|
|
3495
|
-
* True when the file path matches a test/mock convention (e.g.
|
|
3496
|
-
* `** /__tests__/**`, `** /*.test.*`, `** /*.spec.*`, `** /__mocks__/**`).
|
|
3497
|
-
* Test files are intentionally included in hotspot ranking (test
|
|
3498
|
-
* maintenance IS real work), but tagging them lets consumers decide
|
|
3499
|
-
* whether to weight or filter them downstream.
|
|
3500
|
-
*/
|
|
3501
3016
|
is_test_path?: boolean
|
|
3502
3017
|
/**
|
|
3503
3018
|
* Machine-actionable refactor and review hints. Always populated;
|
|
@@ -3509,89 +3024,23 @@ is_test_path?: boolean
|
|
|
3509
3024
|
*/
|
|
3510
3025
|
actions: HotspotAction[]
|
|
3511
3026
|
}
|
|
3512
|
-
/**
|
|
3513
|
-
* Per-file ownership signals attached to hotspot entries when the user
|
|
3514
|
-
* passes `--ownership`. All fields are derived from git history and the
|
|
3515
|
-
* repository's CODEOWNERS file (if any).
|
|
3516
|
-
*/
|
|
3517
3027
|
export interface OwnershipMetrics {
|
|
3518
|
-
/**
|
|
3519
|
-
* Avelino truck factor: minimum contributors covering at least 50% of
|
|
3520
|
-
* recency-weighted commits in the analysis window. Lower = higher
|
|
3521
|
-
* knowledge-loss risk.
|
|
3522
|
-
*/
|
|
3523
3028
|
bus_factor: number
|
|
3524
|
-
/**
|
|
3525
|
-
* Distinct authors in the analysis window after bot filtering.
|
|
3526
|
-
*/
|
|
3527
3029
|
contributor_count: number
|
|
3528
3030
|
top_contributor: ContributorEntry
|
|
3529
|
-
/**
|
|
3530
|
-
* Up to three additional contributors by share, ordered desc.
|
|
3531
|
-
* Useful for "who else could review this file" routing.
|
|
3532
|
-
*/
|
|
3533
3031
|
recent_contributors?: ContributorEntry[]
|
|
3534
|
-
/**
|
|
3535
|
-
* Contributors whose last touch is within 90 days, ordered by share
|
|
3536
|
-
* descending. First-class field so AI agents do not have to
|
|
3537
|
-
* reconstruct it from [`recent_contributors`](Self::recent_contributors)
|
|
3538
|
-
* filtered by [`ContributorEntry::stale_days`]. Excludes the top
|
|
3539
|
-
* contributor (they are the sole author being flagged); consumers
|
|
3540
|
-
* wanting the full list can union with `top_contributor`.
|
|
3541
|
-
*/
|
|
3542
3032
|
suggested_reviewers?: ContributorEntry[]
|
|
3543
|
-
/**
|
|
3544
|
-
* CODEOWNERS-resolved owner for this file, if a rule matched.
|
|
3545
|
-
* Only the primary (first) owner of the matched rule is reported.
|
|
3546
|
-
*/
|
|
3547
3033
|
declared_owner?: (string | null)
|
|
3548
|
-
/**
|
|
3549
|
-
* Tristate: `Some(true)` = CODEOWNERS file exists but no rule matches
|
|
3550
|
-
* this file; `Some(false)` = a CODEOWNERS rule matches; `None` = no
|
|
3551
|
-
* CODEOWNERS file was discovered for the repository (cannot determine).
|
|
3552
|
-
*/
|
|
3553
3034
|
unowned?: (boolean | null)
|
|
3554
3035
|
ownership_state: OwnershipState
|
|
3555
|
-
/**
|
|
3556
|
-
* True when ownership has drifted from the original author to a new
|
|
3557
|
-
* top contributor. Pairs with [`drift_reason`](Self::drift_reason).
|
|
3558
|
-
*/
|
|
3559
3036
|
drift: boolean
|
|
3560
|
-
/**
|
|
3561
|
-
* Human-readable explanation of the drift, populated only when
|
|
3562
|
-
* [`drift`](Self::drift) is true.
|
|
3563
|
-
*/
|
|
3564
3037
|
drift_reason?: (string | null)
|
|
3565
3038
|
}
|
|
3566
|
-
/**
|
|
3567
|
-
* Per-author contribution summary. The identifier is rendered per the
|
|
3568
|
-
* configured ownership.emailMode (handle, anonymized/hash, or raw); the format field
|
|
3569
|
-
* discriminates the modes so type-aware consumers can branch without
|
|
3570
|
-
* re-parsing.
|
|
3571
|
-
*/
|
|
3572
3039
|
export interface ContributorEntry {
|
|
3573
|
-
/**
|
|
3574
|
-
* Display string per the configured email mode: raw email
|
|
3575
|
-
* (`alice@example.com`), local-part handle (`alice`), or stable anonymized hash
|
|
3576
|
-
* pseudonym (`xxh3:<16hex>`). The format depends on `format`.
|
|
3577
|
-
*
|
|
3578
|
-
* Renamed from `email` because in `handle` and `anonymized`/`hash` modes the value
|
|
3579
|
-
* is no longer an email address; consumers tempted to use it as one
|
|
3580
|
-
* (e.g. `mailto:`) would be wrong.
|
|
3581
|
-
*/
|
|
3582
3040
|
identifier: string
|
|
3583
3041
|
format: ContributorIdentifierFormat
|
|
3584
|
-
/**
|
|
3585
|
-
* Recency-weighted share of total weighted commits (0..1, three decimals).
|
|
3586
|
-
*/
|
|
3587
3042
|
share: number
|
|
3588
|
-
/**
|
|
3589
|
-
* Days since this contributor last touched the file.
|
|
3590
|
-
*/
|
|
3591
3043
|
stale_days: number
|
|
3592
|
-
/**
|
|
3593
|
-
* Total commits by this contributor in the analysis window.
|
|
3594
|
-
*/
|
|
3595
3044
|
commits: number
|
|
3596
3045
|
}
|
|
3597
3046
|
/**
|
|
@@ -3634,29 +3083,11 @@ suggested_pattern?: (string | null)
|
|
|
3634
3083
|
*/
|
|
3635
3084
|
heuristic?: (HotspotActionHeuristic | null)
|
|
3636
3085
|
}
|
|
3637
|
-
/**
|
|
3638
|
-
* Summary statistics for hotspot analysis.
|
|
3639
|
-
*/
|
|
3640
3086
|
export interface HotspotSummary {
|
|
3641
|
-
/**
|
|
3642
|
-
* Analysis window display string (e.g., "6 months").
|
|
3643
|
-
*/
|
|
3644
3087
|
since: string
|
|
3645
|
-
/**
|
|
3646
|
-
* Minimum commits threshold.
|
|
3647
|
-
*/
|
|
3648
3088
|
min_commits: number
|
|
3649
|
-
/**
|
|
3650
|
-
* Number of files with churn data meeting the threshold.
|
|
3651
|
-
*/
|
|
3652
3089
|
files_analyzed: number
|
|
3653
|
-
/**
|
|
3654
|
-
* Number of files excluded (below min_commits).
|
|
3655
|
-
*/
|
|
3656
3090
|
files_excluded: number
|
|
3657
|
-
/**
|
|
3658
|
-
* Whether the repository is a shallow clone.
|
|
3659
|
-
*/
|
|
3660
3091
|
shallow_clone: boolean
|
|
3661
3092
|
}
|
|
3662
3093
|
/**
|
|
@@ -4110,21 +3541,9 @@ auto_fixable: boolean
|
|
|
4110
3541
|
* A function exceeding the very-high-risk size threshold (>60 LOC).
|
|
4111
3542
|
*/
|
|
4112
3543
|
export interface LargeFunctionEntry {
|
|
4113
|
-
/**
|
|
4114
|
-
* Absolute file path.
|
|
4115
|
-
*/
|
|
4116
3544
|
path: string
|
|
4117
|
-
/**
|
|
4118
|
-
* Function name, or `"<anonymous>"` for unnamed functions/arrows.
|
|
4119
|
-
*/
|
|
4120
3545
|
name: string
|
|
4121
|
-
/**
|
|
4122
|
-
* 1-based line number.
|
|
4123
|
-
*/
|
|
4124
3546
|
line: number
|
|
4125
|
-
/**
|
|
4126
|
-
* Number of lines in the function.
|
|
4127
|
-
*/
|
|
4128
3547
|
line_count: number
|
|
4129
3548
|
}
|
|
4130
3549
|
/**
|
|
@@ -4443,181 +3862,51 @@ scope: string
|
|
|
4443
3862
|
* fallow JSON-producing command.
|
|
4444
3863
|
*/
|
|
4445
3864
|
export interface ExplainOutput {
|
|
4446
|
-
/**
|
|
4447
|
-
* Canonical rule id, for example `fallow/unused-export`.
|
|
4448
|
-
*/
|
|
4449
3865
|
id: string
|
|
4450
|
-
/**
|
|
4451
|
-
* Human-readable rule name.
|
|
4452
|
-
*/
|
|
4453
3866
|
name: string
|
|
4454
|
-
/**
|
|
4455
|
-
* Short one-line explanation of the issue.
|
|
4456
|
-
*/
|
|
4457
3867
|
summary: string
|
|
4458
|
-
/**
|
|
4459
|
-
* Why the issue matters and what fallow checks.
|
|
4460
|
-
*/
|
|
4461
3868
|
rationale: string
|
|
4462
|
-
/**
|
|
4463
|
-
* Concrete example of the finding.
|
|
4464
|
-
*/
|
|
4465
3869
|
example: string
|
|
4466
|
-
/**
|
|
4467
|
-
* Recommended fix or suppression guidance.
|
|
4468
|
-
*/
|
|
4469
3870
|
how_to_fix: string
|
|
4470
|
-
/**
|
|
4471
|
-
* Docs URL for the rule.
|
|
4472
|
-
*/
|
|
4473
3871
|
docs: string
|
|
4474
3872
|
}
|
|
4475
3873
|
/**
|
|
4476
3874
|
* Envelope emitted by `fallow --format review-github` / `review-gitlab`.
|
|
4477
|
-
* Consumed by `action/scripts/review.sh` and `ci/scripts/review.sh` to
|
|
4478
|
-
* post inline PR / MR review comments.
|
|
4479
3875
|
*/
|
|
4480
3876
|
export interface ReviewEnvelopeOutput {
|
|
4481
|
-
/**
|
|
4482
|
-
* GitHub review event. Omitted for GitLab.
|
|
4483
|
-
*/
|
|
4484
3877
|
event?: (ReviewEnvelopeEvent | null)
|
|
4485
|
-
/**
|
|
4486
|
-
* Review summary body (rendered above per-line comments). Deprecated in
|
|
4487
|
-
* v2 envelopes: prefer [`summary.body`](`ReviewEnvelopeSummary::body`),
|
|
4488
|
-
* which is byte-identical to this field but carries a stable
|
|
4489
|
-
* fingerprint for reconciliation. Kept on v2 emit so v1 consumers that
|
|
4490
|
-
* only look at `body` keep working.
|
|
4491
|
-
*/
|
|
4492
3878
|
body: string
|
|
4493
3879
|
summary?: ReviewEnvelopeSummary
|
|
4494
|
-
/**
|
|
4495
|
-
* Per-line comments. Each is either a [`GitHubReviewComment`] or a
|
|
4496
|
-
* [`GitLabReviewComment`] depending on `meta.provider`.
|
|
4497
|
-
*/
|
|
4498
3880
|
comments: ReviewComment[]
|
|
4499
|
-
/**
|
|
4500
|
-
* Regex consumers run against every existing PR/MR comment body to
|
|
4501
|
-
* extract a fallow-emitted fingerprint marker. Capture group 1 is the
|
|
4502
|
-
* fingerprint string (a bare 16-char hex hash for single-finding
|
|
4503
|
-
* comments, or `<kind>:<16-char-hex>` for compositions such as
|
|
4504
|
-
* `merged:` for same-line collapsed comments).
|
|
4505
|
-
*
|
|
4506
|
-
* The pattern is anchored with `^` / `$` and relies on multiline
|
|
4507
|
-
* matching to anchor at line boundaries inside a multi-line comment
|
|
4508
|
-
* body. Multiline is NOT baked into the pattern via `(?m)` (which
|
|
4509
|
-
* JavaScript RegExp rejects as `Invalid group`); instead the consumer
|
|
4510
|
-
* passes [`Self::marker_regex_flags`] as the flags argument to its
|
|
4511
|
-
* regex engine. JavaScript: `new RegExp(env.marker_regex,
|
|
4512
|
-
* env.marker_regex_flags)`. Rust: `regex::RegexBuilder::new(pat)
|
|
4513
|
-
* .multi_line(flags.contains('m')).build()` (or any equivalent).
|
|
4514
|
-
*/
|
|
4515
3881
|
marker_regex?: string
|
|
4516
|
-
/**
|
|
4517
|
-
* Flags consumers pass alongside [`Self::marker_regex`] when
|
|
4518
|
-
* constructing their regex engine. Currently always `"m"` (multiline
|
|
4519
|
-
* so the anchored `^` / `$` match at every line boundary within a
|
|
4520
|
-
* comment body). Emitting flags as a separate field instead of
|
|
4521
|
-
* baking `(?m)` into the pattern keeps the wire compatible with
|
|
4522
|
-
* JavaScript RegExp, which rejects inline flag groups outside a
|
|
4523
|
-
* `(?flags:X)` grouping.
|
|
4524
|
-
*/
|
|
4525
3882
|
marker_regex_flags?: string
|
|
4526
3883
|
meta: ReviewEnvelopeMeta
|
|
4527
3884
|
}
|
|
4528
3885
|
/**
|
|
4529
|
-
* Summary block on [`ReviewEnvelopeOutput`].
|
|
4530
|
-
* `serde(default)` keeps schemars from marking it required so a future
|
|
4531
|
-
* Deserialize derivation against v1 historical input synthesizes an empty
|
|
4532
|
-
* value rather than erroring.
|
|
3886
|
+
* Summary block on [`ReviewEnvelopeOutput`].
|
|
4533
3887
|
*/
|
|
4534
3888
|
export interface ReviewEnvelopeSummary {
|
|
4535
|
-
/**
|
|
4536
|
-
* Markdown body of the summary. Byte-identical to the legacy top-level
|
|
4537
|
-
* [`ReviewEnvelopeOutput::body`] field; the duplication is intentional
|
|
4538
|
-
* so v1 consumers see no behavior change.
|
|
4539
|
-
*/
|
|
4540
3889
|
body: string
|
|
4541
|
-
/**
|
|
4542
|
-
* FNV-1a 64-bit hash (16 lowercase hex chars) of the summary body
|
|
4543
|
-
* BEFORE the trailing fallow-fingerprint marker line is appended.
|
|
4544
|
-
* (Computing the hash from the post-marker body would be circular:
|
|
4545
|
-
* the marker contains the fingerprint, so the fingerprint cannot
|
|
4546
|
-
* depend on the marker.) To reproduce from [`Self::body`], strip the
|
|
4547
|
-
* line matching [`ReviewEnvelopeOutput::marker_regex`] together with
|
|
4548
|
-
* its leading separator newlines and hash the remainder. Stable
|
|
4549
|
-
* across runs that produce the same summary content; consumers
|
|
4550
|
-
* upsert the sticky summary comment by matching this fingerprint
|
|
4551
|
-
* against the marker_regex extraction of every existing comment body.
|
|
4552
|
-
*/
|
|
4553
3890
|
fingerprint: string
|
|
4554
3891
|
}
|
|
4555
3892
|
/**
|
|
4556
3893
|
* GitHub pull-request review comment.
|
|
4557
3894
|
*/
|
|
4558
3895
|
export interface GitHubReviewComment {
|
|
4559
|
-
/**
|
|
4560
|
-
* File path the comment targets, repo-root relative.
|
|
4561
|
-
*/
|
|
4562
3896
|
path: string
|
|
4563
|
-
/**
|
|
4564
|
-
* 1-indexed line number the comment targets.
|
|
4565
|
-
*/
|
|
4566
3897
|
line: number
|
|
4567
3898
|
side: GitHubReviewSide
|
|
4568
|
-
/**
|
|
4569
|
-
* Markdown body of the comment.
|
|
4570
|
-
*/
|
|
4571
3899
|
body: string
|
|
4572
|
-
/**
|
|
4573
|
-
* Stable fingerprint for the comment, used by `fallow ci
|
|
4574
|
-
* reconcile-review` to detect carryover comments across PR revisions.
|
|
4575
|
-
* For single-finding comments the value is a bare 16-char hex FNV-1a
|
|
4576
|
-
* hash. For merged comments (multiple findings on the same path:line)
|
|
4577
|
-
* the value is `merged:<16-char hex>` over the sorted constituent
|
|
4578
|
-
* fingerprints, so the identity shifts whenever constituent findings
|
|
4579
|
-
* change membership. Bundled wrappers and `fallow ci reconcile-review`
|
|
4580
|
-
* dedupe on this primary fingerprint only; consumers wanting
|
|
4581
|
-
* update-in-place reconciliation (preserving reviewer reply threads
|
|
4582
|
-
* across content changes) implement their own identity tracking via
|
|
4583
|
-
* `marker_regex`.
|
|
4584
|
-
*/
|
|
4585
3900
|
fingerprint: string
|
|
4586
|
-
/**
|
|
4587
|
-
* True when [`Self::body`] was truncated to fit a downstream provider's
|
|
4588
|
-
* note-size budget (today: 65,536 bytes). The body retains the closing
|
|
4589
|
-
* fallow-fingerprint marker so reconciliation continues to work after
|
|
4590
|
-
* truncation.
|
|
4591
|
-
*
|
|
4592
|
-
* Co-presence invariant: `truncated == true` always implies the body
|
|
4593
|
-
* contains an inline `<!-- fallow-truncated -->` HTML marker and the
|
|
4594
|
-
* `> Body truncated by fallow.` blockquote breadcrumb, and vice versa.
|
|
4595
|
-
* All three signals are emitted together; consumers may use any one
|
|
4596
|
-
* (the typed boolean is the authoritative machine-readable signal).
|
|
4597
|
-
*/
|
|
4598
3901
|
truncated?: boolean
|
|
4599
3902
|
}
|
|
4600
3903
|
/**
|
|
4601
3904
|
* GitLab merge-request discussion comment.
|
|
4602
3905
|
*/
|
|
4603
3906
|
export interface GitLabReviewComment {
|
|
4604
|
-
/**
|
|
4605
|
-
* Markdown body of the comment.
|
|
4606
|
-
*/
|
|
4607
3907
|
body: string
|
|
4608
3908
|
position: GitLabReviewPosition
|
|
4609
|
-
/**
|
|
4610
|
-
* Stable fingerprint for the comment. See
|
|
4611
|
-
* [`GitHubReviewComment::fingerprint`] for the single vs `merged:`
|
|
4612
|
-
* shape contract; semantics are identical across providers.
|
|
4613
|
-
*/
|
|
4614
3909
|
fingerprint: string
|
|
4615
|
-
/**
|
|
4616
|
-
* True when [`Self::body`] was truncated to fit GitLab's note-size
|
|
4617
|
-
* budget. See [`GitHubReviewComment::truncated`] for the full
|
|
4618
|
-
* co-presence invariant with the inline HTML marker and human
|
|
4619
|
-
* blockquote breadcrumb.
|
|
4620
|
-
*/
|
|
4621
3910
|
truncated?: boolean
|
|
4622
3911
|
}
|
|
4623
3912
|
/**
|
|
@@ -4625,30 +3914,12 @@ truncated?: boolean
|
|
|
4625
3914
|
* merge-request discussion-position API.
|
|
4626
3915
|
*/
|
|
4627
3916
|
export interface GitLabReviewPosition {
|
|
4628
|
-
/**
|
|
4629
|
-
* Merge-request base SHA.
|
|
4630
|
-
*/
|
|
4631
3917
|
base_sha?: (string | null)
|
|
4632
|
-
/**
|
|
4633
|
-
* Merge-request start SHA.
|
|
4634
|
-
*/
|
|
4635
3918
|
start_sha?: (string | null)
|
|
4636
|
-
/**
|
|
4637
|
-
* Merge-request head SHA.
|
|
4638
|
-
*/
|
|
4639
3919
|
head_sha?: (string | null)
|
|
4640
3920
|
position_type: GitLabReviewPositionType
|
|
4641
|
-
/**
|
|
4642
|
-
* File path on the base side.
|
|
4643
|
-
*/
|
|
4644
3921
|
old_path: string
|
|
4645
|
-
/**
|
|
4646
|
-
* File path on the head side.
|
|
4647
|
-
*/
|
|
4648
3922
|
new_path: string
|
|
4649
|
-
/**
|
|
4650
|
-
* 1-indexed line on the head side.
|
|
4651
|
-
*/
|
|
4652
3923
|
new_line: number
|
|
4653
3924
|
}
|
|
4654
3925
|
/**
|
|
@@ -4657,10 +3928,6 @@ new_line: number
|
|
|
4657
3928
|
export interface ReviewEnvelopeMeta {
|
|
4658
3929
|
schema: ReviewEnvelopeSchema
|
|
4659
3930
|
provider: ReviewProvider
|
|
4660
|
-
/**
|
|
4661
|
-
* Check conclusion derived from the underlying findings. Emitted only
|
|
4662
|
-
* for GitHub envelopes today.
|
|
4663
|
-
*/
|
|
4664
3931
|
check_conclusion?: (ReviewCheckConclusion | null)
|
|
4665
3932
|
}
|
|
4666
3933
|
/**
|
|
@@ -4671,240 +3938,66 @@ check_conclusion?: (ReviewCheckConclusion | null)
|
|
|
4671
3938
|
export interface ReviewReconcileOutput {
|
|
4672
3939
|
schema: ReviewReconcileSchema
|
|
4673
3940
|
provider: ReviewProvider
|
|
4674
|
-
/**
|
|
4675
|
-
* PR / MR target identifier supplied to `fallow ci reconcile-review`.
|
|
4676
|
-
* `null` when the command ran without an explicit target.
|
|
4677
|
-
*/
|
|
4678
3941
|
target?: (string | null)
|
|
4679
|
-
/**
|
|
4680
|
-
* Whether the reconcile ran in dry-run mode.
|
|
4681
|
-
*/
|
|
4682
3942
|
dry_run: boolean
|
|
4683
|
-
/**
|
|
4684
|
-
* Number of comments in the supplied review envelope.
|
|
4685
|
-
*/
|
|
4686
3943
|
comments: number
|
|
4687
|
-
/**
|
|
4688
|
-
* Total fingerprints discovered in the supplied envelope.
|
|
4689
|
-
*/
|
|
4690
3944
|
current_fingerprints: number
|
|
4691
|
-
/**
|
|
4692
|
-
* Existing fingerprints already posted on the PR / MR.
|
|
4693
|
-
*/
|
|
4694
3945
|
existing_fingerprints: number
|
|
4695
|
-
/**
|
|
4696
|
-
* Newly-introduced fingerprints (current minus existing).
|
|
4697
|
-
*/
|
|
4698
3946
|
new_fingerprints: number
|
|
4699
|
-
/**
|
|
4700
|
-
* Stale fingerprints (existing minus current).
|
|
4701
|
-
*/
|
|
4702
3947
|
stale_fingerprints: number
|
|
4703
|
-
/**
|
|
4704
|
-
* Identifiers of the new fingerprints (subset of comments).
|
|
4705
|
-
*/
|
|
4706
3948
|
new: string[]
|
|
4707
|
-
/**
|
|
4708
|
-
* Identifiers of the stale fingerprints (subset of existing).
|
|
4709
|
-
*/
|
|
4710
3949
|
stale: string[]
|
|
4711
|
-
/**
|
|
4712
|
-
* Optional warning when the provider API was unreachable or
|
|
4713
|
-
* auth-rejected. `null` on the happy path.
|
|
4714
|
-
*/
|
|
4715
3950
|
provider_warning?: (string | null)
|
|
4716
|
-
/**
|
|
4717
|
-
* Resolution comments actually posted (zero on dry runs).
|
|
4718
|
-
*/
|
|
4719
3951
|
resolution_comments_posted: number
|
|
4720
|
-
/**
|
|
4721
|
-
* Stale review threads actually resolved (zero on dry runs).
|
|
4722
|
-
*/
|
|
4723
3952
|
threads_resolved: number
|
|
4724
|
-
/**
|
|
4725
|
-
* Operator-facing retry hint when apply stopped early.
|
|
4726
|
-
*/
|
|
4727
3953
|
apply_hint?: (string | null)
|
|
4728
|
-
/**
|
|
4729
|
-
* Errors collected during apply, one entry per failure.
|
|
4730
|
-
*/
|
|
4731
3954
|
apply_errors: string[]
|
|
4732
|
-
/**
|
|
4733
|
-
* Stale fingerprints whose provider mutation failed.
|
|
4734
|
-
*/
|
|
4735
3955
|
failed_fingerprints?: string[]
|
|
4736
|
-
/**
|
|
4737
|
-
* Stale fingerprints not fully applied after the fail-fast stop.
|
|
4738
|
-
*/
|
|
4739
3956
|
unapplied_fingerprints?: string[]
|
|
4740
3957
|
}
|
|
4741
3958
|
/**
|
|
4742
|
-
*
|
|
4743
|
-
* agent-readable runtime coverage setup instructions. In workspaces,
|
|
4744
|
-
* `members` carries one entry per detected runtime package; `runtime_targets`
|
|
4745
|
-
* is the union of all member targets.
|
|
4746
|
-
*
|
|
4747
|
-
* Constructed at runtime by
|
|
4748
|
-
* `crates/cli/src/coverage/mod.rs::build_setup_envelope`; the wire is
|
|
4749
|
-
* `serde_json::to_value(&envelope)`. The drift gate keeps this struct
|
|
4750
|
-
* aligned with `docs/output-schema.json`.
|
|
3959
|
+
* `fallow coverage setup --json` envelope.
|
|
4751
3960
|
*/
|
|
4752
3961
|
export interface CoverageSetupOutput {
|
|
4753
3962
|
schema_version: CoverageSetupSchemaVersion
|
|
4754
3963
|
framework_detected: CoverageSetupFramework
|
|
4755
|
-
/**
|
|
4756
|
-
* Detected JavaScript package manager. `null` when none could be
|
|
4757
|
-
* resolved.
|
|
4758
|
-
*/
|
|
4759
3964
|
package_manager?: (CoverageSetupPackageManager | null)
|
|
4760
|
-
/**
|
|
4761
|
-
* Union of runtime targets across emitted members.
|
|
4762
|
-
*/
|
|
4763
3965
|
runtime_targets: CoverageSetupRuntimeTarget[]
|
|
4764
|
-
/**
|
|
4765
|
-
* Per-runtime-workspace setup recipes. Pure aggregator roots and
|
|
4766
|
-
* build-only library packages are omitted.
|
|
4767
|
-
*/
|
|
4768
3966
|
members: CoverageSetupMember[]
|
|
4769
|
-
|
|
4770
|
-
* Always `null` today. Reserved for a future "config has been written
|
|
4771
|
-
* to disk" indicator.
|
|
4772
|
-
*/
|
|
4773
|
-
config_written?: {
|
|
4774
|
-
[k: string]: unknown
|
|
4775
|
-
}
|
|
4776
|
-
/**
|
|
4777
|
-
* Shell commands the agent should run from the workspace root.
|
|
4778
|
-
*/
|
|
3967
|
+
config_written?: unknown
|
|
4779
3968
|
commands: string[]
|
|
4780
|
-
/**
|
|
4781
|
-
* Compatibility copy of the primary member's files, with workspace
|
|
4782
|
-
* prefixes when the primary member is not the root.
|
|
4783
|
-
*/
|
|
4784
3969
|
files_to_edit: CoverageSetupFileToEdit[]
|
|
4785
|
-
/**
|
|
4786
|
-
* Compatibility copy of the primary member's snippets, with workspace
|
|
4787
|
-
* prefixes when the primary member is not the root.
|
|
4788
|
-
*/
|
|
4789
3970
|
snippets: CoverageSetupSnippet[]
|
|
4790
|
-
/**
|
|
4791
|
-
* Optional Dockerfile RUN/COPY snippet to enable the beacon in
|
|
4792
|
-
* containerised deployments.
|
|
4793
|
-
*/
|
|
4794
3971
|
dockerfile_snippet?: (string | null)
|
|
4795
|
-
/**
|
|
4796
|
-
* Ordered next-step instructions for the agent / human operator.
|
|
4797
|
-
*/
|
|
4798
3972
|
next_steps: string[]
|
|
4799
|
-
/**
|
|
4800
|
-
* Non-fatal warnings raised during setup detection.
|
|
4801
|
-
*/
|
|
4802
3973
|
warnings: string[]
|
|
4803
|
-
|
|
4804
|
-
* `_meta` block emitted only when `--explain` is passed.
|
|
4805
|
-
*/
|
|
4806
|
-
_meta?: {
|
|
4807
|
-
[k: string]: unknown
|
|
4808
|
-
}
|
|
3974
|
+
_meta?: unknown
|
|
4809
3975
|
}
|
|
4810
|
-
/**
|
|
4811
|
-
* Per-workspace setup recipe inside [`CoverageSetupOutput::members`].
|
|
4812
|
-
*/
|
|
4813
3976
|
export interface CoverageSetupMember {
|
|
4814
|
-
/**
|
|
4815
|
-
* Workspace package name (or root marker for single-package projects).
|
|
4816
|
-
*/
|
|
4817
3977
|
name: string
|
|
4818
|
-
/**
|
|
4819
|
-
* Workspace path relative to the analysed root, or `.` for the root
|
|
4820
|
-
* member.
|
|
4821
|
-
*/
|
|
4822
3978
|
path: string
|
|
4823
3979
|
framework_detected: CoverageSetupFramework
|
|
4824
|
-
/**
|
|
4825
|
-
* Package manager detected for this member.
|
|
4826
|
-
*/
|
|
4827
3980
|
package_manager?: (CoverageSetupPackageManager | null)
|
|
4828
|
-
/**
|
|
4829
|
-
* Runtime targets supported by this member's framework.
|
|
4830
|
-
*/
|
|
4831
3981
|
runtime_targets: CoverageSetupRuntimeTarget[]
|
|
4832
|
-
/**
|
|
4833
|
-
* Files the agent should edit to wire in the beacon.
|
|
4834
|
-
*/
|
|
4835
3982
|
files_to_edit: CoverageSetupFileToEdit[]
|
|
4836
|
-
/**
|
|
4837
|
-
* Code snippets the agent should paste into the edited files.
|
|
4838
|
-
*/
|
|
4839
3983
|
snippets: CoverageSetupSnippet[]
|
|
4840
|
-
/**
|
|
4841
|
-
* Optional Dockerfile snippet specific to this member.
|
|
4842
|
-
*/
|
|
4843
3984
|
dockerfile_snippet?: (string | null)
|
|
4844
|
-
/**
|
|
4845
|
-
* Member-scoped warnings.
|
|
4846
|
-
*/
|
|
4847
3985
|
warnings: string[]
|
|
4848
3986
|
}
|
|
4849
|
-
/**
|
|
4850
|
-
* Single file to edit inside [`CoverageSetupMember::files_to_edit`] or
|
|
4851
|
-
* [`CoverageSetupOutput::files_to_edit`].
|
|
4852
|
-
*/
|
|
4853
3987
|
export interface CoverageSetupFileToEdit {
|
|
4854
|
-
/**
|
|
4855
|
-
* Workspace-relative path to the file to edit.
|
|
4856
|
-
*/
|
|
4857
3988
|
path: string
|
|
4858
|
-
/**
|
|
4859
|
-
* Why the file needs editing (e.g. `"Mount the beacon middleware"`).
|
|
4860
|
-
*/
|
|
4861
3989
|
reason: string
|
|
4862
3990
|
}
|
|
4863
|
-
/**
|
|
4864
|
-
* Single code snippet inside [`CoverageSetupMember::snippets`] or
|
|
4865
|
-
* [`CoverageSetupOutput::snippets`].
|
|
4866
|
-
*/
|
|
4867
3991
|
export interface CoverageSetupSnippet {
|
|
4868
|
-
/**
|
|
4869
|
-
* Short label identifying the snippet (used by the human renderer).
|
|
4870
|
-
*/
|
|
4871
3992
|
label: string
|
|
4872
|
-
/**
|
|
4873
|
-
* Workspace-relative path the snippet should be pasted into.
|
|
4874
|
-
*/
|
|
4875
3993
|
path: string
|
|
4876
|
-
/**
|
|
4877
|
-
* Snippet content (literal source text).
|
|
4878
|
-
*/
|
|
4879
3994
|
content: string
|
|
4880
3995
|
}
|
|
4881
|
-
/**
|
|
4882
|
-
* Envelope emitted by `fallow coverage analyze --format json`.
|
|
4883
|
-
*
|
|
4884
|
-
* Focused runtime coverage analysis output. Local mode reads
|
|
4885
|
-
* `--runtime-coverage <path>`. Cloud mode requires explicit `--cloud` /
|
|
4886
|
-
* `--runtime-coverage-cloud` or `FALLOW_RUNTIME_COVERAGE_SOURCE=cloud`;
|
|
4887
|
-
* `FALLOW_API_KEY` alone does NOT select cloud mode.
|
|
4888
|
-
*
|
|
4889
|
-
* Constructed at runtime in
|
|
4890
|
-
* `crates/cli/src/coverage/analyze.rs::print_runtime_json`; the wire is
|
|
4891
|
-
* `serde_json::to_value(&envelope)`. The drift gate keeps this struct
|
|
4892
|
-
* aligned with `docs/output-schema.json`. Carries its own schema-version
|
|
4893
|
-
* discriminator ([`CoverageAnalyzeSchemaVersion`]) because runtime
|
|
4894
|
-
* coverage iterates independently of the main JSON contract version.
|
|
4895
|
-
*/
|
|
4896
3996
|
export interface CoverageAnalyzeOutput {
|
|
4897
3997
|
schema_version: CoverageAnalyzeSchemaVersion
|
|
4898
3998
|
version: ToolVersion
|
|
4899
3999
|
elapsed_ms: ElapsedMs
|
|
4900
4000
|
runtime_coverage: RuntimeCoverageReport
|
|
4901
|
-
/**
|
|
4902
|
-
* `_meta` block with metric / rule definitions, emitted when `--explain`
|
|
4903
|
-
* is passed. Populated via the post-pass injection in
|
|
4904
|
-
* `print_runtime_json` (matches the pattern used by every other typed
|
|
4905
|
-
* envelope; the typed struct sets this to `None` and the JSON layer
|
|
4906
|
-
* merges in the `crate::explain::coverage_analyze_meta()` payload).
|
|
4907
|
-
*/
|
|
4908
4001
|
_meta?: (Meta | null)
|
|
4909
4002
|
}
|
|
4910
4003
|
/**
|
|
@@ -4921,38 +4014,12 @@ boundaries: BoundariesListing
|
|
|
4921
4014
|
* `boundaries` block carried by [`ListBoundariesOutput`].
|
|
4922
4015
|
*/
|
|
4923
4016
|
export interface BoundariesListing {
|
|
4924
|
-
/**
|
|
4925
|
-
* `false` when the project has no `boundaries` configured; `true`
|
|
4926
|
-
* otherwise. When `false` every array below is empty and every count
|
|
4927
|
-
* is `0` (parity is enforced so consumers can read the counts without
|
|
4928
|
-
* first branching on this flag).
|
|
4929
|
-
*/
|
|
4930
4017
|
configured: boolean
|
|
4931
|
-
/**
|
|
4932
|
-
* Length of [`Self::zones`]; emitted alongside the array for parity
|
|
4933
|
-
* with `rule_count` / `logical_group_count`.
|
|
4934
|
-
*/
|
|
4935
4018
|
zone_count: number
|
|
4936
|
-
/**
|
|
4937
|
-
* Boundary zones after preset and `autoDiscover` expansion.
|
|
4938
|
-
*/
|
|
4939
4019
|
zones: BoundariesListZone[]
|
|
4940
|
-
/**
|
|
4941
|
-
* Length of [`Self::rules`].
|
|
4942
|
-
*/
|
|
4943
4020
|
rule_count: number
|
|
4944
|
-
/**
|
|
4945
|
-
* Boundary import rules, each `from -> allow[]`.
|
|
4946
|
-
*/
|
|
4947
4021
|
rules: BoundariesListRule[]
|
|
4948
|
-
/**
|
|
4949
|
-
* Length of [`Self::logical_groups`]. Always present (issue #373).
|
|
4950
|
-
*/
|
|
4951
4022
|
logical_group_count: number
|
|
4952
|
-
/**
|
|
4953
|
-
* Pre-expansion `autoDiscover` groups carrying the user-authored parent
|
|
4954
|
-
* name and grouping intent (issue #373).
|
|
4955
|
-
*/
|
|
4956
4023
|
logical_groups: BoundariesListLogicalGroup[]
|
|
4957
4024
|
}
|
|
4958
4025
|
/**
|
|
@@ -4960,18 +4027,8 @@ logical_groups: BoundariesListLogicalGroup[]
|
|
|
4960
4027
|
* classifies files into a single zone via glob patterns.
|
|
4961
4028
|
*/
|
|
4962
4029
|
export interface BoundariesListZone {
|
|
4963
|
-
/**
|
|
4964
|
-
* Zone identifier as referenced in rules (e.g. `app`, `features/auth`).
|
|
4965
|
-
*/
|
|
4966
4030
|
name: string
|
|
4967
|
-
/**
|
|
4968
|
-
* Compiled glob patterns. Children of an `autoDiscover` parent each
|
|
4969
|
-
* carry a single pattern like `src/features/auth/**`.
|
|
4970
|
-
*/
|
|
4971
4031
|
patterns: string[]
|
|
4972
|
-
/**
|
|
4973
|
-
* Number of discovered files classified into this zone.
|
|
4974
|
-
*/
|
|
4975
4032
|
file_count: number
|
|
4976
4033
|
}
|
|
4977
4034
|
/**
|
|
@@ -4981,14 +4038,7 @@ file_count: number
|
|
|
4981
4038
|
* corresponding [`BoundariesListLogicalGroup::authored_rule`].
|
|
4982
4039
|
*/
|
|
4983
4040
|
export interface BoundariesListRule {
|
|
4984
|
-
/**
|
|
4985
|
-
* Source zone the rule applies to.
|
|
4986
|
-
*/
|
|
4987
4041
|
from: string
|
|
4988
|
-
/**
|
|
4989
|
-
* Target zones [`Self::from`] is allowed to import from. Self-imports
|
|
4990
|
-
* are always allowed implicitly.
|
|
4991
|
-
*/
|
|
4992
4042
|
allow: string[]
|
|
4993
4043
|
}
|
|
4994
4044
|
/**
|
|
@@ -4998,75 +4048,28 @@ allow: string[]
|
|
|
4998
4048
|
* would otherwise flatten it out of [`BoundariesListing::zones`].
|
|
4999
4049
|
*/
|
|
5000
4050
|
export interface BoundariesListLogicalGroup {
|
|
5001
|
-
/**
|
|
5002
|
-
* Logical parent zone name as authored by the user.
|
|
5003
|
-
*/
|
|
5004
4051
|
name: string
|
|
5005
|
-
/**
|
|
5006
|
-
* Discovered child zone names in stable directory-sorted order.
|
|
5007
|
-
*/
|
|
5008
4052
|
children: string[]
|
|
5009
|
-
/**
|
|
5010
|
-
* Verbatim `autoDiscover` strings from the user's config (not
|
|
5011
|
-
* normalized) so round-trip tooling can match byte-for-byte.
|
|
5012
|
-
*/
|
|
5013
4053
|
auto_discover: string[]
|
|
5014
4054
|
status: LogicalGroupStatus
|
|
5015
|
-
/**
|
|
5016
|
-
* Position of the parent zone in the user's pre-expansion `zones[]`.
|
|
5017
|
-
*/
|
|
5018
4055
|
source_zone_index: number
|
|
5019
|
-
/**
|
|
5020
|
-
* Sum of `file_count` across [`Self::children`] plus the fallback
|
|
5021
|
-
* zone's `file_count` when present.
|
|
5022
|
-
*/
|
|
5023
4056
|
file_count: number
|
|
5024
|
-
/**
|
|
5025
|
-
* Pre-expansion rule keyed on the parent name, when the user wrote
|
|
5026
|
-
* one.
|
|
5027
|
-
*/
|
|
5028
4057
|
authored_rule?: (AuthoredRule | null)
|
|
5029
|
-
/**
|
|
5030
|
-
* When the parent zone also carried explicit `patterns`, it stayed in
|
|
5031
|
-
* [`BoundariesListing::zones`] as a fallback classifier; this is its
|
|
5032
|
-
* name. Equal to [`Self::name`] when present.
|
|
5033
|
-
*/
|
|
5034
4058
|
fallback_zone?: (string | null)
|
|
5035
|
-
/**
|
|
5036
|
-
* Parent zone indices merged into this group when the user declared
|
|
5037
|
-
* the same parent name multiple times.
|
|
5038
|
-
*/
|
|
5039
4059
|
merged_from?: (number[] | null)
|
|
5040
|
-
/**
|
|
5041
|
-
* Echo of the parent zone's `root` (subtree scope) as the user wrote
|
|
5042
|
-
* it. `None` when the parent had no `root` field.
|
|
5043
|
-
*/
|
|
5044
4060
|
original_zone_root?: (string | null)
|
|
5045
|
-
/**
|
|
5046
|
-
* Parallel to [`Self::children`]: for child at index `i`, the index
|
|
5047
|
-
* into [`Self::auto_discover`] of the path that produced it. Empty
|
|
5048
|
-
* when only one path was authored (every child trivially maps to
|
|
5049
|
-
* index 0). `serde(default)` keeps the schema's `required` array in
|
|
5050
|
-
* step with the runtime's `skip_serializing_if` behavior.
|
|
5051
|
-
*/
|
|
5052
4061
|
child_source_indices?: number[]
|
|
5053
4062
|
}
|
|
5054
4063
|
/**
|
|
5055
|
-
* Pre-expansion
|
|
5056
|
-
* user's original intent (`{ from: "features", allow: ["shared"] }`) even
|
|
5057
|
-
* after `expand_auto_discover` rewrote it into per-child rules
|
|
5058
|
-
* (`features/auth -> shared`, `features/billing -> shared`).
|
|
4064
|
+
* Pre-expansion rule preserved on a [`LogicalGroup`].
|
|
5059
4065
|
*/
|
|
5060
4066
|
export interface AuthoredRule {
|
|
5061
4067
|
/**
|
|
5062
|
-
*
|
|
4068
|
+
* Authored `allow` list.
|
|
5063
4069
|
*/
|
|
5064
4070
|
allow: string[]
|
|
5065
4071
|
/**
|
|
5066
|
-
*
|
|
5067
|
-
* from JSON output when empty; `serde(default)` keeps the derived
|
|
5068
|
-
* schema in lock-step (schemars 1 marks any field with a
|
|
5069
|
-
* `serde(default)` attribute as non-required).
|
|
4072
|
+
* Authored `allowTypeOnly` list.
|
|
5070
4073
|
*/
|
|
5071
4074
|
allow_type_only?: string[]
|
|
5072
4075
|
}
|
|
@@ -5167,31 +4170,9 @@ health_trend?: (HealthTrend | null)
|
|
|
5167
4170
|
* back to the report root.
|
|
5168
4171
|
*/
|
|
5169
4172
|
actions_meta?: (HealthActionsMeta | null)
|
|
5170
|
-
/**
|
|
5171
|
-
* Resolver mode used when --group-by is active. Present only on grouped
|
|
5172
|
-
* output. The top-level `vital_signs`, `health_score`, and `summary` keep
|
|
5173
|
-
* the active run scope (for example after --workspace); per-group versions
|
|
5174
|
-
* live inside each entry of `groups`.
|
|
5175
|
-
*/
|
|
5176
4173
|
grouped_by?: (GroupByMode | null)
|
|
5177
|
-
/**
|
|
5178
|
-
* Per-group health output, present only when `--group-by` is active.
|
|
5179
|
-
* Each group recomputes its own `vital_signs` and `health_score` from
|
|
5180
|
-
* the files in that group, mirroring how `--workspace` scopes a single
|
|
5181
|
-
* subset.
|
|
5182
|
-
*/
|
|
5183
4174
|
groups?: (HealthGroup[] | null)
|
|
5184
|
-
/**
|
|
5185
|
-
* `_meta` block with metric / rule definitions, emitted when `--explain`
|
|
5186
|
-
* is passed (always present in MCP responses).
|
|
5187
|
-
*/
|
|
5188
4175
|
_meta?: (Meta | null)
|
|
5189
|
-
/**
|
|
5190
|
-
* Workspace-discovery diagnostics surfaced during config load
|
|
5191
|
-
* (issue #473). Mirror of [`CheckOutput::workspace_diagnostics`] so
|
|
5192
|
-
* stand-alone `fallow health --format json` consumers see the same
|
|
5193
|
-
* signal.
|
|
5194
|
-
*/
|
|
5195
4176
|
workspace_diagnostics?: WorkspaceDiagnostic[]
|
|
5196
4177
|
}
|
|
5197
4178
|
/**
|
|
@@ -5233,6 +4214,12 @@ files_analyzed: number
|
|
|
5233
4214
|
* rendered finding count, not the un-truncated total.
|
|
5234
4215
|
*/
|
|
5235
4216
|
functions_above_threshold: number
|
|
4217
|
+
/**
|
|
4218
|
+
* Whether CRAP findings in this group share a single coverage-source kind
|
|
4219
|
+
* (`uniform`) or combine Istanbul / estimated / inherited sources
|
|
4220
|
+
* (`mixed`). Absent when no grouped finding carries CRAP source data.
|
|
4221
|
+
*/
|
|
4222
|
+
coverage_source_consistency?: (CoverageSourceConsistency | null)
|
|
5236
4223
|
/**
|
|
5237
4224
|
* Per-group vital signs recomputed from the files in this group. Absent
|
|
5238
4225
|
* when --score-only suppressed top-level vital signs.
|
|
@@ -5281,19 +4268,15 @@ targets?: RefactoringTargetFinding[]
|
|
|
5281
4268
|
actions_meta?: (HealthActionsMeta | null)
|
|
5282
4269
|
}
|
|
5283
4270
|
/**
|
|
5284
|
-
*
|
|
5285
|
-
*
|
|
4271
|
+
* Wire-shape payload for `fallow dupes --format json` (the body that
|
|
4272
|
+
* flattens into [`crate::output_envelope::DupesOutput`] and is also
|
|
4273
|
+
* emitted under the `dupes` / `duplication` key inside the combined and
|
|
4274
|
+
* audit envelopes).
|
|
5286
4275
|
*
|
|
5287
|
-
*
|
|
5288
|
-
*
|
|
5289
|
-
*
|
|
5290
|
-
*
|
|
5291
|
-
* typed [`crate::output_dupes::CloneGroupFinding`] /
|
|
5292
|
-
* [`crate::output_dupes::CloneFamilyFinding`] wrappers so the `actions[]`
|
|
5293
|
-
* field is part of the schema-derived contract.
|
|
5294
|
-
* `grouped_by` / `groups` / `total_issues` are populated by the grouped
|
|
5295
|
-
* builder; on the ungrouped path they stay `None` and `skip_serializing_if`
|
|
5296
|
-
* drops them.
|
|
4276
|
+
* Mirrors [`DuplicationReport`] field-for-field, except `clone_groups`
|
|
4277
|
+
* and `clone_families` carry the typed wrapper envelopes instead of bare
|
|
4278
|
+
* findings, so the schema (and any TS / agent consumer) sees the typed
|
|
4279
|
+
* `actions[]` natively.
|
|
5297
4280
|
*/
|
|
5298
4281
|
export interface DupesOutput {
|
|
5299
4282
|
schema_version: SchemaVersion
|
|
@@ -5318,29 +4301,8 @@ clone_families: CloneFamilyFinding[]
|
|
|
5318
4301
|
*/
|
|
5319
4302
|
mirrored_directories?: MirroredDirectory[]
|
|
5320
4303
|
stats: DuplicationStats
|
|
5321
|
-
/**
|
|
5322
|
-
* Resolver mode used for partitioning. Present only when `--group-by` is
|
|
5323
|
-
* active.
|
|
5324
|
-
*/
|
|
5325
4304
|
grouped_by?: (GroupByMode | null)
|
|
5326
|
-
/**
|
|
5327
|
-
* Total clone groups across all buckets when `--group-by` is active.
|
|
5328
|
-
* Mirrors the grouped check / health envelopes which expose
|
|
5329
|
-
* `total_issues` so MCP and CI consumers can read the same key across
|
|
5330
|
-
* commands.
|
|
5331
|
-
*/
|
|
5332
4305
|
total_issues?: (number | null)
|
|
5333
|
-
/**
|
|
5334
|
-
* Per-group buckets when `--group-by` is active. Each clone group is
|
|
5335
|
-
* attributed to its largest-owner key (most instances; alphabetical
|
|
5336
|
-
* tiebreak). Sort: most clone groups first, then alphabetical, with
|
|
5337
|
-
* `(unowned)` pinned last.
|
|
5338
|
-
*
|
|
5339
|
-
* Each bucket's `clone_groups` and `clone_families` carry the typed
|
|
5340
|
-
* finding wrappers ([`crate::output_dupes::AttributedCloneGroupFinding`],
|
|
5341
|
-
* [`crate::output_dupes::CloneFamilyFinding`]) so the `actions[]`
|
|
5342
|
-
* augmentation is part of the schema-derived contract.
|
|
5343
|
-
*/
|
|
5344
4306
|
groups?: (DuplicationGroup[] | null)
|
|
5345
4307
|
/**
|
|
5346
4308
|
* `_meta` block with metric / rule definitions, emitted when `--explain`
|
|
@@ -5466,19 +4428,8 @@ schema_version: SchemaVersion
|
|
|
5466
4428
|
version: ToolVersion
|
|
5467
4429
|
elapsed_ms: ElapsedMs
|
|
5468
4430
|
grouped_by: GroupByMode
|
|
5469
|
-
/**
|
|
5470
|
-
* Total number of issues across all groups.
|
|
5471
|
-
*/
|
|
5472
4431
|
total_issues: number
|
|
5473
|
-
/**
|
|
5474
|
-
* One entry per group; each contains the same issue arrays as
|
|
5475
|
-
* `CheckOutput` plus the group key and per-group total.
|
|
5476
|
-
*/
|
|
5477
4432
|
groups: CheckGroupedEntry[]
|
|
5478
|
-
/**
|
|
5479
|
-
* `_meta` block with metric / rule definitions, emitted when `--explain`
|
|
5480
|
-
* is passed.
|
|
5481
|
-
*/
|
|
5482
4433
|
_meta?: (Meta | null)
|
|
5483
4434
|
}
|
|
5484
4435
|
/**
|
|
@@ -5487,23 +4438,8 @@ _meta?: (Meta | null)
|
|
|
5487
4438
|
* `AnalysisResults`.
|
|
5488
4439
|
*/
|
|
5489
4440
|
export interface CheckGroupedEntry {
|
|
5490
|
-
/**
|
|
5491
|
-
* Group identifier produced by the resolver. For `package` grouping:
|
|
5492
|
-
* workspace package name. For `owner` grouping: the CODEOWNERS team.
|
|
5493
|
-
* For `directory` grouping: the top-level directory prefix. For
|
|
5494
|
-
* `section` grouping: the GitLab CODEOWNERS section name (or
|
|
5495
|
-
* `(no section)` / `(unowned)` for unmatched files).
|
|
5496
|
-
*/
|
|
5497
4441
|
key: string
|
|
5498
|
-
/**
|
|
5499
|
-
* Section default owners (GitLab CODEOWNERS `[Section] @owner1
|
|
5500
|
-
* @owner2`). Emitted only when `grouped_by` is `section`. Empty for
|
|
5501
|
-
* the `(no section)` and `(unowned)` buckets.
|
|
5502
|
-
*/
|
|
5503
4442
|
owners?: (string[] | null)
|
|
5504
|
-
/**
|
|
5505
|
-
* Total number of issues in this group.
|
|
5506
|
-
*/
|
|
5507
4443
|
total_issues: number
|
|
5508
4444
|
/**
|
|
5509
4445
|
* Files not reachable from any entry point. Wrapped in
|
|
@@ -5746,101 +4682,180 @@ total_delta: number
|
|
|
5746
4682
|
previous_total: number
|
|
5747
4683
|
current_total: number
|
|
5748
4684
|
}
|
|
5749
|
-
/**
|
|
5750
|
-
* A blocked-then-cleared containment: fallow stopped a commit until it was fixed.
|
|
5751
|
-
*/
|
|
5752
4685
|
export interface ContainmentEvent {
|
|
5753
4686
|
blocked_at: string
|
|
5754
4687
|
cleared_at: string
|
|
5755
4688
|
git_sha?: (string | null)
|
|
5756
4689
|
blocked_counts: ImpactCounts
|
|
5757
4690
|
}
|
|
4691
|
+
export interface ResolutionEvent {
|
|
4692
|
+
kind: string
|
|
4693
|
+
path: string
|
|
4694
|
+
symbol?: (string | null)
|
|
4695
|
+
git_sha?: (string | null)
|
|
4696
|
+
timestamp: string
|
|
4697
|
+
}
|
|
5758
4698
|
/**
|
|
5759
|
-
*
|
|
4699
|
+
* The `fallow security --format json` envelope. `security_findings` is the
|
|
4700
|
+
* unique required field used for untagged narrowing in `FallowOutput`.
|
|
5760
4701
|
*/
|
|
5761
|
-
export interface
|
|
4702
|
+
export interface SecurityOutput {
|
|
4703
|
+
schema_version: SecuritySchemaVersion
|
|
5762
4704
|
/**
|
|
5763
|
-
*
|
|
4705
|
+
* Security candidates. Paths are project-root-relative, forward-slash.
|
|
5764
4706
|
*/
|
|
5765
|
-
|
|
4707
|
+
security_findings: SecurityFinding[]
|
|
4708
|
+
/**
|
|
4709
|
+
* In-band blind spot: number of `"use client"` files whose transitive
|
|
4710
|
+
* import cone contains a dynamic `import()` the reachability BFS could not
|
|
4711
|
+
* follow. A leak hidden behind such an edge would not be reported, so a
|
|
4712
|
+
* zero finding count with a non-zero value here is NOT a clean bill.
|
|
4713
|
+
*/
|
|
4714
|
+
unresolved_edge_files: number
|
|
5766
4715
|
/**
|
|
5767
|
-
*
|
|
4716
|
+
* In-band blind spot: number of sink-shaped nodes the catalogue detector
|
|
4717
|
+
* could not flatten to a static callee path (dynamic dispatch, computed
|
|
4718
|
+
* members, aliased bindings). A zero finding count with a non-zero value
|
|
4719
|
+
* here is NOT a clean bill.
|
|
4720
|
+
*/
|
|
4721
|
+
unresolved_callee_sites: number
|
|
4722
|
+
}
|
|
4723
|
+
/**
|
|
4724
|
+
* A local security CANDIDATE for downstream agent verification, NOT a verified
|
|
4725
|
+
* vulnerability. Emitted only by `fallow security`, never under bare `fallow`
|
|
4726
|
+
* or the `audit` gate. There is deliberately no `confidence` or
|
|
4727
|
+
* `signal_strength` field: fallow does not prove exploitability, so the trace
|
|
4728
|
+
* (its hops and length) is the only honest signal.
|
|
4729
|
+
*/
|
|
4730
|
+
export interface SecurityFinding {
|
|
4731
|
+
kind: SecurityFindingKind
|
|
4732
|
+
/**
|
|
4733
|
+
* The catalogue category id (e.g. `"dangerous-html"`). `None` for
|
|
4734
|
+
* `ClientServerLeak`; `Some` for `TaintedSink`.
|
|
4735
|
+
*/
|
|
4736
|
+
category?: (string | null)
|
|
4737
|
+
/**
|
|
4738
|
+
* The CWE number declared by the matched catalogue entry. `None` for
|
|
4739
|
+
* `ClientServerLeak`; never fabricated beyond the catalogue's value.
|
|
4740
|
+
*/
|
|
4741
|
+
cwe?: (number | null)
|
|
4742
|
+
/**
|
|
4743
|
+
* File the finding is anchored on (the client boundary). Absolute
|
|
4744
|
+
* internally; JSON strips the project root via `serde_path::serialize`.
|
|
5768
4745
|
*/
|
|
5769
4746
|
path: string
|
|
5770
4747
|
/**
|
|
5771
|
-
*
|
|
5772
|
-
* one. `None` for file-level and content-hash-keyed findings (duplication).
|
|
4748
|
+
* 1-based line number of the anchor.
|
|
5773
4749
|
*/
|
|
5774
|
-
|
|
4750
|
+
line: number
|
|
5775
4751
|
/**
|
|
5776
|
-
*
|
|
4752
|
+
* 0-based byte column offset of the anchor.
|
|
5777
4753
|
*/
|
|
5778
|
-
|
|
4754
|
+
col: number
|
|
5779
4755
|
/**
|
|
5780
|
-
*
|
|
4756
|
+
* Agent/human-readable evidence (e.g. the named env var the chain reaches).
|
|
5781
4757
|
*/
|
|
5782
|
-
|
|
5783
|
-
}
|
|
4758
|
+
evidence: string
|
|
5784
4759
|
/**
|
|
5785
|
-
*
|
|
5786
|
-
*
|
|
5787
|
-
*
|
|
5788
|
-
*
|
|
5789
|
-
*
|
|
5790
|
-
*
|
|
5791
|
-
*
|
|
5792
|
-
* `
|
|
5793
|
-
* emitted via `crate::output_dupes::DupesReportPayload::from_report`, and
|
|
5794
|
-
* `health` is the bare [`HealthReport`] body: the runtime emit calls
|
|
5795
|
-
* `serde_json::to_value(&report)` directly rather than wrapping it in the
|
|
5796
|
-
* per-command envelope. The committed schema points `dupes` at
|
|
5797
|
-
* `#/definitions/DupesReportPayload` and `health` at
|
|
5798
|
-
* `#/definitions/HealthReport` so the documented shape matches the
|
|
5799
|
-
* wire; the `committed_property_refs_match_derived_property_refs`
|
|
5800
|
-
* drift test enforces the alignment.
|
|
4760
|
+
* Whether the sink argument was associated with a known untrusted source by
|
|
4761
|
+
* the intra-module source-to-sink back-trace (issue #859): a local binding
|
|
4762
|
+
* referenced in the argument was sourced from a catalogue source path
|
|
4763
|
+
* (`req.query`, `process.argv`, message-event `data`, etc.). `true` ranks
|
|
4764
|
+
* the candidate higher and annotates the evidence; `false` does NOT
|
|
4765
|
+
* suppress the finding (the association is conservative, never a proof, and
|
|
4766
|
+
* fallow prefers false-negatives over false-positives). Always `false` for
|
|
4767
|
+
* `ClientServerLeak`. Skipped from JSON when `false` for output stability.
|
|
5801
4768
|
*/
|
|
5802
|
-
|
|
5803
|
-
schema_version: SchemaVersion
|
|
5804
|
-
version: ToolVersion
|
|
5805
|
-
elapsed_ms: ElapsedMs
|
|
4769
|
+
source_backed?: boolean
|
|
5806
4770
|
/**
|
|
5807
|
-
*
|
|
5808
|
-
*
|
|
5809
|
-
*
|
|
4771
|
+
* Structural import-hop trace from the client boundary to the secret source.
|
|
4772
|
+
* The hop count is the uncalibrated signal; fallow does not prove the path
|
|
4773
|
+
* is exploitable.
|
|
5810
4774
|
*/
|
|
5811
|
-
|
|
4775
|
+
trace: TraceHop[]
|
|
5812
4776
|
/**
|
|
5813
|
-
*
|
|
4777
|
+
* Machine-actionable next steps. Always emitted (possibly empty for
|
|
4778
|
+
* forward-compat). For security candidates this is a single file-level
|
|
4779
|
+
* suppress hint (`auto_fixable: false`); there is no auto-fix because
|
|
4780
|
+
* verification is the agent's job, not fallow's.
|
|
5814
4781
|
*/
|
|
5815
|
-
|
|
4782
|
+
actions: IssueAction[]
|
|
5816
4783
|
/**
|
|
5817
|
-
*
|
|
5818
|
-
*
|
|
5819
|
-
*
|
|
5820
|
-
*
|
|
4784
|
+
* Graph-derived reachability ranking signal (issue #860). `None` until the
|
|
4785
|
+
* post-detection ranking pass fills it; additive on the wire (skipped when
|
|
4786
|
+
* absent). Drives the order findings are emitted in: candidates reachable
|
|
4787
|
+
* from a runtime entry point with a wider blast radius sort first.
|
|
5821
4788
|
*/
|
|
5822
|
-
|
|
4789
|
+
reachability?: (SecurityReachability | null)
|
|
4790
|
+
}
|
|
5823
4791
|
/**
|
|
5824
|
-
*
|
|
5825
|
-
*
|
|
4792
|
+
* One hop in a security finding's structural trace. Stored as an absolute path
|
|
4793
|
+
* internally; JSON serialization strips the project root via
|
|
4794
|
+
* `serde_path::serialize`.
|
|
5826
4795
|
*/
|
|
5827
|
-
|
|
4796
|
+
export interface TraceHop {
|
|
4797
|
+
/**
|
|
4798
|
+
* File on this hop of the import chain.
|
|
4799
|
+
*/
|
|
4800
|
+
path: string
|
|
4801
|
+
/**
|
|
4802
|
+
* 1-based line number. Import-chain hops point at the import site; the
|
|
4803
|
+
* terminal secret-source hop points at the source module when extraction
|
|
4804
|
+
* does not carry a more precise member-access span.
|
|
4805
|
+
*/
|
|
4806
|
+
line: number
|
|
4807
|
+
/**
|
|
4808
|
+
* 0-based byte column offset.
|
|
4809
|
+
*/
|
|
4810
|
+
col: number
|
|
4811
|
+
role: TraceHopRole
|
|
5828
4812
|
}
|
|
5829
4813
|
/**
|
|
5830
|
-
*
|
|
4814
|
+
* Graph-derived reachability ranking signal for a security candidate. Computed
|
|
4815
|
+
* from the EXISTING module graph (runtime reachability + reverse-dep fan-in)
|
|
4816
|
+
* after detection, never proven exploitable. Used to surface candidates that
|
|
4817
|
+
* sit on a request/runtime-reachable surface above isolated helpers or scripts.
|
|
4818
|
+
*
|
|
4819
|
+
* This is a relative-ordering signal, NOT a `confidence` or `signal_strength`
|
|
4820
|
+
* score: fallow does not prove the path is exploitable.
|
|
5831
4821
|
*/
|
|
5832
|
-
export interface
|
|
4822
|
+
export interface SecurityReachability {
|
|
5833
4823
|
/**
|
|
5834
|
-
*
|
|
4824
|
+
* Whether the anchor module is reachable from a runtime/application entry
|
|
4825
|
+
* point (route handlers, server entry, framework runtime roots), the
|
|
4826
|
+
* closest graph proxy for an external/request input surface. Code reachable
|
|
4827
|
+
* only from test entry points does not count.
|
|
5835
4828
|
*/
|
|
5836
|
-
|
|
4829
|
+
reachable_from_entry: boolean
|
|
5837
4830
|
/**
|
|
5838
|
-
*
|
|
4831
|
+
* Number of distinct modules that transitively depend on the anchor module
|
|
4832
|
+
* (fan-in via the graph's reverse-dependency index). A higher value means a
|
|
4833
|
+
* wider surface: more call sites could route untrusted input into the sink.
|
|
5839
4834
|
*/
|
|
5840
|
-
|
|
4835
|
+
blast_radius: number
|
|
5841
4836
|
/**
|
|
5842
|
-
*
|
|
4837
|
+
* Whether the anchor module participates in an architecture-boundary
|
|
4838
|
+
* violation found in the same run (as the importing or imported file).
|
|
4839
|
+
* Optional pairing: a candidate that also crosses a declared boundary is a
|
|
4840
|
+
* stronger review target.
|
|
5843
4841
|
*/
|
|
4842
|
+
crosses_boundary: boolean
|
|
4843
|
+
}
|
|
4844
|
+
/**
|
|
4845
|
+
* Bare `fallow --format json` envelope.
|
|
4846
|
+
*/
|
|
4847
|
+
export interface CombinedOutput {
|
|
4848
|
+
schema_version: SchemaVersion
|
|
4849
|
+
version: ToolVersion
|
|
4850
|
+
elapsed_ms: ElapsedMs
|
|
4851
|
+
_meta?: (CombinedMeta | null)
|
|
4852
|
+
check?: (CheckOutput | null)
|
|
4853
|
+
dupes?: (DupesReportPayload | null)
|
|
4854
|
+
health?: (HealthReport | null)
|
|
4855
|
+
}
|
|
4856
|
+
export interface CombinedMeta {
|
|
4857
|
+
check?: (Meta | null)
|
|
4858
|
+
dupes?: (Meta | null)
|
|
5844
4859
|
health?: (Meta | null)
|
|
5845
4860
|
}
|
|
5846
4861
|
/**
|
|
@@ -5848,23 +4863,10 @@ health?: (Meta | null)
|
|
|
5848
4863
|
*/
|
|
5849
4864
|
export interface CodeClimateIssue {
|
|
5850
4865
|
type: CodeClimateIssueKind
|
|
5851
|
-
/**
|
|
5852
|
-
* Fallow rule identifier (always starts with `fallow/`).
|
|
5853
|
-
*/
|
|
5854
4866
|
check_name: string
|
|
5855
|
-
/**
|
|
5856
|
-
* Human-readable description of the finding.
|
|
5857
|
-
*/
|
|
5858
4867
|
description: string
|
|
5859
|
-
/**
|
|
5860
|
-
* Free-form categories applied by the report renderer.
|
|
5861
|
-
*/
|
|
5862
4868
|
categories: string[]
|
|
5863
4869
|
severity: CodeClimateSeverity
|
|
5864
|
-
/**
|
|
5865
|
-
* Stable fingerprint used by CI dashboards to deduplicate findings
|
|
5866
|
-
* across runs.
|
|
5867
|
-
*/
|
|
5868
4870
|
fingerprint: string
|
|
5869
4871
|
location: CodeClimateLocation
|
|
5870
4872
|
}
|