@titan-design/code-graph 0.10.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.
package/dist/index.d.ts CHANGED
@@ -1,63 +1,12 @@
1
+ import { N as NodeKind, G as GraphNode, a as GraphEdge, b as GraphMetric, I as IdAlias, F as FileFingerprint, S as SnapshotRow, c as GraphFragment, d as NodeRole, E as EdgeKind, e as IdAliasReason, T as TestSourceLink, f as NodeMetrics, B as BlastRadiusEntry, R as ReferenceEdgeLite, g as SymbolConsumers, h as SymbolCouplingOptions, i as SymbolCouplingPair, C as CheckRule, j as CheckResult, k as CheckViolation, l as Severity, V as ViolationBuckets } from './browser-BBzShneN.js';
2
+ export { A as ArchEdge, m as ArchPackage, n as ArchResult, o as ArchSubNode, p as BucketableViolation, q as BusFactorChange, r as BusFactorRow, s as CentralRow, t as CheckRulesFile, u as ComputeArchInput, v as ComputeDriftInput, w as CouplingClass, x as CouplingDelta, y as CouplingRow, D as DEFAULT_HEALTH_WEIGHTS, z as DEFAULT_MAX_PACKAGE_SIZE, H as DeadModuleRow, J as EXTERNAL_BUCKET, K as ForbidImportRule, L as GraphReportResult, M as GrowthRiskRow, O as HealthComponent, P as HealthComponentKey, Q as HealthInput, U as HealthWeights, W as HotExport, X as HotspotDelta, Y as HotspotRow, Z as LayeredDepsRule, _ as LinkMethod, $ as LinkTestsOptions, a0 as MetricMaxRule, a1 as MetricMinRule, a2 as MetricOutlierRule, a3 as MetricProductMaxRule, a4 as NewHotspot, a5 as NoInternalOnlyBarrelsRule, a6 as PackageFlag, a7 as PackageLayer, a8 as PackageRoot, a9 as PackageStats, aa as PairCoupling, ab as PairFlag, ac as PartitionQualityInput, ad as PartitionQualityResult, ae as PenaltyWeight, af as ReportContext, ag as ReportContextInput, ah as ReportDrift, ai as SnapshotContext, aj as SymbolConsumerGroup, ak as SymbolConsumerRow, al as SymbolCouplingPayload, am as SymbolCouplingRow, an as SymbolUtil, ao as TestCoverageRow, ap as UnchangedViolation, aq as UntestedRiskRow, ar as UnusedExportRow, as as ViolationIdentity, at as aggregateEdges, au as bucketFilesByPackage, av as bucketViolations, aw as buildBlastRadius, ax as buildCentralFiles, ay as buildHotExports, az as buildNodeMetrics, aA as buildReportContext, aB as buildSymbolCouplingPayload, aC as busFactorOf, aD as classifyCoupling, aE as collectNodeMetrics, aF as collectSymbolUtil, aG as computeArch, aH as computeHealth, aI as computePartitionQuality, aJ as computeReportDrift, aK as computeSymbolConsumers, aL as computeSymbolCoupling, aM as filteredFileIds, aN as groupTestsBySource, aO as hotspotScoreOf, aP as invertBuckets, aQ as keepNode, aR as linkTestsToSources, aS as lookupMetric, aT as packagesReferencedByEdges, aU as pairKey, aV as publicApiFiles, aW as rebasedViolationKey, aX as referencedNodes, aY as testCoverageCountMetrics, aZ as toSortedEdges, a_ as topBusFactorRisks, a$ as topCentralFiles, b0 as topDeadModules, b1 as topGrowthRisks, b2 as topHotspots, b3 as topTestCoverageRisks, b4 as topUntestedRisks, b5 as topUnusedExports, b6 as violationKey } from './browser-BBzShneN.js';
1
3
  import { Db, Migration } from '@titan-design/store-sqlite';
4
+ import { FileSystemHost, Project } from 'ts-morph';
2
5
  import { Extractor, ParsedFile } from '@titan-design/code-parser';
3
6
  export { Extractor, ParsedFile, getLanguageFromPath, getSupportedLanguages, parseFile, shouldIncludeFile } from '@titan-design/code-parser';
4
- import { Project } from 'ts-morph';
5
- import { c as CoEditPair, C as ChurnEntry, a as ChurnWindow } from './change-coupling-CyqHgRsm.js';
7
+ import { C as ChurnEntry, a as ChurnWindow } from './change-coupling-CT3bcZMD.js';
6
8
  import { Embedder } from '@titan-design/embed';
7
9
 
8
- type NodeKind = "package" | "module" | "file" | "symbol" | "external";
9
- type EdgeKind = "imports" | "re-exports" | "calls" | "extends" | "implements" | "references" | "depends-on";
10
- /** `requalify` maps a bare-name symbol id from before index version 0.14.0 to its scope-qualified successor. */
11
- type IdAliasReason = "rename" | "move" | "merge" | "requalify";
12
- type NodeRole = "test" | "fixture" | "barrel" | "types" | "config" | "script" | "entry" | "generated" | "source";
13
- interface GraphNode {
14
- id: string;
15
- kind: NodeKind;
16
- name: string;
17
- parentId?: string;
18
- language?: string;
19
- role?: NodeRole;
20
- attrs?: Record<string, unknown>;
21
- }
22
- interface GraphEdge {
23
- srcId: string;
24
- dstId: string;
25
- kind: EdgeKind;
26
- attrs?: Record<string, unknown>;
27
- }
28
- interface GraphMetric {
29
- nodeId: string;
30
- name: string;
31
- value: number | null;
32
- unit?: string;
33
- }
34
- interface GraphFragment {
35
- nodes: GraphNode[];
36
- edges: GraphEdge[];
37
- }
38
- interface SnapshotRow {
39
- id: number;
40
- ref: string;
41
- commitHash: string | null;
42
- takenAt: string;
43
- indexVersion: string;
44
- attrs: Record<string, unknown>;
45
- }
46
- interface IdAlias {
47
- oldId: string;
48
- newId: string;
49
- reason: IdAliasReason;
50
- }
51
- interface FileFingerprint {
52
- fileId: string;
53
- contentHash: string;
54
- /**
55
- * Comment/whitespace-insensitive parse-structure hash (C-18). Absent on
56
- * snapshots written before C-18 (they can only reuse whole unchanged files).
57
- */
58
- structuralHash?: string;
59
- }
60
-
61
10
  /** One metric's values over the nodes of one kind in one snapshot; `count` excludes null values. */
