/** * Complexity-delta primitive. * * Compares two versions of a file's content (before/after) and reports, per * function, whether a complexity metric newly crossed a threshold, worsened, * improved, or is unchanged. It is the single source of truth for both the * `lien delta` CLI (write-time) and — as a documented follow-up — the PR-review * engine (review-time), so the two can never structurally disagree. * * It reuses the existing complexity machinery end to end: `chunkFile` produces * one chunk per function/method carrying cyclomatic / cognitive / Halstead * metrics, and thresholds mirror `analyzeComplexityFromChunks`. No new metrics * are invented here. * * See docs/architecture/lien-delta.md for the design and honest limitations * (function-level renames are not tracked; overloads are paired positionally). */ import type { ComplexityMetricType } from './types.js'; import { type ComplexityThresholds } from './chunk-complexity.js'; /** * Complexity thresholds — same shape and defaults as the config * `complexity.thresholds` block that `analyzeComplexityFromChunks` reads. * * #988: kept as a distinct exported name (many call sites/tests import * `ComplexityDeltaThresholds` by name) but is now a plain alias of the * canonical `ComplexityThresholds` shape (chunk-complexity.ts) rather than an * independent duplicate. */ export type ComplexityDeltaThresholds = ComplexityThresholds; /** * Default thresholds — the canonical `DEFAULT_COMPLEXITY_THRESHOLDS` * (chunk-complexity.ts). #988: this used to be an independent hardcoded copy * that this file's own comment admitted was meant to "mirror" the other one — * nothing enforced that. Now the same object, so it structurally can't drift. */ export declare const DEFAULT_COMPLEXITY_DELTA_THRESHOLDS: ComplexityDeltaThresholds; export type ComplexityDeltaVerdict = /** FAILS gate: function added and already over threshold. */ 'new-over-threshold' /** FAILS gate: function existed under threshold and is now over it. */ | 'crossed' /** Advisory: increased but still under threshold. */ | 'worsened' /** Advisory: was over threshold before and still is (no NEW crossing). */ | 'pre-existing' /** Complexity decreased (may still be over threshold — that is fine). */ | 'improved' /** No change. */ | 'unchanged' /** Function added, under threshold. */ | 'new-under-threshold' /** Function deleted. */ | 'removed'; export interface MetricComplexityDelta { metricType: ComplexityMetricType; /** null => function is newly added (absent in "before"). */ before: number | null; /** null => function was removed (absent in "after"). */ after: number | null; threshold: number; verdict: ComplexityDeltaVerdict; } export interface FunctionComplexityDelta { /** Qualified match key, e.g. "MyClass::doThing". */ key: string; symbolName: string; parentClass?: string; filepath: string; language: string; /** Location in the "after" image (or "before" if the function was removed). */ startLine: number; /** Worst verdict across the function's metrics. */ verdict: ComplexityDeltaVerdict; /** True iff the verdict is a failing verdict ('crossed' | 'new-over-threshold'). */ isRegression: boolean; metrics: MetricComplexityDelta[]; } export interface FileComplexityDelta { filepath: string; /** Set when the file was renamed. */ oldPath?: string; status: 'added' | 'deleted' | 'modified' | 'renamed'; /** Functions whose verdict is not 'unchanged', sorted worst-first. */ functions: FunctionComplexityDelta[]; } export interface ComplexityDeltaSummary { filesChanged: number; functionsAnalyzed: number; regressions: number; crossed: number; newOverThreshold: number; worsened: number; improved: number; } export interface ComplexityDeltaResult { files: FileComplexityDelta[]; /** Flattened convenience view of functions with a failing verdict. */ regressions: FunctionComplexityDelta[]; summary: ComplexityDeltaSummary; /** The resolved thresholds actually applied. */ thresholds: ComplexityDeltaThresholds; } export interface FileContentChange { /** Path in the "after" tree (or the deleted path). */ filepath: string; /** HEAD content; null = file added. */ before: string | null; /** Working-tree content; null = file deleted. */ after: string | null; /** Previous path, when renamed. */ oldPath?: string; } export declare function resolveComplexityDeltaThresholds(overrides?: Partial): ComplexityDeltaThresholds; /** Whether any function in the result is a gate-failing regression. */ export declare function hasRegressions(result: ComplexityDeltaResult): boolean; /** * Classify one metric given before/after values and a threshold. * * Semantics note (`improved` vs `pre-existing` for a standing violation): * `improved` is reserved for a decrease that lands **strictly below** the * threshold. A function that was over threshold and drops but is *still* over * threshold (e.g. 20 → 18 against 15) is `pre-existing`, NOT `improved` — the * violation persists, and the report must never imply a still-violating * function is healthy. Neither verdict is a gate regression, so the exit code * is unaffected either way; this is purely about the honesty of the label. * * Exported for unit testing of the boundary cases. */ export declare function classifyMetric(before: number | null, after: number | null, threshold: number): ComplexityDeltaVerdict; /** Compute the complexity delta for a single file's before/after content. */ export declare function computeFileComplexityDelta(change: FileContentChange, thresholds?: Partial): FileComplexityDelta; /** Compute the aggregated complexity delta across many files' before/after content. */ export declare function computeComplexityDelta(changes: FileContentChange[], thresholds?: Partial): ComplexityDeltaResult; //# sourceMappingURL=complexity-delta.d.ts.map