UNPKG

fallow

Version:

Codebase intelligence for TypeScript and JavaScript: health, complexity, duplication, architecture, styling drift, and unused code from one graph. CLI, LSP, and MCP server. Zero config for over 100 frameworks.

18,100 lines • 590 kB
/**
 * AUTOGENERATED FILE. DO NOT EDIT.
 *
 * Generated from `docs/output-schema.json` by
 * `editors/vscode/scripts/codegen-contracts.mjs`. The same file is written to
 * `editors/vscode/src/generated/output-contract.d.ts` (extension internal) and
 * `npm/fallow/types/output-contract.d.ts` (published as `fallow/types`).
 *
 * To change a shape:
 *   1. Find the Rust owner through `derived_definition_names()` and its
 *      imports in `crates/cli/src/bin/schema_emit.rs`, then edit that type.
 *      Rust is the runtime source of truth for the JSON output.
 *   2. Regenerate `docs/output-schema.json` with
 *      `cargo run -p fallow-cli --features schema-emit --bin fallow-schema-emit`.
 *      The schema-emit drift tests compare the committed schema against the
 *      Rust-derived definitions.
 *   3. Run `pnpm --filter fallow-vscode codegen:contracts` from anywhere.
 *   4. Commit the regenerated schema and generated contract files together.
 *
 * CI runs `pnpm --filter fallow-vscode check:contracts` which fails when
 * either committed file disagrees with what regen would produce.
 */
/* eslint-disable */


/**
 * Schemas for the JSON output of fallow commands. Object-shaped envelopes covered by the `FallowOutput` contract carry a top-level `kind` discriminator. Current kind values: `audit`, `explain`, `inspect_target`, `trace`, `trace-error`, `review-envelope`, `review-reconcile`, `coverage-setup`, `coverage-analyze`, `list-boundaries`, `list-workspaces`, `health`, `dupes`, `dead-code-grouped`, `impact`, `impact-cross-repo`, `security`, `security-survivors`, `security-blind-spots`, `dead-code`, `combined`, `feature-flags`, `audit-brief`, `decision-surface`, `review-walkthrough-guide`, `review-walkthrough-validation`, `suppression-inventory`, `doctor`, `type-aware-status`, `similar-code`, `similar-code-inspect`, `similar-code-review`. Consumers should branch on `kind` instead of probing for unique field presence. `CodeClimateOutput` is a bare JSON array (per the Code Climate / GitLab Code Quality spec) and stays a sibling root branch discriminated by checking whether the document root is an array. `ErrorOutput` is the `--format json` failure document, emitted on stdout with a non-zero exit; it carries no `kind` and is discriminated by the `error: true` field.
 */
export type FallowJsonOutput = (FallowOutput | CodeClimateOutput | ErrorOutput)
/**
 * Typed root of every fallow JSON envelope shape that serializes as a JSON
 * object and participates in the documented `FallowOutput` contract. The
 * schema derived from this enum drives the document-root `oneOf` in
 * `docs/output-schema.json`.
 *
 * The wire shape carries a top-level `kind` discriminator so agents and
 * schema-validating clients can select the variant in O(1) instead of probing
 * for unique field presence.
 *
 * One envelope is intentionally NOT in this enum:
 * - `CodeClimateOutput` serializes as a bare JSON array
 *   (`#[serde(transparent)]`) per the Code Climate / GitLab Code Quality
 *   spec; `#[serde(tag = ...)]` cannot internally tag a non-object
 *   variant and wrapping the array would break the spec. The root schema
 *   carries it as a sibling `oneOf` branch alongside `FallowOutput`.
 */
export type FallowOutput = ((AuditOutput & {
kind: "audit"
}) | (ExplainOutput & {
kind: "explain"
}) | (InspectOutput & {
kind: "inspect_target"
}) | ((ExportTrace | ClassMemberTrace | FileTrace | DependencyTrace | CloneTrace | ImpactClosureTrace | ImportPathTrace | SymbolChainTrace | SemanticSymbolTrace) & {
kind: "trace"
}) | (ErrorTrace & {
kind: "trace-error"
}) | (ReviewEnvelopeOutput & {
kind: "review-envelope"
}) | (ReviewReconcileOutput & {
kind: "review-reconcile"
}) | (CoverageSetupOutput & {
kind: "coverage-setup"
}) | (CoverageAnalyzeOutput & {
kind: "coverage-analyze"
}) | (ListBoundariesOutput & {
kind: "list-boundaries"
}) | (WorkspacesOutput & {
kind: "list-workspaces"
}) | (HealthOutput & {
kind: "health"
}) | (DupesOutput & {
kind: "dupes"
}) | (CheckGroupedOutput & {
kind: "dead-code-grouped"
}) | ((ImpactReport | SemanticSymbolImpact) & {
kind: "impact"
}) | (CrossRepoImpactReport & {
kind: "impact-cross-repo"
}) | ((SecurityOutput | SecuritySummaryOutput) & {
kind: "security"
}) | (SecuritySurvivorsOutput & {
kind: "security-survivors"
}) | (SecurityBlindSpotsOutput & {
kind: "security-blind-spots"
}) | (CheckOutput & {
kind: "dead-code"
}) | (CombinedOutput & {
kind: "combined"
}) | (FeatureFlagsOutput & {
kind: "feature-flags"
}) | (ReviewBriefWireOutput & {
kind: "audit-brief"
}) | (DecisionSurfaceOutput & {
kind: "decision-surface"
}) | (WalkthroughGuide & {
kind: "review-walkthrough-guide"
}) | (WalkthroughValidation & {
kind: "review-walkthrough-validation"
}) | (SuppressionInventoryOutput & {
kind: "suppression-inventory"
}) | (DoctorOutput & {
kind: "doctor"
}) | (TypeAwareStatusOutput & {
kind: "type-aware-status"
}) | (SimilarCodeOutput & {
kind: "similar-code"
}) | (SimilarCodeInspectOutput & {
kind: "similar-code-inspect"
}) | (SimilarCodeReviewOutput & {
kind: "similar-code-review"
}))
/**
 * Schema projection for the audit envelope's exact version.
 */
export type AuditSchemaVersion = 12
/**
 * Fallow CLI version that produced this envelope. Renders to the JSON wire as
 * a bare string (e.g. `"2.74.0"`).
 */
export type ToolVersion = string
/**
 * Audit command singleton carried by [`AuditOutput`].
 */
export type AuditCommand = "audit"
/**
 * Verdict for the audit command.
 */
export type AuditVerdict = ("pass" | "warn" | "fail")
/**
 * Analysis duration in milliseconds. Renders to the JSON wire as a bare
 * integer.
 */
export type ElapsedMs = number
/**
 * Value of `audit.gate`: which findings drive the `fallow audit` verdict.
 */
export type AuditGate = ("new-only" | "all")
/**
 * What a gate concluded on this run.
 *
 * Four-valued rather than a boolean because audit's verdict has a warn tier
 * (`crates/cli/src/cli_report.rs` maps it onto three conclusions) and because
 * a gate can stand down without passing. Widening a published boolean later
 * would retype a required field and bump every carrying envelope, so the width
 * is decided here.
 */
export type GateStatus = ("pass" | "warn" | "fail" | "skipped")
/**
 * Analysis mode stored with baselines, snapshots, audit sides, and impact data.
 */
export type SemanticAnalysisMode = ("syntactic" | "type-aware")
/**
 * Semantic capabilities that can share one TypeScript Program session.
 */
export type SemanticCapability = ("symbol-use" | "symbol-trace" | "api-surface" | "symbol-impact" | "type-coupling")
/**
 * Whether the semantic backend answered every requested query safely.
 */
export type SemanticCompleteness = ("complete" | "partial" | "unavailable")
/**
 * Effective policy applied when semantic evidence is incomplete.
 */
export type SemanticCompletenessRequirement = ("best-effort" | "complete")
/**
 * Stable reason why semantic evidence is partial or unavailable.
 */
export type SemanticGapReason = ("no-project" | "ambiguous-project" | "blocking-diagnostics" | "svelte-virtual-module-exports" | "unknown-symbol" | "unknown-entry-point" | "evidence-limit" | "dynamic-behavior" | "virtual-dispatch" | "dynamic-member-access" | "decorated-declaration" | "optional-contract" | "accessor-pair" | "overload-set" | "attached-comment" | "abstract-declaration" | "incomplete-project-coverage" | "framework-contract-provenance" | "capacity" | "unsupported-syntax")
/**
 * Value or type namespace for one exact declaration or reference.
 */
export type SemanticNamespace = ("value" | "type")
/**
 * Conservative outcome for one existing Fallow dead-code candidate.
 */
export type SemanticCandidateDecisionKind = ("confirmed-used" | "contract-preserved" | "confirmed-no-static-references" | "retained-abstained" | "retained-unresolved")
/**
 * How a class member participates in an inherited or implemented relation.
 */
export type SemanticContractRelation = ("interface-implementation" | "abstract-implementation" | "override" | "optional-contract")
/**
 * Heritage relation used by a framework-owned class-member contract.
 */
export type SemanticFrameworkRelation = ("extends" | "implements")
/**
 * Confidence of exact-symbol impact analysis after known dynamic gaps.
 */
export type SemanticImpactConfidence = ("high" | "bounded" | "unavailable")
/**
 * How a TypeScript project was selected for semantic refinement.
 */
export type TypeAwareProjectSource = ("auto" | "explicit")
/**
 * Outcome of semantic refinement for one TypeScript project.
 */
export type TypeAwareProjectStatus = ("refined" | "abstained" | "complete" | "unavailable")
/**
 * How a persistent semantic snapshot was refreshed.
 */
export type TypeAwareInvalidationKind = ("full" | "incremental" | "none")
/**
 * Closed set of reasons for retaining a candidate without semantic scanning.
 */
export type TypeAwareAbstentionReason = ("no-project" | "ambiguous-project" | "blocking-diagnostics")
/**
 * Schema projection for the dead-code envelope's exact version.
 */
export type CheckSchemaVersion = 10
/**
 * A suggested action attached to a finding in the JSON output. Each finding
 * carries an `actions` array; consumers (agents, IDE clients, CI bots) can
 * dispatch on the `type` discriminant to choose the right remediation.
 *
 * The discriminator is `type` (snake_case `type` field), the payload uses the
 * matching kebab-case identifier per variant.
 *
 * ## `auto_fixable` is per-finding, not per action type
 *
 * Every action variant carries an `auto_fixable: bool` field. The value is
 * evaluated PER FINDING, not per action type: the same action type may
 * appear with `auto_fixable: true` on one finding and `auto_fixable: false`
 * on another, depending on per-instance guards in the `fallow fix` applier.
 * Agents that filter on `auto_fixable: true` must branch on the bool of
 * each individual finding's action, not on the action `type` alone.
 *
 * Current per-instance flips:
 *
 * - `remove-catalog-entry` (`unused-catalog-entries`): `true` only when the
 *   finding's `hardcoded_consumers` array is empty and the source is
 *   `pnpm-workspace.yaml`. When a workspace package still pins a hardcoded
 *   version of the same package, `fallow fix` skips the entry to avoid
 *   breaking `pnpm install`. Bun `package.json` catalog entries are also
 *   emitted with `auto_fixable: false` because the current fixer is
 *   YAML-only.
 * - `remove-dependency` vs `move-dependency` (dependency findings): when the
 *   finding's `used_in_workspaces` array is non-empty, the primary action
 *   flips to `move-dependency` with `auto_fixable: false` (`fallow fix` will
 *   not remove a dependency that another workspace imports). On findings
 *   without cross-workspace consumers the action stays `remove-dependency`
 *   with `auto_fixable: true`.
 * - `add-to-config` for `ignoreExports` (`duplicate-exports`): `true` when
 *   `fallow fix` can safely apply the action without further user setup.
 *   That is: a fallow config file exists on disk, OR no config exists AND
 *   the working directory is NOT inside a monorepo subpackage (in which
 *   case the applier creates `.fallowrc.json` from `fallow init`'s
 *   framework-aware scaffolding and layers the new rules on top).
 *   `false` inside a monorepo subpackage with no workspace-root config
 *   (the applier refuses to fragment per-package configs across the
 *   monorepo and points at the workspace root instead).
 * - `update-catalog-reference` (`unresolved-catalog-references`): always
 *   `false` today (the catalog-switching applier is not wired in yet); the
 *   field is non-singleton so that future enablement does not require a
 *   schema change.
 *
 * All `suppress-line` and `suppress-file` actions are uniformly
 * `auto_fixable: false`. The field is non-singleton on the wire so that a
 * future auto-applier (e.g. an LLM-driven suppression writer) can promote
 * individual variants without a schema bump.
 */
export type IssueAction = (FixAction | SuppressLineAction | SuppressFileAction | AddToConfigAction)
/**
 * Discriminant string for [`FixAction`]. Kebab-case per the JSON output
 * contract.
 */
export type FixActionType = ("remove-export" | "delete-file" | "remove-dependency" | "move-dependency" | "remove-enum-member" | "remove-class-member" | "resolve-import" | "install-dependency" | "remove-duplicate" | "move-to-dev" | "move-to-prod" | "refactor-cycle" | "refactor-re-export-cycle" | "refactor-boundary" | "export-type" | "migrate-deprecated-export" | "remove-catalog-entry" | "remove-empty-catalog-group" | "update-catalog-reference" | "add-catalog-entry" | "remove-catalog-reference" | "remove-dependency-override" | "fix-dependency-override" | "resolve-policy-violation" | "move-to-server-module" | "split-mixed-barrel" | "hoist-directive" | "wire-server-action" | "provide-inject" | "use-load-data" | "render-component" | "use-component-prop" | "review-component-prop" | "emit-component-event" | "wire-svelte-event" | "resolve-route-collision" | "resolve-dynamic-segment-name-conflict" | "add-suppression-reason" | "remove-stale-suppression")
/**
 * Singleton discriminant for [`SuppressLineAction`].
 */
export type SuppressLineKind = "suppress-line"
/**
 * Scope marker for line suppressions that span multiple locations.
 */
export type SuppressLineScope = "per-location"
/**
 * Singleton discriminant for [`SuppressFileAction`].
 */
export type SuppressFileKind = "suppress-file"
/**
 * Singleton discriminant for [`AddToConfigAction`].
 */
export type AddToConfigKind = "add-to-config"
/**
 * Value payload for [`AddToConfigAction::value`]. The variants line up with
 * the documented per-`config_key` shapes; deserialization is untagged so
 * downstream consumers can switch on the JSON value's type.
 */
export type AddToConfigValue = (string | IgnoreExportsRule[] | {
[k: string]: unknown
})
/**
 * Audit-mode marker emitted on each finding when `fallow audit --format json`
 * runs with a base ref. `true` means the finding's structural key was not
 * present at the base ref (introduced by the current changeset); `false`
 * means it was inherited. Duplication findings carry one carve-out: a clone
 * group whose structural key is new but whose instances contain no added line
 * from the diff (a group re-shaped by removing duplication elsewhere) is
 * demoted to inherited and serializes `false` (issue #2164). Such demoted
 * groups additionally carry a `demotion_reason` field naming the rule, and
 * are counted in `attribution.duplication_demoted` (issue #2220).
 *
 * Outside of audit sub-results the field is omitted, so call sites typically
 * hold `Option<AuditIntroduced>`. Renders to the JSON wire as a bare boolean.
 */
export type AuditIntroduced = boolean
/**
 * Gate severity of one finding after rule resolution.
 *
 * It is the severity that `rules` and the matching `overrides[].rules` give
 * the finding for its path. `--fail-on-issues` raises `warn` to `error`. A
 * finding whose rule is `off` is not reported, so there is no `off` value.
 * The type is separate from the health `severity` band, which ranks a
 * finding and does not gate it.
 *
 * The `fallow dead-code` findings gate fails when a finding is `error`.
 * Other gates (regression, stale baseline) decide on their own inputs. Two
 * commands differ: the `fallow audit` `new-only` gate fails only on introduced
 * findings, so an inherited `error` finding does not fail the audit, and the
 * combined command (`fallow` without a subcommand) exits 0 for machine
 * formats unless `--fail-on-issues` or `--ci` is set.
 *
 * Complexity findings carry the same type. The `complexity-cyclomatic`,
 * `complexity-cognitive` and `complexity-crap` rules set it, and the
 * `fallow health` findings gate and the audit verdict read it.
 */
export type EffectiveSeverity = ("error" | "warn")
/**
 * A per-finding caveat on a dead-code verdict that a file this run never
 * fully analyzed can distort.
 *
 * Advisory provenance, in the same spirit as the fix path's
 * `low_confidence_off_graph` / `low_confidence_unresolved_imports` skip
 * reasons: a caveat NEVER withholds, reorders, downgrades, or re-severities
 * the finding, and never changes an exit code. It records that the verdict
 * was computed over an import graph fallow already knows is incomplete, so a
 * reader who sees the finding also sees the caveat instead of having to
 * notice a diagnostic at the other end of the envelope.
 *
 * Deliberately NOT named `confidence`: `health --targets` already emits a
 * `confidence` key holding an enum string, and a shared consumer helper that
 * met both would see the same key change type. Emitted on every finding type
 * that registers it: the reachability arrays (`unused_files[]`,
 * `unused_exports[]`, `unused_types[]`), the member arrays
 * (`unused_enum_members[]`, `unused_class_members[]`, `unused_store_members[]`),
 * and the three dependency arrays. Sorted and deduplicated, absent from the
 * wire when empty. The set is open in the same sense
 * `workspace_diagnostics[].kind` is: treat an unrecognised value as "some
 * caveat" rather than as an error.
 */
export type ReachabilityCaveat = ("incomplete-file-analysis" | "incomplete-import-graph")
/**
 * How a consumer references a deprecated export.
 */
export type DeprecatedConsumerKind = ("named-import" | "default-import" | "namespace-import" | "re-export" | "dynamic-import" | "side-effect-import")
/**
 * Where in package.json a dependency is listed.
 *
 * # Examples
 *
 * ```
 * use fallow_types::results::DependencyLocation;
 *
 * // All three variants are constructible
 * let loc = DependencyLocation::Dependencies;
 * let dev = DependencyLocation::DevDependencies;
 * let opt = DependencyLocation::OptionalDependencies;
 * // Debug output includes the variant name
 * assert!(format!("{loc:?}").contains("Dependencies"));
 * assert!(format!("{dev:?}").contains("DevDependencies"));
 * assert!(format!("{opt:?}").contains("OptionalDependencies"));
 * ```
 */
export type DependencyLocation = ("dependencies" | "devDependencies" | "optionalDependencies")
/**
 * The kind of member.
 */
export type MemberKind = ("enum_member" | "class_method" | "class_property" | "namespace_member" | "store_member")
/**
 * Discriminator for [`ReExportCycle`]: which structural shape was detected.
 */
export type ReExportCycleKind = ("multi-node" | "self-loop")
/**
 * Which rule-pack rule kind produced a [`PolicyViolation`].
 */
export type PolicyRuleKind = ("banned-call" | "banned-import" | "banned-effect" | "banned-export" | "gdp-proof-producer")
/**
 * Effective severity of a single [`PolicyViolation`]. Per-rule `severity`
 * overrides the `rules."policy-violation"` master; `off` rules emit nothing,
 * so only `error` and `warn` appear on the wire. The exit-code gate inspects
 * this per-finding value, not the master severity.
 */
export type PolicyViolationSeverity = ("error" | "warn")
/**
 * The origin of a stale suppression: inline comment or JSDoc tag.
 */
export type SuppressionOrigin = ({
/**
 * The issue kind token from the comment (e.g., "unused-exports"), or None for blanket.
 */
issue_kind?: (string | null)
/**
 * Human-authored reason after `--`, when present.
 */
reason?: (string | null)
/**
 * Whether this was a file-level suppression.
 */
is_file_level: boolean
/**
 * Whether `issue_kind` parses to a known `IssueKind`. False when the
 * token is a typo or refers to a kind that was renamed or removed in
 * a newer fallow release. JSON consumers (CI annotations, MCP agents,
 * VS Code) branch on this to choose the right next-step text.
 * Omitted from the wire when `true` so producers that have not yet
 * adopted the field stay byte-compatible. See issue #449.
 */
kind_known?: boolean
type: "comment"
} | {
/**
 * The name of the export that was tagged.
 */
export_name: string
/**
 * Human-authored reason after `--`, when present.
 */
reason?: (string | null)
type: "jsdoc_tag"
})
/**
 * Where an override entry was declared. Serialized as the filename label
 * (`"pnpm-workspace.yaml"` or `"package.json"`) so the value in JSON output
 * matches the value users write in `ignoreDependencyOverrides[].source`.
 */
export type DependencyOverrideSource = ("pnpm-workspace.yaml" | "package.json")
/**
 * Why a dependency-override entry is misconfigured. The active package
 * manager may fail at install time or silently no-op on these entries;
 * surfacing them statically catches the issue first.
 */
export type DependencyOverrideMisconfigReason = ("unparsable-key" | "empty-value")
/**
 * Which advisory a loaded baseline earned on this run.
 *
 * Mirrors `fallow_engine::baseline::BaselineStalenessWarning` so a consumer can
 * render the same distinction the stderr warning makes, instead of inferring it
 * from counts.
 */
export type BaselineStalenessAdvisory = ("none" | "zero-overlap" | "partial")
/**
 * One channel that narrowed a run to part of the project.
 *
 * Serialized as kebab-case inside `scope_reasons` and published as an OPEN
 * set, the same tolerate-unknown contract `gate_outcomes` keys carry: a name
 * this build does not emit means "some narrowing", not an error.
 *
 * Which names a command can emit differs per command, because the three
 * narrowing predicates see different state. `dead-code` reads the flags
 * themselves and can name every channel. `dupes` and `health` see an already
 * resolved changed-file set and report `changed-files`, because at that point
 * the flag that produced it is gone. `health` reports `workspace` for both
 * `--workspace` and `--changed-workspaces` for the same reason. A consumer
 * must therefore not assume a given command emits a given name.
 */
export type ScopeReason = ("diff" | "changed-since" | "package-baselines" | "changed-files" | "workspace" | "changed-workspaces" | "scope" | "file" | "issue-type-filter" | "production" | "include-entry-exports")
/**
 * One reason why a missing id does not prove that the finding is gone.
 *
 * Serialized as kebab-case inside `inconclusive_reasons`. The set is OPEN: a
 * name this build does not emit means "some reason", not an error, and the
 * query stays inconclusive.
 */
export type FindingIdQueryReason = ("diff" | "changed-since" | "package-baselines" | "changed-files" | "workspace" | "changed-workspaces" | "scope" | "file" | "issue-type-filter" | "production" | "include-entry-exports" | "baseline" | "rule-off" | "filtered")
/**
 * Status of a regression-check pass.
 */
export type RegressionStatus = ("pass" | "exceeded" | "skipped")
/**
 * Interpretation of [`RegressionResult::tolerance`].
 */
export type RegressionToleranceKind = ("absolute" | "percentage")
/**
 * What became of one request on this run.
 *
 * Two-valued today. The value set is OPEN so a later `partial` needs no bump,
 * and it is deliberately not added now: nothing emits it, and a permanently
 * unused value reads as a measurement nobody takes.
 */
export type RequestStatus = ("applied" | "not-applied")
/**
 * What a request governs, and therefore what its failure means.
 *
 * Published on every entry so a consumer selects on the class rather than on
 * a name list. Without it the one sentence a consumer can write for the whole
 * object ("the report is wider than requested") is false for any request that
 * does not narrow, which is how a failed `--sarif-file` write came to be
 * reported as an unscoped run. A request name added later carries its own
 * class, so a consumer written today keeps saying the right thing about it.
 *
 * The value set is OPEN, like the names and the statuses: read a class this
 * build does not recognise as "some request", not as an error, and do not read
 * it as `scope`.
 */
export type RequestEffect = ("scope" | "artifact")
/**
 * A diagnostic about a workspace-discovery candidate.
 *
 * The `message` field is a human-readable rendering derived from `kind`. It
 * always ends with a concrete next step ("fix the JSON syntax", "remove from
 * `workspaces`", "add to `ignorePatterns`") so first-time users have a path
 * forward.
 */
export type WorkspaceDiagnostic = ({
/**
 * Path to the directory or file that triggered the diagnostic.
 */
path: string
/**
 * Human-readable rendering derived from `kind` + `path`. Always ends
 * with a next-step hint.
 */
message: string
/**
 * True when this diagnostic reports a run whose RESULTS are degraded:
 * something the user installed, wrote, or expected did not reach the
 * analysis. Projected from [`WorkspaceDiagnosticKind::warns_on_stderr`],
 * which is the same classification that decides whether the CLI prints a
 * stderr line, so a CI log built from this field and a local non-quiet run
 * say the same thing.
 *
 * Omitted when false, which is what keeps every clean run byte-identical.
 * The two unconfigured-check kinds answer false on purpose: they fire in
 * the product's default state on every project that never opted into
 * boundaries or rule packs, so warning on them would warn forever. So does
 * `excluded-by-default-ignore`, which is designed behavior on generated
 * output; the alarm for that case is `no-source-files-analyzed`.
 *
 * Read this instead of hardcoding a kind allowlist: a degrading kind added
 * in a later release then reaches an unchanged consumer.
 */
degrades_analysis?: boolean
} & WorkspaceDiagnostic1)
export type WorkspaceDiagnostic1 = ({
kind: "undeclared-workspace"
} | {
/**
 * `serde_json` parse error text.
 */
error: string
kind: "malformed-package-json"
} | {
/**
 * The glob pattern that matched the directory.
 */
pattern: string
kind: "glob-matched-no-package-json"
} | {
/**
 * JSONC parse error text.
 */
error: string
kind: "malformed-tsconfig"
} | {
kind: "tsconfig-reference-dir-missing"
} | {
/**
 * YAML parse error text.
 */
error: string
kind: "malformed-pnpm-workspace-yaml"
} | {
/**
 * On-disk size of the skipped file in bytes.
 */
size_bytes: number
kind: "skipped-large-file"
} | {
/**
 * On-disk size of the skipped file in bytes.
 */
size_bytes: number
kind: "skipped-minified-file"
} | {
kind: "skipped-source-dotdir"
} | {
/**
 * Filesystem or UTF-8 decoding error from `read_to_string`.
 */
error: string
kind: "source-read-failure"
} | {
/**
 * Number of parser diagnostics reported for the file.
 */
error_count: number
/**
 * `true` when the parser abandoned the file instead of recovering, so
 * the extracted module is a fragment at best.
 */
panicked: boolean
kind: "source-parse-degraded"
} | {
kind: "bun-lockb-override-resolution-skipped"
} | {
kind: "bun-lock-override-resolution-skipped"
} | {
kind: "pnpm-lock-override-resolution-skipped"
} | {
kind: "npm-lock-override-resolution-skipped"
} | {
kind: "bun-resolutions-shadowed-by-overrides"
} | {
cause: PnpmWorkspaceOverridesIgnoredCause
kind: "pnpm-workspace-overrides-ignored"
} | {
kind: "node-modules-missing"
} | {
kind: "boundaries-not-configured"
} | {
kind: "rule-packs-not-configured"
} | {
/**
 * The built-in glob that matched, verbatim (for example
 * `** /build/**`).
 */
pattern: string
/**
 * Candidate source files this pattern excluded in this walk, across
 * every directory it matched, not just the one `path` anchors at.
 * Exact: the walk counts each excluded candidate once.
 */
file_count: number
/**
 * Distinct directories this pattern matched at, `path` included, and
 * not the number of directories that held the files. A
 * directory-shaped pattern (`** /dist/**`) matches at the directory it
 * names, so an excluded subtree counts once however many nested
 * directories inside it held source: a `dist/` holding files in three
 * sub-directories reports `1`. A file-shaped pattern (`** /*.min.js`)
 * has no directory to collapse to and counts each matched file's own
 * parent. Exact either way, and anything above `1` says `path` names
 * one matched location out of several.
 */
directory_count: number
kind: "excluded-by-default-ignore"
} | {
/**
 * Candidate source files the built-in ignore patterns removed from
 * this walk, summed across every pattern. `0` when the walk found no
 * candidate to exclude in the first place.
 */
excluded_file_count: number
kind: "no-source-files-analyzed"
} | {
/**
 * Scoring error text.
 */
error: string
kind: "file-scores-unavailable"
} | {
/**
 * Which input stopped it, as a kebab-case token: `not-a-repository`,
 * `no-commits`, `invalid-since` or `churn-file-unreadable`. The set is
 * open.
 *
 * The cause decides the remedy, which is why it is on the wire: a run
 * outside a repository is fixed by running fallow inside one, a
 * branch without a commit by committing, a malformed `--since` by
 * respelling the flag, and a churn file that changed under the run by
 * rerunning it. A consumer reading only the kind would offer the first
 * remedy for all four.
 */
cause: string
kind: "hotspots-skipped"
} | {
/**
 * `true` when the run also asked for ownership attribution, which a
 * shallow clone skews further by inflating single-author dominance.
 */
ownership_requested: boolean
kind: "shallow-clone"
} | {
kind: "unpinned-clock"
} | {
/**
 * Which input failed, as a kebab-case token: `invalid-bot-pattern` or
 * `codeowners-parse-failed`. The set is open.
 */
cause: string
/**
 * Underlying error text.
 */
error: string
kind: "ownership-unavailable"
} | {
/**
 * Filesystem or JSON error text.
 */
error: string
kind: "trend-snapshot-unreadable"
} | {
/**
 * Why the groups have no baseline, as a kebab-case token:
 * `snapshot-has-no-groups` or `grouped-by-mismatch`. The set is open.
 */
cause: string
kind: "trend-group-baseline-unavailable"
} | {
/**
 * The plugin that read the config, as it labels itself:
 * `module-federation` for a standalone `module-federation.config.*`,
 * or the bundler plugin (`webpack`, `rspack`, `rsbuild`, `vite`) that
 * read the same options inline from its own config.
 */
plugin: string
/**
 * The config key that was present and not fully readable (`exposes`,
 * `remotes`), or the Module Federation runtime function whose
 * argument was not readable (`registerRemotes`, `loadRemote`, `init`,
 * `createInstance`). The set is open.
 */
key: string
/**
 * Why it could not be read, as a kebab-case token:
 * `not-object-literal`, `array-form`, `spread`,
 * `unreadable-entries`, `unrecognized-call`,
 * `import-target-unreadable` or `dynamic-argument`. The set is open.
 *
 * The reason decides the remedy, which is why it is on the wire: a
 * value that is not an object literal is fixed by writing one, while
 * unreadable entries are fixed by naming those entries in the config
 * option the message points at.
 */
reason: string
kind: "plugin-config-unreadable"
} | {
/**
 * The plugin that read the config, as it labels itself (`nuxt`).
 */
plugin: string
/**
 * The config key whose effect is not modeled (`components`,
 * `imports`), or the virtual module a file reads (`#components`,
 * `#imports`). The set is open.
 */
key: string
/**
 * Why the effect is not modeled, as a kebab-case token:
 * `key-effect-not-modeled` when the key's own value is the reason,
 * `config-property-unreadable` when a top-level property of the same
 * config file could not be read statically, so no surface in it can
 * be classified at all. The set is open.
 */
reason: string
kind: "plugin-effect-not-modeled"
} | {
kind: "coverage-auto-detected"
} | {
kind: "flag-age-shallow-clone"
} | {
/**
 * Why no history is available, as a kebab-case token:
 * `not-a-repository` or `no-commits`. The set is open.
 */
cause: string
kind: "flag-age-unavailable"
} | {
/**
 * The `ignoreDependencies` entry, as written in the config.
 */
pattern: string
kind: "ignore-dependencies-glob-unmatched"
} | {
/**
 * The `ignoreFindings` entry, as written in the config.
 */
pattern: string
kind: "ignore-findings-pattern-unmatched"
})
/**
 * Why the declared pnpm version ignores the `overrides` section of
 * `pnpm-workspace.yaml`.
 */
export type PnpmWorkspaceOverridesIgnoredCause = ("package-json-overrides" | "pnpm-version")
/**
 * Discriminant for [`CloneGroupAction::kind`]. Mirrors the action types
 * emitted by the legacy `build_clone_group_actions` walker.
 */
export type CloneGroupActionType = ("extract-shared" | "suppress-line")
/**
 * Why the audit new-only gate demoted an introduced clone group to
 * inherited. Serializes as a kebab-case string on the wire (for example
 * `"no-added-lines"`).
 *
 * Further variants may be added in later releases; consumers should treat an
 * unknown value as "some demotion reason" rather than failing.
 */
export type CloneDemotionReason = "no-added-lines"
/**
 * The kind of refactoring suggested for a clone family.
 */
export type RefactoringKind = ("ExtractFunction" | "ExtractModule")
/**
 * Discriminant for [`CloneFamilyAction::kind`].
 */
export type CloneFamilyActionType = ("extract-shared" | "apply-suggestion" | "suppress-line")
/**
 * Which complexity threshold was exceeded.
 */
export type ExceededThreshold = ("cyclomatic" | "cognitive" | "both" | "crap" | "cyclomatic_crap" | "cognitive_crap" | "all")
/**
 * Severity tier indicating how far a function exceeds complexity thresholds.
 *
 * Determined by the highest tier reached across both cognitive and cyclomatic
 * scores. Default thresholds: cognitive 25/40, cyclomatic 30/50.
 */
export type FindingSeverity = ("moderate" | "high" | "critical")
/**
 * Coverage tier classification for CRAP findings.
 */
export type CoverageTier = ("none" | "partial" | "high")
/**
 * Provenance of a CRAP finding's coverage signal.
 */
export type CoverageSource = ("istanbul" | "estimated" | "estimated_component_inherited")
/**
 * Which complexity metric a [`ComplexityContribution`] adds to.
 */
export type ComplexityMetric = ("cyclomatic" | "cognitive")
/**
 * The syntactic construct that produced a single complexity increment.
 *
 * Mirrors `SonarSource` cognitive-complexity vocabulary where it overlaps.
 * `Case` means a `case` label carrying a test; a bare `default` adds nothing
 * to cyclomatic complexity and so produces no contribution.
 */
export type ComplexityContributionKind = ("if" | "else" | "else-if" | "ternary" | "logical-and" | "logical-or" | "nullish-coalescing" | "logical-assignment" | "optional-chain" | "for" | "for-in" | "for-of" | "while" | "do-while" | "switch" | "case" | "catch" | "labeled-break" | "labeled-continue" | "jsx-depth" | "hook-density" | "prop-count" | "await" | "then")
/**
 * Source for a finding's effective thresholds.
 */
export type ThresholdSource = "override"
/**
 * Discriminant for [`HealthFindingAction::kind`]. Mirrors the action types
 * emitted by `build_health_finding_actions`. A single finding's `actions`
 * array may carry multiple entries of different types: a finding that
 * exceeded both cyclomatic and CRAP at `coverage_tier: partial` will get
 * BOTH `increase-coverage` AND `refactor-function`, plus the trailing
 * `suppress-line`.
 */
export type HealthFindingActionType = ("refactor-function" | "add-tests" | "increase-coverage" | "suppress-file" | "suppress-line")
/**
 * Coverage model used for CRAP score computation.
 */
export type CoverageModel = ("static_binary" | "static_estimated" | "istanbul")
/**
 * Input format of the measured coverage behind `CoverageModel::Istanbul`.
 */
export type CoverageInputFormat = ("istanbul" | "v8")
/**
 * Whether CRAP findings in the report used one coverage-source kind or a mix.
 */
export type CoverageSourceConsistency = ("uniform" | "mixed")
/**
 * One health section that a run produced.
 *
 * A section is in [`HealthReport::sections`] when the run computed it and
 * the report carries its result, also when that result is empty. The value
 * set is OPEN: a later release can add a section, so a consumer must accept
 * a token that it does not know.
 */
export type HealthSection = ("complexity" | "vital-signs" | "score" | "file-scores" | "coverage-gaps" | "hotspots" | "targets" | "trend" | "runtime-coverage" | "css")
/**
 * Lifecycle state for a configured threshold override.
 */
export type ThresholdOverrideStatus = ("active" | "stale" | "insufficient" | "no_match")
/**
 * Which threshold dimension a `thresholdOverrides` state row describes.
 *
 * One configured override produces one row per dimension it participates in,
 * because the complexity ceilings and the CRAP ceiling are evaluated
 * independently: raising `maxCyclomatic` says nothing about whether the unit
 * still breaches `maxCrap`.
 */
export type ThresholdOverrideDimension = ("complexity" | "crap")
/**
 * Discriminant for [`UntestedFileAction::kind`]. Mirrors the action types
 * emitted by `build_untested_file_actions`.
 */
export type UntestedFileActionType = ("add-tests" | "suppress-file")
/**
 * Discriminant for [`UntestedExportAction::kind`]. Mirrors the action
 * types emitted by `build_untested_export_actions`.
 */
export type UntestedExportActionType = ("add-test-import" | "suppress-file")
/**
 * Churn trend indicator based on comparing recent vs older halves of the
 * analysis period.
 */
export type ChurnTrend = ("accelerating" | "stable" | "cooling")
/**
 * Encoding applied to a [`ContributorEntry::identifier`].
 */
export type ContributorIdentifierFormat = ("raw" | "handle" | "anonymized" | "hash")
/**
 * Ownership lifecycle state of a file.
 */
export type OwnershipState = ("active" | "unowned" | "declared_inactive" | "drifting")
/**
 * Discriminant for [`HotspotAction::kind`].
 */
export type HotspotActionType = ("refactor-file" | "add-tests" | "low-bus-factor" | "unowned-hotspot" | "ownership-drift")
/**
 * Strategy discriminant for the suggested CODEOWNERS pattern attached to
 * an `unowned-hotspot` action.
 */
export type HotspotActionHeuristic = "directory-deepest"
/**
 * Where the run's reference epoch came from.
 *
 * Churn recency weighting and ownership staleness are measured against one
 * instant. `head_commit` and `environment` resolve to the same value on every
 * run over the same commit; `wall_clock` does not.
 */
export type ClockSource = ("environment" | "head_commit" | "wall_clock")
/**
 * Runtime coverage JSON contract version. This is scoped to the
 * `runtime_coverage` block and is independent of the top-level fallow
 * JSON `schema_version`.
 */
export type RuntimeCoverageSchemaVersion = "1"
/**
 * Top-level verdict for the whole runtime-coverage report. Mirrors
 * `fallow_cov_protocol::ReportVerdict`. The verdict is the SINGLE most
 * actionable finding; for the full set of findings see
 * [`RuntimeCoverageReport::signals`]. The verdict promotes `hot-path-touched`
 * above `cold-code-detected` in PR-review context (when the CLI was
 * given a change-scope: `--diff-file` or `--changed-since`) because the
 * touched-hot-path is event-tied to the current diff and reviewers need
 * it to be the top-line signal. In standalone analysis (no change
 * scope), `cold-code-detected` remains primary.
 */
export type RuntimeCoverageReportVerdict = ("clean" | "hot-path-touched" | "cold-code-detected" | "license-expired-grace" | "unknown")
/**
 * Discrete signal captured during runtime-coverage post-processing.
 * `verdict` collapses to one summary value; `signals` enumerates ALL
 * findings the report carries so JSON consumers, CI dashboards, and
 * agents can reason about them independently of the headline. Order is
 * stable: severity-descending so the first entry mirrors a sensible
 * non-PR-context verdict.
 */
export type RuntimeCoverageSignal = ("license-expired-grace" | "cold-code-detected" | "hot-path-touched")
/**
 * Runtime coverage source used to produce the summary.
 */
export type RuntimeCoverageDataSource = ("local" | "cloud")
/**
 * Protocol-level per-function runtime coverage verdict derived from the
 * decision table in fallow-cov-protocol. The CLI's `runtime_coverage.findings`
 * array omits `active` entries even though the underlying enum still includes
 * it.
 */
export type RuntimeCoverageVerdict = ("safe_to_delete" | "review_required" | "coverage_unavailable" | "low_traffic" | "active" | "unknown")
/**
 * Confidence level for a runtime coverage finding.
 */
export type RuntimeCoverageConfidence = ("very_high" | "high" | "medium" | "low" | "none" | "unknown")
/**
 * The per-call cost that `optimization_target.cost_score` multiplies with
 * `invocations`.
 */
export type RuntimeCoverageCostBasis = ("inner_iterations" | "cognitive")
/**
 * Blast-radius risk band. The current thresholds are high at >=20 static
 * callers or >=1,000,000 traffic-weighted caller reach; medium at >=5 callers
 * or >=50,000 weighted reach; low otherwise.
 */
export type RuntimeCoverageRiskBand = ("low" | "medium" | "high")
/**
 * License or trial watermark applied to runtime coverage output.
 */
export type RuntimeCoverageWatermark = ("trial-expired" | "license-expired-grace" | "unknown")
/**
 * Coverage-intelligence JSON contract version. Scoped to the
 * `coverage_intelligence` block and independent of the top-level fallow
 * JSON `schema_version`.
 */
export type CoverageIntelligenceSchemaVersion = "1"
/**
 * Headline verdict for the combined coverage-intelligence report.
 */
export type CoverageIntelligenceVerdict = ("risky-change-detected" | "high-confidence-delete" | "review-required" | "refactor-carefully" | "clean" | "unknown")
/**
 * Ordered evidence signals behind a coverage-intelligence finding.
 */
export type CoverageIntelligenceSignal = ("changed" | "hot_path" | "low_test_coverage" | "high_crap" | "static_unused" | "runtime_cold" | "no_test_path" | "runtime_reachable" | "ownership_drift" | "test_covered")
/**
 * Recommended action family for a combined finding.
 */
export type CoverageIntelligenceRecommendation = ("add-test-or-split-before-merge" | "delete-after-confirming-owner" | "review-before-changing" | "refactor-carefully-keep-behavior")
/**
 * Confidence in the joined evidence and resulting recommendation.
 */
export type CoverageIntelligenceConfidence = ("high" | "medium" | "low")
/**
 * Confidence tier for the cross-surface evidence match.
 */
export type CoverageIntelligenceMatchConfidence = ("path-function-line" | "path-line" | "direct")
/**
 * Category of refactoring recommendation.
 */
export type RecommendationCategory = ("urgent_churn_complexity" | "break_circular_dependency" | "split_high_impact" | "remove_dead_code" | "extract_complex_functions" | "extract_dependencies" | "add_test_coverage")
/**
 * A ranked refactoring recommendation for a file.
 *
 * ## Priority Formula
 *
 * ```text
 * priority = min(density, 1) × 30 + hotspot_boost × 25 + dead_code × 20 + fan_in_norm × 15 + fan_out_norm × 10
 * ```
 *
 * Fan-in and fan-out normalization uses adaptive percentile-based thresholds
 * (p95 of the project distribution, with floors) instead of fixed constants.
 *
 * ## Efficiency (default sort)
 *
 * ```text
 * efficiency = priority / effort_numeric   (Low=1, Medium=2, High=3)
 * ```
 *
 * Surfaces quick wins: high-priority, low-effort targets rank first.
 * Effort estimate for a refactoring target.
 */
export type EffortEstimate = ("low" | "medium" | "high")
/**
 * Confidence level for a refactoring recommendation.
 *
 * Based on the data source reliability:
 * - **High**: deterministic graph/AST analysis (dead code, circular deps, complexity)
 * - **Medium**: heuristic thresholds (fan-in/fan-out coupling)
 * - **Low**: depends on git history quality (churn-based recommendations)
 */
export type Confidence = ("high" | "medium" | "low")
/**
 * Discriminant for [`RefactoringTargetAction::kind`].
 */
export type RefactoringTargetActionType = ("apply-refactoring" | "suppress-line")
/**
 * Direction of a metric's change, semantically (improving/declining/stable).
 */
export type TrendDirection = ("improving" | "declining" | "stable")
/**
 * Detector status codes for framework health observability.
 */
export type FrameworkHealthDetectorStatus = ("active" | "disabled_by_config" | "abstained" | "not_checked")
/**
 * Discriminant for [`CssCandidateAction::kind`].
 */
export type CssCandidateActionType = ("verify-unused" | "verify-undefined" | "consolidate" | "replace-with-token" | "standardize" | "simplify-selector")
/**
 * Discriminant for [`UnusedAtRule::kind`].
 */
export type UnusedAtRuleKind = ("property-registration" | "layer")
/**
 * The surface through which a design token is consumed. The `theme-var` /
 * `css-var` / `utility` / `apply` kinds are Tailwind v4 `@theme` consumption; the
 * `js-member` / `js-call` kinds are CSS-in-JS consumption (member access through a
 * same-file or imported StyleX/vanilla-extract token binding, a StyleX
 * theme-group call, or a PandaCSS token-path call). The kind is the disjoint origin signal that
 * distinguishes a Tailwind token entry from a CSS-in-JS token entry in the
 * shared `token_consumers` list.
 */
export type ConsumerKind = ("theme-var" | "css-var" | "utility" | "apply" | "js-member" | "js-call")
/**
 * Trust level for a [`StylingHealth`] grade. TWO variants (not the three-tier
 * `high`/`medium`/`low` of [`crate::Confidence`] / `FeatureFlagConfidence`) ON
 * PURPOSE: styling confidence is binary (the grade is either reliable for the
 * analyzed surface or it is not), not three distinct evidence tiers, so a
 * never-emitted `Medium` would be dead surface. Serializes lowercase (`"high"` /
 * `"low"`), matching the sibling confidence enums' vocabulary.
 */
export type StylingHealthConfidence = ("high" | "low")
/**
 * Effective configured severity for a styling finding.
 */
export type StylingFindingSeverity = ("warn" | "error")
/**
 * Confidence hint for a [`StylingFinding`].
 */
export type StylingFindingConfidence = ("high" | "low")
/**
 * Agent handling hint for a [`StylingFinding`].
 */
export type StylingAgentDisposition = ("fix-confidently" | "verify-first")
/**
 * `target` block of [`InspectOutput`], tagged by `type`.
 */
export type InspectTargetDescriptor = ({
/**
 * File path relative to the analysed root.
 */
file: string
type: "file"
} | {
/**
 * File path relative to the analysed root.
 */
file: string
/**
 * Name of the inspected export.
 */
export_name: string
type: "symbol"
})
/**
 * `identity` block of [`InspectOutput`]; shape follows the target type.
 */
export type InspectIdentity = (InspectFileIdentity | InspectSymbolIdentity)
/**
 * Status of an [`InspectEvidenceSection`].
 */
export type InspectSectionStatus = ("ok" | "partial" | "unavailable" | "error")
/**
 * Granularity an [`InspectEvidenceSection`] payload covers.
 */
export type InspectEvidenceScope = ("symbol" | "file" | "project_filtered_to_file")
/**
 * Wire-version discriminator for [`ImportPathTrace`]. Independent from the
 * global `SchemaVersion`: the import-path payload versions on its own cadence,
 * like the other independently-versioned envelopes. Serializes as a string
 * `const` so JSON consumers can switch on it.
 */
export type ImportPathTraceSchemaVersion = "1"
/**
 * Best-effort classification of why a callee did not resolve to an edge.
 */
export type UnresolvedReason = ("local-or-global" | "member-or-dynamic")
/**
 * Wire-version discriminator for [`ErrorTrace`]. Independent from the global
 * `SchemaVersion` and from the other trace payloads, like
 * [`crate::trace::ImportPathTraceSchemaVersion`]. Serializes as a string
 * `const` so JSON consumers can switch on it.
 */
export type ErrorTraceSchemaVersion = "1"
/**
 * Where a frame's source location sits relative to the analysed project.
 */
export type FrameOrigin = ("in_project" | "node_modules" | "out_of_corpus")
/**
 * What the project graph could say about a frame's identifier.
 *
 * `not_attempted` is not a softer `not_found`: it records that the graph was
 * never consulted, because the frame does not point at project source. Keeping
 * them apart is what lets `resolved + ambiguous + not_found + not_attempted`
 * equal the frame count without any of the four lying about what it measured.
 */
export type FrameResolution = ("resolved" | "ambiguous" | "not_found" | "not_attempted")
/**
 * Singleton GitHub review-event marker.
 */
export type ReviewEnvelopeEvent = "COMMENT"
/**
 * Per-line review comment. Schema is an `anyOf` between GitHub and GitLab
 * shapes; at runtime every entry in a single envelope comes from the same
 * provider because the envelope is built from one provider's branch in
 * `crates/cli/src/report/ci/review.rs::render_review_envelope`.
 */
export type ReviewComment = (GitHubReviewComment | GitLabReviewComment)
/**
 * Singleton side discriminator for [`GitHubReviewComment::side`].
 */
export type GitHubReviewSide = "RIGHT"
/**
 * Singleton position-type discriminator for [`GitLabReviewPosition`].
 */
export type GitLabReviewPositionType = "text"
/**
 * Schema-version discriminator for the review envelope.
 */
export type ReviewEnvelopeSchema = ("fallow-review-envelope/v1" | "fallow-review-envelope/v2" | "fallow-review-envelope/v3")
/**
 * Review-envelope provider tag.
 */
export type ReviewProvider = ("github" | "gitlab")
/**
 * `meta.check_conclusion` for the GitHub review envelope. Maps to the
 * GitHub Checks API conclusion field.
 */
export type ReviewCheckConclusion = ("success" | "neutral" | "failure")
/**
 * Stable identifier used to isolate independent review integrations on the
 * same pull or merge request.
 */
export type ReviewId = string
/**
 * Schema-version discriminator for the review reconcile envelope.
 */
export type ReviewReconcileSchema = "fallow-review-reconcile/v1"
/**
 * Schema-version discriminator for [`CoverageSetupOutput`].
 */
export type CoverageSetupSchemaVersion = "1"
/**
 * Framework detected during coverage setup; drives which instrumentation
 * guidance is emitted.
 */
export type CoverageSetupFramework = ("nextjs" | "nestjs" | "nuxt" | "sveltekit" | "astro" | "remix" | "vite" | "plain_node" | "unknown")
/**
 * Package manager detected from the project's lockfile.
 */
export type CoverageSetupPackageManager = ("npm" | "pnpm" | "yarn" | "bun")
/**
 * Runtime environment coverage capture must instrument.
 */
export type CoverageSetupRuntimeTarget = ("node" | "browser")
/**
 * Schema-version discriminator for [`CoverageAnalyzeOutput`].
 */
export type CoverageAnalyzeSchemaVersion = ("1" | "2")
/**
 * Discovery outcome for a [`LogicalGroup`].
 */
export type LogicalGroupStatus = ("ok" | "empty" | "invalid_path")
/**
 * Exact schema version for [`HealthOutput`].
 */
export type HealthSchemaVersion = 11
/**
 * Resolver mode label for grouped envelopes (dead-code, dupes, health).
 *
 * `owner` groups by CODEOWNERS team, `directory` groups by top-level
 * directory prefix, `package` groups by workspace package name, `section`
 * groups by GitLab CODEOWNERS `[Section]` header name.
 */
export type GroupByMode = ("owner" | "directory" | "package" | "section")
/**
 * What the group trend compared, for one group.
 *
 * The value set is open: read an unknown value as "no trend for this group".
 */
export type GroupTrendStatus = ("compared" | "new_group" | "no_group_baseline")
/**
 * Schema projection for the duplication envelope's CLI and programmatic
 * version lineages.
 */
export type DupesSchemaVersion = (4 | 10)
/**
 * Wire-version discriminator for [`ImpactReport`]. Independent from the global
 * `SchemaVersion` (the impact report versions on its own cadence) and from the
 * on-disk `STORE_SCHEMA_VERSION` (the persisted store shape versions
 * separately). Serializes as a string `const` so JSON consumers can switch on
 * it, matching the other independently-versioned envelopes (e.g.
 * `CoverageAnalyzeSchemaVersion`).
 */
export type ImpactReportSchemaVersion = ("1" | "2")
/**
 * Why Impact tracking is (or is not) active for a project. `Project` = an
 * explicit per-repo `enable`; `User` = the user-global default with no per-repo
 * decision; `Default` = off (no per-repo decision and no global default).
 */
export type EnabledSource = ("project" | "user" | "default")
/**
 * Direction of a count trend between two recorded runs.
 */
export type ImpactTrendDirection = ("improving" | "declining" | "stable")
/**
 * Independent wire-version for the cross-repo report, on its own cadence (it
 * versions separately from the per-project `ImpactReportSchemaVersion` and the
 * on-disk `STORE_SCHEMA_VERSION`).
 */
export type CrossRepoImpactSchemaVersion = ("1" | "2")
/**
 * The `fallow security --format json` schema version. Independently versioned
 * from the main contract, mirroring `ImpactReportSchemaVersion`.
 */
export type SecuritySchemaVersion = ("1" | "2" | "3" | "4" | "5" | "6" | "7" | "8")
/**
 * Severity level for rules.
 *
 * Controls whether an issue type causes CI failure (`error`), is reported
 * without failing (`warn`), or is suppressed entirely (`off`).
 */
export type Severity = ("error" | "warn" | "off")
/**
 * Gate mode for `fallow security --gate <mode>`.
 */
export type SecurityGateMode = ("new" | "newly-reachable")
/**
 * Gate verdict on the wire. `fail` is the CI-state token; human output renders
 * it as "REVIEW REQUIRED" because these stay unverified candidates, never
 * confirmed vulnerabilities.
 */
export type SecurityGateVerdict = ("pass" | "fail")
/**
 * The kind of security candidate. Findings are CANDIDATES for downstream agent
 * verification, NOT verified vulnerabilities.
 */
export type SecurityFindingKind = ("client-server-leak" | "tainted-sink")
/**
 * Verification-priority tier for a security candidate. This is ranking, not an
 * exploitability verdict.
 */
export type SecuritySeverity = ("high" | "medium" | "low")
/**
 * The role a hop plays in a security finding's structural import trace.
 */
export type TraceHopRole = ("client-boundary" | "untrusted-source" | "module-source" | "intermediate" | "secret-source" | "sink")
/**
 * Dead-code issue kind linked to a security candidate.
 */
export type SecurityDeadCodeKind = ("unused-file" | "unused-export")
/**
 * How strongly the untrusted-source signal is associated with the sink, a
 * structured discriminator so a consumer can tier candidates without parsing
 * the human `evidence` prose. Present only when
 * [`SecurityReachability::reachable_from_untrusted_source`] is true. Neither
 * value proves exploitability; both are ranking signals (issue #885 doctrine:
 * rank, never gate).
 */
export type TaintConfidence = ("arg-level" | "module-level")
/**
 * Static URL construction shape captured for URL-shaped security sinks.
 */
export type SecurityUrlShape = ("fixed-origin-dynamic-path" | "dynamic-origin")
/**
 * Runtime coverage state for the function enclosing a security sink.
 * This is production-observation evidence, not an exploitability verdict.
 */
export type SecurityRuntimeState = ("runtime-hot" | "runtime-cold" | "never-executed" | "low-traffic" | "coverage-unavailable" | "runtime-unknown")
/**
 * Family of a control pattern observed in a file on an import trace.
 */
export type SecurityControlKind = ("sanitization" | "validation" | "authentication" | "authorization")
/**
 * Why a sink-shaped callee could not be flattened into a static catalogue
 * path.
 */
export type SkippedSecurityCalleeReason = ("computed-member" | "dynamic-dispatch" | "unsupported-assignment-object")
/**
 * Syntactic expression shape for a skipped security callee.
 */
export type SkippedSecurityCalleeExpressionKind = ("static-member-expression" | "computed-member-expression" | "identifier" | "other")
/**
 * The `fallow security survivors --format json` schema version.
 */
export type SecuritySurvivorsSchemaVersion = "2"
/**
 * Verifier verdict status accepted by `fallow security survivors`.
 */
export type SecurityVerifierVerdictStatus = ("survivor" | "dismissed" | "needs-human-review")
/**
 * The `fallow security blind-spots --format json` schema version.
 */
export type SecurityBlindSpotsSchemaVersion = "1"
/**
 * Schema projection for the combined envelope's exact version.
 */
export type CombinedSchemaVersion = 13
/**
 * Schema projection for the feature-flags envelope's exact version.
 */
export type FeatureFlagsSchemaVersion = 8
/**
 * Feature flag kind values emitted in JSON.
 */
export type FeatureFlagKind = ("environment_variable" | "sdk_call" | "config_object")
/**
 * Feature flag confidence values emitted in JSON.
 */
export type FeatureFlagConfidence = ("high" | "medium" | "low")
/**
 * Feature flag action discriminants.
 */
export type FeatureFlagActionType = ("investigate-flag" | "suppress-line")
/**
 * How the report measures the age of a flag.
 */
export type FlagAgeMode = ("blame" | "pickaxe" | "off")
/**
 * How a retirement row's flag was detected.
 */
export type RetirementFlagKind = ("environment_variable" | "sdk_call" | "config_object" | "constant" | "vendor_export")
/**
 * What a site does with the flag.
 */
export type FlagSiteRole = ("read" | "definition")
/**
 * Why a flag is a retirement candidate.
 */
export type RetirementReason = ("single-read-site" | "test-only" | "literal-constant" | "identical-branches" | "empty-branch" | "guards-dead-code" | "defined-never-read" | "fully-rolled-out" | "archived-in-vendor" | "missing-in-vendor" | "vendor-only")
/**
 * Action discriminants for a retirement row.
 */
export type RetirementActionType = "review-retirement"
/**
 * State of a flag in a `--flag-state` vendor export.
 */
export type VendorFlagState = ("on" | "off" | "rolled_out" | "archived" | "experiment")
/**
 * Independently-versioned wire-version newtype for the brief envelope.
 * Serializes as the integer `REVIEW_BRIEF_SCHEMA_VERSION`.
 */
export type ReviewBriefSchemaVersion = 12
/**
 * The exactly-three shippable decision categories (the SOLID-3). No cut category
 * (abstraction / deletion / convention / irreversibility) is representable: this
 * enum is the structural guarantee that confirmed-noise categories never ship.
 */
export type DecisionCategory = ("coupling-boundary" | "public-api-contract" | "dependency")
/**
 * Coarse risk classification for a changeset, a pure function of the change
 * size (file count plus, once threaded, net lines).
 */
export type RiskClass = ("low" | "medium" | "high")
/**
 * Suggested reviewer effort, a pure function of [`RiskClass`].
 */
export type ReviewEffort = ("glance" | "review" | "deep_dive")
/**
 * The focus label for a review unit. `Skip` is the SAFE explicit-skip label and
 * is runtime-backed ONLY: it is producible solely on the paid runtime path
 * and solely for a unit runtime-proves cold with zero risk signals. Free mode
 * (no runtime evidence) emits only `ReviewHere` / `NotPrioritized`, never `Skip`
 * (the build-focus-map labeller cannot reach the `Skip` arm without runtime
 * input), so the free-tier "rank but never skip" stance holds by construction.
 */
export type FocusLabel = ("review-here" | "not-prioritized" | "skip")
/**
 * A per-unit confidence flag. The EXACT panel-decided strings: a dynamically-
 * wired or re-export-heavy unit carries one so its static-reachability signal is
 * not trusted as complete (the anti-silent-de-prioritization guard). The flag
 * NEVER lowers the score; it is advisory provenance.
 */
export type ConfidenceFlag = ("dynamic-dispatch" | "re-export-indirection")
/**
 * The category of a single weakening signal.
 */
export type WeakeningKind = ("test-weakened" | "threshold-lowered" | "suppression-added" | "security-check-removed")
/**
 * Where a cognitive-complexity improvement came from.
 */
export type CognitiveAttribution = ("nesting-reset" | "fewer-branch-points" | "mixed")
/**
 * Independently-versioned wire-version newtype. Serializes as the integer
 * [`DECISION_SURFACE_SCHEMA_VERSION`].
 */
export type DecisionSurfaceSchemaVersion = number
/**
 * The discriminated action kinds a decision can carry.
 */
export type DecisionActionType = ("ask-expert" | "suppress")
/**
 * Whether a changed source unit has a test file importing it, and whether that
 * test moved with the change. A direct-importer fact from the graph, not a
 * coverage claim: `untouched` says a test exists and was not edited, which is
 * the verification question the reviewer asks the author, never an answer.
 */
export type TestAdjacency = ("none" | "untouched" | "changed")
/**
 * The `fallow suppressions --format json` schema version. Independently
 * versioned from the main contract, mirroring `SecuritySchemaVersion`.
 */
export type SuppressionInventorySchemaVersion = "1"
/**
 * Suppression marker scope.
 */
export type SuppressionInventoryLevel = ("file" | "line")
/**
 * How a suppression in the inventory was authored.
 */
export type SuppressionInventoryOrigin = "comment"
/**
 * Schema projection for the exact doctor envelope version.
 */
export type DoctorSchemaVersion = 2
/**
 * Schema projection for `.` as the privacy-safe diagnosed project root.
 */
export type DoctorProjectRoot = "."
/**
 * Aggregate readiness outcome.
 */
export type DoctorStatus = ("pass" | "warn" | "fail")
/**
 * Stable identifier for a doctor check. Declaration order is output order.
 */
export type DoctorCheckId = ("root" | "config" | "workspaces" | "plugins" | "type-aware" | "dependencies" | "cache" | "graph-cache")
/**
 * Stable category for a doctor check.
 */
export type DoctorCheckCategory = ("project" | "configuration" | "workspace" | "plugin" | "companion" | "cache")
/**
 * Per-check readiness outcome.
 */
export type DoctorCheckStatus = ("pass" | "warn" | "fail" | "skipped")
/**
 * Schema projection for the type-aware status envelope's exact version.
 */
export type TypeAwareStatusSchemaVersion = 8
/**
 * Version singleton for raw similar-code output.
 */
export type SimilarCodeSchemaVersion = "1"
/**
 * Provider families admitted by the version 1 public contract.
 */
export type SimilarCodeProvider = "official-local-companion"
/**
 * Stable, coarse interpretation of a candidate score.
 */
export type SimilarCodeSimilarityBand = ("moderate" | "high" | "very-high")
/**
 * Verification state of a raw semantic candidate.
 */
export type SimilarCodeVerificationStatus = "unverified"
/**
 * Explicit availability state for one optional enrichment source.
 */
export type SimilarCodeEnrichmentState = ("available" | "unavailable" | "not-requested")
/**
 * Read-only actions supported by the candidate workflow.
 */
export type SimilarCodeActionType = ("inspect" | "review")
/**
 * Overall trustworthiness of an emitted result set.
 */
export type SimilarCodeCompletionStatus = ("complete" | "partial")
/**
 * Bounded phase names in the similar-code pipeline.
 */
export type SimilarCodePhase = ("discovery" | "extraction" | "cache" | "embedding" | "validation" | "comparison" | "enrichment")
/**
 * Completion state for one generation phase.
 */
export type SimilarCodePhaseStatus = ("complete" | "partial" | "skipped" | "timed-out")
/**
 * Stable reasons why admitted work was skipped or truncated.
 */
export type SimilarCodeSkipReason = ("below-minimum-lines" | "unsupported-function" | "generated-source" | "function-too-large" | "input-limit" | "source-bytes-limit" | "vector-memory-limit" | "comparison-limit" | "candidate-limit" | "neighbor-limit" | "timeout" | "provider-failure" | "token-truncation" | "enrichment-unavailable")
/**
 * Vector cache outcome for a run.
 */
export type SimilarCodeCacheStatus = ("disabled" | "hit" | "miss" | "mixed")
/**
 * Non-severity diagnostic domain for similar-code generation.
 */
export type SimilarCodeDiagnosticDomain = ("workspace" | "extraction" | "provider" | "cache" | "enrichment" | "review")
/**
 * Version singleton for a similar-code inspect packet.
 */
export type SimilarCodeInspectSchemaVersion = "1"
/**
 * Conservative syntactic side-effect hint for an inspected function.
 */
export type SimilarCodeSideEffectHint = ("pure-looking" | "may-have-side-effects" | "unknown")
/**
 * Version singleton for reviewed similar-code output.
 */
export type SimilarCodeReviewSchemaVersion = "1"
/**
 * Domain outcome assigned by review, never by candidate generation.
 */
export type SimilarCodeDomainOutcome = ("same-responsibility" | "related-but-distinct" | "intentional-duplication" | "unrelated" | "needs-human-review")
/**
 * How review matched an external verdict to the current candidate.
 */
export type SimilarCodeVerdictMatch = ("candidate-id" | "review-key" | "unverified" | "ambiguous-review-key")
/**
 * Discriminator value for [`CodeClimateIssue::kind`].
 */
export type CodeClimateIssueKind = "issue"
/**
 * CodeClimate severity scale.
 */
export type CodeClimateSeverity = ("info" | "minor" | "major" | "critical" | "blocker")
/**
 * Envelope emitted by `fallow --format codeclimate` and
 * `fallow --format gitlab-codequality`. GitLab Code Quality consumes the
 * same shape. The wire form is a bare JSON array, not an object.
 */
export type CodeClimateOutput = CodeClimateIssue[]

/**
 * `fallow audit --format json` envelope.
 */
export interface AuditOutput {
schema_version: AuditSchemaVersion
version: ToolVersion
command: AuditCommand
verdict: AuditVerdict
/**
 * Number of changed files in the audit scope.
 */
changed_files_count: number
/**
 * Git ref the change was diffed against.
 */
base_ref: string
/**
 * Human-readable provenance of `base_ref`, e.g. `merge-base with
 * origin/main`, `local main`, or `FALLOW_AUDIT_BASE=upstream/main`.
 * Present when the base was auto-detected or set via `FALLOW_AUDIT_BASE`;
 * absent for an explicit `--base` (the ref the user typed is already
 * self-describing).
 */
base_description?: (string | null)
/**
 * Commit SHA of the audited head tree, when resolvable.
 */
head_sha?: (string | null)
elapsed_ms: ElapsedMs
/**
 * True when base-snapshot analysis was skipped, so new-vs-inherited
 * attribution could not run.
 */
base_snapshot_skipped?: (boolean | null)
summary: AuditSummary
attribution: AuditAttribution
/**
 * The verdict of every gate this run evaluated, keyed by name. The CLI
 * always emits it, with the command's default exit rule in it also when
 * no flag armed a gate, so a CI integration reads the verdict instead of
 * guessing from a process status it usually cannot see. A gate fails the
 * build when `status` is `fail` AND `enforced` is true. The typed
 * programmatic API runs no CLI gate and leaves it absent. See
 * [`crate::GateOutcomes`].
 */
gate_outcomes?: (GateOutcomes | null)
/**
 * `_meta` block with metric / rule definitions, when `--explain` was
 * passed.
 */
_meta?: (Meta | null)
/**
 * Dead-code findings scoped to the audit changeset.
 */
dead_code?: (CheckOutput | null)
/**
 * Duplication findings scoped to the audit changeset.
 */
duplication?: (DupesReportPayload | null)
/**
 * Complexity findings scoped to the audit changeset.
 */
complexity?: (HealthReport | null)
/**
 * Read-only follow-up commands computed from this run's findings. See
 * `CheckOutput::next_steps` for the contract.
 */
next_steps?: NextStep[]
}
/**
 * Per-category summary counts for the audit result.
 */
export interface AuditSummary {
/**
 * Total dead-code issues reported for the changed files.
 */
dead_code_issues: number
/**
 * Whether any reported dead-code issue has error severity.
 */
dead_code_has_errors: boolean
/**
 * Total complexity findings reported for the changed files.
 */
complexity_findings: number
/**
 * Highest cyclomatic complexity among the findings; `None` when there
 * are no complexity findings.
 */
max_cyclomatic?: (number | null)
/**
 * Clone groups touching the changed files.
 */
duplication_clone_groups: number
}
/**
 * New-vs-inherited issue counts for audit.
 */
export interface AuditAttribution {
gate: AuditGate
/**
 * Dead-code findings absent from the base snapshot.
 */
dead_code_introduced: number
/**
 * Dead-code findings already present in the base snapshot.
 */
dead_code_inherited: number
/**
 * Complexity findings absent from the base snapshot.
 */
complexity_introduced: number
/**
 * Complexity findings already present in the base snapshot.
 */
complexity_inherited: number
/**
 * Clone groups absent from the base snapshot.
 */
duplication_introduced: number
/**
 * Clone groups already present in the base snapshot.
 */
duplication_inherited: number
styling_introduced: number
styling_inherited: number
duplication_demoted: number
}
/**
 * The verdict of every gate a run evaluated, keyed by name.
 *
 * A gate is armed by a flag or by config. The default exit rule of a command
 * is always in the object, also when no flag armed a gate:
 * `error-severity-findings` on `dead-code`, `check` and the combined run
 * (with `health-findings` when the combined run analyzed health),
 * `health-findings` on `health`, `security-advisory` on `security` and
 * `audit-verdict` on `audit`. A reader of the JSON sees a failing run without
 * the exit code. `dupes` has no default exit rule, so a `dupes` run that armed
 * no gate carries no object and always exits 0.
 *
 * An empty object is never emitted. The typed programmatic API runs no CLI
 * gate and leaves the object absent.
 *
 * The names this build can emit are `error-severity-findings`, `regression`,
 * `stale-baseline`, `baseline-growth`, `duplication-threshold`,
 * `duplication-findings`, `health-min-score`,
 * `health-min-severity`, `health-findings`, `health-coverage-gaps`,
 * `health-runtime-coverage`, `security`, `security-advisory`, `audit-verdict`,
 * `type-aware-require` and `parse-error`. The set is OPEN: a name a consumer does not
 * recognise means "some gate", not an error.
 */
export interface GateOutcomes {
[k: string]: GateOutcome
}
/**
 * One gate's verdict on one run.
 *
 * `status` and `enforced` answer different questions and legitimately
 * disagree. `status` is what the rule concluded; `enforced` is whether a
 * `fail` from this gate would make the run exit non-zero. A
 * `health --report-only` run is an explicit request never to fail, so a
 * failing gate there reports `status: fail` with `enforced: false`, and a
 * stale-baseline verdict published without `--fail-on-stale-baseline` reports
 * the same pair.
 *
 * **A gate fails the build when `status` is `fail` AND `enforced` is true.**
 * Neither member decides it alone: `enforced` is true on every armed gate,
 * including the ones that passed, so gating on it by itself fails every run
 * that armed anything. Read `status` on its own to decide what to say, and
 * remember that `warn` and `skipped` are neither a pass nor a failure.
 */
export interface GateOutcome {
status: GateStatus
/**
 * True when a `fail` from this gate makes the run exit non-zero. False
 * when the verdict is published for information only: the gate was never
 * armed, the run was told never to fail, or the combined machine formats
 * exit 0 for the gate (bare `fallow` without `--fail-on-issues`).
 */
enforced: boolean
/**
 * The measured value the gate compared, when there is one: the duplication
 * percentage, the health score, the number of findings at or above the
 * severity floor, the number of `error` findings of `health-findings` and
 * of `error-severity-findings`, or
 * the number of files in `files`. Whole numbers are
 * carried as JSON numbers, so a count of three reads as `3.0`. Absent for
 * gates that compare no number.
 */
observed?: (number | null)
/**
 * The configured limit `observed` was compared against, when there is one.
 * Absent for gates that compare no number.
 */
threshold?: (number | null)
/**
 * How the limit was spelled, for a gate whose `threshold` number does not
 * carry its own unit. `health-min-severity` sets it to the severity floor
 * (`moderate`, `high` or `critical`); `health-findings` and
 * `error-severity-findings` set it to `error`, the rule severity they
 * count; `regression` sets it to the
 * tolerance as the user wrote it (`"50%"` or `"5"`), because `threshold`
 * there is the allowance in issues and the percentage would otherwise be
 * unrecoverable on the grouped envelope, which carries no `regression`
 * object. Absent for gates whose numbers speak for themselves.
 */
threshold_label?: (string | null)
/**
 * The files the gate judged, for a gate that judges files rather than a
 * number. Only `parse-error` sets it: one item per file that did not
 * parse cleanly, sorted by path. Absent when the list is empty.
 */
files?: GateFile[]
}
/**
 * One file a file-judging gate names, with the reason the gate counted it.
 *
 * Today only `parse-error` emits it. The item carries the same facts as the
 * `source-parse-degraded` entry in `workspace_diagnostics[]` for that file,
 * so a consumer can act on the gate without a join.
 */
export interface GateFile {
/**
 * The file path, relative to the project root, with `/` separators.
 */
path: string
/**
 * The number of parser errors for the file.
 */
error_count: number
/**
 * True when the parser stopped in the file instead of recovering, so the
 * analysis saw only the part before the error.
 */
panicked: boolean
}
/**
 * Metric and rule definitions emitted under `_meta` when `--explain` is
 * passed (always present in MCP responses). Helps AI agents and CI systems
 * interpret metric values without re-reading the docs site.
 */
export interface Meta {
/**
 * URL to the documentation page for this command.
 */
docs?: (string | null)
/**
 * Local telemetry correlation metadata for agent follow-up runs.
 */
telemetry?: (TelemetryMeta | null)
/**
 * Provenance for the opt-in TypeScript semantic analysis pass.
 */
type_aware?: (TypeAwareMeta | null)
/**
 * Per-field definitions for envelope fields and action payload fields.
 */
field_definitions?: {
[k: string]: string
}
/**
 * Per-metric definitions: name, description, range, interpretation.
 */
metrics?: {
[k: string]: MetaMetric
}
/**
 * Per-rule definitions for check command output.
 */
rules?: {
[k: string]: MetaRule
}
}
/**
 * Privacy-safe local run metadata emitted for JSON consumers.
 */
export interface TelemetryMeta {
/**
 * Ephemeral local token that may be passed to the hidden `--parent-run`
 * flag on a later command. It is not derived from repository, path, user,
 * machine, project, or cloud data.
 */
analysis_run_id?: (string | null)
}
/**
 * Bounded provenance emitted when the opt-in type-aware pass runs.
 */
export interface TypeAwareMeta {
/**
 * Compatibility identity used by audit, baselines, snapshots, and stores.
 */
identity?: (SemanticAnalysisIdentity | null)
/**
 * Effective CLI or repository policy for incomplete semantic evidence.
 */
required_completeness?: (SemanticCompletenessRequirement | null)
/**
 * Compact status for every requested semantic query.
 */
queries?: SemanticQuerySummary[]
/**
 * Bounded decision and evidence for each semantic dead-code candidate.
 */
candidate_decisions?: SemanticCandidateDecision[]
/**
 * Checker-backed trace evidence requested by focused symbol queries.
 */
symbol_traces?: SemanticSymbolTrace[]
/**
 * Package-public surface and confirmed private type leaks.
 */
api_surface?: (ApiSurfaceResult | null)
/**
 * Exact-symbol blast radius and targeted-test recommendations.
 */
symbol_impacts?: SemanticSymbolImpact[]
/**
 * Advisory project-local public-signature coupling.
 */
type_coupling?: (TypeCouplingReport | null)
/**
 * Whether the semantic companion executed at least one query.
 */
executed: boolean
/**
 * Version of Fallow's backend-neutral sidecar protocol.
 */
protocol_version: number
/**
 * Version of the sidecar package that executed the query.
 */
sidecar_version?: (string | null)
/**
 * Semantic backend capability identifier.
 */
backend: string
/**
 * Backend compiler or engine version that executed the query.
 */
backend_version?: (string | null)
/**
 * TypeScript project configs selected for candidate files.
 */
selected_tsconfigs: string[]
/**
 * Number of candidate findings sent to the sidecar.
 */
candidate_count: number
/**
 * Number of candidates confirmed as used and removed.
 */
confirmed_used_count: number
/**
 * Number of candidates preserved because they implement or override a contract.
 */
contract_preserved_count: number
/**
 * Number of candidates with complete, closed-world no-static-reference evidence.
 */
no_static_references_count: number
/**
 * Number of retained class members eligible for a guarded type-aware fix.
 */
fix_eligible_count: number
/**
 * Number of candidates retained because semantic use was unresolved.
 */
unresolved_count: number
/**
 * Number of candidates retained because semantic analysis abstained.
 */
abstained_count: number
abstention_reasons: TypeAwareAbstentionCounts
/**
 * Per-project semantic refinement status and evidence.
 */
projects: TypeAwareProjectMeta[]
/**
 * Number of bounded warnings returned by the sidecar.
 */
warning_count: number
/**
 * Bounded semantic warnings. Findings mentioned here were retained.
 */
warnings: string[]
/**
 * Semantic pass duration as reported by the sidecar.
 */
elapsed_ms: number
phase_timings_ms: TypeAwarePhaseTimings
}
/**
 * Compatibility identity for comparing two analysis results.
 */
export interface SemanticAnalysisIdentity {
mode: SemanticAnalysisMode
/**
 * Version of the semantic result schema, independent of tool versions.
 */
semantic_schema_version: number
/**
 * Sorted capability set requested for the analysis.
 */
capabilities: SemanticCapability[]
/**
 * Hash of normalized project ownership and compiler configuration.
 */
project_config_hash: string
/**
 * Backend family, such as `typescript-go`.
 */
backend_family: string
completeness: SemanticCompleteness
}
/**
 * Compact per-query status embedded in run metadata.
 */
export interface SemanticQuerySummary {
/**
 * Stable query identifier within this analysis run.
 */
query_id: number
capability: SemanticCapability
/**
 * Operation-specific assertion, never a generic compiler verdict.
 */
assertion: string
status: SemanticCompleteness
/**
 * Stable primary gap reason when partial or unavailable.
 */
reason_code?: (SemanticGapReason | null)
/**
 * Evidence count before bounding.
 */
total_evidence_count: number
/**
 * Whether evidence or payload arrays were truncated.
 */
truncated: boolean
/**
 * Counted omissions.
 */
omissions?: SemanticOmission[]
/**
 * Plain next actions.
 */
actions?: string[]
}
/**
 * Counted omission attached to a partial or unavailable result.
 */
export interface SemanticOmission {
reason_code: SemanticGapReason
/**
 * Number of omitted items or relations.
 */
count: number
}
/**
 * Bounded, inspectable decision record for one semantic dead-code candidate.
 */
export interface SemanticCandidateDecision {
/**
 * Query identifier used to correlate low-level query metadata.
 */
query_id: number
subject: SemanticSymbol
decision: SemanticCandidateDecisionKind
status: SemanticCompleteness
/**
 * Every selected TypeScript project that owns the declaration.
 */
owning_projects: string[]
/**
 * Bounded checker-resolved reference or uncertainty evidence.
 */
evidence?: SemanticReference[]
/**
 * Inherited contract evidence, when present.
 */
contract?: (SemanticContractEvidence | null)
/**
 * Framework-owned contract evidence, distinct from TypeScript contracts.
 */
framework_contract?: (SemanticFrameworkContractEvidence | null)
/**
 * Whether this exact decision may enable a guarded class-member fix.
 */
closed_world_eligible: boolean
/**
 * Exact declaration guard required before a source edit.
 */
edit_guard?: (SemanticEditGuard | null)
/**
 * Primary reason when the candidate remains unresolved or abstained.
 */
reason_code?: (SemanticGapReason | null)
/**
 * Concise explanation suitable for dry-run and agent output.
 */
explanation: string
/**
 * Plain next actions.
 */
actions?: string[]
/**
 * Evidence count before bounding.
 */
total_evidence_count: number
/**
 * Whether evidence was truncated.
 */
truncated: boolean
/**
 * Counted omissions.
 */
omissions?: SemanticOmission[]
}
/**
 * Stable identity for a declaration sent to or returned by the backend.
 */
export interface SemanticSymbol {
/**
 * Project-root-relative declaration path.
 */
path: string
namespace: SemanticNamespace
/**
 * Stable declaration kind, such as `function` or `class-method`.
 */
declaration_kind: string
/**
 * Name exposed to consumers.
 */
exported_name: string
/**
 * Local declaration name.
 */
local_name: string
/**
 * Optional owning class or namespace.
 */
owner?: (string | null)
/**
 * One-based declaration line.
 */
line: number
/**
 * Zero-based UTF-8 byte column.
 */
col: number
}
/**
 * Located semantic reference evidence.
 */
export interface SemanticReference {
/**
 * Project-root-relative reference path.
 */
path: string
/**
 * One-based source line.
 */
line: number
/**
 * Zero-based UTF-8 byte column.
 */
col: number
/**
 * Reference role, such as `read`, `type`, `alias`, or `re-export`.
 */
role: string
namespace: SemanticNamespace
/**
 * Alias and re-export hops between the reference and declaration.
 */
via?: SemanticAliasHop[]
}
/**
 * One alias or re-export hop in semantic provenance.
 */
export interface SemanticAliasHop {
/**
 * Project-root-relative hop path.
 */
path: string
/**
 * Name before this hop.
 */
from_name: string
/**
 * Name exposed after this hop.
 */
to_name: string
/**
 * Relation, such as `import-alias` or `re-export`.
 */
relation: string
}
/**
 * Exact declaration evidence for an inherited or implemented class-member relation.
 */
export interface SemanticContractEvidence {
relation: SemanticContractRelation
declaration: SemanticSymbol
/**
 * Whether the source contract marks this member optional.
 */
optional: boolean
}
/**
 * Checker-validated evidence that a framework contract preserves a member.
 */
export interface SemanticFrameworkContractEvidence {
/**
 * Fallow plugin that supplied the contract.
 */
framework: string
/**
 * Exact package that owns the heritage declaration.
 */
package: string
relation: SemanticFrameworkRelation
declaration: SemanticSymbol
}
/**
 * Exact source span and content hash used to guard semantic source edits.
 */
export interface SemanticEditGuard {
/**
 * Zero-based UTF-8 byte offset where the declaration starts.
 */
start: number
/**
 * Exclusive zero-based UTF-8 byte offset where the declaration ends.
 */
end: number
/**
 * Lowercase SHA-256 digest of the exact declaration text.
 */
declaration_sha256: string
}
/**
 * Typed semantic trace attached to an existing syntactic trace.
 */
export interface SemanticSymbolTrace {
target: SemanticSymbol
identity: SemanticAnalysisIdentity
/**
 * TypeScript project selected for this symbol.
 */
selected_project: string
/**
 * Concrete assertion, such as `references-found`.
 */
assertion: string
status: SemanticCompleteness
/**
 * Bounded reference evidence.
 */
references: SemanticReference[]
/**
 * Count before evidence bounding.
 */
total_reference_count: number
/**
 * Exact reference locations found by the TypeScript checker.
 */
checker_evidence_count: number
/**
 * Alias and re-export hops derived from the semantic graph.
 */
graph_evidence_count: number
/**
 * Whether reference evidence was truncated.
 */
truncated: boolean
/**
 * Counted omissions.
 */
omissions?: SemanticOmission[]
/**
 * Plain next actions for a user or automation consumer.
 */
actions?: string[]
}
/**
 * Package API surface result shared by inspect and private-leak analysis.
 */
export interface ApiSurfaceResult {
/**
 * Concrete assertion, such as `leak-confirmed`.
 */
assertion: string
status: SemanticCompleteness
/**
 * Public API entries.
 */
entries: ApiSurfaceEntry[]
/**
 * Confirmed private type leaks.
 */
private_type_leaks: SemanticPrivateTypeLeak[]
/**
 * Counted omissions.
 */
omissions?: SemanticOmission[]
/**
 * Plain next actions.
 */
actions?: string[]
}
/**
 * One package-public API entry described by the semantic backend.
 */
export interface ApiSurfaceEntry {
exposed: SemanticSymbol
origin: SemanticSymbol
/**
 * Stable normalized signature fingerprint.
 */
signature_fingerprint: string
/**
 * Project-local types referenced by the signature.
 */
referenced_types: PublicTypeReference[]
}
/**
 * One project-local type referenced by a public signature.
 */
export interface PublicTypeReference {
declaration: SemanticSymbol
/**
 * Signature relation, such as return type or generic constraint.
 */
relation: string
}
/**
 * Exact semantic evidence for a private type leak.
 */
export interface SemanticPrivateTypeLeak {
exposed: SemanticSymbol
private_declaration: SemanticSymbol
/**
 * Signature relation through which the type is exposed.
 */
relation: string
/**
 * Stable TypeScript diagnostic code used as supporting evidence.
 */
diagnostic_code?: (number | null)
}
/**
 * Exact-symbol impact and targeted-test recommendation.
 */
export interface SemanticSymbolImpact {
target: SemanticSymbol
identity: SemanticAnalysisIdentity
/**
 * TypeScript project selected for this symbol.
 */
selected_project: string
/**
 * Concrete assertion, such as `consumers-found`.
 */
assertion: string
status: SemanticCompleteness
/**
 * Files that reference the exact symbol directly.
 */
direct_consumers: SemanticImpactPath[]
/**
 * Direct consumer count before evidence bounding.
 */
total_direct_consumer_count: number
/**
 * Transitively affected production files.
 */
affected_files: SemanticImpactPath[]
/**
 * Transitive affected-file count before evidence bounding.
 */
total_affected_file_count: number
/**
 * Directly relevant test entry points.
 */
targeted_tests: SemanticImpactPath[]
/**
 * Targeted-test count before evidence bounding.
 */
total_targeted_test_count: number
confidence: SemanticImpactConfidence
/**
 * Counted omissions, including dynamic behavior.
 */
omissions?: SemanticOmission[]
/**
 * Plain next actions.
 */
actions?: string[]
}
/**
 * One production file or test reached by exact-symbol impact analysis.
 */
export interface SemanticImpactPath {
/**
 * Project-root-relative affected path.
 */
path: string
/**
 * Relation to the target, such as `direct-value-consumer`.
 */
relation: string
/**
 * Shortest graph distance from the target.
 */
distance: number
/**
 * Located provenance path.
 */
via?: string[]
}
/**
 * Advisory project-local public-signature coupling report.
 */
export interface TypeCouplingReport {
identity: SemanticAnalysisIdentity
/**
 * Concrete assertion, such as `coupling-found`.
 */
assertion: string
status: SemanticCompleteness
/**
 * Project summary. Absent when analysis is unavailable, never a fake zero.
 */
summary?: (TypeCouplingSummary | null)
/**
 * Per-file coupling details.
 */
files: TypeCouplingFile[]
/**
 * Highest-degree files contributing to project coupling.
 */
top_contributors?: TypeCouplingFile[]
/**
 * Located project-local type cycles.
 */
cycles?: TypeCouplingCycle[]
/**
 * Counted omissions.
 */
omissions?: SemanticOmission[]
/**
 * Plain next actions.
 */
actions?: string[]
}
/**
 * Project summary for advisory type coupling.
 */
export interface TypeCouplingSummary {
/**
 * Measurement boundary, currently project-local public signatures.
 */
scope: string
/**
 * Edge direction, currently directed.
 */
direction: string
/**
 * Distinct project files in the selected TypeScript projects.
 */
project_size: number
/**
 * Distinct project files included in the denominator.
 */
files_analyzed: number
/**
 * Files participating in at least one project-local type edge.
 */
distinct_coupled_files: number
/**
 * Project-local public-signature edge count before evidence bounding.
 */
edge_count: number
/**
 * Percentage of analyzed files participating in a type edge.
 */
coupled_file_pct: number
/**
 * Median distinct-file type connections.
 */
p50_distinct_connections: number
/**
 * P90 distinct-file type connections.
 */
p90_distinct_connections: number
/**
 * P95 incoming distinct-file type coupling.
 */
p95_public_types_used_by: number
/**
 * P95 outgoing distinct-file type coupling.
 */
p95_public_api_depends_on: number
/**
 * Percentage of files above the adaptive high-coupling threshold.
 */
high_coupling_pct: number
/**
 * Share of edge endpoints represented by the top contributors.
 */
concentration: number
/**
 * Number of project-local public-signature cycles.
 */
cycle_count: number
}
/**
 * Per-file project-local public-signature coupling.
 */
export interface TypeCouplingFile {
/**
 * Project-root-relative file path.
 */
path: string
/**
 * Distinct files this file's public API depends on.
 */
public_api_depends_on: number
/**
 * Located project files this file's public API depends on.
 */
public_api_depends_on_files?: string[]
/**
 * Distinct files whose public types use this file.
 */
public_types_used_by: number
/**
 * Located project files whose public types use this file.
 */
public_types_used_by_files?: string[]
/**
 * Located public-signature edges.
 */
edges: TypeCouplingEdge[]
}
/**
 * One project-local public-signature type edge.
 */
export interface TypeCouplingEdge {
source: SemanticSymbol
target: SemanticSymbol
/**
 * Signature relation.
 */
relation: string
evidence: SemanticSourceLocation
/**
 * Scope, such as `module-export` or `package-public`.
 */
scope: string
}
/**
 * One project-root-relative source location used as semantic evidence.
 */
export interface SemanticSourceLocation {
/**
 * Project-root-relative source path.
 */
path: string
/**
 * One-based source line.
 */
line: number
/**
 * Zero-based UTF-8 byte column.
 */
col: number
}
/**
 * One project-local cycle through public-signature type dependencies.
 */
export interface TypeCouplingCycle {
/**
 * Ordered project-root-relative files, ending at the start file.
 */
files: string[]
}
/**
 * Closed abstention reason counts for stable machine consumption.
 */
export interface TypeAwareAbstentionCounts {
/**
 * Candidates not contained by a selected TypeScript project.
 */
no_project: number
/**
 * Candidates contained by more than one explicit TypeScript project.
 */
ambiguous_project: number
/**
 * Candidates retained because structural diagnostics block scanning.
 */
blocking_diagnostics: number
/**
 * Candidates retained because the raw TypeScript-Go host cannot expose
 * named exports from Svelte virtual modules.
 */
svelte_virtual_module_exports: number
/**
 * Candidates whose exact declaration identity could not be resolved.
 */
unknown_symbol: number
/**
 * Candidates using declaration syntax unsupported by the semantic backend.
 */
unsupported_syntax: number
/**
 * Candidates retained because the bounded semantic request reached capacity.
 */
capacity: number
}
/**
 * Bounded provenance for one TypeScript project handled by the sidecar.
 */
export interface TypeAwareProjectMeta {
/**
 * Project config relative to the analysis root, or `<inferred>`.
 */
config: string
source: TypeAwareProjectSource
status: TypeAwareProjectStatus
/**
 * Candidates assigned to this project.
 */
candidate_count: number
/**
 * Candidates confirmed as used and removed.
 */
confirmed_used_count: number
/**
 * Candidates retained because they implement or override a contract.
 */
contract_preserved_count: number
/**
 * Candidates with complete no-static-reference evidence.
 */
no_static_references_count: number
/**
 * Candidates eligible for a guarded class-member fix.
 */
fix_eligible_count: number
/**
 * Candidates whose exact semantic outcome remained unresolved.
 */
unresolved_count: number
/**
 * Candidates retained without scanning because the project was unsafe.
 */
abstained_count: number
/**
 * Config, program, syntactic, and bind diagnostics that block scanning.
 */
blocking_diagnostic_count: number
/**
 * Source files loaded into this TypeScript program.
 */
source_file_count: number
/**
 * Whether this Program served more than one semantic query in the batch.
 */
program_reused?: (boolean | null)
/**
 * Whether this Program served more than one query in the current batch.
 */
program_shared_across_queries?: (boolean | null)
/**
 * Whether the root-bound semantic session reused the prior snapshot.
 */
program_reused_from_previous_snapshot?: (boolean | null)
/**
 * Monotonic revision within the root-bound semantic session.
 */
snapshot_revision?: (number | null)
/**
 * Full, incremental, or no invalidation before this query.
 */
invalidation_kind?: (TypeAwareInvalidationKind | null)
/**
 * Stable project-level gap reason.
 */
reason_code?: (SemanticGapReason | null)
abstain_reason?: TypeAwareAbstentionReason
}
/**
 * Semantic sidecar timings, separated from Fallow's syntactic pipeline.
 */
export interface TypeAwarePhaseTimings {
/**
 * TypeScript API construction and project snapshot selection.
 */
project_setup: number
/**
 * TypeScript diagnostics collected before any candidate refinement.
 */
diagnostics: number
/**
 * Batched symbol lookup and exact declaration matching.
 */
symbol_scan: number
}
/**
 * Single-metric definition inside [`Meta::metrics`].
 */
export interface MetaMetric {
/**
 * Human-readable metric name.
 */
name?: (string | null)
/**
 * What this metric measures and how it is computed.
 */
description?: (string | null)
/**
 * Valid value range (e.g., `"[0, 100]"`).
 */
range?: (string | null)
/**
 * How to read the value (e.g., `"lower is better"`).
 */
interpretation?: (string | null)
}
/**
 * Single-rule definition inside [`Meta::rules`].
 */
export interface MetaRule {
/**
 * Human-readable rule name.
 */
name?: (string | null)
/**
 * What this rule detects.
 */
description?: (string | null)
/**
 * URL to the rule documentation.
 */
docs?: (string | null)
}
/**
 * Envelope emitted by `fallow dead-code --format json` (plus the `check`
 * block inside the combined and audit envelopes).
 *
 * The body is the full `AnalysisResults` flattened into the envelope so
 * every issue array (`unused_files`, `unused_exports`, ...) lives at the
 * top level, matching the existing wire shape. `entry_points` lifts the
 * otherwise `#[serde(skip)]`'d `AnalysisResults::entry_point_summary` back
 * into the JSON output. `summary` carries the per-category counts the
 * JSON layer always emits.
 */
export interface CheckOutput {
schema_version: CheckSchemaVersion
version: ToolVersion
elapsed_ms: ElapsedMs
/**
 * Total findings across all issue arrays; excludes `next_steps`.
 */
total_issues: number
/**
 * Entry-point totals per source, when the analysis recorded them.
 */
entry_points?: (EntryPoints | null)
summary: CheckSummary
/**
 * Files not reachable from any entry point. Wrapped in
 * [`UnusedFileFinding`] so each entry carries a typed `actions` array
 * natively, replacing the pre-2.76 post-pass injection.
 */
unused_files: UnusedFileFinding[]
/**
 * Exports never imported by other modules. Wrapped in
 * [`UnusedExportFinding`] so each entry carries a typed `actions`
 * array natively.
 */
unused_exports: UnusedExportFinding[]
/**
 * Type exports never imported by other modules. Wrapped in
 * [`UnusedTypeFinding`]: the inner [`UnusedExport`] struct is shared
 * with `unused_exports` but the wrapper emits a type-targeted fix
 * description.
 */
unused_types: UnusedTypeFinding[]
/**
 * Exported symbols whose public signature references same-file private
 * types. Wrapped in [`PrivateTypeLeakFinding`] so each entry carries a
 * typed `actions` array natively.
 */
private_type_leaks: PrivateTypeLeakFinding[]
/**
 * Exports marked `@deprecated` that still have at least one consumer in
 * a reachable file. Wrapped in [`DeprecatedExportInUseFinding`]. Opt-in: the
 * `deprecated-exports-in-use` rule defaults to `off`.
 */
deprecated_exports_in_use?: DeprecatedExportInUseFinding[]
/**
 * Dependencies listed in package.json but never imported. Wrapped in
 * [`UnusedDependencyFinding`] so each entry carries a typed `actions`
 * array natively. The fix action swaps from `remove-dependency` to
 * `move-dependency` when `used_in_workspaces` is non-empty.
 */
unused_dependencies: UnusedDependencyFinding[]
/**
 * Dev dependencies listed in package.json but never imported. Wrapped
 * in [`UnusedDevDependencyFinding`]: same bare struct as
 * `unused_dependencies` with a `devDependencies`-targeted fix
 * description.
 */
unused_dev_dependencies: UnusedDevDependencyFinding[]
/**
 * Optional dependencies listed in package.json but never imported.
 * Wrapped in [`UnusedOptionalDependencyFinding`] with an
 * `optionalDependencies`-targeted fix description.
 */
unused_optional_dependencies: UnusedOptionalDependencyFinding[]
/**
 * Enum members never accessed. Wrapped in
 * [`UnusedEnumMemberFinding`] so each entry carries a typed `actions`
 * array natively.
 */
unused_enum_members: UnusedEnumMemberFinding[]
/**
 * Class members never accessed. Wrapped in
 * [`UnusedClassMemberFinding`]: same inner [`UnusedMember`] struct as
 * `unused_enum_members`, with a class-targeted fix description and the
 * `auto_fixable: false` default to reflect dependency-injection
 * patterns.
 */
unused_class_members: UnusedClassMemberFinding[]
/**
 * Store members (Pinia `state` / `getters` / `actions` key, or a
 * setup-store returned key) declared but never accessed by any consumer
 * project-wide. Wrapped in [`UnusedStoreMemberFinding`]: same inner
 * [`UnusedMember`] struct as `unused_class_members`, with a
 * store-targeted fix description. Cross-graph: the store binding is
 * imported (the module is reachable) yet a specific member is dead.
 */
unused_store_members?: UnusedStoreMemberFinding[]
/**
 * Import specifiers that could not be resolved. Wrapped in
 * [`UnresolvedImportFinding`] so each entry carries a typed `actions`
 * array natively.
 */
unresolved_imports: UnresolvedImportFinding[]
/**
 * Dependencies used in code but not listed in package.json. Wrapped in
 * [`UnlistedDependencyFinding`].
 */
unlisted_dependencies: UnlistedDependencyFinding[]
/**
 * Exports with the same name across multiple modules. Wrapped in
 * [`DuplicateExportFinding`] so each entry carries a typed `actions`
 * array natively, with the position-0 `add-to-config` `ignoreExports`
 * snippet wired in at wrapper construction.
 */
duplicate_exports: DuplicateExportFinding[]
/**
 * Production dependencies only used via type-only imports (could be
 * devDependencies). Only populated in production mode. Wrapped in
 * [`TypeOnlyDependencyFinding`].
 */
type_only_dependencies: TypeOnlyDependencyFinding[]
/**
 * Production dependencies only imported by test files (could be
 * devDependencies). Wrapped in [`TestOnlyDependencyFinding`].
 */
test_only_dependencies?: TestOnlyDependencyFinding[]
/**
 * devDependencies imported by production (non-test, non-config) source code
 * via a runtime/value import; they should be promoted to dependencies.
 * The promote-side mirror of [`TestOnlyDependencyFinding`]. Wrapped in
 * [`DevDependencyInProductionFinding`].
 */
dev_dependencies_in_production?: DevDependencyInProductionFinding[]
/**
 * Circular dependency chains detected in the module graph. Wrapped in
 * [`CircularDependencyFinding`] so each entry carries a typed `actions`
 * array natively.
 */
circular_dependencies: CircularDependencyFinding[]
/**
 * Cycles or self-loops in the re-export edge subgraph (barrel files
 * re-exporting from each other in a loop). Wrapped in
 * [`ReExportCycleFinding`] so each entry carries a typed `actions`
 * array natively (a `refactor-re-export-cycle` informational primary
 * plus a `suppress-file` secondary; cycles are file-scoped so a single
 * suppression breaks the cycle).
 */
re_export_cycles?: ReExportCycleFinding[]
/**
 * Dependency cycles between workspace packages, built from resolved
 * cross-package imports. Wrapped in [`PackageCycleFinding`] so each
 * entry carries a typed `actions` array natively.
 */
package_cycles?: PackageCycleFinding[]
/**
 * Imports that cross architecture boundary rules. Wrapped in
 * [`BoundaryViolationFinding`] so each entry carries a typed `actions`
 * array natively.
 */
boundary_violations?: BoundaryViolationFinding[]
/**
 * Files that matched no architecture boundary zone while
 * `boundaries.coverage.requireAllFiles` was enabled.
 */
boundary_coverage_violations?: BoundaryCoverageViolationFinding[]
/**
 * Calls from zoned files to callees forbidden for that zone via
 * `boundaries.calls.forbidden`. Wrapped in
 * [`BoundaryCallViolationFinding`] so each entry carries a typed
 * `actions` array natively.
 */
boundary_call_violations?: BoundaryCallViolationFinding[]
/**
 * Banned calls, imports, and catalogue-derived effects matched by
 * declarative rule packs
 * (`rulePacks` config). Wrapped in [`PolicyViolationFinding`] so each
 * entry carries a typed `actions` array natively. Each finding carries
 * its effective per-rule severity.
 */
policy_violations?: PolicyViolationFinding[]
/**
 * Suppression comments or JSDoc tags that no longer match any issue.
 */
stale_suppressions?: StaleSuppression[]
/**
 * Entries in package manager catalog sections not referenced by any
 * workspace package via the catalog: protocol. Supports
 * `pnpm-workspace.yaml` catalogs and Bun root `package.json` catalogs.
 * Wrapped in [`UnusedCatalogEntryFinding`] so each entry carries a typed
 * `actions` array natively, with per-instance `auto_fixable` derived
 * from `hardcoded_consumers` and the catalog source file.
 */
unused_catalog_entries?: UnusedCatalogEntryFinding[]
/**
 * Named groups under package manager catalogs sections that declare no
 * package entries. The top-level catalog: map is not reported. Wrapped in
 * [`EmptyCatalogGroupFinding`].
 */
empty_catalog_groups?: EmptyCatalogGroupFinding[]
/**
 * Workspace package.json references to catalogs (`catalog:` or
 * `catalog:<name>`) that do not declare the consumed package. The package
 * manager install will error until the named catalog grows to include the
 * package or the reference is switched / removed. Wrapped in
 * [`UnresolvedCatalogReferenceFinding`] with the discriminated
 * `add-catalog-entry` / `update-catalog-reference` primary at position 0.
 */
unresolved_catalog_references?: UnresolvedCatalogReferenceFinding[]
/**
 * Entries in pnpm-workspace.yaml's overrides section, package.json's
 * pnpm.overrides block, npm or Bun's top-level overrides object, or Bun's
 * top-level resolutions object,
 * whose target package is not declared by any workspace package and is
 * not present in pnpm-lock.yaml, package-lock.json, npm-shrinkwrap.json,
 * or bun.lock. Default severity is warn because projects without a
 * readable lockfile fall back to manifest-only checks; the hint field
 * flags those conservative cases. When the only lockfile is bun's binary
 * bun.lockb, resolution cannot be read and the check emits nothing.
 * Wrapped in [`UnusedDependencyOverrideFinding`].
 */
unused_dependency_overrides?: UnusedDependencyOverrideFinding[]
/**
 * Package-manager override or resolution entries whose key or value does
 * not parse in the declaration source's grammar (empty key, empty value,
 * malformed selector, unbalanced parent matcher). The package manager may
 * reject or ignore these at install time. Default severity is error. Wrapped in
 * [`MisconfiguredDependencyOverrideFinding`].
 */
misconfigured_dependency_overrides?: MisconfiguredDependencyOverrideFinding[]
/**
 * `"use client"` files that export a Next.js server-only / route-segment
 * config name (e.g. `metadata`, `revalidate`, `GET`). Next.js rejects this
 * at build time. Wrapped in [`InvalidClientExportFinding`] so each entry
 * carries a typed `actions` array natively. Default severity is `warn`.
 */
invalid_client_exports?: InvalidClientExportFinding[]
/**
 * Barrel files that re-export BOTH a `"use client"` origin module AND a
 * server-only origin module (the Next.js App Router footgun). Wrapped in
 * [`MixedClientServerBarrelFinding`] so each entry carries a typed
 * `actions` array natively. Default severity is `warn`.
 */
mixed_client_server_barrels?: MixedClientServerBarrelFinding[]
/**
 * `"use client"` / `"use server"` directives written as expression
 * statements after a non-directive statement, so the RSC bundler parses
 * them as ordinary strings and silently ignores them. Wrapped in
 * [`MisplacedDirectiveFinding`] so each entry carries a typed `actions`
 * array natively. Default severity is `warn`.
 */
misplaced_directives?: MisplacedDirectiveFinding[]
/**
 * Vue `inject(KEY)` / Svelte `getContext(KEY)` calls whose symbol KEY is
 * provided nowhere in the project (the injected-never-provided dead-half).
 * Wrapped in [`UnprovidedInjectFinding`] so each entry carries a typed
 * `actions` array natively. Default severity is `warn`.
 */
unprovided_injects?: UnprovidedInjectFinding[]
/**
 * Vue/Svelte single-file components that are reachable but rendered nowhere
 * (the imported-but-never-rendered dead-half). Wrapped in
 * [`UnrenderedComponentFinding`] so each entry carries a typed `actions`
 * array natively. Default severity is `warn`.
 */
unrendered_components?: UnrenderedComponentFinding[]
/**
 * Next.js App Router route files that resolve to the same URL within one
 * app-root (a guaranteed `next build` failure). Wrapped in
 * [`RouteCollisionFinding`] so each entry carries a typed `actions` array
 * natively. One finding per colliding file. Default severity is `warn`.
 */
route_collisions?: RouteCollisionFinding[]
/**
 * Sibling Next.js dynamic route segments at one tree position using
 * different param spellings (a dev / runtime error; `next build` does NOT
 * catch it). Wrapped in [`DynamicSegmentNameConflictFinding`] so each entry
 * carries a typed `actions` array natively. Default severity is `warn`.
 */
dynamic_segment_name_conflicts?: DynamicSegmentNameConflictFinding[]
/**
 * Vue `<script setup>` `defineProps`, Svelte 5 `$props()`, and React props
 * referenced nowhere in their own component. Wrapped in
 * [`UnusedComponentPropFinding`] so each entry carries a typed `actions`
 * array natively. Default severity is `warn`.
 */
unused_component_props?: UnusedComponentPropFinding[]
/**
 * Used optional component inputs absent from inspected reachable callers. Off by default.
 */
absent_component_props?: AbsentComponentPropFinding[]
/**
 * Vue `<script setup>` `defineEmits` events emitted nowhere in their own SFC
 * (no `emit('<name>')` call). Wrapped in [`UnusedComponentEmitFinding`] so
 * each entry carries a typed `actions` array natively. Default severity is
 * `warn`.
 */
unused_component_emits?: UnusedComponentEmitFinding[]
/**
 * Angular `@Input()` / signal `input()` / `model()` inputs read nowhere in
 * their own component (neither the template nor the class body). Wrapped in
 * [`UnusedComponentInputFinding`] so each entry carries a typed `actions`
 * array natively. Default severity is `warn`.
 */
unused_component_inputs?: UnusedComponentInputFinding[]
/**
 * Angular `@Output()` / signal `output()` outputs emitted nowhere in their
 * own component (no `this.<output>.emit(...)`). Wrapped in
 * [`UnusedComponentOutputFinding`] so each entry carries a typed `actions`
 * array natively. Default severity is `warn`.
 */
unused_component_outputs?: UnusedComponentOutputFinding[]
/**
 * Svelte components dispatching a custom event via `createEventDispatcher()`
 * whose event name is listened to nowhere project-wide (cross-file
 * dead-output direction). Wrapped in [`UnusedSvelteEventFinding`] so each
 * entry carries a typed `actions` array natively. Default severity is
 * `warn`.
 */
unused_svelte_events?: UnusedSvelteEventFinding[]
/**
 * Next.js Server Actions (exports of `"use server"` files) that no code in
 * the project references. Reclassified out of `unused_exports` for
 * `"use server"` files. Wrapped in [`UnusedServerActionFinding`] so each
 * entry carries a typed `actions` array natively. Default severity is
 * `warn`.
 */
unused_server_actions?: UnusedServerActionFinding[]
/**
 * SvelteKit `+page.{ts,server.ts,js,server.js}` `load()` return-object keys
 * read by no consumer. Wrapped in [`UnusedLoadDataKeyFinding`] so each entry
 * carries a typed `actions` array natively. Default severity is `warn`.
 */
unused_load_data_keys?: UnusedLoadDataKeyFinding[]
/**
 * `true` when the `unused-load-data-key` detector abstained project-wide
 * because a whole-object use of `page.data` / `$page.data` was seen
 * somewhere (S1 observability: an empty `unused_load_data_keys` with this
 * flag set is NOT a clean bill, it means the rule could not run safely).
 * Serialized only when `true` so the default JSON contract is unchanged.
 */
unused_load_data_keys_global_abstain?: boolean
/**
 * React/Preact props forwarded unchanged through `>= N` intermediate
 * pass-through components until a consumer (located per-chain records).
 * Wrapped in [`PropDrillingChainFinding`] so each entry carries a typed
 * `actions` array natively. Health signal: the rule defaults to `off`
 * (opt-in), so this is dormant and populated ONLY when the user enables it.
 */
prop_drilling_chains?: PropDrillingChainFinding[]
/**
 * React/Preact components whose entire body is a single spread-forwarded
 * child render (`return <Child {...props}/>`): pure structural indirection,
 * a candidate for inlining at call sites. Wrapped in [`ThinWrapperFinding`]
 * so each entry carries a typed `actions` array natively. Health signal: the
 * rule defaults to `off` (opt-in), so this is dormant and populated ONLY
 * when the user enables it.
 */
thin_wrappers?: ThinWrapperFinding[]
/**
 * React/Preact components that participate in a duplicate-prop-shape group:
 * three or more components across two or more files whose statically-known
 * prop NAME set is identical after stripping ubiquitous DOM / passthrough
 * names (a missing shared `Props` type / base component). Wrapped in
 * [`DuplicatePropShapeFinding`] so each entry carries a typed `actions`
 * array and its sibling roster natively. Health signal: the rule defaults to
 * `off` (opt-in), so this is dormant and populated ONLY when the user
 * enables it.
 */
duplicate_prop_shapes?: DuplicatePropShapeFinding[]
/**
 * Count deltas against the matched baseline, in baseline runs.
 */
baseline_deltas?: (BaselineDeltas | null)
/**
 * Which baseline snapshot was matched, in baseline runs.
 */
baseline?: (BaselineMatch | null)
/**
 * This run's view of the loaded baseline, present only in baseline runs.
 * Carries the staleness counts, the advisory verdict and `gate_trips`, the
 * same boolean `--fail-on-stale-baseline` exits on, so a CI integration
 * reads one field instead of restating the rule. Read `change_scoped`
 * before dividing `matched_entries` by `baseline_entries`: a narrowed run
 * can report `matched_entries: 0` on a healthy baseline.
 */
baseline_staleness?: (BaselineStaleness | null)
/**
 * The answer to `--finding-id`, present only when the run received one
 * or more `--finding-id` values. The report then holds only the
 * requested findings. Read `missing` as resolved only when `conclusive`
 * is true; a scope, a baseline or a filter can hide a finding that still
 * exists. See [`crate::FindingIdQuery`].
 */
finding_id_query?: (FindingIdQuery | null)
/**
 * Regression verdict against the baseline, in `--fail-on-regression` runs.
 */
regression?: (RegressionResult | null)
/**
 * The verdict of every gate this run evaluated, keyed by name. The CLI
 * always emits it, with the command's default exit rule in it also when
 * no flag armed a gate, so a CI integration reads the verdict instead of
 * guessing from a process status it usually cannot see. A gate fails the
 * build when `status` is `fail` AND `enforced` is true. The typed
 * programmatic API runs no CLI gate and leaves it absent. See
 * [`crate::GateOutcomes`].
 */
gate_outcomes?: (GateOutcomes | null)
/**
 * Every narrowing or shaping request this run RECEIVED, keyed by name,
 * absent when it was asked for nothing. An entry whose `status` is not
 * `applied` means the run could not do what it was asked and reported
 * something WIDER instead, so what follows is a valid report of a scope
 * nobody requested. Honoured requests are published too, with
 * `status: "applied"`, so an absent object means "nothing was asked for",
 * never "nothing failed". See [`crate::RequestOutcomes`].
 */
request_outcomes?: (RequestOutcomes | null)
/**
 * Applied Git refs for exact workspace packages. Absent when no package
 * baselines were selected, including runs with a global changed-since ref.
 */
package_baselines?: PackageBaselineStatus[]
/**
 * `_meta` block with docs and rule definitions, when `--explain` was
 * passed.
 */
_meta?: (Meta | null)
/**
 * Non-fatal diagnostics about the project itself, from all three stages
 * that record them (issue #473):
 *
 * - workspace discovery, at config load: `undeclared-workspace`,
 *   `malformed-package-json`, `glob-matched-no-package-json`,
 *   `malformed-tsconfig`, `tsconfig-reference-dir-missing`;
 * - source discovery, during the file walk: `skipped-large-file`,
 *   `skipped-minified-file`, `skipped-source-dotdir`,
 *   `excluded-by-default-ignore`, `source-read-failure`,
 *   `source-parse-degraded`;
 * - dead-code analysis, from the dependency-catalog and override
 *   detectors: `malformed-pnpm-workspace-yaml`,
 *   `bun-lockb-override-resolution-skipped`;
 * - framework plugins, while they read their own build configs:
 *   `plugin-config-unreadable`, `plugin-effect-not-modeled`;
 * - the dead-code result, for config patterns that matched nothing:
 *   `ignore-dependencies-glob-unmatched`,
 *   `ignore-findings-pattern-unmatched`.
 *
 * Analysis-stage and plugin-stage kinds therefore reach only the envelopes
 * whose run includes a dead-code analyze pass, never a standalone
 * `fallow dupes --format json`. `path` is project-root-relative with
 * forward slashes; the array is omitted when empty. The same list is
 * repeated on each top-level command's envelope so single-command
 * consumers see it without having to look at a separate top-level field.
 *
 * A diagnostic here is advisory and never withholds a finding. Where an
 * entry reports a source file this run never fully analyzed
 * (`source-parse-degraded`, `source-read-failure`, `skipped-large-file`,
 * `skipped-minified-file`, `skipped-source-dotdir`) it can distort a
 * verdict, so the affected `unused_files[]`, `unused_exports[]`, and
 * dependency entries additionally carry the caveat themselves in their own
 * optional `reachability_caveats[]` array, and a reader who never scrolls
 * back up to this list still sees it. `fallow fix` reads the same array
 * and withholds the removal while a caveat stands.
 *
 * `excluded-by-default-ignore` is the one source-discovery kind that
 * reports unseen files WITHOUT raising a caveat. It names a built-in
 * ignore pattern (`** /dist/**`, `** /build/**`, `** /coverage/**`, or one
 * of the four minified-bundle globs) that removed candidate source files
 * from the walk, which is designed behavior on generated output rather
 * than a degraded run, so it is advisory only and no finding inherits it.
 * One entry per pattern, never per file, so the array stays bounded on a
 * project of any size. Gitignored trees are pruned before the walk sees
 * them and count zero, and `** /node_modules/**` is never reported:
 * installed dependencies are not the first-party source the kind is
 * about.
 */
workspace_diagnostics?: WorkspaceDiagnostic[]
/**
 * Read-only follow-up commands computed from this run's findings, emitted
 * at the JSON root so an agent acting on the output is pointed at fallow's
 * adjacent verification capabilities (trace, complexity breakdown, audit,
 * workspace scoping). Each command is runnable as-is and never mutating;
 * see [`NextStep`] for both contracts. Omitted when empty or when
 * `FALLOW_SUGGESTIONS=off`; does NOT contribute to `total_issues`.
 */
next_steps?: NextStep[]
}
/**
 * Entry-point detection summary embedded in `CheckOutput` and the combined
 * envelope.
 */
export interface EntryPoints {
/**
 * Total number of detected entry points.
 */
total: number
/**
 * Breakdown of entry points by detection source (e.g., `"package.json"`,
 * `"next.js"`, `"config entry"`). Underscored keys so dashboards can
 * drill into individual sources.
 */
sources: {
[k: string]: number
}
}
/**
 * Per-category issue counts for dead-code analysis. Always present in
 * `CheckOutput`; when `--summary` is used the individual issue arrays are
 * omitted but this object stays populated.
 */
export interface CheckSummary {
/**
 * Total number of issues across all categories.
 */
total_issues: number
/**
 * Unused source files.
 */
unused_files: number
/**
 * Unused value exports.
 */
unused_exports: number
/**
 * Unused type exports.
 */
unused_types: number
/**
 * Public exports whose signature references same-file private types.
 */
private_type_leaks: number
/**
 * Exports marked `@deprecated` that are still referenced.
 */
deprecated_exports_in_use: number
/**
 * Combined count of unused entries across `dependencies`,
 * `devDependencies`, and `optionalDependencies`. The per-section
 * breakdown lives in the individual issue arrays on `CheckOutput`.
 */
unused_dependencies: number
/**
 * Unused enum members.
 */
unused_enum_members: number
/**
 * Unused class members.
 */
unused_class_members: number
/**
 * Unused store members.
 */
unused_store_members?: number
/**
 * Vue/Svelte injects whose key is provided nowhere in the project.
 */
unprovided_injects?: number
/**
 * Vue/Svelte components reachable but rendered nowhere in the project.
 */
unrendered_components?: number
/**
 * Vue, Svelte, or React props referenced nowhere inside their own component.
 */
unused_component_props?: number
/**
 * Optional consumed props omitted by known reachable callers, for manual review.
 */
absent_component_props?: number
/**
 * Vue `<script setup>` emits emitted nowhere inside their own SFC.
 */
unused_component_emits?: number
/**
 * Angular `@Input()` bindings referenced nowhere inside their own component.
 */
unused_component_inputs?: number
/**
 * Angular `@Output()` bindings emitted nowhere inside their own component.
 */
unused_component_outputs?: number
/**
 * Svelte components dispatching a custom event via `createEventDispatcher`
 * whose name is listened to nowhere in the project.
 */
unused_svelte_events?: number
/**
 * Next.js Server Actions (exports of `"use server"` files) referenced by no
 * code in the project.
 */
unused_server_actions?: number
/**
 * SvelteKit `load()` return-object keys read by no consumer.
 */
unused_load_data_keys?: number
/**
 * Imports that could not be resolved against the project's module graph.
 */
unresolved_imports: number
/**
 * Dependencies imported but absent from `package.json`.
 */
unlisted_dependencies: number
/**
 * Same-named exports declared in more than one module.
 */
duplicate_exports: number
/**
 * Production dependencies only used via type-only imports (could be
 * devDependencies). Only populated in production mode.
 */
type_only_dependencies: number
/**
 * Production dependencies only imported by test files (could be
 * devDependencies).
 */
test_only_dependencies: number
/**
 * devDependencies imported by production source code with a runtime/value
 * import (should be promoted to dependencies).
 */
dev_dependencies_in_production: number
/**
 * Cycles detected in the import graph.
 */
circular_dependencies: number
/**
 * Cycles or self-loops in the re-export edge subgraph (barrel files
 * re-exporting from each other in a loop).
 */
re_export_cycles?: number
/**
 * Dependency cycles between workspace packages.
 */
package_cycles?: number
/**
 * Imports that cross architecture boundary rules.
 */
boundary_violations: number
/**
 * Files that match no architecture boundary zone.
 */
boundary_coverage_violations?: number
/**
 * Calls from zoned files to callees forbidden for that zone.
 */
boundary_call_violations?: number
/**
 * Banned calls, imports, and catalogue-derived effects matched by
 * declarative rule packs.
 */
policy_violations?: number
/**
 * Suppression comments that no longer match a finding.
 */
stale_suppressions: number
/**
 * Unused pnpm-workspace catalog entries.
 */
unused_catalog_entries: number
/**
 * Empty named catalog groups.
 */
empty_catalog_groups: number
/**
 * Workspace package.json catalog references the workspace catalogs
 * do not declare.
 */
unresolved_catalog_references: number
/**
 * Package-manager overrides whose target package is not declared by any
 * workspace package and not present in the active readable lockfile.
 */
unused_dependency_overrides: number
/**
 * Package-manager overrides whose key or value cannot be parsed.
 */
misconfigured_dependency_overrides: number
/**
 * `"use client"` files that export a Next.js server-only / route-config name.
 */
invalid_client_exports?: number
/**
 * Barrel files that re-export both a `"use client"` origin and a
 * server-only origin.
 */
mixed_client_server_barrels?: number
/**
 * Misplaced `"use client"` / `"use server"` directives written as
 * expression statements after a non-directive statement.
 */
misplaced_directives?: number
/**
 * Next.js App Router route files that resolve to the same URL within one
 * app-root.
 */
route_collisions?: number
/**
 * Sibling Next.js dynamic route segments at one position using different
 * param spellings.
 */
dynamic_segment_name_conflicts?: number
}
/**
 * Wire-shape envelope for an [`UnusedFile`] finding. The bare finding
 * flattens in via `#[serde(flatten)]`, with a typed `actions` array
 * populated at construction time and the audit-pass `introduced` flag
 * attached as an optional sibling.
 */
export interface UnusedFileFinding {
/**
 * Absolute path to the unused file.
 */
path: string
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps: a `delete-file` primary and a `suppress-file`
 * secondary. Always emitted (possibly empty for forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base. `None` when serialized directly from Rust.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
/**
 * Advisory caveats on the reachability verdict behind this finding.
 * Sorted, deduplicated, and omitted from the wire when empty, so a run
 * that analyzed every discovered file is byte-identical. Never gates the
 * finding or the `delete-file` action, though `fallow fix` does withhold
 * the removal of a caveated finding as low confidence.
 */
reachability_caveats?: ReachabilityCaveat[]
}
/**
 * A code-change fix. `type` is one of the kebab-case identifiers in
 * [`FixActionType`].
 */
export interface FixAction {
type: FixActionType
/**
 * Whether `fallow fix` can apply this fix automatically. Evaluated PER
 * FINDING, not per action type: the same `type` may carry
 * `auto_fixable: true` on one finding and `auto_fixable: false` on
 * another when per-instance guards in the applier discriminate (e.g.
 * `remove-catalog-entry` flips on `hardcoded_consumers` and catalog
 * source file, the primary dependency action flips between
 * `remove-dependency` / `move-dependency` on `used_in_workspaces`).
 * Filter on this bool of each individual action, not on `type`. See the
 * [`IssueAction`] enum-level docs for the full list of per-instance
 * flips.
 *
 * One flip is RUN-level rather than finding-level: a dead-code finding
 * carrying `reachability_caveats` reports `false` here, because a file
 * this run never fully read may hold the reference that credits it. Every
 * mutation surface honours the same gate, so a plan built from this flag
 * never expects a write `fallow fix`, the MCP fix tools, or the LSP quick
 * fix will refuse. The action stays in the array at the same position and
 * names the reason in [`Self::note`].
 */
auto_fixable: boolean
/**
 * Human-readable description of the fix.
 */
description: string
/**
 * Optional context note. Present on non-auto-fixable actions, and on
 * auto-fixable re-export findings to warn about public API surface.
 */
note?: (string | null)
/**
 * Only present on `update-catalog-reference` actions: catalogs in the
 * same workspace that DO declare the package, sorted lexicographically.
 * Lets agents pick the catalog to switch to without re-reading the
 * source.
 */
available_in_catalogs?: (string[] | null)
/**
 * Only present on `update-catalog-reference` actions when exactly one
 * alternative catalog declares the package: the unambiguous switch
 * target. Lets deterministic (non-LLM) agents land the edit without
 * picking from a list. Absent when `available_in_catalogs` has zero
 * or more than one entry.
 */
suggested_target?: (string | null)
}
/**
 * Inline-comment suppression for a single finding line.
 */
export interface SuppressLineAction {
type: SuppressLineKind
/**
 * Always false for suppress actions.
 */
auto_fixable: boolean
/**
 * Human-readable description of the suppression.
 */
description: string
/**
 * The inline comment to place above the line (e.g.,
 * `// fallow-ignore-next-line unused-export`). When multiple
 * suppressible findings share the same path and line, this may contain a
 * comma-separated issue-kind list such as
 * `// fallow-ignore-next-line unused-export, complexity`.
 */
comment: string
/**
 * Present on multi-location issue types (e.g., `duplicate_exports`) to
 * indicate the comment must be applied at each location.
 */
scope?: (SuppressLineScope | null)
}
/**
 * File-wide suppression placed at the top of the source file.
 */
export interface SuppressFileAction {
type: SuppressFileKind
/**
 * Always false for suppress actions.
 */
auto_fixable: boolean
/**
 * Human-readable description of the suppression.
 */
description: string
/**
 * The file-level comment to place at the top of the file (e.g.,
 * `// fallow-ignore-file unused-file`).
 */
comment: string
}
/**
 * Edit a fallow config file (`.fallowrc.json`, `fallow.toml`, etc.) to
 * add the offending value to an `ignore*` rule.
 */
export interface AddToConfigAction {
type: AddToConfigKind
/**
 * True when `fallow fix` can apply this config action automatically.
 * Evaluated PER FINDING, not per action type: `ignoreExports`
 * duplicate-export actions are auto-fixable when `fallow fix` can
 * safely write the rule, which today means EITHER a fallow config
 * file already exists OR no config exists and the working directory
 * is NOT inside a monorepo subpackage (in which case the applier
 * creates `.fallowrc.json` from `fallow init`'s framework-aware
 * scaffolding). The action is `false` inside a monorepo subpackage
 * with no workspace-root config because the applier refuses to
 * fragment per-package configs across the monorepo. Older scalar
 * config-ignore actions (e.g. `ignoreDependencies` on dependency
 * findings) are always manual today. Filter on this bool of each
 * individual action, not on the `type` alone. See the [`IssueAction`]
 * enum-level docs for the full list of per-instance flips.
 */
auto_fixable: boolean
/**
 * Human-readable description of the config change.
 */
description: string
/**
 * The fallow config key to add the value to (e.g.,
 * `ignoreDependencies`).
 */
config_key: string
value: AddToConfigValue
/**
 * Optional URL pointing at a stable JSON Schema fragment that describes
 * the shape of `value`. Agents that intend to validate `value` before
 * writing it into a user's config can fetch the linked schema and run
 * it against `value`. The URL is a JSON Pointer fragment into fallow's
 * main config schema (e.g.
 * `schema.json#/properties/ignoreExports` for the ignoreExports
 * action, or `schema.json#/properties/ignoreDependencies/items` for
 * the per-package ignoreDependencies action). Strictly additive:
 * consumers that ignore the field keep working unchanged.
 */
value_schema?: (string | null)
}
/**
 * Single `ignoreExports` rule entry. The fallow config accepts an array of
 * these under the `ignoreExports` key.
 */
export interface IgnoreExportsRule {
/**
 * File path (forward slashes, relative to project root) to which this
 * rule applies. Globs are accepted.
 */
file: string
/**
 * Names of exports inside `file` to silently treat as used.
 */
exports: string[]
}
/**
 * Wire-shape envelope for an [`UnusedExport`] finding consumed under the
 * `unused_exports` key. Same Rust struct as [`UnusedTypeFinding`], with a
 * different fix description so consumers can tell value-export from
 * type-export removal at the action level.
 */
export interface UnusedExportFinding {
/**
 * File containing the unused export.
 */
path: string
/**
 * Name of the unused export.
 */
export_name: string
/**
 * Whether this is a type-only export.
 */
is_type_only: boolean
/**
 * 1-based line number of the export.
 */
line: number
/**
 * 0-based byte column offset.
 */
col: number
/**
 * Byte offset into the source file (used by the fix command).
 */
span_start: number
/**
 * Whether this finding comes from a barrel/index re-export rather than the source definition.
 */
is_re_export: boolean
/**
 * Whether the export's leading JSDoc carries `@deprecated`. Absent from
 * the wire when false.
 */
deprecated?: boolean
/**
 * Plain-text message of the `@deprecated` tag, capped at
 * [`DEPRECATED_REASON_MAX_CHARS`] characters. Absent when the export is
 * not deprecated or the tag carries no text.
 */
deprecated_reason?: (string | null)
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Type-aware evidence for this exact candidate when requested.
 */
semantic?: (SemanticCandidateDecision | null)
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
/**
 * Advisory caveats on the reachability verdict behind this finding.
 * Sorted, deduplicated, and omitted from the wire when empty. Never gates
 * the finding or the `remove-export` action, though `fallow fix` does
 * withhold the removal of a caveated export as low confidence.
 */
reachability_caveats?: ReachabilityCaveat[]
}
/**
 * Wire-shape envelope for an [`UnusedExport`] finding consumed under the
 * `unused_types` key. Wraps the same bare [`UnusedExport`] struct as
 * [`UnusedExportFinding`] but emits a fix action targeted at type-only
 * declarations, with the same `is_re_export`-aware note swap.
 */
export interface UnusedTypeFinding {
/**
 * File containing the unused export.
 */
path: string
/**
 * Name of the unused export.
 */
export_name: string
/**
 * Whether this is a type-only export.
 */
is_type_only: boolean
/**
 * 1-based line number of the export.
 */
line: number
/**
 * 0-based byte column offset.
 */
col: number
/**
 * Byte offset into the source file (used by the fix command).
 */
span_start: number
/**
 * Whether this finding comes from a barrel/index re-export rather than the source definition.
 */
is_re_export: boolean
/**
 * Whether the export's leading JSDoc carries `@deprecated`. Absent from
 * the wire when false.
 */
deprecated?: boolean
/**
 * Plain-text message of the `@deprecated` tag, capped at
 * [`DEPRECATED_REASON_MAX_CHARS`] characters. Absent when the export is
 * not deprecated or the tag carries no text.
 */
deprecated_reason?: (string | null)
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Type-aware evidence for this exact candidate when requested.
 */
semantic?: (SemanticCandidateDecision | null)
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
/**
 * Advisory caveats on the reachability verdict behind this finding.
 * A type export rests on exactly the reachability test an
 * `unused_exports[]` entry does, and the LSP offers the same
 * remove-the-`export`-keyword quick fix for both, so the two must render
 * with the same confidence. Sorted, deduplicated, omitted when empty.
 */
reachability_caveats?: ReachabilityCaveat[]
}
/**
 * Wire-shape envelope for a [`PrivateTypeLeak`] finding. Mirrors
 * [`UnusedFileFinding`]: flattens the bare finding and carries a typed
 * `actions` array (`export-type` primary plus `suppress-line` secondary).
 */
export interface PrivateTypeLeakFinding {
/**
 * File containing the exported symbol.
 */
path: string
/**
 * Export whose public signature leaks the private type.
 */
export_name: string
/**
 * Private type referenced by the public signature.
 */
type_name: string
/**
 * 1-based line number of the leaking type reference.
 */
line: number
/**
 * 0-based byte column offset.
 */
col: number
/**
 * Byte offset of the type reference.
 */
span_start: number
/**
 * Exact checker-backed provenance when type-aware analysis confirmed the
 * package-public leak across files or re-exports.
 */
semantic?: (SemanticPrivateTypeLeak | null)
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`DeprecatedExportInUse`] finding. Carries a
 * manual `migrate-deprecated-export` primary action plus a `suppress-line`
 * secondary. Never auto-fixable: fallow does not rewrite consumers.
 */
export interface DeprecatedExportInUseFinding {
/**
 * File that declares the deprecated export.
 */
path: string
/**
 * Name of the deprecated export.
 */
export_name: string
/**
 * Whether this is a type-only export.
 */
is_type_only: boolean
/**
 * 1-based line number of the export.
 */
line: number
/**
 * 0-based byte column offset of the export.
 */
col: number
/**
 * Byte offset of the export in the source file.
 */
span_start: number
/**
 * Plain-text message of the `@deprecated` tag, capped at
 * [`DEPRECATED_REASON_MAX_CHARS`] characters. Absent when the tag
 * carries no text.
 */
deprecated_reason?: (string | null)
/**
 * Exact number of distinct consumers: reference sites in reachable
 * files, one per path, line, column and kind. `consumers` holds the
 * first [`DEPRECATED_CONSUMER_SAMPLE_CAP`] of them, so the sample is
 * complete when this count is at most the cap.
 */
consumer_count: number
/**
 * Consumer sample sorted by path, line, column and kind, capped at
 * [`DEPRECATED_CONSUMER_SAMPLE_CAP`] entries.
 */
consumers: DeprecatedExportConsumer[]
/**
 * True when the export is part of the public API: it lives in an entry
 * point, or a re-export chain reaches an entry point. External consumers
 * are not visible, so the finding makes no removal claim.
 */
public_api: boolean
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * One file location that references a deprecated export.
 */
export interface DeprecatedExportConsumer {
/**
 * File that references the deprecated export.
 */
path: string
/**
 * 1-based line number of the import or re-export statement.
 */
line: number
/**
 * 0-based byte column offset of the import or re-export statement.
 */
col: number
kind: DeprecatedConsumerKind
}
/**
 * Wire-shape envelope for an [`UnusedDependency`] finding consumed under
 * the `unused_dependencies` key (production deps). Flattens the bare
 * finding; the typed `actions` array carries either a `remove-dependency`
 * or `move-dependency` primary depending on
 * `inner.used_in_workspaces`.
 */
export interface UnusedDependencyFinding {
/**
 * Package name, including internal workspace package names.
 */
package_name: string
location: DependencyLocation
/**
 * Path to the package.json where this dependency is listed.
 * For root deps this is `<root>/package.json`, for workspace deps it is `<ws>/package.json`.
 */
path: string
/**
 * 1-based line number of the dependency entry in package.json.
 */
line: number
/**
 * Workspace roots that import this package even though the declaring workspace does not.
 */
used_in_workspaces?: string[]
/**
 * Workspace roots whose package.json declares this package for the files
 * that import it. Only a root finding fills this field: these imports use
 * the nearer workspace declaration, so the root declaration stays unused.
 */
declared_and_imported_in?: string[]
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
/**
 * Advisory caveats on the verdict behind this finding. A dependency is
 * reported unused when NO module in the project imports its specifier,
 * so a module that parsed with errors can hide the import that would
 * have credited the package. Sorted, deduplicated, and omitted from the
 * wire when empty. Never gates the finding, though `fallow fix`
 * withholds the `remove-dependency` write while a caveat stands.
 */
reachability_caveats?: ReachabilityCaveat[]
}
/**
 * Wire-shape envelope for an [`UnusedDependency`] finding consumed under
 * the `unused_dev_dependencies` key. Same bare struct as
 * [`UnusedDependencyFinding`]; the fix description points at
 * `devDependencies` and the suppress comment uses
 * `unused-dev-dependency`.
 */
export interface UnusedDevDependencyFinding {
/**
 * Package name, including internal workspace package names.
 */
package_name: string
location: DependencyLocation
/**
 * Path to the package.json where this dependency is listed.
 * For root deps this is `<root>/package.json`, for workspace deps it is `<ws>/package.json`.
 */
path: string
/**
 * 1-based line number of the dependency entry in package.json.
 */
line: number
/**
 * Workspace roots that import this package even though the declaring workspace does not.
 */
used_in_workspaces?: string[]
/**
 * Workspace roots whose package.json declares this package for the files
 * that import it. Only a root finding fills this field: these imports use
 * the nearer workspace declaration, so the root declaration stays unused.
 */
declared_and_imported_in?: string[]
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
/**
 * Advisory caveats on the verdict behind this finding. A dependency is
 * reported unused when NO module in the project imports its specifier,
 * so a module that parsed with errors can hide the import that would
 * have credited the package. Sorted, deduplicated, and omitted from the
 * wire when empty. Never gates the finding, though `fallow fix`
 * withholds the `remove-dependency` write while a caveat stands.
 */
reachability_caveats?: ReachabilityCaveat[]
}
/**
 * Wire-shape envelope for an [`UnusedDependency`] finding consumed under
 * the `unused_optional_dependencies` key. Same bare struct as
 * [`UnusedDependencyFinding`]; the fix description points at
 * `optionalDependencies`. Reuses the `unused-dependency` suppress
 * `IssueKind` because there is no dedicated variant for optional deps.
 */
export interface UnusedOptionalDependencyFinding {
/**
 * Package name, including internal workspace package names.
 */
package_name: string
location: DependencyLocation
/**
 * Path to the package.json where this dependency is listed.
 * For root deps this is `<root>/package.json`, for workspace deps it is `<ws>/package.json`.
 */
path: string
/**
 * 1-based line number of the dependency entry in package.json.
 */
line: number
/**
 * Workspace roots that import this package even though the declaring workspace does not.
 */
used_in_workspaces?: string[]
/**
 * Workspace roots whose package.json declares this package for the files
 * that import it. Only a root finding fills this field: these imports use
 * the nearer workspace declaration, so the root declaration stays unused.
 */
declared_and_imported_in?: string[]
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
/**
 * Advisory caveats on the verdict behind this finding. A dependency is
 * reported unused when NO module in the project imports its specifier,
 * so a module that parsed with errors can hide the import that would
 * have credited the package. Sorted, deduplicated, and omitted from the
 * wire when empty. Never gates the finding, though `fallow fix`
 * withholds the `remove-dependency` write while a caveat stands.
 */
reachability_caveats?: ReachabilityCaveat[]
}
/**
 * Wire-shape envelope for an [`UnusedMember`] finding consumed under the
 * `unused_enum_members` key.
 */
export interface UnusedEnumMemberFinding {
/**
 * File containing the unused member.
 */
path: string
/**
 * Name of the parent enum or class.
 */
parent_name: string
/**
 * Name of the unused member.
 */
member_name: string
kind: MemberKind
/**
 * 1-based line number.
 */
line: number
/**
 * 0-based byte column offset.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
/**
 * Advisory caveats on the verdict behind this finding. A member's usage
 * is collected by walking the member accesses of every module the run
 * parsed, so a member whose only reference lives in a file the run never
 * read reads as unused exactly like an export does. Sorted,
 * deduplicated, and omitted from the wire when empty. Never gates the
 * finding; it does withhold the `remove-enum-member` mutation.
 */
reachability_caveats?: ReachabilityCaveat[]
}
/**
 * Wire-shape envelope for an [`UnusedMember`] finding consumed under the
 * `unused_class_members` key. Same Rust struct as
 * [`UnusedEnumMemberFinding`]; the fix action and suppress comment carry
 * the class-member kebab-case identifier instead.
 */
export interface UnusedClassMemberFinding {
/**
 * File containing the unused member.
 */
path: string
/**
 * Name of the parent enum or class.
 */
parent_name: string
/**
 * Name of the unused member.
 */
member_name: string
kind: MemberKind
/**
 * 1-based line number.
 */
line: number
/**
 * 0-based byte column offset.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Type-aware evidence for this exact candidate when requested.
 */
semantic?: (SemanticCandidateDecision | null)
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
/**
 * Advisory caveats on the verdict behind this finding. A class member's
 * usage is collected by the same reachability-free member-access walk an
 * enum member's is, so it takes the enum-member rule unchanged: any module
 * this run analyzed incompletely can hold the access that credits it.
 * Sorted, deduplicated, and omitted from the wire when empty. Never gates
 * the finding; it does withhold the `remove-class-member` mutation that
 * the type-aware pass would otherwise open.
 */
reachability_caveats?: ReachabilityCaveat[]
}
/**
 * Wire-shape envelope for an [`UnusedMember`] finding consumed under the
 * `unused_store_members` key (a Pinia `state` / `getters` / `actions` key, or
 * a setup-store returned key, declared but never accessed by any consumer
 * project-wide). Same Rust struct as [`UnusedClassMemberFinding`]. Emits only
 * a line-level suppress action: there is no safe auto-fix because a store
 * member can be accessed reflectively (a Pinia plugin, `store.$onAction`, or
 * dynamic dispatch) in ways syntactic analysis cannot see, so removal is a
 * behavioral change the user must own.
 */
export interface UnusedStoreMemberFinding {
/**
 * File containing the unused member.
 */
path: string
/**
 * Name of the parent enum or class.
 */
parent_name: string
/**
 * Name of the unused member.
 */
member_name: string
kind: MemberKind
/**
 * 1-based line number.
 */
line: number
/**
 * 0-based byte column offset.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
/**
 * Advisory caveats on the verdict behind this finding. A store member's
 * usage is collected by the same reachability-free member-access walk a
 * class member's is, so it takes the member rule unchanged: any module
 * this run analyzed incompletely can hold the access that credits it.
 * Sorted, deduplicated, and omitted from the wire when empty. There is no
 * mutation here to withhold, because a store member offers none on any
 * surface; this is disclosure only, so a reader deciding by hand is told
 * what the run did not see.
 */
reachability_caveats?: ReachabilityCaveat[]
}
/**
 * Wire-shape envelope for an [`UnresolvedImport`] finding. Mirrors
 * [`UnusedFileFinding`]: flattens the bare finding and carries a typed
 * `actions` array (`resolve-import` primary plus config and inline
 * suppression actions).
 */
export interface UnresolvedImportFinding {
/**
 * File containing the unresolved import.
 */
path: string
/**
 * The import specifier that could not be resolved.
 */
specifier: string
/**
 * 1-based line number.
 */
line: number
/**
 * 0-based byte column offset of the import statement.
 */
col: number
/**
 * 0-based byte column offset of the source string literal (the specifier in quotes).
 * Used by the LSP to underline just the specifier, not the entire import line.
 */
specifier_col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`UnlistedDependency`] finding. Carries an
 * `install-dependency` primary (non-auto-fixable) plus the standard
 * `ignoreDependencies` config suppress.
 */
export interface UnlistedDependencyFinding {
/**
 * Package name, including internal workspace package names, that is
 * imported but not listed in package.json.
 */
package_name: string
/**
 * Import sites where this unlisted dependency is used (file path, line, column).
 */
imported_from: ImportSite[]
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * A location where an import occurs.
 */
export interface ImportSite {
/**
 * File containing the import.
 */
path: string
/**
 * 1-based line number.
 */
line: number
/**
 * 0-based byte column offset.
 */
col: number
}
/**
 * Wire-shape envelope for a [`DuplicateExport`] finding. Carries up to
 * three actions in position-locked order: an `add-to-config` `ignoreExports`
 * snippet (only when `locations[]` carries at least one path) followed by
 * the `remove-duplicate` fix and the multi-location suppress.
 *
 * The `add-to-config` action sits at position 0 because the documented
 * primary slot points at the safe, non-destructive path: the shadcn /
 * Radix / bits-ui namespace-barrel case where every `index.*` reexports
 * the directory's neighbours. The `remove-duplicate` fix stays as the
 * secondary so consumers that pattern-match on `actions[0].type` for
 * "primary fix" never propose deletion of an intentional barrel surface.
 */
export interface DuplicateExportFinding {
/**
 * The duplicated export name.
 */
export_name: string
/**
 * Locations where this export name appears.
 */
locations: DuplicateLocation[]
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * A location where a duplicate export appears.
 */
export interface DuplicateLocation {
/**
 * File containing the duplicate export.
 */
path: string
/**
 * 1-based line number.
 */
line: number
/**
 * 0-based byte column offset.
 */
col: number
}
/**
 * Wire-shape envelope for a [`TypeOnlyDependency`] finding. Carries a
 * `move-to-dev` primary plus the standard `ignoreDependencies` config
 * suppress.
 */
export interface TypeOnlyDependencyFinding {
/**
 * Production dependency that is only used via type-only imports.
 */
package_name: string
/**
 * Path to the package.json where the dependency is listed.
 */
path: string
/**
 * 1-based line number of the dependency entry in package.json.
 */
line: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`TestOnlyDependency`] finding. Carries a
 * `move-to-dev` primary (different prose than [`TypeOnlyDependencyFinding`])
 * plus the standard `ignoreDependencies` config suppress.
 */
export interface TestOnlyDependencyFinding {
/**
 * Production dependency that is only imported by test files, consider
 * moving to devDependencies.
 */
package_name: string
/**
 * Path to the package.json where the dependency is listed.
 */
path: string
/**
 * 1-based line number of the dependency entry in package.json.
 */
line: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`DevDependencyInProduction`] finding. Carries a
 * `move-to-prod` primary (the promote-side mirror of
 * [`TestOnlyDependencyFinding`]'s `move-to-dev`) plus the standard
 * `ignoreDependencies` config suppress.
 */
export interface DevDependencyInProductionFinding {
/**
 * devDependency imported at runtime from production code, consider moving
 * to dependencies.
 */
package_name: string
/**
 * Path to the package.json where the dependency is listed.
 */
path: string
/**
 * 1-based line number of the dependency entry in package.json.
 */
line: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`CircularDependency`] finding. Mirrors
 * [`UnusedFileFinding`]: flattens the bare finding and carries a typed
 * `actions` array (`refactor-cycle` primary plus `suppress-line`
 * secondary).
 */
export interface CircularDependencyFinding {
/**
 * Files forming the cycle, in import order.
 */
files: string[]
/**
 * Number of files in the cycle.
 */
length: number
/**
 * 1-based line number of the import that starts the cycle (in the first file).
 */
line: number
/**
 * 0-based byte column offset of the import that starts the cycle.
 */
col: number
/**
 * Per-file import anchors, one entry per hop in cycle order: `edges[i]`
 * is the import in `files[i]` pointing to `files[(i + 1) % len]`. Always
 * the same length as `files`. Drives the per-file LSP diagnostic
 * squiggly. `#[serde(default)]` so pre-`edges` baselines deserialize;
 * always emitted on output but intentionally not in the schema's
 * `required` set (see the struct doc).
 */
edges?: CircularDependencyEdge[]
/**
 * Whether this cycle crosses workspace package boundaries.
 */
is_cross_package?: boolean
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * One import hop in a circular dependency: the file containing the import
 * and where that import statement sits.
 *
 * `edges[i]` is the import IN `path` (the hop SOURCE, equal to the cycle's
 * `files[i]`) that points to the NEXT file in the cycle
 * (`files[(i + 1) % files.len()]`); the target is not repeated here to keep
 * the wire compact. Enables a per-file diagnostic squiggly anchored under
 * the offending import rather than a single squiggly on the first file.
 *
 * `col` is a 0-based BYTE column, matching the cycle's top-level `col`;
 * converting it to a UTF-16 code-unit column for LSP clients is a tracked
 * follow-up shared with the existing field.
 */
export interface CircularDependencyEdge {
/**
 * The file containing the import (the hop SOURCE; equal to `files[i]`).
 */
path: string
/**
 * 1-based line number of the import statement pointing to the next file.
 */
line: number
/**
 * 0-based byte column offset of the import statement.
 */
col: number
}
/**
 * Wire-shape envelope for a [`ReExportCycle`] finding. Mirrors
 * [`CircularDependencyFinding`]: flattens the bare finding and carries a
 * typed `actions` array (`refactor-re-export-cycle` informational primary
 * plus `suppress-file` secondary; cycles are file-scoped so a single
 * file-level suppression on the alphabetically-first member breaks the
 * cycle, and no `// fallow-ignore-next-line` form makes sense because the
 * diagnostic is anchored at line 1 col 0 of each member).
 */
export interface ReExportCycleFinding {
/**
 * Files participating in the cycle, sorted lexicographically. For a
 * self-loop, exactly one entry.
 */
files: string[]
kind: ReExportCycleKind
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`PackageCycle`] finding. Mirrors
 * [`CircularDependencyFinding`]: flattens the bare finding and carries a
 * typed `actions` array (`refactor-cycle` primary plus `suppress-line`
 * secondary).
 */
export interface PackageCycleFinding {
/**
 * Workspace package labels in cycle order. The first entry is the
 * lexicographically smallest label; the last entry imports the first.
 * A label is the package name. When two or more workspace packages
 * share a name, the label is `name (root)` with the project-relative
 * package root, so that each label names one package.
 */
packages: string[]
/**
 * Package root directories in cycle order: `package_roots[i]` is the
 * root of `packages[i]`.
 */
package_roots: string[]
/**
 * Number of packages in the cycle.
 */
length: number
/**
 * One example import per hop, in cycle order: `edges[i]` goes from
 * `packages[i]` to `packages[(i + 1) % length]`.
 */
edges: PackageCycleEdge[]
/**
 * True when the group of packages that holds this cycle has more
 * cycles than fallow lists. The listing stops at 20 cycles per group,
 * or earlier on a very dense package graph. Break a listed cycle and
 * run again to see the rest.
 */
group_truncated: boolean
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * One package hop in a [`PackageCycle`]: `from_package` imports
 * `to_package`, and `path` holds one example import for that hop.
 *
 * The example import is the first runtime import by `(path, line)`. When
 * every import on the hop is type-only, it is the first type-only import.
 */
export interface PackageCycleEdge {
/**
 * Label of the importing workspace package, as in
 * [`PackageCycle::packages`].
 */
from_package: string
/**
 * Label of the imported workspace package, as in
 * [`PackageCycle::packages`].
 */
to_package: string
/**
 * File in `from_package` that holds the example import.
 */
path: string
/**
 * File in `to_package` that the example import resolves to.
 */
target_path: string
/**
 * 1-based line number of the example import.
 */
line: number
/**
 * 0-based byte column offset of the example import.
 */
col: number
/**
 * True when every import from `from_package` to `to_package` is
 * type-only. A type-only hop has no runtime effect, but it can still
 * force a build order (for example with declaration builds).
 */
type_only: boolean
}
/**
 * Wire-shape envelope for a [`BoundaryViolation`] finding. Mirrors
 * [`UnusedFileFinding`]: flattens the bare finding and carries a typed
 * `actions` array (`refactor-boundary` primary plus `suppress-line`
 * secondary).
 */
export interface BoundaryViolationFinding {
/**
 * The file making the disallowed import.
 */
from_path: string
/**
 * The file being imported that violates the boundary. When the import
 * goes through a re-export chain, this is the origin module that
 * declares the imported symbol, not the barrel.
 */
to_path: string
/**
 * The zone the importing file belongs to.
 */
from_zone: string
/**
 * The zone the imported file belongs to.
 */
to_zone: string
/**
 * The raw import specifier from the source file.
 */
import_specifier: string
/**
 * 1-based line number of the import statement in the source file.
 */
line: number
/**
 * 0-based byte column offset of the import statement.
 */
col: number
/**
 * The barrel file that the source file imports directly, when the
 * violation comes from a re-export chain. Absent for a direct import.
 */
via_path?: (string | null)
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`BoundaryCoverageViolation`] finding. Carries
 * actions for assigning the file to a zone or explicitly allowing it to stay
 * unmatched.
 */
export interface BoundaryCoverageViolationFinding {
/**
 * The unmatched source file.
 */
path: string
/**
 * 1-based line number used for diagnostics.
 */
line: number
/**
 * 0-based byte column offset used for diagnostics.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps.
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`BoundaryCallViolation`] finding. Carries
 * actions for refactoring the forbidden call out of the zone or suppressing
 * it with the shared `boundary-violation` token.
 */
export interface BoundaryCallViolationFinding {
/**
 * The zoned source file making the forbidden call.
 */
path: string
/**
 * 1-based line number of the call site.
 */
line: number
/**
 * 0-based byte column offset of the call site.
 */
col: number
/**
 * The zone the calling file is classified into.
 */
zone: string
/**
 * The callee path as written at the call site (e.g. `cp.exec`).
 */
callee: string
/**
 * The configured pattern that matched (e.g. `child_process.*`), so
 * consumers can see both the written path and the rule that fired.
 */
pattern: string
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps.
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`PolicyViolation`] finding. Carries actions for
 * replacing the banned call, import, or effect, or suppressing it with a scoped
 * `policy-violation:<pack>/<rule-id>` token.
 */
export interface PolicyViolationFinding {
/**
 * The source file containing the banned call, import, or effectful usage.
 */
path: string
/**
 * 1-based line number of the call site or import declaration.
 */
line: number
/**
 * 0-based byte column offset of the call site or import declaration.
 */
col: number
/**
 * Name of the rule pack that declared the matching rule.
 */
pack: string
/**
 * Id of the matching rule inside the pack. `pack` plus `rule_id` is the
 * finding's policy identity.
 */
rule_id: string
kind: PolicyRuleKind
/**
 * What matched: the written callee path for `banned-call` (e.g.
 * `cp.exec`), the raw import specifier for `banned-import` (e.g.
 * `moment/locale/nl`), `<effect>: <callee>` for `banned-effect`, or the
 * exported name for `banned-export`. For `gdp-proof-producer`, the canonical
 * factory with its JSON-quoted literal label or `...` for a dynamic label.
 */
matched: string
severity: PolicyViolationSeverity
/**
 * The rule's author-provided message, when set.
 */
message?: (string | null)
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps.
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
}
/**
 * A suppression comment or JSDoc tag that no longer matches any issue.
 */
export interface StaleSuppression {
/**
 * File containing the stale suppression.
 */
path: string
/**
 * 1-based line number of the suppression comment or tag.
 */
line: number
/**
 * 0-based byte column offset.
 */
col: number
origin: SuppressionOrigin
/**
 * True when `rules.require-suppression-reason` reported a suppression
 * comment or tag that has no reason.
 */
missing_reason?: boolean
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted.
 */
actions: IssueAction[]
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`UnusedCatalogEntry`] finding. Per-instance
 * `auto_fixable` flips to `false` when `hardcoded_consumers` is non-empty or
 * the source is not `pnpm-workspace.yaml`.
 */
export interface UnusedCatalogEntryFinding {
/**
 * Package name declared in the catalog (e.g. `"react"`, `"@scope/lib"`).
 */
entry_name: string
/**
 * Catalog group: `"default"` for the default catalog map, or the named
 * catalog key for entries declared under `catalogs.<name>`.
 */
catalog_name: string
/**
 * Path to the catalog source file, relative to the analyzed root.
 */
path: string
/**
 * 1-based line number of the catalog entry within the source file.
 */
line: number
/**
 * Workspace `package.json` files that declare the same package with a
 * hardcoded version range instead of `catalog:`. Empty when no consumer
 * uses a hardcoded version. Sorted lexicographically for deterministic
 * output.
 */
hardcoded_consumers?: string[]
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted.
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`EmptyCatalogGroup`] finding. Carries a
 * `remove-empty-catalog-group` primary. YAML-sourced findings also include a
 * YAML-comment suppress action.
 */
export interface EmptyCatalogGroupFinding {
/**
 * Catalog group name declared under the `catalogs` map.
 */
catalog_name: string
/**
 * Path to the catalog source file, relative to the analyzed root.
 */
path: string
/**
 * 1-based line number of the empty group header within the source file.
 */
line: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted.
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`UnresolvedCatalogReference`] finding. The
 * primary action at position 0 discriminates on `available_in_catalogs`:
 * `add-catalog-entry` when the array is empty (no other catalog declares
 * the package), or `update-catalog-reference` when at least one
 * alternative exists. When exactly one alternative exists, the action
 * also carries `suggested_target` so deterministic agents can land the
 * edit without picking from a list.
 */
export interface UnresolvedCatalogReferenceFinding {
/**
 * Package name being referenced via the catalog protocol (e.g. `"react"`).
 */
entry_name: string
/**
 * Catalog group the reference points at: `"default"` for bare `catalog:` references,
 * or the named catalog key for `catalog:<name>` references.
 */
catalog_name: string
/**
 * Absolute path to the consumer `package.json`. Matches the storage
 * convention used by every path-anchored finding type (`UnusedFile`,
 * `UnresolvedImport`, `UnusedExport`, etc.) so the shared filtering
 * pipelines (`filter_results_by_changed_files`, per-file overrides,
 * audit attribution) work without a separate root-join pass. JSON
 * output strips the project-root prefix via `serde_path::serialize`.
 */
path: string
/**
 * 1-based line number of the dependency entry in the consumer `package.json`.
 */
line: number
/**
 * Other catalogs in the same catalog source that DO declare this package.
 * Empty when no catalog has the package. Sorted lexicographically. Lets
 * agents and humans decide whether to switch the reference to a different
 * catalog or to add the entry to the named catalog.
 */
available_in_catalogs?: string[]
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted; position 0 is the discriminated
 * primary (see struct docs).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`UnusedDependencyOverride`] finding. Carries
 * a `remove-dependency-override` primary plus an `add-to-config`
 * `ignoreDependencyOverrides` suppress scoped to the target package and
 * declaration source.
 */
export interface UnusedDependencyOverrideFinding {
/**
 * The full original override key as written in the source (e.g.
 * `"react>react-dom"`, `"@types/react@<18"`). Preserved for round-trip
 * reporting so agents see the unmodified spelling.
 */
raw_key: string
/**
 * The target package the override rewrites (e.g. `"react-dom"` for
 * `"react>react-dom"`, `"@types/react"` for `"@types/react@<18"`).
 */
target_package: string
/**
 * Optional parent package (left side of `>`). `None` for bare-target keys.
 */
parent_package?: (string | null)
/**
 * Optional version selector on the target (e.g. `Some("<18")` for
 * `"@types/react@<18"`).
 */
version_constraint?: (string | null)
/**
 * The right-hand side of the entry: the version the package manager should force.
 */
version_range: string
source: DependencyOverrideSource
/**
 * Path to the source file. `pnpm-workspace.yaml` or a `package.json`,
 * stored as an absolute filesystem path so `--changed-since` and
 * per-file `overrides.rules` can compare directly against the analyzer's
 * changed-set / per-path rule lookups. JSON serialization strips the
 * project root via `serde_path::serialize`, matching the
 * `UnresolvedCatalogReference` convention.
 */
path: string
/**
 * 1-based line number of the entry within the source file.
 */
line: number
/**
 * Soft hint reminding consumers to verify the override before removal.
 * Emitted on every unused-override finding (both bare-target and
 * parent-chain shapes) because projects without a readable lockfile still
 * use the conservative package-manifest fallback.
 */
hint?: (string | null)
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted.
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`MisconfiguredDependencyOverride`] finding.
 * Carries a `fix-dependency-override` primary plus the conditional
 * `add-to-config` `ignoreDependencyOverrides` suppress (skipped when both
 * `target_package` and `raw_key` are empty, since the rule matcher keys on
 * a non-empty package name).
 */
export interface MisconfiguredDependencyOverrideFinding {
/**
 * The full original override key as written in the source.
 */
raw_key: string
/**
 * Parsed target package name when the key was syntactically valid (the
 * `EmptyValue` reason path). `None` for `UnparsableKey` findings whose
 * key could not be parsed at all. Used by JSON `add-to-config` actions to
 * emit a paste-ready `ignoreDependencyOverrides` value that matches the
 * suppression matcher (which also keys on `target_package`); avoids the
 * pitfall where `raw_key` like `"react@<18"` would not match the rule
 * that targets package `"react"`.
 */
target_package?: (string | null)
/**
 * The right-hand side of the entry, exactly as written. Empty when the
 * value was missing.
 */
raw_value: string
reason: DependencyOverrideMisconfigReason
source: DependencyOverrideSource
/**
 * Path to the source file. Stored as an absolute filesystem path so
 * `--changed-since` and per-file `overrides.rules` can compare directly.
 * JSON serialization strips the project root via `serde_path::serialize`.
 */
path: string
/**
 * 1-based line number of the entry within the source file.
 */
line: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted.
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`InvalidClientExport`] finding. There is no safe
 * auto-fix: the export itself may be a legitimate client-component value
 * export that happens to collide with a Next.js server-only name, so removing
 * it could break the component. Actions are a manual `move-to-server-module`
 * fix (the real remediation) plus a line-level suppress.
 */
export interface InvalidClientExportFinding {
/**
 * File carrying the `"use client"` directive and the illegal export.
 */
path: string
/**
 * Name of the server-only / route-config export that is illegal in a
 * client file (e.g. `metadata`, `generateMetadata`, `revalidate`, `GET`).
 */
export_name: string
/**
 * The file-level directive that makes the export illegal. Always
 * `"use client"` today; carried so the message can name it verbatim.
 */
directive: string
/**
 * 1-based line number of the export.
 */
line: number
/**
 * 0-based byte column offset of the export.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`MixedClientServerBarrel`] finding. There is no
 * safe auto-fix: splitting a barrel into separate client and server modules is
 * a human decision (the barrel may intentionally aggregate both surfaces).
 * Actions are a manual `split-mixed-barrel` fix (the real remediation) plus a
 * line-level suppress.
 */
export interface MixedClientServerBarrelFinding {
/**
 * The barrel file re-exporting both a client and a server-only origin.
 */
path: string
/**
 * The `"use client"` origin's relative path or specifier as written in the
 * barrel's offending re-export.
 */
client_origin: string
/**
 * The server-only origin's relative path or specifier as written in the
 * barrel's offending re-export.
 */
server_origin: string
/**
 * 1-based line number of the barrel's first offending re-export.
 */
line: number
/**
 * 0-based byte column offset of the barrel's first offending re-export.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`MisplacedDirective`] finding. There is no safe
 * auto-fix: moving a directive to the leading prologue is a small but
 * judgement-bearing edit (the author may have intended the file to be a
 * server module after all). Actions are a manual `hoist-directive` fix (the
 * real remediation) plus a line-level suppress.
 */
export interface MisplacedDirectiveFinding {
/**
 * The file carrying the misplaced directive.
 */
path: string
/**
 * The directive string as written, either `"use client"` or
 * `"use server"` (without the surrounding quotes).
 */
directive: string
/**
 * 1-based line number of the misplaced directive statement.
 */
line: number
/**
 * 0-based byte column offset of the misplaced directive statement.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`UnprovidedInject`] finding. There is no safe
 * auto-fix: the fix is binary but judgement-bearing (add a `provide` for the
 * key, or delete the dead inject). Actions are manual remediation guidance
 * plus a line-level suppress.
 */
export interface UnprovidedInjectFinding {
/**
 * The file carrying the orphan inject / getContext call.
 */
path: string
/**
 * The injected key identifier as written at the call site.
 */
key_name: string
/**
 * Which framework's DI API this came from: `"vue"` or `"svelte"`.
 */
framework: string
/**
 * 1-based line number of the inject / getContext call.
 */
line: number
/**
 * 0-based byte column offset of the inject / getContext call.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`UnrenderedComponent`] finding. There is no safe
 * auto-fix: the fix is binary but judgement-bearing (render the component
 * somewhere, or delete the dead component). Actions are manual remediation
 * guidance plus a line-level suppress.
 */
export interface UnrenderedComponentFinding {
/**
 * The component file that is reachable but rendered nowhere.
 */
path: string
/**
 * The component name. For `"vue"` / `"svelte"` / `"astro"` this is the SFC
 * file stem (PascalCase); for `"angular"` it is the component class name; for
 * `"lit"` it is the registered custom-element TAG (e.g. `x-foo`), not a file
 * stem. Use `path` to anchor the file across all frameworks.
 */
component_name: string
/**
 * Which framework this component belongs to: `"vue"`, `"svelte"`, `"astro"`,
 * `"angular"`, or `"lit"`.
 */
framework: string
/**
 * A barrel/file that re-exports this component, kept for the remediation
 * trace ("reachable via X, rendered nowhere"). Absolute in memory,
 * serialized workspace-relative (like `path`); `None` when not determinable.
 */
reachable_via?: (string | null)
/**
 * 1-based line number of the component (the file head; SFCs have no explicit
 * default-export statement).
 */
line: number
/**
 * 0-based byte column offset.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`RouteCollision`] finding. A route collision is a
 * guaranteed `next build` failure, so the PRIMARY action is manual guidance
 * (move or merge one of the colliding files), NOT a suppress: suppressing a
 * build error never makes the build pass. A file-level suppress is offered as
 * an escape hatch only.
 */
export interface RouteCollisionFinding {
/**
 * This colliding route file (a `page` or `route` leaf).
 */
path: string
/**
 * The URL pathname this file resolves to within its app-root, after
 * stripping route groups `(x)` and parallel-slot `@slot` prefixes (e.g.
 * `/about`, `/api/health`, `/blog/:slug`).
 */
url: string
/**
 * The other route files that resolve to the same URL within the same
 * app-root. Path-sorted for stable output / fingerprints.
 */
conflicting_paths: string[]
/**
 * 1-based line number (file-level finding, always 1).
 */
line: number
/**
 * 0-based byte column offset (file-level finding, always 0).
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`DynamicSegmentNameConflict`] finding. The
 * conflict is a Next.js dev / runtime error (`next build` does NOT catch it),
 * so the primary action is manual guidance (rename the dynamic segments to a
 * single consistent slug name), with a file-level suppress as escape hatch.
 */
export interface DynamicSegmentNameConflictFinding {
/**
 * This route file living under one of the conflicting dynamic segments.
 */
path: string
/**
 * The tree position (parent URL after group/slot normalization) where the
 * dynamic segments conflict, e.g. `/shop` for `/shop/[id]` vs
 * `/shop/[slug]`. The app-root prefix is stripped.
 */
position: string
/**
 * The distinct conflicting dynamic-segment spellings at this position, as
 * written (e.g. `["[id]", "[slug]"]`). Sorted for stable output.
 */
conflicting_segments: string[]
/**
 * The other route files at the same position under a conflicting dynamic
 * segment. Path-sorted for stable output / fingerprints.
 */
conflicting_paths: string[]
/**
 * 1-based line number (file-level finding, always 1).
 */
line: number
/**
 * 0-based byte column offset (file-level finding, always 0).
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`UnusedComponentProp`] finding. There is no safe
 * auto-fix: removing a declared prop is judgement-bearing (the prop may be part
 * of a deliberately-stable public component API). Actions are manual
 * remediation guidance plus a line-level suppress at the prop declaration.
 */
export interface UnusedComponentPropFinding {
/**
 * The component file declaring the unused prop.
 */
path: string
/**
 * The component name.
 */
component_name: string
/**
 * The declared prop name that is never referenced.
 */
prop_name: string
/**
 * 1-based line number of the prop declaration.
 */
line: number
/**
 * 0-based byte column offset of the prop declaration.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`AbsentComponentProp`] finding. There is no safe
 * auto-fix: removing a declared prop is judgement-bearing (the prop may be part
 * of a deliberately-stable public component API). Actions are manual
 * remediation guidance plus a line-level suppress at the prop declaration.
 */
export interface AbsentComponentPropFinding {
/**
 * Source path of the input declaration.
 */
path: string
/**
 * Semantic declaration name, or SFC filename stem.
 */
component_name: string
/**
 * Public framework token.
 */
framework: string
/**
 * Public optional input name.
 */
prop_name: string
/**
 * 1-based declaration line.
 */
line: number
/**
 * 0-based declaration byte column.
 */
col: number
/**
 * Whether omission has a declared default.
 */
has_default: boolean
/**
 * Every inspected reachable caller, deterministically ordered.
 */
inspected_call_sites: ComponentPropCallSite[]
/**
 * Manual-review meaning and limits of static evidence.
 */
explanation: string
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * One inspected reachable component invocation.
 */
export interface ComponentPropCallSite {
/**
 * Caller source path.
 */
path: string
/**
 * 1-based opening-tag line.
 */
line: number
/**
 * 0-based original-source byte column.
 */
col: number
}
/**
 * Wire-shape envelope for an [`UnusedComponentEmit`] finding. There is no safe
 * auto-fix: removing a declared emit is judgement-bearing (the event may be
 * part of a deliberately-stable public component API). Actions are manual
 * remediation guidance plus a line-level suppress at the emit declaration.
 */
export interface UnusedComponentEmitFinding {
/**
 * The `.vue` SFC declaring the unused emit.
 */
path: string
/**
 * The component name (the `.vue` file stem).
 */
component_name: string
/**
 * The declared emit event name that is never emitted.
 */
emit_name: string
/**
 * 1-based line number of the emit declaration.
 */
line: number
/**
 * 0-based byte column offset of the emit declaration.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`UnusedComponentInput`] finding. There is no safe
 * auto-fix: removing a declared input is judgement-bearing (the input may be
 * part of a deliberately-stable public component API). The only action is a
 * line-level suppress at the input declaration.
 */
export interface UnusedComponentInputFinding {
/**
 * The Angular component/directive `.ts` file declaring the unused input.
 */
path: string
/**
 * The component name (the `.ts` file stem).
 */
component_name: string
/**
 * The declared input name that is never read.
 */
input_name: string
/**
 * 1-based line number of the input declaration.
 */
line: number
/**
 * 0-based byte column offset of the input declaration.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`UnusedComponentOutput`] finding. There is no safe
 * auto-fix: removing a declared output is judgement-bearing (the event may be
 * part of a deliberately-stable public component API). The only action is a
 * line-level suppress at the output declaration.
 */
export interface UnusedComponentOutputFinding {
/**
 * The Angular component/directive `.ts` file declaring the unused output.
 */
path: string
/**
 * The component name (the `.ts` file stem).
 */
component_name: string
/**
 * The declared output name that is never emitted.
 */
output_name: string
/**
 * 1-based line number of the output declaration.
 */
line: number
/**
 * 0-based byte column offset of the output declaration.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`UnusedSvelteEvent`] finding. There is no safe
 * auto-fix: removing a dispatched event is judgement-bearing (the event may be
 * part of a deliberately-stable public component API, or a listener may be
 * added later). Actions are manual remediation guidance plus a line-level
 * suppress at the `dispatch` call.
 */
export interface UnusedSvelteEventFinding {
/**
 * The `.svelte` component dispatching the unlistened event.
 */
path: string
/**
 * The component name (the `.svelte` file stem).
 */
component_name: string
/**
 * The dispatched event name that is listened to nowhere.
 */
event_name: string
/**
 * 1-based line number of the `dispatch('<name>')` call.
 */
line: number
/**
 * 0-based byte column offset of the `dispatch('<name>')` call.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`UnusedServerAction`] finding. There is no safe
 * auto-fix: the fix is binary but judgement-bearing (wire the action up to a
 * consumer, or delete it). Actions are manual remediation guidance plus a
 * line-level suppress.
 */
export interface UnusedServerActionFinding {
/**
 * The `"use server"` file that exports the unreferenced action.
 */
path: string
/**
 * The exported action name as written, or `"default"` for a default export.
 */
action_name: string
/**
 * 1-based line number of the export.
 */
line: number
/**
 * 0-based byte column offset of the export.
 */
col: number
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for an [`UnusedLoadDataKey`] finding. There is no safe
 * auto-fix: a `load()` fetch can have side effects, so deleting the key is a
 * human call. Actions are manual remediation guidance plus a line-level
 * suppress.
 */
export interface UnusedLoadDataKeyFinding {
/**
 * The producer `+page.{ts,server.ts,js,server.js}` file declaring the key.
 */
path: string
/**
 * The returned-object key name read by no consumer.
 */
key_name: string
/**
 * 1-based line number of the key in the return object.
 */
line: number
/**
 * 0-based byte column offset of the key.
 */
col: number
/**
 * The route directory relative to the project root (`src/routes/blog`), for
 * agent remediation and per-route trend aggregation. `None` when not
 * determinable.
 */
route_dir?: (string | null)
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Gate severity of this finding after `rules` and `overrides[].rules`
 * resolve for its path. CI formats read it for the annotation, SARIF
 * and CodeClimate level. Absent in output from older versions. Not
 * part of the finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`PropDrillingChain`] finding. There is no safe
 * auto-fix: collapsing a drilling chain (colocate the consumer, lift to a
 * context, or compose the component) is a design decision. The only action is a
 * line-level suppress at the source hop's prop declaration. The rule defaults
 * to `off` (opt-in health signal), so this finding is dormant by default.
 */
export interface PropDrillingChainFinding {
/**
 * The drilled prop name as declared at the chain SOURCE.
 */
prop: string
/**
 * The chain depth = the number of components the prop is forwarded THROUGH
 * (source + intermediates + consumer = `hops.len()`). Always `>= N`.
 */
depth: number
/**
 * The ordered hop trail from source to consumer. The first hop owns the
 * prop, the middle hops are pass-throughs, the last hop consumes it. The
 * finding anchor is the first hop (`path` / `line` for suppression + CI).
 */
hops: PropDrillHop[]
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Rule severity of this finding. This type never gates the run, so the
 * value does not change the exit code. `fallow report --from` reads it
 * for the SARIF level, so the level does not depend on the config at
 * render time. Absent in output from older versions. Not part of the
 * finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * One hop in a prop-drilling chain: a component that received the prop and
 * passed it along (or, at the chain ends, the source that owns it and the
 * consumer that substantively reads it).
 */
export interface PropDrillHop {
/**
 * The file containing this hop's component.
 */
file: string
/**
 * 1-based line of the component definition (or the prop declaration at the
 * source hop). Anchors a jump-to-source for the agent.
 */
line: number
/**
 * The component name at this hop.
 */
component: string
}
/**
 * Wire-shape envelope for a [`ThinWrapper`] finding. There is no safe
 * auto-fix: inlining a thin wrapper at its call sites (or deleting it) is a
 * design decision. The only action is a line-level suppress at the wrapper's
 * definition. The rule defaults to `off` (opt-in health signal), so this
 * finding is dormant by default.
 */
export interface ThinWrapperFinding {
/**
 * The file containing the wrapper component.
 */
file: string
/**
 * 1-based line of the wrapper component definition (the finding anchor for
 * jump-to-source and line-level suppression).
 */
line: number
/**
 * The wrapper component name.
 */
component: string
/**
 * The single child component the wrapper forwards its props to (as written
 * at the render site).
 */
child_component: string
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Rule severity of this finding. This type never gates the run, so the
 * value does not change the exit code. `fallow report --from` reads it
 * for the SARIF level, so the level does not depend on the config at
 * render time. Absent in output from older versions. Not part of the
 * finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * Wire-shape envelope for a [`DuplicatePropShape`] finding. There is no safe
 * auto-fix: extracting a shared `Props` type or a base component for a group of
 * same-shaped components is a design decision. The actions are manual guidance
 * (extract the shared shape) plus a line-level suppress at the component
 * definition and a file-level suppress escape hatch (mirroring the
 * route-collision multi-file model). The rule defaults to `off` (opt-in health
 * signal), so this finding is dormant by default.
 */
export interface DuplicatePropShapeFinding {
/**
 * The file containing this component.
 */
file: string
/**
 * 1-based line of this component definition (the finding anchor for
 * jump-to-source and line-level suppression).
 */
line: number
/**
 * This component name.
 */
component: string
/**
 * The shared SIGNIFICANT prop-name set (sorted, denylist-stripped). The
 * unit being grouped; identical across every member of the group.
 */
shape: string[]
/**
 * The total number of components in this group (this one plus every
 * sibling).
 */
group_size: number
/**
 * The OTHER components sharing this exact prop shape (path-sorted). A
 * file-level-suppressed member drops from its own finding but still appears
 * here, because the group is real regardless of suppression.
 */
sharing_components: DuplicatePropShapeMember[]
/**
 * Stable id of this finding: `dc1:<rule>:<16 hex digits>`, with a
 * `~<k>` suffix when several findings of one type share an identity.
 * Line and column are not inputs, so the id survives line shifts,
 * reformats and reorders. A rename of the file or the symbol gives a
 * new id. Absent in output from older versions.
 */
finding_id?: (string | null)
/**
 * Suggested next steps. Always emitted (possibly empty for
 * forward-compat).
 */
actions: IssueAction[]
/**
 * Set by the audit pass when this finding is introduced relative to
 * the merge-base.
 */
introduced?: (AuditIntroduced | null)
/**
 * Rule severity of this finding. This type never gates the run, so the
 * value does not change the exit code. `fallow report --from` reads it
 * for the SARIF level, so the level does not depend on the config at
 * render time. Absent in output from older versions. Not part of the
 * finding identity, baseline keys or fingerprints.
 */
effective_severity?: (EffectiveSeverity | null)
}
/**
 * One member of a duplicate-prop-shape group: the OTHER components that share
 * the same significant prop-name set, listed in each member's
 * `sharing_components`. Path-sorted for stable output. A located reference (no
 * `shape`, which is carried once on the owning [`DuplicatePropShape`]).
 */
export interface DuplicatePropShapeMember {
/**
 * The file containing the sibling component.
 */
file: string
/**
 * 1-based line of the sibling component definition.
 */
line: number
/**
 * The sibling component name.
 */
component: string
}
/**
 * Per-category delta comparison against a saved baseline. Only present in
 * `CheckOutput` when `--baseline` is used.
 */
export interface BaselineDeltas {
/**
 * Net change in total issues vs baseline (positive = more issues).
 */
total_delta: number
/**
 * Per-category breakdown of current, baseline, and delta counts.
 */
per_category: {
[k: string]: BaselineCategoryDelta
}
}
/**
 * Single-category baseline delta entry inside [`BaselineDeltas::per_category`].
 */
export interface BaselineCategoryDelta {
/**
 * Current issue count for this category.
 */
current: number
/**
 * Baseline issue count for this category.
 */
baseline: number
/**
 * Change from baseline (current - baseline).
 */
delta: number
}
/**
 * Baseline match statistics. Shows how many baseline entries existed and how
 * many matched current issues. Useful for detecting stale baselines
 * programmatically. Only present in `CheckOutput` when `--baseline` is used.
 */
export interface BaselineMatch {
/**
 * Total number of entries in the loaded baseline file.
 */
entries: number
/**
 * Number of baseline entries that matched current issues and were
 * filtered.
 */
matched: number
}
/**
 * One run's machine-readable view of a loaded baseline.
 *
 * `stale` and `gate_trips` answer different questions and legitimately
 * disagree. `stale` mirrors the unasked-for stderr advisory, which stays silent
 * below a quarter of the baseline and on a run that produced no findings at
 * all, because a cleaned project and a rotted baseline look identical from
 * there. `gate_trips` mirrors the opt-in `--fail-on-stale-baseline` rule, which
 * a repository asks for precisely to catch those cases, so it fires on any
 * stale entry. A rotted baseline on a cleaned project reports
 * `stale: false` with `gate_trips: true`; that is the contract, not a defect.
 *
 * `change_scoped` is the member a consumer must read before dividing
 * `matched_entries` by `baseline_entries`. A run narrowed to part of the
 * project compares a whole-project baseline against a slice of it and can
 * report `matched_entries: 0` while the baseline is perfectly healthy, so both
 * `stale` and `gate_trips` are false there by construction. The remedy for a
 * tripped gate is always the same: re-save the baseline from a whole-project
 * run with `--save-baseline`.
 */
export interface BaselineStaleness {
/**
 * Entries carried by the loaded baseline file. On health these are the
 * complexity and CRAP finding entries; runtime-coverage suppressions and
 * refactoring target keys carried by the same file are not counted.
 */
baseline_entries: number
/**
 * Entries that matched a current finding on this run and were filtered out
 * of the report. On health this includes entries matched through a
 * followed file move.
 */
matched_entries: number
/**
 * Entries that matched no current finding on this run:
 * `baseline_entries - matched_entries`.
 */
stale_entries: number
/**
 * Findings this run produced before the baseline filtered them. Zero means
 * there was nothing to compare, either because the project is clean or
 * because the scope was empty, which is why `stale` stays false there even
 * when every entry went unmatched.
 */
current_findings: number
/**
 * Health only: the number of functions above a complexity threshold that
 * the baseline does not accept. This is a count of functions, like
 * `summary.functions_above_threshold`, not a count of baseline entries,
 * and it is the value before `--top`. A run that does not list the
 * complexity findings (for example `--score`) still reports it, so a
 * reader can tell the new functions from the accepted ones.
 *
 * `dead-code` and `dupes` do not emit it. An envelope from a fallow
 * version before this member does not carry it either.
 */
remaining_findings?: (number | null)
/**
 * True when this run analyzed only part of the project, so a whole-project
 * baseline matches less of it for reasons that are not rot. The channels
 * differ per command and include a diff, a base ref, `--changed-since`,
 * `--workspace`, `--changed-workspaces`, `--scope`, `--file`, an
 * issue-type filter, and production mode. Both `stale` and `gate_trips`
 * are false whenever this is true. `scope_reasons` names the channels
 * that fired.
 */
change_scoped: boolean
/**
 * True exactly when the advisory stderr warning fired: not change-scoped,
 * at least one current finding before baseline filtering, and either
 * nothing matched or `stale_entries` reached a quarter of
 * `baseline_entries`.
 */
stale: boolean
warning: BaselineStalenessAdvisory
/**
 * True exactly when `unrecognised_format` is true, or
 * `!change_scoped && baseline_entries > 0 && matched_entries < baseline_entries`.
 * That is the rule `--fail-on-stale-baseline` applies. Deliberately
 * stricter than `stale`: any unmatched entry counts, and so does a file
 * this command could not read as its own, which protects nothing at all.
 * The second half is suppressed by `change_scoped` and the first is not:
 * which command wrote a file does not depend on how much of the project
 * the run looked at.
 *
 * It describes the baseline, not the run's exit code: `health
 * --report-only` is an explicit request never to fail, so that run exits 0
 * and says so on stderr while still reporting `gate_trips: true` here, and
 * `fallow audit` never judges a baseline at all, so its
 * `gate_outcomes["stale-baseline"]` stands down beside a section that
 * reports `true`.
 */
gate_trips: boolean
/**
 * Entries that matched only by following a file move. Only `health` can
 * follow one, in its identity baseline mode; `dead-code` and `dupes` match
 * entries by fingerprint and never classify one as moved, so they report
 * `0`. Always `0` in health's count mode too.
 */
moved_entries: number
/**
 * True when the loaded file is not a baseline of the command that read it:
 * it names another command in its top-level `kind`, or it names none and
 * carries no key this command's own format writes. Read this, not
 * `baseline_entries == 0`, before telling anyone their baseline is the
 * wrong file: a baseline saved from a project that had nothing to record
 * is legitimately empty and is not a mistake.
 *
 * Present only when true, so an envelope from a run that loaded its own
 * baseline is unchanged. All three commands set it, including `dead-code`,
 * which classifies the file before its required fields could reject it.
 * A file with no `kind` is the reading a baseline saved before that member
 * existed gets, which is why the keys remain the fallback.
 */
unrecognised_format?: boolean
/**
 * The command that saved the loaded file, when `unrecognised_format` is
 * true and the file names a writer this version knows: `dead-code`,
 * `dupes` or `health`, the token the file carries in its top-level `kind`.
 *
 * Absent, never null, for this command's own baseline, for a file that
 * names no writer (an empty file, or a baseline saved before `kind`
 * existed) and for a `kind` token this version does not know. The value
 * set is OPEN: a later release can add a writer, so treat an unknown value
 * as "another command". The CLI computes it once per loaded baseline and
 * uses the same value for its stderr note.
 */
saved_by?: string
/**
 * `legacy` when the loaded dead-code baseline has no `identity`, so its
 * entries use the old key forms and some of them hold a line. The run
 * still applies the file. `--save-baseline` rewrites it with line-free
 * keys. Absent for a current baseline and on `dupes` and `health`. The
 * value set is OPEN.
 */
format?: string
/**
 * Which channels narrowed this run, present and non-empty exactly when
 * `change_scoped` is true. Both members are derived from one function, so
 * the boolean and the array cannot disagree.
 *
 * Read it to decide whether the narrowing is removable: a run narrowed
 * only by `diff`, `changed-since`, `changed-files`, `scope`, `file` or
 * `issue-type-filter` can be repeated unscoped to judge the baseline,
 * while `production`, `workspace` and `changed-workspaces` are the
 * caller's own choice about what to analyze and an unscoped repeat would
 * contradict it.
 *
 * The name set is OPEN and the names a command can emit differ per
 * command; see [`ScopeReason`].
 */
scope_reasons?: ScopeReason[]
}
/**
 * The result of a `--finding-id` query, present only when the run received
 * one or more `--finding-id` values.
 *
 * A requested id that is missing from a conclusive run means "fixed,
 * suppressed, or ignored by config", never "unknown": an inline suppression
 * comment or an `ignoreFindings` entry is a choice a person made to hide the
 * finding, so it counts as absent. A missing id in a run that is not
 * conclusive is unknown, never resolved.
 *
 * Every list keeps the order of `requested`. `found` and `missing` partition
 * `requested`. `filtered` is a subset of `missing`.
 */
export interface FindingIdQuery {
/**
 * The requested ids, without duplicates, in the order of the arguments.
 */
requested: string[]
/**
 * The requested ids that this report contains.
 */
found: string[]
/**
 * The requested ids that this report does not contain. When `conclusive`
 * is true, a missing id is fixed, suppressed, or ignored by config.
 * Otherwise its state is unknown.
 */
missing: string[]
/**
 * The missing ids that the analysis still found before a filter of this
 * run (scope, baseline, issue-type filter) removed them. Such a finding
 * still exists.
 */
filtered: string[]
/**
 * True when no option of this run can hide a finding without a fix, and
 * no requested id was filtered. Only then does a missing id mean that
 * the analysis no longer reports the finding.
 */
conclusive: boolean
/**
 * Why the query is not conclusive, sorted. Empty exactly when
 * `conclusive` is true.
 */
inconclusive_reasons: FindingIdQueryReason[]
/**
 * A stable hash (`af1:<16 hex digits>`) of every input other than the
 * source code that decides which findings the run reports:
 * - the fallow version;
 * - the merged config after `extends` (without keys that only shape other
 *   commands), the loaded external plugins and rule packs;
 * - production mode, `includeEntryExports`, the effective rules, the
 *   type-aware mode, requirement and project list, the file size limit;
 * - the root-relative path and content of each repository `.gitignore`,
 *   `.ignore` and `.git/info/exclude`, each `package.json`, each
 *   `tsconfig*.json` and `jsconfig*.json` with the files its `extends`
 *   names, and each file that matches a built-in or external plugin
 *   config pattern (for example `vite.config.ts`).
 *
 * File content is normalized (CRLF to LF, trailing newlines removed).
 * Known exclusions: the global git excludes file and other machine
 * environment outside the `FALLOW_*` variables. Store the fingerprint
 * with a verdict. A later query with another fingerprint is unknown, even
 * when `conclusive` is true. An edit to a source file keeps it; an edit
 * to a manifest or project config changes it, also when the edit fixes a
 * dependency finding.
 */
analysis_fingerprint: string
}
/**
 * Result of regression detection (`--fail-on-regression`). Compares current
 * issue counts against a baseline from config or an explicit file.
 */
export interface RegressionResult {
status: RegressionStatus
/**
 * Baseline total before the change. Absent when status is `skipped`.
 */
baseline_total?: (number | null)
/**
 * Current total after the change. Absent when status is `skipped`.
 */
current_total?: (number | null)
/**
 * Difference current - baseline. Absent when status is `skipped`.
 */
delta?: (number | null)
/**
 * Configured tolerance, interpreted per [`RegressionToleranceKind`].
 * Absent when status is `skipped`.
 */
tolerance?: (number | null)
/**
 * Interpretation of the tolerance value.
 */
tolerance_kind?: (RegressionToleranceKind | null)
/**
 * Whether the regression exceeded the tolerance.
 */
exceeded: boolean
/**
 * Only present when status is `skipped`.
 */
reason?: (string | null)
}
/**
 * Every narrowing or shaping request a run RECEIVED, keyed by name.
 *
 * Received, not failed: a request the run honoured is published with
 * `status: "applied"`, so a consumer can say "scoped to the change"
 * positively. Read an absent object as "nothing was asked for", never as
 * "nothing failed".
 *
 * Absent from an envelope whenever it is empty, so a run that was asked for
 * nothing is byte-identical to one produced before this object existed. An
 * empty object is never emitted: it would assert that something was asked and
 * all of it applied, which is a different and false claim.
 *
 * The names this build can emit are `changed-since`, `diff-filter`,
 * `package-baselines`, `sarif-file` and `group-filter`. The reasons are `git-missing`,
 * `not-a-repository`, `git-failed` and `invalid-ref` for `changed-since`,
 * `unknown-workspace`, `git-missing`, `not-a-repository` and `git-failed`
 * for `package-baselines`, `oversize`,
 * `unreadable`, `not-utf8`, `foreign-namespace` and `ambiguous-base` for
 * `diff-filter`, and `directory-create-failed`, `write-failed` and
 * `serialize-failed` for `sarif-file`. Every set is OPEN: a name a consumer
 * does not recognise means "some request", not an error.
 *
 * `sarif-file` reports a SECONDARY artifact rather than the scope of the
 * report it travels in, and it is in the same object for the same reason the
 * others are: the run was asked to do something and did something else, and
 * nothing in the primary report says so. Which of the two an entry is, every
 * entry says for itself: `affects` is `scope` for the narrowing requests and
 * `artifact` for this one. Select on it. A consumer that instead assumes the
 * whole object narrows the report tells its reader an unwritten SARIF file
 * widened the analysis, which is what `affects` exists to prevent.
 *
 * `scope_size` is emitted for `diff-filter`, in added lines, for
 * `changed-since`, in changed files that the run analyzed, and for
 * `group-filter`, in groups that the run kept. `package-baselines`
 * and `sarif-file` measure no scope; the applied package refs travel in
 * `package_baselines`. A consumer reads the unit off the name, so a name that
 * starts to measure its own scope in a later release needs no change here.
 *
 * `invalid-ref` is reachable only through the programmatic API. The
 * `--changed-since` flag validates its value before a run starts and fails
 * with exit 2 and an error document, which is the right side to err on: a
 * malformed ref is invalid input rather than a report of the wrong scope.
 */
export interface RequestOutcomes {
[k: string]: RequestOutcome
}
/**
 * One request's fate on one run.
 *
 * `reason` and `message` are present exactly when `status` is not `applied`,
 * and absent otherwise, so a consumer that only wants to know whether a
 * report is scoped reads `status` alone.
 */
export interface RequestOutcome {
status: RequestStatus
affects: RequestEffect
/**
 * What was asked, as the user spelled it: the git ref for
 * `changed-since`, the diff source label (`--diff-file pr.diff`,
 * `--diff-stdin`, `$FALLOW_DIFF_FILE build/pr.diff`, or
 * `diffFile pr.diff` for the programmatic option) for `diff-filter`,
 * `workspaces.changedSince` for `package-baselines`, the target path for
 * `sarif-file`, the comma-joined selector patterns for `group-filter`.
 * Echoed rather than normalised, so a
 * consumer must not join it to the project root the way it joins every
 * other path-shaped field.
 */
requested: string
/**
 * How much this request left in scope, in the request's own unit, when the
 * run applied it AND measured that scope. Absent otherwise, including on
 * every unapplied entry: a request that stood down narrowed nothing, so a
 * number there would describe a scope nobody applied.
 *
 * The unit belongs to the name. `diff-filter` counts added lines, which is
 * what its filter keeps a finding for. `changed-since` counts changed
 * files that the run analyzed: a changed file that discovery or an ignore
 * rule dropped does not count, so a change to a README only gives `0`. A
 * combined run counts a file that any of its analyses kept, because a
 * per-analysis `production` setting can give its analyses different
 * files. `group-filter` counts the groups that the run kept.
 * Read the unit off the name the entry is keyed under, never across names,
 * and read an absent member as "not measured" rather than as zero.
 *
 * The count is what the run INDEXED rather than the true total:
 * `diff-filter` indexes at most one million added lines and reports that
 * cap for a larger diff, so read any non-zero value as a lower bound.
 *
 * `0` is the case this member exists for: a request that applied over an
 * EMPTY scope. Every finding then filters out and the report reads clean,
 * so a consumer that sees no findings beside `scope_size: 0` learns that
 * nothing was analyzable rather than that the code is clean.
 */
scope_size?: (number | null)
/**
 * Why the request was not applied, as a kebab-case token. Present exactly
 * when `status` is not `applied`. The set is open per request name; the
 * names this build can emit are listed on [`RequestOutcomes`].
 */
reason?: (string | null)
/**
 * One sentence naming what was asked, what happened instead, and the next
 * step. Byte-identical to the stderr line for the same case, so a
 * consumer that renders this never contradicts a log a human read.
 * Present exactly when `status` is not `applied`.
 */
message?: (string | null)
}
/**
 * One applied baseline for an exact, project-relative workspace root.
 */
export interface PackageBaselineStatus {
/**
 * Workspace package root, relative to the analysis root with `/` separators.
 */
workspace_root: string
/**
 * Git ref used to select changed files in this package.
 */
reference: string
}
/**
 * A read-only follow-up command fallow surfaces from the current findings,
 * emitted as the top-level `next_steps` array on each command's JSON envelope.
 *
 * `next_steps` exists to point agents and humans sideways to fallow's adjacent
 * verification capabilities (trace, complexity breakdown, audit, workspace
 * scoping) that telemetry shows agents rarely discover, because they act on the
 * output in front of them rather than on reference docs.
 *
 * ## Two hard contracts
 *
 * 1. **Read-only.** A `next_step` NEVER suggests `fallow fix` or any mutating
 *    command. Fallow surfaces evidence and verification paths; deciding and
 *    applying the remediation is the agent's job.
 * 2. **Runnable, placeholder-free.** `command` is always runnable as-is. It
 *    never contains an angle-bracket placeholder (`<...>`); finding-derived
 *    values are filled in from a real, deterministically-selected finding, and
 *    any environment- or user-specific value that cannot be made concrete lives
 *    in `reason` instead. An agent can copy `command` and run it without edits.
 *
 * Both contracts are enforced by unit tests in
 * `crates/cli/src/report/suggestions.rs`.
 *
 * Note: a SEPARATE, unrelated `next_steps` field exists on the
 * `coverage setup` envelope (`CoverageSetupOutput.next_steps`) as a plain
 * `Vec<String>` of human onboarding steps. Consumers that read multiple
 * envelope kinds must route on the envelope's `kind` before interpreting a
 * `next_steps` field: on analysis envelopes it is `Vec<NextStep>` objects, on
 * `coverage setup` it is `Vec<String>`.
 */
export interface NextStep {
/**
 * Stable kebab-case key for machine dispatch and de-duplication
 * (for example `"trace-unused-export"`). Identity is stable across runs;
 * the `command` and `reason` strings may vary with the findings.
 */
id: string
/**
 * A runnable, read-only command string. Placeholder-free by contract.
 */
command: string
/**
 * One short phrase explaining why this helps. Carries any value that
 * cannot be made concrete in `command`.
 */
reason: string
}
/**
 * Wire-shape payload for `fallow dupes --format json` (the body that
 * flattens into the `DupesOutput` envelope and is also
 * emitted under the `dupes` / `duplication` key inside the combined and
 * audit envelopes).
 *
 * Mirrors [`DuplicationReport`] field-for-field, except `clone_groups`
 * and `clone_families` carry the typed wrapper envelopes instead of bare
 * findings, so the schema (and any TS / agent consumer) sees the typed
 * `actions[]` natively.
 */
export interface DupesReportPayload {
/**
 * All detected clone groups, each wrapped with typed actions.
 */
clone_groups: CloneGroupFinding[]
/**
 * Clone families, each wrapped with typed actions. Inner `groups`
 * inside each `CloneFamilyFinding` are themselves wrapped as
 * `CloneGroupFinding` entries carrying their own `actions[]` (and
 * optional audit-mode `introduced` flag), so JSON-Schema strict
 * consumers and TS consumers reading `clone_families[].groups[]` see
 * the same shape as the top-level `clone_groups[]` array (preserves
 * the issue #393 regression contract).
 */
clone_families: CloneFamilyFinding[]
/**
 * Mirrored directory pairs.
 */
mirrored_directories?: MirroredDirectory[]
stats: DuplicationStats
}
/**
 * Wire-shape envelope for a [`CloneGroup`] finding. Flattens the bare
 * group via `#[serde(flatten)]` and carries a typed `actions` array plus
 * the optional audit-mode `introduced` flag. The typed envelope replaced
 * the legacy JSON post-pass injection; a guard test in
 * `crates/cli/src/report/json.rs` rejects any reintroduced post-pass.
 */
export interface CloneGroupFinding {
/**
 * All instances where this duplicated code appears.
 */
instances: CloneInstance[]
/**
 * Number of tokens in the duplicated block.
 */
token_count: number
/**
 * Number of lines in the duplicated block.
 */
line_count: number
/**
 * Lowest all-pairs similarity for a near-miss clone group. Exact clone
 * groups omit this field.
 */
similarity?: number
/**
 * Stable content fingerprint, usually `dup:<8hex>` and widened on rare
 * report collisions. Addressable via `fallow dupes --trace dup:<fp>` (and
 * the `trace_clone` MCP tool) to deep-dive this group; shown alongside
 * each group in the human listing.
 */
fingerprint: string
/**
 * Maximum directory-tree or same-file line distance between instances.
 */
spread: number
/**
 * Best-effort human-readable name for the clone: the dominant repeated
 * identifier across the duplicated fragment (e.g. a shared `parseCsv`
 * function). `None` when the clone has no clear dominant name (generic or
 * tied identifiers); consumers then fall back to a file-based label. Lets
 * editors and agents label a clone by what it is rather than an opaque
 * ordinal.
 */
suggested_name?: (string | null)
/**
 * Suggested next steps: an `extract-shared` primary and a
 * `suppress-line` secondary. Always emitted (possibly empty for
 * forward-compat).
 */
actions: CloneGroupAction[]
/**
 * Set by the audit pass when this clone group is introduced relative
 * to the merge-base. `None` when serialized directly from Rust.
 */
introduced?: (AuditIntroduced | null)
/**
 * Set only by `fallow audit` under `--gate new-only`, on groups whose
 * `introduced` flag the gate demoted to `false`: why the demotion
 * happened. `None` everywhere else, including `fallow dupes
 * --format json` (issue #2220).
 */
demotion_reason?: (CloneDemotionReason | null)
}
/**
 * A single instance of duplicated code at a specific location.
 */
export interface CloneInstance {
/**
 * Path to the file containing this clone instance.
 */
file: string
/**
 * 1-based start line of the clone.
 */
start_line: number
/**
 * 1-based end line of the clone.
 */
end_line: number
/**
 * 0-based start column.
 */
start_col: number
/**
 * 0-based end column.
 */
end_col: number
/**
 * The actual source code fragment.
 *
 * Omitted from JSON when the caller asked for a location-only payload
 * (`fallow dupes --no-fragments`, and the MCP `find_dupes` default). The
 * five location fields above address the same text, so a consumer that
 * wants the source reads it from the file.
 */
fragment?: string
/**
 * Whether the file path is a symlink, or lies under a symlinked
 * directory, inside the project root. Omitted when `false`.
 *
 * A clone with a symlinked instance can be the same file under two
 * paths, not copied code. `duplicates.ignoreSymlinks` (or
 * `fallow dupes --ignore-symlinks`) removes these instances.
 */
is_symlink?: boolean
}
/**
 * Per-action wire shape attached to each `CloneGroupFinding` and
 * `AttributedCloneGroupFinding` (see `crates/api/src/dupes_output.rs`):
 * `extract-shared` plus `suppress-line`. The typed wrappers replaced the
 * legacy JSON post-pass injection that used to live in the CLI report layer.
 */
export interface CloneGroupAction {
type: CloneGroupActionType
/**
 * Whether `fallow fix` can auto-apply this action. Both variants are
 * manual today; the field is non-singleton so a future auto-applier
 * does not need a schema change.
 */
auto_fixable: boolean
/**
 * Human-readable description of the action.
 */
description: string
/**
 * The inline comment to insert (e.g.,
 * `// fallow-ignore-next-line code-duplication`). Present on
 * `suppress-line`; absent on `extract-shared`.
 */
comment?: (string | null)
}
/**
 * Wire-shape envelope for a [`CloneFamily`] finding.
 *
 * Unlike most `*Finding` wrappers this one is NOT `#[serde(flatten)]` over
 * the bare [`CloneFamily`], because the family's nested
 * `groups: Vec<CloneGroup>` field needs to carry the typed
 * `CloneGroupFinding` wrapper too (so every nested clone group gets its
 * own `actions[]` array, matching the legacy post-pass behavior; see issue
 * #393 regression test). The wire shape stays byte-identical to the
 * previous post-pass output. No `introduced` field because `fallow audit`
 * attributes clone groups (not families) when running against a base ref.
 */
export interface CloneFamilyFinding {
/**
 * The files involved in this family.
 */
files: string[]
/**
 * Clone groups belonging to this family, each wrapped with typed
 * `actions[]` so consumers that read `clone_families[].groups[]`
 * directly see the same shape as the top-level `clone_groups[]`.
 */
groups: CloneGroupFinding[]
/**
 * Total number of duplicated lines across all groups.
 */
total_duplicated_lines: number
/**
 * Total number of duplicated tokens across all groups.
 */
total_duplicated_tokens: number
/**
 * Refactoring suggestions for this family.
 */
suggestions: RefactoringSuggestion[]
/**
 * Suggested next steps: an `extract-shared` primary, one
 * `apply-suggestion` per `RefactoringSuggestion` on the family, and
 * a trailing `suppress-line`. Always emitted (possibly empty for
 * forward-compat).
 */
actions: CloneFamilyAction[]
}
/**
 * A refactoring suggestion for a clone family.
 */
export interface RefactoringSuggestion {
kind: RefactoringKind
/**
 * Human-readable description of the suggestion.
 */
description: string
/**
 * Estimated lines that could be eliminated.
 */
estimated_savings: number
}
/**
 * Per-action wire shape attached to each `CloneFamilyFinding`. Mirrors
 * the action types previously emitted by
 * `build_clone_family_actions`: `extract-shared`, one `apply-suggestion`
 * per `RefactoringSuggestion` on the family, and a trailing
 * `suppress-line`.
 */
export interface CloneFamilyAction {
type: CloneFamilyActionType
/**
 * Whether `fallow fix` can auto-apply this action. All three variants
 * are manual today.
 */
auto_fixable: boolean
/**
 * Human-readable description of the action.
 */
description: string
/**
 * Additional context. Present on `extract-shared` (explaining that
 * the family's clone groups share the same files); absent otherwise.
 */
note?: (string | null)
/**
 * The inline comment to insert (e.g.,
 * `// fallow-ignore-next-line code-duplication`). Present on
 * `suppress-line` only.
 */
comment?: (string | null)
}
/**
 * A detected mirrored directory pattern: two directory prefixes that contain
 * identical files (e.g., `src/` and `deno/lib/`).
 */
export interface MirroredDirectory {
/**
 * First directory path (lexically smaller).
 */
dir_a: string
/**
 * Second directory path.
 */
dir_b: string
/**
 * Filenames shared between the two directories.
 */
shared_files: string[]
/**
 * Total duplicated lines across all shared files.
 */
total_lines: number
}
/**
 * Aggregate duplication statistics.
 */
export interface DuplicationStats {
/**
 * Total files analyzed.
 */
total_files: number
/**
 * Files containing at least one clone instance.
 */
files_with_clones: number
/**
 * Total lines across all analyzed files.
 */
total_lines: number
/**
 * Lines that are part of at least one clone.
 */
duplicated_lines: number
/**
 * Total tokens across all analyzed files.
 */
total_tokens: number
/**
 * Tokens in redundant clone copies, excluding one retained copy per group.
 */
duplicated_tokens: number
/**
 * Number of clone groups the scoped corpus contains after filtering.
 * `--top` does not change it; compare it with `clone_groups_shown` on the
 * envelope to see how much of the corpus the array carries.
 */
clone_groups: number
/**
 * Number of clone families the scoped corpus contains after filtering.
 * `--top` truncates `clone_families[]` along with `clone_groups[]` but
 * does not change this counter; compare it with `clone_families_shown` on
 * the envelope to see how much of the corpus the array carries.
 */
clone_families: number
/**
 * Total clone instances across the scoped corpus after filtering.
 * `--top` does not change it.
 */
clone_instances: number
/**
 * Percentage of duplicated lines (0.0 to 100.0). `--top` does not change
 * this scoped corpus metric.
 */
duplication_percentage: number
/**
 * Number of clone groups hidden by `duplicates.minOccurrences`. Absent (or
 * `0`) when the filter is at its default of `2` and nothing was hidden.
 * This counter covers only the minimum-occurrence filter.
 */
clone_groups_below_min_occurrences?: number
/**
 * Number of clone groups hidden by `duplicates.ignoredClones`.
 */
clone_groups_ignored?: number
/**
 * Near-miss candidate comparisons skipped by bounded-work limits.
 */
near_candidates_skipped?: number
}
/**
 * Result of complexity analysis for reporting.
 */
export interface HealthReport {
/**
 * Functions and synthetic template entries exceeding complexity
 * thresholds, sorted by the --sort criteria. Each entry wraps its
 * inner `ComplexityViolation` payload (flattened on the wire) with
 * the typed `actions` list and an optional audit-mode `introduced`
 * flag.
 */
findings: HealthFinding[]
summary: HealthSummary
/**
 * The sections that this run produced, in a fixed order. A renderer
 * reads it to tell an empty section from a section that the run did not
 * produce: `findings` is the complexity list only when `complexity` is
 * in this array. The value set is OPEN (see [`HealthSection`]). Absent
 * in an envelope from a fallow version before this member, and on a
 * report that no health run built.
 */
sections?: (HealthSection[] | null)
/**
 * Configured threshold override states. Entries are emitted for active
 * exceptions, stale exceptions, and full-run no-match cleanup hints.
 */
threshold_overrides?: ThresholdOverrideState[]
/**
 * Project-wide vital signs (always computed from available data).
 */
vital_signs?: (VitalSigns | null)
/**
 * Project-wide health score (only populated with `--score`).
 */
health_score?: (HealthScore | null)
/**
 * Per-file health scores. Only present when --file-scores is used. Sorted
 * by risk-aware triage concern, combining low maintainability and high
 * CRAP risk. Zero-function files (barrels) are excluded by default.
 */
file_scores?: FileHealthScore[]
/**
 * Static coverage gaps.
 *
 * Populated when coverage gaps are explicitly requested, or when the
 * top-level `health` command allows config severity to surface them in the
 * default report.
 */
coverage_gaps?: (CoverageGaps | null)
/**
 * Located prop-drilling chains (React/Preact props forwarded unchanged
 * through 3+ pass-through components). Only present when the opt-in
 * `prop-drilling` rule is enabled (it defaults to off). Each entry carries
 * the source, every pass-through hop, and the consumer with file + line +
 * component, so CI / an agent can act. Surfaced alongside hotspots as a
 * graph-derived health signal.
 */
prop_drilling_chains?: PropDrillingChainFinding[]
/**
 * Hotspot entries combining git churn with complexity. Only present when
 * --hotspots is used. Sorted by score descending (highest risk first).
 * Each entry wraps its inner `HotspotEntry` payload (flattened on the
 * wire) with a typed `actions` list.
 */
hotspots?: HotspotFinding[]
/**
 * Hotspot analysis summary.
 *
 * Set whenever the run measured churn, which needs readable git history;
 * `--hotspots` adds the per-file [`hotspots`](Self::hotspots) listing
 * beside it rather than gating this summary.
 */
hotspot_summary?: (HotspotSummary | null)
/**
 * Runtime coverage findings from the paid sidecar (only populated with
 * `--runtime-coverage`).
 */
runtime_coverage?: (RuntimeCoverageReport | null)
/**
 * Combined coverage, runtime, complexity, and change-scope verdicts.
 */
coverage_intelligence?: (CoverageIntelligenceReport | null)
/**
 * Functions exceeding 60 LOC (very high risk). Only present when unit size
 * very-high-risk bin >= 3%. Sorted by line count descending.
 */
large_functions?: LargeFunctionEntry[]
/**
 * Ranked refactoring recommendations. Only present when --targets is used.
 * Sorted by efficiency (priority/effort) descending. Each entry wraps
 * its inner `RefactoringTarget` payload (flattened on the wire) with
 * a typed `actions` list.
 */
targets?: RefactoringTargetFinding[]
/**
 * Adaptive thresholds used for target scoring (only set with `--targets`).
 */
target_thresholds?: (TargetThresholds | null)
/**
 * Health trend comparison against a previous snapshot (only set with `--trend`).
 */
health_trend?: (HealthTrend | null)
/**
 * Audit breadcrumb explaining systemic action-array adjustments. Present
 * only when at least one adjustment was made (e.g., health finding
 * suppression hints omitted because a baseline is active). When --group-by
 * is active, each entry of `groups` may carry its own `actions_meta`
 * describing the same omission so per-group consumers do not need to walk
 * back to the report root.
 */
actions_meta?: (HealthActionsMeta | null)
/**
 * Optional framework-specific detector coverage. Present only when the
 * health run already needed the dead-code analysis output.
 */
framework_health?: (FrameworkHealthDiagnostics | null)
/**
 * Structural CSS analytics (specificity hotspots, `!important` density,
 * over-complex selectors, deep nesting). Present only with `--css`.
 */
css_analytics?: (CssAnalyticsReport | null)
/**
 * Styling-health score and letter grade: a SECOND health axis derived from
 * the CSS analytics (the design-system axis), orthogonal to the JS/TS code
 * `health_score`. Present only with `--css` (the same condition as
 * `css_analytics`), so a plain `fallow health` run is byte-unchanged. The
 * code score is never affected by this field.
 */
styling_health?: (StylingHealth | null)
/**
 * Advisory STYLING FINDINGS: the graduation of the descriptive css
 * candidates into first-class, severity-aware, suppressible findings
 * surfaced in `fallow audit`. Verdict-neutral by default (the rule defaults
 * to `warn`); the styling domain's OWN findings collection, not the dead-code
 * `AnalysisResults`. Present only with `--css`; empty is skipped so a plain
 * run is byte-unchanged.
 */
styling_findings?: StylingFinding[]
}
/**
 * Wire envelope for a single complexity finding.
 */
export interface HealthFinding {
/**
 * File path relative to the project root.
 */
path: string
/**
 * Function name, or a synthesized name for anonymous functions.
 */
name: string
/**
 * 1-based line the function starts on.
 */
line: number
/**
 * 1-based column the function starts on.
 */
col: number
/**
 * Cyclomatic complexity of the function.
 */
cyclomatic: number
/**
 * Cognitive complexity of the function.
 */
cognitive: number
/**
 * Lines of code in the function body.
 */
line_count: number
/**
 * Number of declared parameters.
 */
param_count: number
/**
 * Number of React hook calls in this function's body (`useState` /
 * `useEffect` / `useMemo` / `useCallback` / custom `use*`). Descriptive
 * hotspot context for React components; omitted when zero (non-React).
 */
react_hook_count?: number
/**
 * Deepest JSX element nesting reached in this function's body. Descriptive
 * hotspot context; omitted when zero (renders no JSX).
 */
react_jsx_max_depth?: number
/**
 * Number of props destructured from this component's first parameter.
 * Descriptive hotspot context; omitted when zero.
 */
react_prop_count?: number
/**
 * Per-kind React hook breakdown (state/effect/memo/callback/custom) plus
 * the max `useEffect` dependency-array arity, derived from the cached
 * `hook_uses` IR at the health layer. Descriptive refinement of
 * `react_hook_count`; present only when at least one component-scope hook
 * was attributed, so non-React findings stay byte-identical.
 */
react_hook_profile?: (ReactHookProfile | null)
exceeded: ExceededThreshold
severity: FindingSeverity
/**
 * Gate severity after the `complexity-*` rules and their
 * `overrides[].rules` entries: `error` fails the run, `warn` does not.
 * The most severe rule of the kinds in `exceeded` wins. It is separate
 * from the band in `severity`, which ranks the finding and does not gate
 * it. Absent in reports from older versions.
 */
effective_severity?: (EffectiveSeverity | null)
/**
 * CRAP score (change risk anti-pattern), when coverage data exists.
 */
crap?: (number | null)
/**
 * Test coverage percentage (0-100) backing the CRAP score.
 */
coverage_pct?: (number | null)
/**
 * Coverage tier bucket.
 *
 * Derived from `coverage_pct` when coverage was measured. When
 * `coverage_source` is estimated, `coverage_pct` is absent and the tier
 * describes the static estimate behind the CRAP score rather than an
 * observation, so read the two fields together.
 */
coverage_tier?: (CoverageTier | null)
/**
 * Provenance of the coverage signal.
 */
coverage_source?: (CoverageSource | null)
/**
 * Component file the inherited coverage estimate came from, for
 * component-inherited coverage.
 */
inherited_from?: (string | null)
/**
 * Aggregate of the enclosing component's findings, when rolled up.
 */
component_rollup?: (ComponentRollup | null)
/**
 * Per-decision-point complexity breakdown explaining WHICH constructs drove
 * the cyclomatic and cognitive scores. Populated only when the caller opts
 * in via `health --complexity-breakdown`; empty (and omitted from JSON)
 * otherwise so default and CI output stay lean.
 */
contributions?: ComplexityContribution[]
/**
 * Resolved thresholds used for this finding when a config override changed
 * at least one ceiling. Omitted for findings using global thresholds.
 */
effective_thresholds?: (HealthEffectiveThresholds | null)
/**
 * Source of the effective thresholds. Omitted when thresholds are global.
 */
threshold_source?: (ThresholdSource | null)
/**
 * Machine-actionable fix and suppress hints.
 */
actions: HealthFindingAction[]
/**
 * Audit-mode flag indicating whether the finding is new versus the base
 * snapshot.
 */
introduced?: (boolean | null)
}
/**
 * Per-component React hook profile derived from the cached `hook_uses` IR at
 * the health layer. Descriptive context that refines the bare
 * [`ComplexityViolation::react_hook_count`] headline with a per-kind breakdown
 * and the maximum `useEffect` dependency-array arity.
 *
 * Attached only when at least one component-scope hook was attributed to the
 * function, so non-React findings stay byte-identical on the wire. The
 * per-kind counts cover hooks recorded by the React visitor (calls inside an
 * identified component); a `use*` call inside a plain helper function is
 * counted in `react_hook_count` but NOT here, so the breakdown can sum to LESS
 * than `react_hook_count`. `react_hook_count` remains the headline total; this
 * is an additive refinement.
 */
export interface ReactHookProfile {
/**
 * `useState` call count attributed to this component.
 */
state: number
/**
 * `useEffect` call count attributed to this component.
 */
effect: number
/**
 * `useMemo` call count attributed to this component.
 */
memo: number
/**
 * `useCallback` call count attributed to this component.
 */
callback: number
/**
 * Custom `use*` hook call count attributed to this component.
 */
custom: number
/**
 * Largest `useEffect` dependency-array arity over the attributed effects
 * that carry a literal deps array. `None` when no attributed `useEffect`
 * had a literal array (absent or non-literal deps; ADR-001 syntactic-only,
 * so absence does NOT mean "no coupling").
 */
max_effect_dep_arity?: (number | null)
}
/**
 * Component-level aggregate attached to a template complexity finding,
 * pairing the template's scores with the worst class-side function.
 */
export interface ComponentRollup {
/**
 * Component name.
 */
component: string
/**
 * Name of the worst-scoring function in the component class.
 */
class_worst_function: string
/**
 * Cyclomatic complexity of that worst class function.
 */
class_cyclomatic: number
/**
 * Cognitive complexity of that worst class function.
 */
class_cognitive: number
/**
 * Template file path relative to the project root.
 */
template_path: string
/**
 * Cyclomatic complexity of the template.
 */
template_cyclomatic: number
/**
 * Cognitive complexity of the template.
 */
template_cognitive: number
}
/**
 * A single complexity increment, located at its source line/column.
 *
 * `weight` is the amount this construct added to `metric`; for nested
 * cognitive increments `weight == 1 + nesting`. Consumers that render inline
 * (the VS Code editor breakdown) group contributions by `line` and sum the
 * weights, deferring the per-kind list to a hover.
 */
export interface ComplexityContribution {
/**
 * 1-based line number where the construct begins.
 */
line: number
/**
 * 0-based byte column where the construct begins.
 */
col: number
metric: ComplexityMetric
kind: ComplexityContributionKind
/**
 * The amount added to `metric` at this site (`1 + nesting` for nested
 * cognitive increments, otherwise `1`).
 */
weight: number
/**
 * The nesting depth at the increment site (`0` when not nested). Lets a
 * consumer explain a cognitive `+3` as "+1 base, +2 nesting".
 */
nesting: number
}
/**
 * Resolved thresholds used to evaluate a health finding.
 */
export interface HealthEffectiveThresholds {
/**
 * Effective cyclomatic-complexity ceiling for the matched file.
 */
max_cyclomatic: number
/**
 * Effective cognitive-complexity ceiling for the matched file.
 */
max_cognitive: number
/**
 * Effective CRAP-score ceiling for the matched file.
 */
max_crap: number
/**
 * Effective unit-size ceiling (maximum function length in lines) for the
 * matched file, after applying any `thresholdOverrides` on top of the
 * global `health.maxUnitSize` default.
 */
max_unit_size: number
}
/**
 * Suggested action attached to a [`ComplexityViolation`].
 *
 * Each complexity finding carries an array of these on the JSON wire
 * (`findings[].actions[]`). The action selector in
 * `crates/cli/src/report/json.rs::build_health_finding_actions` picks the
 * primary action based on which thresholds triggered the finding and the
 * bucketed coverage tier. See [`HealthFindingActionType`] for the full
 * discriminant list.
 *
 * `note`, `comment`, and `placement` are populated per-variant: refactor
 * actions carry a `note`, suppress-line / suppress-file actions carry
 * `comment` plus `placement`, and the coverage-leaning actions
 * (`add-tests`, `increase-coverage`) carry only `note`.
 *
 * [`ComplexityViolation`]: ../../fallow-output/src/health_scores.rs
 */
export interface HealthFindingAction {
type: HealthFindingActionType
/**
 * Whether `fallow fix` can auto-apply this action. Today every health
 * finding action is manual, but the field is non-singleton so a future
 * auto-applier (e.g., an LLM-driven `refactor-function` worker) does
 * not need a schema change.
 */
auto_fixable: boolean
/**
 * Human-readable description of the action.
 */
description: string
/**
 * Additional context (e.g., the canonical CRAP formula, or a hint
 * about which branch type to extract). Present on most action types;
 * dropped only when the description carries the full ask.
 */
note?: (string | null)
/**
 * The inline comment to insert (e.g.,
 * `// fallow-ignore-next-line complexity` or
 * `<!-- fallow-ignore-file complexity -->`). Present on
 * `suppress-line` and `suppress-file` action variants.
 */
comment?: (string | null)
/**
 * Where to insert the suppress comment
 * (e.g., `above-function-declaration`, `above-angular-decorator`,
 * `above-template-anchor-line`, `above-component-worst-method`, or
 * `top-of-template`). Present on `suppress-line` and `suppress-file`
 * action variants. `above-template-anchor-line` is used for
 * single-file-component markup (`.svelte`, `.vue`, `.astro`), where the
 * synthetic `<template>` unit is anchored at its first contributing
 * construct rather than at the top of the file, so the comment belongs on
 * the line immediately preceding the reported line.
 */
placement?: (string | null)
/**
 * Project-relative path the action should target when the finding's
 * remediation lives in a different file from where the finding is
 * anchored. Currently populated on the `increase-coverage` action for
 * synthetic Angular `<template>` findings whose CRAP is inherited from
 * the owning `.component.ts`: the action points at the component file
 * (where the user actually adds tests) rather than the `.html` template
 * (where the finding is anchored but which is not directly testable).
 * Absent when the action's target is the finding's own file.
 */
target_path?: (string | null)
}
/**
 * Summary statistics for the health report.
 */
export interface HealthSummary {
/**
 * Files included in the health analysis.
 */
files_analyzed: number
/**
 * Functions and template units checked for threshold findings across the
 * analyzed files. Synthetic module-scope units are excluded. Cyclomatic
 * aggregates include module units too; `vital_signs.cyclomatic_population`
 * reports the disjoint authored-function, module, and template populations
 * behind those aggregates.
 */
functions_analyzed: number
/**
 * Functions exceeding at least one complexity or CRAP threshold.
 */
functions_above_threshold: number
/**
 * Global cyclomatic-complexity ceiling for this run.
 */
max_cyclomatic_threshold: number
/**
 * Global cognitive-complexity ceiling for this run.
 */
max_cognitive_threshold: number
/**
 * Global CRAP-score ceiling for this run.
 */
max_crap_threshold: number
/**
 * Effective global unit-size ceiling (`health.maxUnitSize`, maximum
 * function length in lines) for this run. Sits alongside the other three
 * `max_*_threshold` siblings so a consumer reading the summary sees every
 * configured threshold. Per-file `thresholdOverrides` are not reflected
 * here; this is the global default.
 */
max_unit_size_threshold: number
/**
 * Files with a computed maintainability score; absent when file scoring
 * did not run.
 */
files_scored?: (number | null)
/**
 * Mean maintainability index over scored files (0-100).
 */
average_maintainability?: (number | null)
/**
 * Coverage model behind the CRAP scores, when coverage was used.
 */
coverage_model?: (CoverageModel | null)
/**
 * Input format of the measured coverage (`istanbul` or `v8`). Present
 * only with `coverage_model: "istanbul"`.
 */
coverage_input_format?: (CoverageInputFormat | null)
/**
 * Whether CRAP findings mix coverage sources.
 */
coverage_source_consistency?: (CoverageSourceConsistency | null)
/**
 * Functions matched against the Istanbul coverage file, in Istanbul mode.
 */
istanbul_matched?: (number | null)
/**
 * Functions in the Istanbul coverage file, in Istanbul mode.
 */
istanbul_total?: (number | null)
/**
 * Analyzed files the Istanbul coverage file carried an entry for.
 * Read against `istanbul_files_total`, this separates a coverage file
 * that did not join from code the coverage file says nothing ran in.
 */
istanbul_files_matched?: (number | null)
/**
 * Files described by the Istanbul coverage file, joined or not.
 */
istanbul_files_total?: (number | null)
/**
 * Findings with critical severity.
 */
severity_critical_count: number
/**
 * Findings with high severity.
 */
severity_high_count: number
/**
 * Findings with moderate severity.
 */
severity_moderate_count: number
/**
 * Baseline staleness data, present only when a baseline was loaded.
 */
baseline_staleness?: (BaselineStaleness | null)
}
/**
 * Report entry describing whether a threshold override is active, stale, or
 * no longer matching any analyzed file or function.
 */
export interface ThresholdOverrideState {
status: ThresholdOverrideStatus
/**
 * Index of the entry in the configured `thresholdOverrides` array.
 * Several rows can share one index when the override participates in more
 * than one dimension; group on this to count configured overrides.
 */
override_index: number
dimension: ThresholdOverrideDimension
/**
 * Dimensions the matched unit still breaches despite this override,
 * whether or not this override configures their ceilings. Non-empty means
 * raising the ceiling did not settle the matter: a complexity or CRAP
 * finding survived, or the unit is still longer than the resolved
 * `maxUnitSize`, which keeps it in the large-function list without
 * emitting a finding of its own.
 */
outstanding?: ThresholdOverrideDimension[]
/**
 * Matched file path, when the override matched one.
 */
path?: (string | null)
/**
 * Matched function name, for function-scoped overrides.
 */
function?: (string | null)
/**
 * 1-based line of the matched unit. Absent on `no_match` rows, which
 * describe an entry that matched nothing. Name alone is not an identity:
 * one file can hold several units sharing a name, so this pairs with
 * `col` to keep their rows distinct (issue #2163).
 */
line?: (number | null)
/**
 * 0-based byte column of the matched unit. Absent on `no_match` rows.
 */
col?: (number | null)
configured_thresholds: HealthConfiguredThresholds
effective_thresholds: HealthEffectiveThresholds
/**
 * Current complexity metrics of the matched code, when matched.
 */
metrics?: (ThresholdOverrideMetrics | null)
/**
 * Human-readable explanation of the status.
 */
reason?: (string | null)
}
/**
 * Threshold values configured by a single override entry.
 */
export interface HealthConfiguredThresholds {
/**
 * Cyclomatic ceiling set by the override, when it sets one.
 */
max_cyclomatic?: (number | null)
/**
 * Cognitive ceiling set by the override, when it sets one.
 */
max_cognitive?: (number | null)
/**
 * CRAP ceiling set by the override, when it sets one.
 */
max_crap?: (number | null)
/**
 * Unit-size ceiling set by the override, when it sets one.
 */
max_unit_size?: (number | null)
}
/**
 * Current complexity metrics for a matched threshold override entry.
 */
export interface ThresholdOverrideMetrics {
/**
 * Current cyclomatic complexity of the matched function.
 */
cyclomatic: number
/**
 * Current cognitive complexity of the matched function.
 */
cognitive: number
/**
 * Current CRAP score, when coverage data exists.
 */
crap?: (number | null)
/**
 * Measured line count of the matched unit. Present on complexity rows,
 * where `maxUnitSize` participates in the dimension; absent on CRAP rows
 * and `<component>` rollup rows, which are never scored on unit size.
 */
line_count?: (number | null)
}
/**
 * Project-wide vital signs: a fixed set of metrics for trend tracking.
 *
 * Metrics are `Option` when the data source was not available in the current run
 * (e.g., `duplication_pct` is `None` unless the duplication pipeline was run,
 * `hotspot_count` is `None` without git history).
 */
export interface VitalSigns {
/**
 * Percentage of files not reachable from any entry point.
 */
dead_file_pct?: (number | null)
/**
 * Percentage of exports never imported by other modules.
 */
dead_export_pct?: (number | null)
/**
 * Average cyclomatic complexity across authored functions, module-scope
 * units, and template units. See `cyclomatic_population` for the denominator.
 */
avg_cyclomatic: number
/**
 * Percentage of complexity units at or above the critical cyclomatic threshold.
 * Used by the scale-invariant health score.
 */
critical_complexity_pct?: (number | null)
/**
 * 90th percentile cyclomatic complexity across the same unit population.
 */
p90_cyclomatic: number
/**
 * Population behind the cyclomatic mean, percentile, and critical share.
 * Present on current analyses; absent on older saved snapshots.
 */
cyclomatic_population?: (CyclomaticPopulation | null)
/**
 * Code duplication percentage (None if duplication pipeline was not run).
 */
duplication_pct?: (number | null)
/**
 * Number of hotspot files (score >= 50). None if git history unavailable.
 */
hotspot_count?: (number | null)
/**
 * Number of files in the top 1% of the within-project hotspot ranking.
 */
hotspot_top_pct_count?: (number | null)
/**
 * Average maintainability index across all scored files (0-100).
 */
maintainability_avg?: (number | null)
/**
 * Percentage of scored files with maintainability index below 70. Null if
 * file scores were not computed.
 */
maintainability_low_pct?: (number | null)
/**
 * Number of unused dependencies (dependencies + devDependencies + optional).
 */
unused_dep_count?: (number | null)
/**
 * Unused dependencies per 1,000 files. Null if dead code analysis did not
 * run.
 */
unused_deps_per_k_files?: (number | null)
/**
 * Number of circular dependency chains.
 */
circular_dep_count?: (number | null)
/**
 * Circular dependency chains per 1,000 files. Null if dead code analysis
 * did not run.
 */
circular_deps_per_k_files?: (number | null)
/**
 * Raw counts backing the percentages (for orientation header display).
 */
counts?: (VitalSignsCounts | null)
/**
 * Function size risk profile: percentage of functions in each size bin.
 */
unit_size_profile?: (RiskProfile | null)
/**
 * Functions above 60 LOC per 1,000 functions. Null if no functions
 * analyzed.
 */
functions_over_60_loc_per_k?: (number | null)
/**
 * Parameter count risk profile: percentage of functions in each param bin.
 */
unit_interfacing_profile?: (RiskProfile | null)
/**
 * 95th percentile fan-in across all files. Null if file scores not
 * computed.
 */
p95_fan_in?: (number | null)
/**
 * Percentage of files with fan-in above the project's p95 threshold.
 */
coupling_high_pct?: (number | null)
/**
 * Number of located prop-drilling chains (React/Preact props forwarded
 * unchanged through 3+ pass-through components). `None` unless the opt-in
 * `prop-drilling` rule is enabled (it defaults to off), so the small capped
 * penalty and the hotspot surface are dormant by default.
 */
prop_drilling_chain_count?: (number | null)
/**
 * The deepest located prop-drilling chain's depth (forwarding hops). `None`
 * when no chains were found or the rule is off. Descriptive context only.
 */
prop_drilling_max_depth?: (number | null)
/**
 * 95th-percentile DISTINCT-PARENTS render fan-in across React/Preact
 * components (the component-graph analogue of `p95_fan_in`, which percentiles
 * per-FILE module fan-in). `None` on non-React runs. Descriptive
 * blast-radius context, NOT a gate. Mirrors `compute_coupling_concentration`.
 */
p95_render_fan_in?: (number | null)
/**
 * Percentage of components whose render fan-in exceeds the project's
 * `max(p95, 10)` threshold (reuses the coupling-concentration floor; NO new
 * tunable constant). `None` on non-React runs. Mirrors `coupling_high_pct`.
 */
render_fan_in_high_pct?: (number | null)
/**
 * The single highest DISTINCT-PARENTS count across all components (the
 * headline blast-radius number: the most distinct render LOCATIONS any one
 * component is rendered from, the honest edit-ripple count). `render_sites`
 * (incl. repeats) is secondary per-component context, never the headline.
 * `None` on non-React runs. Descriptive context, no threshold.
 */
max_render_fan_in?: (number | null)
/**
 * The highest-fan-in React/Preact components, located (component name +
 * project-relative path + render-site / distinct-parent counts), sorted by
 * distinct parents (the honest headline axis) descending, tie-broken on
 * render sites descending, and capped at a small N. Lets a consumer see
 * WHICH component carries the headline `max_render_fan_in`, not just the
 * number. Empty (and omitted from JSON) on non-React runs, so the contract
 * stays byte-identical there. Descriptive blast-radius context, NOT a gate.
 */
top_render_fan_in?: RenderFanInTopComponent[]
/**
 * Total lines of code across all parsed modules.
 */
total_loc?: number
}
/**
 * Disjoint populations feeding the cyclomatic distribution. Counts and sums
 * across all three groups reconstruct its weighted mean. Module-scope units
 * contribute to aggregates only and do not produce function findings.
 */
export interface CyclomaticPopulation {
functions: CyclomaticUnitPopulation
modules: CyclomaticUnitPopulation
templates: CyclomaticUnitPopulation
}
/**
 * Cyclomatic measurements for one kind of complexity unit.
 */
export interface CyclomaticUnitPopulation {
/**
 * Number of units measured, including those below finding thresholds.
 */
count: number
/**
 * Sum of cyclomatic complexity, before rounding or threshold filtering.
 */
sum: number
/**
 * Highest cyclomatic complexity, or null when this population is empty.
 */
max?: (number | null)
}
/**
 * Raw counts backing the vital signs percentages.
 *
 * Stored alongside `VitalSigns` in snapshots so that Phase 2b trend reporting
 * can decompose percentage changes into numerator vs denominator shifts.
 */
export interface VitalSignsCounts {
/**
 * Total number of discovered source files.
 */
total_files: number
/**
 * Total number of exports across all files.
 */
total_exports: number
/**
 * Number of unreachable files.
 */
dead_files: number
/**
 * Number of unused exports.
 */
dead_exports: number
/**
 * Lines inside detected clones; absent when duplication did not run.
 */
duplicated_lines?: (number | null)
/**
 * Lines scanned by duplication; absent when duplication did not run.
 */
total_lines?: (number | null)
/**
 * Files with a computed maintainability score; absent when file scoring
 * did not run.
 */
files_scored?: (number | null)
/**
 * Total declared dependencies across manifest sections.
 */
total_deps: number
}
/**
 * Risk profile: percentage of functions in each risk bin.
 *
 * Bins are defined by thresholds that depend on the measured property:
 * - **Unit size**: low risk (1-15 LOC), medium risk (16-30), high risk (31-60), very high risk (>60)
 * - **Unit interfacing**: low risk (0-2 params), medium risk (3-4), high risk (5-6), very high risk (>=7)
 *
 * Percentages sum to approximately 100.0 (subject to rounding).
 */
export interface RiskProfile {
/**
 * Percentage of functions in the low-risk bin.
 */
low_risk: number
/**
 * Percentage of functions in the medium-risk bin.
 */
medium_risk: number
/**
 * Percentage of functions in the high-risk bin.
 */
high_risk: number
/**
 * Percentage of functions in the very-high-risk bin.
 */
very_high_risk: number
}
/**
 * One located high-fan-in React/Preact component for the descriptive
 * `top_render_fan_in` blast-radius list on [`VitalSigns`].
 *
 * The component-graph analogue of a high-fan-in module: `distinct_parents` is
 * the HEADLINE axis (the honest count of distinct parent components / render
 * LOCATIONS that render this component), `render_sites` is secondary "incl.
 * repeats" context (every JSX render SITE, so a single parent rendering one
 * child five times is five sites but one parent). Undercount-safe like the
 * underlying metric: a child rendered via a JSX spread / dynamic /
 * member-expression tag resolves to no component, so a true high-fan-in
 * component can only be undersold.
 */
export interface RenderFanInTopComponent {
/**
 * The component name.
 */
component: string
/**
 * Project-relative path of the file declaring the component. Serialized with
 * forward slashes (same serializer the other relativized health paths use).
 */
path: string
/**
 * Total JSX render SITES that resolve to this component across the project.
 * SECONDARY "incl. repeats" context, not the headline (see `distinct_parents`).
 */
render_sites: number
/**
 * Distinct `(parent_file, parent_component)` keys that render this component.
 * The HEADLINE blast-radius axis: distinct render LOCATIONS.
 */
distinct_parents: number
}
/**
 * Overall project health score: 100 minus capped per-category penalties.
 */
export interface HealthScore {
/**
 * Score formula version; see [`HEALTH_SCORE_FORMULA_VERSION`].
 */
formula_version: number
/**
 * Health score in `[0, 100]`; higher is healthier.
 */
score: number
/**
 * Letter grade from [`letter_grade`] (A>=85, B>=70, C>=55, D>=40, F<40).
 */
grade: string
penalties: HealthScorePenalties
}
/**
 * Per-component penalty breakdown for the health score.
 */
export interface HealthScorePenalties {
/**
 * Points subtracted for unreachable files; absent when dead-code data
 * was not available.
 */
dead_files?: (number | null)
/**
 * Points subtracted for unused exports; absent when dead-code data was
 * not available.
 */
dead_exports?: (number | null)
/**
 * Points subtracted for overall complexity load.
 */
complexity: number
/**
 * Points subtracted for the complexity tail (v1: p90 cyclomatic; v2:
 * critical-complexity density).
 */
p90_complexity: number
/**
 * Points subtracted for low maintainability-index files; absent when
 * file scores were not computed.
 */
maintainability?: (number | null)
/**
 * Points subtracted for churn-times-complexity hotspots; absent without
 * git history.
 */
hotspots?: (number | null)
/**
 * Points subtracted for unused dependencies; absent when dead-code data
 * was not available.
 */
unused_deps?: (number | null)
/**
 * Points subtracted for circular dependency chains; absent when
 * dead-code data was not available.
 */
circular_deps?: (number | null)
/**
 * Penalty for oversized functions, computed against fixed calibration
 * (very-high-risk bin edge at 60 LOC). Deliberately independent of
 * `health.maxUnitSize`, which filters the large-functions findings list
 * only; raising that threshold empties the list without moving this
 * penalty. `health.ignore` removes files from the score entirely.
 */
unit_size?: (number | null)
/**
 * Points subtracted for fan-in coupling concentration; absent when the
 * module graph was not available.
 */
coupling?: (number | null)
/**
 * Points subtracted for duplicated code; absent when the duplication
 * pipeline did not run.
 */
duplication?: (number | null)
/**
 * Small capped penalty for prop-drilling chains. `None` unless the opt-in
 * `prop-drilling` rule is enabled; sized like the coupling penalty (~5pt cap).
 */
prop_drilling?: (number | null)
}
/**
 * Per-file health score combining complexity, coupling, and dead code metrics.
 */
export interface FileHealthScore {
/**
 * File path relative to the project root.
 */
path: string
/**
 * Modules importing this file.
 */
fan_in: number
/**
 * Modules this file imports.
 */
fan_out: number
/**
 * Unused exports as a fraction of the file's exports, in `[0, 1]`.
 */
dead_code_ratio: number
/**
 * Total cyclomatic complexity per line of code.
 */
complexity_density: number
/**
 * Maintainability index (0-100); higher is healthier.
 */
maintainability_index: number
/**
 * Summed cyclomatic complexity over all units, including module and template scope.
 */
total_cyclomatic: number
/**
 * Summed cognitive complexity over all units, including module and template scope.
 */
total_cognitive: number
/**
 * Complexity units in the file, including synthetic module and template units.
 */
function_count: number
/**
 * Lines of code in the file.
 */
lines: number
/**
 * Highest CRAP score among the file's functions. Always the raw measured
 * value; threshold overrides never rewrite it.
 */
crap_max: number
/**
 * Functions whose rounded CRAP score meets or exceeds their effective
 * ceiling, resolved from `health.thresholdOverrides` over the global
 * `maxCrap` / `--max-crap` value. Zero when CRAP enforcement is disabled
 * (global ceiling `0`).
 */
crap_above_threshold: number
/**
 * Functions whose rounded CRAP score is at or above the canonical 30.0
 * baseline but below their effective ceiling: the count the configuration
 * let through. Stays `0` when the effective ceiling is stricter than 30.
 * When CRAP enforcement is disabled (global ceiling `0`), counts every
 * function at or above the canonical baseline. Omitted when zero.
 */
crap_exempted?: number
/**
 * Lowest effective CRAP ceiling among the file's functions, present only
 * when it differs from the run global (`summary.max_crap_threshold`).
 * Consumers fall back to `summary.max_crap_threshold` when absent.
 */
crap_effective_threshold?: (number | null)
}
/**
 * Static test coverage gaps derived from the module graph. Shows runtime files
 * and exports with no test dependency path.
 */
export interface CoverageGaps {
summary: CoverageGapSummary
/**
 * Runtime files with no test dependency path. Each entry carries its
 * own `actions` array via [`UntestedFileFinding`].
 */
files?: UntestedFileFinding[]
/**
 * Runtime exports with no test-reachable reference chain. Each entry
 * carries its own `actions` array via [`UntestedExportFinding`].
 */
exports?: UntestedExportFinding[]
}
/**
 * Aggregate coverage-gap counters for the current analysis scope.
 */
export interface CoverageGapSummary {
/**
 * Runtime-reachable files in scope.
 */
runtime_files: number
/**
 * Runtime-reachable files also reachable from tests.
 */
covered_files: number
/**
 * Percentage of runtime files that are test-reachable.
 */
file_coverage_pct: number
/**
 * Runtime files with no test dependency path.
 */
untested_files: number
/**
 * Runtime exports with no test-reachable reference chain.
 */
untested_exports: number
}
/**
 * Wire-shape envelope for an [`UntestedFile`] finding. Carries the bare
 * [`UntestedFile`] flattened in plus a typed `actions` array. The action
 * vec is computed at construction time using a project-root-relative path
 * so descriptions match `strip_root_prefix`'s post-pass output on the inner
 * `path` field. Schemars derives the merged shape natively; this retires
 * the `augment_finding_definition` graft for `UntestedFile`.
 */
export interface UntestedFileFinding {
/**
 * Absolute file path.
 */
path: string
/**
 * Number of value exports declared by the file.
 */
value_export_count: number
/**
 * Suggested next steps: an `add-tests` primary and a `suppress-file`
 * secondary. Always emitted for the current wire contract.
 */
actions: UntestedFileAction[]
}
/**
 * Suggested action attached to an [`UntestedFile`] coverage-gap finding.
 *
 * `build_untested_file_actions` emits a two-entry array on every
 * untested-file item: an `add-tests` primary action (scaffold tests for
 * the runtime file) and a `suppress-file` action
 * (`// fallow-ignore-file coverage-gaps`). Both variants share the same
 * struct shape; the field that is populated (`note` for `add-tests`,
 * `comment` for `suppress-file`) depends on the `kind`.
 *
 * [`UntestedFile`]: ../../fallow-output/src/health_coverage_gaps.rs
 */
export interface UntestedFileAction {
type: UntestedFileActionType
/**
 * Whether `fallow fix` can auto-apply this action. Today both
 * variants are manual.
 */
auto_fixable: boolean
/**
 * Human-readable description of the action.
 */
description: string
/**
 * Additional context for the `add-tests` variant (explains why no
 * test path reaches this file). Absent on `suppress-file`.
 */
note?: (string | null)
/**
 * The file-level comment to insert. Present on `suppress-file`
 * (`// fallow-ignore-file coverage-gaps`). Absent on `add-tests`.
 */
comment?: (string | null)
}
/**
 * Wire-shape envelope for an [`UntestedExport`] finding. Same pattern as
 * [`UntestedFileFinding`]: flattens the bare finding and carries a typed
 * `actions` array computed at construction time.
 */
export interface UntestedExportFinding {
/**
 * Absolute file path.
 */
path: string
/**
 * Export name.
 */
export_name: string
/**
 * 1-based source line.
 */
line: number
/**
 * 0-based source column.
 */
col: number
/**
 * Suggested next steps: an `add-test-import` primary and a
 * `suppress-file` secondary.
 */
actions: UntestedExportAction[]
}
/**
 * Suggested action attached to an [`UntestedExport`] coverage-gap
 * finding.
 *
 * `build_untested_export_actions` emits a two-entry array on every
 * untested-export item: an `add-test-import` primary action (import the
 * export from a test-reachable module) and a `suppress-file` action
 * (`// fallow-ignore-file coverage-gaps`). The export-specific variant
 * `add-test-import` reflects that a test-reachable reference chain, not
 * just any test coverage, is what closes the gap.
 *
 * [`UntestedExport`]: ../../fallow-output/src/health_coverage_gaps.rs
 */
export interface UntestedExportAction {
type: UntestedExportActionType
/**
 * Whether `fallow fix` can auto-apply this action. Today both
 * variants are manual.
 */
auto_fixable: boolean
/**
 * Human-readable description of the action.
 */
description: string
/**
 * Additional context for the `add-test-import` variant (explains the
 * runtime-reachable / test-unreachable asymmetry). Absent on
 * `suppress-file`.
 */
note?: (string | null)
/**
 * The file-level comment to insert. Present on `suppress-file`
 * (`// fallow-ignore-file coverage-gaps`). Absent on
 * `add-test-import`.
 */
comment?: (string | null)
}
/**
 * Wire envelope for a single hotspot entry.
 *
 * Flattens [`HotspotEntry`] for wire continuity and adds the typed
 * `actions` list. The `#[serde(flatten)]` keeps each `hotspots[]` item
 * byte-identical to the pre-wrapper shape: inner fields (`path`,
 * `score`, `commits`, `weighted_commits`, ...) sit at the top level
 * alongside `actions`. Optional inner fields (`ownership`,
 * `is_test_path`) keep their original `skip_serializing_if` behaviour
 * because serde applies the flatten before the parent serializer runs.
 *
 * Construct via [`HotspotFinding::with_actions`] in the typical health
 * pipeline (the typed action builder operates on the inner
 * [`HotspotEntry`]) or via [`HotspotFinding::from`] for fixture and
 * test code.
 */
export interface HotspotFinding {
/**
 * File path relative to the project root.
 */
path: string
/**
 * Churn-times-complexity hotspot score; higher is riskier.
 */
score: number
/**
 * Commits touching the file in the analysis window.
 */
commits: number
/**
 * Recency-weighted commit count.
 */
weighted_commits: number
/**
 * Lines added to the file in the analysis window.
 */
lines_added: number
/**
 * Lines deleted from the file in the analysis window.
 */
lines_deleted: number
/**
 * Total cyclomatic complexity per line of code.
 */
complexity_density: number
/**
 * Modules importing this file.
 */
fan_in: number
trend: ChurnTrend
/**
 * Ownership metrics, when ownership analysis ran.
 */
ownership?: (OwnershipMetrics | null)
/**
 * True for files matched by test-path patterns; omitted when false.
 */
is_test_path?: boolean
/**
 * Machine-actionable refactor and review hints. Always populated;
 * the list never empties because the action selector unconditionally
 * emits `refactor-file` plus `add-tests`. Ownership-derived variants
 * (`low-bus-factor`, `unowned-hotspot`, `ownership-drift`) are
 * appended when `--ownership` is active and the corresponding signal
 * fires.
 */
actions: HotspotAction[]
}
/**
 * Ownership metrics for a hotspot file, derived from git history and
 * CODEOWNERS declarations.
 */
export interface OwnershipMetrics {
/**
 * Minimum contributors covering half the file's commits.
 */
bus_factor: number
/**
 * Distinct contributors touching the file in the window.
 */
contributor_count: number
top_contributor: ContributorEntry
/**
 * Contributors active in the recent window; omitted when empty.
 */
recent_contributors?: ContributorEntry[]
/**
 * Contributors best positioned to review changes; omitted when empty.
 */
suggested_reviewers?: ContributorEntry[]
/**
 * Owner declared in CODEOWNERS, when one matches the file.
 */
declared_owner?: (string | null)
/**
 * Whether no owner could be resolved; `null` when ownership resolution
 * did not run.
 */
unowned?: (boolean | null)
ownership_state: OwnershipState
/**
 * True when recent contributions drift away from the declared ownership.
 */
drift: boolean
/**
 * Human-readable explanation of the drift, when drifting.
 */
drift_reason?: (string | null)
}
/**
 * One contributor row in ownership metrics.
 */
export interface ContributorEntry {
/**
 * Contributor identifier, encoded per `format`.
 */
identifier: string
format: ContributorIdentifierFormat
/**
 * Contributor's share of the file's commits, in `[0, 1]`.
 */
share: number
/**
 * Days since the contributor's last commit to the file.
 */
stale_days: number
/**
 * Contributor's commits touching the file in the window.
 */
commits: number
}
/**
 * Suggested action attached to a [`HotspotEntry`].
 *
 * The action list always begins with `refactor-file` plus `add-tests`.
 * Ownership-derived variants (`low-bus-factor`, `unowned-hotspot`,
 * `ownership-drift`) are appended only when `--ownership` is active AND
 * the corresponding signal fires for the hotspot.
 *
 * [`HotspotEntry`]: ../../fallow-output/src/health_scores.rs
 */
export interface HotspotAction {
type: HotspotActionType
/**
 * Whether `fallow fix` can auto-apply this action. Today every
 * hotspot action is manual.
 */
auto_fixable: boolean
/**
 * Human-readable description of the action.
 */
description: string
/**
 * Additional context for the action. Absent on `low-bus-factor` when
 * the finding's description already carries the full ask (no
 * suggested reviewers and not a low-commit file).
 */
note?: (string | null)
/**
 * Suggested CODEOWNERS pattern. Present only on `unowned-hotspot`
 * actions. Derived per the [`heuristic`](Self::heuristic) field;
 * consumers should branch on [`heuristic`](Self::heuristic) rather
 * than assume a stable algorithm.
 */
suggested_pattern?: (string | null)
/**
 * Strategy used to derive [`suggested_pattern`](Self::suggested_pattern).
 * Reserved for future evolution (`codeowners-cluster`, etc.).
 */
heuristic?: (HotspotActionHeuristic | null)
}
/**
 * Scope metadata for the hotspot analysis.
 */
export interface HotspotSummary {
/**
 * Start of the churn window, as passed to `git log --since`.
 */
since: string
/**
 * Minimum commit count for a file to qualify as a hotspot.
 */
min_commits: number
/**
 * Files with churn data in the window.
 */
files_analyzed: number
/**
 * Files excluded by test-path and ignore filters.
 */
files_excluded: number
/**
 * True when the repository is a shallow clone, so churn counts are
 * truncated.
 */
shallow_clone: boolean
/**
 * Provenance of the instant every churn and staleness number was measured
 * against. Absent only when a caller assembled a summary without one.
 */
clock?: (ClockProvenance | null)
}
/**
 * The instant a run measured commit ages and staleness against.
 *
 * A consumer reading `weighted_commits`, `stale_days`, or anything derived
 * from them needs to know whether re-running over the same commit yields the
 * same number. The human report says so in a warning that `--quiet` removes,
 * which left the JSON consumer, who cannot see stderr at all, with no way to
 * find out.
 */
export interface ClockProvenance {
source: ClockSource
/**
 * The reference epoch itself, in unix seconds. Pass it back as
 * `FALLOW_CLOCK_EPOCH` to reproduce this run's churn-derived numbers.
 */
epoch_secs: number
/**
 * False only for `wall_clock`, where the numbers drift between runs.
 */
reproducible: boolean
}
/**
 * Runtime coverage findings merged into the health report or emitted by
 * `fallow coverage analyze`. Present in health output when --runtime-coverage
 * is used. Shape mirrors the runtime coverage JSON contract; cloud mode
 * fetches runtime facts explicitly and merges them locally with AST/static
 * analysis.
 */
export interface RuntimeCoverageReport {
schema_version: RuntimeCoverageSchemaVersion
verdict: RuntimeCoverageReportVerdict
/**
 * All signals captured by post-processing. Independent of `verdict`,
 * which is the single most actionable signal under the current
 * context. Empty when the report is `Clean` and not under license
 * grace. Order is stable severity-descending.
 */
signals?: RuntimeCoverageSignal[]
summary: RuntimeCoverageSummary
/**
 * Surfaced runtime coverage findings (`safe_to_delete`, `review_required`,
 * `low_traffic`, `coverage_unavailable`). Omitted when empty. `active`
 * functions stay out of this list so the CLI output remains actionable.
 */
findings?: RuntimeCoverageFinding[]
/**
 * Top runtime functions by invocation count. Omitted when empty.
 */
hot_paths?: RuntimeCoverageHotPath[]
/**
 * First-class blast-radius entries for runtime-observed functions. Present
 * whenever runtime coverage analysis runs.
 */
blast_radius: RuntimeCoverageBlastRadiusEntry[]
/**
 * First-class production-importance entries for runtime-observed
 * functions. Present whenever runtime coverage analysis runs.
 */
importance: RuntimeCoverageImportanceEntry[]
/**
 * License/trial watermark for grace-mode output. Omitted when not
 * applicable.
 */
watermark?: (RuntimeCoverageWatermark | null)
/**
 * Non-fatal merge or coverage diagnostics. Omitted when empty.
 */
warnings?: RuntimeCoverageMessage[]
/**
 * Whether an autonomous agent may act on this report (mirrors
 * the cloud runtime-context contract). `false` when the capture
 * carries no usable runtime evidence (no tracked functions); then
 * `actionability_verdict` is `insufficient_evidence` and
 * `actionability_reason` explains. F4: a non-action floor, never a gate on a
 * positive verdict.
 */
actionable: boolean
/**
 * Why the report is non-actionable; `null` when `actionable` is true.
 */
actionability_reason?: (string | null)
/**
 * First-class non-action verdict (`insufficient_evidence`) when not
 * actionable; `null` otherwise. Mirrors the cloud runtime-context `verdict`;
 * named distinctly from the report-context `verdict` above to avoid a
 * collision.
 */
actionability_verdict?: (string | null)
provenance: RuntimeCoverageProvenance
}
/**
 * Summary block mirroring `fallow_cov_protocol::Summary` (0.3 shape).
 */
export interface RuntimeCoverageSummary {
data_source: RuntimeCoverageDataSource
/**
 * Timestamp of the newest runtime payload included in the report. Null for
 * local single-capture artifacts that do not carry cloud receipt metadata.
 */
last_received_at?: (string | null)
/**
 * Number of functions the sidecar could observe in the V8 or Istanbul
 * dump.
 */
functions_tracked: number
/**
 * Tracked functions that received at least one invocation.
 */
functions_hit: number
/**
 * Tracked functions that were never invoked.
 */
functions_unhit: number
/**
 * Functions the sidecar could not track (lazy-parsed, worker thread,
 * dynamic code, unresolved source map).
 */
functions_untracked: number
/**
 * Ratio of functions_hit / functions_tracked, expressed as a percent.
 */
coverage_percent: number
/**
 * Total number of observed invocations across all functions. Denominator
 * for low-traffic classification.
 */
trace_count: number
/**
 * Days of observation covered by the supplied dump (Phase 2 local analysis
 * emits 0, set by the beacon/cloud in Phase 3+).
 */
period_days: number
/**
 * Distinct deployments contributing to the supplied dump (Phase 2 local
 * analysis emits 0).
 */
deployments_seen: number
/**
 * Capture-quality telemetry. `None` for protocol-0.2 sidecars; protocol-0.3+
 * sidecars always populate it. Fuels the human-output short-window warning
 * and the quantified trial CTA, and is passed through to JSON consumers so
 * agent pipelines can surface the same signal.
 */
capture_quality?: (RuntimeCoverageCaptureQuality | null)
}
/**
 * Quality-of-capture signals emitted by the sidecar so the CLI can explain
 * short-window captures honestly instead of letting users blame the tool.
 */
export interface RuntimeCoverageCaptureQuality {
/**
 * Total observation window in seconds. Finer-grained than period_days
 * (which rounds up to whole days).
 */
window_seconds: number
/**
 * Number of distinct production instances that contributed to the dump.
 */
instances_observed: number
/**
 * True when the untracked-function ratio exceeds the sidecar's lazy-parse
 * threshold (30%). Signals that many untracked functions likely reflect
 * lazy-parsed code rather than unreachable code.
 */
lazy_parse_warning: boolean
/**
 * functions_untracked / functions_tracked as a percentage, rounded to 2
 * decimal places.
 */
untracked_ratio_percent: number
}
/**
 * One per-function runtime-coverage finding in `runtime_coverage.findings`.
 */
export interface RuntimeCoverageFinding {
/**
 * Per-finding suppression key of the form `fallow:prod:<hash>` (first 8 hex
 * of SHA-256(file + function + line + 'prod')). Hashes the current line, so
 * it changes when the function moves. Use this to suppress one finding.
 */
id: string
/**
 * Cross-surface join key of the form `fallow:fn:<hash>`
 * (`fallow_cov_protocol::function_identity_id`, hashes file + name +
 * start_line). The same function shares ONE value across findings, hot
 * paths, blast-radius, and importance entries (the per-finding `id` uses a
 * per-surface salt, so it differs by surface), and across V8, Istanbul,
 * and oxc producers (columns are excluded from the hash). Like `id`, it
 * changes when the function's file, name, or start line changes; it is a
 * cross-surface / cross-producer join key, not a line-move-immune one.
 * `null` when the producing surface (or an un-migrated cloud) supplied no
 * `FunctionIdentity`.
 */
stable_id?: (string | null)
/**
 * Content digest of the function's full-span source slice
 * (`fallow_cov_protocol::source_hash_for`: first 8 bytes of SHA-256 as 16
 * lowercase hex). Unlike `stable_id`, this is stable across line moves: a
 * moved-but-unedited function keeps the same value, so baselines can
 * suppress it after a pure line shift. `null` when the producing surface
 * supplied no `source_hash`.
 */
source_hash?: (string | null)
/**
 * File path relative to the project root.
 */
path: string
/**
 * Static function name as reported in the merged coverage result.
 */
function: string
/**
 * 1-indexed line number the function starts on.
 */
line: number
verdict: RuntimeCoverageVerdict
/**
 * Raw V8 invocation count. `None` when the function was untracked
 * (lazy-parsed, worker thread, or dynamic code).
 */
invocations?: (number | null)
confidence: RuntimeCoverageConfidence
evidence: RuntimeCoverageEvidence
/**
 * Suggested actions for this finding. Omitted when empty.
 */
actions?: RuntimeCoverageAction[]
/**
 * The discriminator inputs that produced this verdict (#321), emitted so an
 * agent can reproduce it and see the confidence cap. `None` for findings
 * not built from the merge pipeline (e.g. baseline round-trips). Omitted
 * from JSON when absent.
 */
discriminators?: (RuntimeCoverageDiscriminators | null)
}
/**
 * Supporting evidence for a finding (mirrors `fallow_cov_protocol::Evidence`).
 */
export interface RuntimeCoverageEvidence {
/**
 * `used` when the function is reachable in the module graph, `unused`
 * otherwise.
 */
static_status: string
/**
 * `covered` when the project's test suite hits this function,
 * `not_covered` otherwise.
 */
test_coverage: string
/**
 * `true` when the function is unreachable in the production module graph
 * but still referenced from a file that production mode excludes (test,
 * spec, story, fixture, or benchmark). Such a function is not dead code:
 * removing it breaks the referencing test. `false` when the production
 * graph was compared against the full tree and no such reference exists.
 * `null` when the report was produced without a production filter, or by
 * a surface that carries no second reachability answer.
 */
test_only_reference?: (boolean | null)
/**
 * `tracked` when V8 observed the function, `untracked` otherwise.
 */
v8_tracking: string
/**
 * Reason the function is untracked. Populated only when v8_tracking is
 * `untracked`. Values: `lazy_parsed`, `worker_thread`, `dynamic_eval`,
 * `unknown`.
 */
untracked_reason?: (string | null)
/**
 * Days of observation backing this finding.
 */
observation_days: number
/**
 * Distinct deployments backing this finding.
 */
deployments_observed: number
}
/**
 * Suggested follow-up action for a runtime coverage finding.
 */
export interface RuntimeCoverageAction {
/**
 * Action identifier, normalized to `type` in JSON output. Known values
 * emitted by `fallow coverage analyze`: `delete-cold-code`
 * (verdict=safe_to_delete), `review-runtime` (verdict=review_required).
 * The sidecar may emit additional protocol-specific identifiers;
 * consumers should treat unknown values as forward-compat extensions.
 */
type: string
/**
 * Human-readable action description.
 */
description: string
/**
 * Whether fallow can apply this action automatically.
 */
auto_fixable: boolean
}
/**
 * Discriminator inputs that PRODUCED a finding's verdict,
 * emitted alongside the verdict so an agent can reproduce it and see the
 * minimum-observation confidence cap instead of re-deriving them from scratch.
 * F4: these make the EXISTING Fallow-owned discriminators legible; they are not
 * a new or external signal and gate nothing. Pairs with `evidence.static_status`
 * (the static half of the discriminator set).
 */
export interface RuntimeCoverageDiscriminators {
/**
 * Three-state runtime tracking: `called` (invocations > 0), `never_called`
 * (V8 tracked it, invocations == 0), or `untracked` (V8 never saw it). The
 * ONLY signal that can issue a deletion verdict.
 */
tracking_state: string
/**
 * `invocations / trace_count` for this function; `null` when untracked (no
 * invocation count). The per-function value behind the low-traffic split.
 */
invocation_ratio?: (number | null)
/**
 * Active/low_traffic split ratio in effect (CLI default 0.001). A tracked
 * function whose `invocation_ratio` is below this reads `low_traffic`, else
 * `active`.
 */
low_traffic_threshold: number
/**
 * Total observed invocations across all functions (the `invocation_ratio`
 * denominator), echoed per finding so the verdict is self-contained.
 */
trace_count: number
/**
 * High-confidence verdict floor (CLI default 5000). When `trace_count` is
 * below it, confidence is capped regardless of the per-function signal.
 */
min_observation_volume: number
/**
 * `trace_count >= min_observation_volume`: whether the dump cleared the
 * confidence floor. `false` means this verdict's confidence is capped.
 */
meets_observation_volume: boolean
}
/**
 * One hot function in `runtime_coverage.hot_paths`, ranked by invocations.
 */
export interface RuntimeCoverageHotPath {
/**
 * Stable content-hash ID of the form `fallow:hot:<hash>`.
 */
id: string
/**
 * Cross-surface join key (`fallow:fn:<hash>`) for the hot function. Stable
 * across line moves; shared with the same function's findings / blast /
 * importance entries. `null` when no `FunctionIdentity` was supplied.
 */
stable_id?: (string | null)
/**
 * File path relative to the project root.
 */
path: string
/**
 * Function name for the hot path.
 */
function: string
/**
 * 1-indexed line number the function starts on.
 */
line: number
/**
 * 1-indexed line the function ends on (inclusive). Mirrors
 * `fallow_cov_protocol::HotPath::end_line` (added in protocol 0.5).
 * Older 0.4-shape sidecars omit the field on the wire; serde defaults
 * to `0`, which the line-overlap filter MUST treat as a single-line
 * range (`line..=line`) rather than a span.
 */
end_line: number
/**
 * Observed invocation count for the hot path.
 */
invocations: number
/**
 * Percentile rank over this response's hot-path distribution. `100`
 * means the busiest, `0` means the quietest function that qualified.
 */
percentile: number
/**
 * Suggested actions for this hot path (e.g., review-on-change). Omitted
 * when empty.
 */
actions?: RuntimeCoverageAction[]
/**
 * Per-call cost inputs and the speed-work score for this hot function.
 * Omitted when the hot path has no `stable_id` or no static counterpart
 * in this checkout.
 */
optimization_target?: (RuntimeCoverageOptimizationTarget | null)
}
/**
 * Speed-work inputs for one hot function: how often it runs and how much
 * work each call does. `importance` ranks the risk of a change; this block
 * ranks where speed work gives the largest gain.
 */
export interface RuntimeCoverageOptimizationTarget {
/**
 * `invocations` multiplied by the per-call cost that `cost_basis` names.
 * Uncapped integer. Compare it only between hot paths with the same
 * `cost_basis`: sort by `cost_basis` first, then by `cost_score`
 * descending. On the `cognitive` basis the per-call cost is at least 1.
 */
cost_score: number
cost_basis: RuntimeCoverageCostBasis
/**
 * Static cognitive complexity of the function. A static proxy for the
 * work per call, not a measurement.
 */
cognitive: number
/**
 * Static cyclomatic complexity of the function.
 */
cyclomatic: number
/**
 * Number of lines in the function body.
 */
line_count: number
/**
 * Peak executions of one block inside the function per call, from V8
 * block coverage. `1.0` means no block ran more than once per call. A
 * loop body that runs 3 times per call gives `3.0`. Calls to other
 * functions do not change the value.
 * Omitted when the coverage input has no block counts for the function.
 */
inner_iterations_per_call?: (number | null)
}
/**
 * One blast-radius entry in `runtime_coverage.blast_radius`: how far a
 * change to the function would ripple.
 */
export interface RuntimeCoverageBlastRadiusEntry {
/**
 * Stable content-hash ID of the form `fallow:blast:<hash>`.
 */
id: string
/**
 * Cross-surface join key (`fallow:fn:<hash>`) for the function. Stable
 * across line moves; shared with the same function's findings / hot-path /
 * importance entries. `null` when no `FunctionIdentity` was supplied.
 */
stable_id?: (string | null)
/**
 * File path relative to the project root.
 */
file: string
/**
 * Function name for the blast-radius entry.
 */
function: string
/**
 * 1-indexed line number the function starts on.
 */
line: number
/**
 * Static caller count from the module graph.
 */
caller_count: number
/**
 * Caller reach weighted by observed runtime traffic.
 */
caller_count_weighted_by_traffic: number
/**
 * Distinct deploy SHAs that touched the function in the observation
 * window. Cloud mode only; omitted in local mode.
 */
deploys_touched?: (number | null)
risk_band: RuntimeCoverageRiskBand
}
/**
 * One production-importance entry in `runtime_coverage.importance`, scoring
 * how much a function matters in production.
 */
export interface RuntimeCoverageImportanceEntry {
/**
 * Stable content-hash ID of the form `fallow:importance:<hash>`.
 */
id: string
/**
 * Cross-surface join key (`fallow:fn:<hash>`) for the function. Stable
 * across line moves; shared with the same function's findings / hot-path /
 * blast-radius entries. `null` when no `FunctionIdentity` was supplied.
 */
stable_id?: (string | null)
/**
 * File path relative to the project root.
 */
file: string
/**
 * Function name for the importance entry.
 */
function: string
/**
 * 1-indexed line number the function starts on.
 */
line: number
/**
 * Observed invocation count for this function.
 */
invocations: number
/**
 * Cyclomatic complexity from the static health pipeline.
 */
cyclomatic: number
/**
 * Number of CODEOWNERS owners matched for this file. Zero means no owner
 * was resolved.
 */
owner_count: number
/**
 * 0-100 explainable score from log-scaled traffic, capped complexity
 * weight, and ownership-risk weight.
 */
importance_score: number
/**
 * Templated one-sentence explanation for the score.
 */
reason: string
}
/**
 * Non-fatal diagnostic emitted while merging runtime coverage.
 */
export interface RuntimeCoverageMessage {
/**
 * Stable machine-readable warning code.
 */
code: string
/**
 * Human-readable warning message.
 */
message: string
}
/**
 * Provenance of a runtime-coverage report, mirroring
 * the cloud runtime-context `provenance` block so the local-capture and cloud
 * surfaces present one portable shape. F4: provenance is context only; it never
 * gates a verdict or confidence.
 */
export interface RuntimeCoverageProvenance {
data_source: RuntimeCoverageDataSource
/**
 * `true` / `false` / `unknown`. Always `unknown` for a local capture: the
 * local path has no deployment-origin signal (the cloud may resolve it).
 */
is_production: string
/**
 * Age in whole days of the most recent evidence; `0` for a fresh local
 * capture, `null` when no runtime data is present.
 */
freshness_days?: (number | null)
/**
 * `functions_untracked / (functions_tracked + functions_untracked)`, in
 * `[0, 1]`. High ratios mark a thin / partial capture.
 */
untracked_ratio: number
/**
 * Fraction of resolution-attempted functions whose position could not be
 * mapped to source, in `[0, 1]`. `0` for a local capture (positions resolve
 * natively or via the sidecar).
 */
unresolved_ratio: number
/**
 * Whether `freshness_days` exceeds `stale_after_days`.
 */
stale: boolean
/**
 * The documented staleness cutoff (days), echoed so the rule travels in-band.
 */
stale_after_days: number
}
/**
 * Combined coverage, runtime, complexity, and change-scope verdicts.
 */
export interface CoverageIntelligenceReport {
schema_version: CoverageIntelligenceSchemaVersion
verdict: CoverageIntelligenceVerdict
summary: CoverageIntelligenceSummary
/**
 * Combined findings, one per matched unit.
 */
findings: CoverageIntelligenceFinding[]
}
/**
 * Aggregate metadata for coverage-intelligence output.
 */
export interface CoverageIntelligenceSummary {
/**
 * Total combined findings.
 */
findings: number
/**
 * Findings with the risky-change verdict.
 */
risky_changes: number
/**
 * Findings with the high-confidence-delete verdict.
 */
high_confidence_deletes: number
/**
 * Findings with the review-required verdict.
 */
review_required: number
/**
 * Findings with the refactor-carefully verdict.
 */
refactor_carefully: number
/**
 * Candidate joins dropped because the cross-surface match was ambiguous.
 */
skipped_ambiguous_matches: number
}
/**
 * One combined coverage-intelligence finding.
 */
export interface CoverageIntelligenceFinding {
/**
 * Stable finding ID of the form `fallow:coverage-intel:<hash>`.
 */
id: string
/**
 * File path relative to the project root.
 */
path: string
/**
 * Function or export identity when known.
 */
identity?: (string | null)
/**
 * 1-indexed source line.
 */
line: number
verdict: CoverageIntelligenceVerdict
/**
 * Ordered evidence signals behind the verdict.
 */
signals: CoverageIntelligenceSignal[]
recommendation: CoverageIntelligenceRecommendation
confidence: CoverageIntelligenceConfidence
/**
 * IDs of related findings from other fallow surfaces.
 */
related_ids?: string[]
evidence: CoverageIntelligenceEvidence
/**
 * Machine-actionable follow-up actions.
 */
actions: CoverageIntelligenceAction[]
}
/**
 * Compact evidence values that led to a recommendation.
 */
export interface CoverageIntelligenceEvidence {
/**
 * Test coverage percentage (0-100), when coverage data exists.
 */
coverage_pct?: (number | null)
/**
 * CRAP score, when complexity and coverage both exist.
 */
crap?: (number | null)
/**
 * Runtime-coverage verdict label, e.g. `hot` or `cold`.
 */
runtime_verdict?: (string | null)
/**
 * Observed runtime invocation count.
 */
invocations?: (number | null)
/**
 * Static usage status label, e.g. `unused`.
 */
static_status?: (string | null)
/**
 * Static test-coverage status label, e.g. `no-test-path`.
 */
test_coverage?: (string | null)
/**
 * True when the unit is inside the current change scope; omitted when
 * false.
 */
changed?: boolean
/**
 * Ownership-drift state label, when ownership analysis ran.
 */
ownership_state?: (string | null)
match_confidence: CoverageIntelligenceMatchConfidence
}
/**
 * Machine-actionable next step for a coverage-intelligence finding.
 */
export interface CoverageIntelligenceAction {
/**
 * Action identifier, normalized to `type` in JSON output.
 */
type: string
/**
 * Human-readable action description.
 */
description: string
/**
 * Whether fallow can apply this action automatically.
 */
auto_fixable: boolean
}
/**
 * A function exceeding the very-high-risk size threshold (>60 LOC).
 */
export interface LargeFunctionEntry {
/**
 * File path relative to the project root.
 */
path: string
/**
 * Function name, or a synthesized name for anonymous functions.
 */
name: string
/**
 * 1-based line the function starts on.
 */
line: number
/**
 * Lines of code in the function body.
 */
line_count: number
}
/**
 * Wire envelope for a single refactoring target.
 *
 * Flattens [`RefactoringTarget`] for wire continuity and adds the typed
 * `actions` list. The `#[serde(flatten)]` keeps each `targets[]` item
 * byte-identical to the pre-wrapper shape: inner fields (`path`,
 * `priority`, `efficiency`, `recommendation`, `category`, ...) sit at
 * the top level alongside `actions`. Optional inner fields (`factors`,
 * `evidence`) keep their original `skip_serializing_if` behaviour.
 *
 * Construct via [`RefactoringTargetFinding::with_actions`] in the
 * typical health pipeline or via [`RefactoringTargetFinding::from`] for
 * fixture and test code.
 */
export interface RefactoringTargetFinding {
/**
 * Absolute file path (stripped to relative in output).
 */
path: string
/**
 * Priority score (0–100, higher = more urgent).
 */
priority: number
/**
 * Efficiency score (priority / effort). Higher = better quick-win value.
 * Surfaces low-effort, high-priority targets first.
 */
efficiency: number
/**
 * One-line actionable recommendation.
 */
recommendation: string
category: RecommendationCategory
effort: EffortEstimate
confidence: Confidence
/**
 * Contributing factors that triggered this recommendation. Empty array
 * omitted from JSON.
 */
factors?: ContributingFactor[]
/**
 * Structured evidence linking to specific analysis data.
 */
evidence?: (TargetEvidence | null)
/**
 * Machine-actionable refactoring and suppression hints. Always
 * populated; the list never empties because the action selector
 * unconditionally emits `apply-refactoring`. A trailing
 * `suppress-line` is appended only when the target carries
 * [`RefactoringTarget::evidence`] linking to specific functions.
 */
actions: RefactoringTargetAction[]
}
/**
 * A contributing factor that triggered or strengthened a recommendation.
 */
export interface ContributingFactor {
/**
 * Metric name (matches JSON field names: `"fan_in"`, `"dead_code_ratio"`, etc.).
 */
metric: string
/**
 * Raw metric value for programmatic use.
 */
value: number
/**
 * Threshold that was exceeded.
 */
threshold: number
/**
 * Human-readable explanation.
 */
detail: string
}
/**
 * Evidence linking a target back to specific analysis data.
 *
 * Provides enough detail for an AI agent to act on a recommendation
 * without a second tool call.
 */
export interface TargetEvidence {
/**
 * Names of unused exports (populated for `RemoveDeadCode` targets).
 */
unused_exports?: string[]
/**
 * Complex functions with line numbers and cognitive scores (populated for `ExtractComplexFunctions`).
 */
complex_functions?: EvidenceFunction[]
/**
 * Files forming the import cycle (populated for `BreakCircularDependency` targets).
 */
cycle_path?: string[]
/**
 * Files that directly import this target, with imported and local symbols.
 */
direct_callers?: DirectCallerEvidence[]
/**
 * Other duplicate-code instances that share a clone group with this target.
 */
clone_siblings?: CloneSiblingEvidence[]
}
/**
 * A function referenced in target evidence.
 */
export interface EvidenceFunction {
/**
 * Function name.
 */
name: string
/**
 * 1-based line number.
 */
line: number
/**
 * Cognitive complexity score.
 */
cognitive: number
}
/**
 * A direct importer referenced in target evidence.
 */
export interface DirectCallerEvidence {
/**
 * File that directly imports the target.
 */
path: string
/**
 * Symbols imported from the target by this file.
 */
symbols?: DirectCallerSymbolEvidence[]
}
/**
 * Symbol details for a direct importer.
 */
export interface DirectCallerSymbolEvidence {
/**
 * Imported binding name.
 */
imported: string
/**
 * Local binding name in the importing file.
 */
local: string
/**
 * Whether the import is type-only.
 */
type_only: boolean
}
/**
 * A duplicate-code sibling referenced in target evidence.
 */
export interface CloneSiblingEvidence {
/**
 * File containing the sibling clone instance.
 */
path: string
/**
 * 1-based start line of the sibling clone.
 */
start_line: number
/**
 * 1-based end line of the sibling clone.
 */
end_line: number
/**
 * Stable duplicate-group handle, matching `dupes --trace dup:<id>`.
 */
fingerprint: string
}
/**
 * Suggested action attached to a [`RefactoringTarget`].
 *
 * The list always begins with `apply-refactoring`. A trailing
 * `suppress-line` is appended only when the target carries `evidence`
 * linking to specific functions (e.g., `extract_complex_functions`,
 * `add_test_coverage`).
 *
 * Unlike [`HealthFindingAction`], the `suppress-line` variant emitted
 * here does NOT carry a `placement` field: the parent
 * [`RefactoringTarget`] points at a file (not a specific function
 * declaration site), so a per-line placement hint would have no
 * referent. Consumers that want the placement metadata should follow
 * the target's `evidence.complex_functions` back to the matching
 * `ComplexityViolation` and read placement from THAT action instead.
 *
 * [`RefactoringTarget`]: ../../fallow-output/src/health_targets.rs
 */
export interface RefactoringTargetAction {
type: RefactoringTargetActionType
/**
 * Whether `fallow fix` can auto-apply this action. Today both
 * variants are manual.
 */
auto_fixable: boolean
/**
 * Human-readable description of the action. For `apply-refactoring`
 * this is the target's own `recommendation` string; for
 * `suppress-line` it is the suppression prompt.
 */
description: string
/**
 * Recommendation category for `apply-refactoring` actions. Mirrors
 * the parent target's
 * [`category`](../../fallow-output/src/health_targets.rs.html)
 * field so consumers can route on the action alone.
 */
category?: (string | null)
/**
 * The inline comment to insert. Present on `suppress-line` actions
 * when evidence exists.
 */
comment?: (string | null)
}
/**
 * Adaptive thresholds used for refactoring target scoring.
 *
 * Derived from the project's metric distribution (percentile-based with floors).
 * Exposed in JSON output so consumers can interpret scores in context.
 */
export interface TargetThresholds {
/**
 * Fan-in saturation point for priority formula (p95, floor 5).
 */
fan_in_p95: number
/**
 * Fan-in moderate threshold for contributing factors (p75, floor 3).
 */
fan_in_p75: number
/**
 * Fan-out saturation point for priority formula (p95, floor 8).
 */
fan_out_p95: number
/**
 * Fan-out high threshold for rules and contributing factors (p90, floor 5).
 */
fan_out_p90: number
}
/**
 * Trend comparison between the current run and a previous snapshot. Shows
 * per-metric deltas with directional indicators.
 */
export interface HealthTrend {
compared_to: TrendPoint
/**
 * Per-metric deltas.
 */
metrics: TrendMetric[]
/**
 * Number of snapshots found in the snapshot directory.
 */
snapshots_loaded: number
overall_direction: TrendDirection
}
/**
 * A reference to a snapshot used in trend comparison.
 */
export interface TrendPoint {
/**
 * ISO 8601 timestamp of the snapshot.
 */
timestamp: string
/**
 * Git SHA at time of snapshot.
 */
git_sha?: (string | null)
/**
 * Health score from the snapshot (stored, not re-derived).
 */
score?: (number | null)
/**
 * Letter grade from the snapshot.
 */
grade?: (string | null)
/**
 * Formula used for the stored score; absent on legacy snapshots.
 * A score delta is emitted only when this matches the current score formula.
 */
score_formula_version?: (number | null)
/**
 * Coverage model used for CRAP computation in this snapshot.
 */
coverage_model?: (CoverageModel | null)
/**
 * Schema version of the compared snapshot.
 */
snapshot_schema_version?: (number | null)
}
/**
 * A single metric's trend between two snapshots.
 */
export interface TrendMetric {
/**
 * Metric identifier, e.g. `"score"` or `"dead_file_pct"`.
 */
name: string
/**
 * Human-readable label, e.g. `"Health Score"` or `"Dead Files"`.
 */
label: string
/**
 * Previous value (from snapshot).
 */
previous: number
/**
 * Current value (from this run).
 */
current: number
/**
 * Absolute change (current - previous).
 */
delta: number
direction: TrendDirection
/**
 * Unit for display, e.g. `"%"`, `""`, or `"pts"`.
 */
unit: string
/**
 * Raw count from previous snapshot (for JSON consumers).
 */
previous_count?: (TrendCount | null)
/**
 * Raw count from current run (for JSON consumers).
 */
current_count?: (TrendCount | null)
}
/**
 * Raw numerator/denominator for a percentage metric.
 */
export interface TrendCount {
/**
 * The numerator, e.g. dead files count.
 */
value: number
/**
 * The denominator, e.g. total files.
 */
total: number
}
/**
 * Auditable breadcrumb recording when health-finding `suppress-line`
 * action hints were omitted from the report.
 *
 * Set at construction time on `HealthReport::actions_meta` (and on
 * each `HealthGroup::actions_meta`
 * when grouped) by the report builder, derived from the active
 * `HealthActionContext`. Lets consumers see "where did the
 * suppress-line hints go?" without having to grep the config or CLI
 * history.
 *
 * Stable `reason` codes:
 * - `baseline-active`: a baseline is active and inline ignores would
 *   become dead annotations once the baseline regenerates.
 * - `config-disabled`: `health.suggestInlineSuppression` is `false`.
 * - `unspecified`: the caller did not record a reason.
 */
export interface HealthActionsMeta {
/**
 * Always `true` when the breadcrumb is emitted. Absent from the wire when
 * no suppression occurred.
 */
suppression_hints_omitted: boolean
/**
 * Stable code describing why the suppression occurred.
 */
reason: string
/**
 * Scope of the omission. Always `"health-findings"` today.
 */
scope: string
}
/**
 * Framework-specific health detector coverage surfaced for agent consumers.
 */
export interface FrameworkHealthDiagnostics {
/**
 * Detected framework IDs, sorted and deduplicated.
 */
detected_frameworks: string[]
/**
 * Detector coverage for the detected frameworks.
 */
detectors: FrameworkHealthDetector[]
}
/**
 * Status for one framework-specific health detector.
 */
export interface FrameworkHealthDetector {
/**
 * Rule or detector ID, matching fallow's stable rule names where possible.
 */
id: string
/**
 * Framework ID that made this detector relevant.
 */
framework: string
status: FrameworkHealthDetectorStatus
/**
 * Stable reason code for non-active statuses.
 */
reason?: (string | null)
}
/**
 * Structural CSS analytics surfaced by `fallow health --css`.
 */
export interface CssAnalyticsReport {
/**
 * Stylesheets with at least one structurally notable rule, in scan order.
 */
files: CssFileAnalytics[]
summary: CssAnalyticsSummary
/**
 * Vue SFCs whose `<style scoped>` defines classes used nowhere else in the
 * component (cleanup candidates).
 */
scoped_unused?: ScopedUnusedClasses[]
/**
 * `@keyframes` defined but referenced via no `animation` / `animation-name`
 * in any stylesheet, with the stylesheet that defines them (cleanup
 * candidates; an animation name can still be applied from JavaScript).
 * The "defined-but-unused" direction.
 */
unreferenced_keyframes?: UnreferencedKeyframes[]
/**
 * Animation references (`animation` / `animation-name`) to a `@keyframes`
 * name that is defined in NO stylesheet anywhere in the project, with the
 * first stylesheet that references them. The "used-but-undefined" direction
 * (the inverse of `unreferenced_keyframes`): usually a typo or a removed
 * animation, occasionally a `@keyframes` defined in CSS-in-JS (which the
 * CSS parser never sees). Conservative candidates, never gated findings.
 */
undefined_keyframes?: UndefinedKeyframes[]
/**
 * Groups of style rules across the project that share an identical
 * declaration block (4+ declarations, sorted and `!important`-aware),
 * grouped by content: copy-paste consolidation candidates (fallow's
 * duplication signal applied to CSS). Sorted by estimated savings
 * descending.
 */
duplicate_declaration_blocks?: CssDuplicateBlock[]
/**
 * CVA / shadcn variant class strings that repeat the same normalized class
 * block in several variant values. Kept separate from CSS declaration-block
 * duplication because the source is JS config, not parsed CSS rules.
 */
cva_duplicate_variant_blocks?: CvaDuplicateVariantBlock[]
/**
 * CVA / shadcn variant class strings that hardcode a Tailwind arbitrary
 * value even though an existing token has the same or nearest comparable
 * value. Advisory: variants often encode product semantics, so agents
 * should verify intent before replacing.
 */
cva_variant_token_drifts?: CvaVariantTokenDrift[]
/**
 * Tailwind arbitrary-value utilities (`w-[13px]`, `bg-[#abc]`) found in
 * markup, which hardcode a one-off value instead of a configured scale
 * token (design-token bypass). Present only when the project uses Tailwind.
 * Sorted by use count descending. Candidates, not findings: an arbitrary
 * value is sometimes the right call.
 */
tailwind_arbitrary_values?: TailwindArbitraryValue[]
/**
 * Located raw CSS declaration values that bypass token surfaces (`var()`,
 * `token()`, `theme()`) on scale-sensitive axes such as color, font-size,
 * line-height, radius, and shadow. Conservative candidates: a raw value can
 * be intentional, but introduced raw values are useful audit feedback.
 */
raw_style_values?: RawStyleValue[]
/**
 * Unused CSS at-rule entities: an `@property` registered but never read via
 * `var()` in any stylesheet, or an `@layer` declared but never populated by
 * a block. Cleanup candidates (an `@property` can be read from JS; a layer
 * can be populated via `@import layer()`). Located by first definition.
 */
unused_at_rules?: UnusedAtRule[]
/**
 * Static `class` / `className` tokens in markup that match no CSS class
 * defined anywhere in the project AND are one edit away from a class that
 * IS defined (a likely typo or stale rename, with the suggested class). The
 * CSS analogue of an unresolved import; the near-miss restriction keeps it
 * near-zero false-positive (Tailwind utilities and third-party classes are
 * not one edit from an authored class). Candidates, never gated: the token
 * could be defined in CSS-in-JS or an external stylesheet the parser never
 * sees. Sorted by `(path, line, class)`.
 */
unresolved_class_references?: UnresolvedClassReference[]
/**
 * Global CSS classes (defined in a plain `.css`/`.scss` rule) whose literal
 * name is referenced by NO in-project markup, static or dynamic (the CSS
 * analogue of an unused export). Heavily gated to stay near-zero-false-
 * positive: emitted only when the project is plain-CSS-dominant, the
 * stylesheet is locally consumed (not a published design-system surface),
 * and the whole project is in scope. Candidates, never gated findings: the
 * class may be used by an HTML email, server template, CMS, or Markdown the
 * parser never scans. Sorted by `(path, line, class)`.
 */
unreferenced_css_classes?: UnreferencedCssClass[]
/**
 * `@font-face` families declared in a stylesheet but referenced by no
 * `font-family` anywhere in the project: a dead web-font payload (the font
 * file is downloaded but never applied). Located at the declaring
 * stylesheet. Cleanup candidates: the family could be applied from inline
 * styles or set via JavaScript. Sorted by `(path, family)`.
 */
unused_font_faces?: UnusedFontFace[]
/**
 * Tailwind v4 `@theme` design tokens (`--color-brand`, `--radius-card`)
 * defined in a stylesheet but used by no generated utility, `var()` read,
 * `@apply`, or arbitrary value anywhere in the project: dead design tokens
 * (the `unused-export` of the token era). Present only when the project is
 * Tailwind v4 (a `tailwindcss` dependency plus at least one `@theme` block)
 * and not a plugin / published-library / partial-scope run. Candidates,
 * never gated findings: the token may be consumed by a Tailwind plugin or a
 * downstream repo. Sorted by `(path, line, token)`.
 */
unused_theme_tokens?: UnusedThemeToken[]
/**
 * Tailwind v4 theme tokens whose comparable values are close to another
 * token in the same theme dictionary. These are opt-in `--css-deep`
 * candidates because they need whole-project token context.
 */
near_duplicate_theme_tokens?: NearDuplicateThemeToken[]
/**
 * CSS-in-JS design tokens whose comparable values are close to another
 * token from the same project. Covers StyleX, vanilla-extract, PandaCSS,
 * styled-components, and Emotion token definitions. These are opt-in
 * `--css-deep` candidates because they need whole-project token context.
 */
near_duplicate_css_in_js_tokens?: NearDuplicateThemeToken[]
/**
 * A location-aware reverse index of design-token consumers. Tailwind v4
 * entries cover `@theme` tokens consumed through `var()` reads, `@apply`
 * bodies, or generated utilities. CSS-in-JS entries cover supported StyleX,
 * vanilla-extract, PandaCSS, styled-components, and Emotion definitions and
 * their member or call consumers. Every entry includes the defining site,
 * located consumer samples, and the full `consumer_count` as a static lower
 * bound. Tailwind entries use the same gated candidate set as
 * `unused_theme_tokens`; CSS-in-JS entries require supported direct imports.
 * Partial-scope runs omit the index. Sorted by token and empty when no
 * eligible token definitions are found. A zero count is evidence for
 * investigation, not deletion proof.
 */
token_consumers?: TokenConsumers[]
/**
 * The project authors `font-size` values in several units (`px`, `rem`,
 * `em`, `%`), with a per-unit distinct-value count: a type-scale
 * inconsistency smell (mixing `px` and `rem` for type works against
 * user-zoom accessibility). Present only above a conservative floor.
 * Advisory candidate, never gated: the spread can be intentional (fixed
 * chrome in `px`, body type in `rem`).
 *
 * Color-notation mixing (hex vs rgb vs hsl) is deliberately NOT surfaced:
 * the CSS parser canonicalizes every legacy sRGB notation to hex before
 * fallow sees the value, so the authored distinction is already gone and
 * cannot be recovered without a separate raw-token pass.
 */
font_size_unit_mix?: (CssNotationConsistency | null)
}
/**
 * Per-stylesheet CSS analytics.
 */
export interface CssFileAnalytics {
/**
 * Project-root-relative, forward-slash path.
 */
path: string
analytics: CssAnalytics
}
/**
 * Stylesheet-level structural CSS analytics, computed from the parsed CSS
 * syntax tree. Feeds `fallow health` penalty weights and located findings,
 * never a standalone CSS score.
 */
export interface CssAnalytics {
/**
 * Total declarations across every style rule (normal plus `!important`).
 */
total_declarations: number
/**
 * Total `!important` declarations across every style rule.
 */
important_declarations: number
/**
 * Number of style rules.
 */
rule_count: number
/**
 * Number of style rules with no declarations.
 */
empty_rule_count: number
/**
 * Deepest style-rule nesting depth observed (0 = no nesting).
 */
max_nesting_depth: number
/**
 * Rules that crossed the structural floor, in source order. Bounded; see
 * [`Self::notable_truncated`]. The scalar aggregates above always reflect
 * the full stylesheet regardless of truncation.
 */
notable_rules: CssRuleMetric[]
/**
 * `true` when more rules crossed the structural floor than `notable_rules`
 * retains (compiled utility CSS can emit thousands of `!important` rules),
 * so consumers can note that per-rule findings were capped.
 */
notable_truncated: boolean
/**
 * Distinct color VALUES in the stylesheet, sorted (a palette-size /
 * design-token-sprawl signal). The parser canonicalizes notation, so the
 * authored format is NOT preserved: `red`, `#f00`, `#ff0000`, and
 * `rgb(255,0,0)` all collapse to one entry, and every legacy sRGB notation
 * renders as hex. Notation-MIXING (hex vs rgb vs hsl) is therefore not
 * detectable from this set; it would need a separate raw-token pass.
 */
colors: string[]
/**
 * Distinct `font-size` declaration values in the stylesheet, sorted.
 */
font_sizes: string[]
/**
 * Distinct `z-index` declaration values in the stylesheet, sorted.
 */
z_indexes: string[]
/**
 * Distinct `box-shadow` declaration values in the stylesheet, sorted. A
 * high count signals an uncontrolled shadow scale (design-token sprawl).
 */
box_shadows: string[]
/**
 * Distinct `border-radius` declaration values in the stylesheet, sorted.
 */
border_radii: string[]
/**
 * Distinct `line-height` declaration values in the stylesheet, sorted.
 */
line_heights: string[]
/**
 * Distinct custom properties (`--x`) DEFINED in the stylesheet, sorted.
 */
defined_custom_properties: string[]
/**
 * Distinct custom properties REFERENCED via `var()` in the stylesheet.
 */
referenced_custom_properties: string[]
/**
 * Distinct `@keyframes` names DEFINED in the stylesheet, sorted.
 */
defined_keyframes: string[]
/**
 * Distinct `@keyframes` names REFERENCED via `animation` / `animation-name`.
 */
referenced_keyframes: string[]
/**
 * Distinct custom properties REGISTERED via an `@property` rule, sorted.
 */
registered_custom_properties: string[]
/**
 * Distinct cascade layers DECLARED (via `@layer a, b;` statements or named
 * `@layer a { }` blocks), sorted.
 */
declared_layers: string[]
/**
 * Distinct cascade layers POPULATED by a named `@layer a { }` block, sorted.
 * A layer declared but never populated (and not imported into) is a
 * cleanup candidate.
 */
populated_layers: string[]
/**
 * Distinct font families DECLARED by an `@font-face` rule in the stylesheet,
 * sorted. A declared family referenced by no `font-family` anywhere is a
 * dead web-font payload (cleanup candidate).
 */
defined_font_faces: string[]
/**
 * Distinct font families REFERENCED via `font-family` / `font` in the
 * stylesheet, sorted (generic keywords like `serif` excluded).
 */
referenced_font_families: string[]
}
/**
 * Structural CSS metrics for a single style rule, computed from the parsed CSS
 * syntax tree. A rule is recorded only when it crosses a structural floor (an
 * id selector, a complex selector, a `!important` declaration, or deep
 * nesting), so the vector stays bounded on normal stylesheets.
 *
 * Not persisted in the extraction cache: `fallow health` computes these
 * on demand from the CSS source, so there is no `bitcode` derive.
 */
export interface CssRuleMetric {
/**
 * 1-based line of the rule's first selector.
 */
line: number
/**
 * 1-based column of the rule's first selector.
 */
col: number
/**
 * Specificity component `a` (id selectors), max across the rule's selectors.
 */
specificity_a: number
/**
 * Specificity component `b` (class / attribute / pseudo-class selectors).
 */
specificity_b: number
/**
 * Specificity component `c` (type / pseudo-element selectors).
 */
specificity_c: number
/**
 * Largest selector component count across the rule's selector list.
 */
complexity: number
/**
 * Declaration count in the rule (normal plus `!important`).
 */
declaration_count: number
/**
 * `!important` declaration count in the rule.
 */
important_count: number
/**
 * Style-rule nesting depth (0 = top level).
 */
nesting_depth: number
}
/**
 * Project-wide CSS analytics aggregates across every analyzed stylesheet
 * (including stylesheets with no notable rule, which are not listed
 * individually in `files`).
 */
export interface CssAnalyticsSummary {
/**
 * Stylesheets analyzed: standard `.css` files, Vue/Svelte SFC `<style>`
 * blocks, and (dep-gated) CSS-in-JS, both the tagged-template form and the
 * object form (`style({...})` / `stylex.create({...})` / `css({...})`). SCSS
 * is skipped. Note: flat atomic object CSS-in-JS (StyleX/Panda) is counted
 * here and contributes to these aggregates, but has no notable rules, so its
 * files never appear in the per-file `files` list.
 */
files_analyzed: number
/**
 * Total style rules across analyzed stylesheets.
 */
total_rules: number
/**
 * Total declarations across analyzed stylesheets.
 */
total_declarations: number
/**
 * Total `!important` declarations across analyzed stylesheets.
 */
important_declarations: number
/**
 * Total empty style rules across analyzed stylesheets.
 */
empty_rules: number
/**
 * Deepest style-rule nesting depth observed across analyzed stylesheets.
 */
max_nesting_depth: number
/**
 * Distinct color values (authored form) across the whole codebase. A high
 * count signals an uncontrolled palette (design-token sprawl).
 */
unique_colors: number
/**
 * Distinct `font-size` values across the whole codebase.
 */
unique_font_sizes: number
/**
 * Distinct `z-index` values across the whole codebase.
 */
unique_z_indexes: number
/**
 * Distinct `box-shadow` values across the whole codebase (shadow-scale sprawl).
 */
unique_box_shadows: number
/**
 * Distinct `border-radius` values across the whole codebase (radius-scale sprawl).
 */
unique_border_radii: number
/**
 * Distinct `line-height` values across the whole codebase (type-scale sprawl).
 */
unique_line_heights: number
/**
 * Distinct custom properties (`--x`) defined anywhere in the codebase.
 */
custom_properties_defined: number
/**
 * Custom properties defined but never referenced via `var()` in any
 * stylesheet (the defined-but-unused direction). These are cleanup
 * CANDIDATES, not confirmed dead: a property may still be read or set from
 * JavaScript or inline HTML styles.
 */
custom_properties_unreferenced: number
/**
 * Distinct custom properties referenced via `var()` that are defined in no
 * stylesheet anywhere (the used-but-undefined direction). A COUNT only, not
 * a located list: a `var(--x)` with no CSS definition is extremely common
 * in JavaScript-driven theming and design-token libraries, so locating
 * these would be net-noise. The count is an architecture signal (how much
 * of the `var()` surface is resolved outside CSS), not a finding.
 */
custom_properties_undefined: number
/**
 * Distinct `@keyframes` defined anywhere in the codebase.
 */
keyframes_defined: number
/**
 * `@keyframes` defined but never referenced via `animation` /
 * `animation-name` in any stylesheet (the defined-but-unused direction;
 * cleanup CANDIDATES; an animation name can still be applied from
 * JavaScript).
 */
keyframes_unreferenced: number
/**
 * Distinct animation names referenced via `animation` / `animation-name`
 * that resolve to no `@keyframes` definition anywhere (the used-but-
 * undefined direction). Located in `undefined_keyframes`; usually a typo or
 * a removed animation.
 */
keyframes_undefined: number
/**
 * Total Vue `<style scoped>` classes used nowhere else in their component
 * (cleanup candidates), across all SFCs.
 */
scoped_unused_classes: number
/**
 * Number of distinct declaration blocks (4+ declarations) that appear in
 * two or more rules across the project (copy-paste consolidation
 * candidates). Located in `duplicate_declaration_blocks`.
 */
duplicate_declaration_blocks: number
/**
 * Total declarations removable by consolidating every duplicate block:
 * the sum of `(occurrence_count - 1) * declaration_count` across groups.
 */
duplicate_declarations_total: number
/**
 * Distinct Tailwind arbitrary-value tokens used in markup (design-token
 * bypass). Zero when the project does not use Tailwind. Located in
 * `tailwind_arbitrary_values`.
 */
tailwind_arbitrary_values: number
/**
 * Total Tailwind arbitrary-value occurrences across markup.
 */
tailwind_arbitrary_value_uses: number
/**
 * Preprocessor stylesheets (`.scss`, `.sass`, `.less`) seen by the styling
 * scan. These are parsed textually for local candidates, not compiled.
 */
preprocessor_stylesheets: number
/**
 * True when project-wide class reachability was skipped because
 * preprocessor stylesheets outnumber plain CSS, making generated classes
 * invisible without a Sass/Less compiler.
 */
preprocessor_reachability_abstained: boolean
/**
 * Located raw CSS declaration values that bypass token surfaces on
 * scale-sensitive axes. Located in `raw_style_values`.
 */
raw_style_values: number
/**
 * `@property` registrations never referenced via `var()` in any stylesheet
 * (located in `unused_at_rules`). Cleanup candidates.
 */
unused_property_registrations: number
/**
 * Cascade layers declared but never populated by a block (located in
 * `unused_at_rules`). Cleanup candidates.
 */
unused_layers: number
/**
 * Static markup class tokens that match no defined CSS class but are one
 * edit from a defined class (likely typos / stale renames). Located in
 * `unresolved_class_references`. Candidates, never gated.
 */
unresolved_class_references: number
/**
 * Global CSS classes defined in a stylesheet but referenced by no in-project
 * markup (located in `unreferenced_css_classes`). Heavily gated cleanup
 * candidates; zero on preprocessor-dominant or partial-scope runs.
 */
unreferenced_css_classes: number
/**
 * `@font-face` families declared but referenced by no `font-family` anywhere
 * (located in `unused_font_faces`). Dead web-font cleanup candidates.
 */
unused_font_faces: number
/**
 * Tailwind v4 `@theme` design tokens defined but used by no generated
 * utility, `var()`, `@apply`, or arbitrary value anywhere (located in
 * `unused_theme_tokens`). Dead-design-token cleanup candidates; zero when
 * the project is not Tailwind v4 or a plugin / published-library /
 * partial-scope run gated the scan out.
 */
unused_theme_tokens: number
/**
 * Tailwind v4 theme tokens whose comparable values are close to another
 * token in the same theme dictionary. Located in
 * `near_duplicate_theme_tokens`.
 */
near_duplicate_theme_tokens: number
/**
 * CSS-in-JS design tokens whose comparable values are close to another
 * token from the same project. Located in
 * `near_duplicate_css_in_js_tokens`.
 */
near_duplicate_css_in_js_tokens: number
/**
 * Number of distinct `font-size` units (`px` / `rem` / `em` / `%`) authored
 * across the codebase. Mixing units is a type-scale consistency smell,
 * broken out in `font_size_unit_mix`.
 */
font_size_units_used: number
/**
 * Number of analyzed stylesheets whose per-rule `notable_rules` list was
 * truncated at the per-file cap, so a consumer knows the per-rule detail is
 * incomplete without walking every file.
 */
notable_truncated_files: number
}
/**
 * A Vue SFC's `<style scoped>` classes that appear nowhere else in the
 * component (cleanup candidates).
 */
export interface ScopedUnusedClasses {
/**
 * Project-root-relative, forward-slash path to the SFC.
 */
path: string
/**
 * The scoped class names with no use elsewhere in the component, sorted.
 */
classes: string[]
/**
 * Read-only verification step(s) an agent can run before removing the
 * candidate. Always at least one entry, so consumers can iterate
 * `actions` uniformly across every finding type.
 */
actions: CssCandidateAction[]
}
/**
 * A read-only verification step attached to a CSS cleanup candidate.
 *
 * CSS candidates (unreferenced `@keyframes`, unused scoped classes) are never
 * auto-removed: an animation name can still be applied from JavaScript, and a
 * class can be assembled from a dynamic string binding. The action gives an
 * agent a machine-readable next step, mirroring the `actions` array carried by
 * every other health finding, plus an optional runnable probe to confirm the
 * candidate is genuinely unused before deleting it.
 */
export interface CssCandidateAction {
type: CssCandidateActionType
/**
 * Always `false`: CSS candidates are never auto-fixed (`fallow fix` does
 * not touch them) because the residual consumer may live outside CSS.
 */
auto_fixable: boolean
/**
 * Human-readable description of what to confirm before removing.
 */
description: string
/**
 * A runnable, read-only, placeholder-free token search that surfaces any
 * out-of-CSS use of the candidate. Absent when no shell-safe command can
 * be built (e.g. the residual risk is a dynamic string binding that a
 * single search cannot probe), in which case `description` is the guide.
 */
command?: (string | null)
}
/**
 * A `@keyframes` defined in a stylesheet but referenced by no animation in any
 * stylesheet (cleanup candidate).
 */
export interface UnreferencedKeyframes {
/**
 * The `@keyframes` name.
 */
name: string
/**
 * Project-root-relative, forward-slash path to the stylesheet that defines it.
 */
path: string
/**
 * Read-only verification step(s) an agent can run before removing the
 * candidate. Always at least one entry, so consumers can iterate
 * `actions` uniformly across every finding type.
 */
actions: CssCandidateAction[]
}
/**
 * An animation reference (`animation` / `animation-name`) to a `@keyframes`
 * name that is defined in no stylesheet anywhere in the project (the
 * "used-but-undefined" direction). Usually a typo or a removed animation;
 * occasionally a `@keyframes` defined in CSS-in-JS the CSS parser never sees.
 */
export interface UndefinedKeyframes {
/**
 * The referenced `@keyframes` name that resolves to no definition.
 */
name: string
/**
 * Project-root-relative, forward-slash path to the first stylesheet that
 * references it.
 */
path: string
/**
 * Read-only verification step(s) an agent can run before fixing the
 * reference. Always at least one entry, so consumers can iterate `actions`
 * uniformly across every finding type.
 */
actions: CssCandidateAction[]
}
/**
 * A group of style rules across the project that share an identical declaration
 * block: a copy-paste consolidation candidate (fallow's duplication signal
 * applied to CSS). Only blocks of 4+ declarations appearing in 2+ rules are
 * reported, so the signal stays a strong copy-paste indicator rather than
 * flagging legitimately-repeated small blocks.
 */
export interface CssDuplicateBlock {
/**
 * Declarations in the shared block.
 */
declaration_count: number
/**
 * Number of rules that share the block (always >= 2).
 */
occurrence_count: number
/**
 * Declarations removable by extracting the block into one shared rule:
 * `(occurrence_count - 1) * declaration_count`.
 */
estimated_savings: number
/**
 * The rules sharing the block, sorted by `(path, line)`.
 */
occurrences: CssBlockOccurrence[]
/**
 * Read-only guidance step(s), so consumers can iterate `actions`
 * uniformly across every finding type. Always at least one entry.
 */
actions: CssCandidateAction[]
}
/**
 * One occurrence of a duplicate declaration block.
 */
export interface CssBlockOccurrence {
/**
 * Project-root-relative, forward-slash path to the stylesheet.
 */
path: string
/**
 * 1-based line of the rule's first selector.
 */
line: number
}
/**
 * A duplicated CVA / shadcn variant class block.
 */
export interface CvaDuplicateVariantBlock {
/**
 * Normalized class block shared by several variant values.
 */
value: string
/**
 * Number of variant values with this class block.
 */
occurrence_count: number
/**
 * First locations of the duplicate values, sorted by path and line.
 */
occurrences: CssBlockOccurrence[]
/**
 * Read-only guidance step(s), so consumers can iterate `actions`
 * uniformly across every candidate type.
 */
actions: CssCandidateAction[]
}
/**
 * A CVA / shadcn variant class value that can reuse an existing styling token.
 */
export interface CvaVariantTokenDrift {
/**
 * Tailwind arbitrary-value utility inside the variant class string.
 */
class_token: string
/**
 * Normalized value inside the arbitrary utility.
 */
value: string
/**
 * Full normalized variant class block containing the token.
 */
variant_classes: string
/**
 * Project-root-relative, forward-slash path to the variant definition.
 */
path: string
/**
 * 1-based line of the variant class string.
 */
line: number
nearest_token: NearestStylingToken
/**
 * Read-only guidance step(s), so consumers can iterate `actions`
 * uniformly across every candidate type.
 */
actions: CssCandidateAction[]
}
/**
 * A styling token candidate that can replace or explain a finding.
 */
export interface NearestStylingToken {
/**
 * Token name, e.g. `--color-brand`.
 */
name: string
/**
 * Normalized token value.
 */
value: string
/**
 * Project-root-relative, forward-slash definition path.
 */
path: string
/**
 * 1-based definition line.
 */
line: number
/**
 * Distance from the finding value. Lower is closer; units depend on the
 * comparable token namespace.
 */
distance: number
}
/**
 * A distinct Tailwind arbitrary-value utility token used in markup, with its
 * total use count and first location (a design-token-bypass candidate).
 */
export interface TailwindArbitraryValue {
/**
 * The `prefix-[value]` token (e.g. `w-[13px]`). Variant prefixes are
 * stripped, so `hover:w-[13px]` and `w-[13px]` aggregate under `w-[13px]`.
 */
value: string
/**
 * Total occurrences across all scanned markup files.
 */
count: number
/**
 * Project-root-relative, forward-slash path to the first file using it.
 */
path: string
/**
 * 1-based line of the first occurrence.
 */
line: number
/**
 * Read-only action(s): a find-all-occurrences search so the token can be
 * replaced with a scale token. Always at least one entry, so consumers can
 * iterate `actions` uniformly across every finding type.
 */
actions: CssCandidateAction[]
}
/**
 * A located raw CSS declaration value on a scale-sensitive styling axis.
 */
export interface RawStyleValue {
/**
 * Value axis, e.g. `color`, `font-size`, `line-height`, `radius`, or `shadow`.
 */
axis: string
/**
 * CSS property where the raw value appears.
 */
property: string
/**
 * Rendered declaration value.
 */
value: string
/**
 * Project-root-relative, forward-slash path to the stylesheet.
 */
path: string
/**
 * 1-based line of the containing style rule.
 */
line: number
/**
 * Concrete token with the same or nearest comparable value, when resolved.
 */
nearest_token?: (NearestStylingToken | null)
/**
 * Read-only guidance step(s). Never auto-fixable.
 */
actions: CssCandidateAction[]
}
/**
 * An unused CSS at-rule entity (an `@property` registration with no `var()`
 * reference, or an `@layer` declaration never populated), located by its first
 * definition. A cleanup candidate, never a gated finding.
 */
export interface UnusedAtRule {
type: UnusedAtRuleKind
/**
 * The entity name (`--x` for `@property`, the layer name for `@layer`).
 */
name: string
/**
 * Project-root-relative, forward-slash path to the first defining stylesheet.
 */
path: string
/**
 * Read-only verification step(s) before removal (parity with other findings).
 */
actions: CssCandidateAction[]
}
/**
 * A static `class` / `className` token in markup that matches no CSS class
 * defined anywhere in the project but is one edit away from a class that IS
 * defined (a likely typo or stale rename). The CSS analogue of an unresolved
 * import. A candidate, never a gated finding: the token could be defined in
 * CSS-in-JS or an external stylesheet the parser never sees.
 */
export interface UnresolvedClassReference {
/**
 * The static class token referenced in markup (no dot).
 */
class: string
/**
 * The defined CSS class one edit away: the likely intended class.
 */
suggestion: string
/**
 * Project-root-relative, forward-slash path to the markup file.
 */
path: string
/**
 * 1-based line of the `class` / `className` attribute.
 */
line: number
/**
 * Read-only verification step(s) before fixing the reference. Always at
 * least one entry, so consumers can iterate `actions` uniformly across
 * every finding type.
 */
actions: CssCandidateAction[]
}
/**
 * A global CSS class defined in a plain `.css`/`.scss` rule whose literal name
 * is referenced by no in-project markup (the CSS analogue of an unused export).
 * A heavily-gated candidate, never a gated finding: the class may be applied
 * from an HTML email, server template, CMS, or Markdown the parser never sees.
 */
export interface UnreferencedCssClass {
/**
 * The class name (no dot).
 */
class: string
/**
 * Project-root-relative, forward-slash path to the defining stylesheet.
 */
path: string
/**
 * 1-based line of the class's first definition.
 */
line: number
/**
 * Read-only verification step(s) before removing. Always at least one entry,
 * so consumers can iterate `actions` uniformly across every finding type.
 */
actions: CssCandidateAction[]
}
/**
 * An `@font-face` family declared in a stylesheet but referenced by no
 * `font-family` anywhere in the project: a dead web-font payload. A cleanup
 * candidate (the family could be applied from inline styles or JavaScript).
 */
export interface UnusedFontFace {
/**
 * The declared font family name (quotes stripped).
 */
family: string
/**
 * Project-root-relative, forward-slash path to the declaring stylesheet.
 */
path: string
/**
 * Read-only verification step(s) before removing. Always at least one entry,
 * so consumers can iterate `actions` uniformly across every finding type.
 */
actions: CssCandidateAction[]
}
/**
 * A Tailwind v4 `@theme` design token defined in a stylesheet whose generated
 * utility, `var()` reads, and arbitrary-value references appear nowhere in the
 * project: a dead design token (the `unused-export` of the token era). A
 * candidate, never a gated finding: the token could be consumed by a Tailwind
 * plugin, a published design-system surface, or a non-CSS-aware build step the
 * scan cannot see (those cases are gated out before this is emitted).
 */
export interface UnusedThemeToken {
/**
 * The full custom property as authored, including the `--` prefix
 * (`--color-brand`).
 */
token: string
/**
 * The Tailwind v4 theme namespace the token belongs to (`color`, `radius`,
 * `font-weight`, `breakpoint`, ...).
 */
namespace: string
/**
 * Project-root-relative, forward-slash path to the declaring stylesheet.
 */
path: string
/**
 * 1-based line of the token's definition inside the `@theme` block.
 */
line: number
/**
 * Read-only verification step(s) before removing. Always at least one entry,
 * so consumers can iterate `actions` uniformly across every finding type.
 */
actions: CssCandidateAction[]
}
/**
 * A Tailwind v4 `@theme` token that appears to duplicate an existing token by
 * value. Emitted conservatively for comparable token namespaces, with the
 * nearest existing token named so an agent has a concrete reuse target.
 */
export interface NearDuplicateThemeToken {
/**
 * The full custom property as authored, including the `--` prefix.
 */
token: string
/**
 * The normalized authored token value.
 */
value: string
/**
 * Project-root-relative, forward-slash path to the token definition.
 */
path: string
/**
 * 1-based line of the token definition inside the `@theme` block.
 */
line: number
nearest_token: NearestStylingToken
/**
 * Read-only guidance step(s) before replacing the token reference.
 */
actions: CssCandidateAction[]
}
/**
 * A location-aware reverse index of where one design token is consumed, so an
 * agent editing the token can see its blast radius before changing or removing
 * it. Covers TWO token origins. The always-available discriminator is the `token`
 * SHAPE: a Tailwind token is the `--`-prefixed custom property (`--color-brand`),
 * a CSS-in-JS token is a dotted access path with no `--` prefix
 * (`vars.color.primary`). The per-consumer `kind` also discriminates origin, but
 * only when `consumer_count > 0` (a `consumer_count: 0` entry has an empty
 * `consumers` array and thus no `kind`), so branch on the `token` prefix for the
 * zero-consumer case. The two origins:
 *
 * - Tailwind v4 `@theme` tokens (kinds `theme-var` / `css-var` / `utility` /
 *   `apply`), built from the same gated candidate set as `unused_theme_tokens`
 *   (v4 + non-plugin + non-published + whole-scope), so a `consumer_count: 0`
 *   corroborates the `unused_theme_tokens` "nothing consumes this" finding.
 * - CSS-in-JS tokens (kind `js-member` / `js-call`) from StyleX `defineVars` /
 *   `unstable_defineVarsNested`, vanilla-extract `createTheme` family definitions,
 *   and PandaCSS `defineTokens`, consumed via same-file or cross-module member
 *   access, StyleX theme-group calls, or PandaCSS `token('...')` calls. NOTE:
 *   CSS-in-JS has NO corroborating dead-token finding (there is no
 *   `unused_theme_tokens` analogue), so a CSS-in-JS `consumer_count: 0` is a weaker
 *   signal than the Tailwind case (and unresolved dynamic imports or computed
 *   accesses are not counted).
 *
 * This is DESCRIPTIVE context (a blast-radius lookup), not a finding, so it
 * deliberately carries no `actions` array (unlike the cleanup-candidate types in
 * this module). `consumer_count` is always a STATIC lower bound (a computed class
 * name like `bg-${color}`, or a CSS-in-JS access through an unresolved alias
 * import, is not counted).
 */
export interface TokenConsumers {
/**
 * The token identity. For a Tailwind `@theme` token this is the full custom
 * property as authored, INCLUDING the `--` prefix (`--color-brand`). For a
 * CSS-in-JS token (kind `js-member` / `js-call`) this is the binding-qualified dotted
 * access path, NO `--` prefix (`vars.color.primary`), matching how consumers
 * read it. The presence of the `--` prefix distinguishes the two origins.
 */
token: string
/**
 * For a Tailwind token, the v4 theme namespace (`color`, `radius`,
 * `font-weight`, ...). For a CSS-in-JS token (kind `js-member` / `js-call`), the defining
 * export BINDING the token set is accessed through (`vars`), which identifies
 * the token set, NOT a semantic group. (The field is thus overloaded by
 * origin; branch on `consumers[].kind` or the `token` shape.)
 */
namespace: string
/**
 * Project-root-relative, forward-slash path to the declaring stylesheet
 * (Tailwind) or the JS/TS token-definition file (CSS-in-JS).
 */
definition_path: string
/**
 * 1-based line of the token's definition (inside the `@theme` block for
 * Tailwind; the token key inside the `defineVars`/`createTheme` object for
 * CSS-in-JS).
 */
definition_line: number
/**
 * The FULL number of consumer locations found, a STATIC LOWER BOUND: a
 * computed class name (`bg-${color}`), unresolved import, dynamic token
 * structure, or computed CSS-in-JS access is not counted. This is the
 * aggregate over every consumer, computed BEFORE
 * [`consumers`](Self::consumers) is capped to a sample.
 */
consumer_count: number
/**
 * A capped, deterministically-sorted sample of consumer locations (at most
 * [`TOKEN_CONSUMER_SAMPLE_CAP`]). The full count lives in
 * [`consumer_count`](Self::consumer_count); use this list to jump to
 * representative consumers, not to enumerate every one.
 */
consumers: TokenConsumerLocation[]
}
/**
 * Where one Tailwind or CSS-in-JS design token is consumed, and through which
 * surface. One entry in a [`TokenConsumers::consumers`] sample.
 */
export interface TokenConsumerLocation {
/**
 * Project-root-relative, forward-slash path to the consuming file.
 */
path: string
/**
 * 1-based line of the consuming reference in that file.
 */
line: number
kind: ConsumerKind
}
/**
 * A design-token notation-consistency candidate: the distinct notations used
 * across the codebase for one value axis (today, length units on `font-size`),
 * with a per-notation distinct-value count. Emitted only above a floor, since
 * mixing notations for one axis is a "no single source of truth" smell.
 * Advisory: the action is "standardize on one notation", not a single search.
 */
export interface CssNotationConsistency {
/**
 * The value axis these notations describe, e.g. `"Colors"` or
 * `"Font sizes"`.
 */
axis: string
/**
 * Per-notation distinct-value counts, sorted by count descending then
 * notation name (so the dominant notation is first and ties are stable).
 */
notations: CssNotationCount[]
/**
 * Read-only guidance step(s), so consumers can iterate `actions` uniformly
 * across every candidate type. Always at least one entry.
 */
actions: CssCandidateAction[]
}
/**
 * One notation bucket and the count of distinct values authored in it.
 */
export interface CssNotationCount {
/**
 * The notation family, e.g. `"hex"`, `"rgb"`, `"hsl"`, `"modern"`, `"px"`,
 * `"rem"`, `"em"`, `"%"`.
 */
notation: string
/**
 * Distinct values authored in this notation across the codebase.
 */
count: number
}
/**
 * Project-level styling-health score: a SECOND health axis computed purely from
 * the structural CSS analytics (`CssAnalyticsReport`), orthogonal to the JS/TS
 * code-health [`HealthScore`]. Surfaced only alongside the `--css` analytics, so
 * a plain `fallow health` run is byte-unchanged. The code score and grade stay
 * untouched: styling health is additive, never folded into the code score.
 *
 * Like [`HealthScore`], the score starts at 100 and subtracts capped per-category
 * penalties; the grade reuses the shared [`letter_grade`] thresholds verbatim
 * (A>=85, B>=70, C>=55, D>=40, F<40), so the two axes are read on one scale.
 */
export interface StylingHealth {
/**
 * Styling formula version; see [`STYLING_HEALTH_FORMULA_VERSION`].
 */
formula_version: number
/**
 * Styling-health score in `[0, 100]`; higher is healthier.
 */
score: number
/**
 * Letter grade from the shared [`letter_grade`] thresholds.
 */
grade: string
penalties: StylingHealthPenalties
confidence: StylingHealthConfidence
/**
 * Human-readable reason the grade is low-confidence: either the declaration
 * and stylesheet counts a thin grade was computed from, or that structure is
 * not assessable for compile-time-atomic CSS-in-JS. `None` when confidence is
 * `High`. Prose, not a stable machine field: gate on `confidence`, not on
 * this string.
 */
confidence_reason?: (string | null)
}
/**
 * Per-category penalty breakdown for the styling-health score. Each field is the
 * number of points subtracted from a starting 100 for one CSS signal family,
 * already capped at its category ceiling. A `0.0` field means "the signal was
 * evaluated and clean"; the whole struct is only ever built when CSS analytics
 * were produced, so there is no "missing pipeline" ambiguity to model with
 * `Option` here (the parent `StylingHealth` is itself `Option` on the report).
 */
export interface StylingHealthPenalties {
/**
 * Copy-paste declaration blocks (`duplicate_declaration_blocks`), scaled by
 * total removable declarations. Capped at 20pt.
 */
duplication: number
/**
 * Dead styling surface, two independently-normalized terms summed and capped
 * at 20pt: (a) unused `@theme` tokens as a share of the total `@theme` token
 * population (size-independent, so a declaration-sparse Tailwind project is
 * not penalized for a few dead tokens); plus (b) the other dead entities
 * (unreferenced classes, unused `@property`/`@layer` at-rules, dead
 * `@font-face` families) as a share of `total_declarations`.
 */
dead_surface: number
/**
 * Broken references: markup classes one edit from a defined class
 * (`unresolved_class_references`) and animations referencing a `@keyframes`
 * defined nowhere (`undefined_keyframes`). Capped at 15pt.
 */
broken_references: number
/**
 * Design-token erosion: mixed `font-size` units (`font_size_unit_mix`),
 * Tailwind arbitrary-value bypasses (`tailwind_arbitrary_values`), and
 * distinct HARDCODED `box-shadow`/`border-radius`/`line-height` values above
 * per-axis baselines (the v3 value-sprawl drift sub-term; `var(--*)`-
 * referenced values are not counted). Capped at 10pt.
 */
token_erosion: number
/**
 * Structural smells from the summary aggregates: `!important` density and
 * deep style-rule nesting. Capped at 10pt.
 */
structural: number
}
/**
 * One advisory STYLING FINDING: the graduation of a descriptive css candidate
 * into a first-class, severity-aware, suppressible finding surfaced in
 * `fallow audit`. The styling domain's OWN finding type (not borrowed into the
 * dead-code `AnalysisResults`, and not glued in the CLI). `code` is the kebab
 * IssueKind code (e.g. `css-token-drift`), so severity / inline suppression /
 * SARIF / MCP all resolve via the shared `issue_meta` contract through
 * `IssueKind::parse(code)`. One `Vec<StylingFinding>` carries every styling
 * family; the `code` discriminates.
 */
export interface StylingFinding {
/**
 * The kebab IssueKind code, e.g. `css-token-drift`.
 */
code: string
/**
 * The specific sub-kind within the family, e.g. `tailwind-arbitrary-value`.
 */
sub_kind: string
/**
 * Workspace-relative path of the finding.
 */
path: string
/**
 * 1-based line of the finding.
 */
line: number
/**
 * The offending literal value, e.g. `w-[13px]`.
 */
value: string
effective_severity: StylingFindingSeverity
/**
 * Optional static lower-bound blast radius. For a dead design token this is
 * `0`; for other styling findings it is omitted.
 */
blast_radius?: (number | null)
/**
 * Confidence hint for agents and review UIs. Structural findings are high,
 * reachability findings are low because dynamic consumers may exist.
 */
confidence?: (StylingFindingConfidence | null)
/**
 * Suggested handling posture for agents. This is advisory data, fallow
 * still never applies styling changes automatically.
 */
agent_disposition?: (StylingAgentDisposition | null)
/**
 * Concrete reuse target for token-drift findings, when one can be resolved.
 */
nearest_token?: (NearestStylingToken | null)
/**
 * One concise machine-readable edit hint for agent consumers.
 */
fix_hint?: (string | null)
/**
 * Suggested next steps (verify / suppress; never an auto-fix).
 */
actions: CssCandidateAction[]
/**
 * Audit-mode flag indicating whether the finding is new versus the base snapshot.
 */
introduced?: (boolean | null)
}
/**
 * Envelope emitted by `fallow explain <issue-type> --format json`.
 *
 * Standalone rule explanation. This command does not run project analysis
 * and intentionally returns a compact object without `schema_version` /
 * `version` metadata; consumers that need those should call any other
 * fallow JSON-producing command.
 */
export interface ExplainOutput {
/**
 * Issue-type identifier, e.g. `unused-export`.
 */
id: string
/**
 * Human-readable issue-type name.
 */
name: string
/**
 * One-line description of what the issue type reports.
 */
summary: string
/**
 * Why the issue matters.
 */
rationale: string
/**
 * Illustrative code example of the issue.
 */
example: string
/**
 * How to resolve findings of this type.
 */
how_to_fix: string
/**
 * Public documentation URL for the issue type.
 */
docs: string
}
/**
 * Envelope emitted by `fallow inspect --format json`.
 */
export interface InspectOutput {
target: InspectTargetDescriptor
identity: InspectIdentity
evidence: InspectEvidence
/**
 * Non-fatal problems encountered while gathering evidence.
 */
warnings: string[]
/**
 * `_meta` block with type-aware backend info, when applicable.
 */
_meta?: (Meta | null)
}
/**
 * Graph identity facts for a file target. `Value`-typed fields carry the
 * graph's verdict when analysis ran and are `null` when it did not.
 */
export interface InspectFileIdentity {
/**
 * File path relative to the analysed root.
 */
file: string
/**
 * Whether the module graph can reach the file from an entry point.
 */
is_reachable?: (boolean | null)
/**
 * Whether the file is itself an entry point.
 */
is_entry_point?: (boolean | null)
/**
 * Number of exports the file declares.
 */
export_count?: (number | null)
/**
 * Number of modules the file imports.
 */
import_count?: (number | null)
/**
 * Number of modules that import the file.
 */
imported_by_count?: (number | null)
}
/**
 * Graph identity facts for a symbol target. `Value`-typed fields carry the
 * graph's verdict when analysis ran and are `null` when it did not.
 */
export interface InspectSymbolIdentity {
/**
 * File path relative to the analysed root.
 */
file: string
/**
 * Name of the inspected export.
 */
export_name: string
/**
 * Whether the containing file is reachable from an entry point.
 */
file_reachable?: (boolean | null)
/**
 * Whether the containing file is itself an entry point.
 */
is_entry_point?: (boolean | null)
/**
 * Whether the export has any recorded consumer.
 */
is_used?: (boolean | null)
/**
 * Explanation of the usage verdict, when the graph recorded one.
 */
reason?: (string | null)
}
/**
 * `evidence` block of [`InspectOutput`]: one section per analysis.
 */
export interface InspectEvidence {
trace_file: InspectEvidenceSection
/**
 * Export-level usage trace; present only for symbol targets.
 */
trace_export?: (InspectEvidenceSection | null)
dead_code: InspectEvidenceSection
duplication: InspectEvidenceSection
complexity: InspectEvidenceSection
security: InspectEvidenceSection
impact_closure: InspectEvidenceSection
/**
 * OPT-IN target-level git churn. Omitted unless historical evidence was
 * explicitly requested by the caller.
 */
churn?: (InspectEvidenceSection | null)
/**
 * OPT-IN symbol-level call chain. Present only when `--symbol-chain` was
 * requested AND the target is a SYMBOL (best-effort, syntactic, OFF the
 * ranked path). `None` (omitted) by default: symbol-level chains are
 * best-effort and not part of the trusted ranked evidence.
 */
symbol_chain?: (InspectEvidenceSection | null)
/**
 * Checker-backed declaration, alias, re-export, and use-site evidence.
 * Present only for a symbol target in type-aware mode.
 */
semantic_trace?: (InspectEvidenceSection | null)
/**
 * Package-public TypeScript surface and private-type reachability.
 * Present only for a symbol target in type-aware mode.
 */
api_surface?: (InspectEvidenceSection | null)
/**
 * Exact-symbol consumers and transitive affected files.
 * Present only for a symbol target in type-aware mode.
 */
symbol_impact?: (InspectEvidenceSection | null)
/**
 * Test entry points reachable from the exact symbol, with provenance.
 * Present only for a symbol target in type-aware mode.
 */
targeted_tests?: (InspectEvidenceSection | null)
}
/**
 * One evidence section: status, the scope the evidence covers, and either a
 * data payload or a message explaining its absence.
 */
export interface InspectEvidenceSection {
status: InspectSectionStatus
scope: InspectEvidenceScope
/**
 * Explanation for `error` / `unavailable` sections.
 */
message?: (string | null)
/**
 * Section payload; present when the analysis ran.
 */
data?: {
[k: string]: unknown
}
}
/**
 * Result of tracing an export: why it is considered used or unused.
 */
export interface ExportTrace {
/**
 * The file containing the export.
 */
file: string
/**
 * The export name being traced.
 */
export_name: string
namespace?: SemanticNamespace
/**
 * Whether the file is reachable from an entry point.
 */
file_reachable: boolean
/**
 * Whether the file is an entry point.
 */
is_entry_point: boolean
/**
 * Whether the export is considered used.
 */
is_used: boolean
/**
 * Files that reference this export directly.
 */
direct_references: ExportReference[]
/**
 * Reachable direct references grouped by namespace. This is additive to
 * `namespace` and `direct_references`, whose winning-lane meaning remains
 * unchanged for backwards compatibility.
 */
direct_references_by_namespace?: NamespacedExportReferences[]
/**
 * A star-export collision that makes the traced name ambiguous. When
 * present, `is_used: false` is an abstention rather than an unused-code
 * verdict.
 */
star_export_ambiguity?: (StarExportAmbiguity | null)
/**
 * Re-export chains that pass through this export.
 */
re_export_chains: ReExportChain[]
/**
 * Human-readable reason summary.
 */
reason: string
/**
 * Exact checker-backed references when type-aware tracing is enabled.
 */
semantic?: (SemanticSymbolTrace | null)
}
/**
 * A direct reference to an export.
 */
export interface ExportReference {
/**
 * File that contains the reference.
 */
from_file: string
/**
 * Reference kind, such as named import, default import, or re-export.
 */
kind: string
}
/**
 * Direct references that credit one namespace of an export binding.
 */
export interface NamespacedExportReferences {
namespace: SemanticNamespace
/**
 * Number of reachable references in this namespace.
 */
reference_count: number
/**
 * Reachable references in deterministic graph order.
 */
references: ExportReference[]
}
/**
 * The `export *` collision that keeps a name from being exported.
 */
export interface StarExportAmbiguity {
/**
 * Files that each declare a colliding declaration under the traced name
 * (project-root-relative), sorted. These are the origins to fix: keep one,
 * rename or explicitly re-export the rest.
 */
sources: string[]
/**
 * The namespaces the collision occurs in, type before value. A name can
 * collide in type space, value space, or both.
 */
namespaces: SemanticNamespace[]
}
/**
 * A re-export chain showing how an export is propagated.
 */
export interface ReExportChain {
/**
 * The barrel file that re-exports this symbol.
 */
barrel_file: string
/**
 * The name it is re-exported as.
 */
exported_as: string
/**
 * Number of references on the barrel's re-exported symbol.
 */
reference_count: number
}
/**
 * Result of tracing a class / enum / store MEMBER: the `--trace FILE:NAME`
 * fallback when `NAME` is not a top-level export but a member declared on one
 * (issue #1744). The trace runs on the module graph only, so it reports the
 * OWNING export's reachability and usage (the gating precondition for
 * member-level crediting) plus a pointer to the right `--unused-*-members`
 * command, rather than per-member crediting provenance.
 */
export interface ClassMemberTrace {
/**
 * The file containing the member.
 */
file: string
/**
 * The member name being traced.
 */
member_name: string
/**
 * The member kind: `class-method`, `class-property`, `enum-member`,
 * `store-member`, or `namespace-member`.
 */
member_kind: string
/**
 * The export that declares this member (the class / enum / store name).
 */
owner_export: string
owner_namespace?: SemanticNamespace
/**
 * Whether the owning export is considered used.
 */
owner_is_used: boolean
/**
 * Whether the file is reachable from an entry point.
 */
owner_file_reachable: boolean
/**
 * Whether the file is an entry point.
 */
owner_is_entry_point: boolean
/**
 * Files that reference the owning export directly.
 */
owner_direct_references: ExportReference[]
/**
 * Re-export chains through which the owning export is reachable. Populated
 * so a machine consumer can tell "used via a barrel" (empty direct refs but
 * non-empty chains) from "genuinely unreferenced".
 */
owner_re_export_chains: ReExportChain[]
/**
 * Human-readable reason summary plus the follow-up command to inspect the
 * member finding.
 */
reason: string
/**
 * Exact checker-backed member references when type-aware tracing is enabled.
 */
semantic?: (SemanticSymbolTrace | null)
}
/**
 * Result of tracing all edges for a file.
 */
export interface FileTrace {
/**
 * The traced file.
 */
file: string
/**
 * Whether this file is reachable from entry points.
 */
is_reachable: boolean
/**
 * Whether this file is an entry point.
 */
is_entry_point: boolean
/**
 * Exports declared by this file.
 */
exports: TracedExport[]
/**
 * Files that this file imports from.
 */
imports_from: string[]
/**
 * Files that import from this file.
 */
imported_by: string[]
/**
 * Re-exports declared by this file.
 */
re_exports: TracedReExport[]
/**
 * The configs that make this file an entry point through Module
 * Federation `exposes`, one per config. Absent when no Federation config
 * exposes the file (issue #2796).
 */
sources?: TraceSource[]
}
/**
 * An export with usage information.
 */
export interface TracedExport {
/**
 * Export name.
 */
name: string
/**
 * Whether the export is type-only.
 */
is_type_only: boolean
/**
 * Number of references to this export.
 */
reference_count: number
/**
 * Files that reference this export.
 */
referenced_by: ExportReference[]
}
/**
 * A re-export with source information.
 */
export interface TracedReExport {
/**
 * Source file being re-exported from.
 */
source_file: string
/**
 * Imported symbol name.
 */
imported_name: string
/**
 * Exported symbol name.
 */
exported_name: string
}
/**
 * A config that names a traced file or a traced dependency, and the key that
 * names it.
 */
export interface TraceSource {
/**
 * The mechanism that names the file or the dependency:
 * `module-federation`. The set is open.
 */
kind: string
/**
 * The plugin that read the config, as it labels itself:
 * `module-federation` for a standalone `module-federation.config.*`,
 * or the bundler plugin (`webpack`, `rspack`, `rsbuild`, `vite`,
 * `nextjs`) that read the same options inline from its own config.
 */
plugin: string
/**
 * The file that names the file or the dependency, relative to the
 * project root: the config file, or the source file of a Module
 * Federation runtime call.
 */
config: string
/**
 * The config key or the runtime function that names the file or the
 * dependency: `exposes` for an exposed file, `remotes` for a remote
 * alias, and `registerRemotes`, `loadRemote`, `init` or `createInstance`
 * for a remote that a runtime call names. The set is open.
 */
key: string
}
/**
 * Result of tracing a dependency: where it is used.
 */
export interface DependencyTrace {
/**
 * The dependency name being traced.
 */
package_name: string
/**
 * Files that import this dependency.
 */
imported_by: string[]
/**
 * Files that import this dependency with type-only imports.
 */
type_only_imported_by: string[]
/**
 * Whether the dependency is invoked from package.json scripts, CI configs
 * or git hooks.
 */
used_in_scripts: boolean
/**
 * Whether the dependency is used at all: imported, invoked from scripts,
 * or listed as a peer by a used package (`peer_of`).
 */
is_used: boolean
/**
 * Total import count.
 */
import_count: number
/**
 * Used packages that list this dependency in their installed
 * `peerDependencies`, required or optional, sorted by name. The
 * unused-dependency check credits such a peer, because the package that
 * lists it loads it at runtime. Absent when no used package lists it.
 */
peer_of?: string[]
/**
 * The configs that declare this name as a Module Federation remote alias
 * under `remotes`, one per config. A remote alias is provided by a
 * remote container at runtime, not by an npm package. Absent when no
 * Federation config declares the name (issue #2796).
 */
sources?: TraceSource[]
/**
 * Why the unused devDependency check credits the dependency as tooling
 * when no file imports it and no script, CI workflow or git hook runs it.
 * When present, `is_used` is `true`. Absent otherwise.
 */
tooling_credit?: (ToolingCredit | null)
/**
 * The manifests that the unused-dependency check flags for this name,
 * relative to the project root and sorted. The check reads each
 * declaring manifest on its own. An import credits the nearest manifest
 * that installs the package, so a name that one workspace uses can still
 * be unused in the root manifest or in another workspace. Absent when no
 * manifest is flagged.
 */
unused_in?: string[]
}
/**
 * Why the unused devDependency check counts a dependency as used tooling
 * although no source file imports it.
 */
export interface ToolingCredit {
/**
 * The evidence: `plugin-config` when the plugin that declares the
 * dependency found its own config file, `plugin-reference` when a
 * package.json script, a CI workflow or a git hook runs one of that
 * plugin's packages, `ambient-types` for a type package that declares
 * globals, `types-target` when the project declares or imports the
 * package that a `@types/` package types, `types-config` when a config
 * file, such as a tsconfig `types` entry, names the type package,
 * `known-tooling` for a library from the tooling catalogue,
 * `known-tooling-config` when a command-line tool from the catalogue has
 * its own config file, and `own-peer` when the same manifest lists the
 * devDependency in `peerDependencies`. The set is open.
 */
reason: string
/**
 * The plugin that declares the dependency as tooling.
 */
plugin?: (string | null)
/**
 * The config file found, relative to the project root, for
 * `plugin-config` and `known-tooling-config`. A `package.json` path when
 * the config is a package.json key. For `own-peer`, the manifest that
 * lists the dependency.
 */
config?: (string | null)
/**
 * The package that a script, CI workflow or git hook runs, for
 * `plugin-reference`, or the package that a `@types/` package types,
 * for `types-target`.
 */
reference?: (string | null)
}
/**
 * Result of tracing a clone: all groups containing the code at a source
 * location or addressed by a stable clone fingerprint.
 */
export interface CloneTrace {
/**
 * File passed to the trace request, root-relative when a group matches.
 */
file: string
/**
 * 1-based line passed to the trace request or representative group line.
 */
line: number
/**
 * The matched clone instance, if one exists.
 */
matched_instance?: (CloneInstance | null)
/**
 * Clone groups matched by the trace request.
 */
clone_groups: TracedCloneGroup[]
}
/**
 * One clone group returned from a clone trace request.
 */
export interface TracedCloneGroup {
/**
 * Stable content fingerprint, usually `dup:<8hex>` and widened on rare
 * report collisions.
 */
fingerprint: string
/**
 * Number of tokens in the duplicated block.
 */
token_count: number
/**
 * Number of lines in the duplicated block.
 */
line_count: number
/**
 * Maximum directory-tree or same-file line distance between instances.
 */
spread: number
/**
 * Lowest all-pairs similarity for a near-miss clone group.
 */
similarity?: number
/**
 * Root-relative clone instances in this group.
 */
instances: CloneInstance[]
suggestion: RefactoringSuggestion
/**
 * Best-effort name for the extracted function. Advisory only.
 */
suggested_name?: (string | null)
}
/**
 * Result of computing the impact closure for a single file as the seed.
 */
export interface ImpactClosureTrace {
/**
 * The seed file, root-relative.
 */
seed: string
/**
 * Root-relative paths transitively affected by the seed.
 */
affected_not_shown: string[]
/**
 * Coordination gaps between the seed and consumers.
 */
coordination_gap: ImpactClosureGap[]
}
/**
 * One coordination-gap entry in an [`ImpactClosureTrace`].
 */
export interface ImpactClosureGap {
/**
 * Root-relative path of the consumer module.
 */
consumer_file: string
/**
 * Exported symbol names the consumer references.
 */
consumed_symbols: string[]
/**
 * Scope note for the syntactic trace.
 */
note: string
}
/**
 * Result of asking how one module reaches another: the shortest import path.
 *
 * `reachable` is the only field that separates "no route exists" from "the
 * route is empty because both ends are the same module". Both report
 * `hops: 0`, so a consumer must read `reachable`, never the hop count.
 */
export interface ImportPathTrace {
schema_version: ImportPathTraceSchemaVersion
/**
 * The module the walk started from, root-relative.
 */
from: string
/**
 * The module the walk was looking for, root-relative.
 */
to: string
/**
 * Whether `to` is reachable from `from` by following import edges.
 */
reachable: boolean
/**
 * Number of import edges on the reported route. `0` both when the two ends
 * are the same module and when there is no route at all.
 */
hops: number
/**
 * The route, in import order. Empty whenever `hops` is `0`.
 */
path: ImportPathHop[]
/**
 * Human-readable summary of the outcome.
 */
reason: string
}
/**
 * One import edge on an [`ImportPathTrace`].
 */
export interface ImportPathHop {
/**
 * The importing module, root-relative.
 */
from: string
/**
 * The imported module, root-relative.
 */
to: string
/**
 * Whether every symbol on this edge is type-only, so the hop is erased at
 * build time. Type-only hops are reported, never skipped: an `import type`
 * chain is a real compile-time coupling.
 */
type_only: boolean
/**
 * Whether the edge carries a runtime value but no static one: the target
 * loads only on demand (`import()`, a lazy glob or template pattern) or
 * on another thread (a worker URL, a worker loader request,
 * `child_process.fork`). False for a
 * static hop and for a type-only hop.
 */
dynamic: boolean
/**
 * 1-based line in `from` of the imported binding that creates this edge:
 * the first value-carrying symbol on the import, or the first symbol when
 * every symbol is type-only. On a multi-line import that is the binding's
 * own line, not the `import` keyword's. Absent when the edge carries no
 * span or the source could not be read.
 */
import_line?: (number | null)
}
/**
 * The result of a symbol-level call-chain trace. Its own surface (`kind:
 * "trace"`), NOT folded into the ranked brief.
 */
export interface SymbolChainTrace {
/**
 * The file containing the traced symbol (project-root-relative).
 */
file: string
/**
 * The traced symbol name.
 */
symbol: string
/**
 * Whether the symbol's defining export was found in the graph. When
 * `false`, the chains are empty and `reason` explains why.
 */
symbol_found: boolean
/**
 * The chain depth applied to both directions.
 */
depth: number
/**
 * Whether this trace is best-effort (always `true`: symbol-level chains are
 * labeled best-effort, syntactic per ADR-001).
 */
best_effort: boolean
/**
 * Caller chain hops (UP). Present only when `--callers` was requested.
 */
callers?: (ChainHop[] | null)
/**
 * Callee chain hops (DOWN) resolved to an import-symbol edge. Present only
 * when `--callees` was requested.
 */
callees?: (ChainHop[] | null)
/**
 * Callees referenced at a call site in the symbol's module that the
 * syntactic walk could NOT resolve to an import-symbol edge (locals,
 * globals, dynamic dispatch, re-bound callees). Reported, never dropped.
 * Present only when `--callees` was requested.
 */
unresolved_callees?: (UnresolvedCallee[] | null)
/**
 * Set when the name is unresolvable because two different `export *`
 * sources of this file supply it. `symbol_found` is `false` in that case
 * for the same reason it is false for an unknown name (the file exports
 * nothing under it per ECMA-262 ResolveExport), so this field is the only
 * thing that separates a barrel mistake from a typo.
 */
star_export_ambiguity?: (StarExportAmbiguity | null)
/**
 * A human-readable summary of the trace outcome.
 */
reason: string
}
/**
 * One hop in a caller / callee chain.
 */
export interface ChainHop {
/**
 * The file at this hop (project-root-relative). For a caller hop this is
 * the importing module; for a callee hop the imported module.
 */
file: string
/**
 * The symbol name as imported across the edge (`default`, `*` for namespace,
 * the imported name otherwise).
 */
imported_as: string
/**
 * The local binding name in the file at this hop.
 */
local_name: string
/**
 * Whether the import edge is type-only (`import type { ... }`).
 */
type_only: boolean
/**
 * The hop's depth (1 = direct caller/callee of the symbol).
 */
depth: number
}
/**
 * A callee referenced at a call site that did not resolve to an import-symbol
 * edge. Surfaced so a missing callee is never silently dropped.
 */
export interface UnresolvedCallee {
/**
 * The callee path as written at the call site (e.g. `helper`,
 * `obj.method`).
 */
callee: string
reason: UnresolvedReason
}
/**
 * Result of resolving a runtime stack trace against the project graph.
 */
export interface ErrorTrace {
schema_version: ErrorTraceSchemaVersion
/**
 * Where the trace was read from: `stdin`, or the path as the caller wrote
 * it.
 */
source: string
/**
 * The first non-blank input line that preceded any recognised frame,
 * verbatim. Conventionally the error type and message, but it is reported
 * as read and NOT parsed into parts. Absent when the input began with a
 * frame or was empty.
 */
header?: (string | null)
/**
 * Every recognised frame, in input order. Nothing is filtered out: a
 * dependency or runtime-internal frame stays in the array with its origin
 * recorded, so hop numbering matches the trace the caller pasted.
 */
frames: ErrorTraceFrame[]
counts: ErrorTraceCounts
/**
 * Human-readable summary of the outcome.
 */
reason: string
}
/**
 * One frame read from the input stack trace.
 */
export interface ErrorTraceFrame {
/**
 * 0-based position in the input trace, so a caller can quote a frame back
 * even after filtering the array.
 */
index: number
/**
 * The input line this frame was read from, trimmed of surrounding
 * whitespace and otherwise verbatim.
 */
raw: string
/**
 * The frame's function identifier as written by the runtime, with the
 * `async` and `new` markers stripped and recorded separately. Absent for a
 * frame the runtime emitted without one.
 */
function?: (string | null)
/**
 * Whether the runtime marked this frame as a constructor call (`new X`).
 */
is_constructor?: boolean
/**
 * Whether the runtime marked this frame as an async call.
 */
is_async?: boolean
/**
 * The frame's file as read from the trace, with any `file://` or
 * `http(s)://` wrapper removed and separators forward-slashed. Reported as
 * read: it is NOT rewritten to the module path it matched, so a caller can
 * see what its runtime actually said. Absent for a frame with no location.
 */
file?: (string | null)
/**
 * 1-based line from the frame's location, when the runtime supplied one.
 */
line?: (number | null)
/**
 * 1-based column from the frame's location, when the runtime supplied one.
 */
column?: (number | null)
origin: FrameOrigin
resolution: FrameResolution
/**
 * Every definition the identifier could name, in deterministic order.
 * Exactly one entry when `resolution` is `resolved`, more than one when it
 * is `ambiguous`, and empty otherwise.
 */
candidates: ErrorTraceCandidate[]
/**
 * How many further candidates a presentation cap withheld.
 * `candidates.len() + candidates_omitted` is the true match count, so an
 * `ambiguous` frame never understates how ambiguous it is.
 */
candidates_omitted: number
/**
 * Set when this frame's own line disagrees with the definition its
 * identifier matched: some OTHER definition in the same file is declared
 * closer above the line the runtime reported.
 *
 * The look-up matches on the identifier alone, so a `resolved` frame is
 * resolved however far its line sits from the match. That is honest about
 * the question asked and silent about a question a reader would ask next,
 * which is why the disagreement is published instead of left to be
 * noticed. The frame is NOT reclassified: the graph does know a
 * definition under this identifier, and only the caller can say whether
 * the runtime ran that one or a same-named definition elsewhere.
 * `reason` names the declaration that sits closer. Only set on a
 * `resolved` frame that carried a line and matched a definition whose own
 * line could be read.
 */
line_mismatch?: boolean
/**
 * Human-readable statement of what happened to this frame.
 */
reason: string
}
/**
 * One definition a frame's identifier could name.
 */
export interface ErrorTraceCandidate {
/**
 * Root-relative file declaring the definition.
 */
file: string
/**
 * The exported name. For a member match this is the owning export.
 */
symbol: string
/**
 * The member name, when the frame's identifier named a member of
 * `symbol` rather than `symbol` itself. Absent for a direct export match.
 */
member?: (string | null)
/**
 * What kind of definition this is: `export`, or the member kind
 * (`class-method`, `class-property`, `enum-member`, `store-member`,
 * `namespace-member`).
 */
kind: string
/**
 * 1-based declaration line of the definition's identifier. Absent when the
 * source file could not be read; never guessed.
 */
line?: (number | null)
}
/**
 * Per-outcome totals for an [`ErrorTrace`].
 *
 * `resolved + ambiguous + not_found + not_attempted == frames`, and
 * `in_project + node_modules + out_of_corpus == frames`. Both identities hold
 * on every run, so a caller can verify that nothing was dropped.
 */
export interface ErrorTraceCounts {
/**
 * Frames reported in `frames`.
 */
frames: number
/**
 * Frames a cap withheld from `frames`. Their outcomes are NOT counted in
 * the fields below, which describe the reported frames only.
 */
frames_omitted: number
/**
 * Frames whose file resolved to project source.
 */
in_project: number
/**
 * Frames whose file lives under an installed dependency tree.
 */
node_modules: number
/**
 * Frames outside the analysed corpus, including frames with no location.
 */
out_of_corpus: number
/**
 * Frames that matched exactly one definition.
 */
resolved: number
/**
 * Frames that matched more than one definition.
 */
ambiguous: number
/**
 * Frames the graph was asked about and could not name.
 */
not_found: number
/**
 * Frames the graph was never asked about.
 */
not_attempted: number
/**
 * Non-blank input lines that were neither recognised as a frame nor taken
 * as `header`. A trace that is entirely unrecognised reports zero frames
 * and a non-zero count here, rather than looking like an empty trace.
 */
unparsed_lines: number
}
/**
 * Envelope emitted by `fallow --format review-github` / `review-gitlab`.
 */
export interface ReviewEnvelopeOutput {
event?: (ReviewEnvelopeEvent | null)
body: string
summary?: ReviewEnvelopeSummary
comments: ReviewComment[]
marker_regex?: string
marker_regex_flags?: string
meta: ReviewEnvelopeMeta
}
/**
 * Summary block on [`ReviewEnvelopeOutput`].
 */
export interface ReviewEnvelopeSummary {
/**
 * Summary comment body markdown.
 */
body: string
/**
 * Stable fingerprint of the summary body, used for sticky updates.
 */
fingerprint: string
}
/**
 * GitHub pull-request review comment.
 */
export interface GitHubReviewComment {
/**
 * File path relative to the repository root.
 */
path: string
/**
 * 1-based line on the new side of the diff.
 */
line: number
side: GitHubReviewSide
/**
 * Comment body markdown, fingerprint marker included.
 */
body: string
/**
 * Stable finding fingerprint used for comment reconciliation.
 */
fingerprint: string
/**
 * The fingerprint that an older Fallow release wrote into the
 * `fallow-fingerprint:v2:` marker of this comment, when it is different
 * from `fingerprint`. For one release, an existing comment whose marker
 * holds this value is the same comment. Omitted when equal.
 */
legacy_fingerprint?: (string | null)
/**
 * True when the body was cut to fit the provider size limit; omitted
 * when false.
 */
truncated?: boolean
}
/**
 * GitLab merge-request discussion comment.
 */
export interface GitLabReviewComment {
/**
 * Comment body markdown, fingerprint marker included.
 */
body: string
position: GitLabReviewPosition
/**
 * Stable finding fingerprint used for comment reconciliation.
 */
fingerprint: string
/**
 * The fingerprint that an older Fallow release wrote into the
 * `fallow-fingerprint:v2:` marker of this comment, when it is different
 * from `fingerprint`. For one release, an existing comment whose marker
 * holds this value is the same comment. Omitted when equal.
 */
legacy_fingerprint?: (string | null)
/**
 * True when the body was cut to fit the provider size limit; omitted
 * when false.
 */
truncated?: boolean
}
/**
 * `position` block inside [`GitLabReviewComment`]. Mirrors the GitLab
 * merge-request discussion-position API.
 */
export interface GitLabReviewPosition {
/**
 * Merge-base SHA of the MR diff; absent when refs were not supplied.
 */
base_sha?: (string | null)
/**
 * First commit SHA of the MR diff; absent when refs were not supplied.
 */
start_sha?: (string | null)
/**
 * Head commit SHA of the MR diff; absent when refs were not supplied.
 */
head_sha?: (string | null)
position_type: GitLabReviewPositionType
/**
 * Pre-rename path when the diff renamed the file, else the same as
 * `new_path`.
 */
old_path: string
/**
 * File path on the new side of the diff.
 */
new_path: string
/**
 * 1-based line on the new side of the diff.
 */
new_line: number
}
/**
 * `meta` block inside [`ReviewEnvelopeOutput`].
 */
export interface ReviewEnvelopeMeta {
schema: ReviewEnvelopeSchema
provider: ReviewProvider
check_conclusion?: (ReviewCheckConclusion | null)
review_id?: (ReviewId | null)
}
/**
 * Envelope emitted by `fallow ci reconcile-review --format json`. Used by
 * CI integrations to drive comment carry-over and stale-comment cleanup
 * across PR / MR revisions.
 */
export interface ReviewReconcileOutput {
schema: ReviewReconcileSchema
provider: ReviewProvider
/**
 * PR / MR reference that was reconciled, when one was resolved.
 */
target?: (string | null)
/**
 * True when no provider mutations were performed.
 */
dry_run: boolean
/**
 * Inline comments in the review envelope being reconciled.
 */
comments: number
/**
 * Distinct fingerprints in the current run's findings.
 */
current_fingerprints: number
/**
 * Distinct fingerprints with an open Fallow lifecycle, including
 * provider-resolved discussions not yet closed by a Fallow marker.
 */
existing_fingerprints: number
/**
 * Fingerprints present in the current run but not yet commented.
 */
new_fingerprints: number
/**
 * Fingerprints with open Fallow lifecycles whose findings no longer exist.
 */
stale_fingerprints: number
/**
 * The new fingerprints themselves.
 */
new: string[]
/**
 * The stale fingerprints themselves.
 */
stale: string[]
/**
 * Non-fatal provider API warning encountered during reconciliation.
 */
provider_warning?: (string | null)
/**
 * Resolution replies posted to stale comment threads.
 */
resolution_comments_posted: number
/**
 * Provider discussion threads resolved or re-closed.
 */
threads_resolved: number
/**
 * Remediation guidance when the apply loop stopped before finishing.
 */
apply_hint?: (string | null)
/**
 * Errors encountered while applying provider mutations.
 */
apply_errors: string[]
/**
 * Fingerprints whose provider mutation failed.
 */
failed_fingerprints?: string[]
/**
 * Fingerprints left unprocessed after a failure aborted the apply loop.
 */
unapplied_fingerprints?: string[]
}
/**
 * `fallow coverage setup --json` envelope.
 */
export interface CoverageSetupOutput {
schema_version: CoverageSetupSchemaVersion
framework_detected: CoverageSetupFramework
/**
 * Package manager detected from lockfiles, when one was found.
 */
package_manager?: (CoverageSetupPackageManager | null)
/**
 * Runtimes the instrumentation must cover at the project root.
 */
runtime_targets: CoverageSetupRuntimeTarget[]
/**
 * Per-member setup guidance for workspace projects.
 */
members: CoverageSetupMember[]
/**
 * Coverage config that was written to disk, when setup wrote one.
 */
config_written?: {
[k: string]: unknown
}
/**
 * Shell commands the user should run to complete setup.
 */
commands: string[]
/**
 * Files the user must edit by hand, with reasons.
 */
files_to_edit: CoverageSetupFileToEdit[]
/**
 * Ready-to-paste code snippets for the files to edit.
 */
snippets: CoverageSetupSnippet[]
/**
 * Dockerfile additions needed for containerized capture, when relevant.
 */
dockerfile_snippet?: (string | null)
/**
 * Ordered human-readable follow-up instructions.
 */
next_steps: string[]
/**
 * Non-fatal problems encountered during detection.
 */
warnings: string[]
/**
 * `_meta` block with docs and field definitions, when requested.
 */
_meta?: {
[k: string]: unknown
}
}
/**
 * Per-workspace-member setup guidance inside [`CoverageSetupOutput::members`].
 */
export interface CoverageSetupMember {
/**
 * Package name of the workspace member.
 */
name: string
/**
 * Member path relative to the workspace root.
 */
path: string
framework_detected: CoverageSetupFramework
/**
 * Package manager detected for this member, when one was found.
 */
package_manager?: (CoverageSetupPackageManager | null)
/**
 * Runtimes the instrumentation must cover for this member.
 */
runtime_targets: CoverageSetupRuntimeTarget[]
/**
 * Files the user must edit by hand, with reasons.
 */
files_to_edit: CoverageSetupFileToEdit[]
/**
 * Ready-to-paste code snippets for the files to edit.
 */
snippets: CoverageSetupSnippet[]
/**
 * Dockerfile additions needed for containerized capture, when relevant.
 */
dockerfile_snippet?: (string | null)
/**
 * Non-fatal problems encountered during detection.
 */
warnings: string[]
}
/**
 * One manual edit the user must make to wire up coverage capture.
 */
export interface CoverageSetupFileToEdit {
/**
 * File path relative to the project root.
 */
path: string
/**
 * Why the file needs editing.
 */
reason: string
}
/**
 * Ready-to-paste code snippet accompanying a file edit.
 */
export interface CoverageSetupSnippet {
/**
 * Short description of what the snippet does.
 */
label: string
/**
 * File path the snippet belongs in.
 */
path: string
/**
 * The snippet source text.
 */
content: string
}
/**
 * Envelope emitted by `fallow coverage analyze --format json`.
 */
export interface CoverageAnalyzeOutput {
schema_version: CoverageAnalyzeSchemaVersion
version: ToolVersion
elapsed_ms: ElapsedMs
runtime_coverage: RuntimeCoverageReport
/**
 * `_meta` block with docs and metric definitions, when `--explain` was
 * passed.
 */
_meta?: (Meta | null)
}
/**
 * Envelope emitted by `fallow list --boundaries --format json`. Surfaces
 * the architecture boundary zones, rules, and the user's pre-expansion
 * `autoDiscover` logical groups so consumers can render grouping intent that
 * expansion would otherwise flatten out of `zones[]`.
 */
export interface ListBoundariesOutput {
boundaries: BoundariesListing
}
/**
 * `boundaries` block carried by [`ListBoundariesOutput`].
 */
export interface BoundariesListing {
/**
 * Whether the project configures architecture boundaries at all.
 */
configured: boolean
/**
 * Number of entries in `zones`.
 */
zone_count: number
/**
 * Boundary zones after preset and `autoDiscover` expansion.
 */
zones: BoundariesListZone[]
/**
 * Number of entries in `rules`.
 */
rule_count: number
/**
 * Import rules operating on expanded zone names.
 */
rules: BoundariesListRule[]
/**
 * Number of entries in `logical_groups`.
 */
logical_group_count: number
/**
 * Pre-expansion `autoDiscover` logical groups.
 */
logical_groups: BoundariesListLogicalGroup[]
}
/**
 * A boundary zone after preset and `autoDiscover` expansion. Each entry
 * classifies files into a single zone via glob patterns.
 */
export interface BoundariesListZone {
/**
 * Zone name referenced by rules.
 */
name: string
/**
 * Glob patterns that classify files into the zone.
 */
patterns: string[]
/**
 * Number of analyzable files the zone matched.
 */
file_count: number
}
/**
 * A boundary import rule, expanded to operate on concrete child zone
 * names after `autoDiscover` flattening. The user's pre-expansion rule
 * (keyed on the logical parent name, if any) is preserved on the
 * corresponding [`BoundariesListLogicalGroup::authored_rule`].
 */
export interface BoundariesListRule {
/**
 * Zone the rule constrains imports from.
 */
from: string
/**
 * Zone names the `from` zone may import.
 */
allow: string[]
}
/**
 * A pre-expansion `autoDiscover` logical group surfaced for observability.
 * Captured during expansion so consumers can see the user-authored parent
 * name and grouping intent after expansion would otherwise flatten it out of
 * [`BoundariesListing::zones`].
 */
export interface BoundariesListLogicalGroup {
/**
 * User-authored parent zone name.
 */
name: string
/**
 * Child zone names produced by discovery.
 */
children: string[]
/**
 * Authored `autoDiscover` paths.
 */
auto_discover: string[]
status: LogicalGroupStatus
/**
 * Index of the authored entry in the pre-expansion `zones[]` config.
 */
source_zone_index: number
/**
 * Files matched across the group's zones.
 */
file_count: number
/**
 * User's pre-expansion rule keyed on the parent name, when authored.
 */
authored_rule?: (AuthoredRule | null)
/**
 * Zone that keeps the parent's own patterns when the parent kept any.
 */
fallback_zone?: (string | null)
/**
 * `zones[]` indices of duplicate parents merged into this group.
 */
merged_from?: (number[] | null)
/**
 * Authored parent `root`, when one was declared.
 */
original_zone_root?: (string | null)
/**
 * Per-child indices into the pre-expansion `zones[]` config.
 */
child_source_indices?: number[]
}
/**
 * Pre-expansion rule preserved on a [`LogicalGroup`].
 */
export interface AuthoredRule {
/**
 * Authored `allow` list.
 */
allow: string[]
/**
 * Authored `allowTypeOnly` list.
 */
allow_type_only?: string[]
}
/**
 * `fallow workspaces --format json` envelope.
 */
export interface WorkspacesOutput {
/**
 * Number of workspace package entries in `workspaces`.
 */
workspace_count: number
/**
 * Workspace packages discovered from package manager and tsconfig workspace
 * declarations. Paths are project-root-relative and use forward slashes.
 */
workspaces: WorkspaceInfo[]
/**
 * Workspace discovery diagnostics produced while reading workspace
 * declarations. Paths are project-root-relative and use forward slashes,
 * like `workspaces[].path` and like the `workspace_diagnostics[]` array on
 * the analysis envelopes. Present for compatibility with the current wire
 * contract, even when empty.
 */
workspace_diagnostics: WorkspaceDiagnostic[]
}
/**
 * One workspace package emitted by `fallow workspaces --format json`.
 */
export interface WorkspaceInfo {
/**
 * Package name from the workspace package.json. This is the value accepted
 * by `--workspace <name>`.
 */
name: string
/**
 * Project-root-relative path to the workspace directory, normalized to
 * forward slashes for cross-platform JSON consumers.
 */
path: string
/**
 * Whether the package is a generated or platform-specific dependency
 * package rather than a hand-authored workspace.
 */
is_internal_dependency: boolean
}
/**
 * Envelope emitted by `fallow health --format json` (plus the `health` block
 * inside the combined and audit envelopes).
 *
 * The body is `HealthReport` flattened into the envelope so every report
 * field (`findings`, `summary`, `vital_signs`, `hotspots`, `actions_meta`,
 * ...) lives at the top level. Grouped runs populate `grouped_by` +
 * `groups` with per-bucket recomputed metrics. The `actions_meta`
 * breadcrumb is modeled on `HealthReport` as an `Option<HealthActionsMeta>`
 * and is set at construction time by the report builder when the active
 * `HealthActionContext` requests suppress-line omission, so the schema
 * documents the field and serde populates it natively.
 */
export interface HealthOutput {
schema_version: HealthSchemaVersion
version: ToolVersion
elapsed_ms: ElapsedMs
/**
 * Functions and synthetic template entries exceeding complexity
 * thresholds, sorted by the --sort criteria. Each entry wraps its
 * inner `ComplexityViolation` payload (flattened on the wire) with
 * the typed `actions` list and an optional audit-mode `introduced`
 * flag.
 */
findings: HealthFinding[]
summary: HealthSummary
/**
 * The sections that this run produced, in a fixed order. A renderer
 * reads it to tell an empty section from a section that the run did not
 * produce: `findings` is the complexity list only when `complexity` is
 * in this array. The value set is OPEN (see [`HealthSection`]). Absent
 * in an envelope from a fallow version before this member, and on a
 * report that no health run built.
 */
sections?: (HealthSection[] | null)
/**
 * Configured threshold override states. Entries are emitted for active
 * exceptions, stale exceptions, and full-run no-match cleanup hints.
 */
threshold_overrides?: ThresholdOverrideState[]
/**
 * Project-wide vital signs (always computed from available data).
 */
vital_signs?: (VitalSigns | null)
/**
 * Project-wide health score (only populated with `--score`).
 */
health_score?: (HealthScore | null)
/**
 * Per-file health scores. Only present when --file-scores is used. Sorted
 * by risk-aware triage concern, combining low maintainability and high
 * CRAP risk. Zero-function files (barrels) are excluded by default.
 */
file_scores?: FileHealthScore[]
/**
 * Static coverage gaps.
 *
 * Populated when coverage gaps are explicitly requested, or when the
 * top-level `health` command allows config severity to surface them in the
 * default report.
 */
coverage_gaps?: (CoverageGaps | null)
/**
 * Located prop-drilling chains (React/Preact props forwarded unchanged
 * through 3+ pass-through components). Only present when the opt-in
 * `prop-drilling` rule is enabled (it defaults to off). Each entry carries
 * the source, every pass-through hop, and the consumer with file + line +
 * component, so CI / an agent can act. Surfaced alongside hotspots as a
 * graph-derived health signal.
 */
prop_drilling_chains?: PropDrillingChainFinding[]
/**
 * Hotspot entries combining git churn with complexity. Only present when
 * --hotspots is used. Sorted by score descending (highest risk first).
 * Each entry wraps its inner `HotspotEntry` payload (flattened on the
 * wire) with a typed `actions` list.
 */
hotspots?: HotspotFinding[]
/**
 * Hotspot analysis summary.
 *
 * Set whenever the run measured churn, which needs readable git history;
 * `--hotspots` adds the per-file [`hotspots`](Self::hotspots) listing
 * beside it rather than gating this summary.
 */
hotspot_summary?: (HotspotSummary | null)
/**
 * Runtime coverage findings from the paid sidecar (only populated with
 * `--runtime-coverage`).
 */
runtime_coverage?: (RuntimeCoverageReport | null)
/**
 * Combined coverage, runtime, complexity, and change-scope verdicts.
 */
coverage_intelligence?: (CoverageIntelligenceReport | null)
/**
 * Functions exceeding 60 LOC (very high risk). Only present when unit size
 * very-high-risk bin >= 3%. Sorted by line count descending.
 */
large_functions?: LargeFunctionEntry[]
/**
 * Ranked refactoring recommendations. Only present when --targets is used.
 * Sorted by efficiency (priority/effort) descending. Each entry wraps
 * its inner `RefactoringTarget` payload (flattened on the wire) with
 * a typed `actions` list.
 */
targets?: RefactoringTargetFinding[]
/**
 * Adaptive thresholds used for target scoring (only set with `--targets`).
 */
target_thresholds?: (TargetThresholds | null)
/**
 * Health trend comparison against a previous snapshot (only set with `--trend`).
 */
health_trend?: (HealthTrend | null)
/**
 * Audit breadcrumb explaining systemic action-array adjustments. Present
 * only when at least one adjustment was made (e.g., health finding
 * suppression hints omitted because a baseline is active). When --group-by
 * is active, each entry of `groups` may carry its own `actions_meta`
 * describing the same omission so per-group consumers do not need to walk
 * back to the report root.
 */
actions_meta?: (HealthActionsMeta | null)
/**
 * Optional framework-specific detector coverage. Present only when the
 * health run already needed the dead-code analysis output.
 */
framework_health?: (FrameworkHealthDiagnostics | null)
/**
 * Structural CSS analytics (specificity hotspots, `!important` density,
 * over-complex selectors, deep nesting). Present only with `--css`.
 */
css_analytics?: (CssAnalyticsReport | null)
/**
 * Styling-health score and letter grade: a SECOND health axis derived from
 * the CSS analytics (the design-system axis), orthogonal to the JS/TS code
 * `health_score`. Present only with `--css` (the same condition as
 * `css_analytics`), so a plain `fallow health` run is byte-unchanged. The
 * code score is never affected by this field.
 */
styling_health?: (StylingHealth | null)
/**
 * Advisory STYLING FINDINGS: the graduation of the descriptive css
 * candidates into first-class, severity-aware, suppressible findings
 * surfaced in `fallow audit`. Verdict-neutral by default (the rule defaults
 * to `warn`); the styling domain's OWN findings collection, not the dead-code
 * `AnalysisResults`. Present only with `--css`; empty is skipped so a plain
 * run is byte-unchanged.
 */
styling_findings?: StylingFinding[]
/**
 * Grouping mode when `--group-by` was passed.
 */
grouped_by?: (GroupByMode | null)
/**
 * Per-bucket recomputed metrics; present only in grouped output.
 */
groups?: (HealthGroup[] | null)
/**
 * The `--group` selector patterns, as given, when the run kept only some
 * groups. A group that is not in `groups` was filtered out by this
 * selector. The project-level sections are not filtered.
 */
group_filter?: (string[] | null)
/**
 * The verdict of every gate this run evaluated, keyed by name. The CLI
 * always emits it, with the command's default exit rule in it also when
 * no flag armed a gate, so a CI integration reads the verdict instead of
 * guessing from a process status it usually cannot see. A gate fails the
 * build when `status` is `fail` AND `enforced` is true. The typed
 * programmatic API runs no CLI gate and leaves it absent. See
 * [`crate::GateOutcomes`].
 */
gate_outcomes?: (GateOutcomes | null)
/**
 * Every narrowing or shaping request this run RECEIVED, keyed by name,
 * absent when it was asked for nothing. An entry whose `status` is not
 * `applied` means the run could not do what it was asked and reported
 * something WIDER instead, so what follows is a valid report of a scope
 * nobody requested. Honoured requests are published too, with
 * `status: "applied"`, so an absent object means "nothing was asked for",
 * never "nothing failed". See [`crate::RequestOutcomes`].
 */
request_outcomes?: (RequestOutcomes | null)
/**
 * `_meta` block with metric definitions, when `--explain` was passed.
 */
_meta?: (Meta | null)
/**
 * Workspace-discovery, source-discovery, and analysis-stage diagnostics
 * for the run. See `CheckOutput::workspace_diagnostics` for the full
 * contract: the kinds each stage records, project-root-relative paths,
 * omitted when empty.
 */
workspace_diagnostics?: WorkspaceDiagnostic[]
/**
 * Read-only follow-up commands computed from this run's findings. See
 * `CheckOutput::next_steps` for the contract.
 */
next_steps?: NextStep[]
}
/**
 * A health report scoped to a single group.
 *
 * `key` is the group label produced by the resolver (workspace package name,
 * CODEOWNERS owner, directory, or section). `owners` is populated only for
 * `--group-by section` (mirrors dead-code grouped output).
 *
 * Per-group `vital_signs` and `health_score` are recomputed from the
 * files in the group, so they answer "what is the health of workspace X" in
 * a single invocation. `files_analyzed` and `functions_above_threshold`
 * summarise the subset for parity with the project-level
 * project-level health summary.
 *
 * A group carries a per-file list (`findings`, `file_scores`, `hotspots`,
 * `large_functions`, `targets`) only when the project report shows the same
 * list. A `--score` run keeps the score and the counts of each group and
 * omits the lists.
 */
export interface HealthGroup {
/**
 * Group identifier produced by the resolver. For 'package' grouping:
 * workspace package name (e.g. '@scope/app-a') or '(root)' for files
 * outside any workspace. For 'owner' grouping: the CODEOWNERS team. For
 * 'directory' grouping: the top-level directory prefix. For 'section'
 * grouping: the GitLab CODEOWNERS section name, or '(no section)' /
 * '(unowned)' for unmatched files.
 */
key: string
/**
 * Section default owners (GitLab CODEOWNERS `[Section] @owner1 @owner2`).
 * Present only when grouped_by is 'section'.
 */
owners?: (string[] | null)
/**
 * Files participating in this group after workspace and ignore filters.
 */
files_analyzed: number
/**
 * Number of findings in this group, mirroring the project-level
 * `summary.functions_above_threshold` semantics post-baseline /
 * post-`--top` truncation. When `--top` was supplied this reflects the
 * rendered finding count of the group, not the un-truncated total.
 */
functions_above_threshold: number
/**
 * Number of critical-severity findings in this group, after the baseline
 * filter and before `--top`. The project `summary.severity_critical_count`
 * counts before the baseline filter, so with `--baseline` the group
 * counts can add up to less.
 */
severity_critical_count: number
/**
 * Number of high-severity findings in this group, after the baseline
 * filter and before `--top`. The project `summary.severity_high_count`
 * counts before the baseline filter, so with `--baseline` the group
 * counts can add up to less.
 */
severity_high_count: number
/**
 * Number of moderate-severity findings in this group, after the baseline
 * filter and before `--top`. The project `summary.severity_moderate_count`
 * counts before the baseline filter, so with `--baseline` the group
 * counts can add up to less.
 */
severity_moderate_count: number
/**
 * Number of ranked hotspot entries in this group, before `--top`. This
 * is the length of the group's ranked hotspot list. It is not
 * `vital_signs.hotspot_count`, which counts only the files with a
 * hotspot score of 50 or more and feeds the health score.
 */
hotspot_count: number
/**
 * Whether CRAP findings in this group share a single coverage-source kind
 * (`uniform`) or combine Istanbul / estimated / inherited sources
 * (`mixed`). Absent when no grouped finding carries CRAP source data.
 */
coverage_source_consistency?: (CoverageSourceConsistency | null)
/**
 * Per-group vital signs recomputed from the files in this group. Absent
 * when --score-only suppressed top-level vital signs.
 */
vital_signs?: (VitalSigns | null)
/**
 * Per-group health score recomputed from the per-group vital signs. Absent
 * when --score was not requested. The duplication penalty counts each
 * clone group with two or more instances in total and one or more in
 * this group, and counts only the lines of the instances in this group.
 * A clone that spans two groups thus lowers the score of each group.
 * `dupes --group-by` assigns each clone group to one owner instead.
 */
health_score?: (HealthScore | null)
/**
 * Trend of this group against the same group in the baseline snapshot.
 * Present only when `--trend` or `--trend-from` was requested and the
 * baseline holds this group with the same `grouped_by` mode.
 */
trend?: (HealthTrend | null)
/**
 * Why `trend` is present or absent. Present only when a trend was
 * requested and a baseline snapshot was loaded.
 */
trend_status?: (GroupTrendStatus | null)
/**
 * Findings restricted to files in this group. Each entry is the typed
 * [`HealthFinding`] wrapper around a
 * `ComplexityViolation`
 * payload.
 */
findings?: HealthFinding[]
/**
 * File scores restricted to files in this group.
 */
file_scores?: FileHealthScore[]
/**
 * Hotspots restricted to files in this group. Each entry is the typed
 * [`HotspotFinding`] wrapper around a
 * `HotspotEntry` payload.
 */
hotspots?: HotspotFinding[]
/**
 * Large functions in files belonging to this group.
 */
large_functions?: LargeFunctionEntry[]
/**
 * Refactoring targets in files belonging to this group. Each entry is
 * the typed [`RefactoringTargetFinding`] wrapper around a
 * `RefactoringTarget`
 * payload.
 */
targets?: RefactoringTargetFinding[]
/**
 * Auditable breadcrumb recording why `suppress-line` action hints
 * were omitted from this group's findings. Mirrors the project-level
 * `HealthReport.actions_meta`; populated at construction time when the
 * per-group `HealthActionContext`
 * suppresses inline hints.
 */
actions_meta?: (HealthActionsMeta | null)
}
/**
 * Envelope emitted by `fallow dupes --format json`.
 *
 * `Report` and `Group` are generic so the envelope can live in
 * `fallow-output` while duplication report wrappers and grouped output
 * internals continue to migrate out of CLI/API-specific crates.
 */
export interface DupesOutput {
schema_version: DupesSchemaVersion
version: ToolVersion
elapsed_ms: ElapsedMs
/**
 * All detected clone groups, each wrapped with typed actions.
 */
clone_groups: CloneGroupFinding[]
/**
 * Clone families, each wrapped with typed actions. Inner `groups`
 * inside each `CloneFamilyFinding` are themselves wrapped as
 * `CloneGroupFinding` entries carrying their own `actions[]` (and
 * optional audit-mode `introduced` flag), so JSON-Schema strict
 * consumers and TS consumers reading `clone_families[].groups[]` see
 * the same shape as the top-level `clone_groups[]` array (preserves
 * the issue #393 regression contract).
 */
clone_families: CloneFamilyFinding[]
/**
 * Mirrored directory pairs.
 */
mirrored_directories?: MirroredDirectory[]
stats: DuplicationStats
/**
 * Number of clone groups carried in `clone_groups[]`.
 */
clone_groups_shown: number
/**
 * Number of scoped-corpus clone groups withheld from `clone_groups[]` by
 * a presentation cap such as `--top`. `0` on an untruncated run, so
 * `clone_groups_shown + clone_groups_omitted == stats.clone_groups`
 * always holds and `stats` keeps describing the whole measured corpus.
 */
clone_groups_omitted: number
/**
 * Number of clone families carried in `clone_families[]`.
 */
clone_families_shown: number
/**
 * Number of scoped-corpus clone families withheld from `clone_families[]`
 * by a presentation cap such as `--top`, which rebuilds the families from
 * the groups that survived the cap. `0` on an untruncated run, so
 * `clone_families_shown + clone_families_omitted == stats.clone_families`
 * always holds and `stats` keeps describing the whole measured corpus.
 */
clone_families_omitted: number
/**
 * Grouping mode when `--group-by` was passed.
 */
grouped_by?: (GroupByMode | null)
/**
 * Total finding count across all groups; present only in grouped output.
 */
total_issues?: (number | null)
/**
 * Grouped findings; present only in grouped output.
 */
groups?: (DuplicationGroup[] | null)
/**
 * This run's view of the loaded baseline, present only in baseline runs.
 * Carries the staleness counts, the advisory verdict and `gate_trips`, the
 * same boolean `--fail-on-stale-baseline` exits on, so a CI integration
 * reads one field instead of restating the rule. Read `change_scoped`
 * before dividing `matched_entries` by `baseline_entries`: a narrowed run
 * can report `matched_entries: 0` on a healthy baseline.
 */
baseline_staleness?: (BaselineStaleness | null)
/**
 * The verdict of every gate this run armed, keyed by name, absent when
 * it armed none. `dupes` has no default exit rule: a run with no armed
 * gate always exits 0, so an absent object means that the run passed.
 * `--fail-on-issues` and `--ci` arm `duplication-findings`, which fails
 * on any clone group. A gate fails the build when `status` is `fail` AND
 * `enforced` is true. See [`crate::GateOutcomes`].
 */
gate_outcomes?: (GateOutcomes | null)
/**
 * Every narrowing or shaping request this run RECEIVED, keyed by name,
 * absent when it was asked for nothing. An entry whose `status` is not
 * `applied` means the run could not do what it was asked and reported
 * something WIDER instead, so what follows is a valid report of a scope
 * nobody requested. Honoured requests are published too, with
 * `status: "applied"`, so an absent object means "nothing was asked for",
 * never "nothing failed". See [`crate::RequestOutcomes`].
 */
request_outcomes?: (RequestOutcomes | null)
/**
 * Applied package Git refs, omitted outside package-baseline runs.
 */
package_baselines?: PackageBaselineStatus[]
/**
 * `_meta` block with metric / rule definitions, emitted when `--explain`
 * is passed (always present in MCP responses).
 */
_meta?: (Meta | null)
/**
 * Workspace-discovery and source-discovery diagnostics for the run
 * (issue #473). See `CheckOutput::workspace_diagnostics` for the full
 * contract; the same list is repeated on each top-level command's
 * envelope so single-command consumers see it without having to look at
 * a separate top-level field. A standalone `fallow dupes` run has no
 * dead-code analyze pass, so the two analysis-stage kinds never appear
 * here.
 */
workspace_diagnostics?: WorkspaceDiagnostic[]
/**
 * Read-only follow-up commands computed from this run's findings. See
 * `CheckOutput::next_steps` for the contract.
 */
next_steps?: NextStep[]
}
/**
 * A single grouped duplication bucket. Per-group `stats` are dedup-aware and
 * computed over the FULL group BEFORE any `--top` truncation.
 */
export interface DuplicationGroup {
/**
 * Group label (owner / directory / package / section). `(unowned)` for
 * files with no CODEOWNERS rule, `(no section)` for pre-section rules in
 * section mode.
 */
key: string
stats: DuplicationStats
/**
 * Clone groups attributed to this owner, each wrapped with the typed
 * `actions[]` array. Each group's `primary_owner` is its largest-owner
 * key; per-instance `owner` lets consumers see cross-bucket fan-out
 * without re-resolving paths.
 */
clone_groups: AttributedCloneGroupFinding[]
/**
 * Clone families overlapping this bucket, each wrapped with the typed
 * `actions[]` array.
 */
clone_families: CloneFamilyFinding[]
}
/**
 * Wire-shape envelope for an [`AttributedCloneGroup`] finding (per-bucket
 * duplication attribution emitted under `fallow dupes --group-by`).
 * Flattens the attributed group and carries the same typed
 * `CloneGroupAction` array as `CloneGroupFinding`; no `introduced`
 * field because `fallow audit` does not run on grouped output.
 */
export interface AttributedCloneGroupFinding {
/**
 * Largest-owner attribution: the resolver key with the most instances in
 * this clone group. Ties broken alphabetically (smallest key wins).
 */
primary_owner: string
/**
 * Number of tokens in the clone group.
 */
token_count: number
/**
 * Number of source lines in the clone group.
 */
line_count: number
/**
 * Lowest all-pairs similarity for a near-miss clone group.
 */
similarity?: number
/**
 * Each instance carries its own `owner` field alongside the standard
 * CloneInstance shape.
 */
instances: AttributedInstance[]
/**
 * Stable content fingerprint, usually `dup:<8hex>` and widened on rare
 * report collisions. Addressable via `fallow dupes --trace dup:<fp>`.
 * Computed from the group's instances, so it matches the top-level
 * `clone_groups[].fingerprint` for the same clone.
 */
fingerprint: string
/**
 * Maximum directory-tree or same-file line distance between instances.
 */
spread: number
/**
 * Suggested next steps. Always emitted.
 */
actions: CloneGroupAction[]
}
/**
 * A clone instance plus its per-instance owner key (for inline JSON / SARIF
 * rendering).
 *
 * Each instance carries its own `owner` field alongside the standard
 * `CloneInstance` shape (file / start_line / end_line / start_col / end_col /
 * fragment), so consumers can attribute instances to resolver keys without
 * re-resolving paths.
 */
export interface AttributedInstance {
/**
 * Path to the file containing this clone instance.
 */
file: string
/**
 * 1-based start line of the clone.
 */
start_line: number
/**
 * 1-based end line of the clone.
 */
end_line: number
/**
 * 0-based start column.
 */
start_col: number
/**
 * 0-based end column.
 */
end_col: number
/**
 * The actual source code fragment.
 *
 * Omitted from JSON when the caller asked for a location-only payload
 * (`fallow dupes --no-fragments`, and the MCP `find_dupes` default). The
 * five location fields above address the same text, so a consumer that
 * wants the source reads it from the file.
 */
fragment?: string
/**
 * Whether the file path is a symlink, or lies under a symlinked
 * directory, inside the project root. Omitted when `false`.
 *
 * A clone with a symlinked instance can be the same file under two
 * paths, not copied code. `duplicates.ignoreSymlinks` (or
 * `fallow dupes --ignore-symlinks`) removes these instances.
 */
is_symlink?: boolean
/**
 * Resolver key for this specific instance (per-instance, not the
 * group-level largest-owner).
 */
owner: string
}
/**
 * Envelope emitted by `fallow dead-code --group-by ... --format json`.
 *
 * Issues are partitioned into resolver buckets (CODEOWNERS team, directory
 * prefix, workspace package, or GitLab CODEOWNERS section) instead of flat
 * arrays. Each bucket carries the same issue-array shape as the ungrouped
 * `CheckOutput` body, plus per-group `key` / `owners` / `total_issues`.
 */
export interface CheckGroupedOutput {
schema_version: CheckSchemaVersion
version: ToolVersion
elapsed_ms: ElapsedMs
grouped_by: GroupByMode
/**
 * Total findings across all groups.
 */
total_issues: number
/**
 * One bucket per resolver key.
 */
groups: CheckGroupedEntry[]
/**
 * `true` when the `unused-load-data-key` detector abstained for the whole
 * project. The abstain has no file, so it is on the root and not in a
 * group. An empty `unused_load_data_keys` with this flag set does not
 * mean the project is clean: the rule could not run safely. Serialized
 * only when `true`, like the flat `CheckOutput` field.
 */
unused_load_data_keys_global_abstain?: boolean
/**
 * This run's view of the loaded baseline, present only in baseline runs.
 * Carries the staleness counts, the advisory verdict and `gate_trips`, the
 * same boolean `--fail-on-stale-baseline` exits on, so a CI integration
 * reads one field instead of restating the rule. Read `change_scoped`
 * before dividing `matched_entries` by `baseline_entries`: a narrowed run
 * can report `matched_entries: 0` on a healthy baseline.
 */
baseline_staleness?: (BaselineStaleness | null)
/**
 * The answer to `--finding-id`, present only when the run received one
 * or more `--finding-id` values. The report then holds only the
 * requested findings. Read `missing` as resolved only when `conclusive`
 * is true; a scope, a baseline or a filter can hide a finding that still
 * exists. See [`crate::FindingIdQuery`].
 */
finding_id_query?: (FindingIdQuery | null)
/**
 * The verdict of every gate this run evaluated, keyed by name. The CLI
 * always emits it, with the command's default exit rule in it also when
 * no flag armed a gate, so a CI integration reads the verdict instead of
 * guessing from a process status it usually cannot see. A gate fails the
 * build when `status` is `fail` AND `enforced` is true. The typed
 * programmatic API runs no CLI gate and leaves it absent. See
 * [`crate::GateOutcomes`].
 */
gate_outcomes?: (GateOutcomes | null)
/**
 * Every narrowing or shaping request this run RECEIVED, keyed by name,
 * absent when it was asked for nothing. An entry whose `status` is not
 * `applied` means the run could not do what it was asked and reported
 * something WIDER instead, so what follows is a valid report of a scope
 * nobody requested. Honoured requests are published too, with
 * `status: "applied"`, so an absent object means "nothing was asked for",
 * never "nothing failed". See [`crate::RequestOutcomes`].
 */
request_outcomes?: (RequestOutcomes | null)
/**
 * Applied package Git refs, omitted outside package-baseline runs.
 */
package_baselines?: PackageBaselineStatus[]
/**
 * `_meta` block with docs and rule definitions, when `--explain` was
 * passed.
 */
_meta?: (Meta | null)
/**
 * Diagnostics collected for the full analysis before issue grouping.
 * See [`CheckOutput::workspace_diagnostics`] for the contract.
 */
workspace_diagnostics?: WorkspaceDiagnostic[]
/**
 * Read-only follow-up commands computed from the full (ungrouped) findings.
 * See [`CheckOutput::next_steps`] for the contract.
 */
next_steps?: NextStep[]
}
/**
 * Single resolver bucket inside `CheckGroupedOutput`. Carries the group's
 * identifier, optional section owners, and a per-group flattened
 * `AnalysisResults`.
 */
export interface CheckGroupedEntry {
/**
 * Resolver key: team name, directory prefix, package name, or section.
 */
key: string
/**
 * Owners of a GitLab CODEOWNERS section; present for section grouping.
 */
owners?: (string[] | null)
/**
 * Findings in this group.
 */
total_issues: number
/**
 * Files not reachable from any entry point. Wrapped in
 * [`UnusedFileFinding`] so each entry carries a typed `actions` array
 * natively, replacing the pre-2.76 post-pass injection.
 */
unused_files: UnusedFileFinding[]
/**
 * Exports never imported by other modules. Wrapped in
 * [`UnusedExportFinding`] so each entry carries a typed `actions`
 * array natively.
 */
unused_exports: UnusedExportFinding[]
/**
 * Type exports never imported by other modules. Wrapped in
 * [`UnusedTypeFinding`]: the inner [`UnusedExport`] struct is shared
 * with `unused_exports` but the wrapper emits a type-targeted fix
 * description.
 */
unused_types: UnusedTypeFinding[]
/**
 * Exported symbols whose public signature references same-file private
 * types. Wrapped in [`PrivateTypeLeakFinding`] so each entry carries a
 * typed `actions` array natively.
 */
private_type_leaks: PrivateTypeLeakFinding[]
/**
 * Exports marked `@deprecated` that still have at least one consumer in
 * a reachable file. Wrapped in [`DeprecatedExportInUseFinding`]. Opt-in: the
 * `deprecated-exports-in-use` rule defaults to `off`.
 */
deprecated_exports_in_use?: DeprecatedExportInUseFinding[]
/**
 * Dependencies listed in package.json but never imported. Wrapped in
 * [`UnusedDependencyFinding`] so each entry carries a typed `actions`
 * array natively. The fix action swaps from `remove-dependency` to
 * `move-dependency` when `used_in_workspaces` is non-empty.
 */
unused_dependencies: UnusedDependencyFinding[]
/**
 * Dev dependencies listed in package.json but never imported. Wrapped
 * in [`UnusedDevDependencyFinding`]: same bare struct as
 * `unused_dependencies` with a `devDependencies`-targeted fix
 * description.
 */
unused_dev_dependencies: UnusedDevDependencyFinding[]
/**
 * Optional dependencies listed in package.json but never imported.
 * Wrapped in [`UnusedOptionalDependencyFinding`] with an
 * `optionalDependencies`-targeted fix description.
 */
unused_optional_dependencies: UnusedOptionalDependencyFinding[]
/**
 * Enum members never accessed. Wrapped in
 * [`UnusedEnumMemberFinding`] so each entry carries a typed `actions`
 * array natively.
 */
unused_enum_members: UnusedEnumMemberFinding[]
/**
 * Class members never accessed. Wrapped in
 * [`UnusedClassMemberFinding`]: same inner [`UnusedMember`] struct as
 * `unused_enum_members`, with a class-targeted fix description and the
 * `auto_fixable: false` default to reflect dependency-injection
 * patterns.
 */
unused_class_members: UnusedClassMemberFinding[]
/**
 * Store members (Pinia `state` / `getters` / `actions` key, or a
 * setup-store returned key) declared but never accessed by any consumer
 * project-wide. Wrapped in [`UnusedStoreMemberFinding`]: same inner
 * [`UnusedMember`] struct as `unused_class_members`, with a
 * store-targeted fix description. Cross-graph: the store binding is
 * imported (the module is reachable) yet a specific member is dead.
 */
unused_store_members?: UnusedStoreMemberFinding[]
/**
 * Import specifiers that could not be resolved. Wrapped in
 * [`UnresolvedImportFinding`] so each entry carries a typed `actions`
 * array natively.
 */
unresolved_imports: UnresolvedImportFinding[]
/**
 * Dependencies used in code but not listed in package.json. Wrapped in
 * [`UnlistedDependencyFinding`].
 */
unlisted_dependencies: UnlistedDependencyFinding[]
/**
 * Exports with the same name across multiple modules. Wrapped in
 * [`DuplicateExportFinding`] so each entry carries a typed `actions`
 * array natively, with the position-0 `add-to-config` `ignoreExports`
 * snippet wired in at wrapper construction.
 */
duplicate_exports: DuplicateExportFinding[]
/**
 * Production dependencies only used via type-only imports (could be
 * devDependencies). Only populated in production mode. Wrapped in
 * [`TypeOnlyDependencyFinding`].
 */
type_only_dependencies: TypeOnlyDependencyFinding[]
/**
 * Production dependencies only imported by test files (could be
 * devDependencies). Wrapped in [`TestOnlyDependencyFinding`].
 */
test_only_dependencies?: TestOnlyDependencyFinding[]
/**
 * devDependencies imported by production (non-test, non-config) source code
 * via a runtime/value import; they should be promoted to dependencies.
 * The promote-side mirror of [`TestOnlyDependencyFinding`]. Wrapped in
 * [`DevDependencyInProductionFinding`].
 */
dev_dependencies_in_production?: DevDependencyInProductionFinding[]
/**
 * Circular dependency chains detected in the module graph. Wrapped in
 * [`CircularDependencyFinding`] so each entry carries a typed `actions`
 * array natively.
 */
circular_dependencies: CircularDependencyFinding[]
/**
 * Cycles or self-loops in the re-export edge subgraph (barrel files
 * re-exporting from each other in a loop). Wrapped in
 * [`ReExportCycleFinding`] so each entry carries a typed `actions`
 * array natively (a `refactor-re-export-cycle` informational primary
 * plus a `suppress-file` secondary; cycles are file-scoped so a single
 * suppression breaks the cycle).
 */
re_export_cycles?: ReExportCycleFinding[]
/**
 * Dependency cycles between workspace packages, built from resolved
 * cross-package imports. Wrapped in [`PackageCycleFinding`] so each
 * entry carries a typed `actions` array natively.
 */
package_cycles?: PackageCycleFinding[]
/**
 * Imports that cross architecture boundary rules. Wrapped in
 * [`BoundaryViolationFinding`] so each entry carries a typed `actions`
 * array natively.
 */
boundary_violations?: BoundaryViolationFinding[]
/**
 * Files that matched no architecture boundary zone while
 * `boundaries.coverage.requireAllFiles` was enabled.
 */
boundary_coverage_violations?: BoundaryCoverageViolationFinding[]
/**
 * Calls from zoned files to callees forbidden for that zone via
 * `boundaries.calls.forbidden`. Wrapped in
 * [`BoundaryCallViolationFinding`] so each entry carries a typed
 * `actions` array natively.
 */
boundary_call_violations?: BoundaryCallViolationFinding[]
/**
 * Banned calls, imports, and catalogue-derived effects matched by
 * declarative rule packs
 * (`rulePacks` config). Wrapped in [`PolicyViolationFinding`] so each
 * entry carries a typed `actions` array natively. Each finding carries
 * its effective per-rule severity.
 */
policy_violations?: PolicyViolationFinding[]
/**
 * Suppression comments or JSDoc tags that no longer match any issue.
 */
stale_suppressions?: StaleSuppression[]
/**
 * Entries in package manager catalog sections not referenced by any
 * workspace package via the catalog: protocol. Supports
 * `pnpm-workspace.yaml` catalogs and Bun root `package.json` catalogs.
 * Wrapped in [`UnusedCatalogEntryFinding`] so each entry carries a typed
 * `actions` array natively, with per-instance `auto_fixable` derived
 * from `hardcoded_consumers` and the catalog source file.
 */
unused_catalog_entries?: UnusedCatalogEntryFinding[]
/**
 * Named groups under package manager catalogs sections that declare no
 * package entries. The top-level catalog: map is not reported. Wrapped in
 * [`EmptyCatalogGroupFinding`].
 */
empty_catalog_groups?: EmptyCatalogGroupFinding[]
/**
 * Workspace package.json references to catalogs (`catalog:` or
 * `catalog:<name>`) that do not declare the consumed package. The package
 * manager install will error until the named catalog grows to include the
 * package or the reference is switched / removed. Wrapped in
 * [`UnresolvedCatalogReferenceFinding`] with the discriminated
 * `add-catalog-entry` / `update-catalog-reference` primary at position 0.
 */
unresolved_catalog_references?: UnresolvedCatalogReferenceFinding[]
/**
 * Entries in pnpm-workspace.yaml's overrides section, package.json's
 * pnpm.overrides block, npm or Bun's top-level overrides object, or Bun's
 * top-level resolutions object,
 * whose target package is not declared by any workspace package and is
 * not present in pnpm-lock.yaml, package-lock.json, npm-shrinkwrap.json,
 * or bun.lock. Default severity is warn because projects without a
 * readable lockfile fall back to manifest-only checks; the hint field
 * flags those conservative cases. When the only lockfile is bun's binary
 * bun.lockb, resolution cannot be read and the check emits nothing.
 * Wrapped in [`UnusedDependencyOverrideFinding`].
 */
unused_dependency_overrides?: UnusedDependencyOverrideFinding[]
/**
 * Package-manager override or resolution entries whose key or value does
 * not parse in the declaration source's grammar (empty key, empty value,
 * malformed selector, unbalanced parent matcher). The package manager may
 * reject or ignore these at install time. Default severity is error. Wrapped in
 * [`MisconfiguredDependencyOverrideFinding`].
 */
misconfigured_dependency_overrides?: MisconfiguredDependencyOverrideFinding[]
/**
 * `"use client"` files that export a Next.js server-only / route-segment
 * config name (e.g. `metadata`, `revalidate`, `GET`). Next.js rejects this
 * at build time. Wrapped in [`InvalidClientExportFinding`] so each entry
 * carries a typed `actions` array natively. Default severity is `warn`.
 */
invalid_client_exports?: InvalidClientExportFinding[]
/**
 * Barrel files that re-export BOTH a `"use client"` origin module AND a
 * server-only origin module (the Next.js App Router footgun). Wrapped in
 * [`MixedClientServerBarrelFinding`] so each entry carries a typed
 * `actions` array natively. Default severity is `warn`.
 */
mixed_client_server_barrels?: MixedClientServerBarrelFinding[]
/**
 * `"use client"` / `"use server"` directives written as expression
 * statements after a non-directive statement, so the RSC bundler parses
 * them as ordinary strings and silently ignores them. Wrapped in
 * [`MisplacedDirectiveFinding`] so each entry carries a typed `actions`
 * array natively. Default severity is `warn`.
 */
misplaced_directives?: MisplacedDirectiveFinding[]
/**
 * Vue `inject(KEY)` / Svelte `getContext(KEY)` calls whose symbol KEY is
 * provided nowhere in the project (the injected-never-provided dead-half).
 * Wrapped in [`UnprovidedInjectFinding`] so each entry carries a typed
 * `actions` array natively. Default severity is `warn`.
 */
unprovided_injects?: UnprovidedInjectFinding[]
/**
 * Vue/Svelte single-file components that are reachable but rendered nowhere
 * (the imported-but-never-rendered dead-half). Wrapped in
 * [`UnrenderedComponentFinding`] so each entry carries a typed `actions`
 * array natively. Default severity is `warn`.
 */
unrendered_components?: UnrenderedComponentFinding[]
/**
 * Next.js App Router route files that resolve to the same URL within one
 * app-root (a guaranteed `next build` failure). Wrapped in
 * [`RouteCollisionFinding`] so each entry carries a typed `actions` array
 * natively. One finding per colliding file. Default severity is `warn`.
 */
route_collisions?: RouteCollisionFinding[]
/**
 * Sibling Next.js dynamic route segments at one tree position using
 * different param spellings (a dev / runtime error; `next build` does NOT
 * catch it). Wrapped in [`DynamicSegmentNameConflictFinding`] so each entry
 * carries a typed `actions` array natively. Default severity is `warn`.
 */
dynamic_segment_name_conflicts?: DynamicSegmentNameConflictFinding[]
/**
 * Vue `<script setup>` `defineProps`, Svelte 5 `$props()`, and React props
 * referenced nowhere in their own component. Wrapped in
 * [`UnusedComponentPropFinding`] so each entry carries a typed `actions`
 * array natively. Default severity is `warn`.
 */
unused_component_props?: UnusedComponentPropFinding[]
/**
 * Used optional component inputs absent from inspected reachable callers. Off by default.
 */
absent_component_props?: AbsentComponentPropFinding[]
/**
 * Vue `<script setup>` `defineEmits` events emitted nowhere in their own SFC
 * (no `emit('<name>')` call). Wrapped in [`UnusedComponentEmitFinding`] so
 * each entry carries a typed `actions` array natively. Default severity is
 * `warn`.
 */
unused_component_emits?: UnusedComponentEmitFinding[]
/**
 * Angular `@Input()` / signal `input()` / `model()` inputs read nowhere in
 * their own component (neither the template nor the class body). Wrapped in
 * [`UnusedComponentInputFinding`] so each entry carries a typed `actions`
 * array natively. Default severity is `warn`.
 */
unused_component_inputs?: UnusedComponentInputFinding[]
/**
 * Angular `@Output()` / signal `output()` outputs emitted nowhere in their
 * own component (no `this.<output>.emit(...)`). Wrapped in
 * [`UnusedComponentOutputFinding`] so each entry carries a typed `actions`
 * array natively. Default severity is `warn`.
 */
unused_component_outputs?: UnusedComponentOutputFinding[]
/**
 * Svelte components dispatching a custom event via `createEventDispatcher()`
 * whose event name is listened to nowhere project-wide (cross-file
 * dead-output direction). Wrapped in [`UnusedSvelteEventFinding`] so each
 * entry carries a typed `actions` array natively. Default severity is
 * `warn`.
 */
unused_svelte_events?: UnusedSvelteEventFinding[]
/**
 * Next.js Server Actions (exports of `"use server"` files) that no code in
 * the project references. Reclassified out of `unused_exports` for
 * `"use server"` files. Wrapped in [`UnusedServerActionFinding`] so each
 * entry carries a typed `actions` array natively. Default severity is
 * `warn`.
 */
unused_server_actions?: UnusedServerActionFinding[]
/**
 * SvelteKit `+page.{ts,server.ts,js,server.js}` `load()` return-object keys
 * read by no consumer. Wrapped in [`UnusedLoadDataKeyFinding`] so each entry
 * carries a typed `actions` array natively. Default severity is `warn`.
 */
unused_load_data_keys?: UnusedLoadDataKeyFinding[]
/**
 * `true` when the `unused-load-data-key` detector abstained project-wide
 * because a whole-object use of `page.data` / `$page.data` was seen
 * somewhere (S1 observability: an empty `unused_load_data_keys` with this
 * flag set is NOT a clean bill, it means the rule could not run safely).
 * Serialized only when `true` so the default JSON contract is unchanged.
 */
unused_load_data_keys_global_abstain?: boolean
/**
 * React/Preact props forwarded unchanged through `>= N` intermediate
 * pass-through components until a consumer (located per-chain records).
 * Wrapped in [`PropDrillingChainFinding`] so each entry carries a typed
 * `actions` array natively. Health signal: the rule defaults to `off`
 * (opt-in), so this is dormant and populated ONLY when the user enables it.
 */
prop_drilling_chains?: PropDrillingChainFinding[]
/**
 * React/Preact components whose entire body is a single spread-forwarded
 * child render (`return <Child {...props}/>`): pure structural indirection,
 * a candidate for inlining at call sites. Wrapped in [`ThinWrapperFinding`]
 * so each entry carries a typed `actions` array natively. Health signal: the
 * rule defaults to `off` (opt-in), so this is dormant and populated ONLY
 * when the user enables it.
 */
thin_wrappers?: ThinWrapperFinding[]
/**
 * React/Preact components that participate in a duplicate-prop-shape group:
 * three or more components across two or more files whose statically-known
 * prop NAME set is identical after stripping ubiquitous DOM / passthrough
 * names (a missing shared `Props` type / base component). Wrapped in
 * [`DuplicatePropShapeFinding`] so each entry carries a typed `actions`
 * array and its sibling roster natively. Health signal: the rule defaults to
 * `off` (opt-in), so this is dormant and populated ONLY when the user
 * enables it.
 */
duplicate_prop_shapes?: DuplicatePropShapeFinding[]
}
/**
 * The rendered impact report, derived purely from the store.
 */
export interface ImpactReport {
schema_version: ImpactReportSchemaVersion
/**
 * Whether impact tracking is active for this project.
 */
enabled: boolean
enabled_source: EnabledSource
/**
 * Number of recorded runs in the store.
 */
record_count: number
/**
 * `_meta` block with docs and field definitions, when `--explain` was
 * passed.
 */
_meta?: (Meta | null)
/**
 * Timestamp of the earliest recorded run; absent with no records.
 */
first_recorded?: (string | null)
/**
 * Git SHA of the most recent recorded run, so a consumer can tell which
 * commit the `surfacing` counts belong to. This is an ABBREVIATED SHA
 * (`git rev-parse --short`), so it is for display/correlation only and will
 * not match a full 40-character SHA from `$GITHUB_SHA` or the git API
 * without expansion. None when the latest run had no SHA (not a git repo)
 * or there are no records yet.
 */
latest_git_sha?: (string | null)
/**
 * Counts from the most recent recorded run. These are CHANGED-FILE scoped
 * (each record comes from a `fallow audit` run, whose default `new-only`
 * gate counts only findings in the changed files of that run), NOT a
 * whole-project total.
 */
surfacing?: (ImpactCounts | null)
/**
 * Trend between the two most recent records. None until two records exist.
 * Trend between the two most recent changed-file records. None until two
 * records exist.
 */
trend?: (TrendSummary | null)
/**
 * Counts from the most recent whole-project `fallow` run. WHOLE-PROJECT
 * scope (not changed-file), so this is the current issue total across the
 * whole repo, context next to the actionable changed-file `surfacing`
 * count. None until a full `fallow` run has been recorded. v1.6.
 */
project_surfacing?: (ImpactCounts | null)
/**
 * Trend between the two most recent whole-project records. Comparable over
 * time (same whole-project denominator every run), unlike the changed-file
 * `trend`. None until two full `fallow` runs exist. v1.6.
 */
project_trend?: (TrendSummary | null)
/**
 * Recorded gate runs grouped by source, over the same bounded window of
 * recorded runs `record_count` reports. A floor, not a lifetime total, and
 * absent when no run in that window carries a gate source. Local
 * provenance, never an adoption metric.
 */
gate_runs?: (GateRunCounts | null)
/**
 * Lifetime count of commit-gate containment events.
 */
containment_count: number
/**
 * Most recent containment events (newest last), capped for display.
 */
recent_containment: ContainmentEvent[]
/**
 * Lifetime count of findings fallow credits as genuinely resolved (code
 * removed or refactored, never a `fallow-ignore`). v1.5.
 */
resolved_total: number
/**
 * Lifetime count of findings silenced by a newly-added `fallow-ignore`.
 * Reported as honest context, never as a win. v1.5.
 */
suppressed_total: number
/**
 * Most recent resolution events (newest last), capped for display. v1.5.
 */
recent_resolved: ResolutionEvent[]
/**
 * Whether per-finding attribution has a baseline yet. False on a freshly
 * upgraded v1 store (no frontier captured), which the renderer uses to show
 * "resolution tracking starts from your next run" instead of a bare zero.
 */
attribution_active: boolean
/**
 * Whether the local agent onboarding prompt has been explicitly declined.
 * Stored in the user config dir (per project) so agents avoid cross-session
 * nags without writing into the repo.
 */
onboarding_declined: boolean
/**
 * Whether the user ever made an explicit enable/disable decision for
 * Impact tracking. `enabled: false` with `explicit_decision: false` means
 * "never asked"; with `true` it means "asked and declined". Agents use
 * this to offer the impact opt-in exactly once per project.
 */
explicit_decision: boolean
}
/**
 * Per-category issue counts captured at a recorded run.
 */
export interface ImpactCounts {
/**
 * Sum of the category counts.
 */
total_issues: number
/**
 * Dead-code findings.
 */
dead_code: number
/**
 * Complexity findings.
 */
complexity: number
/**
 * Duplication findings.
 */
duplication: number
}
/**
 * A computed trend between the two most recent records.
 */
export interface TrendSummary {
direction: ImpactTrendDirection
/**
 * Signed delta in total issues, current minus previous.
 */
total_delta: number
/**
 * Total issues in the earlier run.
 */
previous_total: number
/**
 * Total issues in the later run.
 */
current_total: number
}
/**
 * Recorded gate runs grouped by the gate that produced them. Local
 * provenance only: the store never leaves the machine, so this answers "where
 * do my gate runs come from", never "how widely is fallow adopted".
 *
 * Counted over the recorded runs the store still holds, which is the same
 * window `record_count` reports. The store keeps a bounded number of runs and
 * drops the oldest, so on a long-lived project these are the shape of recent
 * gate activity, not a lifetime total: read them as a floor. Absent when no
 * run in that window carries a gate source, which is not the same as "no gate
 * ever ran here".
 */
export interface GateRunCounts {
/**
 * Runs recorded by the agent gate (`--gate-marker agent`).
 */
agent: number
/**
 * Runs recorded by the git pre-commit hook (`--gate-marker pre-commit`).
 */
pre_commit: number
/**
 * Runs recorded by a CI gate (`--gate-marker ci`).
 */
ci: number
/**
 * Gate runs whose marker this build does not recognise, plus every gate
 * run recorded before the store kept its source (store schema 6 and older).
 */
unknown: number
}
/**
 * A commit-gate containment event recorded by `fallow impact`.
 */
export interface ContainmentEvent {
/**
 * Timestamp when the commit gate blocked the commit.
 */
blocked_at: string
/**
 * Timestamp when a later run passed clean.
 */
cleared_at: string
/**
 * Abbreviated SHA of the cleared commit, when in a git repo.
 */
git_sha?: (string | null)
blocked_counts: ImpactCounts
}
/**
 * A resolved or suppressed finding attribution event.
 */
export interface ResolutionEvent {
/**
 * Finding kind that was resolved or suppressed, e.g. `unused-export`.
 */
kind: string
/**
 * Root-relative path of the resolved finding.
 */
path: string
/**
 * Symbol name, for symbol-level findings.
 */
symbol?: (string | null)
/**
 * Abbreviated SHA of the resolving commit, when in a git repo.
 */
git_sha?: (string | null)
/**
 * Timestamp the resolution was recorded.
 */
timestamp: string
}
/**
 * The cross-repo aggregate report, `fallow impact --all --format json`.
 */
export interface CrossRepoImpactReport {
schema_version: CrossRepoImpactSchemaVersion
/**
 * Per-project stores successfully parsed (add `unreadable_count` for the
 * total number of store files found in the user config dir).
 */
project_count: number
/**
 * Stores with recorded history (the rows in `projects`); excludes
 * enabled-but-empty stores, which are still counted in `project_count`.
 */
tracked_count: number
/**
 * Stores that failed to parse and were skipped (corrupt or newer-schema).
 */
unreadable_count: number
totals: CrossRepoTotals
/**
 * Per-project rows, one for each store with recorded history.
 */
projects: CrossRepoProjectEntry[]
}
/**
 * Grand totals across every tracked project (including repos whose directory no
 * longer exists on disk: their past wins still count toward lifetime impact).
 */
export interface CrossRepoTotals {
/**
 * Lifetime genuinely-resolved findings across projects.
 */
resolved_total: number
/**
 * Lifetime `fallow-ignore` suppressions across projects.
 */
suppressed_total: number
/**
 * Lifetime commit-gate containment events across projects.
 */
containment_count: number
/**
 * Sum of whole-project issue totals across projects that have a full-run
 * baseline, as of EACH project's last full `fallow` run (not a simultaneous
 * snapshot).
 */
project_wide_issues: number
/**
 * Projects that have recorded at least one full `fallow` run.
 */
projects_with_baseline: number
}
/**
 * One project's row in the cross-repo roll-up.
 */
export interface CrossRepoProjectEntry {
/**
 * Stable, non-reversible project key (the store filename stem); the
 * cross-tool/cross-run JOIN key. NEVER a path.
 */
project_key: string
/**
 * Repo basename for display (never a full path). Absent on pre-v5 stores
 * (the row falls back to the short key).
 */
label?: (string | null)
/**
 * Timestamp of the project's most recent recorded run (changed-file or
 * whole-project), for the LAST RUN column and the default `recent` sort.
 */
last_recorded?: (string | null)
report: ImpactReport
}
/**
 * The `fallow security --format json` envelope. `FallowOutput` discriminates it
 * by the `kind: "security"` tag; the optional `gate` block is additive and is
 * not part of that discrimination.
 */
export interface SecurityOutput {
schema_version: SecuritySchemaVersion
version: ToolVersion
elapsed_ms: ElapsedMs
config: SecurityOutputConfig
/**
 * The verdict of every gate this run evaluated, keyed by name. The CLI
 * always emits it, with the command's default exit rule in it also when
 * no flag armed a gate, so a CI integration reads the verdict instead of
 * guessing from a process status it usually cannot see. A gate fails the
 * build when `status` is `fail` AND `enforced` is true. The typed
 * programmatic API runs no CLI gate and leaves it absent. See
 * [`crate::GateOutcomes`].
 */
gate_outcomes?: (GateOutcomes | null)
/**
 * Every narrowing or shaping request this run RECEIVED, keyed by name,
 * absent when it was asked for nothing. An entry whose `status` is not
 * `applied` means the run could not do what it was asked and reported
 * something WIDER instead, so what follows is a valid report of a scope
 * nobody requested. Honoured requests are published too, with
 * `status: "applied"`, so an absent object means "nothing was asked for",
 * never "nothing failed". See [`crate::RequestOutcomes`].
 */
request_outcomes?: (RequestOutcomes | null)
/**
 * Security-specific rule and field metadata, emitted with `--explain`.
 */
_meta?: (Meta | null)
/**
 * Gate verdict, present only when `--gate <mode>` was set (issue #886).
 * Emitted on pass too (`verdict: "pass"`, `new_count: 0`) so consumers
 * distinguish "gate ran and passed" from "gate did not run" (absent).
 */
gate?: (SecurityGate | null)
/**
 * Diagnostics owned by this security analysis run.
 */
workspace_diagnostics?: WorkspaceDiagnostic[]
/**
 * Security candidates. Paths are project-root-relative, forward-slash.
 */
security_findings: SecurityFinding[]
/**
 * Opt-in attack-surface inventory from untrusted entry points to reachable
 * sinks. Present only when `--surface` was requested.
 */
attack_surface?: (SecurityAttackSurfaceEntry[] | null)
/**
 * In-band blind spot: number of `"use client"` files whose transitive
 * import cone contains a dynamic `import()` the reachability BFS could not
 * follow. A leak hidden behind such an edge would not be reported, so a
 * zero finding count with a non-zero value here is NOT a clean bill.
 */
unresolved_edge_files: number
/**
 * In-band blind spot: number of sink-shaped nodes the catalogue detector
 * could not flatten to a static callee path (dynamic dispatch, computed
 * members, aliased bindings). A zero finding count with a non-zero value
 * here is NOT a clean bill.
 */
unresolved_callee_sites: number
/**
 * Bounded diagnostics for unresolved callee blind spots.
 */
unresolved_callee_diagnostics?: (SecurityUnresolvedCalleeDiagnostics | null)
}
/**
 * Allowlisted config context for `fallow security --format json`.
 */
export interface SecurityOutputConfig {
rules: SecurityOutputRulesConfig
/**
 * `security.categories.include` from config. `null` means unset, `[]`
 * means explicitly empty.
 */
categories_include: (string[] | null)
/**
 * `security.categories.exclude` from config. `null` means unset, `[]`
 * means explicitly empty.
 */
categories_exclude: (string[] | null)
}
/**
 * Per-rule severity context inside [`SecurityOutputConfig::rules`].
 */
export interface SecurityOutputRulesConfig {
security_client_server_leak: SecurityRuleSeverityConfig
security_sink: SecurityRuleSeverityConfig
}
/**
 * Configured-versus-effective severity for one security rule.
 */
export interface SecurityRuleSeverityConfig {
configured: Severity
effective: Severity
}
/**
 * The `gate` block on `SecurityOutput`, present only when `--gate <mode>` ran.
 * Invariant: `verdict == Fail  IFF  exit code 8  IFF  new_count > 0`.
 */
export interface SecurityGate {
mode: SecurityGateMode
verdict: SecurityGateVerdict
/**
 * Number of candidates matching the selected gate mode.
 */
new_count: number
}
/**
 * A local security CANDIDATE for downstream agent verification, NOT a verified
 * vulnerability. Emitted only by `fallow security`, never under bare `fallow`
 * or the `audit` gate. There is deliberately no `confidence` or
 * `signal_strength` field: fallow does not prove exploitability, so the trace
 * (its hops and length) is the only honest signal.
 */
export interface SecurityFinding {
/**
 * Stable per-finding correlation id, identical across runs for the same
 * rule + anchor path + line + column. An autonomous agent that triaged this
 * candidate on a prior run uses it to correlate the candidate after a
 * rebase. Equal to the SARIF `partialFingerprints["fallowSecurity/v2"]`
 * value for the same finding (one shared helper computes both).
 */
finding_id: string
kind: SecurityFindingKind
/**
 * The catalogue category id (e.g. `"dangerous-html"`). `Some` for
 * `TaintedSink`. For `ClientServerLeak` this is `None` for the secret-leak
 * finding, and `Some("server-only-import")` when a `"use client"` cone
 * reaches server-only code.
 */
category?: (string | null)
/**
 * The CWE number declared by the matched catalogue entry. `None` for
 * `ClientServerLeak`; never fabricated beyond the catalogue's value.
 */
cwe?: (number | null)
/**
 * File the finding is anchored on (the client boundary). Absolute
 * internally; JSON strips the project root via `serde_path::serialize`.
 */
path: string
/**
 * 1-based line number of the anchor.
 */
line: number
/**
 * 0-based byte column offset of the anchor.
 */
col: number
/**
 * Agent/human-readable evidence (e.g. the named env var the chain reaches).
 */
evidence: string
/**
 * Whether the sink argument was associated with a known untrusted source by
 * the intra-module source-to-sink back-trace (issue #859): a local binding
 * referenced in the argument was sourced from a catalogue source path
 * (`req.query`, `process.argv`, message-event `data`, etc.). `true` ranks
 * the candidate higher and annotates the evidence; `false` does NOT
 * suppress the finding (the association is conservative, never a proof, and
 * fallow prefers false-negatives over false-positives). Always `false` for
 * `ClientServerLeak`. Skipped from JSON when `false` for output stability.
 */
source_backed?: boolean
severity: SecuritySeverity
/**
 * Structural import-hop trace from the client boundary to the secret source.
 * The hop count is the uncalibrated signal; fallow does not prove the path
 * is exploitable.
 */
trace: TraceHop[]
/**
 * Machine-actionable next steps. Always emitted (possibly empty for
 * forward-compat). For security candidates this is a single file-level
 * suppress hint (`auto_fixable: false`); there is no auto-fix because
 * verification is the agent's job, not fallow's.
 */
actions: IssueAction[]
/**
 * Dead-code cross-link when the same sink candidate sits in code fallow also
 * reports as removable. Agents should verify the dead-code finding and delete
 * the code instead of hardening the sink when deletion is safe.
 */
dead_code?: (SecurityDeadCodeContext | null)
/**
 * Graph-derived reachability ranking signal (issues #860 and #885). `None`
 * until the post-detection ranking pass fills it; additive on the wire
 * (skipped when absent). Drives the order findings are emitted in:
 * runtime-reachable candidates sort first, followed by source-backed and
 * source-reachable candidates, then wider blast radius.
 */
reachability?: (SecurityReachability | null)
candidate: SecurityCandidate
/**
 * Source-to-sink taint-flow triple, present only when an untrusted source
 * is import-reachable to this sink. Absent (skipped) otherwise.
 */
taint_flow?: (SecurityTaintFlow | null)
/**
 * Production runtime coverage context for the function enclosing this
 * security sink. Present only when `fallow security --runtime-coverage`
 * runs and the candidate is a `tainted-sink`.
 */
runtime?: (SecurityRuntimeContext | null)
/**
 * Internal projection used by `fallow security --surface`. The CLI strips
 * this from per-finding JSON and promotes it to the top-level
 * `attack_surface` field only when requested.
 */
attack_surface?: (SecurityAttackSurfaceEntry | null)
}
/**
 * One hop in a security finding's structural trace. Stored as an absolute path
 * internally; JSON serialization strips the project root via
 * `serde_path::serialize`.
 */
export interface TraceHop {
/**
 * File on this hop of the import chain.
 */
path: string
/**
 * 1-based line number. Import-chain hops point at the import site; the
 * terminal secret-source hop points at the source module when extraction
 * does not carry a more precise member-access span.
 */
line: number
/**
 * 0-based byte column offset.
 */
col: number
role: TraceHopRole
}
/**
 * Dead-code cross-link attached to a security candidate when fallow's dead-code
 * pass reports the same anchor as removable code.
 */
export interface SecurityDeadCodeContext {
kind: SecurityDeadCodeKind
/**
 * Unused export name when `kind` is `unused-export`.
 */
export_name?: (string | null)
/**
 * Dead-code finding line when available.
 */
line?: (number | null)
/**
 * Agent-facing guidance for deciding between deletion and hardening.
 */
guidance: string
}
/**
 * Graph-derived reachability ranking signal for a security candidate. Computed
 * from the existing module graph after detection, never proven exploitable.
 * Used to surface candidates that sit on a request/runtime-reachable surface,
 * receive same-module source evidence, or are import-reachable from an
 * untrusted-source module above isolated helpers or scripts.
 *
 * This is a relative-ordering signal, NOT a `confidence` or `signal_strength`
 * score: fallow does not prove the path is exploitable.
 */
export interface SecurityReachability {
/**
 * Whether the anchor module is reachable from a runtime/application entry
 * point (route handlers, server entry, framework runtime roots), the
 * closest graph proxy for an external/request input surface. Code reachable
 * only from test entry points does not count.
 */
reachable_from_entry: boolean
/**
 * Whether the anchor module is reachable over value imports from a module
 * that reads a known untrusted input source. Module-level only: this does
 * not prove a specific source value reaches the sink argument.
 */
reachable_from_untrusted_source?: boolean
/**
 * Structured tier of the untrusted-source association: `arg-level` when the
 * sink argument traces to a same-module source read (strong), `module-level`
 * when only the module is import-reachable from a source (weak). Present
 * exactly when `reachable_from_untrusted_source` is true, so a consumer can
 * separate strong from weak candidates from this field alone without parsing
 * the `evidence` string. Not an exploitability proof.
 */
taint_confidence?: (TaintConfidence | null)
/**
 * Number of value-import hops from the untrusted-source module to the sink
 * module when `reachable_from_untrusted_source` is true.
 */
untrusted_source_hop_count?: (number | null)
/**
 * Module-level import path from the untrusted-source module to the sink
 * anchor. Empty when no source module reaches this candidate. The path is a
 * ranking explanation, not a value-flow proof.
 */
untrusted_source_trace?: TraceHop[]
/**
 * Number of distinct modules that transitively depend on the anchor module
 * (fan-in via the graph's reverse-dependency index). A higher value means a
 * wider surface: more call sites could route untrusted input into the sink.
 */
blast_radius: number
/**
 * Whether the anchor module participates in an architecture-boundary
 * violation found in the same run (as the importing or imported file).
 * Optional pairing: a candidate that also crosses a declared boundary is a
 * stronger review target.
 */
crosses_boundary: boolean
}
/**
 * An agent-actionable candidate record on a [`SecurityFinding`]. fallow fills
 * `source_kind`, `sink`, and `boundary`. The exploitability IMPACT is
 * deliberately NOT a field: `severity` on the parent finding is only a
 * review-priority tier, while deciding exploitability remains the consuming
 * agent's job. A perpetually-null `impact` key would only train consumers to
 * ignore it. The agent reads this record, then writes its own impact verdict
 * downstream.
 */
export interface SecurityCandidate {
/**
 * The kind of untrusted input that reaches the sink, as a stable catalogue
 * source id (`"http-request-input"`, `"process-env"`, `"process-argv"`,
 * `"message-event-data"`, `"location-input"`, ...). `None`/absent when no
 * untrusted source was matched (always `None` for `client-server-leak`).
 * This is an OPEN string set, driven by the data-driven source catalogue; a
 * consumer should treat an unknown id as "untrusted source of unknown kind"
 * and never drop the candidate on that basis.
 */
source_kind?: (string | null)
sink: SecurityCandidateSink
boundary: SecurityCandidateBoundary
/**
 * Network-destination context, present only on `secret-to-network` (#890)
 * candidates: the host the secret-bearing call targets, so an agent can
 * triage exfil from intended auth. Absent for every other category.
 */
network?: (SecurityNetworkContext | null)
}
/**
 * The sink slot of a [`SecurityCandidate`]: a self-contained description of the
 * matched sink site. Echoes the finding's own span (`path`/`line`/`col`) plus
 * the catalogue `category`/`cwe` and the captured `callee`, so an agent can act
 * on `candidate.sink` in isolation (e.g. after fanning a finding out to a
 * sub-agent) without reading the parent finding.
 */
export interface SecurityCandidateSink {
/**
 * File of the sink site. Absolute internally; JSON strips the project root
 * via `serde_path::serialize`.
 */
path: string
/**
 * 1-based line of the sink site.
 */
line: number
/**
 * 0-based byte column of the sink site.
 */
col: number
/**
 * Catalogue category id of the sink (e.g. `"dangerous-html"`). For
 * `client-server-leak` this is `None` for the secret-leak finding, and
 * `Some("server-only-import")` when a `"use client"` cone reaches
 * server-only code.
 */
category?: (string | null)
/**
 * CWE number declared by the catalogue entry. `None` for
 * `client-server-leak`; never fabricated beyond the catalogue's value.
 */
cwe?: (number | null)
/**
 * The sink callee (the dangerous function or member path, e.g.
 * `"el.innerHTML"`, `"child_process.exec"`) captured by the catalogue match.
 * `None` for `client-server-leak` and matches that name no callee.
 */
callee?: (string | null)
/**
 * URL construction shape for SSRF and open-redirect style candidates when
 * fallow can classify whether the origin is fixed or dynamic. Absent for
 * non-URL sinks and unclassified URL expressions.
 */
url_shape?: (SecurityUrlShape | null)
}
/**
 * The boundary slot of a [`SecurityCandidate`]: which structural boundaries the
 * candidate's flow crosses. A flow that crosses a client/server or module
 * boundary is a stronger review target than a self-contained one; the boundary
 * is fallow's structural signal over a pure source-sink match.
 *
 * Two further boundary kinds are RESERVED for a follow-up and are deliberately
 * absent here rather than emitted as always-false: `export_visibility` (is the
 * sink on a publicly-exported symbol?) and a package boundary (does the flow
 * cross an npm-package edge?). Both need new graph derivation that does not
 * exist today; emitting them as `false` would misreport "we checked and it does
 * not cross" when fallow has not checked at all.
 */
export interface SecurityCandidateBoundary {
/**
 * Whether the finding crosses a client/server boundary (a `"use client"`
 * file appears in the trace). True only for `client-server-leak` today;
 * `tainted-sink` candidates carry no client/server marker.
 */
client_server: boolean
/**
 * Whether an untrusted source reaches the sink across one or more
 * value-import (module) hops. Derived from the reachability hop count.
 */
cross_module: boolean
/**
 * The architecture-zone crossing when the anchor participates in a declared
 * boundary-rule violation in the same run. `None` when it crosses no
 * declared zone boundary.
 */
architecture_zone?: (SecurityZoneCrossing | null)
}
/**
 * A declared architecture-zone crossing, recovered by correlating a finding's
 * anchor against the run's architecture-boundary violations.
 */
export interface SecurityZoneCrossing {
/**
 * Zone the importing side belongs to.
 */
from: string
/**
 * Zone the imported side belongs to.
 */
to: string
}
/**
 * Network-destination context for a `secret-to-network` candidate (#890): where
 * the secret-bearing network call sends its data. Present only on
 * network-category candidates. A consuming agent uses it to triage exfil
 * (dynamic / untrusted destination) from intended auth (a literal provider
 * host) without re-reading source.
 */
export interface SecurityNetworkContext {
/**
 * The network call's destination as a static URL string literal, or absent
 * when the destination is DYNAMIC (not a literal). A dynamic destination is
 * the higher-signal exfil case; a literal provider host is usually intended
 * auth.
 */
destination?: (string | null)
}
/**
 * A source-to-sink taint-flow triple, emitted only when an untrusted source is
 * import-reachable to the sink (`reachability.reachable_from_untrusted_source`).
 * The `{ source, sink, path }` shape matches the model agent SAST tooling
 * expects (cf. Semgrep `taint_source` / `taint_sink`, SARIF `threadFlows`).
 */
export interface SecurityTaintFlow {
source: TaintEndpoint
sink: TaintEndpoint
path: TaintPath
}
/**
 * One endpoint (source or sink node) of a [`SecurityTaintFlow`].
 */
export interface TaintEndpoint {
/**
 * File of the endpoint. Absolute internally; JSON strips the project root.
 */
path: string
/**
 * 1-based line of the endpoint.
 */
line: number
/**
 * 0-based byte column of the endpoint.
 */
col: number
}
/**
 * Compact taint-flow path shape. The ordered per-hop trace is NOT duplicated
 * here: it lives on [`SecurityReachability::untrusted_source_trace`]. This
 * carries only the flow's structural summary (intra-module flow plus the
 * cross-module hop count) so consumers do not parse two copies of the hops.
 */
export interface TaintPath {
/**
 * Whether the source and sink sit in the same module (no import hop between
 * them); the source-to-sink association is intra-module.
 */
intra_module: boolean
/**
 * Number of value-import hops from the untrusted-source module to the sink
 * module. Zero for an intra-module flow.
 */
cross_module_hops: number
}
/**
 * Runtime coverage context attached to a security candidate when
 * `fallow security --runtime-coverage` is supplied.
 */
export interface SecurityRuntimeContext {
state: SecurityRuntimeState
/**
 * Enclosing function name from static extraction.
 */
function: string
/**
 * 1-based line where the enclosing function starts.
 */
line: number
/**
 * Observed invocation count when the runtime report provides it.
 */
invocations?: (number | null)
/**
 * Runtime coverage stable function id, when available.
 */
stable_id?: (string | null)
/**
 * Short candidate-framed explanation of the runtime evidence.
 */
evidence?: (string | null)
}
/**
 * One untrusted entry to reachable sink path for `fallow security --surface`.
 */
export interface SecurityAttackSurfaceEntry {
source: TaintEndpoint
sink: SecurityCandidateSink
/**
 * Ordered source to sink path. Same shape as the reachability trace so
 * consumers can reuse existing path handling.
 */
path: TraceHop[]
defensive_boundary: SecurityDefensiveBoundary
}
/**
 * Agent-facing defensive-boundary verification context for one surface path.
 */
export interface SecurityDefensiveBoundary {
/**
 * Control patterns observed in files on this import trace. These are
 * verification hints, not proof of sink protection or value-level data flow.
 */
controls: SecurityDefensiveControl[]
/**
 * Verification question for the consuming agent. It is a prompt, not a
 * missing-guard verdict.
 */
verification_prompt: string
}
/**
 * Control pattern observed in a file on an attack-surface import trace.
 * Its presence does not prove that it executes before the sink or protects
 * the same input.
 */
export interface SecurityDefensiveControl {
kind: SecurityControlKind
/**
 * File of the control site. Absolute internally; JSON strips the project root.
 */
path: string
/**
 * 1-based line of the control site.
 */
line: number
/**
 * 0-based byte column of the control site.
 */
col: number
/**
 * Flattened callee path or a stable synthetic guard name.
 */
callee: string
}
/**
 * Bounded unresolved-callee diagnostics for `fallow security --format json`.
 */
export interface SecurityUnresolvedCalleeDiagnostics {
/**
 * Deterministic sample rows, capped by `sample_limit`.
 */
sampled: SecurityUnresolvedCalleeSample[]
/**
 * Files with the most unresolved callees, capped by `top_files_limit`.
 */
top_files: SecurityUnresolvedCalleeTopFile[]
/**
 * Full count by unresolved-callee reason, sorted by count then reason.
 */
by_reason: SecurityUnresolvedCalleeReasonCount[]
/**
 * Maximum number of sample rows emitted.
 */
sample_limit: number
/**
 * Maximum number of top-file rows emitted.
 */
top_files_limit: number
}
/**
 * One sampled unresolved-callee row.
 */
export interface SecurityUnresolvedCalleeSample {
/**
 * File path relative to the analysed root.
 */
path: string
/**
 * 1-based line of the skipped call site.
 */
line: number
/**
 * 1-based column of the skipped call site.
 */
col: number
reason: SkippedSecurityCalleeReason
expression_kind: SkippedSecurityCalleeExpressionKind
}
/**
 * Count of unresolved callees in one file.
 */
export interface SecurityUnresolvedCalleeTopFile {
/**
 * File path relative to the analysed root.
 */
path: string
/**
 * Number of unresolved callees in this file.
 */
count: number
}
/**
 * Count of unresolved callees for one reason.
 */
export interface SecurityUnresolvedCalleeReasonCount {
reason: SkippedSecurityCalleeReason
/**
 * Number of unresolved callees with this reason.
 */
count: number
}
/**
 * Compact `fallow security --summary --format json` payload. Uses the same
 * `kind: "security"` discriminator as the full payload, but omits candidate
 * arrays and exposes only aggregate counts.
 */
export interface SecuritySummaryOutput {
schema_version: SecuritySchemaVersion
version: ToolVersion
elapsed_ms: ElapsedMs
config: SecurityOutputConfig
/**
 * The verdict of every gate this run evaluated, keyed by name. The CLI
 * always emits it, with the command's default exit rule in it also when
 * no flag armed a gate, so a CI integration reads the verdict instead of
 * guessing from a process status it usually cannot see. A gate fails the
 * build when `status` is `fail` AND `enforced` is true. The typed
 * programmatic API runs no CLI gate and leaves it absent. See
 * [`crate::GateOutcomes`].
 */
gate_outcomes?: (GateOutcomes | null)
/**
 * Every narrowing or shaping request this run RECEIVED, keyed by name,
 * absent when it was asked for nothing. An entry whose `status` is not
 * `applied` means the run could not do what it was asked and reported
 * something WIDER instead, so what follows is a valid report of a scope
 * nobody requested. Honoured requests are published too, with
 * `status: "applied"`, so an absent object means "nothing was asked for",
 * never "nothing failed". See [`crate::RequestOutcomes`].
 */
request_outcomes?: (RequestOutcomes | null)
/**
 * Security-specific rule and field metadata, emitted with `--explain`.
 */
_meta?: (Meta | null)
/**
 * Gate verdict, present only when `--gate <mode>` was set.
 */
gate?: (SecurityGate | null)
/**
 * Diagnostics owned by the full security analysis summarized here.
 */
workspace_diagnostics?: WorkspaceDiagnostic[]
summary: SecuritySummary
}
/**
 * Aggregate counts for `fallow security --summary --format json`.
 */
export interface SecuritySummary {
/**
 * Number of security candidates after all filters, gates, and scopes.
 */
security_findings: number
by_severity: SecuritySeverityCounts
/**
 * Finding counts by catalogue category, or by kind for findings without a
 * catalogue category.
 */
by_category: {
[k: string]: number
}
by_reachability: SecurityReachabilityCounts
by_runtime_state: SecurityRuntimeStateCounts
/**
 * Number of client files whose dynamic imports could not be followed.
 */
unresolved_edge_files: number
/**
 * Number of sink-shaped callees that could not be statically flattened.
 */
unresolved_callee_sites: number
/**
 * Number of attack-surface entries included in the prepared full output.
 */
attack_surface_entries: number
}
/**
 * Fixed severity counters for summary JSON.
 */
export interface SecuritySeverityCounts {
/**
 * High-severity candidates.
 */
high: number
/**
 * Medium-severity candidates.
 */
medium: number
/**
 * Low-severity candidates.
 */
low: number
}
/**
 * Fixed reachability counters for summary JSON.
 */
export interface SecurityReachabilityCounts {
/**
 * Candidates reachable from an entry point.
 */
entry_reachable: number
/**
 * Candidates reachable from an untrusted input source.
 */
untrusted_source_reachable: number
/**
 * Candidates where taint flows through a call argument.
 */
arg_level: number
/**
 * Candidates where taint is only module-level.
 */
module_level: number
/**
 * Candidates whose flow crosses a client/server boundary.
 */
crosses_boundary: number
/**
 * Candidates backed by a concrete taint source.
 */
source_backed: number
}
/**
 * Fixed runtime coverage counters for summary JSON.
 */
export interface SecurityRuntimeStateCounts {
/**
 * Candidates on frequently executed runtime paths.
 */
runtime_hot: number
/**
 * Candidates on rarely executed runtime paths.
 */
runtime_cold: number
/**
 * Candidates on paths never seen executing.
 */
never_executed: number
/**
 * Candidates on low-traffic paths.
 */
low_traffic: number
/**
 * Candidates in files runtime coverage did not observe.
 */
coverage_unavailable: number
/**
 * Candidates whose runtime state could not be classified.
 */
runtime_unknown: number
/**
 * Candidates analysed without any runtime coverage data.
 */
not_collected: number
}
/**
 * The `fallow security survivors --format json` envelope.
 */
export interface SecuritySurvivorsOutput {
schema_version: SecuritySurvivorsSchemaVersion
version: ToolVersion
elapsed_ms: ElapsedMs
/**
 * Diagnostics preserved from the candidate security report.
 */
workspace_diagnostics?: WorkspaceDiagnostic[]
summary: SecuritySurvivorsSummary
/**
 * Verifier-retained candidates keyed by finding id.
 */
survivors: {
[k: string]: SecuritySurvivor
}
/**
 * Ambiguous candidates keyed by finding id. These are not dismissed and are
 * kept explicit so queues can decide whether to include them.
 */
needs_human_review: {
[k: string]: SecuritySurvivor
}
}
/**
 * Aggregate counts for survivor rendering.
 */
export interface SecuritySurvivorsSummary {
/**
 * Candidates in the input security report.
 */
candidates: number
/**
 * Verifier verdicts supplied.
 */
verdicts: number
/**
 * Candidates the verifier retained.
 */
survivors: number
/**
 * Candidates the verifier dismissed.
 */
dismissed: number
/**
 * Candidates the verifier flagged as ambiguous.
 */
needs_human_review: number
/**
 * Candidates without any verdict.
 */
unverdicted: number
}
/**
 * One verifier-retained candidate row.
 */
export interface SecuritySurvivor {
/**
 * Stable candidate id from `security_findings[].finding_id`.
 */
finding_id: string
verdict: SecurityVerifierVerdictStatus
/**
 * Short machine-oriented verdict reason.
 */
reason?: (string | null)
/**
 * Longer free-form verdict explanation.
 */
rationale?: (string | null)
/**
 * Optional verifier-provided confidence or review priority.
 */
confidence?: (string | null)
/**
 * Optional verifier-provided impact statement.
 */
impact?: (string | null)
/**
 * Optional verifier-owned remediation direction.
 */
fix_direction?: (string | null)
candidate: SecurityFinding
}
/**
 * The `fallow security blind-spots --format json` envelope.
 */
export interface SecurityBlindSpotsOutput {
schema_version: SecurityBlindSpotsSchemaVersion
version: ToolVersion
elapsed_ms: ElapsedMs
/**
 * Diagnostics owned by the security analysis used for this view.
 */
workspace_diagnostics?: WorkspaceDiagnostic[]
summary: SecurityBlindSpotsSummary
/**
 * Grouped unresolved callee diagnostics, derived from existing samples.
 */
groups: SecurityBlindSpotGroup[]
}
/**
 * Aggregate counts for blind-spot output.
 */
export interface SecurityBlindSpotsSummary {
/**
 * Files containing at least one unresolved callee.
 */
unresolved_edge_files: number
/**
 * Total unresolved callee sites in the analysis.
 */
unresolved_callee_sites: number
/**
 * Callee sites captured in the bounded diagnostic sample.
 */
sampled_callee_sites: number
}
/**
 * One actionable blind-spot group.
 */
export interface SecurityBlindSpotGroup {
reason: SkippedSecurityCalleeReason
expression_kind: SkippedSecurityCalleeExpressionKind
/**
 * Count in the bounded diagnostic sample.
 */
sampled_count: number
/**
 * Top files in this bounded diagnostic sample.
 */
files: SecurityBlindSpotFile[]
/**
 * Suggested next action for this group.
 */
suggestion: string
}
/**
 * One file inside a blind-spot group.
 */
export interface SecurityBlindSpotFile {
/**
 * File path relative to the analysed root.
 */
path: string
/**
 * Count in the bounded diagnostic sample.
 */
sampled_count: number
}
/**
 * Bare `fallow --format json` envelope.
 */
export interface CombinedOutput {
schema_version: CombinedSchemaVersion
version: ToolVersion
elapsed_ms: ElapsedMs
/**
 * The verdict of every gate this run evaluated, keyed by name. The CLI
 * always emits it, with the default exit rule of each section that ran
 * (`error-severity-findings`, `health-findings`). Without
 * `--fail-on-issues` or `--ci`, the machine formats of the combined run
 * exit 0 for findings, so most entries have `enforced: false`. With one of
 * these flags, the findings rules and `duplication-threshold` are
 * `enforced`, the dupes section adds an enforced `duplication-findings`
 * entry, and every format exits 1 when one fails. For the default
 * exit rules (`error-severity-findings`,
 * `health-findings`), `status` gives the verdict of the human run. An
 * advisory entry can report `fail` without a failure of the human run: an
 * example is a `stale-baseline` entry that `--fail-on-stale-baseline` did
 * not arm. A gate fails the build when `status` is `fail` AND `enforced`
 * is true. The typed
 * programmatic API leaves it absent. See [`crate::GateOutcomes`].
 */
gate_outcomes?: (GateOutcomes | null)
/**
 * Every narrowing or shaping request this run RECEIVED, keyed by name,
 * absent when it was asked for nothing. An entry whose `status` is not
 * `applied` means the run could not do what it was asked and reported
 * something WIDER instead, so what follows is a valid report of a scope
 * nobody requested. Honoured requests are published too, with
 * `status: "applied"`, so an absent object means "nothing was asked for",
 * never "nothing failed". See [`crate::RequestOutcomes`].
 */
request_outcomes?: (RequestOutcomes | null)
/**
 * Applied package Git refs of the `check` and `dupes` sections. The map
 * does not narrow the `health` section.
 */
package_baselines?: PackageBaselineStatus[]
/**
 * Per-section `_meta` blocks, when `--explain` was passed.
 */
_meta?: (CombinedMeta | null)
/**
 * Dead-code section of the combined run.
 */
check?: (CheckOutput | null)
/**
 * Duplication section of the combined run.
 */
dupes?: (CombinedDupesSection | null)
/**
 * Health section of the combined run.
 */
health?: (HealthReport | null)
/**
 * Workspace-discovery, source-discovery, and analysis-stage diagnostics
 * for the run (issue #2366). See `CheckOutput::workspace_diagnostics` for
 * the full contract: root-relative paths, omitted when empty. The
 * combined envelope carries them here rather than inside a section, so a
 * run that skips a section (`--skip check`, `--only health`,
 * `--only dupes`) still reports every diagnostic its analyses recorded.
 */
workspace_diagnostics?: WorkspaceDiagnostic[]
/**
 * Read-only follow-up commands aggregated across the combined run's
 * findings. See `CheckOutput::next_steps` for the contract.
 */
next_steps?: NextStep[]
}
/**
 * Optional `_meta` block for [`CombinedOutput`].
 */
export interface CombinedMeta {
/**
 * `_meta` block for the dead-code section.
 */
check?: (Meta | null)
/**
 * `_meta` block for the duplication section.
 */
dupes?: (Meta | null)
/**
 * `_meta` block for the health section.
 */
health?: (Meta | null)
/**
 * Telemetry identifiers for the run.
 */
telemetry?: (TelemetryMeta | null)
}
/**
 * Wire shape of the `dupes` section inside the bare combined envelope.
 *
 * The section is the standalone payload plus the view of the loaded
 * duplication baseline. The standalone `dupes` envelope carries the same
 * `baseline_staleness` key at its root.
 */
export interface CombinedDupesSection {
/**
 * All detected clone groups, each wrapped with typed actions.
 */
clone_groups: CloneGroupFinding[]
/**
 * Clone families, each wrapped with typed actions. Inner `groups`
 * inside each `CloneFamilyFinding` are themselves wrapped as
 * `CloneGroupFinding` entries carrying their own `actions[]` (and
 * optional audit-mode `introduced` flag), so JSON-Schema strict
 * consumers and TS consumers reading `clone_families[].groups[]` see
 * the same shape as the top-level `clone_groups[]` array (preserves
 * the issue #393 regression contract).
 */
clone_families: CloneFamilyFinding[]
/**
 * Mirrored directory pairs.
 */
mirrored_directories?: MirroredDirectory[]
stats: DuplicationStats
/**
 * This run's view of the loaded duplication baseline, present only when
 * `--dupes-baseline` loaded one. Read `change_scoped` before dividing
 * `matched_entries` by `baseline_entries`: a narrowed run can report
 * `matched_entries: 0` on a healthy baseline.
 */
baseline_staleness?: (BaselineStaleness | null)
}
/**
 * Envelope emitted by `fallow flags --format json`.
 */
export interface FeatureFlagsOutput {
schema_version: FeatureFlagsSchemaVersion
version: ToolVersion
elapsed_ms: ElapsedMs
/**
 * What the run was asked to narrow and whether it did. See
 * [`crate::RequestOutcomes`] for the full contract.
 *
 * `fallow flags` accepts `--changed-since`, and an unresolvable ref widens
 * the scan to the whole project rather than failing the run. Until this
 * member existed the only account of that was a stderr line, which `--quiet`
 * removes, so a flag inventory read as scoped to the change could silently
 * be the whole project's (issue #2734).
 *
 * The command applies no diff filter, so the object carries the
 * `changed-since` entry only. Omitted when the run was asked for nothing,
 * which keeps a scan that passed no narrowing flag byte-identical and moves
 * no `schema_version`.
 */
request_outcomes?: (RequestOutcomes | null)
/**
 * Detected feature-flag findings.
 */
feature_flags: FeatureFlagFinding[]
/**
 * Number of entries in `feature_flags`.
 */
total_flags: number
/**
 * Workspace-discovery and source-discovery diagnostics for the run. See
 * `CheckOutput::workspace_diagnostics` for the full contract.
 *
 * A flags run walks and parses the project like every other analysis, so
 * it records the same discovery kinds: a `skipped-large-file`,
 * `skipped-minified-file`, or `source-read-failure` file was never
 * scanned for flags, and a `source-parse-degraded` file was scanned from
 * a partial module. Each is a reason a flag can be missing from
 * `feature_flags[]`, which is exactly what a consumer reading a
 * zero-result run needs to know. The analysis-stage kinds appear here
 * too: the scan correlates flags with dead exports, so it runs the
 * dead-code analyze pass that records them.
 *
 * Omitted when empty, so a project with no discovery noise sees no
 * change.
 */
workspace_diagnostics?: WorkspaceDiagnostic[]
/**
 * One row per flag with the reasons the flag can be retired.
 *
 * Present only with `--retirement`. Without that option the key is
 * omitted, so the envelope stays byte-identical and `schema_version`
 * does not move. The per-site `feature_flags[]` array is the same with
 * and without the option.
 */
retirement?: (FlagRetirementReport | null)
/**
 * `_meta` block; see [`FeatureFlagsMeta`].
 */
_meta?: (FeatureFlagsMeta | null)
}
/**
 * One feature flag finding in JSON output.
 */
export interface FeatureFlagFinding {
/**
 * File path relative to the analysed root.
 */
path: string
/**
 * Detected flag identifier, e.g. the env var or SDK key name.
 */
flag_name: string
kind: FeatureFlagKind
confidence: FeatureFlagConfidence
/**
 * 1-based line of the flag usage.
 */
line: number
/**
 * 1-based column of the flag usage.
 */
col: number
/**
 * Suggested follow-up actions (investigate / suppress).
 */
actions: FeatureFlagAction[]
/**
 * Flag SDK the call belongs to, for SDK-call findings.
 */
sdk_name?: (string | null)
/**
 * Overlap with dead-code findings when the flag guards unused exports.
 */
dead_code_overlap?: (FeatureFlagDeadCodeOverlap | null)
}
/**
 * Per-finding action emitted for feature flag findings.
 */
export interface FeatureFlagAction {
type: FeatureFlagActionType
/**
 * Whether `fallow fix` can apply the action automatically.
 */
auto_fixable: boolean
/**
 * Human-readable action description.
 */
description: string
/**
 * Suppression comment to insert, for suppress actions.
 */
comment?: (string | null)
}
/**
 * Dead-code overlap block attached when a flag guards unused exports.
 */
export interface FeatureFlagDeadCodeOverlap {
/**
 * Lines inside the flag-guarded region.
 */
guarded_lines: number
/**
 * Number of unused exports the flag guards.
 */
dead_export_count: number
/**
 * Names of the unused exports the flag guards.
 */
dead_exports: string[]
}
/**
 * The `retirement` block of `fallow flags --retirement --format json`.
 */
export interface FlagRetirementReport {
/**
 * The analysis clock that ages count from, as an RFC 3339 UTC
 * timestamp. `null` when the age mode is `off`, and also when no git
 * history is available: outside a repository, on a branch without
 * commits, or in a shallow clone. A `workspace_diagnostics` entry then
 * gives the reason.
 */
generated_at_clock?: (string | null)
age_mode: FlagAgeMode
/**
 * The vendor export that the report read. Present only with
 * `--flag-state`.
 */
vendor_state?: (RetirementVendorState | null)
summary: RetirementSummary
/**
 * Verdict of `--fail-on-regression` against a flags regression
 * baseline. Present only when the gate ran.
 */
regression?: (FlagRegressionResult | null)
/**
 * Verdict of `--max-flag-age`. Present only with that option.
 */
max_flag_age?: (FlagAgeGate | null)
/**
 * One row per flag after the `--min-age`, `--reason`, `--sort` and
 * `--top` options. A row with an empty `reasons` array is not a
 * candidate.
 */
flags: RetirementFlag[]
}
/**
 * The `--flag-state` vendor export that the report read.
 */
export interface RetirementVendorState {
/**
 * The vendor name from the export, for example `launchdarkly`.
 */
source: string
/**
 * When the export was made, as the export gives it.
 */
exported_at: string
/**
 * Days between `exported_at` and the analysis clock. `null` when the
 * date cannot be read.
 */
export_age_days?: (number | null)
/**
 * Number of flags in the export.
 */
flags: number
}
/**
 * Totals of the retirement report.
 */
export interface RetirementSummary {
/**
 * Distinct flags in the code in scope, before `--min-age` and
 * `--reason`. The `vendor-only` rows of a `--flag-state` export do not
 * count here, so the count does not change when a key is added in the
 * vendor only. `by_reason` counts them.
 */
distinct_flags: number
/**
 * Rows in scope with at least one reason, `vendor-only` rows included.
 */
candidates: number
/**
 * Number of rows in scope per reason.
 */
by_reason: {
[k: string]: number
}
}
/**
 * Verdict of the flags regression gate.
 */
export interface FlagRegressionResult {
status: RegressionStatus
/**
 * The `--tolerance` value. Absent when the status is `skipped`.
 */
tolerance?: (number | null)
/**
 * How to read `tolerance`. Absent when the status is `skipped`.
 */
tolerance_kind?: (RegressionToleranceKind | null)
/**
 * The compared counts: `distinct_flags` first, then each `--reason`
 * code. Empty when the status is `skipped`.
 */
metrics: FlagRegressionMetric[]
/**
 * Whether one count grew more than the tolerance.
 */
exceeded: boolean
/**
 * Why the gate did not run. Present only when the status is `skipped`.
 */
reason?: (string | null)
}
/**
 * One count that the flags regression gate compares.
 */
export interface FlagRegressionMetric {
/**
 * `distinct_flags`, or a reason code from `--reason`.
 */
metric: string
/**
 * The count in the baseline.
 */
baseline: number
/**
 * The count in this run.
 */
current: number
/**
 * `current - baseline`.
 */
delta: number
/**
 * Whether the growth is more than the tolerance.
 */
exceeded: boolean
}
/**
 * Verdict of `--max-flag-age`.
 */
export interface FlagAgeGate {
status: RegressionStatus
/**
 * The `--max-flag-age` value in days.
 */
max_days: number
/**
 * Whether one flag in scope is older than `max_days`.
 */
exceeded: boolean
/**
 * Flags in the code in scope without a measured age. The gate cannot
 * check these flags.
 */
unmeasured: number
/**
 * Why the gate did not run. Present only when the status is `skipped`.
 */
reason?: (string | null)
/**
 * The flags in scope that are older than `max_days`, oldest first.
 * The `--reason`, `--min-age` and `--top` options do not change this
 * list.
 */
flags: FlagAgeGateEntry[]
}
/**
 * A flag that is older than `--max-flag-age`.
 */
export interface FlagAgeGateEntry {
/**
 * Flag identifier.
 */
flag_name: string
kind: RetirementFlagKind
/**
 * Flag SDK, for SDK flags with a known provider.
 */
sdk_name?: (string | null)
/**
 * Workspace root of the flag, in a project with workspaces.
 */
workspace?: (string | null)
/**
 * Age of the flag in days.
 */
age_days: number
}
/**
 * One flag in the retirement report.
 */
export interface RetirementFlag {
/**
 * Flag identifier.
 */
flag_name: string
kind: RetirementFlagKind
/**
 * Flag SDK, for SDK flags with a known provider.
 */
sdk_name?: (string | null)
/**
 * Workspace root relative to the analysed root, when the project has
 * workspaces and the flag is inside one. Part of the flag identity.
 */
workspace?: (string | null)
/**
 * Every site of the flag, sorted by path, line and column.
 */
sites: RetirementSite[]
/**
 * Number of sites in this row that read the flag.
 */
read_sites: number
/**
 * Whether every read site is in a test, story or mock file. Read sites
 * of the same flag in other workspaces count too.
 */
test_only: boolean
/**
 * First commit that added the flag name. Set in `pickaxe` mode only.
 */
first_seen?: (FlagCommit | null)
/**
 * Oldest commit among the lines that still hold the flag.
 */
oldest_surviving_site?: (FlagCommit | null)
/**
 * Newest commit among the lines that still hold the flag.
 */
last_touched?: (FlagCommit | null)
/**
 * Days between the flag's oldest known commit and the analysis clock.
 * In `blame` mode this is a lower bound.
 */
age_days?: (number | null)
/**
 * Retirement reasons, in report order. Empty for a flag that is not a
 * candidate.
 */
reasons: RetirementReason[]
/**
 * Evidence for each reason.
 */
evidence: RetirementEvidence[]
/**
 * Follow-up actions. Empty for a flag that is not a candidate.
 */
actions: RetirementAction[]
/**
 * The vendor state of the flag. Present only with `--flag-state`, for
 * a flag whose key is in the export.
 */
vendor?: (RetirementVendor | null)
}
/**
 * One site of a flag in the retirement report.
 */
export interface RetirementSite {
/**
 * File path relative to the analysed root.
 */
path: string
/**
 * 1-based line.
 */
line: number
/**
 * 0-based byte column.
 */
col: number
role: FlagSiteRole
/**
 * Whether the file is a test, story or mock file.
 */
in_test: boolean
}
/**
 * A commit that git history links to a flag.
 */
export interface FlagCommit {
/**
 * Abbreviated commit hash.
 */
commit: string
/**
 * Commit date in UTC, as `YYYY-MM-DD`.
 */
date: string
}
/**
 * One piece of evidence for a retirement reason.
 */
export interface RetirementEvidence {
reason: RetirementReason
/**
 * File path relative to the analysed root. For `vendor-only`, the path
 * of the `--flag-state` file: relative to the root when the file is
 * inside it, else as given.
 */
path: string
/**
 * 1-based line.
 */
line: number
/**
 * What the evidence shows.
 */
detail: string
}
/**
 * A follow-up action for a retirement candidate.
 */
export interface RetirementAction {
type: RetirementActionType
/**
 * Always `false`: Fallow never removes a flag.
 */
auto_fixable: boolean
/**
 * Human-readable action description.
 */
description: string
}
/**
 * The vendor state of one flag in the retirement report.
 */
export interface RetirementVendor {
/**
 * The key in the vendor export, before `flags.vendorKeyPrefix` is
 * removed.
 */
key: string
state: VendorFlagState
/**
 * Whether the flag serves one variation, when the export says so.
 */
serves_single_variation?: (boolean | null)
/**
 * When the vendor created the flag, as the export gives it.
 */
created_at?: (string | null)
/**
 * When the vendor last evaluated the flag, as the export gives it.
 */
last_evaluated_at?: (string | null)
}
/**
 * Optional `_meta` block for [`FeatureFlagsOutput`]. Both fields are optional
 * because the two contributors are independent: `feature_flags` details are
 * present only with `--explain`, and `telemetry` is injected post-pass by
 * [`attach_telemetry_meta`] whenever an analysis run id is available (which is
 * the default path). Mirrors `Meta` / `CombinedMeta`, which also model
 * `telemetry` as an optional, never-required property.
 */
export interface FeatureFlagsMeta {
/**
 * Feature-flag detection explanations, emitted only with `--explain`.
 */
feature_flags?: (FeatureFlagsMetaDetails | null)
/**
 * Local telemetry correlation metadata for agent follow-up runs.
 */
telemetry?: (TelemetryMeta | null)
}
/**
 * Feature flag explanatory metadata.
 */
export interface FeatureFlagsMetaDetails {
/**
 * What the flags command reports.
 */
description: string
kinds: FeatureFlagsKindMeta
confidence: FeatureFlagsConfidenceMeta
/**
 * Public documentation URL for the flags command.
 */
docs: string
}
/**
 * Feature flag kind explanations.
 */
export interface FeatureFlagsKindMeta {
/**
 * Explanation of the `environment_variable` kind.
 */
environment_variable: string
/**
 * Explanation of the `sdk_call` kind.
 */
sdk_call: string
/**
 * Explanation of the `config_object` kind.
 */
config_object: string
}
/**
 * Feature flag confidence explanations.
 */
export interface FeatureFlagsConfidenceMeta {
/**
 * Explanation of the `high` confidence level.
 */
high: string
/**
 * Explanation of the `medium` confidence level.
 */
medium: string
/**
 * Explanation of the `low` confidence level.
 */
low: string
}
/**
 * Complete `fallow audit --brief --format json` wire envelope.
 *
 * This is distinct from [`ReviewBriefOutput`], which is the reusable review
 * digest embedded in walkthrough output. The wire envelope also carries audit
 * metadata, optional telemetry, and the subtract-style analysis subreports.
 */
export interface ReviewBriefWireOutput {
schema_version: ReviewBriefSchemaVersion
version: ToolVersion
/**
 * Command discriminator singleton: always `"audit-brief"`.
 */
command: string
verdict: AuditVerdict
/**
 * Number of changed files in the audit scope.
 */
changed_files_count: number
/**
 * Base ref used to determine the changeset.
 */
base_ref: string
/**
 * Human-readable description of the resolved base, when available.
 */
base_description?: (string | null)
/**
 * Head commit SHA, when available.
 */
head_sha?: (string | null)
elapsed_ms: ElapsedMs
/**
 * Whether base-snapshot analysis was skipped for this run.
 */
base_snapshot_skipped?: (boolean | null)
summary: AuditSummary
attribution: AuditAttribution
/**
 * Optional metric definitions and local telemetry correlation metadata.
 */
_meta?: (Meta | null)
decisions: DecisionSurface
triage: DiffTriage
graph_facts: GraphFacts
partition: PartitionFacts
impact_closure: ImpactClosureFacts
focus: FocusMap
deltas: ReviewDeltas
/**
 * Reviewer-private weakening signals.
 */
weakening: WeakeningSignal[]
routing: RoutingFacts
/**
 * Owner-group reach from the CODEOWNERS file. Absent when no CODEOWNERS
 * file is found or the file cannot be read.
 */
ownership?: (OwnershipFacts | null)
/**
 * Dead-code findings scoped to the audit changeset.
 */
dead_code?: (CheckOutput | null)
/**
 * Duplication findings scoped to the audit changeset.
 */
duplication?: (DupesReportPayload | null)
/**
 * Complexity findings scoped to the audit changeset.
 */
complexity?: (HealthReport | null)
/**
 * Branching conservation across the changeset. Absent when no base
 * comparison ran.
 */
branching?: (BranchingReport | null)
}
/**
 * The ranked, capped decision surface plus the set of signal_ids the
 * deterministic layer emitted (the anti-hallucination allowlist).
 */
export interface DecisionSurface {
/**
 * Ranked decisions, highest consequence first.
 */
decisions: Decision[]
/**
 * Present when more than the cap were extracted.
 */
truncated?: (TruncationNote | null)
/**
 * Every signal_id the deterministic layer emitted, INCLUDING those whose
 * decision was collapsed below the cap or suppressed. The anti-hallucination
 * allowlist: an agent decision whose id is absent is rejected.
 */
emitted_signal_ids: string[]
}
/**
 * One consequential structural decision, framed as a judgment question for a
 * human with taste, anchored to a fallow-emitted signal.
 */
export interface Decision {
/**
 * Deterministic anchor to the fallow-emitted candidate this decision frames.
 * `accept_signal_id` rejects any id not in the emitted set.
 */
signal_id: string
category: DecisionCategory
/**
 * The decision framed as a judgment question for the human.
 */
question: string
/**
 * Root-relative file the decision is anchored at.
 */
anchor_file: string
/**
 * 1-based anchor line, when the underlying signal carries one (0 = file head).
 */
anchor_line: number
/**
 * The raw fallow-emitted candidate key the `signal_id` hashes.
 */
signal_key: string
/**
 * The `signal_id` this decision WOULD have had before any rename in this
 * change (the anchor file's pre-rename path). Present only when the anchor was
 * renamed. A review-memory layer carries a dismissal across a `git mv`: if
 * `previous_signal_id` was dismissed in an earlier PR, treat this decision as
 * dismissed too. Keeps `signal_id` itself exact + deterministic.
 */
previous_signal_id?: (string | null)
/**
 * Blast radius: count of modules affected beyond the diff by this decision.
 */
blast: number
/**
 * `blast * reversibility_weight`: the rank key (sorted descending).
 */
consequence: number
/**
 * The routed expert(s) to ask, from ownership routing. Empty when no
 * ownership signal is available for the anchor file.
 */
expert: string[]
/**
 * Whether the anchor file's only qualified owner is one person.
 */
bus_factor_one?: boolean
/**
 * Honest per-decision count: in-repo modules OUTSIDE the diff that already
 * depend on this decision's anchor. This is the DISPLAY number (taste
 * ownership: the human reads reversibility from the count itself), distinct
 * from `blast` (the project-wide proxy used only for ranking). Never a door
 * label. Internal-only by construction, so it cannot see a published library's
 * external consumers; the public-API trade-off clause names that risk in prose.
 */
internal_consumer_count: number
/**
 * The named structural sacrifice this change makes, stated as a fact, never a
 * recommendation (e.g. "Couples `app` to `infra`; 4 in-repo modules already
 * depend on this anchor."). A sibling fact to `question`; it never tells the
 * human what to choose.
 */
tradeoff: string
}
/**
 * A note for decisions collapsed below the cap.
 */
export interface TruncationNote {
/**
 * How many decisions were collapsed below the cap.
 */
collapsed: number
/**
 * Human-readable collapse reason.
 */
reason: string
}
/**
 * Stage 0 of the brief: triage facts derived purely from the diff size.
 *
 * `hunks` and `net_lines` are populated when the caller supplies parsed diff
 * evidence. They remain absent when no diff is available.
 */
export interface DiffTriage {
/**
 * Number of changed files in the audit scope.
 */
files: number
/**
 * Number of diff hunks, or `None` when no diff evidence was supplied.
 */
hunks?: (number | null)
/**
 * Net added-minus-removed lines, or `None` without diff evidence.
 */
net_lines?: (number | null)
risk_class: RiskClass
review_effort: ReviewEffort
}
/**
 * Stage 1 of the brief: graph-derived orientation facts.
 *
 * `boundaries_touched` is derived from the run's boundary-violation zones.
 * `exports_added` and `api_width_delta` both report the exports-aware public
 * API widening count. Removed exports are not represented in this
 * widening-only signal. The set of modules the changed code reaches is Stage
 * 3's `impact_closure`, which owns both its magnitude and its paths.
 */
export interface GraphFacts {
/**
 * Number of public API exports added by the changeset. Zero means the
 * changeset adds no public API exports.
 */
exports_added: number
/**
 * Widening-only public API delta, currently equal to `exports_added`.
 * Removed exports are not represented, so zero means no public API exports
 * were added.
 */
api_width_delta: number
/**
 * Architecture boundary zones touched by the changeset, deduped and sorted.
 * Derived from the run's boundary-violation findings.
 */
boundaries_touched: string[]
}
/**
 * Stage 2 of the brief: the partition + order. The changed files split into
 * coherent BY-MODULE units (the only byte-identical-deterministic clustering
 * definition straight from the graph), plus a dependency-sensible review ORDER
 * over those units (definitions before consumers, mechanical/leaf units last,
 * ties broken by the path sort). Stage 2 sits UNDER the decision surface as a
 * drill-down; it is the backbone the directed-review loop hands the agent.
 *
 * Feature-cluster and concern partitioning are deferred (they need scoring
 * heuristics whose tie-breaks are a fresh nondeterminism surface).
 */
export interface PartitionFacts {
/**
 * The by-module units, sorted by module directory. Empty when no graph was
 * retained or no changed file maps to a known module.
 */
units: ReviewUnitFact[]
/**
 * The dependency-sensible review order: module-directory strings,
 * definitions before consumers, mechanical/leaf units last. A permutation of
 * the `units` module directories.
 */
order: string[]
/**
 * Connected components of the inter-unit dependency graph: groups of
 * module directories that share no import edge with any unit outside the
 * group. Present only when there are two or more; a single slice is just
 * `order`. A slice proves the absence of import edges to the rest of the
 * change, nothing more: whether it can land on its own is still a
 * judgment (generated files and lockstep contracts share no edge and
 * still belong together). An orientation fact, never a demand to split.
 */
independent_slices?: string[][]
}
/**
 * One review unit: a coherent by-module cluster of the changed set.
 */
export interface ReviewUnitFact {
/**
 * The module directory the unit covers (root-relative, forward-slashed).
 * The empty string is the repository-root group.
 */
module_dir: string
/**
 * The changed files in this unit, path-sorted.
 */
files: string[]
}
/**
 * Stage 3 of the brief: the impact closure. The transitive
 * affected-but-not-in-diff set plus the coordination gap. The differentiator a
 * diff tool fundamentally cannot do, because it has no graph.
 *
 * Honest scope (ADR-001, syntactic): the coordination gap is an attention
 * pointer at the exact inter-module failure mode, NOT a correctness proof.
 */
export interface ImpactClosureFacts {
/**
 * The FULL number of files transitively affected by the changeset
 * (reverse-deps + re-export chains) that are NOT in the diff. Computed
 * BEFORE [`affected_not_shown`](Self::affected_not_shown) is capped to a
 * sample, so it is always the true magnitude of the blast radius.
 */
affected_count: number
/**
 * A capped, path-sorted sample of the affected root-relative paths (at most
 * [`AFFECTED_SAMPLE_CAP`]), deduped. The full count lives in
 * [`affected_count`](Self::affected_count) and the distribution in
 * [`affected_by_dir`](Self::affected_by_dir); use this list to jump to
 * representative files, NEVER to enumerate the blast radius or to infer its
 * shape. Because it is a prefix of the sorted set, it clusters in whichever
 * directory sorts first. To reconstruct the full set, run
 * `fallow check --impact-closure <path>` once per changed file and union the
 * results: that flag seeds from a single file, so no single command
 * reproduces this changeset-wide union.
 */
affected_not_shown: string[]
/**
 * The blast radius rolled up by parent directory: how the affected files
 * distribute, heaviest directory first, ties broken by directory path so the
 * order is deterministic. This is the SHAPE signal, and unlike
 * [`affected_not_shown`](Self::affected_not_shown) its counts are exact for
 * every directory it lists. At most [`AFFECTED_DIR_CAP`] entries.
 */
affected_by_dir: AffectedDirectory[]
/**
 * How many directories did not fit within [`AFFECTED_DIR_CAP`] and are
 * absent from [`affected_by_dir`](Self::affected_by_dir). They are the
 * lightest ones; their files are still counted in
 * [`affected_count`](Self::affected_count). Zero when nothing was omitted.
 * Add this to `affected_by_dir.len()` for the true number of directories
 * the change reaches.
 */
affected_by_dir_omitted: number
/**
 * Coordination gaps: a changed file exports a contract consumed by a module
 * absent from the diff. One entry per (changed file, consumer) pair. NOT a
 * subset of [`affected_not_shown`](Self::affected_not_shown): the gap
 * deliberately skips story and test consumers that the affected set counts.
 */
coordination_gap: CoordinationGapFact[]
}
/**
 * One directory of the blast radius and how many affected files it holds.
 */
export interface AffectedDirectory {
/**
 * Root-relative parent directory, forward-slashed. The empty string is the
 * repository root.
 */
dir: string
/**
 * How many affected-but-not-in-diff files live directly in `dir`. Exact,
 * never sampled.
 */
count: number
}
/**
 * One coordination-gap entry: a changed file exports symbols consumed by a
 * `consumer_file` that is NOT in the diff. Deduped per (changed, consumer) pair
 * (firing-precision rule R2).
 */
export interface CoordinationGapFact {
/**
 * Root-relative path of the changed file whose contract is consumed elsewhere.
 */
changed_file: string
/**
 * Root-relative path of the consumer module that is NOT in the diff.
 */
consumer_file: string
/**
 * The exported symbol names the consumer references, sorted.
 */
consumed_symbols: string[]
/**
 * Honest scope note: this is a syntactic attention pointer, not a proof.
 */
note: string
}
/**
 * The weighted focus map: the ranked `review-here` units plus the FULL
 * `deprioritized` escape-hatch list, so nothing is hidden.
 *
 * Completeness invariant (the escape-hatch done-condition): the two lists
 * partition the unit set, so `review_here.len() + deprioritized.len()` equals
 * the total unit count by construction.
 */
export interface FocusMap {
/**
 * Units labeled `review-here`, ranked by composite score (descending), ties
 * broken by path for determinism.
 */
review_here: FocusUnit[]
/**
 * EVERY de-prioritized unit (`not-prioritized`, plus runtime-backed `skip`
 * units on the paid path) -- the escape hatch. Always present and fully
 * enumerated so a reviewer can always "show me what you de-prioritized"; the
 * human brief collapses it by default and re-expands under
 * `--show-deprioritized`. Nothing is ever hidden, including a `skip`.
 */
deprioritized: FocusUnit[]
}
/**
 * One review unit on the focus map: its file, composite score, label, human
 * reason, and any confidence flags.
 */
export interface FocusUnit {
/**
 * Root-relative path of the changed file this unit covers.
 */
file: string
score: FocusScore
label: FocusLabel
/**
 * A human-readable reason for the label, built from the present signals.
 */
reason: string
/**
 * Confidence flags (advisory; never lower the score). Sorted, deduped.
 */
confidence?: ConfidenceFlag[]
}
/**
 * The composite attention score, with the deterministic component sub-scores
 * kept on the wire so the runtime layer adds its weight without recomputing the
 * signals.
 */
export interface FocusScore {
/**
 * Fan-in/out blast-radius component.
 */
fan_io: number
/**
 * Security source -> sink taint-touch component (0 until a security pass is
 * threaded onto the brief path; the seam is built and tested).
 *
 * Omitted from the wire while it is zero, the same treatment `runtime`
 * gets. Publishing a permanently-zero component as a required field made
 * it read as a measurement that found nothing, when nothing measured it.
 * A consumer that sums components must read an absent component as zero.
 */
security_taint?: number
/**
 * Risk-zone component (boundary / public-API / security-sensitive).
 */
risk_zone: number
/**
 * Change-shape component (new/widened export, signature change proxy).
 */
change_shape: number
/**
 * Runtime-weight component (paid): a hot path (runtime evidence of high
 * invocation) adds an invocation-bucketed weight so it amplifies the blast
 * and outranks an otherwise-equal cold unit. `0` in free mode (no runtime
 * input), so the free-tier total stays the four deterministic components and
 * is byte-identical to the no-runtime baseline.
 */
runtime?: number
/**
 * The summed total of every present component (the four deterministic ones
 * plus the runtime weight).
 */
total: number
}
/**
 * Diff-aware deterministic deltas (6.A), framed new-vs-pre-existing against
 * the audit base snapshot. Each entry is a brief summary/verdict line.
 *
 * `public_api` is batch-consolidated to ONE decision per change (rule R1):
 * the `added` list carries the introduced public-export keys as evidence, but a
 * reviewer reads "the public surface widened by N", never one decision per
 * symbol.
 */
export interface ReviewDeltas {
/**
 * Cross-zone boundary EDGES introduced vs base (R2 first-edge-only: one per
 * `<from_zone>-><to_zone>` pair, never per import). New-vs-pre-existing.
 */
boundary_introduced: string[]
/**
 * Circular dependencies introduced vs base (canonical file-set keys).
 */
cycle_introduced: string[]
/**
 * Exports-aware public-API surface delta: the public-export keys
 * (`<rel_path>::<name>`) added vs base, resolved through `package.json`
 * `exports` + re-export reachability. A symbol re-exported only through an
 * internal barrel NOT in `exports` is absent here (zero delta); one
 * reachable through an `exports` path is present (exactly one).
 */
public_api_added: string[]
/**
 * Third-party dependencies a changed `package.json` declares that the base
 * manifest did not, as `<manifest>::<name>` keys. Every dependency section
 * participates. Always present, empty when no manifest changed.
 */
dependency_added: string[]
/**
 * Declared dependencies whose range moved across a major version (or a
 * `0.x` minor) vs base, as `<manifest>::<name>@<from>-><to>` keys. Minor
 * and patch moves are not candidates; a non-numeric range is skipped.
 * Always present, empty when nothing crossed a major version.
 */
dependency_major_bumped: string[]
}
/**
 * One weakening signal: a category, the file it was detected in, and a short
 * human-readable evidence string. Reviewer-private; never gates.
 */
export interface WeakeningSignal {
kind: WeakeningKind
/**
 * Root-relative path of the changed file the signal was detected in.
 */
file: string
/**
 * Short evidence string (e.g. the offending token or the threshold delta).
 */
evidence: string
}
/**
 * The full routing section: one unit per changed source file with a routable
 * signal. Files with no ownership signal are omitted (no noise).
 */
export interface RoutingFacts {
/**
 * Per-changed-file routing units, sorted by file path.
 */
units: RoutingUnit[]
}
/**
 * One routed unit with its experts and bus-factor flag.
 */
export interface RoutingUnit {
/**
 * Root-relative path of the changed file.
 */
file: string
/**
 * The routed expert(s): the CODEOWNERS declared owner when present, else the
 * top git-blame / recency contributor; empty when no signal is available.
 */
expert: string[]
/**
 * Whether the only qualified owner is a single contributor (bus-factor-1):
 * a knowledge-concentration risk worth a second reviewer.
 */
bus_factor_one?: boolean
}
/**
 * How far a changeset reaches across CODEOWNERS owner groups.
 *
 * Computed from the CODEOWNERS file alone: no git history is read, so the
 * section is present whenever a CODEOWNERS file is found, also when the churn
 * walk behind `routing` finds nothing. Each file maps to its primary owner
 * (the first owner of the last matching rule). A file that no rule matches,
 * or that a GitLab negation rule matches, belongs to the `(unowned)` group.
 * The owner strings use the same vocabulary as `routing.units[].expert`.
 *
 * Absent from the brief when no CODEOWNERS file is found, or when the file
 * cannot be read or does not parse. A configured `codeowners` path that
 * fails also prints a warning on stderr.
 *
 * `groups[].direct_count` counts all changed files, source or not, so the
 * sum over all groups is the number of changed files, not the size of
 * `impact_closure.in_diff`. Slice owners count only the files of the
 * partition units, which are source files.
 */
export interface OwnershipFacts {
/**
 * Distinct owner groups across the changed files and the impact closure.
 * The `(unowned)` group counts as one group. Exact, never capped.
 */
group_count: number
/**
 * Owner groups that own no changed file and appear only through the
 * impact closure. Exact, never capped.
 */
transitive_only_count: number
/**
 * Changed files that belong to the `(unowned)` group.
 */
unowned_direct_count: number
/**
 * The owner groups, sorted by `direct_count` descending, then
 * `affected_count` descending, then `owner`. At most [`OWNER_GROUP_CAP`]
 * entries. The counts in each entry are exact.
 */
groups: OwnerGroupFact[]
/**
 * How many owner groups did not fit within [`OWNER_GROUP_CAP`] and are
 * absent from `groups`. Zero when nothing was omitted.
 */
groups_omitted: number
/**
 * The owner set of each independent slice, aligned by index with
 * `partition.independent_slices`. Present only when that list is present
 * (two or more slices). A fact for the reviewer, never a demand to split.
 */
slices?: OwnershipSliceFact[]
}
/**
 * One owner group and how many files of the changeset it owns.
 */
export interface OwnerGroupFact {
/**
 * The CODEOWNERS owner (`@user`, `@org/team`, or an email), or
 * `(unowned)`.
 */
owner: string
/**
 * Changed files this group owns.
 */
direct_count: number
/**
 * Files of the impact closure (affected, not in the diff) this group owns.
 */
affected_count: number
}
/**
 * The owners of one independent slice of the partition.
 */
export interface OwnershipSliceFact {
/**
 * The module directories of the slice, as in
 * `partition.independent_slices`.
 */
module_dirs: string[]
/**
 * The distinct owners of the changed files in the slice, sorted. The
 * `(unowned)` group is a distinct owner. Never empty.
 */
owners: string[]
/**
 * True when the slice has exactly one owner, so one owner group can
 * review it on its own.
 */
separable: boolean
}
/**
 * The brief's branching section.
 */
export interface BranchingReport {
/**
 * Files carrying the split signature: branching within `tolerance` of
 * where it was, more functions, a smaller largest function. Empty when
 * none do, which is the common case and is not itself a finding. See
 * `SplitInPlace` for why this describes a shape rather than asserting a
 * refactor.
 */
split_in_place: SplitInPlace[]
/**
 * The band inside which a file's branching counts as held. Published
 * because a claim against an unpublished threshold is not reproducible by
 * a consumer.
 */
tolerance: number
scope: BranchingScope
branch_points: BranchingMetric
functions: BranchingMetric
peak_unit_cyclomatic: BranchingMetric
/**
 * Base-side branch points of the files that have no head entry.
 */
branch_points_only_in_base: number
cognitive: BranchingCognitive
/**
 * The files that moved the numbers most, largest absolute branch-point
 * change first.
 */
by_file: BranchingFileDelta[]
/**
 * Files with a change that the list did not name.
 */
by_file_omitted: number
}
/**
 * One file present on both revisions whose branching held while it gained
 * functions and its largest function shrank.
 *
 * Local by construction: nothing here depends on any other file, so unrelated
 * work in the changeset cannot make it more or less true. A set-level
 * classifier cannot make this claim, because a changeset contains arbitrary
 * other work and an aggregate cannot attribute.
 *
 * It is a description, not an inference. The three conditions are the
 * signature a split leaves, and they are also satisfiable without one: the
 * peak is a file-level maximum (`FileBranching::peak_cyclomatic`), so it can
 * fall because the largest function left the file while arriving helpers
 * happen to carry the branching it took with it. Both numbers are reported so
 * a reader can see that for themselves, and the rendered text states what was
 * measured rather than concluding a refactor happened.
 *
 * Files carrying synthetic template units are excluded, because those units
 * are outside every count here, so the numbers would not describe the file a
 * reader opens. Test paths are excluded too: their totals are reported in
 * `BranchingScope` instead.
 */
export interface SplitInPlace {
/**
 * Root-relative path.
 */
path: string
/**
 * Branch points on the base revision.
 */
branch_points_before: number
/**
 * And on head. Within `tolerance` of `branch_points_before`, which is what
 * "held" means here. Both are reported because one number alone cannot be
 * checked.
 */
branch_points_after: number
/**
 * Accounted functions before the split.
 */
functions_before: number
/**
 * Accounted functions after it.
 */
functions_after: number
/**
 * Highest single-function cyclomatic score before.
 */
peak_before: number
/**
 * And after. It falls by construction when a function is split, which is
 * why it is evidence here and never a metric to celebrate.
 */
peak_after: number
}
/**
 * Size and composition of the compared set.
 */
export interface BranchingScope {
/**
 * Files carrying units on both revisions.
 */
files_both: number
/**
 * Files carrying units on the head revision only.
 */
files_added: number
/**
 * Files that carried units on the base revision only, whether they were
 * deleted or merely lost every accounted unit. Reported, and excluded from
 * every headline number: such a file contributes its whole base-side total
 * as a fall with no head counterpart.
 */
files_only_in_base: number
/**
 * Branch points on test-shaped paths within the head totals. Test code
 * routinely dominates both terms, so a reader needs to see its share
 * before reading the headline.
 */
test_branch_points: number
/**
 * Functions on test-shaped paths within the head totals.
 */
test_functions: number
/**
 * Share of head branch points owned by the single largest file, so a
 * reader can see when one vendored or generated file owns the number.
 */
largest_file_share_of_branch_points: number
}
/**
 * One metric across the two revisions.
 */
export interface BranchingMetric {
/**
 * Value on the base revision.
 */
previous: number
/**
 * Value on the head revision.
 */
current: number
/**
 * `current - previous`. Signed, so a consumer never has to infer direction
 * from a separate field.
 */
delta: number
}
/**
 * The cognitive figure and what drove it.
 *
 * `previous` and `current` exclude prop-count and hook-density increments.
 * Both are cognitive-only, and prop count records an excess over a floor, so
 * it is superlinear in a split and would move this number with branching and
 * nesting both flat. This therefore does not match the cognitive score the
 * complexity findings report.
 */
export interface BranchingCognitive {
/**
 * Cognitive weight on the base revision.
 */
previous: number
/**
 * Cognitive weight on the head revision.
 */
current: number
/**
 * `current - previous`.
 */
delta: number
/**
 * Change in the summed nesting depth behind those increments.
 */
nesting_weight_delta: number
/**
 * What the improvement is attributable to, absent when cognitive did not
 * fall. There is nothing to attribute when the number rose or held.
 */
attributed_to?: (CognitiveAttribution | null)
}
/**
 * One file's contribution to the change.
 */
export interface BranchingFileDelta {
/**
 * Root-relative path.
 */
path: string
/**
 * Change in branch points for this file.
 */
branch_points_delta: number
/**
 * Change in accounted functions for this file.
 */
functions_delta: number
}
/**
 * The separable `decision-surface` envelope: the single call that puts taste-
 * decisions in front of a human, callable WITHOUT the full pipeline (the
 * `decision_surface` MCP tool's output). Carries `kind`/`schema_version` plus
 * structured `actions[]` per decision.
 */
export interface DecisionSurfaceOutput {
schema_version: DecisionSurfaceSchemaVersion
/**
 * Fallow CLI version that produced this output.
 */
version: string
/**
 * Command discriminator singleton: always `"decision-surface"`.
 */
command: string
/**
 * The ranked, capped decisions, each with structured actions.
 */
decisions: DecisionWithActions[]
/**
 * Present when more than the cap were extracted.
 */
truncated?: (TruncationNote | null)
/**
 * Count of fallow-emitted signal_ids (the anti-hallucination allowlist size).
 */
signal_count: number
}
/**
 * One decision plus its structured `actions[]`.
 */
export interface DecisionWithActions {
/**
 * Deterministic anchor to the fallow-emitted candidate this decision frames.
 * `accept_signal_id` rejects any id not in the emitted set.
 */
signal_id: string
category: DecisionCategory
/**
 * The decision framed as a judgment question for the human.
 */
question: string
/**
 * Root-relative file the decision is anchored at.
 */
anchor_file: string
/**
 * 1-based anchor line, when the underlying signal carries one (0 = file head).
 */
anchor_line: number
/**
 * The raw fallow-emitted candidate key the `signal_id` hashes.
 */
signal_key: string
/**
 * The `signal_id` this decision WOULD have had before any rename in this
 * change (the anchor file's pre-rename path). Present only when the anchor was
 * renamed. A review-memory layer carries a dismissal across a `git mv`: if
 * `previous_signal_id` was dismissed in an earlier PR, treat this decision as
 * dismissed too. Keeps `signal_id` itself exact + deterministic.
 */
previous_signal_id?: (string | null)
/**
 * Blast radius: count of modules affected beyond the diff by this decision.
 */
blast: number
/**
 * `blast * reversibility_weight`: the rank key (sorted descending).
 */
consequence: number
/**
 * The routed expert(s) to ask, from ownership routing. Empty when no
 * ownership signal is available for the anchor file.
 */
expert: string[]
/**
 * Whether the anchor file's only qualified owner is one person.
 */
bus_factor_one?: boolean
/**
 * Honest per-decision count: in-repo modules OUTSIDE the diff that already
 * depend on this decision's anchor. This is the DISPLAY number (taste
 * ownership: the human reads reversibility from the count itself), distinct
 * from `blast` (the project-wide proxy used only for ranking). Never a door
 * label. Internal-only by construction, so it cannot see a published library's
 * external consumers; the public-API trade-off clause names that risk in prose.
 */
internal_consumer_count: number
/**
 * The named structural sacrifice this change makes, stated as a fact, never a
 * recommendation (e.g. "Couples `app` to `infra`; 4 in-repo modules already
 * depend on this anchor."). A sibling fact to `question`; it never tells the
 * human what to choose.
 */
tradeoff: string
/**
 * Structured actions: route to the expert, or suppress.
 */
actions: DecisionAction[]
}
/**
 * A structured action attached to a surfaced decision (the agent-actionable
 * surface). Mirrors the typed-action shape the rest of fallow emits.
 */
export interface DecisionAction {
type: DecisionActionType
/**
 * Human-readable description of the action.
 */
description: string
/**
 * Runnable command or paste-ready suppression comment.
 */
command?: (string | null)
/**
 * Whether fallow can carry the action out automatically. Always `false`:
 * a decision is a human judgment, never auto-applied.
 */
auto_fixable: boolean
}
/**
 * The `fallow review --walkthrough-guide` envelope: the current digest + schema
 * the agent fetches. The tool owns this; the skill stays thin (it fetches this
 * rather than embedding a frozen copy). Always emitted with exit 0.
 */
export interface WalkthroughGuide {
schema_version: ReviewBriefSchemaVersion
/**
 * Fallow CLI version that produced this guide.
 */
version: string
/**
 * Command discriminator singleton: always `"review-walkthrough-guide"`.
 */
command: string
/**
 * The deterministic graph-snapshot hash pinned into the digest. The agent
 * echoes it back; a mismatch on reentry refuses the payload as stale.
 */
graph_snapshot_hash: string
digest: ReviewBriefOutput
direction: ReviewDirection
/**
 * The per-hunk change anchors: one stable id per changed region. An agent
 * may cite a `change_anchor` as a judgment anchor in addition to an emitted
 * `signal_id`, so a trade-off about a changed region with no graph finding
 * can still anchor (and be post-validated) rather than hallucinate.
 */
change_anchors: ChangeAnchor[]
agent_schema: AgentSchema
/**
 * The injection-resistance note (digest is graph-only; PR prose untrusted).
 */
injection_note: string
}
/**
 * The full `fallow audit --brief --format json` envelope. Carries the
 * informational verdict, the triage and graph-facts orientation stages, plus
 * the reused "subtract" section (the same dead-code / duplication / complexity
 * payload `fallow audit --format json` emits).
 */
export interface ReviewBriefOutput {
schema_version: ReviewBriefSchemaVersion
/**
 * Fallow CLI version that produced this output.
 */
version: string
/**
 * Command discriminator singleton: always `"audit-brief"`.
 */
command: string
triage: DiffTriage
graph_facts: GraphFacts
partition: PartitionFacts
impact_closure: ImpactClosureFacts
focus: FocusMap
deltas: ReviewDeltas
/**
 * 6.F, headline: reviewer-private weakening signals (tests
 * removed/skipped, thresholds lowered, suppressions added, security steps
 * removed). Advisory, never gates, never auto-posted.
 */
weakening: WeakeningSignal[]
routing: RoutingFacts
/**
 * How far the change reaches across CODEOWNERS owner groups, computed
 * from the CODEOWNERS file alone. Absent when no CODEOWNERS file is
 * found or the file cannot be read.
 */
ownership?: (OwnershipFacts | null)
decisions: DecisionSurface
/**
 * Branching conservation across the changeset: total branching against
 * the number of functions now holding it. Absent when no base comparison
 * ran, which keeps the wire shape byte-identical for a consumer that
 * never had a base snapshot.
 */
branching?: (BranchingReport | null)
}
/**
 * The review direction artifact: the order to review in, the coherent units,
 * and per-unit concern lens + out-of-diff + expert. A minimal projection of the
 * EXISTING graph facts (routing units + impact closure); the full weighted-focus
 * engine is a later epic. Graph-derived only (injection-resistant).
 */
export interface ReviewDirection {
/**
 * The dependency-sensible review order: unit file paths, units carrying
 * out-of-diff consumers first (review the load-bearing definitions before
 * the mechanical units).
 */
order: string[]
/**
 * Coherent review units, in `order`.
 */
units: DirectionUnit[]
}
/**
 * One directed review unit projected from the graph: a file the change touches,
 * the concern to check, the out-of-diff consumers it must account for, and the
 * routed expert. Graph-derived only (routing + impact closure), NEVER from prose.
 */
export interface DirectionUnit {
/**
 * Root-relative path of the unit to review.
 */
file: string
/**
 * The concern lens the agent should check for this unit, derived from the
 * unit's risk signals (impact-closure consumers vs a plain touched file).
 */
concern_lens: string
/**
 * Per-unit review-effort budget: the weighted-focus composite score for
 * this file. A cloud fan-out spends AI passes/verifiers PROPORTIONAL to this
 * (higher = review harder); a local single-agent loop can ignore it.
 */
scoring_budget: number
/**
 * Root-relative paths of modules affected by this unit but NOT in the diff
 * (the out-of-diff context the agent must reason about).
 */
out_of_diff: string[]
/**
 * Routed expert(s), when ownership signals are available.
 */
expert: string[]
/**
 * Direct test adjacency of this unit. Absent when the graph was not
 * retained or the unit is itself a test file.
 */
test_adjacency?: (TestAdjacency | null)
}
/**
 * One stable per-hunk CHANGE ANCHOR: a changed region the agent may cite as a
 * judgment anchor IN ADDITION to a `signal_id`. Where a `signal_id` anchors a
 * graph FINDING ("fallow emitted this exact finding"), a change_anchor anchors
 * only a changed REGION ("fallow confirms this region changed") , a strictly
 * weaker guarantee, surfaced as `anchor_kind` on the accepted judgment so a
 * consumer can tell the two apart. Graph/diff-derived; NEVER from prose.
 */
export interface ChangeAnchor {
/**
 * Stable, CONTENT-addressed id: `chg:<16-hex>` over the file path + the
 * normalized added text (line numbers are NOT hashed, so an edit above the
 * hunk or a whitespace-only change does not move the id).
 */
change_anchor: string
/**
 * Root-relative path of the changed file.
 */
file: string
/**
 * 1-based first line of the hunk in the head file (display/deep-link only;
 * NOT part of the id).
 */
start_line: number
/**
 * Number of added lines in the hunk (display only; NOT part of the id).
 */
line_count: number
/**
 * Rename-durable anchor: the id this same hunk would have had under the
 * pre-rename path. `None` unless the file was renamed in this change, so an
 * agent that cited the anchor before a `git mv` still resolves.
 */
previous_change_anchor?: (string | null)
}
/**
 * The shape the agent must return, embedded in the guide so a thin skill needs
 * no frozen copy. Documents the anchoring + staleness contract in the wire.
 */
export interface AgentSchema {
/**
 * How the agent must structure each judgment: cite an emitted `signal_id`
 * or `change_anchor`, add free-text `framing` (non-deterministic, fenced),
 * an optional `concern`, and an optional `action` from the closed
 * vocabulary.
 */
judgment_shape: string
/**
 * The agent MUST echo this `graph_snapshot_hash` back in its JSON; a
 * mismatch on reentry REFUSES the payload as stale.
 */
echo_field: string
/**
 * The anchoring rule name.
 */
anchoring_rule: string
/**
 * The closed `action` vocabulary ([`JUDGMENT_ACTIONS`]): a judgment with
 * any other value is rejected on reentry.
 */
action_vocabulary: string[]
/**
 * The recommended `concern` vocabulary ([`JUDGMENT_CONCERNS`]), documented
 * for grouping; free text is still accepted.
 */
concern_vocabulary: string[]
}
/**
 * The `fallow review --walkthrough-file` validation envelope: the result of
 * post-validating the agent's judgment against the live graph. Always exit 0.
 */
export interface WalkthroughValidation {
schema_version: ReviewBriefSchemaVersion
/**
 * Fallow CLI version that produced this validation.
 */
version: string
/**
 * Command discriminator singleton: always `"review-walkthrough-validation"`.
 */
command: string
/**
 * The current run's deterministic graph-snapshot hash.
 */
graph_snapshot_hash: string
/**
 * `true` when the agent's echoed hash != the current hash (the tree moved):
 * the WHOLE payload is refused, `accepted` is empty.
 */
stale: boolean
/**
 * Judgments that cite a real fallow-emitted signal, framing fenced.
 */
accepted: AcceptedJudgment[]
/**
 * Judgments rejected (unanchored signal id, or all-rejected when stale).
 */
rejected: RejectedJudgment[]
/**
 * Count of accepted judgments.
 */
accepted_count: number
/**
 * Count of rejected judgments.
 */
rejected_count: number
/**
 * Count of accepted judgments whose `signal_id` resolved against the live
 * allowlist. Zero unanchored when this equals `accepted_count` and there are
 * no rejections (the clean done-condition).
 */
unanchored_count: number
}
/**
 * One accepted judgment: the real anchored signal passed through with the
 * agent's framing FENCED as non-deterministic.
 */
export interface AcceptedJudgment {
/**
 * The fallow-emitted `signal_id` (verified against the allowlist). Empty
 * when this judgment was anchored by a `change_anchor` instead.
 */
signal_id: string
/**
 * The fallow-emitted `change_anchor` (verified against the allowlist). Empty
 * when this judgment was anchored by a `signal_id`.
 */
change_anchor: string
/**
 * Which anchor resolved: `"signal"` (a graph FINDING, the strong anchor) or
 * `"change"` (a changed REGION only, the weaker anchor). Lets a consumer
 * distinguish a finding-anchored judgment from a region-anchored one rather
 * than collapsing both into one accepted bucket.
 */
anchor_kind: string
/**
 * The agent's fenced free-text framing.
 */
agent_framing: string
/**
 * The agent's optional concern category.
 */
concern?: (string | null)
/**
 * The author-action label the judgment carries (`block`, `address`,
 * `consider`, or `fyi`), validated against [`JUDGMENT_ACTIONS`]. It tells
 * the receiving author what is required and what is optional; it is the
 * reviewer's instruction, fenced with the framing, never a gate.
 */
action?: (string | null)
/**
 * Hard fence: always `false`. The framing is agent prose, never a
 * deterministic fallow result, so it never gates or auto-posts.
 */
deterministic: boolean
}
/**
 * One rejected judgment plus the reason it was rejected.
 */
export interface RejectedJudgment {
/**
 * The `signal_id` the agent cited (fallow never emitted it). Empty when the
 * judgment cited a `change_anchor` instead.
 */
signal_id: string
/**
 * The `change_anchor` the agent cited (fallow never emitted it). Empty when
 * the judgment cited a `signal_id` instead.
 */
change_anchor: string
/**
 * The rejection reason: `unanchored-signal-id` (cited a signal fallow did
 * not emit), `unknown-change-anchor` (cited a region fallow did not emit),
 * `stale-snapshot` (the tree moved), or `invalid-action` (an `action`
 * outside [`JUDGMENT_ACTIONS`]; the anchor itself resolved).
 */
reason: string
/**
 * The offending value for an `invalid-action` rejection, echoed so the
 * agent can correct it in one round trip. Absent for the other reasons.
 */
invalid_value?: (string | null)
}
/**
 * The `fallow suppressions --format json` envelope. `FallowOutput`
 * discriminates it by the `kind: "suppression-inventory"` tag.
 *
 * A read-only projection over the suppression markers present in analyzed
 * files this run: nothing here is a finding, and the command that emits it
 * always exits 0.
 */
export interface SuppressionInventoryOutput {
schema_version: SuppressionInventorySchemaVersion
/**
 * What the run was asked to narrow and whether it did. See
 * [`crate::RequestOutcomes`] for the full contract.
 *
 * `fallow suppressions` accepts `--changed-since`, and an unresolvable ref
 * widens the inventory to the whole project rather than failing the run.
 * Until this member existed the only account of that was a stderr line,
 * which `--quiet` removes, so an inventory read as scoped to the change
 * could silently be the whole project's (issue #2734).
 *
 * The command applies no diff filter, so the object carries the
 * `changed-since` entry only. Omitted when the run was asked for nothing,
 * which keeps an inventory that passed no narrowing flag byte-identical and
 * leaves `schema_version` at `1`.
 */
request_outcomes?: (RequestOutcomes | null)
summary: SuppressionInventorySummary
/**
 * Per-file suppression listings, sorted by path then line.
 */
files: SuppressionInventoryFile[]
}
/**
 * Project-level totals for the suppression inventory.
 */
export interface SuppressionInventorySummary {
/**
 * Total suppression markers in scope.
 */
total: number
/**
 * Number of files carrying at least one marker.
 */
files: number
/**
 * Markers without a human-authored `--` reason.
 */
without_reason: number
/**
 * Markers that also appear as stale-suppression findings this run. This
 * is a JOIN against the existing stale-suppression detector's output
 * (matched by file and kind), not a new detection.
 */
stale: number
/**
 * Marker counts per suppressed kind, sorted by count (descending) then
 * kind. `kind` is `null` for blanket markers, mirroring the per-entry
 * contract.
 */
by_kind: SuppressionKindCount[]
}
/**
 * One `by_kind` row in the suppression inventory summary.
 */
export interface SuppressionKindCount {
/**
 * The suppressed issue kind in kebab-case, or `null` for blanket markers
 * (rendered as "blanket" in human output; machine consumers branch on
 * `null`).
 */
kind?: (string | null)
/**
 * Number of markers targeting this kind.
 */
count: number
}
/**
 * One file's suppression listing.
 */
export interface SuppressionInventoryFile {
/**
 * Project-root-relative path, forward-slash separated.
 */
path: string
/**
 * Markers in this file, sorted by line.
 */
suppressions: SuppressionInventoryEntry[]
}
/**
 * One suppression marker in the inventory.
 */
export interface SuppressionInventoryEntry {
/**
 * 1-based line of the suppression comment itself; 0 only if unknown.
 */
line: number
/**
 * The suppressed issue kind in kebab-case (e.g. `"unused-export"`), or
 * `null` for a blanket marker that suppresses every kind on its target.
 * Human output renders the blanket case as the literal word "blanket";
 * the JSON contract deliberately keeps `null` so machine consumers
 * branch on `null` instead of a magic string.
 */
kind?: (string | null)
level: SuppressionInventoryLevel
origin: SuppressionInventoryOrigin
/**
 * Human-authored reason after `--`; `null` when absent.
 */
reason?: (string | null)
/**
 * Whether a human-authored reason is present.
 */
reason_present: boolean
}
/**
 * Versioned readiness envelope emitted by `fallow doctor --format json`.
 */
export interface DoctorOutput {
schema_version: DoctorSchemaVersion
version: ToolVersion
root: DoctorProjectRoot
status: DoctorStatus
summary: DoctorSummary
/**
 * Checks in stable contract order.
 */
checks: DoctorCheck[]
}
/**
 * Counts for every per-check status.
 */
export interface DoctorSummary {
/**
 * Successful checks.
 */
pass: number
/**
 * Advisory checks.
 */
warn: number
/**
 * Failed checks.
 */
fail: number
/**
 * Inapplicable or prerequisite-blocked checks.
 */
skipped: number
}
/**
 * One deterministic doctor check result.
 */
export interface DoctorCheck {
id: DoctorCheckId
category: DoctorCheckCategory
status: DoctorCheckStatus
/**
 * Whether failure makes the project not ready.
 */
required: boolean
/**
 * Human-readable result with no host-specific absolute paths.
 */
message: string
/**
 * Optional actionable next command.
 */
remediation?: (DoctorRemediation | null)
}
/**
 * Actionable remediation attached to a doctor check.
 */
export interface DoctorRemediation {
/**
 * Command the user or agent can choose to run.
 */
command: string
cwd: DoctorProjectRoot
/**
 * Whether running the command can modify project files or dependencies.
 */
mutating: boolean
}
/**
 * Envelope emitted by `fallow type-aware status --format json`.
 */
export interface TypeAwareStatusOutput {
schema_version: TypeAwareStatusSchemaVersion
version: ToolVersion
/**
 * Whether a usable type-aware companion was found.
 */
available: boolean
/**
 * How the companion was discovered, e.g. `installed-sibling`.
 */
discovery_source?: (string | null)
/**
 * Root-relative companion path, or only the executable name when the
 * companion lives outside the analyzed project.
 */
companion_path?: (string | null)
/**
 * npm package version of the companion, when known.
 */
package_version?: (string | null)
/**
 * Type-aware protocol version fallow speaks.
 */
protocol_version: number
/**
 * Checker backend family, e.g. `typescript-go`.
 */
backend_family?: (string | null)
/**
 * Version of the checker backend, when known.
 */
backend_version?: (string | null)
/**
 * How to make the companion available, when it is not.
 */
remediation?: (string | null)
}
/**
 * Raw `fallow similar-code --format json` output.
 */
export interface SimilarCodeOutput {
schema_version: SimilarCodeSchemaVersion
version: ToolVersion
elapsed_ms: ElapsedMs
generation: SimilarCodeGeneration
/**
 * Deterministically ordered unverified candidates.
 */
candidates: SimilarCodeCandidate[]
completion: SimilarCodeCompletion
/**
 * Non-severity diagnostics in deterministic order.
 */
diagnostics: SimilarCodeDiagnostic[]
}
/**
 * Complete provenance needed to reproduce candidate generation.
 */
export interface SimilarCodeGeneration {
/**
 * Version of extraction and normalization semantics used for both IDs.
 */
extraction_semantics_version: number
/**
 * Version of the calculation that produces model embeddings.
 */
embedding_semantics_version: number
provider: SimilarCodeProviderProvenance
model: SimilarCodeModelProvenance
parameters: SimilarCodeGenerationParameters
scope: SimilarCodeScopeProvenance
/**
 * Minimum cosine similarity admitted into the candidate set.
 */
threshold: number
/**
 * Minimum source line count admitted into function extraction.
 */
min_lines: number
}
/**
 * Immutable local provider provenance for one generation run.
 */
export interface SimilarCodeProviderProvenance {
provider: SimilarCodeProvider
/**
 * Exact companion package version.
 */
companion_version: string
/**
 * Companion protocol version negotiated for this run.
 */
protocol_version: number
/**
 * Whether source content left the local machine. Version 1 requires false.
 */
source_left_machine: boolean
}
/**
 * Immutable model artifact provenance.
 */
export interface SimilarCodeModelProvenance {
/**
 * Stable model identifier.
 */
model_id: string
/**
 * Immutable model revision.
 */
revision: string
/**
 * SHA-256 digest of the exact model artifact bytes.
 */
artifact_sha256: string
/**
 * SPDX license identifier or reviewed license label.
 */
license: string
/**
 * Embedding vector dimensions.
 */
dimensions: number
}
/**
 * Parameters that materially affect generated embeddings and scores.
 */
export interface SimilarCodeGenerationParameters {
/**
 * Numeric representation used for model inference.
 */
dtype: string
/**
 * Pooling strategy applied to model output.
 */
pooling: string
/**
 * Whether vectors were normalized before comparison.
 */
normalized: boolean
/**
 * Maximum inference batch size used by the run.
 */
batch_size: number
/**
 * Maximum tokenizer length before deterministic truncation.
 */
max_tokens: number
/**
 * Digest over the complete effective generation parameter set.
 */
parameter_sha256: string
}
/**
 * Effective endpoint scope used for corpus admission and pair retention.
 */
export interface SimilarCodeScopeProvenance {
/**
 * Whether file, changed-file, diff, or workspace scoping was active.
 */
active: boolean
/**
 * Sorted project-root-relative paths satisfying every active predicate.
 */
paths: string[]
}
/**
 * One unverified semantic similar-code candidate.
 */
export interface SimilarCodeCandidate {
/**
 * Snapshot-stable opaque candidate identity.
 */
candidate_id: string
/**
 * Content-stable key used for safe line-movement rebinding.
 */
review_key: string
left: SimilarCodeLocation
right: SimilarCodeLocation
/**
 * Cosine similarity reported by the pinned provider and model.
 */
similarity: number
similarity_band: SimilarCodeSimilarityBand
verification_status: SimilarCodeVerificationStatus
enrichment: SimilarCodeEnrichmentAvailability
/**
 * Read-only inspect and review affordances.
 */
actions: SimilarCodeAction[]
}
/**
 * Exact named location of one candidate function.
 */
export interface SimilarCodeLocation {
/**
 * Project-root-relative, forward-slash path.
 */
path: string
/**
 * Extracted function or method name.
 */
name: string
/**
 * One-based inclusive start line.
 */
start_line: number
/**
 * One-based inclusive start column.
 */
start_column: number
/**
 * One-based inclusive end line.
 */
end_line: number
/**
 * One-based inclusive end column.
 */
end_column: number
/**
 * SHA-256 digest of the exact extracted function source.
 */
source_sha256: string
}
/**
 * Availability of every supported source-grounded enrichment.
 */
export interface SimilarCodeEnrichmentAvailability {
graph_relationship: SimilarCodeEnrichmentState
entry_point_reachability: SimilarCodeEnrichmentState
callers: SimilarCodeEnrichmentState
callees: SimilarCodeEnrichmentState
ownership: SimilarCodeEnrichmentState
churn: SimilarCodeEnrichmentState
tests: SimilarCodeEnrichmentState
deterministic_clone_coverage: SimilarCodeEnrichmentState
runtime: SimilarCodeEnrichmentState
}
/**
 * Read-only follow-up exposed for a candidate.
 */
export interface SimilarCodeAction {
action: SimilarCodeActionType
/**
 * Human-readable description of the read-only operation.
 */
description: string
/**
 * Explicit mutation guarantee. Version 1 requires this to be true.
 */
read_only: boolean
}
/**
 * Typed completion, limit, skip, and cache accounting.
 */
export interface SimilarCodeCompletion {
status: SimilarCodeCompletionStatus
/**
 * Per-phase completion in pipeline order.
 */
phases: SimilarCodePhaseCompletion[]
limits: SimilarCodeLimits
/**
 * Aggregated skips in phase and reason order.
 */
skips: SimilarCodeSkip[]
cache: SimilarCodeCacheSummary
/**
 * Aggregate model inference wall time reported by the local provider.
 */
provider_inference_ms: number
}
/**
 * Accounting for one bounded generation phase.
 */
export interface SimilarCodePhaseCompletion {
phase: SimilarCodePhase
status: SimilarCodePhaseStatus
/**
 * Number of admitted inputs processed by this phase.
 */
processed: number
/**
 * Total admitted inputs known to this phase, when available.
 */
total?: (number | null)
/**
 * Stable explanation when the phase did not complete.
 */
reason?: (string | null)
}
/**
 * Effective resource limits for a similar-code run.
 */
export interface SimilarCodeLimits {
/**
 * Maximum source files admitted.
 */
max_files: number
/**
 * Maximum extracted functions admitted.
 */
max_functions: number
/**
 * Maximum aggregate normalized source bytes admitted.
 */
max_source_bytes: number
/**
 * Maximum normalized bytes admitted for one function.
 */
max_function_bytes: number
/**
 * Maximum embedding batch size.
 */
max_batch_size: number
/**
 * Maximum vector bytes retained for comparison.
 */
max_vector_bytes: number
/**
 * Maximum pair comparisons performed.
 */
max_comparisons: number
/**
 * Maximum candidates returned.
 */
max_candidates: number
/**
 * Maximum returned neighbors per function.
 */
max_neighbors_per_function: number
/**
 * End-to-end timeout in milliseconds.
 */
timeout_ms: number
}
/**
 * Count of skipped work for a stable reason.
 */
export interface SimilarCodeSkip {
phase: SimilarCodePhase
reason: SimilarCodeSkipReason
/**
 * Number of inputs skipped for this phase and reason.
 */
count: number
}
/**
 * Privacy-safe cache accounting. Source fragments are never represented.
 */
export interface SimilarCodeCacheSummary {
status: SimilarCodeCacheStatus
/**
 * Valid vector cache hits.
 */
hits: number
/**
 * Vector cache misses.
 */
misses: number
/**
 * Newly written vector cache entries.
 */
writes: number
/**
 * Corrupt or incompatible entries ignored safely.
 */
invalid_entries: number
}
/**
 * Actionable diagnostic without a severity or gate implication.
 */
export interface SimilarCodeDiagnostic {
domain: SimilarCodeDiagnosticDomain
/**
 * Stable machine-readable code.
 */
code: string
/**
 * Bounded human-readable explanation.
 */
message: string
/**
 * Optional project-root-relative path.
 */
path?: (string | null)
}
/**
 * `fallow similar-code inspect --format json` output.
 */
export interface SimilarCodeInspectOutput {
schema_version: SimilarCodeInspectSchemaVersion
version: ToolVersion
elapsed_ms: ElapsedMs
generation: SimilarCodeGeneration
candidate: SimilarCodeCandidate
packet: SimilarCodeInspectPacket
completion: SimilarCodeCompletion
/**
 * Non-severity diagnostics in deterministic order.
 */
diagnostics: SimilarCodeDiagnostic[]
}
/**
 * Bounded source-grounded packet for one immutable candidate.
 */
export interface SimilarCodeInspectPacket {
/**
 * Candidate identity this packet describes.
 */
candidate_id: string
/**
 * Content-stable review key this packet describes.
 */
review_key: string
availability: SimilarCodeEnrichmentAvailability
/**
 * Graph relationship label, when relationship evidence is available.
 */
graph_relationship?: (string | null)
left: SimilarCodeSideEvidence
right: SimilarCodeSideEvidence
}
/**
 * Bounded evidence for one side of an inspect packet.
 */
export interface SimilarCodeSideEvidence {
/**
 * Bounded source window included only in inspect output, never raw output or cache.
 */
source_window?: (string | null)
/**
 * Declared parameter count when extraction supplied it.
 */
parameter_count?: (number | null)
/**
 * Whether the inspected function is declared async.
 */
is_async?: (boolean | null)
/**
 * Whether the inspected function is a generator.
 */
is_generator?: (boolean | null)
/**
 * Whether the inspected function contains an await expression.
 */
has_await?: (boolean | null)
/**
 * Whether the inspected function contains a throw expression.
 */
has_throw?: (boolean | null)
/**
 * Conservative syntactic side-effect classification.
 */
side_effect_hint?: (SimilarCodeSideEffectHint | null)
/**
 * Whether the function is reachable from a configured entry point.
 */
entry_point_reachable?: (boolean | null)
/**
 * Bounded, deterministically ordered direct callers.
 */
callers: SimilarCodeNamedReference[]
/**
 * Bounded, deterministically ordered direct callees.
 */
callees: SimilarCodeNamedReference[]
/**
 * Bounded, deterministically ordered ownership labels.
 */
owners: string[]
/**
 * Recent commit count in the configured churn window.
 */
churn_commits?: (number | null)
/**
 * Bounded, root-relative related test paths.
 */
tests: string[]
/**
 * Fraction covered by deterministic clone groups, from zero through one.
 */
deterministic_clone_coverage?: (number | null)
/**
 * Runtime observation count when compatible runtime evidence is present.
 */
runtime_observations?: (number | null)
}
/**
 * One named graph reference used in an inspect packet.
 */
export interface SimilarCodeNamedReference {
/**
 * Project-root-relative, forward-slash path.
 */
path: string
/**
 * Referenced symbol name.
 */
name: string
/**
 * One-based source line.
 */
line: number
}
/**
 * `fallow similar-code review --format json` output.
 */
export interface SimilarCodeReviewOutput {
schema_version: SimilarCodeReviewSchemaVersion
version: ToolVersion
elapsed_ms: ElapsedMs
generation: SimilarCodeGeneration
review: SimilarCodeReviewProvenance
/**
 * Raw candidates joined with verdicts in candidate order.
 */
candidates: SimilarCodeReviewedCandidate[]
completion: SimilarCodeCompletion
/**
 * Non-severity diagnostics in deterministic order.
 */
diagnostics: SimilarCodeDiagnostic[]
}
/**
 * Digests that make the review join reproducible without exposing source.
 */
export interface SimilarCodeReviewProvenance {
/**
 * SHA-256 digest of the exact candidate JSON input bytes.
 */
candidates_sha256: string
/**
 * SHA-256 digest of the exact verdict JSON input bytes.
 */
verdicts_sha256: string
}
/**
 * One candidate joined with its separate verdict, if safely matched.
 */
export interface SimilarCodeReviewedCandidate {
candidate: SimilarCodeCandidate
/**
 * Safely matched external verdict, absent when still unverified.
 */
verdict?: (SimilarCodeVerdict | null)
verdict_match: SimilarCodeVerdictMatch
outcome: SimilarCodeDomainOutcome
}
/**
 * Separate verdict input for one immutable candidate.
 */
export interface SimilarCodeVerdict {
/**
 * Snapshot identity from the raw candidate.
 */
candidate_id: string
/**
 * Content-stable identity from the raw candidate.
 */
review_key: string
/**
 * Whether the pair is useful enough to review. Null means undecided.
 */
candidate_worthy?: (boolean | null)
/**
 * Whether the two functions behave equivalently. Null means undecided.
 */
behaviorally_equivalent?: (boolean | null)
/**
 * Whether consolidation is safe. Null means undecided.
 */
refactor_safe?: (boolean | null)
outcome: SimilarCodeDomainOutcome
/**
 * Bounded explanation grounded in the inspected sources.
 */
rationale: string
}
/**
 * Single CodeClimate-compatible issue inside [`CodeClimateOutput`].
 */
export interface CodeClimateIssue {
type: CodeClimateIssueKind
/**
 * Fallow rule identifier, e.g. `fallow/unused-file`.
 */
check_name: string
/**
 * Human-readable finding description.
 */
description: string
/**
 * CodeClimate category labels, e.g. `Clarity` or `Duplication`.
 */
categories: string[]
severity: CodeClimateSeverity
/**
 * Stable finding fingerprint GitLab uses to track issues across pushes.
 */
fingerprint: string
location: CodeClimateLocation
/**
 * Other source locations that provide evidence for the finding. GitLab's
 * Code Quality widget ignores this standard CodeClimate field, but Fallow
 * preserves it for review-comment rendering.
 */
other_locations?: CodeClimateLocation[]
/**
 * Optional owner attribution used by grouped dead-code output.
 */
owner?: (string | null)
/**
 * Optional grouping attribution used by grouped health and duplication
 * output.
 */
group?: (string | null)
}
/**
 * Location block inside [`CodeClimateIssue::location`].
 */
export interface CodeClimateLocation {
/**
 * File path relative to the analysed root.
 */
path: string
lines: CodeClimateLines
}
/**
 * Inclusive line range for [`CodeClimateLocation`].
 */
export interface CodeClimateLines {
/**
 * 1-based start line.
 */
begin: number
/**
 * Inclusive 1-based end line. Omitted for point findings.
 */
end?: (number | null)
}
/**
 * Structured JSON error emitted on stdout when `--format json` is active and a
 * command fails. It carries no `kind` discriminator: it is distinguished from
 * the kind-tagged success envelopes by the required `error: true` field, and is
 * a document-root branch alongside `FallowOutput` and `CodeClimateOutput` in
 * `docs/output-schema.json`. Agents that pass `--format json` and observe a
 * non-zero exit code parse this shape from stdout.
 */
export interface ErrorOutput {
/**
 * Always `true`. The discriminator that separates an error document from a
 * success envelope (which instead carries a `kind`).
 */
error: boolean
/**
 * Human-readable error message.
 */
message: string
/**
 * The process exit code the CLI returns alongside this document.
 */
exit_code: number
/**
 * Stable machine-readable code such as `FALLOW_INVALID_COVERAGE_PATH`,
 * when the failure has one. Present so an agent can branch on the reason
 * without pattern-matching the human message.
 */
code?: (string | null)
/**
 * Remediation hint for the caller, when the failure has one.
 */
help?: (string | null)
}


/**
 * @deprecated Legacy alias for the dead-code/check schema version. Use the exact envelope-specific alias instead.
 */
export type SchemaVersion = CheckSchemaVersion;
/**
 * Inner complexity-violation payload, flattened into `HealthFinding`
 * on the wire via `#[serde(flatten)]`. Exposed here because
 * json-schema-to-typescript dedupes definitions whose property set is
 * fully subsumed by a flattening parent; the schema definition exists
 * in `docs/output-schema.json` but the TS interface is suppressed.
 * Consumers that need to type just the inner payload should use this
 * alias; consumers that need the full envelope (with `actions` and
 * optional `introduced`) should use `HealthFinding` directly.
 */
export type ComplexityViolation = Omit<HealthFinding, "actions" | "introduced">;

/**
 * Inner hotspot payload, flattened into `HotspotFinding` on the wire
 * via `#[serde(flatten)]`. Exposed here for the same reason as
 * `ComplexityViolation`: jstt dedupes the inner because its property
 * set is fully subsumed by the wrapper. Consumers that want only the
 * inner shape should use this alias; consumers that need the full
 * envelope with `actions` should use `HotspotFinding` directly.
 * Unlike `HealthFinding`, the wrapper does not carry `introduced`
 * because hotspot ranking does not run through audit attribution.
 */
export type HotspotEntry = Omit<HotspotFinding, "actions">;

/**
 * Inner refactoring-target payload, flattened into
 * `RefactoringTargetFinding` on the wire via `#[serde(flatten)]`.
 * Exposed here for the same reason as `ComplexityViolation`: jstt
 * dedupes the inner because its property set is fully subsumed by
 * the wrapper. Consumers that want only the inner shape should use
 * this alias; consumers that need the full envelope with `actions`
 * should use `RefactoringTargetFinding` directly. Unlike
 * `HealthFinding`, the wrapper does not carry `introduced` because
 * refactoring targets do not run through audit attribution.
 */
export type RefactoringTarget = Omit<RefactoringTargetFinding, "actions">;

//
// =============================================================================
// Backwards-compat aliases
// =============================================================================
//
// The aliases below map pre-#384 / #408 / #409 bare names to their typed
// envelope wrappers. The wire shape is byte-identical: each wrapper flattens
// the bare finding's fields via `#[serde(flatten)]` and adds `actions[]`
// (and, where the wrapper participates in `fallow audit` attribution, the
// optional `introduced` flag). Per-alias rationale lives in each alias's
// JSDoc below.
//
// Why these aliases exist: `json-schema-to-typescript` drops the orphan
// inner definitions for `#[serde(flatten)]` wrappers (even with
// `unreachableDefinitions: true`), so the bare names disappear from this
// generated `.d.ts` without an explicit alias. External consumers that
// import the bare names from `fallow/types` would break at upgrade time
// otherwise.
//
// Stability commitment: legacy output aliases remain supported throughout v3.
// Removing them requires an explicit deprecation period and a future major
// release. New code should prefer the `*Finding` wrapper names. Full
// public-consumer policy:
// https://github.com/fallow-rs/fallow/blob/main/docs/backwards-compatibility.md
//

/**
 * Backwards-compat alias for the pre-#409 bare clone-group name.
 * jstt dedupes the bare interface because every field is fully
 * subsumed by `CloneGroupFinding` (the wrapper flattens the bare
 * `CloneGroup` via `#[serde(flatten)]`). Aliased to the full
 * wrapper (not `Omit<>`-stripped) because the pre-migration wire
 * always carried `actions[]` on every clone group via the legacy
 * `inject_dupes_actions` post-pass, so the bare alias matching the
 * wrapper shape is the byte-faithful continuation. Consumers that
 * imported `CloneGroup` from `fallow/types` pre-migration continue
 * to work via this alias; new code should prefer `CloneGroupFinding`.
 */
export type CloneGroup = CloneGroupFinding;

/**
 * Backwards-compat alias for the pre-#409 bare clone-family name.
 * jstt dedupes the bare interface because every field is subsumed
 * by `CloneFamilyFinding`. The wrapper's `groups[]` items are
 * `CloneGroupFinding` rather than bare `CloneGroup`, which matches
 * the pre-migration wire shape (the legacy `inject_dupes_actions`
 * post-pass injected `actions[]` on every nested group too).
 * Consumers that imported `CloneFamily` from `fallow/types`
 * pre-migration continue to work via this alias; new code should
 * prefer `CloneFamilyFinding`.
 */
export type CloneFamily = CloneFamilyFinding;

/**
 * Backwards-compat alias for the pre-#409 bare attributed-clone-group
 * name (`fallow dupes --group-by` per-bucket attribution).
 * Consumers that imported `AttributedCloneGroup` from `fallow/types`
 * pre-migration continue to work via this alias; new code should
 * prefer `AttributedCloneGroupFinding`.
 */
export type AttributedCloneGroup = AttributedCloneGroupFinding;

/**
 * Backwards-compat alias for the pre-#409 `DuplicationReport` name.
 * The wire shape is byte-identical between the two: the typed
 * `DupesReportPayload` mirrors `DuplicationReport` field-for-field
 * with `clone_groups[]` / `clone_families[]` carrying typed
 * `CloneGroupFinding` / `CloneFamilyFinding` wrappers instead of
 * bare findings (the pre-migration wire ALSO carried `actions[]`
 * on each item via the legacy `inject_dupes_actions` post-pass).
 * Consumers that imported `DuplicationReport` from `fallow/types`
 * pre-migration continue to work via this alias; new code should
 * prefer `DupesReportPayload`.
 */
export type DuplicationReport = DupesReportPayload;

/**
 * Backwards-compat alias for the pre-#384 bare `BoundaryViolation` name.
 * The wire shape is byte-identical: `BoundaryViolationFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `BoundaryViolation` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `BoundaryViolationFinding`.
 */
export type BoundaryViolation = BoundaryViolationFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `CircularDependency` name.
 * The wire shape is byte-identical: `CircularDependencyFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `CircularDependency` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `CircularDependencyFinding`.
 */
export type CircularDependency = CircularDependencyFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `DeprecatedExportInUse` name.
 * The wire shape is byte-identical: `DeprecatedExportInUseFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `DeprecatedExportInUse` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `DeprecatedExportInUseFinding`.
 */
export type DeprecatedExportInUse = DeprecatedExportInUseFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `DevDependencyInProduction` name.
 * The wire shape is byte-identical: `DevDependencyInProductionFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `DevDependencyInProduction` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `DevDependencyInProductionFinding`.
 */
export type DevDependencyInProduction = DevDependencyInProductionFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `DuplicateExport` name.
 * The wire shape is byte-identical: `DuplicateExportFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `DuplicateExport` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `DuplicateExportFinding`.
 */
export type DuplicateExport = DuplicateExportFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `EmptyCatalogGroup` name.
 * The wire shape is byte-identical: `EmptyCatalogGroupFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `EmptyCatalogGroup` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `EmptyCatalogGroupFinding`.
 */
export type EmptyCatalogGroup = EmptyCatalogGroupFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `MisconfiguredDependencyOverride` name.
 * The wire shape is byte-identical: `MisconfiguredDependencyOverrideFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `MisconfiguredDependencyOverride` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `MisconfiguredDependencyOverrideFinding`.
 */
export type MisconfiguredDependencyOverride = MisconfiguredDependencyOverrideFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `PackageCycle` name.
 * The wire shape is byte-identical: `PackageCycleFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `PackageCycle` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `PackageCycleFinding`.
 */
export type PackageCycle = PackageCycleFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `PrivateTypeLeak` name.
 * The wire shape is byte-identical: `PrivateTypeLeakFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `PrivateTypeLeak` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `PrivateTypeLeakFinding`.
 */
export type PrivateTypeLeak = PrivateTypeLeakFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `ReExportCycle` name.
 * The wire shape is byte-identical: `ReExportCycleFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `ReExportCycle` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `ReExportCycleFinding`.
 */
export type ReExportCycle = ReExportCycleFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `TestOnlyDependency` name.
 * The wire shape is byte-identical: `TestOnlyDependencyFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `TestOnlyDependency` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `TestOnlyDependencyFinding`.
 */
export type TestOnlyDependency = TestOnlyDependencyFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `TypeOnlyDependency` name.
 * The wire shape is byte-identical: `TypeOnlyDependencyFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `TypeOnlyDependency` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `TypeOnlyDependencyFinding`.
 */
export type TypeOnlyDependency = TypeOnlyDependencyFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `UnlistedDependency` name.
 * The wire shape is byte-identical: `UnlistedDependencyFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `UnlistedDependency` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `UnlistedDependencyFinding`.
 */
export type UnlistedDependency = UnlistedDependencyFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `UnresolvedCatalogReference` name.
 * The wire shape is byte-identical: `UnresolvedCatalogReferenceFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `UnresolvedCatalogReference` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `UnresolvedCatalogReferenceFinding`.
 */
export type UnresolvedCatalogReference = UnresolvedCatalogReferenceFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `UnresolvedImport` name.
 * The wire shape is byte-identical: `UnresolvedImportFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `UnresolvedImport` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `UnresolvedImportFinding`.
 */
export type UnresolvedImport = UnresolvedImportFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `UnusedCatalogEntry` name.
 * The wire shape is byte-identical: `UnusedCatalogEntryFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `UnusedCatalogEntry` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `UnusedCatalogEntryFinding`.
 */
export type UnusedCatalogEntry = UnusedCatalogEntryFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `UnusedDependencyOverride` name.
 * The wire shape is byte-identical: `UnusedDependencyOverrideFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `UnusedDependencyOverride` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `UnusedDependencyOverrideFinding`.
 */
export type UnusedDependencyOverride = UnusedDependencyOverrideFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `UnusedExport` name.
 * The wire shape is byte-identical: `UnusedExportFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `UnusedExport` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `UnusedExportFinding`.
 */
export type UnusedExport = UnusedExportFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `UnusedFile` name.
 * The wire shape is byte-identical: `UnusedFileFinding` flattens the bare
 * finding's fields via `#[serde(flatten)]` and adds `actions[]` plus
 * the optional audit-mode `introduced` flag. Consumers that imported
 * `UnusedFile` from `fallow/types` pre-migration continue to work via
 * this alias; new code should prefer `UnusedFileFinding`.
 */
export type UnusedFile = UnusedFileFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `UnusedDependency` union name.
 * Maps to the union of typed wrappers (`UnusedDependencyFinding`, `UnusedDevDependencyFinding`, `UnusedOptionalDependencyFinding`)
 * that replaced the pre-migration bare union. The wire shape per variant
 * is byte-identical (each wrapper flattens its bare payload and adds
 * `actions[]` plus optional `introduced`). Consumers that imported
 * `UnusedDependency` from `fallow/types` pre-migration continue to work via
 * this alias; new code should narrow on the specific wrapper variant.
 */
export type UnusedDependency = UnusedDependencyFinding | UnusedDevDependencyFinding | UnusedOptionalDependencyFinding;

/**
 * Backwards-compat alias for the pre-#384 bare `UnusedMember` union name.
 * Maps to the union of typed wrappers (`UnusedClassMemberFinding`, `UnusedEnumMemberFinding`, `UnusedStoreMemberFinding`)
 * that replaced the pre-migration bare union. The wire shape per variant
 * is byte-identical (each wrapper flattens its bare payload and adds
 * `actions[]` plus optional `introduced`). Consumers that imported
 * `UnusedMember` from `fallow/types` pre-migration continue to work via
 * this alias; new code should narrow on the specific wrapper variant.
 */
export type UnusedMember = UnusedClassMemberFinding | UnusedEnumMemberFinding | UnusedStoreMemberFinding;

/**
 * @deprecated Renamed to BaselineStaleness in 3.27.0, when dead-code and dupes started carrying the same shape. The members are unchanged.
 */
export type HealthBaselineStaleness = BaselineStaleness;