fallow 2.84.0 → 2.86.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.
@@ -25,33 +25,20 @@
25
25
 
26
26
 
27
27
  /**
28
- * Schemas for the JSON output of fallow commands. To identify which envelope you have, check for the unique top-level field: `summary.total_issues` (check), `health_score` (health), `clone_groups` (dupes), `runtime_coverage` (coverage analyze), `boundaries` (list --boundaries), `command: "audit"` (audit), `body` plus `comments` (review-github / review-gitlab), `schema: "fallow-review-reconcile/v1"` (ci reconcile-review), `framework_detected` plus `members` (coverage setup), `id` plus `how_to_fix` (explain), `check`+`dupes`+`health` keys together (bare combined invocation). `HealthOutput` and `DupesOutput` flatten their body (`HealthReport` / `DupesReportPayload`) into top-level fields, so the discriminator field is from the body shape itself, not a wrapper key. Every object-shaped envelope is a variant of `FallowOutput`; `CodeClimateOutput` is a bare JSON array (per the Code Climate / GitLab Code Quality spec) and stays a sibling root branch.
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 `--format json` envelope shape that
33
- * serializes as a JSON object. The schema derived from this enum drives
34
- * the document-root `oneOf` in `docs/output-schema.json`, replacing the
35
- * previously hand-maintained block.
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
- * `#[serde(untagged)]` preserves wire compatibility: consumers see exactly
38
- * the same top-level keys today (`schema_version`, `version`, plus the
39
- * per-envelope shape). The schema's `oneOf` lets agents narrow by trying
40
- * variants in order; field sets differ enough that the first matching
41
- * variant is the correct one in practice. Note that [`HealthOutput`] and
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 | ExplainOutput | ReviewEnvelopeOutput | ReviewReconcileOutput | CoverageSetupOutput | CoverageAnalyzeOutput | ListBoundariesOutput | HealthOutput | DupesOutput | CheckGroupedOutput | CheckOutput | CombinedOutput)
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 = 6
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`].
@@ -489,6 +447,32 @@ export type RuntimeCoverageRiskBand = ("low" | "medium" | "high")
489
447
  * License or trial watermark applied to runtime coverage output.
490
448
  */
491
449
  export type RuntimeCoverageWatermark = ("trial-expired" | "license-expired-grace" | "unknown")
450
+ /**
451
+ * Coverage-intelligence JSON contract version. Scoped to the
452
+ * `coverage_intelligence` block and independent of the top-level fallow
453
+ * JSON `schema_version`.
454
+ */
455
+ export type CoverageIntelligenceSchemaVersion = "1"
456
+ /**
457
+ * Headline verdict for the combined coverage-intelligence report.
458
+ */
459
+ export type CoverageIntelligenceVerdict = ("risky-change-detected" | "high-confidence-delete" | "review-required" | "refactor-carefully" | "clean" | "unknown")
460
+ /**
461
+ * Ordered evidence signals behind a coverage-intelligence finding.
462
+ */
463
+ export type CoverageIntelligenceSignal = ("changed" | "hot_path" | "low_test_coverage" | "high_crap" | "static_unused" | "runtime_cold" | "no_test_path" | "runtime_reachable" | "ownership_drift" | "test_covered")
464
+ /**
465
+ * Recommended action family for a combined finding.
466
+ */
467
+ export type CoverageIntelligenceRecommendation = ("add-test-or-split-before-merge" | "delete-after-confirming-owner" | "review-before-changing" | "refactor-carefully-keep-behavior")
468
+ /**
469
+ * Confidence in the joined evidence and resulting recommendation.
470
+ */
471
+ export type CoverageIntelligenceConfidence = ("high" | "medium" | "low")
472
+ /**
473
+ * Confidence tier for the cross-surface evidence match.
474
+ */
475
+ export type CoverageIntelligenceMatchConfidence = ("path-function-line" | "path-line" | "direct")
492
476
  /**
493
477
  * Category of refactoring recommendation.
494
478
  */
@@ -568,34 +552,13 @@ export type ReviewCheckConclusion = ("success" | "neutral" | "failure")
568
552
  * Schema-version discriminator for the review reconcile envelope.
569
553
  */
570
554
  export type ReviewReconcileSchema = "fallow-review-reconcile/v1"
571
- /**
572
- * Singleton schema-version discriminator for [`CoverageSetupOutput`].
573
- */
574
555
  export type CoverageSetupSchemaVersion = "1"
575
- /**
576
- * Framework label inside coverage setup output.
577
- */
578
556
  export type CoverageSetupFramework = ("nextjs" | "nestjs" | "nuxt" | "sveltekit" | "astro" | "remix" | "vite" | "plain_node" | "unknown")
579
- /**
580
- * Package manager label inside coverage setup output.
581
- */
582
557
  export type CoverageSetupPackageManager = ("npm" | "pnpm" | "yarn" | "bun")
583
- /**
584
- * Runtime target inside coverage setup output.
585
- */
586
558
  export type CoverageSetupRuntimeTarget = ("node" | "browser")
587
- /**
588
- * Singleton schema-version discriminator for [`CoverageAnalyzeOutput`].
589
- * Independent from the global [`SchemaVersion`] because the runtime
590
- * coverage envelope versions independently from the rest of the
591
- * JSON contract.
592
- */
593
559
  export type CoverageAnalyzeSchemaVersion = "1"
594
560
  /**
595
- * Discovery outcome for a [`LogicalGroup`]. Discriminates "no children" into
596
- * "the directory exists and is empty" versus "at least one `autoDiscover`
597
- * path was invalid or unreadable", so consumers can render an actionable
598
- * hint instead of "0 children, mystery".
561
+ * Discovery outcome for a [`LogicalGroup`].
599
562
  */
600
563
  export type LogicalGroupStatus = ("ok" | "empty" | "invalid_path")
601
564
  /**
@@ -606,6 +569,33 @@ export type LogicalGroupStatus = ("ok" | "empty" | "invalid_path")
606
569
  * groups by GitLab CODEOWNERS `[Section]` header name.
607
570
  */
608
571
  export type GroupByMode = ("owner" | "directory" | "package" | "section")
572
+ /**
573
+ * Wire-version discriminator for [`ImpactReport`]. Independent from the global
574
+ * `SchemaVersion` (the impact report versions on its own cadence) and from the
575
+ * on-disk `STORE_SCHEMA_VERSION` (the persisted store shape versions
576
+ * separately). Serializes as a string `const` so JSON consumers can switch on
577
+ * it, matching the other independently-versioned envelopes (e.g.
578
+ * `CoverageAnalyzeSchemaVersion`).
579
+ */
580
+ export type ImpactReportSchemaVersion = "1"
581
+ /**
582
+ * Direction of a count trend between two recorded runs.
583
+ */
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")
609
599
  /**
610
600
  * Discriminator value for [`CodeClimateIssue::kind`].
611
601
  */
@@ -622,69 +612,22 @@ export type CodeClimateSeverity = ("info" | "minor" | "major" | "critical" | "bl
622
612
  export type CodeClimateOutput = CodeClimateIssue[]
623
613
 
624
614
  /**
625
- * Envelope emitted by `fallow audit --format json`. Combines dead code,
626
- * complexity, and duplication scoped to changed files with a verdict
627
- * (`pass` / `warn` / `fail`), a per-category summary, optional
628
- * new-vs-inherited attribution, and full sub-results.
629
- *
630
- * Like [`CombinedOutput`], `audit`'s `duplication` and `complexity`
631
- * sub-keys hold body shapes rather than per-command envelopes:
632
- * `duplication` is [`DupesReportPayload`] (the typed wrapper payload
633
- * emitted via `crate::output_dupes::DupesReportPayload::from_report`),
634
- * `complexity` is [`HealthReport`]. `dead_code` is the full
635
- * [`CheckOutput`] envelope. The committed schema points `duplication`
636
- * at `#/definitions/DupesReportPayload` and `complexity` at
637
- * `#/definitions/HealthReport` so the documented shape matches the
638
- * wire; the `committed_property_refs_match_derived_property_refs`
639
- * drift test enforces the alignment.
615
+ * `fallow audit --format json` envelope.
640
616
  */
641
617
  export interface AuditOutput {
642
618
  schema_version: SchemaVersion
643
619
  version: ToolVersion
644
620
  command: AuditCommand
645
621
  verdict: AuditVerdict
646
- /**
647
- * Number of files changed between base ref and HEAD.
648
- */
649
622
  changed_files_count: number
650
- /**
651
- * Git ref used as comparison base (explicit or auto-detected).
652
- */
653
623
  base_ref: string
654
- /**
655
- * Short SHA of HEAD. Omitted when git is unavailable.
656
- */
657
624
  head_sha?: (string | null)
658
625
  elapsed_ms: ElapsedMs
659
- /**
660
- * Only emitted when --performance is set. true means audit reused the
661
- * current run's keys as the base snapshot because every changed file was
662
- * either a non-behavioral doc or token-equivalent at the base ref (the
663
- * docs-only-diff fast path); false means the regular base worktree
664
- * analysis ran.
665
- */
666
626
  base_snapshot_skipped?: (boolean | null)
667
627
  summary: AuditSummary
668
628
  attribution: AuditAttribution
669
- /**
670
- * Full dead code results (omitted if no changed files). Issue objects
671
- * include introduced: true/false when audit can compare against the base
672
- * ref.
673
- */
674
629
  dead_code?: (CheckOutput | null)
675
- /**
676
- * Full duplication results (omitted if no changed files). Clone groups
677
- * include introduced: true/false when audit can compare against the base
678
- * ref. Carries typed [`crate::output_dupes::CloneGroupFinding`] and
679
- * [`crate::output_dupes::CloneFamilyFinding`] wrappers (matches what
680
- * `crates/cli/src/audit.rs` emits via
681
- * `crate::output_dupes::DupesReportPayload::from_report`).
682
- */
683
630
  duplication?: (DupesReportPayload | null)
684
- /**
685
- * Full complexity results (omitted if no changed files). Findings include
686
- * introduced: true/false when audit can compare against the base ref.
687
- */
688
631
  complexity?: (HealthReport | null)
689
632
  }
690
633
  /**
@@ -724,14 +667,7 @@ export interface CheckOutput {
724
667
  schema_version: SchemaVersion
725
668
  version: ToolVersion
726
669
  elapsed_ms: ElapsedMs
727
- /**
728
- * Total number of issues found across all categories.
729
- */
730
670
  total_issues: number
731
- /**
732
- * Entry-point detection summary. Present when the analysis populated
733
- * the metadata block; absent in synthesised fixtures.
734
- */
735
671
  entry_points?: (EntryPoints | null)
736
672
  summary: CheckSummary
737
673
  /**
@@ -886,34 +822,10 @@ unused_dependency_overrides?: UnusedDependencyOverrideFinding[]
886
822
  * error. Wrapped in [`MisconfiguredDependencyOverrideFinding`].
887
823
  */
888
824
  misconfigured_dependency_overrides?: MisconfiguredDependencyOverrideFinding[]
889
- /**
890
- * Per-category delta comparison against a saved baseline. Only present
891
- * when `--baseline` is used (today only via the combined invocation).
892
- */
893
825
  baseline_deltas?: (BaselineDeltas | null)
894
- /**
895
- * Baseline match statistics. Only present when `--baseline` is used.
896
- */
897
826
  baseline?: (BaselineMatch | null)
898
- /**
899
- * Regression check result. Only present when `--fail-on-regression` is
900
- * used.
901
- */
902
827
  regression?: (RegressionResult | null)
903
- /**
904
- * `_meta` block with metric / rule definitions, emitted when `--explain`
905
- * is passed (always present in MCP responses).
906
- */
907
828
  _meta?: (Meta | null)
908
- /**
909
- * Workspace-discovery diagnostics surfaced by
910
- * `discover_workspaces_with_diagnostics` (issue #473): malformed
911
- * declared-workspace `package.json`, glob matches with no `package.json`,
912
- * malformed `tsconfig.json`, missing tsconfig reference paths. Omitted
913
- * when empty so consumers on monorepos without discovery noise see no
914
- * new field. Pairing of `#[serde(default, skip_serializing_if = ...)]`
915
- * is required for schemars to mark the field non-required.
916
- */
917
829
  workspace_diagnostics?: WorkspaceDiagnostic[]
918
830
  }
