@titan-design/code-graph 0.2.0 → 0.4.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
@@ -2,7 +2,7 @@ import { Db, Migration } from '@titan-design/store-sqlite';
2
2
  import { Extractor, ParsedFile } from '@titan-design/code-parser';
3
3
  export { Extractor, ParsedFile, getLanguageFromPath, getSupportedLanguages, parseFile, shouldIncludeFile } from '@titan-design/code-parser';
4
4
  import { Project } from 'ts-morph';
5
- import { c as CoEditPair, a as ChurnWindow, C as ChurnEntry } from './change-coupling-CyqHgRsm.js';
5
+ import { c as CoEditPair, C as ChurnEntry, a as ChurnWindow } from './change-coupling-CyqHgRsm.js';
6
6
  import { Embedder } from '@titan-design/embed';
7
7
 
8
8
  type NodeKind = "package" | "module" | "file" | "symbol" | "external";
@@ -58,6 +58,25 @@ interface FileFingerprint {
58
58
  structuralHash?: string;
59
59
  }
60
60
 
61
+ /** One metric's values over the nodes of one kind in one snapshot; `count` excludes null values. */
62
+ interface MetricAggregate {
63
+ name: string;
64
+ nodeKind: NodeKind;
65
+ count: number;
66
+ sum: number | null;
67
+ min: number | null;
68
+ max: number | null;
69
+ }
70
+ /** One node's value for one metric, joined to the node's own vocabulary columns. */
71
+ interface TopMetricRow {
72
+ nodeId: string;
73
+ name: string;
74
+ kind: string;
75
+ role: string | null;
76
+ value: number | null;
77
+ unit: string | null;
78
+ }
79
+
61
80
  interface SnapshotInsert {
62
81
  ref: string;
63
82
  commitHash?: string;
@@ -73,11 +92,18 @@ interface SnapshotInsert {
73
92
  declare class CodeGraphStore {
74
93
  readonly db: Db;
75
94
  private readonly statements;
95
+ private readonly targeted;
76
96
  constructor(db: Db);
77
97
  createSnapshot(input: SnapshotInsert): number;
78
98
  insertNodes(snapshotId: number, nodes: readonly GraphNode[]): void;
79
99
  insertEdges(snapshotId: number, edges: readonly GraphEdge[]): void;
80
100
  insertMetrics(snapshotId: number, metrics: readonly GraphMetric[]): void;
101
+ /**
102
+ * Replace every metric of one `name` for a snapshot in a single transaction —
103
+ * for an overlay such as coverage, which is re-ingested wholesale and must not
104
+ * accumulate stale rows across runs.
105
+ */
106
+ replaceMetricsByName(snapshotId: number, name: string, metrics: readonly GraphMetric[]): void;
81
107
  insertAliases(snapshotId: number, aliases: readonly IdAlias[]): void;
82
108
  insertFingerprints(snapshotId: number, fingerprints: readonly FileFingerprint[]): void;
83
109
  getSnapshot(id: number): SnapshotRow | null;
@@ -100,8 +126,31 @@ declare class CodeGraphStore {
100
126
  includeReferences?: boolean;
101
127
  }): GraphEdge[];
102
128
  listMetrics(snapshotId: number): GraphMetric[];
129
+ /** Every metric on one node, from the primary key instead of a whole-snapshot read. */
130
+ listMetricsForNode(snapshotId: number, nodeId: string): GraphMetric[];
131
+ /** Distinct metric names stored for a snapshot, ascending. */
132
+ listMetricNames(snapshotId: number): string[];
133
+ /** The highest-valued nodes for one metric, descending; `limit` defaults to 20. */
134
+ topByMetric(opts: {
135
+ snapshotId: number;
136
+ metric: string;
137
+ limit?: number;
138
+ kind?: string;
139
+ }): TopMetricRow[];
140
+ /** Edges into or out of one node; `references` edges are excluded unless asked, as in {@link listEdges}. */
141
+ listEdgesTouching(snapshotId: number, nodeId: string, opts?: {
142
+ includeReferences?: boolean;
143
+ }): GraphEdge[];
144
+ /** Count, sum, min, and max per metric name and node kind; pass `name` for one metric. */
145
+ aggregateMetrics(snapshotId: number, opts?: {
146
+ name?: string;
147
+ }): MetricAggregate[];
103
148
  listAliases(snapshotId: number): IdAlias[];
104
149
  listFingerprints(snapshotId: number): FileFingerprint[];
150
+ /** Drop whole snapshots. The domain tables carry no foreign key, so each is cleared by hand. */
151
+ deleteSnapshots(ids: readonly number[]): void;
152
+ vacuum(): void;
153
+ countRowsByTable(tables: readonly string[]): Record<string, number>;
105
154
  close(): void;
106
155
  }
107
156
  /** Open (creating if absent) a code graph database and bring it to the current schema. */
@@ -126,16 +175,37 @@ declare const KIT: {
126
175
  * `file_fingerprint` is the reuse basis the incremental indexer diffs against.
127
176
  */
128
177
  declare const DOMAIN_DDL = "\n CREATE TABLE IF NOT EXISTS edge (\n snapshot_id INTEGER NOT NULL,\n src_id TEXT NOT NULL,\n dst_id TEXT NOT NULL,\n kind TEXT NOT NULL,\n attrs TEXT,\n PRIMARY KEY (snapshot_id, src_id, dst_id, kind)\n );\n CREATE INDEX IF NOT EXISTS idx_edge_dst ON edge(snapshot_id, dst_id);\n CREATE INDEX IF NOT EXISTS idx_edge_kind ON edge(snapshot_id, kind);\n\n CREATE TABLE IF NOT EXISTS metric (\n snapshot_id INTEGER NOT NULL,\n node_id TEXT NOT NULL,\n name TEXT NOT NULL,\n value REAL,\n unit TEXT,\n PRIMARY KEY (snapshot_id, node_id, name)\n );\n CREATE INDEX IF NOT EXISTS idx_metric_name ON metric(snapshot_id, name);\n\n CREATE TABLE IF NOT EXISTS id_alias (\n snapshot_id INTEGER NOT NULL,\n old_id TEXT NOT NULL,\n new_id TEXT NOT NULL,\n reason TEXT NOT NULL,\n PRIMARY KEY (snapshot_id, old_id, new_id)\n );\n\n CREATE TABLE IF NOT EXISTS file_fingerprint (\n snapshot_id INTEGER NOT NULL,\n file_id TEXT NOT NULL,\n content_hash TEXT NOT NULL,\n structural_hash TEXT,\n PRIMARY KEY (snapshot_id, file_id)\n );\n";
178
+ /** Every table keyed by `snapshot_id`. No DDL above declares a foreign key, so dropping a snapshot walks this list. */
179
+ declare const SNAPSHOT_SCOPED_TABLES: readonly ["node", "edge", "metric", "id_alias", "file_fingerprint"];
129
180
  declare const MIGRATIONS: Migration[];
130
181
  /** The top of this package's schema. A code graph database is never shared, so this is the top of one. */
131
182
  declare const SCHEMA_VERSION: number;
132
183
 
184
+ interface PrunePlan {
185
+ keep: SnapshotRow[];
186
+ remove: SnapshotRow[];
187
+ }
188
+ interface PruneOptions {
189
+ keep?: number;
190
+ keepRefs?: readonly string[];
191
+ }
192
+ declare function planPrune(store: CodeGraphStore, options?: PruneOptions): PrunePlan;
193
+ interface PruneResult {
194
+ plan: PrunePlan;
195
+ rowsBefore: Record<string, number>;
196
+ rowsAfter: Record<string, number>;
197
+ vacuumed: boolean;
198
+ }
199
+ declare function runPrune(store: CodeGraphStore, options?: PruneOptions & {
200
+ vacuum?: boolean;
201
+ }): PruneResult;
202
+
133
203
  /**
134
204
  * Bumping this invalidates every reuse basis: a snapshot written by a different
135
205
  * index version is never reused, so a change to node/edge shape or to a metric's
136
206
  * value for the same bytes can never be carried forward from an incompatible graph.
137
207
  */
138
- declare const INDEX_VERSION = "0.14.0";
208
+ declare const INDEX_VERSION = "0.15.0";
139
209
  interface IndexOptions {
140
210
  /** Roots to walk. Node ids are still rooted at the git toplevel, so importers across roots share an id space. */
141
211
  paths: string[];
@@ -290,6 +360,51 @@ declare class TsMorphGraphExtractor implements Extractor<GraphFragment> {
290
360
  */
291
361
  declare function buildFileModuleNodes(repoRoot: string, absPath: string): GraphNode[];
292
362
 
363
+ /**
364
+ * C-81 — **deep AST on-pull**. Structural facts too heavy to persist at index
365
+ * time (class members, per-parameter types, return type) are recomputed lazily
366
+ * here from the working-tree source when a consumer actually pulls a target.
367
+ * Persisting them would bloat every symbol node and break the incremental reuse
368
+ * basis (inferred types embed absolute `import("…")` paths); computing on demand
369
+ * keeps the index lean while still answering "what are the members / params of
370
+ * this" for the one target being read.
371
+ *
372
+ * A single-file in-memory ts-morph project (no tsconfig, no dependency walk) —
373
+ * so the extraction is cheap and reads only explicit syntactic annotations.
374
+ */
375
+ interface ParamInfo {
376
+ name: string;
377
+ type: string | null;
378
+ }
379
+ interface MemberInfo {
380
+ name: string;
381
+ memberKind: string;
382
+ signature: string | null;
383
+ isStatic: boolean;
384
+ }
385
+ interface DeepAst {
386
+ target: string;
387
+ kind: "file" | "symbol";
388
+ declarationKind: string | null;
389
+ signature: string | null;
390
+ purpose: string | null;
391
+ params: ParamInfo[];
392
+ returnType: string | null;
393
+ /** Class/interface members for a symbol; exported declarations for a file. */
394
+ members: MemberInfo[];
395
+ note?: string;
396
+ }
397
+ interface DeepAstInput {
398
+ /** Repo-relative file id, e.g. `src/a.ts`. */
399
+ filePath: string;
400
+ /** Absolute path to the file on disk. */
401
+ absPath: string;
402
+ /** Symbol name for a symbol target; omit for a file target. */
403
+ symbolName?: string;
404
+ }
405
+ /** Deep structural facts for one target, or null when the source is unreadable. */
406
+ declare function computeDeepAst(input: DeepAstInput): DeepAst | null;
407
+
293
408
  declare function fileId(repoRoot: string, absPath: string): string;
294
409
  declare function moduleId(repoRoot: string, absPath: string): string;
295
410
  declare function parentModuleId(id: string): string | null;
@@ -466,9 +581,84 @@ interface DetectRenamesOptions {
466
581
  declare function detectGitHead(repoRoot: string): string | null;
467
582
  declare function isInsideGitRepo(repoRoot: string): boolean;
468
583
 
584
+ /** Resolve a ref (branch, tag, sha) to its current commit sha, or null. */
585
+ declare function resolveGitRef(repoRoot: string, ref: string): string | null;
469
586
  declare function detectRenames(options: DetectRenamesOptions): RenamePair[];
470
587
  declare function buildAliases(repoRoot: string, pairs: readonly RenamePair[]): IdAlias[];
471
588
 
589
+ /** Snapshot attrs key naming the snapshot its id aliases were computed against; written from index version 0.15.0. */
590
+ declare const ALIAS_BASE_ATTR = "aliasBase";
591
+ type LineageSnapshot = Pick<SnapshotRow, "id" | "commitHash" | "attrs">;
592
+ /** Each snapshot's alias base, or null for a root: the tree the id_alias rows form across snapshots. */
593
+ type Lineage = ReadonlyMap<number, number | null>;
594
+ interface LineageStep {
595
+ snapshotId: number;
596
+ /** `forward` applies the snapshot's aliases (base to snapshot); `backward` undoes them. */
597
+ direction: "forward" | "backward";
598
+ }
599
+ /**
600
+ * The recorded base per snapshot. A snapshot written before 0.15.0 records none,
601
+ * so it gets the newest earlier snapshot with a commit: the one the indexer diffed against then.
602
+ */
603
+ declare function buildLineage(snapshots: readonly LineageSnapshot[]): Lineage;
604
+ /** Steps from one snapshot to another through their nearest common base, or null when they share none. */
605
+ declare function lineagePath(lineage: Lineage, fromId: number, toId: number, maxHops?: number): LineageStep[] | null;
606
+
607
+ type AliasLoader = (snapshotId: number) => readonly IdAlias[];
608
+ interface AliasResolution {
609
+ requestedId: string;
610
+ /** The id in the target snapshot's id space; equal to `requestedId` when nothing moved. */
611
+ id: string;
612
+ /** Each alias applied in walk order; a backward step lists the alias it undid. */
613
+ hops: IdAlias[];
614
+ /** `move` when any hop changed directory, otherwise the last hop's reason; absent without hops. */
615
+ reason?: IdAliasReason;
616
+ }
617
+ interface AliasChain {
618
+ fromSnapshotId: number | null;
619
+ toSnapshotId: number;
620
+ /** False when the snapshots share no alias base; only the target's own aliases apply then, as before 0.15.0. */
621
+ connected: boolean;
622
+ steps: readonly LineageStep[];
623
+ resolve(id: string): string;
624
+ trace(id: string): AliasResolution;
625
+ }
626
+ interface AliasChainInput {
627
+ lineage: Lineage;
628
+ loadAliases: AliasLoader;
629
+ /** Null resolves an id from any ancestor of `to`. */
630
+ from: number | null;
631
+ to: number;
632
+ maxHops?: number;
633
+ }
634
+ /** Carry node ids from one snapshot into another across every rename between them. */
635
+ declare function createAliasChain(input: AliasChainInput): AliasChain;
636
+
637
+ interface AliasChainOptions {
638
+ maxHops?: number;
639
+ }
640
+ interface ResolveAliasOptions extends AliasChainOptions {
641
+ /** The snapshot the id was read in; omitted, the id may come from any ancestor of the target. */
642
+ fromSnapshotId?: number;
643
+ }
644
+ interface PriorSnapshotOptions {
645
+ /** Only snapshots older than this id count; omitted, every snapshot does. */
646
+ before?: number;
647
+ /** A git checkout to resolve `ref` in; without it only the snapshot's `ref` label matches. */
648
+ repoRoot?: string;
649
+ }
650
+ /** The alias-base tree over every snapshot in the store. */
651
+ declare function loadLineage(store: CodeGraphStore): Lineage;
652
+ /** Carry node ids from `fromSnapshotId` into `toSnapshotId` through every alias between them, in either direction. */
653
+ declare function aliasChain(store: CodeGraphStore, fromSnapshotId: number | null, toSnapshotId: number, options?: AliasChainOptions): AliasChain;
654
+ /** Resolve an id to its form in `toSnapshotId`: `a.ts` renamed to `b.ts` then `c.ts` resolves to `c.ts`. */
655
+ declare function resolveAlias(store: CodeGraphStore, id: string, toSnapshotId: number, options?: ResolveAliasOptions): AliasResolution;
656
+ /**
657
+ * The newest snapshot a ref denotes: first the snapshot of the commit git resolves
658
+ * `ref` to (with `repoRoot`), else the newest snapshot whose `ref` label is `ref`.
659
+ */
660
+ declare function priorSnapshotForRef(store: CodeGraphStore, ref: string, options?: PriorSnapshotOptions): SnapshotRow | null;
661
+
472
662
  declare function computeMetrics(nodes: readonly GraphNode[], edges: readonly GraphEdge[]): GraphMetric[];
473
663
 
474
664
  /**
@@ -528,6 +718,17 @@ interface HistoryMetricsOptions {
528
718
  /** Epoch seconds that windows end at; defaults to the current time. */
529
719
  nowEpoch?: number;
530
720
  }
721
+ /** Windows the dashboard switcher offers; churn is stored for each by default. */
722
+ declare const DEFAULT_CHURN_WINDOWS: number[];
723
+ /** De-duped, ascending windows including the primary, with `lifetime` last (widest) when requested. */
724
+ declare function resolveChurnWindows(requested: number[] | undefined, primaryWindow: number, includeLifetime: boolean): ChurnWindow[];
725
+ interface LoadedHistory {
726
+ metrics: GraphMetric[];
727
+ /** Churn entries inside the primary window, which also scopes test-coverage linking and ownership. */
728
+ primaryEntries: readonly ChurnEntry[];
729
+ }
730
+ /** {@link buildHistoryMetrics} plus the primary-window entries it read; null when git or history is unavailable. */
731
+ declare function loadHistoryMetrics(nodes: Iterable<GraphNode>, idRoot: string, options?: HistoryMetricsOptions): LoadedHistory | null;
531
732
  interface TestCoverageOwnershipOptions {
532
733
  /** Window named in the metric suffix. Default 30. */
533
734
  windowDays?: ChurnWindow;
@@ -648,6 +849,17 @@ interface SymbolSpan {
648
849
  */
649
850
  declare function attributeCoverage(coverage: IstanbulCoverage, fileIdOf: (absPath: string) => string | null, symbolsByFile: ReadonlyMap<string, readonly SymbolSpan[]>): GraphMetric[];
650
851
 
852
+ /** Metric-name suffix for a window: `30d`, `180d`, or `lifetime`. */
853
+ declare function windowSuffix(window: ChurnWindow): string;
854
+ /**
855
+ * Age-discount metrics: `recency_{w}` = min(1, age/w) for each file that churned
856
+ * in window `w`, plus one window-independent `file_age_days` per file. Multiplying
857
+ * a hotspot score by recency stops a young file's burst of churn from reading as
858
+ * decay. Recency is emitted (as 1) even when the age is unknown, so a rule that
859
+ * needs every hotspot factor is never silently disabled; `file_age_days` only when known.
860
+ */
861
+ declare function computeRecencyWindows(firstSeen: ReadonlyMap<string, number>, churnedIdsByWindow: ReadonlyMap<ChurnWindow, ReadonlySet<string>>, nowEpoch: number): GraphMetric[];
862
+
651
863
  interface PageRankOptions {
652
864
  /** Per-node teleport weight. Unset or empty → uniform teleport across all nodes. */
653
865
  personalization?: ReadonlyMap<string, number>;
@@ -774,6 +986,86 @@ declare function computeSymbolConsumers(edges: readonly ReferenceEdgeLite[]): Sy
774
986
  */
775
987
  declare function computeSymbolCoupling(edges: readonly ReferenceEdgeLite[], options?: SymbolCouplingOptions): SymbolCouplingPair[];
776
988
 
989
+ /**
990
+ * Per-package and per-pair structural quality metrics, plus an overall
991
+ * Newman-Girvan modularity Q for the package partition.
992
+ *
993
+ * Operates on a barrel-resolved edge set by default: edges that land on a
994
+ * file with role="barrel" are rewritten to land on the underlying files
995
+ * the barrel re-exports from (transitively). The intent is to measure the
996
+ * real dependency surface, not the re-export plumbing.
997
+ *
998
+ * See C-8 task notes for the design rationale and empirical calibration
999
+ * data from the 2026-05-21 codewatch dogfood.
1000
+ */
1001
+ interface PartitionQualityInput {
1002
+ /** Logical packages — at minimum needs an id. */
1003
+ packages: ReadonlyArray<{
1004
+ id: string;
1005
+ }>;
1006
+ /** Package id → list of file ids assigned to that package. */
1007
+ fileByPackage: ReadonlyMap<string, ReadonlyArray<string>>;
1008
+ /** All file/module/external nodes for the snapshot. Used to identify role="barrel". */
1009
+ nodes: readonly GraphNode[];
1010
+ /** All edges for the snapshot. */
1011
+ edges: readonly GraphEdge[];
1012
+ /**
1013
+ * When true, edges landing on a barrel file are resolved through its
1014
+ * re-export chain to the underlying source files. The cheap implementation
1015
+ * fans each barrel import into N synthetic edges (one per re-export
1016
+ * target), which over-attributes — a single `import { x } from "./pkg"`
1017
+ * becomes N edges as if every re-export were used. Default false until
1018
+ * a weighted or symbol-tracking version is available.
1019
+ */
1020
+ resolveBarrels?: boolean;
1021
+ }
1022
+ type PackageFlag = "weak-boundary";
1023
+ type PairFlag = "tight" | "moderate" | "none";
1024
+ type PackageLayer = "top" | "middle" | "foundation";
1025
+ interface PackageStats {
1026
+ pkgId: string;
1027
+ fileCount: number;
1028
+ internalEdges: number;
1029
+ outgoingEdges: number;
1030
+ incomingEdges: number;
1031
+ /** internal / (internal + outgoing) — higher = more self-contained. */
1032
+ cohesion: number;
1033
+ /** outgoing / (outgoing + incoming) — Martin's I metric at the package level. */
1034
+ instability: number;
1035
+ /**
1036
+ * Abstractness proxy A ∈ [0,1]: share of the package's files with role
1037
+ * "types" (dedicated type/interface definitions). codewatch has no
1038
+ * symbol-level abstract/concrete counts, so this file-role ratio stands in
1039
+ * for Martin's A. Enables the instability×abstractness main-sequence plot.
1040
+ */
1041
+ abstractness: number;
1042
+ layer: PackageLayer;
1043
+ flags: PackageFlag[];
1044
+ }
1045
+ interface PairCoupling {
1046
+ from: string;
1047
+ to: string;
1048
+ edges: number;
1049
+ /** edges / files(from) — fraction of from-side files contributing dependencies into `to`. */
1050
+ intensity: number;
1051
+ flag: PairFlag;
1052
+ }
1053
+ interface PartitionQualityResult {
1054
+ modularityQ: number;
1055
+ totalEdges: number;
1056
+ perPackage: PackageStats[];
1057
+ pairCoupling: PairCoupling[];
1058
+ /** Total raised flags across packages + pairs (excluding "moderate"). */
1059
+ flagsCount: number;
1060
+ }
1061
+ declare function computePartitionQuality(input: PartitionQualityInput): PartitionQualityResult;
1062
+ /**
1063
+ * Invert a package→files bucket map into a file→package lookup, skipping the
1064
+ * empty-string "unassigned" bucket. Shared by partition-quality and the CLI's
1065
+ * arch/wiki package rollups, which all need the same file→package direction.
1066
+ */
1067
+ declare function invertBuckets(fileByPackage: ReadonlyMap<string, ReadonlyArray<string>>): Map<string, string>;
1068
+
777
1069
  /**
778
1070
  * PageRank over one snapshot's file-level graph (symbol nodes and `references`
779
1071
  * edges excluded, as codewatch's `graph relevant` reads it). Pass
@@ -801,6 +1093,51 @@ declare function listEdges(store: CodeGraphStore, snapshotId: number, opts?: {
801
1093
  includeReferences?: boolean;
802
1094
  }): GraphEdge[];
803
1095
  declare function listMetrics(store: CodeGraphStore, snapshotId: number): GraphMetric[];
1096
+ declare function listMetricsForNode(store: CodeGraphStore, snapshotId: number, nodeId: string): GraphMetric[];
1097
+ declare function listEdgesTouching(store: CodeGraphStore, snapshotId: number, nodeId: string, opts?: {
1098
+ includeReferences?: boolean;
1099
+ }): GraphEdge[];
1100
+ declare function aggregateMetrics(store: CodeGraphStore, snapshotId: number, opts?: {
1101
+ name?: string;
1102
+ }): MetricAggregate[];
1103
+
1104
+ /** The `unit` a metric row is stored with; every row of one metric name carries the same unit. */
1105
+ type MetricUnit = "count" | "lines" | "ratio" | "days" | "percent";
1106
+ /** How file values combine into a directory; `none` means no rollup reproduces the group's true value. */
1107
+ type MetricRollup = "sum" | "max" | "mean" | "none";
1108
+ /** Which way is worse, for ranking and colouring; `neutral` makes no value judgement. */
1109
+ type MetricDirection = "higher-worse" | "lower-worse" | "neutral";
1110
+ /** A missing row reads as zero (a sparse writer's floor) or is left out of means, percentiles, and ranks. */
1111
+ type MetricAbsence = "zero" | "exclude";
1112
+ /** The code-graph module that writes the metric, for provenance. */
1113
+ type MetricSource = "degree" | "source-metrics" | "lcom" | "dead-code" | "growth-risk" | "history" | "test-linker" | "coverage";
1114
+ interface MetricDescriptor {
1115
+ /** The stored name, or a template such as `churn_{w}` when {@link windowed} is set. */
1116
+ name: string;
1117
+ unit: MetricUnit;
1118
+ /** Node kinds the writer attaches this metric to. */
1119
+ appliesTo: readonly NodeKind[];
1120
+ rollup: MetricRollup;
1121
+ direction: MetricDirection;
1122
+ absent: MetricAbsence;
1123
+ source: MetricSource;
1124
+ description: string;
1125
+ /** Set on templates: `{w}` in `name` and `description` stands for a window such as `30d` or `lifetime`. */
1126
+ windowed?: true;
1127
+ /** Set on a resolved windowed descriptor: the window its name carries. */
1128
+ window?: string;
1129
+ }
1130
+
1131
+ /** Every metric name code-graph writes, with windowed names as `{w}` templates. */
1132
+ declare const METRIC_CATALOGUE: readonly MetricDescriptor[];
1133
+
1134
+ /** The descriptor for a stored metric name, resolving windowed names such as `churn_90d`; null when uncatalogued. */
1135
+ declare function describeMetric(name: string): MetricDescriptor | null;
1136
+ /** Descriptors for a set of stored names, in the order given; uncatalogued names are returned separately. */
1137
+ declare function describeMetrics(names: Iterable<string>): {
1138
+ described: MetricDescriptor[];
1139
+ unknown: string[];
1140
+ };
804
1141
 
805
1142
  type Severity = "error" | "warning";
806
1143
  interface MetricMaxRule {
@@ -883,12 +1220,26 @@ interface CheckResult {
883
1220
  passed: boolean;
884
1221
  }
885
1222
 
1223
+ /** The three whole-snapshot reads rule evaluation needs; any store with them can be checked. */
1224
+ type RuleStore = Pick<CodeGraphStore, "listNodes" | "listEdges" | "listMetrics">;
1225
+
886
1226
  interface RunChecksOptions {
887
1227
  snapshotId: number;
888
1228
  rules: readonly CheckRule[];
889
1229
  baselineSnapshotId?: number;
890
1230
  }
891
1231
  declare function runChecks(store: CodeGraphStore, options: RunChecksOptions): CheckResult;
1232
+ /** Every rule's violations in one snapshot with no baseline; needs only the three whole-snapshot reads. */
1233
+ declare function snapshotViolations(store: RuleStore, snapshotId: number, rules: readonly CheckRule[]): CheckViolation[];
1234
+ /** Identity of a violation across snapshots: severity, value and message may change, the key does not. */
1235
+ declare function violationKey(v: CheckViolation): string;
1236
+ /** {@link violationKey} with both node ids carried into another snapshot's id space; unmoved ids key as before. */
1237
+ declare function rebasedViolationKey(v: CheckViolation, resolve: (id: string) => string): string;
1238
+
1239
+ /** Glob when the pattern has a `*` (`**` crosses directories, `*` stays in a segment); otherwise a case-sensitive substring. */
1240
+ declare function patternToRegex(pattern: string): RegExp;
1241
+ declare function compilePatterns(patterns: readonly string[] | undefined): RegExp[];
1242
+ declare function matchesAny(value: string, patterns: readonly RegExp[]): boolean;
892
1243
 
893
1244
  interface ValidateRulesOptions {
894
1245
  /** Called with a human-readable message when a deprecated alias is healed. */
@@ -952,7 +1303,10 @@ interface DiffSnapshotsOptions {
952
1303
  fromSnapshotId: number;
953
1304
  toSnapshotId: number;
954
1305
  }
955
- /** Structural diff of two snapshots; the to-snapshot's id aliases carry renamed nodes across so a move is not a delete plus an add. */
1306
+ /**
1307
+ * Structural diff of two snapshots. Ids follow the alias chain between them, across
1308
+ * every rename in between, so a move is not a delete plus an add.
1309
+ */
956
1310
  declare function diffSnapshots(store: CodeGraphStore, options: DiffSnapshotsOptions): GraphDiff;
957
1311
 
958
1312
  interface UnchangedViolation {
@@ -975,7 +1329,11 @@ interface DiffCheckResultsOptions {
975
1329
  toSnapshotId: number;
976
1330
  rules: readonly CheckRule[];
977
1331
  }
978
- /** Run the rules on both snapshots and bucket each violation as new, resolved, or unchanged (worsened or improved by value). */
1332
+ /**
1333
+ * Run the rules on both snapshots and bucket each violation as new, resolved, or
1334
+ * unchanged (worsened or improved by value). From-side ids follow the alias chain
1335
+ * into the to-snapshot, so a moved file's violations stay unchanged.
1336
+ */
979
1337
  declare function diffCheckResults(store: CodeGraphStore, options: DiffCheckResultsOptions): CheckDiff;
980
1338
 
981
1339
  interface EmbeddableSymbol {
@@ -1074,4 +1432,124 @@ declare function tryEmbedSnapshot(store: CodeGraphStore, snapshotId: number, emb
1074
1432
  */
1075
1433
  declare function findSimilarCapability(store: CodeGraphStore, snapshotId: number, query: string, embedder: Embedder, opts?: FindSimilarOptions): Promise<SimilarResult>;
1076
1434
 
1077
- export { ALL_ROLES, COVERAGE_METRIC_NAME, type CachedEmbedResult, type CheckDiff, type CheckResult, type CheckRule, type CheckRulesFile, type CheckSnapshotOptions, type CheckSnapshotResult, type CheckViolation, CodeGraphStore, DEAD_CODE_METRIC_NAMES, DOMAIN_DDL, type DiffCheckResultsOptions, type DiffSnapshotsOptions, type EdgeKind, type EmbedAttempt, type EmbedCoverage, type EmbedSnapshotResult, type EmbeddableSymbol, type FileFingerprint, type FindSimilarOptions, type ForbidImportRule, GROWTH_RISK_METRIC_NAMES, type GraphDiff, type GraphDiffSummary, type GraphEdge, type GraphFragment, type GraphMetric, type GraphNode, INDEX_VERSION, type IdAlias, type IdAliasReason, type IndexOptions, type IndexResult, type IstanbulCoverage, KIT, LanguageExtractor, type LanguageExtractorOptions, type LayeredDepsRule, type LineSpan, type LinkMethod, type LinkTestsOptions, MIGRATIONS, type MetricDelta, type MetricMaxRule, type MetricMinRule, type MetricProductMaxRule, type NoInternalOnlyBarrelsRule, type NodeKind, type NodeRename, type NodeRole, type PageRankOptions, type PageRankResult, type PageRankRow, PythonGraphExtractor, type ReadFile, type ReferenceEdgeLite, type RelevanceOptions, type ReuseBasis, type RunChecksOptions, SCHEMA_VERSION, SOURCE_METRIC_NAMES, SYMBOL_EMBEDDING_NAMESPACE, SYMBOL_ID_SEP, type Severity, type SimilarCandidate, type SimilarResult, type SnapshotInsert, type SnapshotRow, type SnapshotSpec, type SourceLanguage, type SymbolConsumers, type SymbolCouplingOptions, type SymbolCouplingPair, type SymbolSpan, type TestCoverageOwnershipOptions, type TestSourceLink, TsMorphGraphExtractor, type TsMorphGraphExtractorOptions, type UnchangedViolation, type ValidateRulesOptions, annotateRoles, attributeCoverage, buildAliases, buildEmbedText, buildFileModuleNodes, buildIndexerMetrics, canonicalEdgeKind, canonicalMetricName, canonicalRole, checkSnapshot, classifyRole, collectDeclaredNames, collectDeclaredSpans, computeDeadCodeMetrics, computeGrowthRiskMetrics, computeMetrics, computePageRank, computeRelevance, computeRoleHints, computeSourceMetrics, computeSymbolConsumers, computeSymbolCoupling, computeTestCoverageOwnership, detectGitHead, detectGitToplevel, detectRenames, diffCheckResults, diffSnapshots, edgeWeight, embedSnapshot, embedTextsCached, externalId, fileId, findSimilarCapability, getEdgeWeight, groupTestsBySource, hashContent, hashEmbedText, indexPaths, isGeneratedByHeuristic, isGeneratedFile, isInsideGitRepo, linkTestsToSources, listEdges, listEmbeddableSymbols, listMetrics, listNodes, loadCheckRules, loadGeneratedPatterns, moduleId, openCodeGraph, packageId, parentModuleId, parseSymbolId, pruneDanglingReferences, readSourceFiles, resolveBarrelEdges, resolveSnapshot, runChecks, snapshotPageRank, snapshotReferenceEdges, snapshotRelevance, snapshotSymbolConsumers, snapshotSymbolCoupling, structuralSignature, symbolId, testCoverageCountMetrics, tryEmbedSnapshot, validateRules, walkSourceFiles };
1435
+ /** A text-summarization backend the product injects; `model` keys the stored summaries. */
1436
+ interface Summarizer {
1437
+ readonly model: string;
1438
+ summarize(prompt: string): Promise<string>;
1439
+ }
1440
+ interface ConventionSymbol {
1441
+ name: string;
1442
+ signature: string;
1443
+ purpose?: string;
1444
+ }
1445
+ interface ConventionArea {
1446
+ /** Stable over membership: `area-` plus a hash of the member file list. */
1447
+ id: string;
1448
+ /** Dominant directory of the members. */
1449
+ label: string;
1450
+ /** Member files, most-connected first. */
1451
+ files: string[];
1452
+ size: number;
1453
+ topSymbols: ConventionSymbol[];
1454
+ /** Hash of the summarizer prompt, which is the summary-cache key. */
1455
+ contentHash: string;
1456
+ summary?: string;
1457
+ }
1458
+ interface ConventionCoverage {
1459
+ /** Non-generated indexed files in the snapshot. */
1460
+ files: number;
1461
+ /** Files inside a kept area; the rest are too small or isolated to summarize. */
1462
+ grouped: number;
1463
+ areas: number;
1464
+ summarized: number;
1465
+ }
1466
+ interface ConventionMap {
1467
+ model: string;
1468
+ coverage: ConventionCoverage;
1469
+ areas: ConventionArea[];
1470
+ }
1471
+ interface SummarizeConventionsResult extends ConventionMap {
1472
+ /** Cache misses sent to the summarizer on this call. */
1473
+ newlySummarized: number;
1474
+ /** Areas whose summary was already stored. */
1475
+ reused: number;
1476
+ }
1477
+ interface ConventionMatch {
1478
+ id: string;
1479
+ label: string;
1480
+ summary: string;
1481
+ files: string[];
1482
+ size: number;
1483
+ /** Cosine similarity of the query to the area summary, in [-1, 1]. */
1484
+ score: number;
1485
+ }
1486
+ interface ConventionQueryResult {
1487
+ query: string;
1488
+ model: string;
1489
+ embeddingModel: string;
1490
+ coverage: ConventionCoverage;
1491
+ matches: ConventionMatch[];
1492
+ }
1493
+ /** The cut level: how coarse the partition is and which areas are kept. */
1494
+ interface ConventionOptions {
1495
+ /** Coarse community count to merge down to; default scales with repo size. */
1496
+ targetCount?: number;
1497
+ /** Areas smaller than this stay ungrouped (not summarized). Default 3. */
1498
+ minSize?: number;
1499
+ }
1500
+ interface FindConventionsOptions extends ConventionOptions {
1501
+ /** Matches returned. Default 3. */
1502
+ limit?: number;
1503
+ }
1504
+ interface ConventionCorpus {
1505
+ areas: ConventionArea[];
1506
+ /** Summarizer prompt per area, keyed by the area's `contentHash`. */
1507
+ prompts: Map<string, string>;
1508
+ coverage: ConventionCoverage;
1509
+ }
1510
+
1511
+ /**
1512
+ * Greedy-modularity (Clauset-Newman-Moore) community detection over the
1513
+ * undirected file dependency graph. Deterministic: ties and labels resolve
1514
+ * by sorted node id, so the same graph always yields the same partition.
1515
+ *
1516
+ * Two optional controls shape the C-88 capability-altitude cut, in opposite
1517
+ * directions: `maxSize` skips any merge that would grow a community past it
1518
+ * (greedy CNM otherwise snowballs a dense graph into one giant blob — the
1519
+ * cap shapes formation instead of trying to split after the fact), and
1520
+ * `targetCount` keeps merging past the natural modularity stop (taking the
1521
+ * least-bad allowed merge) while more communities than that remain, so a
1522
+ * sparse fragmented graph still coarsens. Disconnected components can never
1523
+ * merge, so the floor is the component count.
1524
+ */
1525
+ declare function detectCommunities(fileIds: readonly string[], edges: readonly GraphEdge[], opts?: {
1526
+ targetCount?: number;
1527
+ maxSize?: number;
1528
+ }): Map<string, string[]>;
1529
+
1530
+ /** Coarse capability-altitude cut: about one area per 25 files, clamped to 6..40. */
1531
+ declare function defaultTargetCount(fileCount: number): number;
1532
+ /**
1533
+ * The deterministic half of the convention layer: partition the barrel-resolved
1534
+ * file graph into coarse areas and build each area's summarizer prompt and cache key.
1535
+ */
1536
+ declare function buildConventionAreas(store: CodeGraphStore, snapshotId: number, opts?: ConventionOptions): ConventionCorpus;
1537
+
1538
+ /** The blob_cache namespace for area summaries; the model column holds `summarizer.model`. */
1539
+ declare const COMMUNITY_SUMMARY_NAMESPACE = "code-graph/community-summary";
1540
+ /**
1541
+ * Generate or reuse the summary of every area. Only cache misses reach the
1542
+ * summarizer, so cost scales with what structurally changed, not with repo size.
1543
+ */
1544
+ declare function summarizeConventions(store: CodeGraphStore, snapshotId: number, summarizer: Summarizer, opts?: ConventionOptions): Promise<SummarizeConventionsResult>;
1545
+ /** Read-only view: the partition plus whatever summaries are already stored. Never calls a summarizer. */
1546
+ declare function getConventionMap(store: CodeGraphStore, snapshotId: number, model: string, opts?: ConventionOptions): ConventionMap;
1547
+
1548
+ /**
1549
+ * "How does this repo do X?": rank summarized areas by similarity of their
1550
+ * summary to the question. Returns candidates, not verdicts. Summary vectors
1551
+ * share the content-addressed embedding cache with the symbol layer.
1552
+ */
1553
+ declare function findConventions(store: CodeGraphStore, snapshotId: number, query: string, embedder: Embedder, model: string, opts?: FindConventionsOptions): Promise<ConventionQueryResult>;
1554
+
1555
+ 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, DOMAIN_DDL, type DeepAst, type DeepAstInput, type DiffCheckResultsOptions, type DiffSnapshotsOptions, type EdgeKind, type EmbedAttempt, type EmbedCoverage, type EmbedSnapshotResult, type EmbeddableSymbol, type FileFingerprint, type FindConventionsOptions, type FindSimilarOptions, 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 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, type Severity, type SimilarCandidate, type SimilarResult, type SnapshotInsert, type SnapshotRow, type SnapshotSpec, type SourceLanguage, type SummarizeConventionsResult, type Summarizer, type SymbolConsumers, type SymbolCouplingOptions, type SymbolCouplingPair, type SymbolSpan, type TestCoverageOwnershipOptions, type TestSourceLink, type TopMetricRow, TsMorphGraphExtractor, type TsMorphGraphExtractorOptions, type UnchangedViolation, type ValidateRulesOptions, aggregateMetrics, aliasChain, annotateRoles, attributeCoverage, buildAliases, buildConventionAreas, buildEmbedText, buildFileModuleNodes, buildIndexerMetrics, buildLineage, canonicalEdgeKind, canonicalMetricName, canonicalRole, checkSnapshot, classifyRole, collectDeclaredNames, collectDeclaredSpans, compilePatterns, computeDeadCodeMetrics, computeDeepAst, 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, fileId, findConventions, findSimilarCapability, getConventionMap, getEdgeWeight, groupTestsBySource, hashContent, hashEmbedText, indexPaths, invertBuckets, isGeneratedByHeuristic, isGeneratedFile, isInsideGitRepo, lineagePath, linkTestsToSources, listEdges, listEdgesTouching, listEmbeddableSymbols, listMetrics, listMetricsForNode, listNodes, loadCheckRules, loadGeneratedPatterns, loadHistoryMetrics, loadLineage, matchesAny, moduleId, openCodeGraph, packageId, parentModuleId, parseSymbolId, patternToRegex, planPrune, priorSnapshotForRef, pruneDanglingReferences, readSourceFiles, rebasedViolationKey, resolveAlias, resolveBarrelEdges, resolveChurnWindows, resolveGitRef, resolveSnapshot, runChecks, runPrune, snapshotPageRank, snapshotReferenceEdges, snapshotRelevance, snapshotSymbolConsumers, snapshotSymbolCoupling, snapshotViolations, structuralSignature, summarizeConventions, symbolId, testCoverageCountMetrics, tryEmbedSnapshot, validateRules, violationKey, walkSourceFiles, windowSuffix };