@titan-design/code-graph 0.8.0 → 0.9.1
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/README.md +6 -3
- package/dist/index.d.ts +69 -2
- package/dist/index.js +193 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -148,7 +148,7 @@ always recomputed over the whole assembled graph, so a heavily-reused run and a
|
|
|
148
148
|
|
|
149
149
|
## Store layout
|
|
150
150
|
|
|
151
|
-
`openCodeGraph` opens the database with the kit's pragmas and runs
|
|
151
|
+
`openCodeGraph` opens the database with the kit's pragmas and runs four migrations.
|
|
152
152
|
|
|
153
153
|
It refuses a database stamped past `SCHEMA_VERSION` with `SchemaTooNewError`: a code graph
|
|
154
154
|
database belongs to one build, so a higher version means a newer build already moved the
|
|
@@ -163,7 +163,8 @@ every consumer reads directly: `node.language`, `node.role`, `snapshot.commit_ha
|
|
|
163
163
|
Domain tables in `schema.ts`: `edge` (snapshot-scoped, keyed
|
|
164
164
|
`(snapshot_id, src_id, dst_id, kind)` — the kit's own edge table is bi-temporal, which is the
|
|
165
165
|
wrong time model for a population re-indexed all at once), `metric`, `id_alias`, and
|
|
166
|
-
`file_fingerprint`.
|
|
166
|
+
`file_fingerprint`. Migration 4 adds `finding` and `verdict`, both keyed
|
|
167
|
+
`(snapshot_id, key)` on a `findingKey` and pruned with their snapshot.
|
|
167
168
|
|
|
168
169
|
The symbol layer is hidden by default. `listNodes` drops `symbol` nodes and `listEdges` drops
|
|
169
170
|
`references` and `calls` edges unless you ask for them (`includeReferences` covers both), so a caller reasoning about module structure sees
|
|
@@ -233,7 +234,9 @@ fail a check.
|
|
|
233
234
|
flags every symbol strictly above the 95th percentile of `symbol_body_lines` over all symbols
|
|
234
235
|
that carry it, interpolated linearly between ranks. `percentile` runs from 50 to 100. The rule
|
|
235
236
|
stays silent until `minSample` nodes (default 20) carry the metric. Each violation's
|
|
236
|
-
`threshold` is the computed percentile value.
|
|
237
|
+
`threshold` is the computed percentile value. For sparse metrics whose percentile sits at or
|
|
238
|
+
near zero, `floor` flags a node only if its value also exceeds that absolute number, and
|
|
239
|
+
`rankNonZero: true` ranks and gates on non-zero carriers only.
|
|
237
240
|
|
|
238
241
|
```ts
|
|
239
242
|
import { checkSnapshot, loadCheckRules, openCodeGraph } from "@titan-design/code-graph";
|
package/dist/index.d.ts
CHANGED
|
@@ -175,8 +175,14 @@ declare const KIT: {
|
|
|
175
175
|
* `file_fingerprint` is the reuse basis the incremental indexer diffs against.
|
|
176
176
|
*/
|
|
177
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
|
+
/**
|
|
179
|
+
* Stored audit findings and model verdicts. `key` is a `findingKey`, not `Finding.id`,
|
|
180
|
+
* because an external tool's id embeds its line number and can repeat within one audit.
|
|
181
|
+
* The whole `Finding` and a verdict's citations live in `attrs`.
|
|
182
|
+
*/
|
|
183
|
+
declare const FINDING_DDL = "\n CREATE TABLE IF NOT EXISTS finding (\n snapshot_id INTEGER NOT NULL,\n key TEXT NOT NULL,\n finding_id TEXT NOT NULL,\n tool TEXT NOT NULL,\n signal TEXT NOT NULL,\n path TEXT NOT NULL,\n line_start INTEGER,\n line_end INTEGER,\n severity TEXT NOT NULL,\n excerpt_hash TEXT,\n attrs TEXT NOT NULL,\n PRIMARY KEY (snapshot_id, key)\n );\n CREATE INDEX IF NOT EXISTS idx_finding_path ON finding(snapshot_id, path);\n\n CREATE TABLE IF NOT EXISTS verdict (\n snapshot_id INTEGER NOT NULL,\n key TEXT NOT NULL,\n verdict TEXT NOT NULL CHECK (verdict IN ('confirmed', 'justified', 'unclear')),\n rationale TEXT NOT NULL,\n excerpt_hash TEXT NOT NULL,\n model TEXT,\n run_id TEXT,\n cost_usd REAL,\n provenance TEXT,\n control_run TEXT,\n carried_from INTEGER,\n attrs TEXT NOT NULL,\n PRIMARY KEY (snapshot_id, key)\n );\n";
|
|
178
184
|
/** 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"];
|
|
185
|
+
declare const SNAPSHOT_SCOPED_TABLES: readonly ["node", "edge", "metric", "id_alias", "file_fingerprint", "finding", "verdict"];
|
|
180
186
|
declare const MIGRATIONS: Migration[];
|
|
181
187
|
/** The top of this package's schema. A code graph database is never shared, so this is the top of one. */
|
|
182
188
|
declare const SCHEMA_VERSION: number;
|
|
@@ -1299,6 +1305,67 @@ interface ExternalDiagnostic {
|
|
|
1299
1305
|
declare function toFindings(result: CheckResult): Finding[];
|
|
1300
1306
|
declare function externalToFinding(input: ExternalDiagnostic): Finding;
|
|
1301
1307
|
|
|
1308
|
+
type VerdictLabel = "confirmed" | "justified" | "unclear";
|
|
1309
|
+
interface VerdictCitation {
|
|
1310
|
+
path: string;
|
|
1311
|
+
lineStart: number;
|
|
1312
|
+
lineEnd: number;
|
|
1313
|
+
quote: string;
|
|
1314
|
+
}
|
|
1315
|
+
/** A finding as stored against a snapshot, addressed by its `findingKey`. */
|
|
1316
|
+
interface StoredFinding {
|
|
1317
|
+
key: string;
|
|
1318
|
+
finding: Finding;
|
|
1319
|
+
/** Hash of the excerpt a reader would be shown for this finding; carry-forward compares it. */
|
|
1320
|
+
excerptHash?: string;
|
|
1321
|
+
}
|
|
1322
|
+
/** A model's judgement of one stored finding, after its citations were verified. */
|
|
1323
|
+
interface StoredVerdict {
|
|
1324
|
+
key: string;
|
|
1325
|
+
verdict: VerdictLabel;
|
|
1326
|
+
rationale: string;
|
|
1327
|
+
citations: VerdictCitation[];
|
|
1328
|
+
/** Hash of the excerpt the verdict judged. */
|
|
1329
|
+
excerptHash: string;
|
|
1330
|
+
model?: string;
|
|
1331
|
+
costUsd?: number;
|
|
1332
|
+
runId?: string;
|
|
1333
|
+
provenance?: string;
|
|
1334
|
+
controlRun?: "ok" | "provisional";
|
|
1335
|
+
dropped?: string[];
|
|
1336
|
+
/** The snapshot this verdict was carried from, when carry-forward copied it. */
|
|
1337
|
+
carriedFrom?: number;
|
|
1338
|
+
}
|
|
1339
|
+
interface FindingKeyInput {
|
|
1340
|
+
finding: Finding;
|
|
1341
|
+
/** The innermost symbol node id containing the finding; the finding's path when absent. */
|
|
1342
|
+
anchor?: string;
|
|
1343
|
+
/** The source text of the flagged lines. */
|
|
1344
|
+
flaggedText: string;
|
|
1345
|
+
excerptHash?: string;
|
|
1346
|
+
}
|
|
1347
|
+
/** Whitespace-insensitive, so reindenting or reflowing the flagged lines keeps the key. */
|
|
1348
|
+
declare function normalizeFlaggedText(text: string): string;
|
|
1349
|
+
declare function hashText(text: string): string;
|
|
1350
|
+
/**
|
|
1351
|
+
* A finding's identity across snapshots: no line number, so an inserted line above it
|
|
1352
|
+
* keeps the key, and a collision ordinal separates same-rule findings on the same text.
|
|
1353
|
+
*/
|
|
1354
|
+
declare function findingKey(input: FindingKeyInput, ordinal?: number): string;
|
|
1355
|
+
/** Key a whole audit's findings, numbering collisions in source order so the result ignores input order. */
|
|
1356
|
+
declare function keyFindings(inputs: readonly FindingKeyInput[]): StoredFinding[];
|
|
1357
|
+
/** Insert or replace findings by key within one snapshot. */
|
|
1358
|
+
declare function saveFindings(store: CodeGraphStore, snapshotId: number, findings: readonly StoredFinding[]): void;
|
|
1359
|
+
declare function listFindings(store: CodeGraphStore, snapshotId: number): StoredFinding[];
|
|
1360
|
+
/** Insert or replace verdicts by key within one snapshot. */
|
|
1361
|
+
declare function saveVerdicts(store: CodeGraphStore, snapshotId: number, verdicts: readonly StoredVerdict[]): void;
|
|
1362
|
+
declare function listVerdicts(store: CodeGraphStore, snapshotId: number): StoredVerdict[];
|
|
1363
|
+
/**
|
|
1364
|
+
* Copy each verdict of `from` onto the finding in `to` with the same key whose excerpt hash
|
|
1365
|
+
* equals the one the verdict judged. A verdict `to` already holds is kept. Returns the count copied.
|
|
1366
|
+
*/
|
|
1367
|
+
declare function carryForwardVerdicts(store: CodeGraphStore, from: number, to: number): number;
|
|
1368
|
+
|
|
1302
1369
|
/** Glob when the pattern has a `*` (`**` crosses directories, `*` stays in a segment); otherwise a case-sensitive substring. */
|
|
1303
1370
|
declare function patternToRegex(pattern: string): RegExp;
|
|
1304
1371
|
declare function compilePatterns(patterns: readonly string[] | undefined): RegExp[];
|
|
@@ -1619,4 +1686,4 @@ declare function getConventionMap(store: CodeGraphStore, snapshotId: number, mod
|
|
|
1619
1686
|
*/
|
|
1620
1687
|
declare function findConventions(store: CodeGraphStore, snapshotId: number, query: string, embedder: Embedder, model: string, opts?: FindConventionsOptions): Promise<ConventionQueryResult>;
|
|
1621
1688
|
|
|
1622
|
-
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, type FileFingerprint, type FindConventionsOptions, type FindSimilarOptions, type Finding, 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 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, externalToFinding, 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, percentileOf, planPrune, priorSnapshotForRef, pruneDanglingReferences, readSourceFiles, rebasedViolationKey, resolveAlias, resolveBarrelEdges, resolveChurnWindows, resolveGitRef, resolveSnapshot, runChecks, runPrune, snapshotPageRank, snapshotReferenceEdges, snapshotRelevance, snapshotSymbolConsumers, snapshotSymbolCoupling, snapshotViolations, structuralSignature, summarizeConventions, symbolId, testCoverageCountMetrics, toFindings, tryEmbedSnapshot, validateRules, violationKey, walkSourceFiles, windowSuffix };
|
|
1689
|
+
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 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 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, 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, testCoverageCountMetrics, toFindings, tryEmbedSnapshot, validateRules, violationKey, walkSourceFiles, windowSuffix };
|
package/dist/index.js
CHANGED
|
@@ -62,11 +62,53 @@ var DOMAIN_DDL = `
|
|
|
62
62
|
PRIMARY KEY (snapshot_id, file_id)
|
|
63
63
|
);
|
|
64
64
|
`;
|
|
65
|
-
var
|
|
65
|
+
var FINDING_DDL = `
|
|
66
|
+
CREATE TABLE IF NOT EXISTS finding (
|
|
67
|
+
snapshot_id INTEGER NOT NULL,
|
|
68
|
+
key TEXT NOT NULL,
|
|
69
|
+
finding_id TEXT NOT NULL,
|
|
70
|
+
tool TEXT NOT NULL,
|
|
71
|
+
signal TEXT NOT NULL,
|
|
72
|
+
path TEXT NOT NULL,
|
|
73
|
+
line_start INTEGER,
|
|
74
|
+
line_end INTEGER,
|
|
75
|
+
severity TEXT NOT NULL,
|
|
76
|
+
excerpt_hash TEXT,
|
|
77
|
+
attrs TEXT NOT NULL,
|
|
78
|
+
PRIMARY KEY (snapshot_id, key)
|
|
79
|
+
);
|
|
80
|
+
CREATE INDEX IF NOT EXISTS idx_finding_path ON finding(snapshot_id, path);
|
|
81
|
+
|
|
82
|
+
CREATE TABLE IF NOT EXISTS verdict (
|
|
83
|
+
snapshot_id INTEGER NOT NULL,
|
|
84
|
+
key TEXT NOT NULL,
|
|
85
|
+
verdict TEXT NOT NULL CHECK (verdict IN ('confirmed', 'justified', 'unclear')),
|
|
86
|
+
rationale TEXT NOT NULL,
|
|
87
|
+
excerpt_hash TEXT NOT NULL,
|
|
88
|
+
model TEXT,
|
|
89
|
+
run_id TEXT,
|
|
90
|
+
cost_usd REAL,
|
|
91
|
+
provenance TEXT,
|
|
92
|
+
control_run TEXT,
|
|
93
|
+
carried_from INTEGER,
|
|
94
|
+
attrs TEXT NOT NULL,
|
|
95
|
+
PRIMARY KEY (snapshot_id, key)
|
|
96
|
+
);
|
|
97
|
+
`;
|
|
98
|
+
var SNAPSHOT_SCOPED_TABLES = [
|
|
99
|
+
"node",
|
|
100
|
+
"edge",
|
|
101
|
+
"metric",
|
|
102
|
+
"id_alias",
|
|
103
|
+
"file_fingerprint",
|
|
104
|
+
"finding",
|
|
105
|
+
"verdict"
|
|
106
|
+
];
|
|
66
107
|
var MIGRATIONS = [
|
|
67
108
|
kitMigration(1, { snapshot: KIT.snapshot, entitySnap: KIT.entitySnap, cacheBlob: KIT.cacheBlob }),
|
|
68
109
|
{ version: 2, name: "code graph columns", up: (db) => db.exec(KIT_EXTENSIONS) },
|
|
69
|
-
{ version: 3, name: "code graph tables", up: (db) => db.exec(DOMAIN_DDL) }
|
|
110
|
+
{ version: 3, name: "code graph tables", up: (db) => db.exec(DOMAIN_DDL) },
|
|
111
|
+
{ version: 4, name: "findings and verdicts", up: (db) => db.exec(FINDING_DDL) }
|
|
70
112
|
];
|
|
71
113
|
var SCHEMA_VERSION = Math.max(...MIGRATIONS.map((m) => m.version));
|
|
72
114
|
|
|
@@ -1592,6 +1634,9 @@ function isInsideGitRepo(repoRoot) {
|
|
|
1592
1634
|
function resolveGitRef(repoRoot, ref) {
|
|
1593
1635
|
return runGit(repoRoot, ["rev-parse", ref]);
|
|
1594
1636
|
}
|
|
1637
|
+
function isGitAncestor(repoRoot, ancestor, descendant) {
|
|
1638
|
+
return runGit(repoRoot, ["merge-base", "--is-ancestor", ancestor, descendant]) !== null;
|
|
1639
|
+
}
|
|
1595
1640
|
function detectRenames(options) {
|
|
1596
1641
|
const threshold = options.similarityThreshold ?? 80;
|
|
1597
1642
|
const args = [
|
|
@@ -1763,9 +1808,15 @@ function priorSnapshotForRef(store, ref, options = {}) {
|
|
|
1763
1808
|
const byCommit = commit ? candidates2.find((s) => s.commitHash === commit) : void 0;
|
|
1764
1809
|
return byCommit ?? candidates2.find((s) => s.ref === ref) ?? null;
|
|
1765
1810
|
}
|
|
1766
|
-
function
|
|
1811
|
+
function aliasTargetCommit(target) {
|
|
1812
|
+
return target.commit ?? detectGitHead(target.repoRoot);
|
|
1813
|
+
}
|
|
1814
|
+
function aliasBaseFor(store, ref, target) {
|
|
1767
1815
|
const committed = allSnapshots(store).filter((s) => s.commitHash).reverse();
|
|
1768
|
-
|
|
1816
|
+
const candidate = committed.find((s) => s.ref === ref) ?? committed[0];
|
|
1817
|
+
if (!candidate?.commitHash) return null;
|
|
1818
|
+
const commit = aliasTargetCommit(target);
|
|
1819
|
+
return commit && isGitAncestor(target.repoRoot, candidate.commitHash, commit) ? candidate : null;
|
|
1769
1820
|
}
|
|
1770
1821
|
|
|
1771
1822
|
// src/identity/index-aliases.ts
|
|
@@ -1786,17 +1837,17 @@ function movedSymbolAliases(store, baseSnapshotId, fileAliases, nodes) {
|
|
|
1786
1837
|
}
|
|
1787
1838
|
return out;
|
|
1788
1839
|
}
|
|
1789
|
-
function renameAliases(input, baseCommit) {
|
|
1790
|
-
if (input.detectRenames === false ||
|
|
1791
|
-
const target = input.commitHash ?? detectGitHead(input.rootDir) ?? void 0;
|
|
1792
|
-
if (target === baseCommit) return [];
|
|
1840
|
+
function renameAliases(input, baseCommit, target) {
|
|
1841
|
+
if (input.detectRenames === false || target === baseCommit) return [];
|
|
1793
1842
|
const pairs = detectRenames({ repoRoot: input.rootDir, fromCommit: baseCommit, toCommit: target });
|
|
1794
1843
|
return buildAliases(input.idRoot, pairs);
|
|
1795
1844
|
}
|
|
1796
1845
|
function computeAliasBridge(store, input) {
|
|
1797
|
-
const
|
|
1798
|
-
|
|
1799
|
-
const
|
|
1846
|
+
const target = { repoRoot: input.rootDir, commit: input.commitHash };
|
|
1847
|
+
const base = aliasBaseFor(store, input.ref, target);
|
|
1848
|
+
const commit = base ? aliasTargetCommit(target) : null;
|
|
1849
|
+
if (!base?.commitHash || !commit) return { baseSnapshotId: null, aliases: [] };
|
|
1850
|
+
const fileAliases = renameAliases(input, base.commitHash, commit);
|
|
1800
1851
|
const symbols = movedSymbolAliases(store, base.id, fileAliases, input.nodes);
|
|
1801
1852
|
return { baseSnapshotId: base.id, aliases: [...fileAliases, ...symbols] };
|
|
1802
1853
|
}
|
|
@@ -5605,6 +5656,127 @@ function withoutUndefined(finding) {
|
|
|
5605
5656
|
return Object.fromEntries(Object.entries(finding).filter(([, v]) => v !== void 0));
|
|
5606
5657
|
}
|
|
5607
5658
|
|
|
5659
|
+
// src/check/finding-store.ts
|
|
5660
|
+
import { createHash as createHash2 } from "crypto";
|
|
5661
|
+
function normalizeFlaggedText(text) {
|
|
5662
|
+
return text.split(/\r?\n/).map((line) => line.trim().replace(/\s+/g, " ")).filter((line) => line.length > 0).join("\n");
|
|
5663
|
+
}
|
|
5664
|
+
function hashText(text) {
|
|
5665
|
+
return createHash2("sha256").update(text, "utf8").digest("hex").slice(0, 16);
|
|
5666
|
+
}
|
|
5667
|
+
function findingKey(input, ordinal = 0) {
|
|
5668
|
+
const { finding } = input;
|
|
5669
|
+
const anchor = input.anchor ?? finding.path;
|
|
5670
|
+
const textHash = hashText(normalizeFlaggedText(input.flaggedText));
|
|
5671
|
+
return `${finding.tool}:${finding.signal}:${anchor}:${textHash}#${ordinal}`;
|
|
5672
|
+
}
|
|
5673
|
+
function keyFindings(inputs) {
|
|
5674
|
+
const ordered = [...inputs].sort(compareBySource);
|
|
5675
|
+
const seen = /* @__PURE__ */ new Map();
|
|
5676
|
+
return ordered.map((input) => {
|
|
5677
|
+
const base = findingKey(input);
|
|
5678
|
+
const ordinal = seen.get(base) ?? 0;
|
|
5679
|
+
seen.set(base, ordinal + 1);
|
|
5680
|
+
const stored = { key: findingKey(input, ordinal), finding: input.finding };
|
|
5681
|
+
if (input.excerptHash !== void 0) stored.excerptHash = input.excerptHash;
|
|
5682
|
+
return stored;
|
|
5683
|
+
});
|
|
5684
|
+
}
|
|
5685
|
+
function compareBySource(a, b) {
|
|
5686
|
+
const fa = a.finding;
|
|
5687
|
+
const fb = b.finding;
|
|
5688
|
+
return fa.path.localeCompare(fb.path) || (fa.lineStart ?? 0) - (fb.lineStart ?? 0) || (fa.lineEnd ?? 0) - (fb.lineEnd ?? 0) || (fa.evidence ?? "").localeCompare(fb.evidence ?? "") || fa.id.localeCompare(fb.id);
|
|
5689
|
+
}
|
|
5690
|
+
function saveFindings(store, snapshotId, findings) {
|
|
5691
|
+
const insert = store.db.prepare(
|
|
5692
|
+
`INSERT OR REPLACE INTO finding
|
|
5693
|
+
(snapshot_id, key, finding_id, tool, signal, path, line_start, line_end, severity, excerpt_hash, attrs)
|
|
5694
|
+
VALUES (@snapshotId, @key, @findingId, @tool, @signal, @path, @lineStart, @lineEnd, @severity, @excerptHash, @attrs)`
|
|
5695
|
+
);
|
|
5696
|
+
store.db.transaction(() => {
|
|
5697
|
+
for (const { key, finding, excerptHash } of findings) {
|
|
5698
|
+
insert.run({
|
|
5699
|
+
snapshotId,
|
|
5700
|
+
key,
|
|
5701
|
+
findingId: finding.id,
|
|
5702
|
+
tool: finding.tool,
|
|
5703
|
+
signal: finding.signal,
|
|
5704
|
+
path: finding.path,
|
|
5705
|
+
lineStart: finding.lineStart ?? null,
|
|
5706
|
+
lineEnd: finding.lineEnd ?? null,
|
|
5707
|
+
severity: finding.severity,
|
|
5708
|
+
excerptHash: excerptHash ?? null,
|
|
5709
|
+
attrs: JSON.stringify(finding)
|
|
5710
|
+
});
|
|
5711
|
+
}
|
|
5712
|
+
})();
|
|
5713
|
+
}
|
|
5714
|
+
function listFindings(store, snapshotId) {
|
|
5715
|
+
const rows = store.db.prepare("SELECT key, excerpt_hash, attrs FROM finding WHERE snapshot_id = ? ORDER BY key").all(snapshotId);
|
|
5716
|
+
return rows.map((row) => {
|
|
5717
|
+
const stored = { key: row.key, finding: JSON.parse(row.attrs) };
|
|
5718
|
+
if (row.excerpt_hash !== null) stored.excerptHash = row.excerpt_hash;
|
|
5719
|
+
return stored;
|
|
5720
|
+
});
|
|
5721
|
+
}
|
|
5722
|
+
function saveVerdicts(store, snapshotId, verdicts) {
|
|
5723
|
+
const insert = store.db.prepare(
|
|
5724
|
+
`INSERT OR REPLACE INTO verdict
|
|
5725
|
+
(snapshot_id, key, verdict, rationale, excerpt_hash, model, run_id, cost_usd, provenance, control_run,
|
|
5726
|
+
carried_from, attrs)
|
|
5727
|
+
VALUES (@snapshotId, @key, @verdict, @rationale, @excerptHash, @model, @runId, @costUsd, @provenance,
|
|
5728
|
+
@controlRun, @carriedFrom, @attrs)`
|
|
5729
|
+
);
|
|
5730
|
+
store.db.transaction(() => {
|
|
5731
|
+
for (const v of verdicts) insert.run(verdictParams(snapshotId, v));
|
|
5732
|
+
})();
|
|
5733
|
+
}
|
|
5734
|
+
function verdictParams(snapshotId, v) {
|
|
5735
|
+
return {
|
|
5736
|
+
snapshotId,
|
|
5737
|
+
key: v.key,
|
|
5738
|
+
verdict: v.verdict,
|
|
5739
|
+
rationale: v.rationale,
|
|
5740
|
+
excerptHash: v.excerptHash,
|
|
5741
|
+
model: v.model ?? null,
|
|
5742
|
+
runId: v.runId ?? null,
|
|
5743
|
+
costUsd: v.costUsd ?? null,
|
|
5744
|
+
provenance: v.provenance ?? null,
|
|
5745
|
+
controlRun: v.controlRun ?? null,
|
|
5746
|
+
carriedFrom: v.carriedFrom ?? null,
|
|
5747
|
+
attrs: JSON.stringify({ citations: v.citations, dropped: v.dropped ?? [] })
|
|
5748
|
+
};
|
|
5749
|
+
}
|
|
5750
|
+
function listVerdicts(store, snapshotId) {
|
|
5751
|
+
const rows = store.db.prepare("SELECT * FROM verdict WHERE snapshot_id = ? ORDER BY key").all(snapshotId);
|
|
5752
|
+
return rows.map(rowToVerdict);
|
|
5753
|
+
}
|
|
5754
|
+
function rowToVerdict(row) {
|
|
5755
|
+
const attrs = JSON.parse(row.attrs);
|
|
5756
|
+
const verdict = {
|
|
5757
|
+
key: row.key,
|
|
5758
|
+
verdict: row.verdict,
|
|
5759
|
+
rationale: row.rationale,
|
|
5760
|
+
citations: attrs.citations,
|
|
5761
|
+
excerptHash: row.excerpt_hash
|
|
5762
|
+
};
|
|
5763
|
+
if (row.model !== null) verdict.model = row.model;
|
|
5764
|
+
if (row.cost_usd !== null) verdict.costUsd = row.cost_usd;
|
|
5765
|
+
if (row.run_id !== null) verdict.runId = row.run_id;
|
|
5766
|
+
if (row.provenance !== null) verdict.provenance = row.provenance;
|
|
5767
|
+
if (row.control_run !== null) verdict.controlRun = row.control_run;
|
|
5768
|
+
if (attrs.dropped.length > 0) verdict.dropped = attrs.dropped;
|
|
5769
|
+
if (row.carried_from !== null) verdict.carriedFrom = row.carried_from;
|
|
5770
|
+
return verdict;
|
|
5771
|
+
}
|
|
5772
|
+
function carryForwardVerdicts(store, from, to) {
|
|
5773
|
+
const targets = new Map(listFindings(store, to).map((f) => [f.key, f.excerptHash]));
|
|
5774
|
+
const existing = new Set(listVerdicts(store, to).map((v) => v.key));
|
|
5775
|
+
const carried = listVerdicts(store, from).filter((v) => !existing.has(v.key) && targets.get(v.key) === v.excerptHash).map((v) => ({ ...v, carriedFrom: v.carriedFrom ?? from }));
|
|
5776
|
+
saveVerdicts(store, to, carried);
|
|
5777
|
+
return carried.length;
|
|
5778
|
+
}
|
|
5779
|
+
|
|
5608
5780
|
// src/check/validate.ts
|
|
5609
5781
|
function validateRules(input, options = {}) {
|
|
5610
5782
|
if (!input || typeof input !== "object") {
|
|
@@ -6489,6 +6661,7 @@ export {
|
|
|
6489
6661
|
DEFAULT_OUTLIER_MIN_SAMPLE,
|
|
6490
6662
|
DOMAIN_DDL,
|
|
6491
6663
|
EXCEPTION_METRIC_NAMES,
|
|
6664
|
+
FINDING_DDL,
|
|
6492
6665
|
GROWTH_RISK_METRIC_NAMES,
|
|
6493
6666
|
INDEX_VERSION,
|
|
6494
6667
|
KIT,
|
|
@@ -6516,6 +6689,7 @@ export {
|
|
|
6516
6689
|
canonicalEdgeKind,
|
|
6517
6690
|
canonicalMetricName,
|
|
6518
6691
|
canonicalRole,
|
|
6692
|
+
carryForwardVerdicts,
|
|
6519
6693
|
checkSnapshot,
|
|
6520
6694
|
classifyRole,
|
|
6521
6695
|
collectDeclaredNames,
|
|
@@ -6552,6 +6726,7 @@ export {
|
|
|
6552
6726
|
fileId,
|
|
6553
6727
|
findConventions,
|
|
6554
6728
|
findSimilarCapability,
|
|
6729
|
+
findingKey,
|
|
6555
6730
|
getConventionMap,
|
|
6556
6731
|
getEdgeWeight,
|
|
6557
6732
|
getLanguageFromPath3 as getLanguageFromPath,
|
|
@@ -6559,25 +6734,30 @@ export {
|
|
|
6559
6734
|
groupTestsBySource,
|
|
6560
6735
|
hashContent,
|
|
6561
6736
|
hashEmbedText,
|
|
6737
|
+
hashText,
|
|
6562
6738
|
indexPaths,
|
|
6563
6739
|
invertBuckets,
|
|
6564
6740
|
isGeneratedByHeuristic,
|
|
6565
6741
|
isGeneratedFile,
|
|
6566
6742
|
isInsideGitRepo,
|
|
6743
|
+
keyFindings,
|
|
6567
6744
|
lineagePath,
|
|
6568
6745
|
linkTestsToSources,
|
|
6569
6746
|
listEdges,
|
|
6570
6747
|
listEdgesTouching,
|
|
6571
6748
|
listEmbeddableSymbols,
|
|
6749
|
+
listFindings,
|
|
6572
6750
|
listMetrics,
|
|
6573
6751
|
listMetricsForNode,
|
|
6574
6752
|
listNodes,
|
|
6753
|
+
listVerdicts,
|
|
6575
6754
|
loadCheckRules,
|
|
6576
6755
|
loadGeneratedPatterns,
|
|
6577
6756
|
loadHistoryMetrics,
|
|
6578
6757
|
loadLineage,
|
|
6579
6758
|
matchesAny,
|
|
6580
6759
|
moduleId,
|
|
6760
|
+
normalizeFlaggedText,
|
|
6581
6761
|
openCodeGraph,
|
|
6582
6762
|
packageId,
|
|
6583
6763
|
parentModuleId,
|
|
@@ -6597,6 +6777,8 @@ export {
|
|
|
6597
6777
|
resolveSnapshot,
|
|
6598
6778
|
runChecks,
|
|
6599
6779
|
runPrune,
|
|
6780
|
+
saveFindings,
|
|
6781
|
+
saveVerdicts,
|
|
6600
6782
|
shouldIncludeFile2 as shouldIncludeFile,
|
|
6601
6783
|
snapshotPageRank,
|
|
6602
6784
|
snapshotReferenceEdges,
|