919
831
  /**
@@ -2281,6 +2193,13 @@ token_count: number
2281
2193
  * Number of lines in the duplicated block.
2282
2194
  */
2283
2195
  line_count: number
2196
+ /**
2197
+ * Stable content fingerprint, usually `dup:<8hex>` and widened on rare
2198
+ * report collisions. Addressable via `fallow dupes --trace dup:<fp>` (and
2199
+ * the `trace_clone` MCP tool) to deep-dive this group; shown alongside
2200
+ * each group in the human listing.
2201
+ */
2202
+ fingerprint: string
2284
2203
  /**
2285
2204
  * Suggested next steps: an `extract-shared` primary and a
2286
2205
  * `suppress-line` secondary. Always emitted (possibly empty for
@@ -2561,6 +2480,10 @@ hotspot_summary?: (HotspotSummary | null)
2561
2480
  * `--runtime-coverage`).
2562
2481
  */
2563
2482
  runtime_coverage?: (RuntimeCoverageReport | null)
2483
+ /**
2484
+ * Combined coverage, runtime, complexity, and change-scope verdicts.
2485
+ */
2486
+ coverage_intelligence?: (CoverageIntelligenceReport | null)
2564
2487
  /**
2565
2488
  * Functions exceeding 60 LOC (very high risk). Only present when unit size
2566
2489
  * very-high-risk bin >= 3%. Sorted by line count descending.
@@ -2593,177 +2516,41 @@ actions_meta?: (HealthActionsMeta | null)
2593
2516
  }
2594
2517
  /**
2595
2518
  * Wire envelope for a single complexity finding.
2596
- *
2597
- * Flattens [`ComplexityViolation`] for wire continuity and adds the typed
2598
- * `actions` list plus the audit-mode `introduced` flag. The
2599
- * `#[serde(flatten)]` keeps each `findings[]` item byte-identical to the
2600
- * pre-wrapper shape: inner fields (`path`, `name`, `line`, `cyclomatic`,
2601
- * ...) sit at the top level alongside `actions` and optional `introduced`.
2602
- *
2603
- * Construct via [`HealthFinding::with_actions`] in the typical health
2604
- * pipeline (the wrapper computes its own `actions` from a
2605
- * [`HealthActionContext`]) or via [`HealthFinding::new`] when the caller
2606
- * already has the action list (e.g., tests, audit cross-attribution).
2607
2519
  */
2608
2520
  export interface HealthFinding {
2609
- /**
2610
- * Absolute file path.
2611
- */
2612
2521
  path: string
2613
- /**
2614
- * Function name, `"<anonymous>"` for unnamed functions/arrows, or
2615
- * `"<template>"` for synthetic Angular template findings.
2616
- */
2617
2522
  name: string
2618
- /**
2619
- * 1-based line number.
2620
- */
2621
2523
  line: number
2622
- /**
2623
- * 0-based column.
2624
- */
2625
2524
  col: number
2626
- /**
2627
- * Cyclomatic complexity.
2628
- */
2629
2525
  cyclomatic: number
2630
- /**
2631
- * SonarSource cognitive complexity (structural + nesting penalty).
2632
- */
2633
2526
  cognitive: number
2634
- /**
2635
- * Number of lines in the function.
2636
- */
2637
2527
  line_count: number
2638
- /**
2639
- * Number of parameters (excluding TypeScript's this parameter).
2640
- */
2641
2528
  param_count: number
2642
2529
  exceeded: ExceededThreshold
2643
2530
  severity: FindingSeverity
2644
- /**
2645
- * CRAP score (`CC^2 * (1 - cov/100)^3 + CC`), rounded to one decimal.
2646
- * Present when the function also exceeded `--max-crap`, otherwise absent.
2647
- */
2648
2531
  crap?: (number | null)
2649
- /**
2650
- * Per-function statement coverage percentage (0.0 to 100.0) used to
2651
- * derive `crap`. Present when Istanbul data matched the function,
2652
- * otherwise absent (estimated model or unmatched functions).
2653
- */
2654
2532
  coverage_pct?: (number | null)
2655
- /**
2656
- * Bucketed coverage tier used to drive action selection. Present whenever
2657
- * CRAP triggered the finding (Istanbul or estimated), absent otherwise.
2658
- * `none` = coverage is at most 0% (file not test-reachable, or Istanbul
2659
- * reports 0); `partial` = coverage is in `(0, 70)`; `high` = coverage is
2660
- * at or above the high watermark (default `>= 70`, or the estimated 85%
2661
- * band).
2662
- */
2663
2533
  coverage_tier?: (CoverageTier | null)
2664
- /**
2665
- * Provenance of the coverage signal. Present whenever CRAP triggered the
2666
- * finding. `istanbul` = direct fnMap match; `estimated` = graph-based
2667
- * estimate against the finding's own file; `estimated_component_inherited`
2668
- * = graph-based estimate inherited from an Angular component `.ts`
2669
- * reached via the inverse `templateUrl` edge (synthetic `<template>`
2670
- * findings on `.html` files only).
2671
- */
2672
2534
  coverage_source?: (CoverageSource | null)
2673
- /**
2674
- * Owning component file that contributed reachability when
2675
- * `coverage_source == "estimated_component_inherited"`. Always paired
2676
- * with that variant of `coverage_source` and absent otherwise. The
2677
- * value is the `.ts` file fallow walked to via the inverse `templateUrl`
2678
- * edge (e.g. `permissions.component.ts`); the JSON serializer strips it
2679
- * to project-relative form just like other path fields. Lets human and
2680
- * AI consumers explain "the template scored partial because the
2681
- * component it belongs to is tested" without re-deriving the link.
2682
- */
2683
2535
  inherited_from?: (string | null)
2684
- /**
2685
- * Breakdown of a synthetic `<component>` rollup finding into its
2686
- * worst-class-function and template contributions. Present only on
2687
- * findings whose [`name`](Self::name) is the literal string
2688
- * `"<component>"` (Angular components whose class AND template both
2689
- * contributed to a per-component complexity rollup); absent on every
2690
- * other finding kind.
2691
- *
2692
- * The owning [`HealthFinding`](crate::health_types::HealthFinding)'s
2693
- * [`cyclomatic`](Self::cyclomatic) / [`cognitive`](Self::cognitive)
2694
- * totals are `class_worst_function + template`, so consumers ranking
2695
- * by complexity see the component as one unit. The breakdown carries
2696
- * the pre-summation numbers plus the worst class function's name so
2697
- * consumers can explain "this component ranked high because the
2698
- * template added 6 cyclomatic on top of the worst class function's 3".
2699
- */
2700
2536
  component_rollup?: (ComponentRollup | null)
2701
2537
  /**
2702
- * Machine-actionable fix and suppress hints. Always populated; never
2703
- * empty in the typical pipeline (the action selector emits at least
2704
- * `suppress-line` or `suppress-file` unless suppressed by the
2705
- * context).
2538
+ * Machine-actionable fix and suppress hints.
2706
2539
  */
2707
2540
  actions: HealthFindingAction[]
2708
2541
  /**
2709
- * Audit-mode flag indicating whether the finding is new versus the
2710
- * audit base snapshot. `Some(true)` when introduced in the diff,
2711
- * `Some(false)` when present in both snapshots, `None` outside audit
2712
- * mode (the field is skipped from the wire).
2542
+ * Audit-mode flag indicating whether the finding is new versus the base
2543
+ * snapshot.
2713
2544
  */
2714
2545
  introduced?: (boolean | null)
2715
2546
  }
2716
- /**
2717
- * Per-component breakdown attached to a synthetic `<component>`
2718
- * [`HealthFinding`](crate::health_types::HealthFinding). See
2719
- * [`ComplexityViolation::component_rollup`] for the owning-finding
2720
- * contract; the wrapper flattens the inner type's
2721
- * [`component_rollup`](ComplexityViolation::component_rollup) field
2722
- * onto its own wire shape.
2723
- */
2724
2547
  export interface ComponentRollup {
2725
- /**
2726
- * Angular component class name (e.g. `"HostGameComponent"`). Derived
2727
- * from the worst class function's `ClassName.methodName` identifier.
2728
- */
2729
2548
  component: string
2730
- /**
2731
- * Name of the worst class function/method whose individual cyclomatic
2732
- * is the largest among the component's class findings (e.g.
2733
- * `"ngOnInit"`). When two methods tie on cyclomatic the first by
2734
- * iteration order wins; consumers should treat the choice as
2735
- * representative, not authoritative.
2736
- */
2737
2549
  class_worst_function: string
2738
- /**
2739
- * Cyclomatic complexity of the worst class function alone (the
2740
- * `class_worst_function`).
2741
- */
2742
2550
  class_cyclomatic: number
2743
- /**
2744
- * Cognitive complexity of the worst class function alone.
2745
- */
2746
2551
  class_cognitive: number
2747
- /**
2748
- * Path of the Angular template that contributed to the rollup.
2749
- * External-template components use the `.html` template file path;
2750
- * inline-template components use the owning `.ts` itself (since the
2751
- * `<template>` finding for inline templates is anchored at the
2752
- * component's `@Component` decorator on the same file). Stored
2753
- * absolute internally; the JSON output strips it to project-relative
2754
- * form via the global `strip_root_prefix` post-pass (as with every
2755
- * other `PathBuf` field in this crate).
2756
- */
2757
2552
  template_path: string
2758
- /**
2759
- * Cyclomatic complexity contributed by the template alone (control
2760
- * flow on `*ngIf` / `*ngFor` / `@if` / `@for` / `@switch` etc.).
2761
- */
2762
2553
  template_cyclomatic: number
2763
- /**
2764
- * Cognitive complexity contributed by the template alone (nesting +
2765
- * branching penalty on the same constructs as `template_cyclomatic`).
2766
- */
2767
2554
  template_cognitive: number
2768
2555
  }
