@titan-design/code-graph 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/extractors/symbol-id.ts","../src/analysis/test-linker.ts","../src/history-recency.ts","../src/unimported-roles.ts","../src/analysis/pagerank.ts","../src/analysis/dashboard-node-metrics.ts","../src/analysis/symbol-coupling.ts","../src/analysis/partition-quality.ts","../src/check/patterns.ts","../src/analysis/graph-report-sections.ts","../src/analysis/unused-exports.ts","../src/analysis/dead-modules.ts","../src/analysis/growth-risks.ts","../src/analysis/untested-risks.ts","../src/analysis/graph-report-drift.ts","../src/analysis/dashboard-health.ts","../src/analysis/dashboard-coupling.ts","../src/analysis/package-buckets.ts","../src/analysis/graph-arch-compute.ts","../src/check/violation-key.ts","../src/diff/violation-buckets.ts"],"sourcesContent":["// Symbol nodes hang under their declaring file: `<fileId>#<qualifiedName>` (C-53),\n// where a member carries its enclosing scopes, `Job.run` (TP-182).\n// `#` never occurs in a posix path or a JS export identifier, so it can't\n// collide with a real file id — and it is printable, unlike the NUL separators\n// that made git treat files as binary (C-21).\n// Kept apart from ids.ts, which needs node:path, so browser-safe derivations can parse ids.\nexport const SYMBOL_ID_SEP = \"#\";\n\nexport function symbolId(fileId: string, qualifiedName: string): string {\n return `${fileId}${SYMBOL_ID_SEP}${qualifiedName}`;\n}\n\n/**\n * Inverse of {@link symbolId}: split a `<fileId>#<name>` symbol id back into its\n * declaring file and qualified name. Returns null for an id with no separator (a\n * plain file id), so callers can filter the symbol layer cleanly. `#` is illegal\n * in both posix paths and JS identifiers, so the first occurrence is the split.\n */\nexport function parseSymbolId(\n id: string,\n): { fileId: string; name: string } | null {\n const idx = id.indexOf(SYMBOL_ID_SEP);\n if (idx < 0) return null;\n return { fileId: id.slice(0, idx), name: id.slice(idx + 1) };\n}\n","import { couplingFor, type CoEditPair } from \"../history/change-coupling.js\";\nimport type { GraphMetric, GraphNode } from \"../types.js\";\n\n/** How a test↔source pairing was inferred. */\nexport type LinkMethod = \"path\" | \"coedit\";\n\nexport interface TestSourceLink {\n /** Node id of the test file. */\n testId: string;\n /** Node id of the (non-test) file it covers. */\n sourceId: string;\n method: LinkMethod;\n}\n\nexport interface LinkTestsOptions {\n /** Minimum co-edit count for a pass-2 (coedit) link. Default 2. */\n minCoEditCount?: number;\n}\n\nconst DEFAULT_MIN_CO_EDIT_COUNT = 2;\nconst TEST_INFIX_RE = /\\.(?:test|spec)(\\.[^./]+)$/;\nconst TESTS_DIR_RE = /(^|\\/)(?:__tests__|tests?)\\//;\n\n/**\n * Candidate source paths for a test file, by convention. Strips a\n * `.test`/`.spec` infix (co-located case) and collapses a `__tests__/` or\n * `test(s)/` directory segment (sub-directory case). Returns paths distinct\n * from the test id itself.\n */\nfunction pathCandidates(testId: string): string[] {\n const out = new Set<string>();\n const deInfixed = testId.replace(TEST_INFIX_RE, \"$1\");\n if (deInfixed !== testId) out.add(deInfixed);\n const deDir = deInfixed.replace(TESTS_DIR_RE, \"$1\");\n if (deDir !== testId) out.add(deDir);\n return [...out];\n}\n\nfunction splitFileIds(nodes: readonly GraphNode[]): { testIds: string[]; nonTestIds: Set<string> } {\n const testIds: string[] = [];\n const nonTestIds = new Set<string>();\n for (const n of nodes) {\n if (n.kind !== \"file\") continue;\n if (n.role === \"test\") testIds.push(n.id);\n else nonTestIds.add(n.id);\n }\n return { testIds, nonTestIds };\n}\n\n/**\n * Two-pass test↔source linker. Pass 1 pairs each test file with non-test files\n * matching its path conventions (high confidence). Pass 2 supplements tests\n * left unpaired by pass 1 with their strongest co-edited non-test partner from\n * change-coupling. Handles orphan tests (no pairing), orphan/untested sources\n * (no incoming link), and one-to-many pairings (a test matching several\n * sources, or a source covered by several tests).\n */\nexport function linkTestsToSources(\n nodes: readonly GraphNode[],\n coEditPairs: readonly CoEditPair[],\n options: LinkTestsOptions = {},\n): TestSourceLink[] {\n const minCount = options.minCoEditCount ?? DEFAULT_MIN_CO_EDIT_COUNT;\n const { testIds, nonTestIds } = splitFileIds(nodes);\n const links: TestSourceLink[] = [];\n for (const testId of testIds) {\n const matched = pathCandidates(testId).filter((c) => nonTestIds.has(c));\n if (matched.length > 0) {\n for (const sourceId of matched) {\n links.push({ testId, sourceId, method: \"path\" });\n }\n continue;\n }\n const partner = couplingFor(coEditPairs, testId).find((p) => p.count >= minCount && nonTestIds.has(p.partner));\n if (partner) {\n links.push({ testId, sourceId: partner.partner, method: \"coedit\" });\n }\n }\n return links;\n}\n\n/**\n * Per-source coverage breadth: how many distinct test files link to each\n * covered source. Emitted only for sources with at least one linked test.\n */\nexport function testCoverageCountMetrics(links: readonly TestSourceLink[]): GraphMetric[] {\n const testsBySource = groupTestsBySource(links);\n const out: GraphMetric[] = [];\n for (const [sourceId, tests] of testsBySource) {\n out.push({\n nodeId: sourceId,\n name: \"linked_test_count\",\n value: tests.size,\n unit: \"count\",\n });\n }\n return out;\n}\n\n/** Map each covered source to the set of test files that link to it. */\nexport function groupTestsBySource(links: readonly TestSourceLink[]): Map<string, Set<string>> {\n const out = new Map<string, Set<string>>();\n for (const l of links) {\n let set = out.get(l.sourceId);\n if (!set) {\n set = new Set();\n out.set(l.sourceId, set);\n }\n set.add(l.testId);\n }\n return out;\n}\n","import type { ChurnWindow } from \"./history/index.js\";\nimport type { GraphMetric } from \"./types.js\";\n\nconst DAY_SECONDS = 86400;\n\n/** Metric-name suffix for a window: `30d`, `180d`, or `lifetime`. */\nexport function windowSuffix(window: ChurnWindow): string {\n return window === \"lifetime\" ? \"lifetime\" : `${window}d`;\n}\n\n/**\n * Age-discount metrics: `recency_{w}` = min(1, age/w) for each file that churned\n * in window `w`, plus one window-independent `file_age_days` per file. Multiplying\n * a hotspot score by recency stops a young file's burst of churn from reading as\n * decay. Recency is emitted (as 1) even when the age is unknown, so a rule that\n * needs every hotspot factor is never silently disabled; `file_age_days` only when known.\n */\nexport function computeRecencyWindows(\n firstSeen: ReadonlyMap<string, number>,\n churnedIdsByWindow: ReadonlyMap<ChurnWindow, ReadonlySet<string>>,\n nowEpoch: number,\n): GraphMetric[] {\n const out: GraphMetric[] = [];\n const ageEmitted = new Set<string>();\n for (const [windowDays, ids] of churnedIdsByWindow) {\n const suffix = windowSuffix(windowDays);\n for (const id of ids) {\n const seen = firstSeen.get(id);\n let recency = 1;\n if (seen !== undefined) {\n const ageDays = Math.max(0, (nowEpoch - seen) / DAY_SECONDS);\n // Lifetime has no window to age against, so its recency stays 1.\n if (windowDays !== \"lifetime\") recency = Math.min(1, ageDays / windowDays);\n if (!ageEmitted.has(id)) {\n out.push({ nodeId: id, name: \"file_age_days\", value: Math.round(ageDays), unit: \"days\" });\n ageEmitted.add(id);\n }\n }\n out.push({ nodeId: id, name: `recency_${suffix}`, value: round3(recency), unit: \"ratio\" });\n }\n }\n return out;\n}\n\nexport function round3(n: number): number {\n return Math.round(n * 1000) / 1000;\n}\n","import type { NodeRole } from \"./types.js\";\n\n/**\n * Roles of files nothing imports by design, so reachability and dead-export\n * reports treat them as roots. One list, typed by role, so a new role cannot be\n * added to one report and missed in another. It lives apart from `roles.ts`,\n * which reads files, so browser-safe report code can import it.\n */\nexport const UNIMPORTED_ROLES: ReadonlySet<NodeRole> = new Set<NodeRole>([\n \"test\",\n \"fixture\",\n \"story\",\n \"lab\",\n \"config\",\n \"script\",\n \"entry\",\n]);\n","import type { EdgeKind, GraphEdge, GraphNode } from \"../types.js\";\n\nexport interface PageRankOptions {\n /** Per-node teleport weight. Unset or empty → uniform teleport across all nodes. */\n personalization?: ReadonlyMap<string, number>;\n /** Probability of following an edge vs teleporting (default 0.85). */\n damping?: number;\n /** L1 convergence threshold (default 1e-6). */\n tolerance?: number;\n /** Cap on power-iteration steps (default 100). */\n maxIterations?: number;\n /** Per-kind edge weight; missing kinds default to 1.0. */\n edgeWeights?: Partial<Record<EdgeKind, number>>;\n}\n\nexport interface PageRankRow {\n nodeId: string;\n score: number;\n}\n\nexport interface PageRankResult {\n /** Sorted descending by score; ties broken by node id ascending. */\n rows: PageRankRow[];\n iterations: number;\n converged: boolean;\n}\n\nconst DEFAULT_DAMPING = 0.85;\nconst DEFAULT_TOLERANCE = 1e-6;\nconst DEFAULT_MAX_ITERS = 100;\n\nconst DEFAULT_EDGE_WEIGHTS: Record<EdgeKind, number> = {\n imports: 1.0,\n \"re-exports\": 0.5,\n calls: 1.5,\n extends: 1.0,\n implements: 1.0,\n references: 1.0,\n \"depends-on\": 1.0,\n};\n\n/** Edge weight used by PageRank for a given edge kind, honoring user overrides. */\nexport function getEdgeWeight(\n kind: EdgeKind,\n overrides?: Partial<Record<EdgeKind, number>>,\n): number {\n return overrides?.[kind] ?? DEFAULT_EDGE_WEIGHTS[kind] ?? 1.0;\n}\n\ninterface Adjacency {\n outNeighbors: Array<Array<{ to: number; w: number }>>;\n outWeightSum: number[];\n}\n\nexport function computePageRank(\n nodes: readonly GraphNode[],\n edges: readonly GraphEdge[],\n options: PageRankOptions = {},\n): PageRankResult {\n const n = nodes.length;\n if (n === 0) return { rows: [], iterations: 0, converged: true };\n\n const damping = options.damping ?? DEFAULT_DAMPING;\n const tolerance = options.tolerance ?? DEFAULT_TOLERANCE;\n const maxIters = options.maxIterations ?? DEFAULT_MAX_ITERS;\n const weights = { ...DEFAULT_EDGE_WEIGHTS, ...(options.edgeWeights ?? {}) };\n\n const idx = buildIdIndex(nodes);\n const adj = buildAdjacency(idx, edges, weights, n);\n const pers = buildPersonalization(idx, n, options.personalization);\n\n const { rank, iterations, converged } = powerIterate(\n pers,\n adj,\n damping,\n tolerance,\n maxIters,\n );\n\n return { rows: rankToRows(nodes, rank), iterations, converged };\n}\n\nfunction buildIdIndex(nodes: readonly GraphNode[]): Map<string, number> {\n const idx = new Map<string, number>();\n for (let i = 0; i < nodes.length; i++) idx.set(nodes[i]!.id, i);\n return idx;\n}\n\nfunction buildAdjacency(\n idx: ReadonlyMap<string, number>,\n edges: readonly GraphEdge[],\n weights: Record<EdgeKind, number>,\n n: number,\n): Adjacency {\n const outNeighbors: Array<Array<{ to: number; w: number }>> = Array.from(\n { length: n },\n () => [],\n );\n const outWeightSum = new Array<number>(n).fill(0);\n for (const e of edges) {\n const si = idx.get(e.srcId);\n const di = idx.get(e.dstId);\n if (si === undefined || di === undefined) continue;\n const w = weights[e.kind] ?? 1.0;\n if (w <= 0) continue;\n outNeighbors[si]!.push({ to: di, w });\n outWeightSum[si]! += w;\n }\n return { outNeighbors, outWeightSum };\n}\n\nfunction powerIterate(\n pers: readonly number[],\n adj: Adjacency,\n damping: number,\n tolerance: number,\n maxIters: number,\n): { rank: number[]; iterations: number; converged: boolean } {\n const n = pers.length;\n let rank = pers.slice();\n let next = new Array<number>(n).fill(0);\n let iterations = 0;\n let converged = false;\n for (let iter = 0; iter < maxIters; iter++) {\n iterations = iter + 1;\n const dangling = sumDangling(rank, adj.outWeightSum);\n const teleport = 1 - damping + damping * dangling;\n seedTeleport(next, pers, teleport);\n distributeRank(next, rank, adj, damping);\n const diff = l1Diff(rank, next);\n [rank, next] = [next, rank];\n next.fill(0);\n if (diff < tolerance) {\n converged = true;\n break;\n }\n }\n return { rank, iterations, converged };\n}\n\nfunction sumDangling(\n rank: readonly number[],\n outWeightSum: readonly number[],\n): number {\n let total = 0;\n for (let i = 0; i < rank.length; i++) {\n if (outWeightSum[i] === 0) total += rank[i]!;\n }\n return total;\n}\n\nfunction seedTeleport(\n next: number[],\n pers: readonly number[],\n teleport: number,\n): void {\n for (let i = 0; i < next.length; i++) next[i] = teleport * pers[i]!;\n}\n\nfunction distributeRank(\n next: number[],\n rank: readonly number[],\n adj: Adjacency,\n damping: number,\n): void {\n for (let i = 0; i < rank.length; i++) {\n const s = adj.outWeightSum[i]!;\n if (s === 0 || rank[i]! === 0) continue;\n const factor = (damping * rank[i]!) / s;\n const list = adj.outNeighbors[i]!;\n for (let k = 0; k < list.length; k++) {\n next[list[k]!.to]! += factor * list[k]!.w;\n }\n }\n}\n\nfunction l1Diff(a: readonly number[], b: readonly number[]): number {\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff += Math.abs(b[i]! - a[i]!);\n return diff;\n}\n\nfunction rankToRows(\n nodes: readonly GraphNode[],\n rank: readonly number[],\n): PageRankRow[] {\n return nodes\n .map((node, i) => ({ nodeId: node.id, score: rank[i]! }))\n .sort(compareRows);\n}\n\nfunction compareRows(a: PageRankRow, b: PageRankRow): number {\n if (b.score !== a.score) return b.score - a.score;\n return a.nodeId < b.nodeId ? -1 : a.nodeId > b.nodeId ? 1 : 0;\n}\n\nfunction buildPersonalization(\n idx: ReadonlyMap<string, number>,\n n: number,\n pers: PageRankOptions[\"personalization\"],\n): number[] {\n if (!pers || pers.size === 0) return uniformVector(n);\n const out = new Array<number>(n).fill(0);\n const total = applySeedWeights(out, idx, pers);\n if (total === 0) return uniformVector(n);\n for (let i = 0; i < n; i++) out[i]! /= total;\n return out;\n}\n\nfunction uniformVector(n: number): number[] {\n return new Array<number>(n).fill(1 / n);\n}\n\nfunction applySeedWeights(\n out: number[],\n idx: ReadonlyMap<string, number>,\n pers: NonNullable<PageRankOptions[\"personalization\"]>,\n): number {\n let total = 0;\n for (const [id, w] of pers) {\n if (!isValidWeight(w)) continue;\n const i = idx.get(id);\n if (i === undefined) continue;\n out[i] = w;\n total += w;\n }\n return total;\n}\n\nfunction isValidWeight(w: number): boolean {\n return Number.isFinite(w) && w > 0;\n}\n","import type { GraphReportResult } from \"./graph-report-types.js\";\n\n/**\n * Per-file structural metrics for the dashboard Dossier heat readout. Extracted\n * from dashboard-payload so that file stays under the max-file-loc budget.\n */\nexport interface NodeMetrics {\n loc?: number;\n cognitiveMax?: number;\n cyclomaticMax?: number;\n maxNesting?: number;\n fanIn?: number;\n fanOut?: number;\n utilization?: number;\n /** Distinct test files linking to this source (C-4); shown in the Dossier (C-59). */\n linkedTests?: number;\n /** Node role (e.g. \"barrel\") — lets the Dossier explain barrel-resolved utilization. */\n role?: string;\n}\n\n/** The numeric NodeMetrics fields fed from metric rows (excludes `role`). */\ntype NumericMetricField = Exclude<keyof NodeMetrics, \"role\">;\n\n/** Metric name → NodeMetrics field, for the structural metrics the Dossier heats. */\nconst METRIC_FIELD: Record<string, NumericMetricField> = {\n loc: \"loc\",\n cognitive_max: \"cognitiveMax\",\n cyclomatic_max: \"cyclomaticMax\",\n max_nesting_depth: \"maxNesting\",\n fan_in: \"fanIn\",\n fan_out: \"fanOut\",\n utilization: \"utilization\",\n linked_test_count: \"linkedTests\",\n // Per-symbol complexity (C-58) lands on `symbol` nodes; map onto the same\n // fields so a symbol node reads its OWN complexity (file nodes never carry the\n // `symbol_*` names, so there's no collision on a shared field).\n symbol_cognitive: \"cognitiveMax\",\n symbol_cyclomatic: \"cyclomaticMax\",\n};\n\n/** Fold the flat metric rows into a per-node structural-metrics map. */\nexport function collectNodeMetrics(\n rows: { nodeId: string; name: string; value: number | null }[],\n): Map<string, NodeMetrics> {\n const byNode = new Map<string, NodeMetrics>();\n for (const m of rows) {\n const field = METRIC_FIELD[m.name];\n if (!field || m.value === null) continue;\n const entry = byNode.get(m.nodeId) ?? {};\n entry[field] = m.value;\n byNode.set(m.nodeId, entry);\n }\n return byNode;\n}\n\n/** Every file the Dossier can open on (referenced in hotspots / silos / coupling / coverage / central / drift). */\nexport function referencedNodes(\n report: GraphReportResult,\n): Set<string> {\n const referenced = new Set<string>();\n for (const h of report.hotspots) referenced.add(h.nodeId);\n for (const b of report.busFactorRisks) referenced.add(b.nodeId);\n for (const t of report.testCoverageRisks) referenced.add(t.nodeId);\n for (const c of report.couplingClusters) { referenced.add(c.fileA); referenced.add(c.fileB); }\n for (const c of report.centralFiles) referenced.add(c.nodeId);\n const drift = report.drift;\n if (drift) {\n for (const h of drift.newHotspots) referenced.add(h.nodeId);\n for (const d of drift.worsenedHotspots) referenced.add(d.nodeId);\n for (const c of drift.newCoupling) { referenced.add(c.fileA); referenced.add(c.fileB); }\n }\n return referenced;\n}\n\n/**\n * Structural metrics for every file the Dossier can open on. Scoped to\n * referenced nodes rather than the whole graph to keep the payload tight — the\n * Dossier never opens on an unreferenced file.\n */\nexport function buildNodeMetrics(\n report: GraphReportResult,\n metrics: ReadonlyMap<string, NodeMetrics>,\n): Record<string, NodeMetrics> {\n const out: Record<string, NodeMetrics> = {};\n for (const id of referencedNodes(report)) {\n const m = metrics.get(id);\n if (m) out[id] = m;\n }\n return out;\n}\n\n/**\n * Reading-order centrality (top-N central files) PLUS the centrality of every\n * node referenced elsewhere in the payload (hotspots, silos, coupling), so the\n * Dossier can always show a PageRank score instead of \"—\" for a hotspot that\n * falls outside the top-N central list. Sorted descending, so the Overview\n * \"reading order\" (top-6 slice) is unaffected.\n */\nexport function buildCentralFiles(\n report: GraphReportResult,\n centrality: ReadonlyMap<string, number>,\n): { nodeId: string; score: number }[] {\n const byId = new Map<string, number>();\n for (const c of report.centralFiles) byId.set(c.nodeId, c.score);\n const referenced = new Set<string>();\n for (const h of report.hotspots) referenced.add(h.nodeId);\n for (const b of report.busFactorRisks) referenced.add(b.nodeId);\n for (const c of report.couplingClusters) { referenced.add(c.fileA); referenced.add(c.fileB); }\n for (const id of referenced) if (!byId.has(id)) byId.set(id, centrality.get(id) ?? 0);\n return [...byId].map(([nodeId, score]) => ({ nodeId, score })).sort((a, b) => b.score - a.score);\n}\n\n/** One declared symbol's utilization (C-53), tagged with its declaring file. */\nexport interface SymbolUtil {\n symbolId: string;\n name: string;\n fileId: string;\n utilization: number;\n /** Whether the symbol is exported (model B, C-64); internal helpers are false. */\n exported: boolean;\n}\n\n/**\n * Pair each `symbol` node with its utilization metric and declaring file (C-53).\n * Model B (C-64) adds non-exported function/class nodes; the `exported` attr\n * (defaulting true for pre-C-64 nodes that lack it) lets the Dossier separate a\n * file's public surface from its internal helpers.\n */\nexport function collectSymbolUtil(\n nodes: readonly {\n id: string;\n kind: string;\n name: string;\n parentId?: string;\n attrs?: Record<string, unknown>;\n }[],\n metrics: ReadonlyMap<string, NodeMetrics>,\n): SymbolUtil[] {\n const out: SymbolUtil[] = [];\n for (const n of nodes) {\n if (n.kind !== \"symbol\" || !n.parentId) continue;\n out.push({\n symbolId: n.id,\n name: n.name,\n fileId: n.parentId,\n utilization: metrics.get(n.id)?.utilization ?? 0,\n exported: n.attrs?.exported !== false,\n });\n }\n return out;\n}\n\nexport interface HotExport {\n name: string;\n utilization: number;\n /** The symbol's OWN cognitive complexity (C-58); undefined for a class/type/re-export. */\n cognitive?: number;\n /** Distinct files that reference this export (inbound `references` edges, C-59). */\n consumers: number;\n /** Exported (public surface) vs internal helper (model B, C-64). */\n exported: boolean;\n}\n\n/**\n * Per-file symbol detail for the Dossier (C-59, C-64): each of a file's declared\n * symbols with its per-symbol complexity (C-58), utilization (C-53), and\n * consumer count (inbound references). Keeps zero-utilization symbols — a complex\n * but unused symbol is worth seeing. Model B (C-64) adds internal helpers, so the\n * two surfaces are ranked and capped *separately* (the Dossier renders them in\n * their own sections): exports by utilization-then-complexity, internal functions\n * by complexity — otherwise a large public surface would slice every internal\n * helper out before it could show. Scoped to Dossier-openable files; top 8 each.\n */\nexport function buildHotExports(\n symbols: readonly SymbolUtil[],\n referenced: ReadonlySet<string>,\n metrics: ReadonlyMap<string, NodeMetrics>,\n consumersBySymbol: ReadonlyMap<string, number>,\n): Record<string, HotExport[]> {\n const byFile = new Map<string, HotExport[]>();\n for (const s of symbols) {\n if (!referenced.has(s.fileId)) continue;\n const bucket = byFile.get(s.fileId) ?? [];\n bucket.push({\n name: s.name,\n utilization: s.utilization,\n cognitive: metrics.get(s.symbolId)?.cognitiveMax,\n consumers: consumersBySymbol.get(s.symbolId) ?? 0,\n exported: s.exported,\n });\n byFile.set(s.fileId, bucket);\n }\n const out: Record<string, HotExport[]> = {};\n for (const [fileId, syms] of byFile) {\n const exports = syms\n .filter((s) => s.exported)\n .sort((a, b) => b.utilization - a.utilization || (b.cognitive ?? 0) - (a.cognitive ?? 0))\n .slice(0, 8);\n const internal = syms\n .filter((s) => !s.exported)\n .sort((a, b) => (b.cognitive ?? 0) - (a.cognitive ?? 0) || a.name.localeCompare(b.name))\n .slice(0, 8);\n out[fileId] = [...exports, ...internal];\n }\n return out;\n}\n\nexport interface BlastRadiusEntry {\n symbolId: string;\n name: string;\n fileId: string;\n utilization: number;\n complexity: number;\n churn: number;\n score: number;\n}\n\n/**\n * Rank exports by blast radius = utilization × cognitive complexity × file\n * churn (C-53, \"idea d\"). Surfaces the single riskiest thing to touch: a\n * heavily-used export that is both hard to reason about and actively changing.\n * Complexity is the export's OWN cognitive complexity (C-58, per-symbol) when\n * known, falling back to the file's max for exports without a computed symbol\n * complexity (e.g. class or re-exported symbols). This is what lets two exports\n * of one hot file separate instead of tying on the file-broadcast value.\n * Stable files (churn 0) score 0 and drop out — a load-bearing export in a calm\n * file isn't a change hazard.\n */\nexport function buildBlastRadius(\n symbols: readonly SymbolUtil[],\n metrics: ReadonlyMap<string, NodeMetrics>,\n churnByFile: ReadonlyMap<string, number>,\n limit = 15,\n): BlastRadiusEntry[] {\n const out: BlastRadiusEntry[] = [];\n for (const s of symbols) {\n const complexity =\n metrics.get(s.symbolId)?.cognitiveMax ??\n metrics.get(s.fileId)?.cognitiveMax ??\n 0;\n const churn = churnByFile.get(s.fileId) ?? 0;\n const score = s.utilization * complexity * churn;\n if (score <= 0) continue;\n out.push({ symbolId: s.symbolId, name: s.name, fileId: s.fileId, utilization: s.utilization, complexity, churn, score });\n }\n return out.sort((a, b) => b.score - a.score).slice(0, limit);\n}\n","import { parseSymbolId } from \"../extractors/symbol-id.js\";\n\n/**\n * Symbol-level change coupling (C-60). Decomposes a god-file the file-level\n * coupling view rolls up as one blob (e.g. `types.ts`) into *which symbol* is\n * used where. Built on the C-53 `references` edge substrate: `src` is the\n * importing file, `dst` is the imported symbol node id (`<fileId>#<name>`).\n *\n * Two slices, both pure functions of the assembled reference edge set (a\n * whole-graph rollup like utilization/PageRank, sound under incremental reuse\n * since it reads the reassembled edges, not a per-file cache):\n *\n * - **Slice C — per-symbol consumers**: invert the edges by `dst`, giving the\n * set of files that import each symbol. Directly answers \"what IN this file is\n * used where\"; covers span-less types/consts that a git-hunk approach cannot.\n * - **Slice B — co-import coupling**: group edges by `src`, and every pair of\n * symbols co-imported by the same file is a coupling pair. Two symbols that\n * are always imported together travel together — structural (used-together),\n * drift-free coupling, as opposed to the temporal (changed-together) git\n * co-edit signal.\n */\n\n/** The minimal shape of a `references` edge this module consumes. */\nexport interface ReferenceEdgeLite {\n /** Importing file id. */\n srcId: string;\n /** Imported symbol node id (`<fileId>#<name>`). */\n dstId: string;\n}\n\n/** One symbol's consumer set (Slice C). */\nexport interface SymbolConsumers {\n symbolId: string;\n /** Declaring file, parsed from the symbol id. */\n fileId: string;\n /** Export name. */\n name: string;\n /** Distinct importing file ids, sorted. */\n consumers: string[];\n}\n\n/** A pair of symbols co-imported by the same file (Slice B). */\nexport interface SymbolCouplingPair {\n aId: string;\n aFile: string;\n aName: string;\n bId: string;\n bFile: string;\n bName: string;\n /** Distinct files that import both symbols. */\n coImports: number;\n /** True when the two symbols are declared in different files. */\n crossFile: boolean;\n}\n\nexport interface SymbolCouplingOptions {\n /** Skip pairs co-imported by fewer than this many files. Default 2. */\n minCoImports?: number;\n /**\n * Skip importing files that reference more than this many distinct symbols —\n * a wide barrel-style importer would otherwise explode into O(n²) noise\n * pairs, mirroring change-coupling's large-commit guard. Default 40.\n */\n largeImporterThreshold?: number;\n}\n\nconst DEFAULT_MIN_CO_IMPORTS = 2;\nconst DEFAULT_LARGE_IMPORTER_THRESHOLD = 40;\n\n/**\n * Group reference edges by imported symbol, yielding each symbol's distinct\n * consuming files (Slice C). Sorted by consumer count desc, then symbol id, so\n * the most broadly-depended-on exports lead.\n */\nexport function computeSymbolConsumers(\n edges: readonly ReferenceEdgeLite[],\n): SymbolConsumers[] {\n const bySymbol = new Map<string, Set<string>>();\n for (const e of edges) {\n let set = bySymbol.get(e.dstId);\n if (!set) {\n set = new Set();\n bySymbol.set(e.dstId, set);\n }\n set.add(e.srcId);\n }\n const out: SymbolConsumers[] = [];\n for (const [id, consumers] of bySymbol) {\n const parsed = parseSymbolId(id);\n if (!parsed) continue;\n out.push({\n symbolId: id,\n fileId: parsed.fileId,\n name: parsed.name,\n consumers: [...consumers].sort(),\n });\n }\n return out.sort(compareConsumers);\n}\n\nfunction compareConsumers(a: SymbolConsumers, b: SymbolConsumers): number {\n if (b.consumers.length !== a.consumers.length) {\n return b.consumers.length - a.consumers.length;\n }\n return a.symbolId < b.symbolId ? -1 : a.symbolId > b.symbolId ? 1 : 0;\n}\n\n/**\n * Every pair of symbols co-imported by the same file becomes a coupling pair,\n * counted by how many distinct files co-import them (Slice B). Wide importers\n * are dropped to keep the pairing near-linear. Sorted by co-import count desc,\n * cross-file pairs preferred at a tie (they are the actionable ones — a\n * same-file pair is just cohesion within one module).\n */\nexport function computeSymbolCoupling(\n edges: readonly ReferenceEdgeLite[],\n options: SymbolCouplingOptions = {},\n): SymbolCouplingPair[] {\n const minCoImports = options.minCoImports ?? DEFAULT_MIN_CO_IMPORTS;\n const largeThreshold =\n options.largeImporterThreshold ?? DEFAULT_LARGE_IMPORTER_THRESHOLD;\n const bySource = groupSymbolsBySource(edges);\n const counts = new Map<string, number>();\n for (const symbols of bySource.values()) {\n if (symbols.length < 2 || symbols.length > largeThreshold) continue;\n accumulateSymbolPairs(symbols, counts);\n }\n return finalizePairs(counts, minCoImports);\n}\n\nfunction groupSymbolsBySource(\n edges: readonly ReferenceEdgeLite[],\n): Map<string, string[]> {\n const perSource = new Map<string, Set<string>>();\n for (const e of edges) {\n if (!parseSymbolId(e.dstId)) continue;\n let set = perSource.get(e.srcId);\n if (!set) {\n set = new Set();\n perSource.set(e.srcId, set);\n }\n set.add(e.dstId);\n }\n const out = new Map<string, string[]>();\n for (const [src, set] of perSource) out.set(src, [...set].sort());\n return out;\n}\n\nfunction accumulateSymbolPairs(\n symbols: readonly string[],\n counts: Map<string, number>,\n): void {\n for (let i = 0; i < symbols.length; i++) {\n for (let j = i + 1; j < symbols.length; j++) {\n const key = `${symbols[i]!}\\t${symbols[j]!}`;\n counts.set(key, (counts.get(key) ?? 0) + 1);\n }\n }\n}\n\nfunction finalizePairs(\n counts: ReadonlyMap<string, number>,\n minCoImports: number,\n): SymbolCouplingPair[] {\n const out: SymbolCouplingPair[] = [];\n for (const [key, coImports] of counts) {\n if (coImports < minCoImports) continue;\n const [aId, bId] = key.split(\"\\t\") as [string, string];\n const a = parseSymbolId(aId)!;\n const b = parseSymbolId(bId)!;\n out.push({\n aId,\n aFile: a.fileId,\n aName: a.name,\n bId,\n bFile: b.fileId,\n bName: b.name,\n coImports,\n crossFile: a.fileId !== b.fileId,\n });\n }\n return out.sort(comparePairs);\n}\n\nfunction comparePairs(a: SymbolCouplingPair, b: SymbolCouplingPair): number {\n if (b.coImports !== a.coImports) return b.coImports - a.coImports;\n if (a.crossFile !== b.crossFile) return a.crossFile ? -1 : 1;\n if (a.aId !== b.aId) return a.aId < b.aId ? -1 : 1;\n return a.bId < b.bId ? -1 : a.bId > b.bId ? 1 : 0;\n}\n","import type { GraphEdge, GraphNode } from \"../types.js\";\n\n/**\n * Per-package and per-pair structural quality metrics, plus an overall\n * Newman-Girvan modularity Q for the package partition.\n *\n * Operates on a barrel-resolved edge set by default: edges that land on a\n * file with role=\"barrel\" are rewritten to land on the underlying files\n * the barrel re-exports from (transitively). The intent is to measure the\n * real dependency surface, not the re-export plumbing.\n *\n * See C-8 task notes for the design rationale and empirical calibration\n * data from the 2026-05-21 codewatch dogfood.\n */\n\nexport interface PartitionQualityInput {\n /** Logical packages — at minimum needs an id. */\n packages: ReadonlyArray<{ id: string }>;\n /** Package id → list of file ids assigned to that package. */\n fileByPackage: ReadonlyMap<string, ReadonlyArray<string>>;\n /** All file/module/external nodes for the snapshot. Used to identify role=\"barrel\". */\n nodes: readonly GraphNode[];\n /** All edges for the snapshot. */\n edges: readonly GraphEdge[];\n /**\n * When true, edges landing on a barrel file are resolved through its\n * re-export chain to the underlying source files. The cheap implementation\n * fans each barrel import into N synthetic edges (one per re-export\n * target), which over-attributes — a single `import { x } from \"./pkg\"`\n * becomes N edges as if every re-export were used. Default false until\n * a weighted or symbol-tracking version is available.\n */\n resolveBarrels?: boolean;\n}\n\nexport type PackageFlag = \"weak-boundary\";\nexport type PairFlag = \"tight\" | \"moderate\" | \"none\";\nexport type PackageLayer = \"top\" | \"middle\" | \"foundation\";\n\nexport interface PackageStats {\n pkgId: string;\n fileCount: number;\n internalEdges: number;\n outgoingEdges: number;\n incomingEdges: number;\n /** internal / (internal + outgoing) — higher = more self-contained. */\n cohesion: number;\n /** outgoing / (outgoing + incoming) — Martin's I metric at the package level. */\n instability: number;\n /**\n * Abstractness proxy A ∈ [0,1]: share of the package's files with role\n * \"types\" (dedicated type/interface definitions). codewatch has no\n * symbol-level abstract/concrete counts, so this file-role ratio stands in\n * for Martin's A. Enables the instability×abstractness main-sequence plot.\n */\n abstractness: number;\n layer: PackageLayer;\n flags: PackageFlag[];\n}\n\nexport interface PairCoupling {\n from: string;\n to: string;\n edges: number;\n /** edges / files(from) — fraction of from-side files contributing dependencies into `to`. */\n intensity: number;\n flag: PairFlag;\n}\n\nexport interface PartitionQualityResult {\n modularityQ: number;\n totalEdges: number;\n perPackage: PackageStats[];\n pairCoupling: PairCoupling[];\n /** Total raised flags across packages + pairs (excluding \"moderate\"). */\n flagsCount: number;\n}\n\n// Layer classification — calibrated 2026-05-21 against codewatch's\n// own package shape (cli=1.00 top, analyzer/checker/render=0.70-0.80\n// middle, graph=0.20 foundation-leaning, core/profile=0.00 pure\n// foundation). The earlier 0.7 top threshold mis-labeled middle layers.\nconst FOUNDATION_INSTABILITY_MAX = 0.3;\nconst TOP_INSTABILITY_MIN = 0.9;\nconst WEAK_COHESION_MAX = 0.5;\nconst TIGHT_INTENSITY_MIN = 0.6;\nconst MODERATE_INTENSITY_MIN = 0.3;\n\nexport function computePartitionQuality(\n input: PartitionQualityInput,\n): PartitionQualityResult {\n const resolveBarrels = input.resolveBarrels ?? false;\n const pkgByFile = invertBuckets(input.fileByPackage);\n const barrelTargets = resolveBarrels\n ? buildBarrelTargetMap(input.nodes, input.edges)\n : new Map<string, ReadonlyArray<string>>();\n const resolvedPairs = buildResolvedPairs(\n input.edges,\n pkgByFile,\n barrelTargets,\n resolveBarrels,\n );\n const counts = countByPackage(resolvedPairs);\n const totalEdges = resolvedPairs.length;\n const filesByPkg = filesPerPackage(input.fileByPackage);\n\n const abstractByPkg = countAbstractFiles(input.nodes, pkgByFile);\n const perPackage = input.packages.map((p) =>\n buildPackageStats(p.id, counts, filesByPkg, abstractByPkg),\n );\n const pairCoupling = buildPairCoupling(resolvedPairs, filesByPkg);\n const modularityQ = computeModularity(perPackage, totalEdges);\n const flagsCount =\n perPackage.reduce((acc, p) => acc + p.flags.length, 0) +\n pairCoupling.filter((c) => c.flag === \"tight\").length;\n\n return { modularityQ, totalEdges, perPackage, pairCoupling, flagsCount };\n}\n\n/**\n * Invert a package→files bucket map into a file→package lookup, skipping the\n * empty-string \"unassigned\" bucket. Shared by partition-quality and the CLI's\n * arch/wiki package rollups, which all need the same file→package direction.\n */\nexport function invertBuckets(\n fileByPackage: ReadonlyMap<string, ReadonlyArray<string>>,\n): Map<string, string> {\n const out = new Map<string, string>();\n for (const [pkgId, files] of fileByPackage) {\n if (pkgId === \"\") continue;\n for (const f of files) out.set(f, pkgId);\n }\n return out;\n}\n\nfunction filesPerPackage(\n fileByPackage: ReadonlyMap<string, ReadonlyArray<string>>,\n): Map<string, number> {\n const out = new Map<string, number>();\n for (const [pkgId, files] of fileByPackage) {\n out.set(pkgId, files.length);\n }\n return out;\n}\n\n/**\n * For each barrel file, compute its transitive non-barrel re-export targets.\n * Map omits barrels that re-export nothing reachable (or that aren't barrels).\n */\nfunction buildBarrelTargetMap(\n nodes: readonly GraphNode[],\n edges: readonly GraphEdge[],\n): Map<string, ReadonlyArray<string>> {\n const barrelIds = new Set(\n nodes.filter((n) => n.role === \"barrel\").map((n) => n.id),\n );\n if (barrelIds.size === 0) return new Map();\n const directReExports = new Map<string, string[]>();\n for (const e of edges) {\n if (!barrelIds.has(e.srcId)) continue;\n if (e.kind !== \"re-exports\") continue;\n let list = directReExports.get(e.srcId);\n if (!list) {\n list = [];\n directReExports.set(e.srcId, list);\n }\n list.push(e.dstId);\n }\n\n const resolved = new Map<string, string[]>();\n const resolve = (barrel: string, visiting: Set<string>): string[] => {\n if (resolved.has(barrel)) return resolved.get(barrel)!;\n if (visiting.has(barrel)) return []; // cycle guard\n visiting.add(barrel);\n const out: string[] = [];\n for (const target of directReExports.get(barrel) ?? []) {\n if (barrelIds.has(target)) {\n for (const inner of resolve(target, visiting)) out.push(inner);\n } else {\n out.push(target);\n }\n }\n visiting.delete(barrel);\n resolved.set(barrel, out);\n return out;\n };\n for (const id of barrelIds) resolve(id, new Set());\n return resolved;\n}\n\n/**\n * Project edges into (fromPkg, toPkg) pairs, resolving through barrels and\n * dropping any edge whose source file isn't in a tracked package. Re-export\n * edges themselves are dropped — they're the resolution mechanism, not\n * substantive dependencies.\n */\nfunction buildResolvedPairs(\n edges: readonly GraphEdge[],\n pkgByFile: ReadonlyMap<string, string>,\n barrelTargets: ReadonlyMap<string, ReadonlyArray<string>>,\n resolveBarrels: boolean,\n): Array<{ from: string; to: string }> {\n const out: Array<{ from: string; to: string }> = [];\n for (const e of edges) {\n // When resolving barrels, the re-export edges become the resolution\n // mechanism (followed implicitly via barrelTargets), so we drop them\n // from the analysis edge set. When NOT resolving, they're substantive\n // dependencies on their own.\n if (resolveBarrels && e.kind === \"re-exports\") continue;\n const fromPkg = pkgByFile.get(e.srcId);\n if (!fromPkg) continue;\n const targets = barrelTargets.get(e.dstId) ?? [e.dstId];\n for (const dst of targets) {\n const toPkg = pkgByFile.get(dst);\n if (toPkg) out.push({ from: fromPkg, to: toPkg });\n }\n }\n return out;\n}\n\ninterface PackageCounts {\n internal: number;\n outgoing: number;\n incoming: number;\n}\n\nfunction countByPackage(\n pairs: ReadonlyArray<{ from: string; to: string }>,\n): Map<string, PackageCounts> {\n const out = new Map<string, PackageCounts>();\n const get = (id: string): PackageCounts => {\n let c = out.get(id);\n if (!c) {\n c = { internal: 0, outgoing: 0, incoming: 0 };\n out.set(id, c);\n }\n return c;\n };\n for (const p of pairs) {\n if (p.from === p.to) {\n get(p.from).internal += 1;\n } else {\n get(p.from).outgoing += 1;\n get(p.to).incoming += 1;\n }\n }\n return out;\n}\n\nfunction buildPackageStats(\n pkgId: string,\n counts: ReadonlyMap<string, PackageCounts>,\n filesByPkg: ReadonlyMap<string, number>,\n abstractByPkg: ReadonlyMap<string, number>,\n): PackageStats {\n const c = counts.get(pkgId) ?? { internal: 0, outgoing: 0, incoming: 0 };\n const cohesion = ratioOrZero(c.internal, c.internal + c.outgoing);\n const instability = ratioOrZero(c.outgoing, c.outgoing + c.incoming);\n const fileCount = filesByPkg.get(pkgId) ?? 0;\n const abstractness = ratioOrZero(abstractByPkg.get(pkgId) ?? 0, fileCount);\n const layer = classifyLayer(instability);\n const flags: PackageFlag[] = [];\n if (layer !== \"top\" && cohesion < WEAK_COHESION_MAX && c.internal + c.outgoing > 0) {\n flags.push(\"weak-boundary\");\n }\n return {\n pkgId,\n fileCount,\n internalEdges: c.internal,\n outgoingEdges: c.outgoing,\n incomingEdges: c.incoming,\n cohesion,\n instability,\n abstractness,\n layer,\n flags,\n };\n}\n\n/** Count files with role \"types\" per package — the abstractness numerator. */\nfunction countAbstractFiles(\n nodes: readonly GraphNode[],\n pkgByFile: ReadonlyMap<string, string>,\n): Map<string, number> {\n const out = new Map<string, number>();\n for (const n of nodes) {\n if (n.kind !== \"file\" || n.role !== \"types\") continue;\n const pkg = pkgByFile.get(n.id);\n if (pkg) out.set(pkg, (out.get(pkg) ?? 0) + 1);\n }\n return out;\n}\n\nfunction ratioOrZero(num: number, denom: number): number {\n return denom === 0 ? 0 : num / denom;\n}\n\nfunction classifyLayer(instability: number): PackageLayer {\n if (instability <= FOUNDATION_INSTABILITY_MAX) return \"foundation\";\n if (instability >= TOP_INSTABILITY_MIN) return \"top\";\n return \"middle\";\n}\n\nfunction buildPairCoupling(\n pairs: ReadonlyArray<{ from: string; to: string }>,\n filesByPkg: ReadonlyMap<string, number>,\n): PairCoupling[] {\n const counts = new Map<string, { from: string; to: string; edges: number }>();\n for (const p of pairs) {\n if (p.from === p.to) continue;\n // Collision-safe key: a JSON tuple, not a raw separator that a real\n // package id could contain (a raw NUL also made git treat this file\n // as binary and hid its diffs).\n const key = JSON.stringify([p.from, p.to]);\n const entry = counts.get(key);\n if (entry) entry.edges += 1;\n else counts.set(key, { from: p.from, to: p.to, edges: 1 });\n }\n const out: PairCoupling[] = [];\n for (const { from, to, edges } of counts.values()) {\n const filesFrom = filesByPkg.get(from) ?? 0;\n const intensity = filesFrom === 0 ? 0 : edges / filesFrom;\n const flag = classifyPairIntensity(intensity);\n out.push({ from, to, edges, intensity, flag });\n }\n return out.sort(comparePairs);\n}\n\nfunction classifyPairIntensity(intensity: number): PairFlag {\n if (intensity >= TIGHT_INTENSITY_MIN) return \"tight\";\n if (intensity >= MODERATE_INTENSITY_MIN) return \"moderate\";\n return \"none\";\n}\n\nfunction comparePairs(a: PairCoupling, b: PairCoupling): number {\n if (a.from !== b.from) return a.from < b.from ? -1 : 1;\n return a.to < b.to ? -1 : 1;\n}\n\n/**\n * Newman-Girvan modularity Q against the package partition (undirected\n * approximation: each directed edge contributes 1 to the source's degree\n * and 1 to the target's). For totalEdges=0 returns 0.\n */\nfunction computeModularity(\n perPackage: readonly PackageStats[],\n totalEdges: number,\n): number {\n if (totalEdges === 0) return 0;\n let Q = 0;\n for (const p of perPackage) {\n const e_cc = p.internalEdges / totalEdges;\n const touching = p.internalEdges * 2 + p.outgoingEdges + p.incomingEdges;\n const a_c = touching / (2 * totalEdges);\n Q += e_cc - a_c * a_c;\n }\n return Q;\n}\n","/** Glob when the pattern has a `*` (`**` crosses directories, `*` stays in a segment); otherwise a case-sensitive substring. */\nexport function patternToRegex(pattern: string): RegExp {\n if (!pattern.includes(\"*\")) {\n return new RegExp(escapeRegex(pattern));\n }\n let out = \"\";\n for (let i = 0; i < pattern.length; i++) {\n const c = pattern[i]!;\n if (c === \"*\" && pattern[i + 1] === \"*\") {\n out += \".*\";\n i++;\n } else if (c === \"*\") {\n out += \"[^/]*\";\n } else {\n out += escapeRegex(c);\n }\n }\n return new RegExp(`^${out}$`);\n}\n\nexport function compilePatterns(patterns: readonly string[] | undefined): RegExp[] {\n if (!patterns) return [];\n return patterns.map(patternToRegex);\n}\n\nexport function matchesAny(value: string, patterns: readonly RegExp[]): boolean {\n for (const rx of patterns) {\n if (rx.test(value)) return true;\n }\n return false;\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.+?(){}|^$\\\\[\\]]/g, \"\\\\$&\");\n}\n","import { matchesAny } from \"../check/patterns.js\";\nimport type { ChurnWindow } from \"../history/window.js\";\nimport { windowSuffix } from \"../history-recency.js\";\nimport type { GraphEdge, GraphMetric, GraphNode } from \"../types.js\";\nimport { computePageRank } from \"./pagerank.js\";\nimport type {\n BusFactorRow,\n CentralRow,\n HotspotRow,\n TestCoverageRow,\n} from \"./graph-report-types.js\";\n\nconst COMPLEXITY_METRICS = [\"cognitive_max\", \"cyclomatic_max\"] as const;\n\nexport interface ReportContext {\n nodes: readonly GraphNode[];\n nodeById: Map<string, GraphNode>;\n metricsByName: Map<string, Map<string, number>>;\n excluders: readonly RegExp[];\n excludedRoles: ReadonlySet<string>;\n windowDays: ChurnWindow;\n /** Metric-name suffix for {@link windowDays} (`30d` … or `lifetime`). */\n windowSuffix: string;\n}\n\nexport interface ReportContextInput {\n nodes: readonly GraphNode[];\n metrics: readonly GraphMetric[];\n excluders: readonly RegExp[];\n excludedRoles: ReadonlySet<string>;\n windowDays: ChurnWindow;\n}\n\nexport function buildReportContext(input: ReportContextInput): ReportContext {\n const metricsByName = new Map<string, Map<string, number>>();\n for (const m of input.metrics) {\n if (m.value === null) continue;\n let bucket = metricsByName.get(m.name);\n if (!bucket) {\n bucket = new Map();\n metricsByName.set(m.name, bucket);\n }\n bucket.set(m.nodeId, m.value);\n }\n return {\n nodes: input.nodes,\n nodeById: new Map(input.nodes.map((n) => [n.id, n])),\n metricsByName,\n excluders: input.excluders,\n excludedRoles: input.excludedRoles,\n windowDays: input.windowDays,\n windowSuffix: windowSuffix(input.windowDays),\n };\n}\n\nexport function keepNode(ctx: ReportContext, nodeId: string): boolean {\n if (matchesAny(nodeId, ctx.excluders)) return false;\n const node = ctx.nodeById.get(nodeId);\n if (!node || node.kind !== \"file\") return false;\n // Generated code (codegen output, C-73) is always excluded here — the shared\n // gate for hotspots, unused-exports, and the rest of the quality report — so a\n // churning `client.gen.ts` never crowds out the files a human actually owns.\n if (node.role === \"generated\") return false;\n if (node.role && ctx.excludedRoles.has(node.role)) return false;\n return true;\n}\n\nexport function lookupMetric(\n ctx: ReportContext,\n name: string,\n nodeId: string,\n): number | undefined {\n return ctx.metricsByName.get(name)?.get(nodeId);\n}\n\nfunction pickComplexityMetric(ctx: ReportContext): string {\n for (const m of COMPLEXITY_METRICS) {\n if (ctx.metricsByName.has(m)) return m;\n }\n return \"cyclomatic_max\";\n}\n\nexport function topHotspots(\n ctx: ReportContext,\n limit: number,\n): HotspotRow[] {\n const churnName = `churn_${ctx.windowSuffix}`;\n const complexityName = pickComplexityMetric(ctx);\n const rows: HotspotRow[] = [];\n for (const node of ctx.nodes) {\n if (!keepNode(ctx, node.id)) continue;\n const churn = lookupMetric(ctx, churnName, node.id) ?? 0;\n const complexity = lookupMetric(ctx, complexityName, node.id) ?? 0;\n if (churn === 0 || complexity === 0) continue;\n const recency = lookupMetric(ctx, `recency_${ctx.windowSuffix}`, node.id) ?? 1;\n rows.push({ nodeId: node.id, churn, complexity, recency, score: Math.round(churn * complexity * recency) });\n }\n rows.sort((a, b) => b.score - a.score);\n return rows.slice(0, limit);\n}\n\nexport function hotspotScoreOf(ctx: ReportContext, nodeId: string): number {\n if (!keepNode(ctx, nodeId)) return 0;\n const churn = lookupMetric(ctx, `churn_${ctx.windowSuffix}`, nodeId) ?? 0;\n const complexity = lookupMetric(ctx, pickComplexityMetric(ctx), nodeId) ?? 0;\n if (churn === 0 || complexity === 0) return 0;\n return hotspotScore(ctx, nodeId, churn, complexity);\n}\n\n/** The complexity factor a file's hotspot score multiplies, read even when the file has no churn; undefined when unmeasured. */\nexport function hotspotComplexityOf(ctx: ReportContext, nodeId: string): number | undefined {\n return lookupMetric(ctx, pickComplexityMetric(ctx), nodeId);\n}\n\n/**\n * churn × complexity, discounted by the file's recency so a freshly-authored\n * file's initial churn burst doesn't read as decay (see recency_{window}d). The\n * factor is 1 (no discount) for files older than the window or when git can't\n * supply an age. Rounded to keep scores integer-friendly for display/thresholds.\n */\nfunction hotspotScore(ctx: ReportContext, nodeId: string, churn: number, complexity: number): number {\n const recency = lookupMetric(ctx, `recency_${ctx.windowSuffix}`, nodeId) ?? 1;\n return Math.round(churn * complexity * recency);\n}\n\nexport function busFactorOf(\n ctx: ReportContext,\n nodeId: string,\n): number | undefined {\n return lookupMetric(ctx, `bus_factor_${ctx.windowSuffix}`, nodeId);\n}\n\nexport function topBusFactorRisks(\n ctx: ReportContext,\n limit: number,\n): BusFactorRow[] {\n const churnName = `churn_${ctx.windowSuffix}`;\n const bfName = `bus_factor_${ctx.windowSuffix}`;\n const shareName = `top_author_share_${ctx.windowSuffix}`;\n const rows: BusFactorRow[] = [];\n for (const node of ctx.nodes) {\n if (!keepNode(ctx, node.id)) continue;\n const bf = lookupMetric(ctx, bfName, node.id);\n if (bf === undefined || bf > 1) continue;\n rows.push({\n nodeId: node.id,\n busFactor: bf,\n topAuthorShare: lookupMetric(ctx, shareName, node.id) ?? 1,\n churn: lookupMetric(ctx, churnName, node.id) ?? 0,\n });\n }\n rows.sort((a, b) => b.churn - a.churn);\n return rows.slice(0, limit);\n}\n\n/**\n * Sources whose *test coverage* is a single-author silo (test bus factor = 1)\n * — the honest, role-split view: production code can be well-spread while the\n * tests that guard it are owned by one person (or vice versa).\n */\nexport function topTestCoverageRisks(\n ctx: ReportContext,\n limit: number,\n): TestCoverageRow[] {\n const bfName = `test_bus_factor_${ctx.windowSuffix}`;\n const shareName = `test_top_author_share_${ctx.windowSuffix}`;\n const rows: TestCoverageRow[] = [];\n for (const node of ctx.nodes) {\n if (!keepNode(ctx, node.id)) continue;\n const bf = lookupMetric(ctx, bfName, node.id);\n if (bf === undefined || bf > 1) continue;\n rows.push({\n nodeId: node.id,\n testBusFactor: bf,\n testTopAuthorShare: lookupMetric(ctx, shareName, node.id) ?? 1,\n linkedTests: lookupMetric(ctx, \"linked_test_count\", node.id) ?? 0,\n });\n }\n rows.sort((a, b) => b.linkedTests - a.linkedTests);\n return rows.slice(0, limit);\n}\n\nexport function topCentralFiles(\n nodes: readonly GraphNode[],\n edges: readonly GraphEdge[],\n ctx: ReportContext,\n limit: number,\n): CentralRow[] {\n const pageRank = computePageRank(nodes, edges, {});\n const rows: CentralRow[] = [];\n for (const r of pageRank.rows) {\n if (!keepNode(ctx, r.nodeId)) continue;\n rows.push({ nodeId: r.nodeId, score: r.score });\n if (rows.length >= limit) break;\n }\n return rows;\n}\n","import type { GraphEdge, GraphNode } from \"../types.js\";\nimport { keepNode, lookupMetric, type ReportContext } from \"./graph-report-sections.js\";\nimport type { UnusedExportRow } from \"./graph-report-types.js\";\n\n/**\n * The set of files re-exported by a `barrel`-role node (1-hop `re-exports`\n * edges) — i.e. files whose exports form a package's public surface. An unused\n * export declared in one of these may still be consumed *externally* (by an npm\n * consumer of a published package), so it's flagged lower-confidence rather than\n * excluded. Transitive barrel chains are not followed (v1); a symbol behind two\n * barrels reads as internal.\n */\nexport function publicApiFiles(\n nodes: readonly GraphNode[],\n edges: readonly GraphEdge[],\n): Set<string> {\n const barrels = new Set(\n nodes.filter((n) => n.role === \"barrel\").map((n) => n.id),\n );\n const out = new Set<string>();\n for (const e of edges) {\n if (e.kind === \"re-exports\" && barrels.has(e.srcId)) out.add(e.dstId);\n }\n return out;\n}\n\n/**\n * Exported symbols (C-64 `attrs.exported`) with zero inbound `references` — an\n * export that nothing imports by name (utilization is barrel-resolved, C-53, so\n * an export used *through* a barrel reads > 0). Framed as \"no reference found\",\n * not \"dead\": it may be used only internally within its own file, or consumed\n * externally if the repo is a published library — hence the `publicApi` split.\n * Ranked by the export's own cognitive complexity (a complex unused export is\n * the most worth removing), scoped to kept (non-excluded) files.\n */\nexport function topUnusedExports(\n symbolNodes: readonly GraphNode[],\n publicApi: ReadonlySet<string>,\n ctx: ReportContext,\n limit: number,\n): UnusedExportRow[] {\n const rows: UnusedExportRow[] = [];\n for (const n of symbolNodes) {\n if (n.kind !== \"symbol\" || n.attrs?.exported !== true) continue;\n if ((lookupMetric(ctx, \"utilization\", n.id) ?? 0) > 0) continue;\n const fileId = n.parentId;\n if (!fileId || !keepNode(ctx, fileId)) continue;\n rows.push({\n nodeId: n.id,\n name: n.name,\n fileId,\n cognitive: lookupMetric(ctx, \"symbol_cognitive\", n.id) ?? 0,\n publicApi: publicApi.has(fileId),\n });\n }\n rows.sort(\n (a, b) =>\n Number(a.publicApi) - Number(b.publicApi) ||\n b.cognitive - a.cognitive ||\n a.nodeId.localeCompare(b.nodeId),\n );\n return rows.slice(0, limit);\n}\n","import type { GraphEdge, GraphNode, NodeRole } from \"../types.js\";\nimport { UNIMPORTED_ROLES } from \"../unimported-roles.js\";\nimport { keepNode, lookupMetric, type ReportContext } from \"./graph-report-sections.js\";\nimport type { DeadModuleRow } from \"./graph-report-types.js\";\n\n/**\n * Roles that seed reachability (and are never themselves \"dead\"): every role\n * nothing imports by design, plus package barrels (re-export hubs). Everything a\n * repo actually runs is reachable from these — with dynamic `import()` edges now\n * captured (C-65), the CLI's lazily-loaded command surface is reachable too, so\n * live commands aren't falsely flagged.\n */\nconst ENTRY_ROOT_ROLES: ReadonlySet<NodeRole> = new Set<NodeRole>([...UNIMPORTED_ROLES, \"barrel\"]);\n\n/**\n * A file that is conventionally a bundler entry point even though nothing imports\n * it — a `main.{ts,tsx,js,jsx}` (Vite/CRA/webpack default, referenced from\n * `index.html`, not from code). Seeds reachability so a whole SPA under it isn't\n * flagged unreferenced. (`index.*` is already the `barrel` role.)\n */\nconst ENTRY_FILE_RE = /(?:^|\\/)main\\.[jt]sx?$/;\n\nfunction isEntryRoot(node: GraphNode): boolean {\n return (\n node.kind === \"file\" &&\n ((node.role !== undefined && ENTRY_ROOT_ROLES.has(node.role)) ||\n ENTRY_FILE_RE.test(node.id))\n );\n}\n\n/**\n * Files unreachable from the entry roots by a forward BFS over `imports` /\n * `re-exports` edges — \"no importer found given configured entry points\" (C-65),\n * NOT proven dead. Catches transitively-dead chains, not just fan-in-0 files.\n * Blind spots (disclosed): a computed dynamic `import(variable)`, DI/registry\n * strings, and any package entry that isn't an index barrel escape the roots and\n * could make a live file look dead — so treat it as a lead, not a verdict.\n * Ranked by LOC (a large unreferenced file is the most worth removing).\n */\nexport function topDeadModules(\n nodes: readonly GraphNode[],\n edges: readonly GraphEdge[],\n ctx: ReportContext,\n limit: number,\n): DeadModuleRow[] {\n const reached = reachableFromEntryRoots(nodes, edges);\n const rows: DeadModuleRow[] = [];\n for (const n of nodes) {\n if (n.kind !== \"file\" || reached.has(n.id) || isEntryRoot(n)) continue;\n if (!keepNode(ctx, n.id)) continue;\n rows.push({ nodeId: n.id, loc: lookupMetric(ctx, \"loc\", n.id) ?? 0, role: n.role ?? \"source\" });\n }\n rows.sort((a, b) => b.loc - a.loc || a.nodeId.localeCompare(b.nodeId));\n return rows.slice(0, limit);\n}\n\n/** Adjacency of forward module edges (`imports` / `re-exports`) by source file. */\nfunction outgoingModuleEdges(\n edges: readonly GraphEdge[],\n): Map<string, string[]> {\n const out = new Map<string, string[]>();\n for (const e of edges) {\n if (e.kind !== \"imports\" && e.kind !== \"re-exports\") continue;\n const bucket = out.get(e.srcId);\n if (bucket) bucket.push(e.dstId);\n else out.set(e.srcId, [e.dstId]);\n }\n return out;\n}\n\n/** Files reachable from the entry roots by a forward BFS over module edges. */\nfunction reachableFromEntryRoots(\n nodes: readonly GraphNode[],\n edges: readonly GraphEdge[],\n): Set<string> {\n const out = outgoingModuleEdges(edges);\n const reached = new Set<string>();\n const queue: string[] = [];\n for (const n of nodes) {\n if (isEntryRoot(n)) {\n reached.add(n.id);\n queue.push(n.id);\n }\n }\n for (let i = 0; i < queue.length; i++) {\n for (const dst of out.get(queue[i]!) ?? []) {\n if (reached.has(dst)) continue;\n reached.add(dst);\n queue.push(dst);\n }\n }\n return reached;\n}\n","import { keepNode, lookupMetric, type ReportContext } from \"./graph-report-sections.js\";\nimport type { GrowthRiskRow } from \"./graph-report-types.js\";\n\nconst LOOP_SHAPES: Record<number, string> = { 2: \"quadratic-shaped\", 3: \"cubic-shaped\" };\n\n/** A loop-nesting depth's scaling-smell label — a shape, never a proven bound. */\nfunction loopShape(depth: number): string {\n return LOOP_SHAPES[depth] ?? `${depth}-deep loop nesting`;\n}\n\n/** The scaling smells a file exhibits, in reading order (deep loops → recursion → search). */\nfunction fileSmells(ctx: ReportContext, nodeId: string): string[] {\n const smells: string[] = [];\n const depth = lookupMetric(ctx, \"loop_depth\", nodeId) ?? 0;\n if (depth >= 2) smells.push(`${loopShape(depth)} loop nesting`);\n const rec = lookupMetric(ctx, \"recursive_functions\", nodeId) ?? 0;\n if (rec > 0) smells.push(`${rec} recursive function${rec === 1 ? \"\" : \"s\"}`);\n const search = lookupMetric(ctx, \"search_in_loop\", nodeId) ?? 0;\n if (search > 0) smells.push(`${search} linear search${search === 1 ? \"\" : \"es\"} in loops`);\n return smells;\n}\n\n/**\n * Files carrying a structural scaling smell (C-66): deep loop nesting, direct\n * recursion, or a linear-scan method call inside a loop. A HEURISTIC, not Big-O:\n * depth-2 loops over two different collections are linear, `.includes` on a `Set`\n * is O(1), and recursion may be well-bounded. Ranked by loop depth, then smell\n * count.\n */\nexport function topGrowthRisks(\n ctx: ReportContext,\n limit: number,\n): GrowthRiskRow[] {\n const rows: GrowthRiskRow[] = [];\n for (const node of ctx.nodes) {\n if (!keepNode(ctx, node.id)) continue;\n const smells = fileSmells(ctx, node.id);\n if (smells.length === 0) continue;\n rows.push({\n nodeId: node.id,\n loopDepth: lookupMetric(ctx, \"loop_depth\", node.id) ?? 0,\n smells,\n });\n }\n rows.sort(\n (a, b) =>\n b.loopDepth - a.loopDepth ||\n b.smells.length - a.smells.length ||\n a.nodeId.localeCompare(b.nodeId),\n );\n return rows.slice(0, limit);\n}\n","import {\n hotspotScoreOf,\n keepNode,\n lookupMetric,\n type ReportContext,\n} from \"./graph-report-sections.js\";\nimport type { UntestedRiskRow } from \"./graph-report-types.js\";\n\n/**\n * Files that are load-bearing + complex + churning AND under-tested (C-63) —\n * the sharpest single risk signal: `hotspot × (1 − coverage/100)`. Requires an\n * ingested coverage overlay (`graph coverage`); with no coverage, the section is\n * empty (never a stale or assumed number — coverage is an overlay, not inferred).\n * A fully-covered hotspot (coverage 100) scores 0 and drops out.\n */\nexport function topUntestedRisks(\n ctx: ReportContext,\n limit: number,\n): UntestedRiskRow[] {\n const rows: UntestedRiskRow[] = [];\n for (const node of ctx.nodes) {\n if (!keepNode(ctx, node.id)) continue;\n const coverage = lookupMetric(ctx, \"coverage_pct\", node.id);\n if (coverage === undefined || coverage >= 100) continue;\n const hotspot = hotspotScoreOf(ctx, node.id);\n const score = Math.round(hotspot * (1 - coverage / 100));\n if (score <= 0) continue;\n rows.push({ nodeId: node.id, coverage, hotspot, score });\n }\n rows.sort((a, b) => b.score - a.score || a.nodeId.localeCompare(b.nodeId));\n return rows.slice(0, limit);\n}\n","import type {\n BusFactorChange,\n BusFactorRow,\n CouplingDelta,\n CouplingRow,\n HotspotDelta,\n HotspotRow,\n NewHotspot,\n ReportDrift,\n} from \"./graph-report-types.js\";\nimport type { SnapshotRow } from \"../types.js\";\n\nexport interface ComputeDriftInput {\n baselineSnapshot: SnapshotRow;\n currentHotspots: readonly HotspotRow[];\n baselineHotspots: readonly HotspotRow[];\n /** Current churn × complexity for any nodeId. 0 when file is gone or no longer has churn/complexity. */\n currentHotspotScore: (nodeId: string) => number;\n /**\n * Baseline churn × complexity for a file that existed at baseline, or\n * `undefined` when the file is newborn (absent from the baseline snapshot).\n * Distinguishes a brand-new hotspot from an existing file that climbed in.\n */\n baselineHotspotScore?: (nodeId: string) => number | undefined;\n currentSilos: readonly BusFactorRow[];\n baselineSilos: readonly BusFactorRow[];\n /** Current bus_factor for any nodeId. undefined when file has no churn in window. */\n currentBusFactor: (nodeId: string) => number | undefined;\n currentCoupling: readonly CouplingRow[];\n baselineCoupling: readonly CouplingRow[];\n}\n\nexport function computeReportDrift(input: ComputeDriftInput): ReportDrift {\n const hot = diffHotspots(\n input.currentHotspots,\n input.baselineHotspots,\n input.currentHotspotScore,\n input.baselineHotspotScore ?? (() => undefined),\n );\n const silos = diffSilos(\n input.currentSilos,\n input.baselineSilos,\n input.currentBusFactor,\n );\n const coupling = diffCoupling(input.currentCoupling, input.baselineCoupling);\n return {\n baselineSnapshot: input.baselineSnapshot,\n newHotspots: hot.added,\n resolvedHotspots: hot.resolved,\n displacedHotspots: hot.displaced,\n worsenedHotspots: hot.worsened,\n improvedHotspots: hot.improved,\n newSilos: silos.added,\n resolvedSilos: silos.resolved,\n displacedSilos: silos.displaced,\n newCoupling: coupling.added,\n intensifiedCoupling: coupling.intensified,\n };\n}\n\nfunction diffHotspots(\n cur: readonly HotspotRow[],\n base: readonly HotspotRow[],\n currentScore: (nodeId: string) => number,\n baselineScore: (nodeId: string) => number | undefined,\n): {\n added: NewHotspot[];\n resolved: HotspotDelta[];\n displaced: HotspotDelta[];\n worsened: HotspotDelta[];\n improved: HotspotDelta[];\n} {\n const baseById = new Map(base.map((r) => [r.nodeId, r] as const));\n const curById = new Map(cur.map((r) => [r.nodeId, r] as const));\n const added: NewHotspot[] = [];\n const resolved: HotspotDelta[] = [];\n const displaced: HotspotDelta[] = [];\n const worsened: HotspotDelta[] = [];\n const improved: HotspotDelta[] = [];\n for (const r of cur) {\n const b = baseById.get(r.nodeId);\n if (!b) {\n added.push({ ...r, before: baselineScore(r.nodeId) });\n continue;\n }\n const delta = r.score - b.score;\n if (delta > 0) worsened.push({ nodeId: r.nodeId, before: b.score, after: r.score, delta });\n else if (delta < 0) improved.push({ nodeId: r.nodeId, before: b.score, after: r.score, delta });\n }\n for (const b of base) {\n if (curById.has(b.nodeId)) continue;\n const after = currentScore(b.nodeId);\n const delta = after - b.score;\n const row: HotspotDelta = { nodeId: b.nodeId, before: b.score, after, delta };\n if (after < b.score) resolved.push(row);\n else displaced.push(row);\n }\n worsened.sort((a, b) => b.delta - a.delta);\n improved.sort((a, b) => a.delta - b.delta);\n resolved.sort((a, b) => a.delta - b.delta);\n displaced.sort((a, b) => b.before - a.before);\n return { added, resolved, displaced, worsened, improved };\n}\n\nfunction diffSilos(\n cur: readonly BusFactorRow[],\n base: readonly BusFactorRow[],\n currentBusFactor: (nodeId: string) => number | undefined,\n): {\n added: BusFactorChange[];\n resolved: BusFactorChange[];\n displaced: BusFactorChange[];\n} {\n const baseIds = new Set(base.map((r) => r.nodeId));\n const curIds = new Set(cur.map((r) => r.nodeId));\n const added: BusFactorChange[] = [];\n const resolved: BusFactorChange[] = [];\n const displaced: BusFactorChange[] = [];\n for (const r of cur) {\n if (!baseIds.has(r.nodeId)) added.push({ nodeId: r.nodeId, churn: r.churn });\n }\n for (const r of base) {\n if (curIds.has(r.nodeId)) continue;\n const bf = currentBusFactor(r.nodeId);\n if (bf === undefined || bf > 1) {\n resolved.push({ nodeId: r.nodeId, churn: r.churn });\n } else {\n displaced.push({ nodeId: r.nodeId, churn: r.churn });\n }\n }\n added.sort((a, b) => b.churn - a.churn);\n resolved.sort((a, b) => b.churn - a.churn);\n displaced.sort((a, b) => b.churn - a.churn);\n return { added, resolved, displaced };\n}\n\nfunction diffCoupling(\n cur: readonly CouplingRow[],\n base: readonly CouplingRow[],\n): { added: CouplingRow[]; intensified: CouplingDelta[] } {\n const key = (r: { fileA: string; fileB: string }): string =>\n `${r.fileA}\\t${r.fileB}`;\n const baseByKey = new Map(base.map((r) => [key(r), r] as const));\n const added: CouplingRow[] = [];\n const intensified: CouplingDelta[] = [];\n for (const r of cur) {\n const b = baseByKey.get(key(r));\n if (!b) added.push(r);\n else if (r.count > b.count) {\n intensified.push({\n fileA: r.fileA,\n fileB: r.fileB,\n before: b.count,\n after: r.count,\n });\n }\n }\n intensified.sort((a, b) => b.after - b.before - (a.after - a.before));\n return { added, intensified };\n}\n","/**\n * Composite dashboard health score. Extracted from dashboard-payload.ts (which\n * sits over the max-file-loc budget) so that file stays lean as new payload\n * slices land.\n */\n\nexport type HealthComponentKey = \"hotspots\" | \"findings\" | \"complexity\" | \"hidden-coupling\";\n\nexport interface HealthComponent {\n key: HealthComponentKey;\n label: string;\n penalty: number;\n /** The most this component can take off the score. */\n cap: number;\n detail: string;\n}\n\n/** Points per counted item, and the most one component can take. */\nexport interface PenaltyWeight {\n each: number;\n cap: number;\n}\n\nexport interface HealthWeights {\n hotspots: PenaltyWeight;\n findings: { eachNew: number; eachCarry: number; cap: number };\n /** `each` point per unit of max complexity over `budget`. */\n complexity: PenaltyWeight & { budget: number };\n hiddenCoupling: PenaltyWeight;\n}\n\nexport const DEFAULT_HEALTH_WEIGHTS: HealthWeights = {\n hotspots: { each: 10, cap: 30 },\n findings: { eachNew: 8, eachCarry: 3, cap: 20 },\n complexity: { each: 1, budget: 30, cap: 15 },\n hiddenCoupling: { each: 2, cap: 10 },\n};\n\nexport interface HealthInput {\n scary: number;\n newViolations: number;\n carryViolations: number;\n maxComplexity: number;\n hiddenCoupling: number;\n /** The hotspot score `scary` counted files at; only the detail text reads it. */\n scaryCutoff?: number;\n}\n\nfunction healthComponents(x: HealthInput, w: HealthWeights): HealthComponent[] {\n const over = Math.max(0, x.maxComplexity - w.complexity.budget);\n return [\n { key: \"hotspots\", label: \"scary hotspots\", penalty: Math.min(w.hotspots.cap, x.scary * w.hotspots.each), cap: w.hotspots.cap, detail: `${x.scary} file(s) ≥ ${x.scaryCutoff ?? 3000}` },\n {\n key: \"findings\",\n label: \"fitness violations\",\n penalty: Math.min(w.findings.cap, x.newViolations * w.findings.eachNew + x.carryViolations * w.findings.eachCarry),\n cap: w.findings.cap,\n detail: `${x.newViolations} new, ${x.carryViolations} parked (non-hotspot rules)`,\n },\n { key: \"complexity\", label: \"complexity over budget\", penalty: Math.min(w.complexity.cap, over * w.complexity.each), cap: w.complexity.cap, detail: `max ${x.maxComplexity} vs budget ${w.complexity.budget}` },\n { key: \"hidden-coupling\", label: \"hidden coupling\", penalty: Math.min(w.hiddenCoupling.cap, x.hiddenCoupling * w.hiddenCoupling.each), cap: w.hiddenCoupling.cap, detail: `${x.hiddenCoupling} pair(s) co-change without an import` },\n ];\n}\n\n/**\n * Composite health as a transparent sum of independent penalty components, so\n * the UI can show *why* the score is what it is instead of a black-box number.\n * Each component is capped and drawn from a distinct dimension — the hotspots\n * component owns scary files, so the violations component excludes the\n * scary-hotspots rule (no double-count). Ownership (knowledge-silo / bus-factor)\n * signal is deliberately NOT a health component: it saturates on single-author\n * repos and lives on the Ownership tab, not in the cross-cutting score.\n * `weights` defaults to the dashboard's; a caller may weigh the components its own way.\n */\nexport function computeHealth(\n x: HealthInput,\n weights: HealthWeights = DEFAULT_HEALTH_WEIGHTS,\n): { health: number; healthBreakdown: HealthComponent[] } {\n const breakdown = healthComponents(x, weights);\n const total = breakdown.reduce((s, c) => s + c.penalty, 0);\n return { health: Math.max(0, 100 - total), healthBreakdown: breakdown };\n}\n","import type { NodeMetrics, SymbolUtil } from \"./dashboard-node-metrics.js\";\nimport type { SymbolCouplingPayload } from \"./dashboard-symbol-coupling.js\";\n\n/**\n * Snapshot-level (window-independent) derived data: which file pairs are joined\n * by a static import edge, and each node's PageRank centrality. Computed once\n * per snapshot from a single db open, shared across all window payloads.\n */\nexport interface SnapshotContext {\n linkedPairs: ReadonlySet<string>;\n centrality: ReadonlyMap<string, number>;\n /**\n * Ids of files that participate in at least one *internal* (repo-to-repo)\n * import/re-export edge. A file missing here has no resolved imports in the\n * graph — either it isn't indexed, or its imports couldn't be resolved (e.g. a\n * dir outside the tsconfig project, whose relative specifiers resolve to junk).\n * Either way the import evidence is absent, so a co-change touching it can't be\n * called hidden-vs-import-backed; it's \"unverifiable\", not \"hidden\".\n */\n connectedNodes: ReadonlySet<string>;\n /** Per-node structural metrics (loc, cognitive/cyclomatic max, nesting, fan) for the Dossier. */\n metrics: ReadonlyMap<string, NodeMetrics>;\n /** Per-export utilization (C-53), for the Dossier \"hot exports\" list and the blast-radius section. */\n symbols: readonly SymbolUtil[];\n /** Inbound `references` count per symbol id (C-59): how many files consume each export. */\n consumersBySymbol: ReadonlyMap<string, number>;\n /** Symbol-level coupling slices (C-60): co-imported pairs + per-symbol consumers. */\n symbolCoupling?: SymbolCouplingPayload;\n}\n\nexport type CouplingClass = { hidden: boolean; unindexed: boolean };\n\n/**\n * Classify a co-changed pair against the static import graph:\n * - unindexed: an endpoint has no resolved internal imports → can't tell (not hidden).\n * - hidden: both connected, but no import/re-export edge joins them → the signal.\n * - expected: both connected and import-backed → usually fine.\n */\nexport function classifyCoupling(\n a: string,\n b: string,\n ctx: SnapshotContext,\n): CouplingClass {\n const unindexed = !ctx.connectedNodes.has(a) || !ctx.connectedNodes.has(b);\n if (unindexed) return { hidden: false, unindexed: true };\n return { hidden: !ctx.linkedPairs.has(pairKey(a, b)), unindexed: false };\n}\n\n/** Order-independent key for an undirected file pair (JSON tuple; no in-band separator). */\nexport function pairKey(a: string, b: string): string {\n return a < b ? JSON.stringify([a, b]) : JSON.stringify([b, a]);\n}\n","export interface PackageRoot {\n /** Path relative to repo root, posix-separated (e.g., \"packages/cli\"). Empty for repo-root package. */\n id: string;\n /** Display name from package.json or directory basename. */\n name: string;\n}\n\n/**\n * For each file id, find the longest matching package prefix.\n * Returns a Map from package id → file ids assigned to it.\n * Files matching no package are returned under the empty-string key.\n */\nexport function bucketFilesByPackage(\n fileIds: readonly string[],\n packages: readonly PackageRoot[],\n): Map<string, string[]> {\n const sorted = [...packages].sort((a, b) => b.id.length - a.id.length);\n const out = new Map<string, string[]>();\n for (const id of fileIds) {\n const pkg = matchPackage(id, sorted);\n const key = pkg?.id ?? \"\";\n let list = out.get(key);\n if (!list) {\n list = [];\n out.set(key, list);\n }\n list.push(id);\n }\n return out;\n}\n\nfunction matchPackage(\n fileId: string,\n packagesByLongestId: readonly PackageRoot[],\n): PackageRoot | null {\n for (const p of packagesByLongestId) {\n if (p.id === \"\") continue;\n if (fileId === p.id) return p;\n if (fileId.startsWith(`${p.id}/`)) return p;\n }\n return null;\n}\n","import { compilePatterns, matchesAny } from \"../check/patterns.js\";\nimport type { GraphEdge, GraphNode, SnapshotRow } from \"../types.js\";\nimport type { ArchEdge, ArchPackage, ArchResult, ArchSubNode } from \"./graph-arch-types.js\";\nimport { bucketFilesByPackage, type PackageRoot } from \"./package-buckets.js\";\nimport { invertBuckets } from \"./partition-quality.js\";\n\nexport const EXTERNAL_BUCKET = \"(external)\";\n\n/** Default file-count threshold above which a package is drilled (C-10). */\nexport const DEFAULT_MAX_PACKAGE_SIZE = 30;\n\nexport interface ComputeArchInput {\n snapshot: SnapshotRow;\n nodes: readonly GraphNode[];\n edges: readonly GraphEdge[];\n packages: readonly PackageRoot[];\n exclude?: string[];\n excludeRole?: string[];\n includeExternal?: boolean;\n minEdges?: number;\n depth?: \"modules\";\n maxPackageSize?: number;\n}\n\nexport function computeArch(input: ComputeArchInput): ArchResult {\n const fileIds = filteredFileIds(input.nodes, input);\n const fileByPackage = bucketFilesByPackage(fileIds, input.packages);\n const drill = planDrill(input, fileByPackage);\n const nodeByFile = buildNodeByFile(fileByPackage, drill);\n const pkgByFile = invertBuckets(fileByPackage);\n const externalIds = new Set(\n input.nodes.filter((n) => n.kind === \"external\").map((n) => n.id),\n );\n const counts = aggregateEdges(\n input.edges,\n pkgByFile,\n nodeByFile,\n externalIds,\n Boolean(input.includeExternal),\n );\n const minEdges = Math.max(1, input.minEdges ?? 1);\n return {\n snapshot: input.snapshot,\n packages: activePackages(\n input.packages,\n fileByPackage,\n Boolean(input.includeExternal),\n counts,\n drill,\n ),\n edges: toSortedEdges(counts, minEdges),\n includesExternal: Boolean(input.includeExternal),\n };\n}\n\n/**\n * Roles kept out of the rendered dependency graph by default (C-63): test and\n * fixture files import production code but aren't part of its module structure,\n * so including them clutters the graph with test→source edges. `--exclude-role`\n * is additive on top; there is no un-exclude (a dependency graph of tests isn't a\n * use case). Their relationship to source is surfaced instead as the C-4\n * linked-test-count + C-63 coverage metrics.\n */\nconst DEFAULT_GRAPH_EXCLUDED_ROLES = [\"test\", \"fixture\"];\n\nexport function filteredFileIds(\n nodes: readonly GraphNode[],\n options: { exclude?: string[]; excludeRole?: string[] },\n): string[] {\n const excluders = compilePatterns(options.exclude);\n const excludedRoles = new Set([\n ...DEFAULT_GRAPH_EXCLUDED_ROLES,\n ...(options.excludeRole ?? []),\n ]);\n return nodes\n .filter((n) => n.kind === \"file\")\n .filter((n) => !excludedRoles.has(n.role ?? \"\"))\n .filter((n) => !matchesAny(n.id, excluders))\n .map((n) => n.id);\n}\n\nexport function aggregateEdges(\n edges: ReadonlyArray<{ srcId: string; dstId: string }>,\n pkgByFile: ReadonlyMap<string, string>,\n nodeByFile: ReadonlyMap<string, string>,\n externalIds: ReadonlySet<string>,\n includeExternal: boolean,\n): Map<string, Map<string, number>> {\n const counts = new Map<string, Map<string, number>>();\n for (const e of edges) {\n const fromPkg = pkgByFile.get(e.srcId);\n if (!fromPkg) continue;\n const dest = resolveDestination(\n e.dstId,\n pkgByFile,\n nodeByFile,\n externalIds,\n includeExternal,\n );\n if (!dest || dest.pkg === fromPkg) continue;\n bump(counts, nodeByFile.get(e.srcId) ?? fromPkg, dest.node);\n }\n return counts;\n}\n\ninterface Destination {\n /** Package the destination belongs to (for the same-package skip check). */\n pkg: string;\n /** Node the edge points at — a sub-dir node when the package is drilled. */\n node: string;\n}\n\nfunction resolveDestination(\n dstId: string,\n pkgByFile: ReadonlyMap<string, string>,\n nodeByFile: ReadonlyMap<string, string>,\n externalIds: ReadonlySet<string>,\n includeExternal: boolean,\n): Destination | null {\n const pkg = pkgByFile.get(dstId);\n if (pkg !== undefined) return { pkg, node: nodeByFile.get(dstId) ?? pkg };\n if (externalIds.has(dstId) && includeExternal) {\n return { pkg: EXTERNAL_BUCKET, node: EXTERNAL_BUCKET };\n }\n return null;\n}\n\nfunction bump(\n counts: Map<string, Map<string, number>>,\n from: string,\n to: string,\n): void {\n let row = counts.get(from);\n if (!row) {\n row = new Map();\n counts.set(from, row);\n }\n row.set(to, (row.get(to) ?? 0) + 1);\n}\n\nexport function toSortedEdges(\n counts: ReadonlyMap<string, ReadonlyMap<string, number>>,\n minEdges: number,\n): ArchEdge[] {\n const out: ArchEdge[] = [];\n for (const [from, row] of counts) {\n for (const [to, count] of row) {\n if (count < minEdges) continue;\n out.push({ from, to, count });\n }\n }\n out.sort(compareArchEdges);\n return out;\n}\n\nfunction compareArchEdges(a: ArchEdge, b: ArchEdge): number {\n if (a.from !== b.from) return a.from < b.from ? -1 : 1;\n return a.to < b.to ? -1 : 1;\n}\n\nfunction activePackages(\n all: readonly PackageRoot[],\n fileByPackage: ReadonlyMap<string, ReadonlyArray<string>>,\n includeExternal: boolean,\n counts: ReadonlyMap<string, ReadonlyMap<string, number>>,\n drill: ReadonlyMap<string, DrillPlan>,\n): ArchPackage[] {\n const referenced = packagesReferencedByEdges(counts);\n const out: ArchPackage[] = [];\n for (const p of all) {\n const files = fileByPackage.get(p.id)?.length ?? 0;\n if (files === 0 && !referenced.has(p.id)) continue;\n const plan = drill.get(p.id);\n out.push(\n plan\n ? { id: p.id, name: p.name, files, subNodes: plan.subNodes }\n : { id: p.id, name: p.name, files },\n );\n }\n if (includeExternal && referenced.has(EXTERNAL_BUCKET)) {\n out.push({ id: EXTERNAL_BUCKET, name: EXTERNAL_BUCKET, files: 0 });\n }\n return out;\n}\n\nexport function packagesReferencedByEdges(\n counts: ReadonlyMap<string, ReadonlyMap<string, number>>,\n): Set<string> {\n const referenced = new Set<string>();\n for (const [from, row] of counts) {\n referenced.add(from);\n for (const to of row.keys()) referenced.add(to);\n }\n return referenced;\n}\n\n/**\n * A drilled package: its files grouped into top-level sub-directory nodes.\n * `fileToSub` maps each file id to the sub-node it renders under so edges can\n * be re-pointed from the package to the specific directory.\n */\ninterface DrillPlan {\n subNodes: ArchSubNode[];\n fileToSub: Map<string, string>;\n}\n\n/**\n * Drilling is presentational and opt-in: either `--depth modules` or an\n * explicit `--max-package-size` enables it. Only packages with more files than\n * the threshold are drilled; the default output is byte-identical.\n */\nfunction planDrill(\n input: ComputeArchInput,\n fileByPackage: ReadonlyMap<string, ReadonlyArray<string>>,\n): Map<string, DrillPlan> {\n const enabled =\n input.depth === \"modules\" || input.maxPackageSize !== undefined;\n if (!enabled) return new Map();\n const threshold = Math.max(1, input.maxPackageSize ?? DEFAULT_MAX_PACKAGE_SIZE);\n const out = new Map<string, DrillPlan>();\n for (const [pkgId, files] of fileByPackage) {\n if (pkgId === \"\" || files.length <= threshold) continue;\n out.set(pkgId, buildDrillPlan(pkgId, files));\n }\n return out;\n}\n\n/** file id → the node it aggregates to: a sub-dir node if drilled, else the package. */\nfunction buildNodeByFile(\n fileByPackage: ReadonlyMap<string, ReadonlyArray<string>>,\n drill: ReadonlyMap<string, DrillPlan>,\n): Map<string, string> {\n const out = new Map<string, string>();\n for (const [pkgId, files] of fileByPackage) {\n if (pkgId === \"\") continue;\n const plan = drill.get(pkgId);\n for (const f of files) out.set(f, plan?.fileToSub.get(f) ?? pkgId);\n }\n return out;\n}\n\nfunction buildDrillPlan(\n pkgId: string,\n files: ReadonlyArray<string>,\n): DrillPlan {\n const rels = files.map((f) => stripPkgPrefix(f, pkgId));\n const commonRoot = drillRoot(rels.map(dirSegments));\n const fileToSub = new Map<string, string>();\n const groups = new Map<string, { label: string; count: number }>();\n files.forEach((file, i) => {\n const { id, label } = subNodeFor(pkgId, rels[i]!, commonRoot);\n fileToSub.set(file, id);\n const g = groups.get(id);\n if (g) g.count += 1;\n else groups.set(id, { label, count: 1 });\n });\n const subNodes = [...groups.entries()]\n .map(([id, g]) => ({ id, label: g.label, files: g.count }))\n .sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));\n return { subNodes, fileToSub };\n}\n\n/**\n * The sub-node a file belongs to: the directory one level below the package's\n * common file root. Files sitting directly in the common root fall into a\n * \"(root)\" bucket.\n */\nfunction subNodeFor(\n pkgId: string,\n rel: string,\n commonRoot: readonly string[],\n): { id: string; label: string } {\n const segment = dirSegments(rel).slice(commonRoot.length)[0] ?? \"\";\n if (segment !== \"\") {\n return { id: `${pkgId}/${[...commonRoot, segment].join(\"/\")}`, label: segment };\n }\n const id = commonRoot.length\n ? `${pkgId}/${commonRoot.join(\"/\")}`\n : `${pkgId}/(root)`;\n return { id, label: \"(root)\" };\n}\n\nfunction stripPkgPrefix(fileId: string, pkgId: string): string {\n if (fileId === pkgId) return \"\";\n return fileId.startsWith(`${pkgId}/`) ? fileId.slice(pkgId.length + 1) : fileId;\n}\n\nfunction dirSegments(rel: string): string[] {\n const i = rel.lastIndexOf(\"/\");\n return i < 0 ? [] : rel.slice(0, i).split(\"/\");\n}\n\n/**\n * The directory prefix to peel before splitting a package into sub-dir nodes.\n * Descends through a lone wrapper directory (e.g. `src`) — tolerating a few\n * leaf files sitting above it — but stops as soon as the next level branches\n * into siblings, or descending would collapse everything into one group.\n */\nfunction drillRoot(dirs: readonly (readonly string[])[]): string[] {\n const root: string[] = [];\n for (;;) {\n const deeper = dirs.filter((d) => d.length > root.length);\n if (deeper.length === 0) break;\n const seg = deeper[0]![root.length]!;\n const singleChild = deeper.every((d) => d[root.length] === seg);\n if (!singleChild || countGroups(dirs, root.length + 1) < 2) break;\n root.push(seg);\n }\n return root;\n}\n\n/**\n * Distinct sub-dir groups if the common root were `rootLen` segments deep:\n * files at or above that depth collapse into one \"(root)\" group, deeper files\n * group by their segment at `rootLen`.\n */\nfunction countGroups(\n dirs: readonly (readonly string[])[],\n rootLen: number,\n): number {\n const groups = new Set<string>();\n for (const d of dirs) groups.add(d.length <= rootLen ? \"(root)\" : d[rootLen]!);\n return groups.size;\n}\n","import type { CheckViolation } from \"./types.js\";\n\n/** The fields that identify a violation; a finding read back from an export carries them too. */\nexport type ViolationIdentity = Pick<CheckViolation, \"ruleId\" | \"nodeId\" | \"destinationId\">;\n\n/** Identity of a violation across snapshots: severity, value and message may change, the key does not. */\nexport function violationKey(v: ViolationIdentity): string {\n return v.destinationId ? `${v.ruleId}|${v.nodeId}|${v.destinationId}` : `${v.ruleId}|${v.nodeId}`;\n}\n\n/** {@link violationKey} with both node ids carried into another snapshot's id space; unmoved ids key as before. */\nexport function rebasedViolationKey(v: ViolationIdentity, resolve: (id: string) => string): string {\n const destinationId = v.destinationId ? resolve(v.destinationId) : v.destinationId;\n return violationKey({ ...v, nodeId: resolve(v.nodeId), destinationId });\n}\n","import type { CheckViolation } from \"../check/types.js\";\nimport { rebasedViolationKey, violationKey, type ViolationIdentity } from \"../check/violation-key.js\";\n\n/** What bucketing reads from a violation: its identity and, when the rule measures one, its value. */\nexport type BucketableViolation = ViolationIdentity & Pick<CheckViolation, \"value\">;\n\nexport interface UnchangedViolation<V extends BucketableViolation = CheckViolation> {\n from: V;\n to: V;\n delta: number | null;\n}\n\nexport interface ViolationBuckets<V extends BucketableViolation = CheckViolation> {\n newViolations: V[];\n resolvedViolations: V[];\n unchanged: UnchangedViolation<V>[];\n worsened: UnchangedViolation<V>[];\n improved: UnchangedViolation<V>[];\n}\n\n/**\n * Bucket two snapshots' violations as new, resolved, or unchanged (worsened or improved\n * by value). `resolve` carries from-side ids into the to-snapshot, so a moved file's\n * violations stay unchanged; omitted, ids match as they are. Needs no store.\n */\nexport function bucketViolations<V extends BucketableViolation>(\n from: readonly V[],\n to: readonly V[],\n resolve?: (id: string) => string,\n): ViolationBuckets<V> {\n const fromByKey = indexByKey(from, resolve);\n const toByKey = indexByKey(to);\n const buckets: ViolationBuckets<V> = { newViolations: [], resolvedViolations: [], unchanged: [], worsened: [], improved: [] };\n for (const [key, toV] of toByKey) {\n const fromV = fromByKey.get(key);\n if (!fromV) {\n buckets.newViolations.push(toV);\n continue;\n }\n const entry: UnchangedViolation<V> = { from: fromV, to: toV, delta: computeDelta(fromV, toV) };\n buckets.unchanged.push(entry);\n if (entry.delta !== null && entry.delta > 0) buckets.worsened.push(entry);\n else if (entry.delta !== null && entry.delta < 0) buckets.improved.push(entry);\n }\n for (const [key, fromV] of fromByKey) {\n if (!toByKey.has(key)) buckets.resolvedViolations.push(fromV);\n }\n return buckets;\n}\n\nfunction indexByKey<V extends BucketableViolation>(violations: readonly V[], resolve?: (id: string) => string): Map<string, V> {\n const out = new Map<string, V>();\n for (const v of violations) out.set(resolve ? rebasedViolationKey(v, resolve) : violationKey(v), v);\n return out;\n}\n\nfunction computeDelta(from: BucketableViolation, to: BucketableViolation): number | null {\n if (from.value === undefined || to.value === undefined) return null;\n return to.value - from.value;\n}\n"],"mappings":"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","names":["comparePairs"]}
@@ -1,14 +1,35 @@
1
- import { C as ChurnEntry, a as ChurnWindow } from '../change-coupling-CyqHgRsm.js';
2
- export { b as ChangeCouplingResult, c as CoEditPair, d as ComputeChangeCouplingOptions, e as CouplingPartner, L as LoadChurnOptions, f as computeChangeCoupling, g as couplingFor, h as entriesWithin, l as loadChurnEntries, p as parseChurnLog, r as resolveRenamedPath } from '../change-coupling-CyqHgRsm.js';
1
+ import { C as ChurnEntry, a as ChurnWindow } from '../change-coupling-CT3bcZMD.js';
2
+ export { b as ChangeCouplingResult, c as CoEditPair, d as ComputeChangeCouplingOptions, e as CouplingPartner, L as LoadChurnOptions, f as computeChangeCoupling, g as couplingFor, h as entriesWithin, l as loadChurnEntries, p as parseChurnLog, r as resolveRenamedPath } from '../change-coupling-CT3bcZMD.js';
3
+
4
+ /** A regular file or symlink in a commit's tree: its repo-relative path and blob id. */
5
+ interface TreeBlob {
6
+ path: string;
7
+ oid: string;
8
+ /** A symlink (mode 120000): the blob holds the link's target. */
9
+ link: boolean;
10
+ }
11
+ interface ResolvedCommit {
12
+ commit: string;
13
+ /** Committer time, seconds since the epoch. */
14
+ commitEpoch: number;
15
+ }
16
+ /** Resolve `rev` to a commit sha and its committer epoch; throws naming `rev` when it is not a commit. */
17
+ declare function resolveCommit(repoRoot: string, rev: string): ResolvedCommit;
18
+ /** Every regular file and symlink in `commit`'s tree, via `git ls-tree -r -z`. */
19
+ declare function listTreeBlobs(repoRoot: string, commit: string): TreeBlob[];
20
+ /** UTF-8 content of each blob in `oids`, read with a single `git cat-file --batch`. */
21
+ declare function readBlobs(repoRoot: string, oids: readonly string[]): Map<string, string>;
3
22
 