62
11
  interface MetricAggregate {
63
12
  name: string;
@@ -95,6 +44,13 @@ declare class CodeGraphStore {
95
44
  private readonly targeted;
96
45
  constructor(db: Db);
97
46
  createSnapshot(input: SnapshotInsert): number;
47
+ /**
48
+ * Run `write` in one transaction so a reader on another connection sees a new
49
+ * snapshot only once every row written for it is committed, never a snapshot
50
+ * row whose nodes or edges are still being inserted. The per-table inserts
51
+ * nest as savepoints inside it.
52
+ */
53
+ atomically<T>(write: () => T): T;
98
54
  insertNodes(snapshotId: number, nodes: readonly GraphNode[]): void;
99
55
  insertEdges(snapshotId: number, edges: readonly GraphEdge[]): void;
100
56
  insertMetrics(snapshotId: number, metrics: readonly GraphMetric[]): void;
@@ -185,7 +141,7 @@ declare const FINDING_DDL = "\n CREATE TABLE IF NOT EXISTS finding (\n snaps
185
141
  declare const SNAPSHOT_SCOPED_TABLES: readonly ["node", "edge", "metric", "id_alias", "file_fingerprint", "finding", "verdict"];
186
142
  declare const MIGRATIONS: Migration[];
187
143
  /** The top of this package's schema. A code graph database is never shared, so this is the top of one. */
188
- declare const SCHEMA_VERSION: number;
144
+ declare const SCHEMA_VERSION$1: number;
189
145
 
190
146
  interface PrunePlan {
191
147
  keep: SnapshotRow[];
@@ -206,12 +162,40 @@ declare function runPrune(store: CodeGraphStore, options?: PruneOptions & {
206
162
  vacuum?: boolean;
207
163
  }): PruneResult;
208
164
 
165
+ /**
166
+ * Where the indexer reads a tree from. Every path is absolute and node ids are
167
+ * derived from it, so a source that is not the working tree (a git commit, say)
168
+ * keeps ids byte-identical by answering for the same absolute paths.
169
+ */
170
+ interface IndexSource {
171
+ /**
172
+ * Source files under `rootDirs` that pass the ingest filter for `languages`,
173
+ * deduped and sorted, so float metrics summed in file order match across sources.
174
+ */
175
+ listFiles(rootDirs: readonly string[], languages: readonly string[]): Promise<string[]>;
176
+ /** UTF-8 content of a file; throws when it does not exist. */
177
+ readFile(abs: string): string;
178
+ fileExists(abs: string): boolean;
179
+ /** The host ts-morph resolves imports and reads tsconfig through. */
180
+ readonly fileSystem: FileSystemHost;
181
+ /** Set when the source is a git commit rather than the working tree; undefined for the working tree. */
182
+ readonly revision?: {
183
+ /** The repo's toplevel, canonicalized with realpath. */
184
+ readonly repoRoot: string;
185
+ readonly commit: string;
186
+ /** Committer time, seconds since the epoch. */
187
+ readonly commitEpoch: number;
188
+ };
189
+ }
190
+ /** The checked-out working tree, read through `node:fs`. The default source. */
191
+ declare function workingTreeSource(): IndexSource;
192
+
209
193
  /**
210
194
  * Bumping this invalidates every reuse basis: a snapshot written by a different
211
195
  * index version is never reused, so a change to node/edge shape or to a metric's
212
196
  * value for the same bytes can never be carried forward from an incompatible graph.
213
197
  */
214
- declare const INDEX_VERSION = "0.18.0";
198
+ declare const INDEX_VERSION = "0.22.0";
215
199
  interface IndexOptions {
216
200
  /** Roots to walk. Node ids are still rooted at the git toplevel, so importers across roots share an id space. */
217
201
  paths: string[];
@@ -235,6 +219,8 @@ interface IndexOptions {
235
219
  churnWindows?: number[];
236
220
  /** Also store an all-time `lifetime` churn and ownership window over full git history. */
237
221
  lifetime?: boolean;
222
+ /** Where files are listed and read from. Defaults to {@link workingTreeSource}. */
223
+ source?: IndexSource;
238
224
  }
239
225
  interface IndexResult {
240
226
  snapshotId: number;
@@ -260,9 +246,19 @@ interface IndexResult {
260
246
  */
261
247
  declare function indexPaths(store: CodeGraphStore, options: IndexOptions): Promise<IndexResult>;
262
248
 
249
+ /**
250
+ * Index `rev`'s tree from git objects, without a checkout and without writing to
251
+ * the repo. Files keep their `<repo root>/<tree path>` absolute paths, so ids
252
+ * match a working-tree index of the same commit. Workspace links into the repo
253
+ * resolve to the commit; installed packages and untracked build output such as
254
+ * a gitignored dist/ resolve against today's disk.
255
+ */
256
+ declare function gitTreeSource(repoRoot: string, rev: string): IndexSource;
257
+
263
258
  interface LanguageExtractorOptions {
264
259
  repoRoot: string;
265
260
  tsConfigPath?: string;
261
+ source?: IndexSource;
266
262
  }
267
263
  /**
268
264
  * One extractor over every supported language: each delegate already returns an
@@ -285,8 +281,9 @@ declare class LanguageExtractor implements Extractor<GraphFragment> {
285
281
  */
286
282
  declare class PythonGraphExtractor implements Extractor<GraphFragment> {
287
283
  private readonly repoRoot;
284
+ private readonly source;
288
285
  readonly name = "python-graph";
289
- constructor(repoRoot: string);
286
+ constructor(repoRoot: string, source?: IndexSource);
290
287
  extract(file: ParsedFile): GraphFragment[];
291
288
  private collectImports;
292
289
  /**
@@ -301,14 +298,21 @@ interface TsMorphGraphExtractorOptions {
301
298
  repoRoot: string;
302
299
  tsConfigPath?: string;
303
300
  project?: Project;
301
+ source?: IndexSource;
304
302
  }
305
303
  declare class TsMorphGraphExtractor implements Extractor<GraphFragment> {
306
304
  readonly name = "ts-morph-graph";
307
305
  private readonly repoRoot;
308
306
  private readonly tsConfigPath?;
309
307
  private project?;
308
+ private readonly ownsProject;
309
+ private readonly source;
310
+ private readonly retired;
311
+ private readonly fileExists;
310
312
  constructor(options: TsMorphGraphExtractorOptions);
311
313
  extract(file: ParsedFile): GraphFragment[];
314
+ private retire;
315
+ private extractFrom;
312
316
  private ensureProject;
313
317
  private loadSourceFile;
314
318
  private buildFileAndModuleNodes;
@@ -411,10 +415,6 @@ interface DeepAstInput {
411
415
  /** Deep structural facts for one target, or null when the source is unreadable. */
412
416
  declare function computeDeepAst(input: DeepAstInput): DeepAst | null;
413
417
 
414
- declare function fileId(repoRoot: string, absPath: string): string;
415
- declare function moduleId(repoRoot: string, absPath: string): string;
416
- declare function parentModuleId(id: string): string | null;
417
- declare function packageId(name: string): string;
418
418
  declare const SYMBOL_ID_SEP = "#";
419
419
  declare function symbolId(fileId: string, qualifiedName: string): string;
420
420
  /**
@@ -427,6 +427,12 @@ declare function parseSymbolId(id: string): {
427
427
  fileId: string;
428
428
  name: string;
429
429
  } | null;
430
+
431
+ declare function fileId(repoRoot: string, absPath: string): string;
432
+ declare function moduleId(repoRoot: string, absPath: string): string;
433
+ declare function parentModuleId(id: string): string | null;
434
+ declare function packageId(name: string): string;
435
+
430
436
  declare function externalId(specifier: string): string;
431
437
 
432
438
  /** SHA-256 of file content. The fingerprint that gates per-file reuse. */
@@ -449,7 +455,7 @@ interface ReadFile {
449
455
  hash: string;
450
456
  }
451
457
  /** Read + hash every source file. Cheap I/O; the parse/extract it gates is not. */
452
- declare function readSourceFiles(filePaths: readonly string[]): Promise<ReadFile[]>;
458
+ declare function readSourceFiles(filePaths: readonly string[], source?: IndexSource): Promise<ReadFile[]>;
453
459
  /**
454
460
  * Everything from a prior snapshot needed to rebuild an unchanged file's
455
461
  * contribution to the graph without re-parsing it: its content fingerprint,
@@ -530,12 +536,22 @@ declare function pruneDanglingReferences(nodes: ReadonlyMap<string, GraphNode>,
530
536
  */
531
537
  declare function resolveBarrelEdges(nodes: readonly GraphNode[], edges: readonly GraphEdge[]): GraphEdge[];
532
538
 
539
+ /**
540
+ * Repo-configured role globs, read from `.codewatch/roles.json`. They label
541
+ * paths no filename convention can (a design system's `lab/` playground is
542
+ * too generic a directory name to guess), and they beat the built-in role
543
+ * heuristics. Globs use `.gitattributes` syntax, matched against file ids.
544
+ */
545
+ type RoleGlobs = Partial<Record<NodeRole, string[]>>;
546
+
533
547
  declare const ALL_ROLES: readonly NodeRole[];
534
548
  interface RoleHints {
535
549
  /** File begins with a `#!` shebang, i.e. it is an executable entry point. */
536
550
  hasShebang?: boolean;
537
551
  /** File is codegen output (`.gitattributes linguist-generated` or heuristic). */
538
552
  isGenerated?: boolean;
553
+ /** Role a `.codewatch/roles.json` glob assigns this file. */
554
+ configuredRole?: NodeRole;
539
555
  }
540
556
  declare function classifyRole(id: string, hints?: RoleHints): NodeRole;
541
557
  interface AnnotateRolesOptions {
@@ -543,7 +559,11 @@ interface AnnotateRolesOptions {
543
559
  shebangIds?: ReadonlySet<string>;
544
560
  /** Node ids detected as generated (codegen output). */
545
561
  generatedIds?: ReadonlySet<string>;
562
+ /** Configured role globs; see {@link loadRoleGlobs}. */
563
+ roleGlobs?: RoleGlobs;
546
564
  }
565
+ /** Read `<rootDir>/.codewatch/roles.json`; a repo without one configures no globs. */
566
+ declare function loadRoleGlobs(rootDir: string, source?: IndexSource): RoleGlobs;
547
567
  interface ReadFileLike {
548
568
  filePath: string;
549
569
  content: string;
@@ -554,9 +574,17 @@ interface ReadFileLike {
554
574
  * `.gitattributes linguist-generated` under `idRoot` plus filename/path
555
575
  * heuristics). Lives here so the indexer stays at its file-size ceiling.
556
576
  */
557
- declare function computeRoleHints(readFiles: readonly ReadFileLike[], idRoot: string, toId: (root: string, filePath: string) => string): AnnotateRolesOptions;
577
+ declare function computeRoleHints(readFiles: readonly ReadFileLike[], idRoot: string, toId: (root: string, filePath: string) => string, source?: IndexSource): AnnotateRolesOptions;
558
578
  declare function annotateRoles(nodes: readonly GraphNode[], options?: AnnotateRolesOptions): GraphNode[];
559
579
 
580
+ /**
581
+ * Roles of files nothing imports by design, so reachability and dead-export
582
+ * reports treat them as roots. One list, typed by role, so a new role cannot be
583
+ * added to one report and missed in another. It lives apart from `roles.ts`,
584
+ * which reads files, so browser-safe report code can import it.
585
+ */
586
+ declare const UNIMPORTED_ROLES: ReadonlySet<NodeRole>;
587
+
560
588
  /** True when a path looks generated by filename/dir convention alone. */
561
589
  declare function isGeneratedByHeuristic(id: string): boolean;
562
590
  /**
@@ -566,7 +594,7 @@ declare function isGeneratedByHeuristic(id: string): boolean;
566
594
  */
567
595
  declare function isGeneratedFile(id: string, patterns: readonly RegExp[]): boolean;
568
596
  /** Read `<rootDir>/.gitattributes` and compile its linguist-generated patterns. */
569
- declare function loadGeneratedPatterns(rootDir: string): RegExp[];
597
+ declare function loadGeneratedPatterns(rootDir: string, source?: IndexSource): RegExp[];
570
598
 
571
599
  declare function canonicalMetricName(name: string): string;
572
600
  declare function canonicalRole(name: string): NodeRole;
@@ -695,36 +723,6 @@ declare const SYMBOL_METRIC_NAMES: readonly string[];
695
723
  */
696
724
  declare const EXCEPTION_METRIC_NAMES: readonly string[];
697
725
 
698
- /** How a test↔source pairing was inferred. */
699
- type LinkMethod = "path" | "coedit";
700
- interface TestSourceLink {
701
- /** Node id of the test file. */
702
- testId: string;
703
- /** Node id of the (non-test) file it covers. */
704
- sourceId: string;
705
- method: LinkMethod;
706
- }
707
- interface LinkTestsOptions {
708
- /** Minimum co-edit count for a pass-2 (coedit) link. Default 2. */
709
- minCoEditCount?: number;
710
- }
711
- /**
712
- * Two-pass test↔source linker. Pass 1 pairs each test file with non-test files
713
- * matching its path conventions (high confidence). Pass 2 supplements tests
714
- * left unpaired by pass 1 with their strongest co-edited non-test partner from
715
- * change-coupling. Handles orphan tests (no pairing), orphan/untested sources
716
- * (no incoming link), and one-to-many pairings (a test matching several
717
- * sources, or a source covered by several tests).
718
- */
719
- declare function linkTestsToSources(nodes: readonly GraphNode[], coEditPairs: readonly CoEditPair[], options?: LinkTestsOptions): TestSourceLink[];
720
- /**
721
- * Per-source coverage breadth: how many distinct test files link to each
722
- * covered source. Emitted only for sources with at least one linked test.
723
- */
724
- declare function testCoverageCountMetrics(links: readonly TestSourceLink[]): GraphMetric[];
725
- /** Map each covered source to the set of test files that link to it. */
726
- declare function groupTestsBySource(links: readonly TestSourceLink[]): Map<string, Set<string>>;
727
-
728
726
  interface HistoryMetricsOptions {
729
727
  /** Primary window: scopes ownership. Default 30. */
730
728
  churnWindowDays?: number;
@@ -734,6 +732,8 @@ interface HistoryMetricsOptions {
734
732
  includeLifetime?: boolean;
735
733
  /** Epoch seconds that windows end at; defaults to the current time. */
736
734
  nowEpoch?: number;
735
+ /** Read history up to this rev instead of HEAD; pair it with the rev's commit time as `nowEpoch`. */
736
+ rev?: string;
737
737
  }
738
738
  /** Windows the dashboard switcher offers; churn is stored for each by default. */
739
739
  declare const DEFAULT_CHURN_WINDOWS: number[];
@@ -866,6 +866,29 @@ interface SymbolSpan {
866
866
  */
867
867
  declare function attributeCoverage(coverage: IstanbulCoverage, fileIdOf: (absPath: string) => string | null, symbolsByFile: ReadonlyMap<string, readonly SymbolSpan[]>): GraphMetric[];
868
868
 
869
+ type MinifyLanguage = "typescript" | "tsx" | "python";
870
+ /**
871
+ * Minified source plus its line map. `lineMap[i]` is the 1-based original line
872
+ * of output line `i + 1`; `originalLine` reads it by output line number.
873
+ */
874
+ interface MinifiedSource {
875
+ text: string;
876
+ lineMap: number[];
877
+ }
878
+ /** The one line that stands in for a leading import block. */
879
+ declare function importMarker(count: number, language: MinifyLanguage): string;
880
+ /** The 1-based original line of a 1-based output line, or undefined past the end. */
881
+ declare function originalLine(minified: MinifiedSource, outputLine: number): number | undefined;
882
+ declare function isMinifyLanguage(language: string): language is MinifyLanguage;
883
+ /**
884
+ * Safe-minify source for LLM context: drops comments and docstrings, strips
885
+ * trailing whitespace, collapses blank-line runs and replaces a leading import
886
+ * block with `importMarker`. It never renames or dedents, so every kept line is
887
+ * its original line minus comments. Any other language comes back unchanged
888
+ * with an identity line map.
889
+ */
890
+ declare function minifySource(text: string, language: string): Promise<MinifiedSource>;
891
+
869
892
  /** Metric-name suffix for a window: `30d`, `180d`, or `lifetime`. */
870
893
  declare function windowSuffix(window: ChurnWindow): string;
871
894
  /**
@@ -929,159 +952,237 @@ interface RelevanceOptions {
929
952
  */
930
953
  declare function computeRelevance(nodes: readonly GraphNode[], edges: readonly GraphEdge[], seedIds: readonly string[], options?: RelevanceOptions): Map<string, number>;
931
954
 
955
+ /** Marks a gap between two symbols that are not adjacent in their file. */
956
+ declare const ELISION_MARKER = "...";
957
+ /** Ends a line cut at `maxLineChars`; it counts toward the limit. */
958
+ declare const TRUNCATION_SUFFIX = "\u2026";
959
+ declare const DEFAULT_MAX_LINE_CHARS = 120;
960
+ interface SignatureTreeOptions {
961
+ maxLineChars?: number;
962
+ }
932
963
  /**
933
- * Symbol-level change coupling (C-60). Decomposes a god-file the file-level
934
- * coupling view rolls up as one blob (e.g. `types.ts`) into *which symbol* is
935
- * used where. Built on the C-53 `references` edge substrate: `src` is the
936
- * importing file, `dst` is the imported symbol node id (`<fileId>#<name>`).
964
+ * Render symbols as a signatures-only tree for LLM context: per file (sorted by
965
+ * path) a header line, then one line per symbol, with `ELISION_MARKER` between
966
+ * symbols that are not adjacent. Pure; output is independent of input order.
937
967
  *
938
- * Two slices, both pure functions of the assembled reference edge set (a
939
- * whole-graph rollup like utilization/PageRank, sound under incremental reuse
940
- * since it reads the reassembled edges, not a per-file cache):
941
- *
942
- * - **Slice C — per-symbol consumers**: invert the edges by `dst`, giving the
943
- * set of files that import each symbol. Directly answers "what IN this file is
944
- * used where"; covers span-less types/consts that a git-hunk approach cannot.
945
- * - **Slice B — co-import coupling**: group edges by `src`, and every pair of
946
- * symbols co-imported by the same file is a coupling pair. Two symbols that
947
- * are always imported together travel together — structural (used-together),
948
- * drift-free coupling, as opposed to the temporal (changed-together) git
949
- * co-edit signal.
950
- */
951
- /** The minimal shape of a `references` edge this module consumes. */
952
- interface ReferenceEdgeLite {
953
- /** Importing file id. */
954
- srcId: string;
955
- /** Imported symbol node id (`<fileId>#<name>`). */
956
- dstId: string;
957
- }
958
- /** One symbol's consumer set (Slice C). */
959
- interface SymbolConsumers {
960
- symbolId: string;
961
- /** Declaring file, parsed from the symbol id. */
962
- fileId: string;
963
- /** Export name. */
968
+ * A line is the symbol's `attrs.signature`, else `<name> (<kind>)`. Symbols
969
+ * order by `attrs.startLine`, then name, then id. A qualified name
970
+ * (`Job.run`) indents one level per dot. Symbols with no `startLine`
971
+ * (exported types and consts) render after the line-ordered ones, sorted by
972
+ * name then id, and get no elision markers because their position is unknown.
973
+ */
974
+ declare function renderSignatureTree(nodes: readonly GraphNode[], options?: SignatureTreeOptions): string;
975
+
976
+ /**
977
+ * C-74 — deterministic per-file / per-symbol **context dossier** (Class A). A
978
+ * pure projection of the already-computed graph: no LLM, no source re-parse. The
979
+ * command layer (graph-context.ts) loads the snapshot and hands the assembled
980
+ * inputs here; this module owns only the shaping + the markdown render, so it is
981
+ * unit-testable on synthetic graphs.
982
+ */
983
+ /** Bumped when the dossier shape changes, so a RAG store can invalidate records. */
984
+ declare const SCHEMA_VERSION = "2";
985
+ interface Provenance {
986
+ snapshotId: number;
987
+ ref: string;
988
+ commitHash: string | null;
989
+ takenAt: string;
990
+ indexVersion: string;
991
+ }
992
+ /**
993
+ * Consumers split by declaring-file role (C-74/G3): for core exports most inbound
994
+ * references are test files, which is truthful but low-signal for "what production
995
+ * code depends on this" — so source and test consumers are separated, not flattened.
996
+ */
997
+ interface Consumers {
998
+ source: string[];
999
+ test: string[];
1000
+ counts: {
1001
+ source: number;
1002
+ test: number;
1003
+ total: number;
1004
+ };
1005
+ note: string;
1006
+ }
1007
+ interface FileOwnership {
1008
+ primaryOwner: string;
1009
+ busFactor: number;
1010
+ authorCount: number;
1011
+ }
1012
+ /** One of a file's declared symbols, projected for the dossier. */
1013
+ interface SymbolLine {
964
1014
  name: string;
965
- /** Distinct importing file ids, sorted. */
966
- consumers: string[];
967
- }
968
- /** A pair of symbols co-imported by the same file (Slice B). */
969
- interface SymbolCouplingPair {
970
- aId: string;
971
- aFile: string;
972
- aName: string;
973
- bId: string;
974
- bFile: string;
975
- bName: string;
976
- /** Distinct files that import both symbols. */
977
- coImports: number;
978
- /** True when the two symbols are declared in different files. */
979
- crossFile: boolean;
980
- }
981
- interface SymbolCouplingOptions {
982
- /** Skip pairs co-imported by fewer than this many files. Default 2. */
983
- minCoImports?: number;
1015
+ exported: boolean;
1016
+ /** One-line type signature (C-79), when indexed for this declaration. */
1017
+ signature?: string;
1018
+ cognitive?: number;
1019
+ cyclomatic?: number;
1020
+ utilization: number;
1021
+ /** Distinct files that reference this symbol (inbound `references`). */
1022
+ consumers: number;
984
1023
  /**
985
- * Skip importing files that reference more than this many distinct symbols —
986
- * a wide barrel-style importer would otherwise explode into O(n²) noise
987
- * pairs, mirroring change-coupling's large-commit guard. Default 40.
1024
+ * Symbol-level importance (C-89): the declaring file's PageRank centrality
1025
+ * redistributed across its symbols by utilization share, so a graph-central
1026
+ * file's rank lands on the definitions actually carrying its inbound use.
988
1027
  */
989
- largeImporterThreshold?: number;
1028
+ importance?: number;
990
1029
  }
1030
+ interface SymbolDossier {
1031
+ exported: boolean;
1032
+ /** G1 slot — the one-line type signature indexed from `attrs.signature` (C-79); null when unannotated or unresolvable. */
1033
+ signature: string | null;
1034
+ /** G2 slot — leading docstring / intent. Null today. */
1035
+ purpose: string | null;
1036
+ complexity: {
1037
+ cognitive?: number;
1038
+ cyclomatic?: number;
1039
+ };
1040
+ utilization: number;
1041
+ consumers: Consumers;
1042
+ blastRadius: number;
1043
+ coupledWith: {
1044
+ symbolId: string;
1045
+ name: string;
1046
+ fileId: string;
1047
+ coImports: number;
1048
+ }[];
1049
+ }
1050
+ interface FileDossier {
1051
+ metrics: NodeMetrics;
1052
+ churn: {
1053
+ windowDays: number;
1054
+ value: number;
1055
+ } | null;
1056
+ centrality: number;
1057
+ ownership: FileOwnership | null;
1058
+ symbols: SymbolLine[];
1059
+ dependsOn: string[];
1060
+ consumers: Consumers;
1061
+ blastRadius: BlastRadiusEntry[];
1062
+ }
1063
+ interface ContextDossier {
1064
+ schemaVersion: string;
1065
+ target: {
1066
+ id: string;
1067
+ kind: "file" | "symbol";
1068
+ name: string;
1069
+ path: string;
1070
+ span?: {
1071
+ startLine: number;
1072
+ endLine: number;
1073
+ };
1074
+ };
1075
+ provenance: Provenance;
1076
+ symbol?: SymbolDossier;
1077
+ file?: FileDossier;
1078
+ notes: string[];
1079
+ }
1080
+ interface ContextBuildInput {
1081
+ target: GraphNode;
1082
+ kind: "file" | "symbol";
1083
+ provenance: Provenance;
1084
+ /** All nodes for the snapshot, symbols INCLUDED. */
1085
+ nodes: readonly GraphNode[];
1086
+ /** `references` edges as {srcId=importing file, dstId=symbol id}. */
1087
+ refEdges: readonly ReferenceEdgeLite[];
1088
+ /** `imports` edges as {srcId, dstId} (file → file/external). */
1089
+ importEdges: readonly {
1090
+ srcId: string;
1091
+ dstId: string;
1092
+ }[];
1093
+ metrics: ReadonlyMap<string, NodeMetrics>;
1094
+ churnByFile: ReadonlyMap<string, number>;
1095
+ churnWindowDays: number;
1096
+ centrality: ReadonlyMap<string, number>;
1097
+ ownership: ReadonlyMap<string, FileOwnership> | null;
1098
+ /** File id → node role, for splitting consumers into source vs test (G3). */
1099
+ roleByFile: ReadonlyMap<string, string>;
1100
+ }
1101
+ declare function buildContextDossier(input: ContextBuildInput): ContextDossier;
1102
+
991
1103
  /**
992
- * Group reference edges by imported symbol, yielding each symbol's distinct
993
- * consuming files (Slice C). Sorted by consumer count desc, then symbol id, so
994
- * the most broadly-depended-on exports lead.
995
- */
996
- declare function computeSymbolConsumers(edges: readonly ReferenceEdgeLite[]): SymbolConsumers[];
997
- /**
998
- * Every pair of symbols co-imported by the same file becomes a coupling pair,
999
- * counted by how many distinct files co-import them (Slice B). Wide importers
1000
- * are dropped to keep the pairing near-linear. Sorted by co-import count desc,
1001
- * cross-file pairs preferred at a tie (they are the actionable ones — a
1002
- * same-file pair is just cohesion within one module).
1104
+ * Human/agent-readable markdown projection of a {@link ContextDossier} (C-74).
1105
+ * The JSON form is the RAG-store record; this is the same facts rendered for a
1106
+ * reader (or an agent that prefers prose). Deterministic — no LLM.
1003
1107
  */
1004
- declare function computeSymbolCoupling(edges: readonly ReferenceEdgeLite[], options?: SymbolCouplingOptions): SymbolCouplingPair[];
1108
+ declare function renderContextMarkdown(d: ContextDossier): string;
1005
1109
 
1006
1110
  /**
1007
- * Per-package and per-pair structural quality metrics, plus an overall
1008
- * Newman-Girvan modularity Q for the package partition.
1009
- *
1010
- * Operates on a barrel-resolved edge set by default: edges that land on a
1011
- * file with role="barrel" are rewritten to land on the underlying files
1012
- * the barrel re-exports from (transitively). The intent is to measure the
1013
- * real dependency surface, not the re-export plumbing.
1014
- *
1015
- * See C-8 task notes for the design rationale and empirical calibration
1016
- * data from the 2026-05-21 codewatch dogfood.
1111
+ * C-80 — the **context bundle**: one deterministic pull of the complete paired
1112
+ * context for a symbol/file, the push half of the ingestion API. It wraps the
1113
+ * C-74 dossier with the three things an ingestor needs to embed/answer WITHOUT
1114
+ * opening the file: the raw **source chunk** (source text of the span), the
1115
+ * resolved **graph linkages as explicit edges** (callers / dependencies /
1116
+ * coupled-with — not just counts), and **coverage** (C-63, when the overlay is
1117
+ * ingested). No LLM; a pure projection of the graph + the working-tree source.
1017
1118
  */
1018
- interface PartitionQualityInput {
1019
- /** Logical packages — at minimum needs an id. */
1020
- packages: ReadonlyArray<{
1021
- id: string;
1022
- }>;
1023
- /** Package id → list of file ids assigned to that package. */
1024
- fileByPackage: ReadonlyMap<string, ReadonlyArray<string>>;
1025
- /** All file/module/external nodes for the snapshot. Used to identify role="barrel". */
1026
- nodes: readonly GraphNode[];
1027
- /** All edges for the snapshot. */
1028
- edges: readonly GraphEdge[];
1029
- /**
1030
- * When true, edges landing on a barrel file are resolved through its
1031
- * re-export chain to the underlying source files. The cheap implementation
1032
- * fans each barrel import into N synthetic edges (one per re-export
1033
- * target), which over-attributes — a single `import { x } from "./pkg"`
1034
- * becomes N edges as if every re-export were used. Default false until
1035
- * a weighted or symbol-tracking version is available.
1036
- */
1037
- resolveBarrels?: boolean;
1038
- }
1039
- type PackageFlag = "weak-boundary";
1040
- type PairFlag = "tight" | "moderate" | "none";
1041
- type PackageLayer = "top" | "middle" | "foundation";
1042
- interface PackageStats {
1043
- pkgId: string;
1044
- fileCount: number;
1045
- internalEdges: number;
1046
- outgoingEdges: number;
1047
- incomingEdges: number;
1048
- /** internal / (internal + outgoing) — higher = more self-contained. */
1049
- cohesion: number;
1050
- /** outgoing / (outgoing + incoming) — Martin's I metric at the package level. */
1051
- instability: number;
1052
- /**
1053
- * Abstractness proxy A ∈ [0,1]: share of the package's files with role
1054
- * "types" (dedicated type/interface definitions). codewatch has no
1055
- * symbol-level abstract/concrete counts, so this file-role ratio stands in
1056
- * for Martin's A. Enables the instability×abstractness main-sequence plot.
1057
- */
1058
- abstractness: number;
1059
- layer: PackageLayer;
1060
- flags: PackageFlag[];
1119
+ declare const BUNDLE_SCHEMA_VERSION = "2";
1120
+ interface SourceChunk {
1121
+ /** Repo-relative file id the chunk was read from. */
1122
+ path: string;
1123
+ /** 1-based inclusive line span for a symbol; null for a whole-file target. */
1124
+ span: {
1125
+ startLine: number;
1126
+ endLine: number;
1127
+ } | null;
1128
+ /** The source text, or null when the file could not be read. */
1129
+ text: string | null;
1130
+ note?: string;
1061
1131
  }
1062
- interface PairCoupling {
1132
+ /** A resolved graph linkage, deterministic and directional relative to the target. */
1133
+ interface BundleEdge {
1063
1134
  from: string;
1064
1135
  to: string;
1065
- edges: number;
1066
- /** edges / files(from) — fraction of from-side files contributing dependencies into `to`. */
1067
- intensity: number;
1068
- flag: PairFlag;
1136
+ kind: string;
1137
+ weight: number;
1138
+ /**
1139
+ * Seeded-PageRank relevance of this edge's neighbour to the target (C-89).
1140
+ * Present only when the target was seeded (the bundle path); the neighbour is
1141
+ * whichever endpoint is not the target's file. Edges are ordered by it, so the
1142
+ * most target-relevant linkages lead, not the globally-famous ones.
1143
+ */
1144
+ relevance?: number;
1145
+ }
1146
+ interface BundleEdges {
1147
+ /** Inbound — who references/imports the target. */
1148
+ callers: BundleEdge[];
1149
+ /** Outbound — what the target references/imports. */
1150
+ dependencies: BundleEdge[];
1151
+ /** Co-import coupling partners (symbol targets only). */
1152
+ coupledWith: BundleEdge[];
1153
+ note: string;
1154
+ }
1155
+ interface Coverage {
1156
+ pct: number | null;
1157
+ note?: string;
1069
1158
  }
1070
- interface PartitionQualityResult {
1071
- modularityQ: number;
1072
- totalEdges: number;
1073
- perPackage: PackageStats[];
1074
- pairCoupling: PairCoupling[];
1075
- /** Total raised flags across packages + pairs (excluding "moderate"). */
1076
- flagsCount: number;
1159
+ interface ContextBundle {
1160
+ schemaVersion: string;
1161
+ dossier: ContextDossier;
1162
+ source: SourceChunk;
1163
+ edges: BundleEdges;
1164
+ coverage: Coverage;
1077
1165
  }
1078
- declare function computePartitionQuality(input: PartitionQualityInput): PartitionQualityResult;
1079
- /**
1080
- * Invert a package→files bucket map into a file→package lookup, skipping the
1081
- * empty-string "unassigned" bucket. Shared by partition-quality and the CLI's
1082
- * arch/wiki package rollups, which all need the same file→package direction.
1083
- */
1084
- declare function invertBuckets(fileByPackage: ReadonlyMap<string, ReadonlyArray<string>>): Map<string, string>;
1166
+ interface BundleBuildInput {
1167
+ dossier: ContextDossier;
1168
+ target: GraphNode;
1169
+ kind: "file" | "symbol";
1170
+ /** Weighted `references` edges (srcId = importing file → dstId = symbol id). */
1171
+ refEdges: readonly GraphEdge[];
1172
+ /** Weighted `imports` edges (file → file/external). */
1173
+ importEdges: readonly GraphEdge[];
1174
+ /** Git toplevel to resolve the source path, or null (read relative to cwd). */
1175
+ repoRoot: string | null;
1176
+ /** `coverage_pct` for the target node, or null when the overlay is absent. */
1177
+ coveragePct: number | null;
1178
+ /** File id → seeded-relevance score (C-89); absent/empty ⇒ weight-ordered cold path. */
1179
+ relevanceByFile?: ReadonlyMap<string, number>;
1180
+ /** The target's declaring file id, for locating each edge's neighbour (C-89). */
1181
+ targetFileId?: string;
1182
+ }
1183
+ declare function buildContextBundle(input: BundleBuildInput): ContextBundle;
1184
+ /** Concatenated text projection — hand this straight to an embedder/LLM. */
1185
+ declare function renderBundleText(bundle: ContextBundle): string;
1085
1186
 
1086
1187
  /**
1087
1188
  * PageRank over one snapshot's file-level graph (symbol nodes and `references`
@@ -1156,111 +1257,6 @@ declare function describeMetrics(names: Iterable<string>): {
1156
1257
  unknown: string[];
1157
1258
  };
1158
1259
 
1159
- type Severity = "error" | "warning";
1160
- interface MetricMaxRule {
1161
- type: "metric-max";
1162
- id: string;
1163
- metric: string;
1164
- max: number;
1165
- kind?: NodeKind;
1166
- severity?: Severity;
1167
- exclude?: string[];
1168
- excludeRoles?: NodeRole[];
1169
- }
1170
- interface MetricMinRule {
1171
- type: "metric-min";
1172
- id: string;
1173
- metric: string;
1174
- min: number;
1175
- kind?: NodeKind;
1176
- severity?: Severity;
1177
- exclude?: string[];
1178
- excludeRoles?: NodeRole[];
1179
- }
1180
- interface MetricProductMaxRule {
1181
- type: "metric-product-max";
1182
- id: string;
1183
- metrics: string[];
1184
- max: number;
1185
- kind?: NodeKind;
1186
- severity?: Severity;
1187
- exclude?: string[];
1188
- excludeRoles?: NodeRole[];
1189
- }
1190
- /** Flags nodes whose value sits strictly above the given percentile of the metric over every node of the kind. */
1191
- interface MetricOutlierRule {
1192
- type: "metric-outlier";
1193
- id: string;
1194
- metric: string;
1195
- kind: NodeKind;
1196
- /** 50 to 100; the threshold interpolates linearly between the two nearest ranked values. */
1197
- percentile: number;
1198
- /** Fewest nodes that must carry the metric before any is judged; defaults to 20. */
1199
- minSample?: number;
1200
- /** A node is flagged only if its value also exceeds this absolute floor, guarding sparse metrics whose percentile sits at or near zero. */
1201
- floor?: number;
1202
- /** When true, rank and gate on the pool of carriers with a non-zero value only; zero-valued nodes are never flagged. */
1203
- rankNonZero?: boolean;
1204
- severity?: Severity;
1205
- }
1206
- interface ForbidImportRule {
1207
- type: "forbid-import";
1208
- id: string;
1209
- from: string;
1210
- to: string;
1211
- severity?: Severity;
1212
- }
1213
- interface LayeredDepsRule {
1214
- type: "layered-deps";
1215
- id: string;
1216
- layers: string[][];
1217
- severity?: Severity;
1218
- }
1219
- interface NoInternalOnlyBarrelsRule {
1220
- type: "no-internal-only-barrels";
1221
- id: string;
1222
- /** Path prefixes marking package roots; node ids carry no intrinsic package membership. */
1223
- packageRoots: string[];
1224
- severity?: Severity;
1225
- /** Globs or substrings to skip, such as CLI bin entries the role classifier calls barrels. */
1226
- exclude?: string[];
1227
- }
1228
- type CheckRule = MetricMaxRule | MetricMinRule | MetricProductMaxRule | MetricOutlierRule | ForbidImportRule | LayeredDepsRule | NoInternalOnlyBarrelsRule;
1229
- interface CheckRulesFile {
1230
- rules: CheckRule[];
1231
- }
1232
- interface CheckViolation {
1233
- ruleId: string;
1234
- severity: Severity;
1235
- nodeId: string;
1236
- message: string;
1237
- metric?: string;
1238
- value?: number;
1239
- threshold?: number;
1240
- destinationId?: string;
1241
- isCarryover?: boolean;
1242
- /** Repo-relative file the violation sits in; a symbol's parent file. */
1243
- path?: string;
1244
- lineStart?: number;
1245
- lineEnd?: number;
1246
- symbol?: string;
1247
- /** One line a reader can check without re-running the rule, such as `loc=412 (max 350)`. */
1248
- evidence?: string;
1249
- tool?: string;
1250
- }
1251
- interface CheckResult {
1252
- snapshotId: number;
1253
- baselineSnapshotId?: number;
1254
- rulesEvaluated: number;
1255
- nodesEvaluated: number;
1256
- violations: CheckViolation[];
1257
- newErrors: number;
1258
- newWarnings: number;
1259
- carryoverErrors: number;
1260
- carryoverWarnings: number;
1261
- passed: boolean;
1262
- }
1263
-
1264
1260
  /** The three whole-snapshot reads rule evaluation needs; any store with them can be checked. */
1265
1261
  type RuleStore = Pick<CodeGraphStore, "listNodes" | "listEdges" | "listMetrics">;
1266
1262
 
@@ -1272,10 +1268,6 @@ interface RunChecksOptions {
1272
1268
  declare function runChecks(store: CodeGraphStore, options: RunChecksOptions): CheckResult;
1273
1269
  /** Every rule's violations in one snapshot with no baseline; needs only the three whole-snapshot reads. */
1274
1270
  declare function snapshotViolations(store: RuleStore, snapshotId: number, rules: readonly CheckRule[]): CheckViolation[];
1275
- /** Identity of a violation across snapshots: severity, value and message may change, the key does not. */
1276
- declare function violationKey(v: CheckViolation): string;
1277
- /** {@link violationKey} with both node ids carried into another snapshot's id space; unmoved ids key as before. */
1278
- declare function rebasedViolationKey(v: CheckViolation, resolve: (id: string) => string): string;
1279
1271
 
1280
1272
  /** One tool-neutral audit finding; every finding cites a path so a citation checker can verify it. */
1281
1273
  interface Finding {
@@ -1423,6 +1415,27 @@ interface GraphDiffSummary {
1423
1415
  removedEdges: number;
1424
1416
  metricChanges: number;
1425
1417
  }
1418
+ /** Why a symbol present in both snapshots changed; `renamed` alone means its footprint held. */
1419
+ type FootprintChangeReason = "signature" | "consumers" | "coupling" | "renamed";
1420
+ interface FootprintChange {
1421
+ /** The to-snapshot id; a removed symbol keeps its from-snapshot id. */
1422
+ symbolId: string;
1423
+ /** The from-snapshot id, set only when it differs from `symbolId`. */
1424
+ previousId?: string;
1425
+ status: "added" | "removed" | "changed";
1426
+ /** Empty for an added or removed symbol. */
1427
+ reasons: FootprintChangeReason[];
1428
+ /** The declaring file, in the to-snapshot's id space where it still exists there. */
1429
+ fileId: string;
1430
+ }
1431
+ interface FootprintDiff {
1432
+ fromSnapshotId: number;
1433
+ toSnapshotId: number;
1434
+ /** Sorted by `symbolId`; an unchanged symbol is absent. */
1435
+ changes: FootprintChange[];
1436
+ /** Sorted distinct declaring files of `changes`; consumer files never appear here. */
1437
+ files: string[];
1438
+ }
1426
1439
  interface GraphDiff {
1427
1440
  summary: GraphDiffSummary;
1428
1441
  addedNodes: GraphNode[];
@@ -1443,20 +1456,10 @@ interface DiffSnapshotsOptions {
1443
1456
  */
1444
1457
  declare function diffSnapshots(store: CodeGraphStore, options: DiffSnapshotsOptions): GraphDiff;
1445
1458
 
1446
- interface UnchangedViolation {
1447
- from: CheckViolation;
1448
- to: CheckViolation;
1449
- delta: number | null;
1450
- }
1451
- interface CheckDiff {
1459
+ interface CheckDiff extends ViolationBuckets {
1452
1460
  fromSnapshotId: number;
1453
1461
  toSnapshotId: number;
1454
1462
  rulesEvaluated: number;
1455
- newViolations: CheckViolation[];
1456
- resolvedViolations: CheckViolation[];
1457
- unchanged: UnchangedViolation[];
1458
- worsened: UnchangedViolation[];
1459
- improved: UnchangedViolation[];
1460
1463
  }
1461
1464
  interface DiffCheckResultsOptions {
1462
1465
  fromSnapshotId: number;
@@ -1511,6 +1514,56 @@ declare function computeFootprints(graph: FootprintGraph, options?: FootprintOpt
1511
1514
  */
1512
1515
  declare function symbolSetHash(unit: FootprintUnit, footprints: ReadonlyMap<string, SymbolFootprint>): string;
1513
1516
 
1517
+ interface DiffFootprintsOptions extends FootprintOptions {
1518
+ fromSnapshotId: number;
1519
+ toSnapshotId: number;
1520
+ }
1521
+ /**
1522
+ * Symbols whose documentable footprint differs between two snapshots. From-snapshot ids
1523
+ * follow the alias chain first, so a moved file reads as `renamed`, not a remove plus an add.
1524
+ */
1525
+ declare function diffFootprints(store: CodeGraphStore, options: DiffFootprintsOptions): FootprintDiff;
1526
+
1527
+ /**
1528
+ * What a consumer generated a unit's doc from. Provenance lives with the consumer, not the
1529
+ * store, so it survives snapshot prune.
1530
+ */
1531
+ interface UnitProvenance {
1532
+ unitId: string;
1533
+ /** The to-snapshot's commit hash; null for a working-tree snapshot. */
1534
+ commit: string | null;
1535
+ symbolSetHash: string;
1536
+ /** The generator the consumer used; the gate records it but never calls one. */
1537
+ model: string | null;
1538
+ }
1539
+ interface UnitProvenanceInput {
1540
+ unit: FootprintUnit;
1541
+ footprints: ReadonlyMap<string, SymbolFootprint>;
1542
+ snapshot: Pick<SnapshotRow, "commitHash">;
1543
+ model?: string | null;
1544
+ }
1545
+ type RegenerateReason = "new" | "changed";
1546
+ interface UnitRegeneration {
1547
+ unitId: string;
1548
+ reason: RegenerateReason;
1549
+ }
1550
+ interface GateUnitsInput {
1551
+ prior: readonly UnitProvenance[];
1552
+ units: readonly FootprintUnit[];
1553
+ footprints: ReadonlyMap<string, SymbolFootprint>;
1554
+ }
1555
+ /** An empty `regenerate` means the caller makes no LLM call. */
1556
+ interface GateResult {
1557
+ regenerate: UnitRegeneration[];
1558
+ skip: string[];
1559
+ /** Prior units that no longer exist. */
1560
+ orphaned: string[];
1561
+ }
1562
+ /** A fresh provenance record for one unit at the given snapshot. */
1563
+ declare function unitProvenance(input: UnitProvenanceInput): UnitProvenance;
1564
+ /** Splits units into those whose symbol-set hash moved since the prior record and those that did not. */
1565
+ declare function gateUnits(input: GateUnitsInput): GateResult;
1566
+
1514
1567
  interface EmbeddableSymbol {
1515
1568
  id: string;
1516
1569
  name: string;
@@ -1727,4 +1780,4 @@ declare function getConventionMap(store: CodeGraphStore, snapshotId: number, mod
1727
1780
  */
1728
1781
  declare function findConventions(store: CodeGraphStore, snapshotId: number, query: string, embedder: Embedder, model: string, opts?: FindConventionsOptions): Promise<ConventionQueryResult>;
1729
1782
 
1730
- export { ALIAS_BASE_ATTR, ALL_ROLES, type AliasChain, type AliasChainInput, type AliasChainOptions, type AliasLoader, type AliasResolution, COMMUNITY_SUMMARY_NAMESPACE, COVERAGE_METRIC_NAME, type CachedEmbedResult, type CheckDiff, type CheckResult, type CheckRule, type CheckRulesFile, type CheckSnapshotOptions, type CheckSnapshotResult, type CheckViolation, CodeGraphStore, type ConventionArea, type ConventionCorpus, type ConventionCoverage, type ConventionMap, type ConventionMatch, type ConventionOptions, type ConventionQueryResult, type ConventionSymbol, DEAD_CODE_METRIC_NAMES, DEFAULT_CHURN_WINDOWS, DEFAULT_OUTLIER_MIN_SAMPLE, DOMAIN_DDL, type DeepAst, type DeepAstInput, type DiffCheckResultsOptions, type DiffSnapshotsOptions, EXCEPTION_METRIC_NAMES, type EdgeKind, type EmbedAttempt, type EmbedCoverage, type EmbedSnapshotResult, type EmbeddableSymbol, type ExternalDiagnostic, FINDING_DDL, type FileFingerprint, type FindConventionsOptions, type FindSimilarOptions, type Finding, type FindingKeyInput, type FootprintGraph, type FootprintOptions, type FootprintParts, type FootprintUnit, type ForbidImportRule, GROWTH_RISK_METRIC_NAMES, type GraphDiff, type GraphDiffSummary, type GraphEdge, type GraphFragment, type GraphMetric, type GraphNode, type HistoryMetricsOptions, INDEX_VERSION, type IdAlias, type IdAliasReason, type IndexOptions, type IndexResult, type IstanbulCoverage, KIT, LanguageExtractor, type LanguageExtractorOptions, type LayeredDepsRule, type LineSpan, type Lineage, type LineageSnapshot, type LineageStep, type LinkMethod, type LinkTestsOptions, type LoadedHistory, METRIC_CATALOGUE, MIGRATIONS, type MemberInfo, type MetricAbsence, type MetricAggregate, type MetricDelta, type MetricDescriptor, type MetricDirection, type MetricMaxRule, type MetricMinRule, type MetricOutlierRule, type MetricProductMaxRule, type MetricRollup, type MetricSource, type MetricUnit, type NoInternalOnlyBarrelsRule, type NodeKind, type NodeRename, type NodeRole, type PackageFlag, type PackageLayer, type PackageStats, type PageRankOptions, type PageRankResult, type PageRankRow, type PairCoupling, type PairFlag, type ParamInfo, type PartitionQualityInput, type PartitionQualityResult, type PriorSnapshotOptions, type PruneOptions, type PrunePlan, type PruneResult, PythonGraphExtractor, type ReadFile, type ReferenceEdgeLite, type RelevanceOptions, type ResolveAliasOptions, type ReuseBasis, type RuleStore, type RunChecksOptions, SCHEMA_VERSION, SNAPSHOT_SCOPED_TABLES, SOURCE_METRIC_NAMES, SYMBOL_EMBEDDING_NAMESPACE, SYMBOL_ID_SEP, SYMBOL_METRIC_NAMES, type Severity, type SimilarCandidate, type SimilarResult, type SnapshotInsert, type SnapshotRow, type SnapshotSpec, type SourceLanguage, type StoredFinding, type StoredVerdict, type SummarizeConventionsResult, type Summarizer, type SymbolConsumers, type SymbolCouplingOptions, type SymbolCouplingPair, type SymbolFootprint, type SymbolSpan, type TestCoverageOwnershipOptions, type TestSourceLink, type TopMetricRow, TsMorphGraphExtractor, type TsMorphGraphExtractorOptions, type UnchangedViolation, type ValidateRulesOptions, type VerdictCitation, type VerdictLabel, aggregateMetrics, aliasChain, annotateRoles, attributeCoverage, buildAliases, buildConventionAreas, buildEmbedText, buildFileModuleNodes, buildIndexerMetrics, buildLineage, canonicalEdgeKind, canonicalMetricName, canonicalRole, carryForwardVerdicts, checkSnapshot, classifyRole, collectDeclaredNames, collectDeclaredSpans, compilePatterns, computeDeadCodeMetrics, computeDeepAst, computeFootprints, computeGrowthRiskMetrics, computeMetrics, computePageRank, computePartitionQuality, computeRecencyWindows, computeRelevance, computeRoleHints, computeSourceMetrics, computeSymbolConsumers, computeSymbolCoupling, computeTestCoverageOwnership, createAliasChain, defaultTargetCount, describeMetric, describeMetrics, detectCommunities, detectGitHead, detectGitToplevel, detectRenames, diffCheckResults, diffSnapshots, edgeWeight, embedSnapshot, embedTextsCached, externalId, externalToFinding, fileId, findConventions, findSimilarCapability, findingKey, getConventionMap, getEdgeWeight, groupTestsBySource, hashContent, hashEmbedText, hashText, indexPaths, invertBuckets, isGeneratedByHeuristic, isGeneratedFile, isInsideGitRepo, keyFindings, lineagePath, linkTestsToSources, listEdges, listEdgesTouching, listEmbeddableSymbols, listFindings, listMetrics, listMetricsForNode, listNodes, listVerdicts, loadCheckRules, loadGeneratedPatterns, loadHistoryMetrics, loadLineage, matchesAny, moduleId, normalizeFlaggedText, openCodeGraph, packageId, parentModuleId, parseSymbolId, patternToRegex, percentileOf, planPrune, priorSnapshotForRef, pruneDanglingReferences, readSourceFiles, rebasedViolationKey, resolveAlias, resolveBarrelEdges, resolveChurnWindows, resolveGitRef, resolveSnapshot, runChecks, runPrune, saveFindings, saveVerdicts, snapshotPageRank, snapshotReferenceEdges, snapshotRelevance, snapshotSymbolConsumers, snapshotSymbolCoupling, snapshotViolations, structuralSignature, summarizeConventions, symbolId, symbolSetHash, testCoverageCountMetrics, toFindings, tryEmbedSnapshot, validateRules, violationKey, walkSourceFiles, windowSuffix };
1783
+ export { ALIAS_BASE_ATTR, ALL_ROLES, type AliasChain, type AliasChainInput, type AliasChainOptions, type AliasLoader, type AliasResolution, type AnnotateRolesOptions, BUNDLE_SCHEMA_VERSION, BlastRadiusEntry, type BundleBuildInput, type BundleEdge, type BundleEdges, COMMUNITY_SUMMARY_NAMESPACE, SCHEMA_VERSION as CONTEXT_SCHEMA_VERSION, COVERAGE_METRIC_NAME, type CachedEmbedResult, type CheckDiff, CheckResult, CheckRule, type CheckSnapshotOptions, type CheckSnapshotResult, CheckViolation, CodeGraphStore, type Consumers, type ContextBuildInput, type ContextBundle, type ContextDossier, type ConventionArea, type ConventionCorpus, type ConventionCoverage, type ConventionMap, type ConventionMatch, type ConventionOptions, type ConventionQueryResult, type ConventionSymbol, type Coverage, DEAD_CODE_METRIC_NAMES, DEFAULT_CHURN_WINDOWS, DEFAULT_MAX_LINE_CHARS, DEFAULT_OUTLIER_MIN_SAMPLE, DOMAIN_DDL, type DeepAst, type DeepAstInput, type DiffCheckResultsOptions, type DiffFootprintsOptions, type DiffSnapshotsOptions, ELISION_MARKER, EXCEPTION_METRIC_NAMES, EdgeKind, type EmbedAttempt, type EmbedCoverage, type EmbedSnapshotResult, type EmbeddableSymbol, type ExternalDiagnostic, FINDING_DDL, type FileDossier, FileFingerprint, type FileOwnership, type FindConventionsOptions, type FindSimilarOptions, type Finding, type FindingKeyInput, type FootprintChange, type FootprintChangeReason, type FootprintDiff, type FootprintGraph, type FootprintOptions, type FootprintParts, type FootprintUnit, GROWTH_RISK_METRIC_NAMES, type GateResult, type GateUnitsInput, type GraphDiff, type GraphDiffSummary, GraphEdge, GraphFragment, GraphMetric, GraphNode, type HistoryMetricsOptions, INDEX_VERSION, IdAlias, IdAliasReason, type IndexOptions, type IndexResult, type IndexSource, type IstanbulCoverage, KIT, LanguageExtractor, type LanguageExtractorOptions, type LineSpan, type Lineage, type LineageSnapshot, type LineageStep, type LoadedHistory, METRIC_CATALOGUE, MIGRATIONS, type MemberInfo, type MetricAbsence, type MetricAggregate, type MetricDelta, type MetricDescriptor, type MetricDirection, type MetricRollup, type MetricSource, type MetricUnit, type MinifiedSource, type MinifyLanguage, NodeKind, NodeMetrics, type NodeRename, NodeRole, type PageRankOptions, type PageRankResult, type PageRankRow, type ParamInfo, type PriorSnapshotOptions, type Provenance, type PruneOptions, type PrunePlan, type PruneResult, PythonGraphExtractor, type ReadFile, ReferenceEdgeLite, type RegenerateReason, type RelevanceOptions, type ResolveAliasOptions, type ReuseBasis, type RoleGlobs, type RoleHints, type RuleStore, type RunChecksOptions, SCHEMA_VERSION$1 as SCHEMA_VERSION, SNAPSHOT_SCOPED_TABLES, SOURCE_METRIC_NAMES, SYMBOL_EMBEDDING_NAMESPACE, SYMBOL_ID_SEP, SYMBOL_METRIC_NAMES, Severity, type SignatureTreeOptions, type SimilarCandidate, type SimilarResult, type SnapshotInsert, SnapshotRow, type SnapshotSpec, type SourceChunk, type SourceLanguage, type StoredFinding, type StoredVerdict, type SummarizeConventionsResult, type Summarizer, SymbolConsumers, SymbolCouplingOptions, SymbolCouplingPair, type SymbolDossier, type SymbolFootprint, type SymbolLine, type SymbolSpan, TRUNCATION_SUFFIX, type TestCoverageOwnershipOptions, TestSourceLink, type TopMetricRow, TsMorphGraphExtractor, type TsMorphGraphExtractorOptions, UNIMPORTED_ROLES, type UnitProvenance, type UnitProvenanceInput, type UnitRegeneration, type ValidateRulesOptions, type VerdictCitation, type VerdictLabel, ViolationBuckets, aggregateMetrics, aliasChain, annotateRoles, attributeCoverage, buildAliases, buildContextBundle, buildContextDossier, buildConventionAreas, buildEmbedText, buildFileModuleNodes, buildIndexerMetrics, buildLineage, canonicalEdgeKind, canonicalMetricName, canonicalRole, carryForwardVerdicts, checkSnapshot, classifyRole, collectDeclaredNames, collectDeclaredSpans, compilePatterns, computeDeadCodeMetrics, computeDeepAst, computeFootprints, computeGrowthRiskMetrics, computeMetrics, computePageRank, computeRecencyWindows, computeRelevance, computeRoleHints, computeSourceMetrics, computeTestCoverageOwnership, createAliasChain, defaultTargetCount, describeMetric, describeMetrics, detectCommunities, detectGitHead, detectGitToplevel, detectRenames, diffCheckResults, diffFootprints, diffSnapshots, edgeWeight, embedSnapshot, embedTextsCached, externalId, externalToFinding, fileId, findConventions, findSimilarCapability, findingKey, gateUnits, getConventionMap, getEdgeWeight, gitTreeSource, hashContent, hashEmbedText, hashText, importMarker, indexPaths, isGeneratedByHeuristic, isGeneratedFile, isInsideGitRepo, isMinifyLanguage, keyFindings, lineagePath, listEdges, listEdgesTouching, listEmbeddableSymbols, listFindings, listMetrics, listMetricsForNode, listNodes, listVerdicts, loadCheckRules, loadGeneratedPatterns, loadHistoryMetrics, loadLineage, loadRoleGlobs, matchesAny, minifySource, moduleId, normalizeFlaggedText, openCodeGraph, originalLine, packageId, parentModuleId, parseSymbolId, patternToRegex, percentileOf, planPrune, priorSnapshotForRef, pruneDanglingReferences, readSourceFiles, renderBundleText, renderContextMarkdown, renderSignatureTree, resolveAlias, resolveBarrelEdges, resolveChurnWindows, resolveGitRef, resolveSnapshot, runChecks, runPrune, saveFindings, saveVerdicts, snapshotPageRank, snapshotReferenceEdges, snapshotRelevance, snapshotSymbolConsumers, snapshotSymbolCoupling, snapshotViolations, structuralSignature, summarizeConventions, symbolId, symbolSetHash, toFindings, tryEmbedSnapshot, unitProvenance, validateRules, walkSourceFiles, windowSuffix, workingTreeSource };