2769
2556
  /**
@@ -2832,77 +2619,20 @@ target_path?: (string | null)
2832
2619
  * Summary statistics for the health report.
2833
2620
  */
2834
2621
  export interface HealthSummary {
2835
- /**
2836
- * Number of files analyzed.
2837
- */
2838
2622
  files_analyzed: number
2839
- /**
2840
- * Total number of functions found.
2841
- */
2842
2623
  functions_analyzed: number
2843
- /**
2844
- * Number of functions exceeding at least one threshold (before --top
2845
- * truncation).
2846
- */
2847
2624
  functions_above_threshold: number
2848
- /**
2849
- * Configured cyclomatic threshold.
2850
- */
2851
2625
  max_cyclomatic_threshold: number
2852
- /**
2853
- * Configured cognitive threshold.
2854
- */
2855
2626
  max_cognitive_threshold: number
2856
- /**
2857
- * Configured CRAP (Change Risk Anti-Patterns) score threshold. Functions
2858
- * meeting or exceeding this score appear as findings with the `crap` and
2859
- * optional `coverage_pct` fields populated.
2860
- */
2861
2627
  max_crap_threshold: number
2862
- /**
2863
- * Number of files with health scores. Only present when --file-scores is
2864
- * used. 0 indicates the flag was set but scoring failed.
2865
- */
2866
2628
  files_scored?: (number | null)
2867
- /**
2868
- * Average maintainability index across all scored files (before --top
2869
- * truncation). Only present when --file-scores is used and at least one
2870
- * file was scored.
2871
- */
2872
2629
  average_maintainability?: (number | null)
2873
- /**
2874
- * Coverage model used for CRAP score computation. 'static_estimated'
2875
- * (default) uses per-function graph-based estimation from export
2876
- * references: directly test-referenced = 85%, indirectly reachable = 40%,
2877
- * untested = 0%. 'istanbul' uses real per-function statement coverage from
2878
- * a coverage-final.json file (--coverage flag or auto-detected).
2879
- * 'static_binary' is the legacy binary model. Only present when file
2880
- * scores are computed.
2881
- */
2882
2630
  coverage_model?: (CoverageModel | null)
2883
- /**
2884
- * Number of functions matched against Istanbul coverage data.
2885
- * Only present when `coverage_model` is `istanbul`.
2886
- */
2631
+ coverage_source_consistency?: (CoverageSourceConsistency | null)
2887
2632
  istanbul_matched?: (number | null)
2888
- /**
2889
- * Total functions that could potentially be matched.
2890
- * Only present when `coverage_model` is `istanbul`.
2891
- */
2892
2633
  istanbul_total?: (number | null)
2893
- /**
2894
- * Number of findings with critical severity (cognitive >= 40 or cyclomatic
2895
- * >= 50).
2896
- */
2897
2634
  severity_critical_count: number
2898
- /**
2899
- * Number of findings with high severity (cognitive 25-39 or cyclomatic
2900
- * 30-49).
2901
- */
2902
2635
  severity_high_count: number
2903
- /**
2904
- * Number of findings with moderate severity.
2905
- */
2906
2636
  severity_moderate_count: number
2907
2637
  }
2908
2638
  /**
@@ -3053,173 +2783,43 @@ high_risk: number
3053
2783
  */
3054
2784
  very_high_risk: number
3055
2785
  }
3056
- /**
3057
- * Project-level health score. Score = 100 minus available penalties from dead
3058
- * code, complexity, maintainability, hotspots, unused deps, circular deps,
3059
- * unit size, coupling, and duplication. Missing metrics do not penalize;
3060
- * --score computes the score and duplication penalty, while churn-backed
3061
- * hotspot penalties require hotspot analysis (--hotspots, or --targets with
3062
- * --score).
3063
- */
3064
2786
  export interface HealthScore {
3065
- /**
3066
- * Health score formula version. Version 2 uses scale-invariant
3067
- * density/tail metrics for monorepo-safe scoring.
3068
- */
3069
2787
  formula_version: number
3070
- /**
3071
- * Overall score (0-100, higher is better). Reproducible: 100 -
3072
- * sum(penalties) == score.
3073
- */
3074
2788
  score: number
3075
- /**
3076
- * Letter grade. A: score >= 85, B: 70-84, C: 55-69, D: 40-54, F: below 40.
3077
- */
3078
2789
  grade: string
3079
2790
  penalties: HealthScorePenalties
3080
2791
  }
3081
2792
  /**
3082
2793
  * Per-component penalty breakdown for the health score.
3083
- *
3084
- * Each field shows how many points were subtracted for that component.
3085
- * `None` means the metric was not available (pipeline didn't run).
3086
2794
  */
3087
2795
  export interface HealthScorePenalties {
3088
- /**
3089
- * Points lost from dead files (max 15). Null if dead code pipeline not
3090
- * run.
3091
- */
3092
2796
  dead_files?: (number | null)
3093
- /**
3094
- * Points lost from dead exports (max 15). Null if dead code pipeline not
3095
- * run.
3096
- */
3097
2797
  dead_exports?: (number | null)
3098
- /**
3099
- * Points lost from critical-complexity density (max 20). Older snapshots
3100
- * without density fields fall back to average cyclomatic complexity above
3101
- * 1.5.
3102
- */
3103
2798
  complexity: number
3104
- /**
3105
- * Points lost from legacy p90 cyclomatic complexity above 10. Current
3106
- * scale-invariant runs report 0 because tail complexity is folded into
3107
- * complexity.
3108
- */
3109
2799
  p90_complexity: number
3110
- /**
3111
- * Points lost from low maintainability index density (max 15).
3112
- */
3113
2800
  maintainability?: (number | null)
3114
- /**
3115
- * Points lost from top-percentile hotspot density (max 10). Null if
3116
- * hotspots not computed.
3117
- */
3118
2801
  hotspots?: (number | null)
3119
- /**
3120
- * Points lost from unused dependency density (max 25). Null if dead code
3121
- * pipeline not run.
3122
- */
3123
2802
  unused_deps?: (number | null)
3124
- /**
3125
- * Points lost from circular dependency density (max 25). Null if dead code
3126
- * pipeline not run.
3127
- */
3128
2803
  circular_deps?: (number | null)
3129
- /**
3130
- * Points lost from oversized-function density (max 10). Null if no
3131
- * functions analyzed.
3132
- */
3133
2804
  unit_size?: (number | null)
3134
- /**
3135
- * Points lost from coupling concentration density (max 5). Null if file
3136
- * scores not computed.
3137
- */
3138
2805
  coupling?: (number | null)
3139
- /**
3140
- * Points lost from code duplication (max 10). Penalty = min(max(0,
3141
- * duplication_pct - 5) * 1, 10). Null if duplication pipeline not run.
3142
- */
3143
2806
  duplication?: (number | null)
3144
2807
  }
3145
2808
  /**
3146
2809
  * Per-file health score combining complexity, coupling, and dead code metrics.
3147
- *
3148
- * Files with zero functions (barrel files, re-export files) are excluded by default.
3149
- *
3150
- * ## Maintainability Index Formula
3151
- *
3152
- * ```text
3153
- * dampening = min(lines / 50, 1.0)
3154
- * fan_out_penalty = min(ln(fan_out + 1) × 4, 15)
3155
- * maintainability = 100
3156
- * - (complexity_density × 30 × dampening)
3157
- * - (dead_code_ratio × 20)
3158
- * - fan_out_penalty
3159
- * ```
3160
- *
3161
- * Clamped to \[0, 100\]. Higher is better. The dampening factor prevents
3162
- * complexity density from dominating the score on small files (< 50 lines).
3163
- *
3164
- * - **complexity_density**: total cyclomatic complexity / lines of code
3165
- * - **dead_code_ratio**: fraction of value exports (excluding type-only exports) with zero references (0.0–1.0)
3166
- * - **fan_out_penalty**: logarithmic scaling with cap at 15 points; reflects diminishing marginal risk of additional imports
3167
2810
  */
3168
2811
  export interface FileHealthScore {
3169
- /**
3170
- * File path (absolute; stripped to relative in output).
3171
- */
3172
2812
  path: string
3173
- /**
3174
- * Number of files that import this file.
3175
- */
3176
2813
  fan_in: number
3177
- /**
3178
- * Number of files this file imports.
3179
- */
3180
2814
  fan_out: number
3181
- /**
3182
- * Fraction of value exports with zero references (0.0–1.0). Files with no value exports get 0.0.
3183
- * Type-only exports (interfaces, type aliases) are excluded from both numerator and denominator
3184
- * to avoid inflating the ratio for well-typed codebases that export props types alongside components.
3185
- */
3186
2815
  dead_code_ratio: number
3187
- /**
3188
- * Total cyclomatic complexity / lines of code.
3189
- */
3190
2816
  complexity_density: number
3191
- /**
3192
- * Weighted composite score (0–100, higher is better).
3193
- */
3194
2817
  maintainability_index: number
3195
- /**
3196
- * Sum of cyclomatic complexity across all functions.
3197
- */
3198
2818
  total_cyclomatic: number
3199
- /**
3200
- * Sum of cognitive complexity across all functions.
3201
- */
3202
2819
  total_cognitive: number
3203
- /**
3204
- * Number of functions in this file.
3205
- */
3206
2820
  function_count: number
3207
- /**
3208
- * Total lines of code (from line_offsets).
3209
- */
3210
2821
  lines: number
3211
- /**
3212
- * Maximum CRAP score among functions in this file. Computed via the active
3213
- * `coverage_model` per the canonical formula CC^2 * (1 - cov/100)^3 + CC
3214
- * (Savoia & Evans, 2007). Coverage source: `static_estimated` (default,
3215
- * graph-based per-function estimate), `istanbul` (real per-function
3216
- * statement coverage from --coverage), or the legacy `static_binary`
3217
- * (whole-file 0%/100%, retained for compatibility).
3218
- */
3219
2822
  crap_max: number
3220
- /**
3221
- * Count of functions with CRAP >= 30 (CC >= 5 without test path).
3222
- */
3223
2823
  crap_above_threshold: number
3224
2824
  }
3225
2825
  /**
@@ -3403,51 +3003,16 @@ comment?: (string | null)
3403
3003
  * test code.
3404
3004
  */
