import type { GraphBundle, GraphEdge, NodeMetric } from "audit-tools/shared"; /** A per-node structural metric surfaced as a flat, deterministically sorted row. */ export interface NodeMetricSignal { /** Repo-path node id the metric was computed for. */ node: string; /** Raw metric value. */ value: number; /** Concrete algorithm name (e.g. `cyclomatic-approx`, `duplicate-line-count`). */ measure: string; /** Scope of source the measure actually covered. */ reach: NodeMetric["reach"]; } /** * A seam: an edge of the undirected graph projection whose removal disconnects * the two endpoints (a bridge / cut-edge). Seams flag the load-bearing single * connections in the dependency graph — the places where one link is the only * thing keeping two regions joined. `from`/`to` are sorted lexicographically so * the same undirected edge always has a stable orientation. */ export interface SeamSignal { from: string; to: string; } /** * Whole-graph derived signals, single-sourced. * * The audit graph is built per-file by the language analyzers (compiler-fidelity * import/call/reference edges) and the regex floor. Several architectural * heuristics are not edges but *queries over the merged edge set* — cycles, hub * concentration, orphans, and the "deletion test" (low-in-degree leaves). These * were previously recomputed ad-hoc inside the design assessment; they live here * now so BOTH the design assessment and the risk register read one source of * truth, and so the risk register can finally weight graph-structural risk. * * Language-neutral by construction: it consumes only `from`/`to` node ids (repo * paths), so every analyzer that contributes import/call edges — TS, Python, … — * feeds these signals identically. No new dependency, no per-ecosystem fork. */ export interface GraphSignals { /** * Deduplicated directed cycles over the structural load-order projection * ({@link structuralImportEdges}: the `imports` + `calls` buckets ONLY — * `references` prose mentions and `heuristics` edges can never fabricate a * cycle). Each entry is the node sequence of one cycle (the cycle closes from * the last node back to the first); rotations of the same directed cycle are * collapsed, distinct directed cycles over the same node set are kept apart. */ cycles: string[][]; /** * Per-node incoming edge count (number of edges whose `to` is the node), * over the merged edge set ({@link allGraphEdges}). */ fanIn: Map; /** * Per-node outgoing edge count (number of edges whose `from` is the node), * over the merged edge set ({@link allGraphEdges}). */ fanOut: Map; /** Every node that participates in at least one (load-order) cycle. */ nodesInCycles: Set; /** * Hub nodes: structural load-order fan-in AND fan-out both at least * {@link hubThreshold}. Derived from {@link structuralImportEdges} degrees, * NOT the published merged-set `fanIn`/`fanOut` maps, so reference/heuristic * edges cannot inflate a node into hub status. */ hubs: Set; /** * The structural fan-in/fan-out threshold a node must meet on both sides to * be a hub (scaled by the load-order connected-set size). */ hubThreshold: number; /** * Deletion-test candidates (the ralph-architecture-sweep heuristic): nodes that * nothing imports (`fanIn === 0`) yet which import others (`fanOut > 0`). A * low-in-degree leaf is an easy deletion/refactor target — though it may also be * a legitimate entrypoint (CLI main, test root), so this is an advisory signal * the LLM lenses adjudicate, not a verdict. Pure orphans (`fanIn === 0 && * fanOut === 0`) are deliberately NOT here — those are the separate zero-edge * orphan signal ({@link connected}-based) and would otherwise double-report. */ deletionCandidates: Set; /** Every node touched by any edge (as `from` or `to`) — the connected set. */ connected: Set; /** * Per-node complexity rows READ from `bundle.node_metrics` (no source access, * no IO — a pure reader). Sorted by node id. Empty when node_metrics is * absent/malformed. */ complexity: NodeMetricSignal[]; /** * Per-node duplication rows READ from `bundle.node_metrics` (no source access, * no IO — a pure reader). Sorted by node id. Empty when node_metrics is * absent/malformed. */ duplication: NodeMetricSignal[]; /** * Seams: bridges / cut-edges of the UNDIRECTED projection of the merged edge * set ({@link allGraphEdges}). Derived (not read): parallel edges of differing * kind are merged so they are not misreported as bridges, self-loops are * dropped, and articulation/bridge detection runs via a low-link DFS that * terminates correctly across disconnected components. Sorted by from-then-to. */ seams: SeamSignal[]; } /** * Flatten every edge bucket of a graph bundle into one edge list. `routes` is * excluded (it is a `{path,handler,method}` shape, not a `from`/`to` edge); the * `co_change` bucket is excluded too — it is temporal coupling (git-history * mining), not a structural dependency, so it must never feed cycle / hub / seam * detection. Malformed entries (missing string endpoints) are dropped so a bad * analyzer output can never throw here. */ export declare function allGraphEdges(graphBundle: GraphBundle): GraphEdge[]; /** * Select ONLY the structural load-order edges (the `imports` + `calls` * buckets) of a graph bundle. Cycle detection and hub derivation consume THIS * selection, never {@link allGraphEdges}: a `references` edge (a prose/path * mention) or a low-confidence `heuristics` edge closing a would-be loop must * not fabricate a phantom cycle that corrupts `member_of_cycle` risk signals. * The `routes` / co-change exclusions of {@link allGraphEdges} are a floor, * not a ceiling — this selector additionally excludes every non-load-order * bucket. Malformed entries (missing string endpoints) are dropped so a bad * analyzer output can never throw here. */ export declare function structuralImportEdges(graphBundle: GraphBundle): GraphEdge[]; /** * Derive every whole-graph signal from a graph bundle in one pass. Pure: no IO, * no mutation of the input — a deterministic function of the edge set, so the * structure executor (which has the live bundle) and the design-assessment * executor (which re-reads the persisted bundle) compute identical signals * without a new persisted artifact. */ export declare function deriveGraphSignals(graphBundle: GraphBundle): GraphSignals; //# sourceMappingURL=graphSignals.d.ts.map