4
23
  interface FirstSeenOptions {
5
24
  /** Directory paths are made relative to; paths outside it are dropped. */
6
25
  repoRoot: string;
7
26
  knownPaths?: ReadonlySet<string>;
27
+ /** Walk history from this rev instead of HEAD. */
28
+ rev?: string;
8
29
  }
9
30
  /**
10
31
  * Earliest commit time (epoch seconds) per path, from one reverse-ordered pass
11
- * over full history: the first commit that touched a path approximates its birth.
32
+ * over full history up to `rev` (default HEAD): the first commit that touched a path approximates its birth.
12
33
  * Rebased onto `repoRoot` and filtered to `knownPaths` when given. Renames slightly
13
34
  * underestimate age (pre-rename history lives under the old path). Returns null
14
35
  * when git is unavailable, so callers degrade to "no age discount".
@@ -53,4 +74,4 @@ declare function authorLinesByPath(entries: readonly ChurnEntry[], known: Readon
53
74
  /** Summarize one author tally; null when it holds no churn. */
54
75
  declare function summarizeOwnership(byAuthor: ReadonlyMap<string, number>, threshold?: number): OwnershipForFile | null;
55
76
 
56
- export { ChurnEntry, ChurnWindow, type ComputeOwnershipOptions, type FirstSeenOptions, type OwnershipForFile, type PathChurn, aggregateChurn, aggregateChurnWindows, authorLinesByPath, computeOwnership, loadFileFirstSeen, summarizeOwnership };
77
+ export { ChurnEntry, ChurnWindow, type ComputeOwnershipOptions, type FirstSeenOptions, type OwnershipForFile, type PathChurn, type ResolvedCommit, type TreeBlob, aggregateChurn, aggregateChurnWindows, authorLinesByPath, computeOwnership, listTreeBlobs, loadFileFirstSeen, readBlobs, resolveCommit, summarizeOwnership };
@@ -2,16 +2,21 @@ import {
2
2
  aggregateChurn,
3
3
  aggregateChurnWindows,
4
4
  authorLinesByPath,
5
- computeChangeCoupling,
6
5
  computeOwnership,
7
- couplingFor,
8
6
  entriesWithin,
7
+ listTreeBlobs,
9
8
  loadChurnEntries,
10
9
  loadFileFirstSeen,
11
10
  parseChurnLog,
11
+ readBlobs,
12
+ resolveCommit,
12
13
  resolveRenamedPath,
13
14
  summarizeOwnership
14
- } from "../chunk-PFI5XMUG.js";
15
+ } from "../chunk-GX563F6R.js";
16
+ import {
17
+ computeChangeCoupling,
18
+ couplingFor
19
+ } from "../chunk-HIVVDGWE.js";
15
20
  export {
16
21
  aggregateChurn,
17
22
  aggregateChurnWindows,
@@ -20,9 +25,12 @@ export {
20
25
  computeOwnership,
21
26
  couplingFor,
22
27
  entriesWithin,
28
+ listTreeBlobs,
23
29
  loadChurnEntries,
24
30
  loadFileFirstSeen,
25
31
  parseChurnLog,
32
+ readBlobs,
33
+ resolveCommit,
26
34
  resolveRenamedPath,
27
35
  summarizeOwnership
28
36
  };