3405
3005
  export interface HotspotFinding {
3406
- /**
3407
- * File path (absolute; stripped to relative in output).
3408
- */
3409
3006
  path: string
3410
- /**
3411
- * Hotspot score (0–100). Higher means more risk.
3412
- */
3413
3007
  score: number
3414
- /**
3415
- * Number of commits in the analysis window.
3416
- */
3417
3008
  commits: number
3418
- /**
3419
- * Recency-weighted commit count (exponential decay, half-life 90 days).
3420
- */
3421
3009
  weighted_commits: number
3422
- /**
3423
- * Total lines added across all commits.
3424
- */
3425
3010
  lines_added: number
3426
- /**
3427
- * Total lines deleted across all commits.
3428
- */
3429
3011
  lines_deleted: number
3430
- /**
3431
- * Cyclomatic complexity / lines of code.
3432
- */
3433
3012
  complexity_density: number
3434
- /**
3435
- * Number of files that import this file (blast radius).
3436
- */
3437
3013
  fan_in: number
3438
3014
  trend: ChurnTrend
3439
- /**
3440
- * Ownership signals (bus factor, contributors, declared owner, drift).
3441
- * Populated only when `--ownership` is requested.
3442
- */
3443
3015
  ownership?: (OwnershipMetrics | null)
3444
- /**
3445
- * True when the file path matches a test/mock convention (e.g.
3446
- * `** /__tests__/**`, `** /*.test.*`, `** /*.spec.*`, `** /__mocks__/**`).
3447
- * Test files are intentionally included in hotspot ranking (test
3448
- * maintenance IS real work), but tagging them lets consumers decide
3449
- * whether to weight or filter them downstream.
3450
- */
3451
3016
  is_test_path?: boolean
3452
3017
  /**
3453
3018
  * Machine-actionable refactor and review hints. Always populated;
@@ -3459,89 +3024,23 @@ is_test_path?: boolean
3459
3024
  */
3460
3025
  actions: HotspotAction[]
3461
3026
  }
3462
- /**
3463
- * Per-file ownership signals attached to hotspot entries when the user
3464
- * passes `--ownership`. All fields are derived from git history and the
3465
- * repository's CODEOWNERS file (if any).
3466
- */
3467
3027
  export interface OwnershipMetrics {
3468
- /**
3469
- * Avelino truck factor: minimum contributors covering at least 50% of
3470
- * recency-weighted commits in the analysis window. Lower = higher
3471
- * knowledge-loss risk.
3472
- */
3473
3028
  bus_factor: number
3474
- /**
3475
- * Distinct authors in the analysis window after bot filtering.
3476
- */
3477
3029
  contributor_count: number
3478
3030
  top_contributor: ContributorEntry
3479
- /**
3480
- * Up to three additional contributors by share, ordered desc.
3481
- * Useful for "who else could review this file" routing.
3482
- */
3483
3031
  recent_contributors?: ContributorEntry[]
3484
- /**
3485
- * Contributors whose last touch is within 90 days, ordered by share
3486
- * descending. First-class field so AI agents do not have to
3487
- * reconstruct it from [`recent_contributors`](Self::recent_contributors)
3488
- * filtered by [`ContributorEntry::stale_days`]. Excludes the top
3489
- * contributor (they are the sole author being flagged); consumers
3490
- * wanting the full list can union with `top_contributor`.
3491
- */
3492
3032
  suggested_reviewers?: ContributorEntry[]
3493
- /**
3494
- * CODEOWNERS-resolved owner for this file, if a rule matched.
3495
- * Only the primary (first) owner of the matched rule is reported.
3496
- */
3497
3033
  declared_owner?: (string | null)
3498
- /**
3499
- * Tristate: `Some(true)` = CODEOWNERS file exists but no rule matches
3500
- * this file; `Some(false)` = a CODEOWNERS rule matches; `None` = no
3501
- * CODEOWNERS file was discovered for the repository (cannot determine).
3502
- */
3503
3034
  unowned?: (boolean | null)
3504
3035
  ownership_state: OwnershipState
3505
- /**
3506
- * True when ownership has drifted from the original author to a new
3507
- * top contributor. Pairs with [`drift_reason`](Self::drift_reason).
3508
- */
3509
3036
  drift: boolean
3510
- /**
3511
- * Human-readable explanation of the drift, populated only when
3512
- * [`drift`](Self::drift) is true.
3513
- */
3514
3037
  drift_reason?: (string | null)
3515
3038
  }
3516
- /**
3517
- * Per-author contribution summary. The identifier is rendered per the
3518
- * configured ownership.emailMode (handle, anonymized/hash, or raw); the format field
3519
- * discriminates the modes so type-aware consumers can branch without
3520
- * re-parsing.
3521
- */
3522
3039
  export interface ContributorEntry {
3523
- /**
3524
- * Display string per the configured email mode: raw email
3525
- * (`alice@example.com`), local-part handle (`alice`), or stable anonymized hash
3526
- * pseudonym (`xxh3:<16hex>`). The format depends on `format`.
3527
- *
3528
- * Renamed from `email` because in `handle` and `anonymized`/`hash` modes the value
3529
- * is no longer an email address; consumers tempted to use it as one
3530
- * (e.g. `mailto:`) would be wrong.
3531
- */
3532
3040
  identifier: string
3533
3041
  format: ContributorIdentifierFormat
3534
- /**
3535
- * Recency-weighted share of total weighted commits (0..1, three decimals).
3536
- */
3537
3042
  share: number
3538
- /**
3539
- * Days since this contributor last touched the file.
3540
- */
3541
3043
  stale_days: number
3542
- /**
3543
- * Total commits by this contributor in the analysis window.
3544
- */
3545
3044
  commits: number
3546
3045
  }
3547
3046
  /**
@@ -3584,29 +3083,11 @@ suggested_pattern?: (string | null)
3584
3083
  */
3585
3084
  heuristic?: (HotspotActionHeuristic | null)
3586
3085
  }
3587
- /**
3588
- * Summary statistics for hotspot analysis.
3589
- */
3590
3086
  export interface HotspotSummary {
3591
- /**
3592
- * Analysis window display string (e.g., "6 months").
3593
- */
3594
3087
  since: string
3595
- /**
3596
- * Minimum commits threshold.
3597
- */
3598
3088
  min_commits: number
3599
- /**
3600
- * Number of files with churn data meeting the threshold.
3601
- */
3602
3089
  files_analyzed: number
3603
- /**
3604
- * Number of files excluded (below min_commits).
3605
- */
3606
3090
  files_excluded: number
3607
- /**
3608
- * Whether the repository is a shallow clone.
3609
- */
3610
3091
  shallow_clone: boolean
3611
3092
  }
3612
3093
  /**
@@ -3981,24 +3462,88 @@ code: string
3981
3462
  message: string
3982
3463
  }
3983
3464
  /**
3984
- * A function exceeding the very-high-risk size threshold (>60 LOC).
3465
+ * Combined coverage, runtime, complexity, and change-scope verdicts.
3985
3466
  */
3986
- export interface LargeFunctionEntry {
3467
+ export interface CoverageIntelligenceReport {
3468
+ schema_version: CoverageIntelligenceSchemaVersion
3469
+ verdict: CoverageIntelligenceVerdict
3470
+ summary: CoverageIntelligenceSummary
3471
+ findings: CoverageIntelligenceFinding[]
3472
+ }
3987
3473
  /**
3988
- * Absolute file path.
3474
+ * Aggregate metadata for coverage-intelligence output.
3475
+ */
3476
+ export interface CoverageIntelligenceSummary {
3477
+ findings: number
3478
+ risky_changes: number
3479
+ high_confidence_deletes: number
3480
+ review_required: number
3481
+ refactor_carefully: number
3482
+ skipped_ambiguous_matches: number
3483
+ }
3484
+ /**
3485
+ * One combined coverage-intelligence finding.
3486
+ */
3487
+ export interface CoverageIntelligenceFinding {
3488
+ /**
3489
+ * Stable finding ID of the form `fallow:coverage-intel:<hash>`.
3490
+ */
3491
+ id: string
3492
+ /**
3493
+ * File path relative to the project root.
3989
3494
  */
3990
3495
  path: string
3991
3496
  /**
3992
- * Function name, or `"<anonymous>"` for unnamed functions/arrows.
3497
+ * Function or export identity when known.
3993
3498
  */
3994
- name: string
3499
+ identity?: (string | null)
3995
3500
  /**
3996
- * 1-based line number.
3501
+ * 1-indexed source line.
3997
3502
  */
3998
3503
  line: number
3504
+ verdict: CoverageIntelligenceVerdict
3505
+ signals: CoverageIntelligenceSignal[]
3506
+ recommendation: CoverageIntelligenceRecommendation
3507
+ confidence: CoverageIntelligenceConfidence
3508
+ related_ids?: string[]
3509
+ evidence: CoverageIntelligenceEvidence
3510
+ actions: CoverageIntelligenceAction[]
3511
+ }
3999
3512
  /**
4000
- * Number of lines in the function.
3513
+ * Compact evidence values that led to a recommendation.
4001
3514
  */
3515
+ export interface CoverageIntelligenceEvidence {
3516
+ coverage_pct?: (number | null)
3517
+ crap?: (number | null)
3518
+ runtime_verdict?: (string | null)
3519
+ invocations?: (number | null)
3520
+ static_status?: (string | null)
3521
+ test_coverage?: (string | null)
3522
+ changed?: boolean
3523
+ ownership_state?: (string | null)
3524
+ match_confidence: CoverageIntelligenceMatchConfidence
3525
+ }
3526
+ /**
3527
+ * Machine-actionable next step for a coverage-intelligence finding.
3528
+ */
3529
+ export interface CoverageIntelligenceAction {
3530
+ /**
3531
+ * Action identifier, normalized to `type` in JSON output.
3532
+ */
3533
+ type: string
3534
+ description: string
3535
+ /**
3536
+ * Whether fallow can apply this action automatically.
3537
+ */
3538
+ auto_fixable: boolean
3539
+ }
3540
+ /**
3541
+ * A function exceeding the very-high-risk size threshold (>60 LOC).
3542
+ */
3543
+ export interface LargeFunctionEntry {
3544
+ path: string
3545
+ name: string
3546
+ line: number
4002
3547
  line_count: number
4003
3548
  }
4004
3549
  /**
@@ -4317,181 +3862,51 @@ scope: string
4317
3862
  * fallow JSON-producing command.
4318
3863
  */
4319
3864
  export interface ExplainOutput {
4320
- /**
4321
- * Canonical rule id, for example `fallow/unused-export`.
4322
- */
4323
3865
  id: string
4324
- /**
4325
- * Human-readable rule name.
4326
- */
4327
3866
  name: string
4328
- /**
4329
- * Short one-line explanation of the issue.
4330
- */
4331
3867
  summary: string
4332
- /**
4333
- * Why the issue matters and what fallow checks.
4334
- */
4335
3868
  rationale: string
4336
- /**
4337
- * Concrete example of the finding.
4338
- */
4339
3869
  example: string
4340
- /**
4341
- * Recommended fix or suppression guidance.
4342
- */
4343
3870
  how_to_fix: string
4344
- /**
4345
- * Docs URL for the rule.
4346
- */
4347
3871
  docs: string
4348
3872
  }
4349
3873
  /**
4350
3874
  * Envelope emitted by `fallow --format review-github` / `review-gitlab`.
4351
- * Consumed by `action/scripts/review.sh` and `ci/scripts/review.sh` to
4352
- * post inline PR / MR review comments.
4353
3875
  */
4354
3876
  export interface ReviewEnvelopeOutput {
4355
- /**
4356
- * GitHub review event. Omitted for GitLab.
4357
- */
4358
3877
  event?: (ReviewEnvelopeEvent | null)
4359
- /**
4360
- * Review summary body (rendered above per-line comments). Deprecated in
4361
- * v2 envelopes: prefer [`summary.body`](`ReviewEnvelopeSummary::body`),
4362
- * which is byte-identical to this field but carries a stable
4363
- * fingerprint for reconciliation. Kept on v2 emit so v1 consumers that
4364
- * only look at `body` keep working.
4365
- */
4366
3878
  body: string
4367
3879
  summary?: ReviewEnvelopeSummary
4368
- /**
4369
- * Per-line comments. Each is either a [`GitHubReviewComment`] or a
4370
- * [`GitLabReviewComment`] depending on `meta.provider`.
4371
- */
4372
3880
  comments: ReviewComment[]
4373
- /**
4374
- * Regex consumers run against every existing PR/MR comment body to
4375
- * extract a fallow-emitted fingerprint marker. Capture group 1 is the
4376
- * fingerprint string (a bare 16-char hex hash for single-finding
4377
- * comments, or `<kind>:<16-char-hex>` for compositions such as
4378
- * `merged:` for same-line collapsed comments).
4379
- *
4380
- * The pattern is anchored with `^` / `$` and relies on multiline
4381
- * matching to anchor at line boundaries inside a multi-line comment
4382
- * body. Multiline is NOT baked into the pattern via `(?m)` (which
4383
- * JavaScript RegExp rejects as `Invalid group`); instead the consumer
4384
- * passes [`Self::marker_regex_flags`] as the flags argument to its
4385
- * regex engine. JavaScript: `new RegExp(env.marker_regex,
4386
- * env.marker_regex_flags)`. Rust: `regex::RegexBuilder::new(pat)
4387
- * .multi_line(flags.contains('m')).build()` (or any equivalent).
4388
- */
4389
3881
  marker_regex?: string
4390
- /**
4391
- * Flags consumers pass alongside [`Self::marker_regex`] when
4392
- * constructing their regex engine. Currently always `"m"` (multiline
4393
- * so the anchored `^` / `$` match at every line boundary within a
4394
- * comment body). Emitting flags as a separate field instead of
4395
- * baking `(?m)` into the pattern keeps the wire compatible with
4396
- * JavaScript RegExp, which rejects inline flag groups outside a
4397
- * `(?flags:X)` grouping.
4398
- */
4399
3882
  marker_regex_flags?: string
4400
3883
  meta: ReviewEnvelopeMeta
4401
3884
  }
4402
3885
  /**
4403
- * Summary block on [`ReviewEnvelopeOutput`]. Always present on v2 emit;
4404
- * `serde(default)` keeps schemars from marking it required so a future
4405
- * Deserialize derivation against v1 historical input synthesizes an empty
4406
- * value rather than erroring.
3886
+ * Summary block on [`ReviewEnvelopeOutput`].
4407
3887
  */
4408
3888
  export interface ReviewEnvelopeSummary {
4409
- /**
4410
- * Markdown body of the summary. Byte-identical to the legacy top-level
4411
- * [`ReviewEnvelopeOutput::body`] field; the duplication is intentional
4412
- * so v1 consumers see no behavior change.
4413
- */
4414
3889
  body: string
4415
- /**
4416
- * FNV-1a 64-bit hash (16 lowercase hex chars) of the summary body
4417
- * BEFORE the trailing fallow-fingerprint marker line is appended.
4418
- * (Computing the hash from the post-marker body would be circular:
4419
- * the marker contains the fingerprint, so the fingerprint cannot
4420
- * depend on the marker.) To reproduce from [`Self::body`], strip the
4421
- * line matching [`ReviewEnvelopeOutput::marker_regex`] together with
4422
- * its leading separator newlines and hash the remainder. Stable
4423
- * across runs that produce the same summary content; consumers
4424
- * upsert the sticky summary comment by matching this fingerprint
4425
- * against the marker_regex extraction of every existing comment body.
4426
- */
4427
3890
  fingerprint: string
4428
3891
  }
4429
3892
  /**
4430
3893
  * GitHub pull-request review comment.
4431
3894
  */
4432
3895
  export interface GitHubReviewComment {
4433
- /**
4434
- * File path the comment targets, repo-root relative.
4435
- */
4436
3896
  path: string
4437
- /**
4438
- * 1-indexed line number the comment targets.
4439
- */
4440
3897
  line: number
4441
3898
  side: GitHubReviewSide
4442
- /**
4443
- * Markdown body of the comment.
4444
- */
4445
3899
  body: string
4446
- /**
4447
- * Stable fingerprint for the comment, used by `fallow ci
4448
- * reconcile-review` to detect carryover comments across PR revisions.
4449
- * For single-finding comments the value is a bare 16-char hex FNV-1a
4450
- * hash. For merged comments (multiple findings on the same path:line)
4451
- * the value is `merged:<16-char hex>` over the sorted constituent
4452
- * fingerprints, so the identity shifts whenever constituent findings
4453
- * change membership. Bundled wrappers and `fallow ci reconcile-review`
4454
- * dedupe on this primary fingerprint only; consumers wanting
4455
- * update-in-place reconciliation (preserving reviewer reply threads
4456
- * across content changes) implement their own identity tracking via
4457
- * `marker_regex`.
4458
- */
4459
3900
  fingerprint: string
4460
- /**
4461
- * True when [`Self::body`] was truncated to fit a downstream provider's
4462
- * note-size budget (today: 65,536 bytes). The body retains the closing
4463
- * fallow-fingerprint marker so reconciliation continues to work after
4464
- * truncation.
4465
- *
4466
- * Co-presence invariant: `truncated == true` always implies the body
4467
- * contains an inline `<!-- fallow-truncated -->` HTML marker and the
4468
- * `> Body truncated by fallow.` blockquote breadcrumb, and vice versa.
4469
- * All three signals are emitted together; consumers may use any one
4470
- * (the typed boolean is the authoritative machine-readable signal).
4471
- */
4472
3901
  truncated?: boolean
4473
3902
  }
4474
3903
  /**
4475
3904
  * GitLab merge-request discussion comment.
4476
3905
  */
4477
3906
  export interface GitLabReviewComment {
4478
- /**
4479
- * Markdown body of the comment.
4480
- */
4481
3907
  body: string
4482
3908
  position: GitLabReviewPosition
4483
- /**
4484
- * Stable fingerprint for the comment. See
4485
- * [`GitHubReviewComment::fingerprint`] for the single vs `merged:`
4486
- * shape contract; semantics are identical across providers.
4487
- */
4488
3909
  fingerprint: string
4489
- /**
4490
- * True when [`Self::body`] was truncated to fit GitLab's note-size
4491
- * budget. See [`GitHubReviewComment::truncated`] for the full
4492
- * co-presence invariant with the inline HTML marker and human
4493
- * blockquote breadcrumb.
4494
- */
4495
3910
  truncated?: boolean
4496
3911
  }
4497
3912
  /**
@@ -4499,30 +3914,12 @@ truncated?: boolean
4499
3914
  * merge-request discussion-position API.
4500
3915
  */
4501
3916
  export interface GitLabReviewPosition {
4502
- /**
4503
- * Merge-request base SHA.
4504
- */
4505
3917
  base_sha?: (string | null)
4506
- /**
4507
- * Merge-request start SHA.
4508
- */
4509
3918
  start_sha?: (string | null)
4510
- /**
4511
- * Merge-request head SHA.
4512
- */
4513
3919
  head_sha?: (string | null)
4514
3920
  position_type: GitLabReviewPositionType
4515
- /**
4516
- * File path on the base side.
4517
- */
4518
3921
  old_path: string
4519
- /**
4520
- * File path on the head side.
4521
- */
4522
3922
  new_path: string
4523
- /**
4524
- * 1-indexed line on the head side.
4525
- */
4526
3923
  new_line: number
4527
3924
  }
4528
3925
  /**
@@ -4531,10 +3928,6 @@ new_line: number
4531
3928
  export interface ReviewEnvelopeMeta {
4532
3929
  schema: ReviewEnvelopeSchema
4533
3930
  provider: ReviewProvider
4534
- /**
4535
- * Check conclusion derived from the underlying findings. Emitted only
4536
- * for GitHub envelopes today.
4537
- */
4538
3931
  check_conclusion?: (ReviewCheckConclusion | null)
4539
3932
  }
4540
3933
  /**
@@ -4545,240 +3938,66 @@ check_conclusion?: (ReviewCheckConclusion | null)
4545
3938
  export interface ReviewReconcileOutput {
4546
3939
  schema: ReviewReconcileSchema
4547
3940
  provider: ReviewProvider
4548
- /**
4549
- * PR / MR target identifier supplied to `fallow ci reconcile-review`.
4550
- * `null` when the command ran without an explicit target.
4551
- */
4552
3941
  target?: (string | null)
4553
- /**
4554
- * Whether the reconcile ran in dry-run mode.
4555
- */
4556
3942
  dry_run: boolean
4557
- /**
4558
- * Number of comments in the supplied review envelope.
4559
- */
4560
3943
  comments: number
4561
- /**
4562
- * Total fingerprints discovered in the supplied envelope.
4563
- */
4564
3944
  current_fingerprints: number
4565
- /**
4566
- * Existing fingerprints already posted on the PR / MR.
4567
- */
4568
3945
  existing_fingerprints: number
4569
- /**
4570
- * Newly-introduced fingerprints (current minus existing).
4571
- */
4572
3946
  new_fingerprints: number
4573
- /**
4574
- * Stale fingerprints (existing minus current).
4575
- */
4576
3947
  stale_fingerprints: number
4577
- /**
4578
- * Identifiers of the new fingerprints (subset of comments).
4579
- */
4580
3948
  new: string[]
4581
- /**
4582
- * Identifiers of the stale fingerprints (subset of existing).
4583
- */
4584
3949
  stale: string[]
4585
- /**
4586
- * Optional warning when the provider API was unreachable or
4587
- * auth-rejected. `null` on the happy path.
4588
- */
4589
3950
  provider_warning?: (string | null)
4590
- /**
4591
- * Resolution comments actually posted (zero on dry runs).
4592
- */
4593
3951
  resolution_comments_posted: number
4594
- /**
4595
- * Stale review threads actually resolved (zero on dry runs).
4596
- */
4597
3952
  threads_resolved: number
4598
- /**
4599
- * Operator-facing retry hint when apply stopped early.
4600
- */
4601
3953
  apply_hint?: (string | null)
4602
- /**
4603
- * Errors collected during apply, one entry per failure.
4604
- */
4605
3954
  apply_errors: string[]
4606
- /**
4607
- * Stale fingerprints whose provider mutation failed.
4608
- */
4609
3955
  failed_fingerprints?: string[]
4610
- /**
4611
- * Stale fingerprints not fully applied after the fail-fast stop.
4612
- */
4613
3956
  unapplied_fingerprints?: string[]
4614
3957
  }
4615
3958
  /**
4616
- * Envelope emitted by `fallow coverage setup --json`. Deterministic
4617
- * agent-readable runtime coverage setup instructions. In workspaces,
4618
- * `members` carries one entry per detected runtime package; `runtime_targets`
4619
- * is the union of all member targets.
4620
- *
4621
- * Constructed at runtime by
4622
- * `crates/cli/src/coverage/mod.rs::build_setup_envelope`; the wire is
4623
- * `serde_json::to_value(&envelope)`. The drift gate keeps this struct
4624
- * aligned with `docs/output-schema.json`.
3959
+ * `fallow coverage setup --json` envelope.
4625
3960
  */
4626
3961
  export interface CoverageSetupOutput {
4627
3962
  schema_version: CoverageSetupSchemaVersion
4628
3963
  framework_detected: CoverageSetupFramework
4629
- /**
4630
- * Detected JavaScript package manager. `null` when none could be
4631
- * resolved.
4632
- */
4633
3964
  package_manager?: (CoverageSetupPackageManager | null)
4634
- /**
4635
- * Union of runtime targets across emitted members.
4636
- */
4637
3965
  runtime_targets: CoverageSetupRuntimeTarget[]
4638
- /**
4639
- * Per-runtime-workspace setup recipes. Pure aggregator roots and
4640
- * build-only library packages are omitted.
4641
- */
4642
3966
  members: CoverageSetupMember[]
4643
- /**
4644
- * Always `null` today. Reserved for a future "config has been written
4645
- * to disk" indicator.
4646
- */
4647
- config_written?: {
4648
- [k: string]: unknown
4649
- }
4650
- /**
4651
- * Shell commands the agent should run from the workspace root.
4652
- */
3967
+ config_written?: unknown
4653
3968
  commands: string[]
4654
- /**
4655
- * Compatibility copy of the primary member's files, with workspace
4656
- * prefixes when the primary member is not the root.
4657
- */
4658
3969
  files_to_edit: CoverageSetupFileToEdit[]
4659
- /**
4660
- * Compatibility copy of the primary member's snippets, with workspace
4661
- * prefixes when the primary member is not the root.
4662
- */
4663
3970
  snippets: CoverageSetupSnippet[]
4664
- /**
4665
- * Optional Dockerfile RUN/COPY snippet to enable the beacon in
4666
- * containerised deployments.
4667
- */
4668
3971
  dockerfile_snippet?: (string | null)
4669
- /**
4670
- * Ordered next-step instructions for the agent / human operator.
4671
- */
4672
3972
  next_steps: string[]
4673
- /**
4674
- * Non-fatal warnings raised during setup detection.
4675
- */
4676
3973
  warnings: string[]
4677
- /**
4678
- * `_meta` block emitted only when `--explain` is passed.
4679
- */
4680
- _meta?: {
4681
- [k: string]: unknown
3974
+ _meta?: unknown
4682
3975
  }
4683
- }
4684
- /**
4685
- * Per-workspace setup recipe inside [`CoverageSetupOutput::members`].
4686
- */
4687
3976
  export interface CoverageSetupMember {
4688
- /**
4689
- * Workspace package name (or root marker for single-package projects).
4690
- */
4691
3977
  name: string
4692
- /**
4693
- * Workspace path relative to the analysed root, or `.` for the root
4694
- * member.
4695
- */
4696
3978
  path: string
4697
3979
  framework_detected: CoverageSetupFramework
4698
- /**
4699
- * Package manager detected for this member.
4700
- */
4701
3980
  package_manager?: (CoverageSetupPackageManager | null)
4702
- /**
4703
- * Runtime targets supported by this member's framework.
4704
- */
4705
3981
  runtime_targets: CoverageSetupRuntimeTarget[]
4706
- /**
4707
- * Files the agent should edit to wire in the beacon.
4708
- */
4709
3982
  files_to_edit: CoverageSetupFileToEdit[]
4710
- /**
4711
- * Code snippets the agent should paste into the edited files.
4712
- */
4713
3983
  snippets: CoverageSetupSnippet[]
4714
- /**
4715
- * Optional Dockerfile snippet specific to this member.
4716
- */
4717
3984
  dockerfile_snippet?: (string | null)
4718
- /**
4719
- * Member-scoped warnings.
4720
- */
4721
3985
  warnings: string[]
4722
3986
  }
4723
- /**
4724
- * Single file to edit inside [`CoverageSetupMember::files_to_edit`] or
4725
- * [`CoverageSetupOutput::files_to_edit`].
4726
- */
4727
3987
  export interface CoverageSetupFileToEdit {
4728
- /**
4729
- * Workspace-relative path to the file to edit.
4730
- */
4731
3988
  path: string
4732
- /**
4733
- * Why the file needs editing (e.g. `"Mount the beacon middleware"`).
4734
- */
4735
3989
  reason: string
4736
3990
  }
4737
- /**
4738
- * Single code snippet inside [`CoverageSetupMember::snippets`] or
4739
- * [`CoverageSetupOutput::snippets`].
4740
- */
4741
3991
  export interface CoverageSetupSnippet {
4742
- /**
4743
- * Short label identifying the snippet (used by the human renderer).
4744
- */
4745
3992
  label: string
4746
- /**
4747
- * Workspace-relative path the snippet should be pasted into.
4748
- */
4749
3993
  path: string
4750
- /**
4751
- * Snippet content (literal source text).
4752
- */
4753
3994
  content: string
4754
3995
  }
4755
- /**
4756
- * Envelope emitted by `fallow coverage analyze --format json`.
4757
- *
4758
- * Focused runtime coverage analysis output. Local mode reads
4759
- * `--runtime-coverage <path>`. Cloud mode requires explicit `--cloud` /
4760
- * `--runtime-coverage-cloud` or `FALLOW_RUNTIME_COVERAGE_SOURCE=cloud`;
4761
- * `FALLOW_API_KEY` alone does NOT select cloud mode.
4762
- *
4763
- * Constructed at runtime in
4764
- * `crates/cli/src/coverage/analyze.rs::print_runtime_json`; the wire is
4765
- * `serde_json::to_value(&envelope)`. The drift gate keeps this struct
4766
- * aligned with `docs/output-schema.json`. Carries its own schema-version
4767
- * discriminator ([`CoverageAnalyzeSchemaVersion`]) because runtime
4768
- * coverage iterates independently of the main JSON contract version.
4769
- */
4770
3996
  export interface CoverageAnalyzeOutput {
4771
3997
  schema_version: CoverageAnalyzeSchemaVersion
4772
3998
  version: ToolVersion
4773
3999
  elapsed_ms: ElapsedMs
4774
4000
  runtime_coverage: RuntimeCoverageReport
4775
- /**
4776
- * `_meta` block with metric / rule definitions, emitted when `--explain`
4777
- * is passed. Populated via the post-pass injection in
4778
- * `print_runtime_json` (matches the pattern used by every other typed
4779
- * envelope; the typed struct sets this to `None` and the JSON layer
4780
- * merges in the `crate::explain::coverage_analyze_meta()` payload).
4781
- */
4782
4001
  _meta?: (Meta | null)
4783
4002
  }
4784
4003
  /**
@@ -4795,38 +4014,12 @@ boundaries: BoundariesListing
4795
4014
  * `boundaries` block carried by [`ListBoundariesOutput`].
4796
4015
  */
4797
4016
  export interface BoundariesListing {
4798
- /**
4799
- * `false` when the project has no `boundaries` configured; `true`
4800
- * otherwise. When `false` every array below is empty and every count
4801
- * is `0` (parity is enforced so consumers can read the counts without
4802
- * first branching on this flag).
4803
- */
4804
4017
  configured: boolean
4805
- /**
4806
- * Length of [`Self::zones`]; emitted alongside the array for parity
4807
- * with `rule_count` / `logical_group_count`.
4808
- */
4809
4018
  zone_count: number
4810
- /**
4811
- * Boundary zones after preset and `autoDiscover` expansion.
4812
- */
4813
4019
  zones: BoundariesListZone[]
4814
- /**
4815
- * Length of [`Self::rules`].
4816
- */
4817
4020
  rule_count: number
4818
- /**
4819
- * Boundary import rules, each `from -> allow[]`.
4820
- */
4821
4021
  rules: BoundariesListRule[]
4822
- /**
4823
- * Length of [`Self::logical_groups`]. Always present (issue #373).
4824
- */
4825
4022
  logical_group_count: number
4826
- /**
4827
- * Pre-expansion `autoDiscover` groups carrying the user-authored parent
4828
- * name and grouping intent (issue #373).
4829
- */
4830
4023
  logical_groups: BoundariesListLogicalGroup[]
4831
4024
  }
4832
4025
  /**
@@ -4834,18 +4027,8 @@ logical_groups: BoundariesListLogicalGroup[]
4834
4027
  * classifies files into a single zone via glob patterns.
4835
4028
  */
4836
4029
  export interface BoundariesListZone {
4837
- /**
4838
- * Zone identifier as referenced in rules (e.g. `app`, `features/auth`).
4839
- */
4840
4030
  name: string
4841
- /**
4842
- * Compiled glob patterns. Children of an `autoDiscover` parent each
4843
- * carry a single pattern like `src/features/auth/**`.
4844
- */
4845
4031
  patterns: string[]
4846
- /**
4847
- * Number of discovered files classified into this zone.
4848
- */
4849
4032
  file_count: number
4850
4033
  }
4851
4034
  /**
@@ -4855,14 +4038,7 @@ file_count: number
4855
4038
  * corresponding [`BoundariesListLogicalGroup::authored_rule`].
4856
4039
  */
4857
4040
  export interface BoundariesListRule {
4858
- /**
4859
- * Source zone the rule applies to.
4860
- */
4861
4041
  from: string
4862
- /**
4863
- * Target zones [`Self::from`] is allowed to import from. Self-imports
4864
- * are always allowed implicitly.
4865
- */
4866
4042
  allow: string[]
4867
4043
  }
4868
4044
  /**
@@ -4872,75 +4048,28 @@ allow: string[]
4872
4048
  * would otherwise flatten it out of [`BoundariesListing::zones`].
4873
4049
  */
4874
4050
  export interface BoundariesListLogicalGroup {
4875
- /**
4876
- * Logical parent zone name as authored by the user.
4877
- */
4878
4051
  name: string
4879
- /**
4880
- * Discovered child zone names in stable directory-sorted order.
4881
- */
4882
4052
  children: string[]
4883
- /**
4884
- * Verbatim `autoDiscover` strings from the user's config (not
4885
- * normalized) so round-trip tooling can match byte-for-byte.
4886
- */
4887
4053
  auto_discover: string[]
4888
4054
  status: LogicalGroupStatus
4889
- /**
4890
- * Position of the parent zone in the user's pre-expansion `zones[]`.
4891
- */
4892
4055
  source_zone_index: number
4893
- /**
4894
- * Sum of `file_count` across [`Self::children`] plus the fallback
4895
- * zone's `file_count` when present.
4896
- */
4897
4056
  file_count: number
4898
- /**
4899
- * Pre-expansion rule keyed on the parent name, when the user wrote
4900
- * one.
4901
- */
4902
4057
  authored_rule?: (AuthoredRule | null)
4903
- /**
4904
- * When the parent zone also carried explicit `patterns`, it stayed in
4905
- * [`BoundariesListing::zones`] as a fallback classifier; this is its
4906
- * name. Equal to [`Self::name`] when present.
4907
- */
4908
4058
  fallback_zone?: (string | null)
4909
- /**
4910
- * Parent zone indices merged into this group when the user declared
4911
- * the same parent name multiple times.
4912
- */
4913
4059
  merged_from?: (number[] | null)
4914
- /**
4915
- * Echo of the parent zone's `root` (subtree scope) as the user wrote
4916
- * it. `None` when the parent had no `root` field.
4917
- */
4918
4060
  original_zone_root?: (string | null)
4919
- /**
4920
- * Parallel to [`Self::children`]: for child at index `i`, the index
4921
- * into [`Self::auto_discover`] of the path that produced it. Empty
4922
- * when only one path was authored (every child trivially maps to
4923
- * index 0). `serde(default)` keeps the schema's `required` array in
4924
- * step with the runtime's `skip_serializing_if` behavior.
4925
- */
4926
4061
  child_source_indices?: number[]
4927
4062
  }
4928
4063
  /**
4929
- * Pre-expansion `from`-rule preserved on a [`LogicalGroup`]. Surfaces the
4930
- * user's original intent (`{ from: "features", allow: ["shared"] }`) even
4931
- * after `expand_auto_discover` rewrote it into per-child rules
4932
- * (`features/auth -> shared`, `features/billing -> shared`).
4064
+ * Pre-expansion rule preserved on a [`LogicalGroup`].
4933
4065
  */
4934
4066
  export interface AuthoredRule {
4935
4067
  /**
4936
- * Pre-expansion `allow` list as the user wrote it.
4068
+ * Authored `allow` list.
4937
4069
  */
4938
4070
  allow: string[]
4939
4071
  /**
4940
- * Pre-expansion `allowTypeOnly` list as the user wrote it. Omitted
4941
- * from JSON output when empty; `serde(default)` keeps the derived
4942
- * schema in lock-step (schemars 1 marks any field with a
4943
- * `serde(default)` attribute as non-required).
4072
+ * Authored `allowTypeOnly` list.
4944
4073
  */
4945
4074
  allow_type_only?: string[]
4946
4075
  }
@@ -5008,6 +4137,10 @@ hotspot_summary?: (HotspotSummary | null)
5008
4137
  * `--runtime-coverage`).
5009
4138
  */
5010
4139
  runtime_coverage?: (RuntimeCoverageReport | null)
4140
+ /**
4141
+ * Combined coverage, runtime, complexity, and change-scope verdicts.
4142
+ */
4143
+ coverage_intelligence?: (CoverageIntelligenceReport | null)
5011
4144
  /**
5012
4145
  * Functions exceeding 60 LOC (very high risk). Only present when unit size
5013
4146
  * very-high-risk bin >= 3%. Sorted by line count descending.
@@ -5037,31 +4170,9 @@ health_trend?: (HealthTrend | null)
5037
4170
  * back to the report root.
5038
4171
  */
5039
4172
  actions_meta?: (HealthActionsMeta | null)
5040
- /**
5041
- * Resolver mode used when --group-by is active. Present only on grouped
5042
- * output. The top-level `vital_signs`, `health_score`, and `summary` keep
5043
- * the active run scope (for example after --workspace); per-group versions
5044
- * live inside each entry of `groups`.
5045
- */
5046
4173
  grouped_by?: (GroupByMode | null)
5047
- /**
5048
- * Per-group health output, present only when `--group-by` is active.
5049
- * Each group recomputes its own `vital_signs` and `health_score` from
5050
- * the files in that group, mirroring how `--workspace` scopes a single
5051
- * subset.
5052
- */
5053
4174
  groups?: (HealthGroup[] | null)
5054
- /**
5055
- * `_meta` block with metric / rule definitions, emitted when `--explain`
5056
- * is passed (always present in MCP responses).
5057
- */
5058
4175
  _meta?: (Meta | null)
5059
- /**
5060
- * Workspace-discovery diagnostics surfaced during config load
5061
- * (issue #473). Mirror of [`CheckOutput::workspace_diagnostics`] so
5062
- * stand-alone `fallow health --format json` consumers see the same
5063
- * signal.
5064
- */
5065
4176
  workspace_diagnostics?: WorkspaceDiagnostic[]
5066
4177
  }
5067
4178
  /**
@@ -5103,6 +4214,12 @@ files_analyzed: number
5103
4214
  * rendered finding count, not the un-truncated total.
5104
4215
  */
5105
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)
5106
4223
  /**
5107
4224
  * Per-group vital signs recomputed from the files in this group. Absent
5108
4225
  * when --score-only suppressed top-level vital signs.
@@ -5151,19 +4268,15 @@ targets?: RefactoringTargetFinding[]
5151
4268
  actions_meta?: (HealthActionsMeta | null)
5152
4269
  }
5153
4270
  /**
5154
- * Envelope emitted by `fallow dupes --format json` (plus the `dupes` block
5155
- * inside the combined and audit envelopes).
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).
5156
4275
  *
5157
- * The body is the typed [`DupesReportPayload`] flattened into the envelope
5158
- * so the wire shape stays `{ schema_version, version, elapsed_ms,
5159
- * clone_groups, clone_families, stats, ... }` exactly as the existing JSON
5160
- * layer emits. The payload's `clone_groups` and `clone_families` carry
5161
- * typed [`crate::output_dupes::CloneGroupFinding`] /
5162
- * [`crate::output_dupes::CloneFamilyFinding`] wrappers so the `actions[]`
5163
- * field is part of the schema-derived contract.
5164
- * `grouped_by` / `groups` / `total_issues` are populated by the grouped
5165
- * builder; on the ungrouped path they stay `None` and `skip_serializing_if`
5166
- * 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.
5167
4280
  */
5168
4281
  export interface DupesOutput {
5169
4282
  schema_version: SchemaVersion
@@ -5188,29 +4301,8 @@ clone_families: CloneFamilyFinding[]
5188
4301
  */
5189
4302
  mirrored_directories?: MirroredDirectory[]
5190
4303
  stats: DuplicationStats
5191
- /**
5192
- * Resolver mode used for partitioning. Present only when `--group-by` is
5193
- * active.
5194
- */
5195
4304
  grouped_by?: (GroupByMode | null)
5196
- /**
5197
- * Total clone groups across all buckets when `--group-by` is active.
5198
- * Mirrors the grouped check / health envelopes which expose
5199
- * `total_issues` so MCP and CI consumers can read the same key across
5200
- * commands.
5201
- */
5202
4305
  total_issues?: (number | null)
5203
- /**
5204
- * Per-group buckets when `--group-by` is active. Each clone group is
5205
- * attributed to its largest-owner key (most instances; alphabetical
5206
- * tiebreak). Sort: most clone groups first, then alphabetical, with
5207
- * `(unowned)` pinned last.
5208
- *
5209
- * Each bucket's `clone_groups` and `clone_families` carry the typed
5210
- * finding wrappers ([`crate::output_dupes::AttributedCloneGroupFinding`],
5211
- * [`crate::output_dupes::CloneFamilyFinding`]) so the `actions[]`
5212
- * augmentation is part of the schema-derived contract.
5213
- */
5214
4306
  groups?: (DuplicationGroup[] | null)
5215
4307
  /**
5216
4308
  * `_meta` block with metric / rule definitions, emitted when `--explain`
@@ -5271,6 +4363,13 @@ line_count: number
5271
4363
  * CloneInstance shape.
5272
4364
  */
5273
4365
  instances: AttributedInstance[]
4366
+ /**
4367
+ * Stable content fingerprint, usually `dup:<8hex>` and widened on rare
4368
+ * report collisions. Addressable via `fallow dupes --trace dup:<fp>`.
4369
+ * Computed from the group's instances, so it matches the top-level
4370
+ * `clone_groups[].fingerprint` for the same clone.
4371
+ */
4372
+ fingerprint: string
5274
4373
  /**
5275
4374
  * Suggested next steps. Always emitted.
5276
4375
  */
@@ -5329,19 +4428,8 @@ schema_version: SchemaVersion
5329
4428
  version: ToolVersion
5330
4429
  elapsed_ms: ElapsedMs
5331
4430
  grouped_by: GroupByMode
5332
- /**
5333
- * Total number of issues across all groups.
5334
- */
5335
4431
  total_issues: number
5336
- /**
5337
- * One entry per group; each contains the same issue arrays as
5338
- * `CheckOutput` plus the group key and per-group total.
5339
- */
5340
4432
  groups: CheckGroupedEntry[]
5341
- /**
5342
- * `_meta` block with metric / rule definitions, emitted when `--explain`
5343
- * is passed.
5344
- */
5345
4433
  _meta?: (Meta | null)
5346
4434
  }
5347
4435
  /**
@@ -5350,23 +4438,8 @@ _meta?: (Meta | null)
5350
4438
  * `AnalysisResults`.
5351
4439
  */
5352
4440
  export interface CheckGroupedEntry {
5353
- /**
5354
- * Group identifier produced by the resolver. For `package` grouping:
5355
- * workspace package name. For `owner` grouping: the CODEOWNERS team.
5356
- * For `directory` grouping: the top-level directory prefix. For
5357
- * `section` grouping: the GitLab CODEOWNERS section name (or
5358
- * `(no section)` / `(unowned)` for unmatched files).
5359
- */
5360
4441
  key: string
5361
- /**
5362
- * Section default owners (GitLab CODEOWNERS `[Section] @owner1
5363
- * @owner2`). Emitted only when `grouped_by` is `section`. Empty for
5364
- * the `(no section)` and `(unowned)` buckets.
5365
- */
5366
4442
  owners?: (string[] | null)
5367
- /**
5368
- * Total number of issues in this group.
5369
- */
5370
4443
  total_issues: number
5371
4444
  /**
5372
4445
  * Files not reachable from any entry point. Wrapped in
@@ -5522,89 +4595,229 @@ unused_dependency_overrides?: UnusedDependencyOverrideFinding[]
5522
4595
  misconfigured_dependency_overrides?: MisconfiguredDependencyOverrideFinding[]
5523
4596
  }
5524
4597
  /**
5525
- * Envelope emitted by bare `fallow --format json` (the combined
5526
- * invocation). Wraps the per-analysis sub-results inside a single envelope
5527
- * with the standard `schema_version` / `version` / `elapsed_ms` header.
5528
- *
5529
- * Each sub-result is `Option<...>` so `--only` / `--skip` can suppress a
5530
- * pass without leaving an empty key on the wire. The `check` sub-result is
5531
- * the full [`CheckOutput`] envelope (including its own `schema_version` /
5532
- * `version` / `elapsed_ms`), `dupes` is the typed [`DupesReportPayload`]
5533
- * emitted via `crate::output_dupes::DupesReportPayload::from_report`, and
5534
- * `health` is the bare [`HealthReport`] body: the runtime emit calls
5535
- * `serde_json::to_value(&report)` directly rather than wrapping it in the
5536
- * per-command envelope. The committed schema points `dupes` at
5537
- * `#/definitions/DupesReportPayload` and `health` at
5538
- * `#/definitions/HealthReport` so the documented shape matches the
5539
- * wire; the `committed_property_refs_match_derived_property_refs`
5540
- * drift test enforces the alignment.
4598
+ * The rendered impact report, derived purely from the store (no analysis run).
5541
4599
  */
5542
- export interface CombinedOutput {
5543
- schema_version: SchemaVersion
5544
- version: ToolVersion
5545
- elapsed_ms: ElapsedMs
4600
+ export interface ImpactReport {
4601
+ schema_version: ImpactReportSchemaVersion
4602
+ enabled: boolean
4603
+ record_count: number
4604
+ first_recorded?: (string | null)
5546
4605
  /**
5547
- * Sectioned `_meta` block emitted only when `--explain` is passed.
5548
- * Contains `check`, `dupes`, and/or `health` keys matching the analyses
5549
- * enabled for the combined run.
4606
+ * Git SHA of the most recent recorded run, so a consumer can tell which
4607
+ * commit the `surfacing` counts belong to. This is an ABBREVIATED SHA
4608
+ * (`git rev-parse --short`), so it is for display/correlation only and will
4609
+ * not match a full 40-character SHA from `$GITHUB_SHA` or the git API
4610
+ * without expansion. None when the latest run had no SHA (not a git repo)
4611
+ * or there are no records yet.
5550
4612
  */
5551
- _meta?: (CombinedMeta | null)
4613
+ latest_git_sha?: (string | null)
5552
4614
  /**
5553
- * Dead-code analysis sub-envelope. Absent when `--skip check`.
4615
+ * Counts from the most recent recorded run. These are CHANGED-FILE scoped
4616
+ * (each record comes from a `fallow audit` run, whose default `new-only`
4617
+ * gate counts only findings in the changed files of that run), NOT a
4618
+ * whole-project total.
5554
4619
  */
5555
- check?: (CheckOutput | null)
4620
+ surfacing?: (ImpactCounts | null)
5556
4621
  /**
5557
- * Duplication analysis body (typed [`DupesReportPayload`], not the full
5558
- * `DupesOutput` envelope). Absent when `--skip dupes`. The payload
5559
- * wraps each clone group / family with its typed `actions[]` array via
5560
- * `crate::output_dupes::DupesReportPayload::from_report`.
4622
+ * Trend between the two most recent records. None until two records exist.
5561
4623
  */
5562
- dupes?: (DupesReportPayload | null)
4624
+ trend?: (TrendSummary | null)
5563
4625
  /**
5564
- * Complexity analysis body (bare `HealthReport`, not the full
5565
- * `HealthOutput` envelope). Absent when `--skip health`.
4626
+ * Counts from the most recent whole-project `fallow` run. WHOLE-PROJECT
4627
+ * scope (not changed-file), so this is the current issue total across the
4628
+ * whole repo, context next to the actionable changed-file `surfacing`
4629
+ * count. None until a full `fallow` run has been recorded. v1.6.
5566
4630
  */
5567
- health?: (HealthReport | null)
4631
+ project_surfacing?: (ImpactCounts | null)
4632
+ /**
4633
+ * Trend between the two most recent whole-project records. Comparable over
4634
+ * time (same whole-project denominator every run), unlike the changed-file
4635
+ * `trend`. None until two full `fallow` runs exist. v1.6.
4636
+ */
4637
+ project_trend?: (TrendSummary | null)
4638
+ containment_count: number
4639
+ /**
4640
+ * Most recent containment events (newest last), capped for display.
4641
+ */
4642
+ recent_containment: ContainmentEvent[]
4643
+ /**
4644
+ * Lifetime count of findings fallow credits as genuinely resolved (code
4645
+ * removed or refactored, never a `fallow-ignore`). v1.5.
4646
+ */
4647
+ resolved_total: number
4648
+ /**
4649
+ * Lifetime count of findings silenced by a newly-added `fallow-ignore`.
4650
+ * Reported as honest context, never as a win. v1.5.
4651
+ */
4652
+ suppressed_total: number
4653
+ /**
4654
+ * Most recent resolution events (newest last), capped for display. v1.5.
4655
+ */
4656
+ recent_resolved: ResolutionEvent[]
4657
+ /**
4658
+ * Whether per-finding attribution has a baseline yet. False on a freshly
4659
+ * upgraded v1 store (no frontier captured), which the renderer uses to show
4660
+ * "resolution tracking starts from your next run" instead of a bare zero.
4661
+ */
4662
+ attribution_active: boolean
5568
4663
  }
5569
4664
  /**
5570
- * Sectioned `_meta` block for the bare combined JSON envelope.
4665
+ * Per-category issue counts captured at a recorded run.
5571
4666
  */
5572
- export interface CombinedMeta {
4667
+ export interface ImpactCounts {
4668
+ total_issues: number
4669
+ dead_code: number
4670
+ complexity: number
4671
+ duplication: number
4672
+ }
5573
4673
  /**
5574
- * Dead-code metadata from `crate::explain::check_meta()`.
4674
+ * A computed trend between the two most recent records.
5575
4675
  */
5576
- check?: (Meta | null)
4676
+ export interface TrendSummary {
4677
+ direction: ImpactTrendDirection
5577
4678
  /**
5578
- * Duplication metadata from `crate::explain::dupes_meta()`.
4679
+ * Signed delta in total issues (current minus previous).
5579
4680
  */
5580
- dupes?: (Meta | null)
4681
+ total_delta: number
4682
+ previous_total: number
4683
+ current_total: number
4684
+ }
4685
+ export interface ContainmentEvent {
4686
+ blocked_at: string
4687
+ cleared_at: string
4688
+ git_sha?: (string | null)
4689
+ blocked_counts: ImpactCounts
4690
+ }
4691
+ export interface ResolutionEvent {
4692
+ kind: string
4693
+ path: string
4694
+ symbol?: (string | null)
4695
+ git_sha?: (string | null)
4696
+ timestamp: string
4697
+ }
5581
4698
  /**
5582
- * Health metadata from `crate::explain::health_meta()`.
4699
+ * The `fallow security --format json` envelope. `security_findings` is the
4700
+ * unique required field used for untagged narrowing in `FallowOutput`.
5583
4701
  */
5584
- health?: (Meta | null)
4702
+ export interface SecurityOutput {
4703
+ schema_version: SecuritySchemaVersion
4704
+ /**
4705
+ * Security candidates. Paths are project-root-relative, forward-slash.
4706
+ */
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
4715
+ /**
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
5585
4722
  }
5586
4723
  /**
5587
- * Single CodeClimate-compatible issue inside [`CodeClimateOutput`].
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.
5588
4729
  */
5589
- export interface CodeClimateIssue {
5590
- type: CodeClimateIssueKind
4730
+ export interface SecurityFinding {
4731
+ kind: SecurityFindingKind
5591
4732
  /**
5592
- * Fallow rule identifier (always starts with `fallow/`).
4733
+ * The catalogue category id (e.g. `"dangerous-html"`). `None` for
4734
+ * `ClientServerLeak`; `Some` for `TaintedSink`.
5593
4735
  */
5594
- check_name: string
4736
+ category?: (string | null)
5595
4737
  /**
5596
- * Human-readable description of the finding.
4738
+ * The CWE number declared by the matched catalogue entry. `None` for
4739
+ * `ClientServerLeak`; never fabricated beyond the catalogue's value.
5597
4740
  */
5598
- description: string
4741
+ cwe?: (number | null)
5599
4742
  /**
5600
- * Free-form categories applied by the report renderer.
4743
+ * File the finding is anchored on (the client boundary). Absolute
4744
+ * internally; JSON strips the project root via `serde_path::serialize`.
5601
4745
  */
5602
- categories: string[]
5603
- severity: CodeClimateSeverity
4746
+ path: string
4747
+ /**
4748
+ * 1-based line number of the anchor.
4749
+ */
4750
+ line: number
4751
+ /**
4752
+ * 0-based byte column offset of the anchor.
4753
+ */
4754
+ col: number
4755
+ /**
4756
+ * Agent/human-readable evidence (e.g. the named env var the chain reaches).
4757
+ */
4758
+ evidence: string
5604
4759
  /**
5605
- * Stable fingerprint used by CI dashboards to deduplicate findings
5606
- * across runs.
4760
+ * Structural import-hop trace from the client boundary to the secret source.
4761
+ * The hop count is the uncalibrated signal; fallow does not prove the path
4762
+ * is exploitable.
5607
4763
  */
4764
+ trace: TraceHop[]
4765
+ /**
4766
+ * Machine-actionable next steps. Always emitted (possibly empty for
4767
+ * forward-compat). For security candidates this is a single file-level
4768
+ * suppress hint (`auto_fixable: false`); there is no auto-fix because
4769
+ * verification is the agent's job, not fallow's.
4770
+ */
4771
+ actions: IssueAction[]
4772
+ }
4773
+ /**
4774
+ * One hop in a security finding's structural trace. Stored as an absolute path
4775
+ * internally; JSON serialization strips the project root via
4776
+ * `serde_path::serialize`.
4777
+ */
4778
+ export interface TraceHop {
4779
+ /**
4780
+ * File on this hop of the import chain.
4781
+ */
4782
+ path: string
4783
+ /**
4784
+ * 1-based line number. Import-chain hops point at the import site; the
4785
+ * terminal secret-source hop points at the source module when extraction
4786
+ * does not carry a more precise member-access span.
4787
+ */
4788
+ line: number
4789
+ /**
4790
+ * 0-based byte column offset.
4791
+ */
4792
+ col: number
4793
+ role: TraceHopRole
4794
+ }
4795
+ /**
4796
+ * Bare `fallow --format json` envelope.
4797
+ */
4798
+ export interface CombinedOutput {
4799
+ schema_version: SchemaVersion
4800
+ version: ToolVersion
4801
+ elapsed_ms: ElapsedMs
4802
+ _meta?: (CombinedMeta | null)
4803
+ check?: (CheckOutput | null)
4804
+ dupes?: (DupesReportPayload | null)
4805
+ health?: (HealthReport | null)
4806
+ }
4807
+ export interface CombinedMeta {
4808
+ check?: (Meta | null)
4809
+ dupes?: (Meta | null)
4810
+ health?: (Meta | null)
4811
+ }
4812
+ /**
4813
+ * Single CodeClimate-compatible issue inside [`CodeClimateOutput`].
4814
+ */
4815
+ export interface CodeClimateIssue {
4816
+ type: CodeClimateIssueKind
4817
+ check_name: string
4818
+ description: string
4819
+ categories: string[]
4820
+ severity: CodeClimateSeverity
5608
4821
  fingerprint: string
5609
4822
  location: CodeClimateLocation
5610
4823
  }