ucn 4.1.3 → 4.2.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/.claude/skills/ucn/SKILL.md +80 -353
- package/.claude/skills/ucn/references/commands.md +93 -0
- package/.claude/skills/ucn/references/trust-contract.md +63 -0
- package/README.md +67 -44
- package/cli/index.js +24 -19
- package/core/account.js +18 -0
- package/core/analysis.js +27 -7
- package/core/bridge.js +0 -1
- package/core/brief.js +2 -3
- package/core/build-worker.js +5 -0
- package/core/cache.js +51 -6
- package/core/callers.js +1709 -162
- package/core/check.js +107 -19
- package/core/confidence.js +29 -16
- package/core/deadcode.js +1 -2
- package/core/entrypoints.js +32 -27
- package/core/execute.js +19 -3
- package/core/graph-build.js +33 -0
- package/core/output/analysis.js +53 -14
- package/core/output/check.js +11 -0
- package/core/output/doctor.js +40 -19
- package/core/output/endpoints.js +0 -2
- package/core/output/search.js +26 -1
- package/core/parser.js +1 -1
- package/core/project.js +25 -4
- package/core/registry.js +2 -2
- package/core/reporting.js +166 -108
- package/core/search.js +271 -49
- package/core/tracing.js +2 -2
- package/core/trust-matrix.js +158 -0
- package/core/verify.js +0 -1
- package/eslint.config.js +2 -2
- package/languages/go.js +143 -13
- package/languages/html.js +5 -0
- package/languages/index.js +5 -0
- package/languages/java.js +61 -4
- package/languages/javascript.js +233 -42
- package/languages/python.js +54 -15
- package/languages/rust.js +180 -22
- package/languages/utils.js +0 -1
- package/mcp/server.js +118 -38
- package/package.json +11 -4
package/core/search.js
CHANGED
|
@@ -11,7 +11,7 @@ const path = require('path');
|
|
|
11
11
|
const { escapeRegExp, codeUnitCompare, inlineTestRanges, lineInRanges, classDispatchNames } = require('./shared');
|
|
12
12
|
const { isTestFile } = require('./discovery');
|
|
13
13
|
const { detectLanguage, getParser, getLanguageModule, langTraits } = require('../languages');
|
|
14
|
-
const { getCachedCalls } = require('./callers');
|
|
14
|
+
const { getCachedCalls, _nameBindingReaches } = require('./callers');
|
|
15
15
|
const { extractImports } = require('./imports');
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -181,6 +181,7 @@ function usages(index, name, options = {}) {
|
|
|
181
181
|
const definitions = options.exclude || options.in
|
|
182
182
|
? allDefinitions.filter(d => index.matchesFilters(d.relativePath, options))
|
|
183
183
|
: allDefinitions;
|
|
184
|
+
const targetFiles = new Set(allDefinitions.map(d => d.file).filter(Boolean));
|
|
184
185
|
|
|
185
186
|
for (const def of definitions) {
|
|
186
187
|
usagesList.push({
|
|
@@ -230,6 +231,19 @@ function usages(index, name, options = {}) {
|
|
|
230
231
|
_importedHasDef = true;
|
|
231
232
|
break;
|
|
232
233
|
}
|
|
234
|
+
// A module namespace may expose the target through a
|
|
235
|
+
// re-export chain (`import httpx; httpx.URL(...)`,
|
|
236
|
+
// where httpx/__init__.py re-exports URL). Direct-file
|
|
237
|
+
// symbol checks silently dropped these compiler-true
|
|
238
|
+
// usages. Reuse the caller engine's conservative
|
|
239
|
+
// name-ownership chase: yes confirms; unknown stays
|
|
240
|
+
// visible in this raw-usage inventory; only a proven
|
|
241
|
+
// no is filtering evidence.
|
|
242
|
+
if (targetFiles.size > 0 &&
|
|
243
|
+
_nameBindingReaches(index, imp, name, targetFiles) !== 'no') {
|
|
244
|
+
_importedHasDef = true;
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
233
247
|
}
|
|
234
248
|
return _importedHasDef;
|
|
235
249
|
};
|
|
@@ -833,40 +847,108 @@ function structuralSearch(index, options = {}) {
|
|
|
833
847
|
function example(index, name, options = {}) {
|
|
834
848
|
index._beginOp();
|
|
835
849
|
try {
|
|
836
|
-
//
|
|
837
|
-
//
|
|
838
|
-
//
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
: ['test', 'spec', '__tests__', '__mocks__', 'fixture', 'mock'];
|
|
842
|
-
const usageResults = usages(index, name, {
|
|
843
|
-
codeOnly: true,
|
|
844
|
-
className: options.className,
|
|
845
|
-
// Scope examples to files matching --file (the flag was accepted but
|
|
846
|
-
// silently ignored before).
|
|
850
|
+
// Resolve the exact target first. `--file`/stable handles identify the
|
|
851
|
+
// DEFINITION; they must never restrict candidate call sites to that same
|
|
852
|
+
// file (the old implementation did exactly that, selecting recursive or
|
|
853
|
+
// sibling calls while hiding every external example).
|
|
854
|
+
const { def } = index.resolveSymbol(name, {
|
|
847
855
|
file: options.file,
|
|
848
|
-
|
|
849
|
-
|
|
856
|
+
className: options.className,
|
|
857
|
+
line: options.line,
|
|
850
858
|
});
|
|
859
|
+
if (!def) return null;
|
|
860
|
+
|
|
861
|
+
// Candidate identity comes from the contracted caller engine, not raw
|
|
862
|
+
// same-name usages. This prevents an example for Java A.equals from being
|
|
863
|
+
// selected out of B.equals and lets module re-exports/receiver evidence
|
|
864
|
+
// participate exactly as they do in about/context/impact.
|
|
865
|
+
const rawCallers = index.findCallers(name, {
|
|
866
|
+
targetDefinitions: [def],
|
|
867
|
+
collectAccount: true,
|
|
868
|
+
});
|
|
869
|
+
const candidates = [
|
|
870
|
+
...rawCallers.filter(c => c.tier !== 'unverified')
|
|
871
|
+
.map(c => ({ ...c, evidenceTier: 'confirmed' })),
|
|
872
|
+
...rawCallers.filter(c => c.tier === 'unverified')
|
|
873
|
+
.map(c => ({ ...c, evidenceTier: 'unverified' })),
|
|
874
|
+
...(rawCallers.unverifiedEntries || [])
|
|
875
|
+
.map(c => ({ ...c, evidenceTier: 'unverified' })),
|
|
876
|
+
].filter(c => !c.functionReference && c.calledAs !== 'bound');
|
|
877
|
+
|
|
878
|
+
// Dedupe by site, preferring confirmed evidence when a parser shape
|
|
879
|
+
// reaches the same line through more than one resolution path.
|
|
880
|
+
candidates.sort((a, b) =>
|
|
881
|
+
(a.evidenceTier === b.evidenceTier ? 0 : (a.evidenceTier === 'confirmed' ? -1 : 1)) ||
|
|
882
|
+
codeUnitCompare(a.relativePath || a.file || '', b.relativePath || b.file || '') ||
|
|
883
|
+
(a.line || 0) - (b.line || 0));
|
|
884
|
+
const seenSites = new Set();
|
|
885
|
+
const allCalls = [];
|
|
886
|
+
for (const candidate of candidates) {
|
|
887
|
+
const rel = candidate.relativePath || path.relative(index.root, candidate.file);
|
|
888
|
+
const siteKey = `${rel}:${candidate.line}`;
|
|
889
|
+
if (seenSites.has(siteKey)) continue;
|
|
890
|
+
seenSites.add(siteKey);
|
|
891
|
+
const filePath = path.isAbsolute(candidate.file)
|
|
892
|
+
? candidate.file : path.join(index.root, rel);
|
|
893
|
+
let lines = [];
|
|
894
|
+
try { lines = index._readFile(filePath).split('\n'); } catch { /* fail-loud metadata stays on the project */ }
|
|
895
|
+
const at = candidate.line - 1;
|
|
896
|
+
allCalls.push({
|
|
897
|
+
...candidate,
|
|
898
|
+
file: filePath,
|
|
899
|
+
relativePath: rel,
|
|
900
|
+
usageType: 'call',
|
|
901
|
+
content: candidate.content || lines[at] || '',
|
|
902
|
+
before: lines.slice(Math.max(0, at - 5), at),
|
|
903
|
+
after: lines.slice(at + 1, at + 6),
|
|
904
|
+
});
|
|
905
|
+
}
|
|
851
906
|
|
|
852
|
-
const
|
|
907
|
+
const callSiteInScope = call => {
|
|
908
|
+
if (!options.callSiteFile) return true;
|
|
909
|
+
const resolved = index.resolveFilePathForQuery(options.callSiteFile);
|
|
910
|
+
if (typeof resolved === 'string') return call.file === resolved;
|
|
911
|
+
return call.relativePath.includes(options.callSiteFile);
|
|
912
|
+
};
|
|
913
|
+
const confirmedAllCalls = allCalls.filter(call =>
|
|
914
|
+
call.evidenceTier === 'confirmed' && callSiteInScope(call));
|
|
915
|
+
const unverifiedAllCalls = allCalls.filter(call =>
|
|
916
|
+
call.evidenceTier === 'unverified' && callSiteInScope(call));
|
|
917
|
+
const includeByTestPolicy = call => {
|
|
918
|
+
if (options.includeTests) return true;
|
|
919
|
+
const fe = index.files.get(call.file);
|
|
920
|
+
return !isTestFile(call.relativePath, fe?.language);
|
|
921
|
+
};
|
|
922
|
+
// A command called `example` must never silently promote a same-name or
|
|
923
|
+
// possible-dispatch candidate into a target example. Select only from the
|
|
924
|
+
// confirmed evidence band; report the unverified pool as an abstention.
|
|
925
|
+
const calls = confirmedAllCalls.filter(includeByTestPolicy);
|
|
926
|
+
const visibleUnverified = unverifiedAllCalls.filter(includeByTestPolicy);
|
|
853
927
|
if (calls.length === 0) {
|
|
854
|
-
//
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
928
|
+
// Tell the user when compiler-shaped candidates exist only in tests.
|
|
929
|
+
if (!options.includeTests && confirmedAllCalls.length > 0) {
|
|
930
|
+
return {
|
|
931
|
+
best: null,
|
|
932
|
+
totalCalls: 0,
|
|
933
|
+
confirmedCalls: 0,
|
|
934
|
+
unverifiedCalls: visibleUnverified.length,
|
|
935
|
+
excludedTestCalls: confirmedAllCalls.length,
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
if (visibleUnverified.length > 0) {
|
|
939
|
+
return {
|
|
940
|
+
advisory: 'scored-selection',
|
|
941
|
+
best: null,
|
|
942
|
+
totalCalls: 0,
|
|
943
|
+
confirmedCalls: 0,
|
|
944
|
+
unverifiedCalls: visibleUnverified.length,
|
|
945
|
+
unverifiedCandidates: visibleUnverified.slice(0, 10).map(c => ({
|
|
946
|
+
file: c.relativePath,
|
|
947
|
+
line: c.line,
|
|
948
|
+
content: c.content,
|
|
949
|
+
reason: c.reason || c.resolution || 'identity-unverified',
|
|
950
|
+
})),
|
|
951
|
+
};
|
|
870
952
|
}
|
|
871
953
|
return null;
|
|
872
954
|
}
|
|
@@ -916,7 +998,13 @@ function example(index, name, options = {}) {
|
|
|
916
998
|
// Advisory command (v4 two-tier surface): scored example selection, not
|
|
917
999
|
// a verified "best" claim.
|
|
918
1000
|
if (!options.diverse) {
|
|
919
|
-
return {
|
|
1001
|
+
return {
|
|
1002
|
+
advisory: 'scored-selection',
|
|
1003
|
+
best,
|
|
1004
|
+
totalCalls: calls.length,
|
|
1005
|
+
confirmedCalls: calls.filter(c => c.evidenceTier === 'confirmed').length,
|
|
1006
|
+
unverifiedCalls: visibleUnverified.length,
|
|
1007
|
+
};
|
|
920
1008
|
}
|
|
921
1009
|
|
|
922
1010
|
// --diverse: cluster call sites by AST argument-shape and return one
|
|
@@ -977,6 +1065,8 @@ function example(index, name, options = {}) {
|
|
|
977
1065
|
advisory: 'scored-selection',
|
|
978
1066
|
best,
|
|
979
1067
|
totalCalls: calls.length,
|
|
1068
|
+
confirmedCalls: calls.filter(c => c.evidenceTier === 'confirmed').length,
|
|
1069
|
+
unverifiedCalls: visibleUnverified.length,
|
|
980
1070
|
clusters,
|
|
981
1071
|
totalClusters: clusterList.length,
|
|
982
1072
|
};
|
|
@@ -1027,14 +1117,17 @@ function tests(index, nameOrFile, options = {}) {
|
|
|
1027
1117
|
// Resolve --file scoping: find the source file that defines this symbol
|
|
1028
1118
|
// and only include test files that import from it (directly or via re-exports).
|
|
1029
1119
|
let sourceFileFilter = null;
|
|
1120
|
+
let targetDefs = [];
|
|
1030
1121
|
if (options.file && !isFilePath) {
|
|
1031
|
-
|
|
1032
|
-
if (
|
|
1033
|
-
sourceFileFilter = _buildSourceFileImporters(index,
|
|
1122
|
+
targetDefs = index.find(nameOrFile, { exact: true, file: options.file, className: options.className });
|
|
1123
|
+
if (targetDefs.length > 0) {
|
|
1124
|
+
sourceFileFilter = _buildSourceFileImporters(index, targetDefs);
|
|
1034
1125
|
}
|
|
1035
1126
|
// If no defs found, sourceFileFilter stays null → no file scoping applied.
|
|
1036
1127
|
// The execute handler validates before calling, so this path means
|
|
1037
1128
|
// the file matched but no exact symbol — fall through gracefully.
|
|
1129
|
+
} else if (!isFilePath) {
|
|
1130
|
+
targetDefs = index.find(nameOrFile, { exact: true, className: options.className });
|
|
1038
1131
|
}
|
|
1039
1132
|
|
|
1040
1133
|
// Find all test files
|
|
@@ -1133,7 +1226,9 @@ function tests(index, nameOrFile, options = {}) {
|
|
|
1133
1226
|
let localShadow = false;
|
|
1134
1227
|
if (!isFilePath) {
|
|
1135
1228
|
const localDefs = (entry.symbols || []).filter(s => s.name === searchTerm);
|
|
1229
|
+
const targetDefinedHere = !!options.file && targetDefs.some(d => d.file === testPath);
|
|
1136
1230
|
if (localDefs.length > 0 &&
|
|
1231
|
+
!targetDefinedHere &&
|
|
1137
1232
|
!(entry.importBindings || []).some(b => b.name === searchTerm)) {
|
|
1138
1233
|
localShadow = testRanges
|
|
1139
1234
|
? localDefs.every(s => lineInRanges(s.startLine, testRanges))
|
|
@@ -1179,7 +1274,8 @@ function tests(index, nameOrFile, options = {}) {
|
|
|
1179
1274
|
matchType = 'call';
|
|
1180
1275
|
} else {
|
|
1181
1276
|
// 'reference' — check if inside string literal
|
|
1182
|
-
matchType = strPattern.test(lineContent) ? 'string-ref' :
|
|
1277
|
+
matchType = strPattern.test(lineContent) ? 'string-ref' :
|
|
1278
|
+
(usage.inAttribute ? 'unverified-reference' : 'reference');
|
|
1183
1279
|
}
|
|
1184
1280
|
|
|
1185
1281
|
// className scoping for calls: check receiver
|
|
@@ -1188,11 +1284,22 @@ function tests(index, nameOrFile, options = {}) {
|
|
|
1188
1284
|
}
|
|
1189
1285
|
|
|
1190
1286
|
// className scoping for references: require class-associated receiver
|
|
1191
|
-
if (className && (matchType === 'reference' || matchType === 'string-ref'
|
|
1287
|
+
if (className && (matchType === 'reference' || matchType === 'string-ref' ||
|
|
1288
|
+
matchType === 'unverified-reference')) {
|
|
1192
1289
|
// Bare references (no receiver) like `fn = save` have no class
|
|
1193
1290
|
// association — skip them. Only keep member-access references
|
|
1194
1291
|
// where the receiver matches the target class.
|
|
1195
|
-
if (!usage.receiver) continue;
|
|
1292
|
+
if (!usage.receiver && matchType !== 'unverified-reference') continue;
|
|
1293
|
+
if (!usage.receiver && matchType === 'unverified-reference') {
|
|
1294
|
+
matches.push({
|
|
1295
|
+
line: usage.line,
|
|
1296
|
+
content: lineContent.trim(),
|
|
1297
|
+
matchType,
|
|
1298
|
+
evidenceTier: 'unverified',
|
|
1299
|
+
reason: 'macro-generated-reference',
|
|
1300
|
+
});
|
|
1301
|
+
continue;
|
|
1302
|
+
}
|
|
1196
1303
|
if (!dispatchNames.has(usage.receiver) &&
|
|
1197
1304
|
!(instanceTypeMap && dispatchNames.has(instanceTypeMap.get(usage.receiver)))) {
|
|
1198
1305
|
continue;
|
|
@@ -1275,6 +1382,90 @@ function tests(index, nameOrFile, options = {}) {
|
|
|
1275
1382
|
}
|
|
1276
1383
|
}
|
|
1277
1384
|
|
|
1385
|
+
// Compiler-shaped caller supplement (trust command arm): the literal
|
|
1386
|
+
// usage scan above is excellent for imports/type/string references, but
|
|
1387
|
+
// receiver aliases and re-exported module calls are owned by the caller
|
|
1388
|
+
// engine. Merge its CONFIRMED exact-target test sites so `tests` cannot
|
|
1389
|
+
// miss calls that `about`/`impact` already know about. For a globally
|
|
1390
|
+
// unique callable name, also retain account-unverified sites under the
|
|
1391
|
+
// explicit `unverified-call` label: independent compiler/LSP evaluation
|
|
1392
|
+
// shows these contain real interface/trait dispatch that a test-discovery
|
|
1393
|
+
// command must not silently drop. Repeated names never receive that broad
|
|
1394
|
+
// supplement because it would leak sibling classes/files.
|
|
1395
|
+
if (!isFilePath && targetDefs.length > 0) {
|
|
1396
|
+
const testInfo = new Map(testFiles.map(t => [t.path, t]));
|
|
1397
|
+
const callerSites = [];
|
|
1398
|
+
const globallyUniqueTarget = index.find(searchTerm, { exact: true }).length === 1;
|
|
1399
|
+
for (const def of targetDefs) {
|
|
1400
|
+
const raw = index.findCallers(searchTerm, {
|
|
1401
|
+
targetDefinitions: [def],
|
|
1402
|
+
collectAccount: true,
|
|
1403
|
+
});
|
|
1404
|
+
callerSites.push(
|
|
1405
|
+
...raw.map(c => ({ ...c, _testEvidenceTier: 'confirmed' })),
|
|
1406
|
+
...(globallyUniqueTarget ? (raw.unverifiedEntries || []).map(c => ({
|
|
1407
|
+
...c,
|
|
1408
|
+
_testEvidenceTier: 'unverified',
|
|
1409
|
+
})) : []),
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
for (const site of callerSites) {
|
|
1414
|
+
const info = testInfo.get(site.file);
|
|
1415
|
+
if (!info) continue;
|
|
1416
|
+
if (info.testRanges && !lineInRanges(site.line, info.testRanges)) continue;
|
|
1417
|
+
// For a repeated name, the source-import scope is an additional
|
|
1418
|
+
// identity guard. For a globally unique name, exact-target caller
|
|
1419
|
+
// evidence is stronger than filesystem layout: workspace-level
|
|
1420
|
+
// Rust tests, Go black-box packages, and generated test trees may
|
|
1421
|
+
// exercise a crate/package without importing the defining file.
|
|
1422
|
+
// Unverified sites remain visibly tiered; they are not promoted.
|
|
1423
|
+
if (sourceFileFilter && !globallyUniqueTarget && !sourceFileFilter.has(site.file)) continue;
|
|
1424
|
+
// A bare callback/reference/call can bind a standalone function
|
|
1425
|
+
// with the same name, but it cannot identify a class method
|
|
1426
|
+
// target. Use the parser's call-kind bit rather than requiring a
|
|
1427
|
+
// textual receiver: fluent chains such as `.addStaticBlock(...)`
|
|
1428
|
+
// have a call receiver but no simple receiver identifier. Rust
|
|
1429
|
+
// proc-macro attribute references are handled by the AST usage
|
|
1430
|
+
// branch above as explicitly unverified references.
|
|
1431
|
+
if (className && !site.isMethod) continue;
|
|
1432
|
+
if (excludeArr.length > 0 &&
|
|
1433
|
+
!index.matchesFilters(info.entry.relativePath, { exclude: excludeArr })) continue;
|
|
1434
|
+
const localSameName = (info.entry.symbols || []).some(s => s.name === searchTerm);
|
|
1435
|
+
const importsTargetName = (info.entry.importBindings || []).some(b => b.name === searchTerm);
|
|
1436
|
+
const explicitlyScopedToLocal = !!options.file && targetDefs.some(d => d.file === site.file);
|
|
1437
|
+
if (localSameName && !importsTargetName && !explicitlyScopedToLocal && !site.receiver) continue;
|
|
1438
|
+
|
|
1439
|
+
let fileResult = results.find(r => r.file === info.entry.relativePath);
|
|
1440
|
+
if (!fileResult) {
|
|
1441
|
+
fileResult = { file: info.entry.relativePath, matches: [] };
|
|
1442
|
+
results.push(fileResult);
|
|
1443
|
+
}
|
|
1444
|
+
if (fileResult.matches.some(m => m.line === site.line &&
|
|
1445
|
+
['call', 'unverified-call'].includes(m.matchType))) continue;
|
|
1446
|
+
fileResult.matches = fileResult.matches.filter(m =>
|
|
1447
|
+
!(m.line === site.line && m.matchType === 'test-case'));
|
|
1448
|
+
fileResult.matches.push({
|
|
1449
|
+
line: site.line,
|
|
1450
|
+
content: (site.content || index.getLineContent(site.file, site.line) || '').trim(),
|
|
1451
|
+
matchType: site._testEvidenceTier === 'confirmed' ? 'call' : 'unverified-call',
|
|
1452
|
+
evidenceTier: site._testEvidenceTier,
|
|
1453
|
+
...(site.resolution && { resolution: site.resolution }),
|
|
1454
|
+
...(site.reason && { reason: site.reason }),
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
for (const fileResult of results) {
|
|
1459
|
+
fileResult.matches.sort((a, b) => (a.line || 0) - (b.line || 0) ||
|
|
1460
|
+
codeUnitCompare(a.matchType || '', b.matchType || ''));
|
|
1461
|
+
if (options.callsOnly) {
|
|
1462
|
+
fileResult.matches = fileResult.matches.filter(m =>
|
|
1463
|
+
['call', 'test-case', 'unverified-call'].includes(m.matchType));
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
results.sort((a, b) => codeUnitCompare(a.file || '', b.file || ''));
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1278
1469
|
return results;
|
|
1279
1470
|
} finally { index._endOp(); }
|
|
1280
1471
|
}
|
|
@@ -1304,6 +1495,25 @@ function _buildSourceFileImporters(index, defs) {
|
|
|
1304
1495
|
}
|
|
1305
1496
|
}
|
|
1306
1497
|
|
|
1498
|
+
// Go imports own packages, not individual files. The resolver represents
|
|
1499
|
+
// a package import with one deterministic file (for example active_help.go),
|
|
1500
|
+
// so starting the reverse walk from only command.go loses real importers
|
|
1501
|
+
// of other declarations in that same package. Expand only languages whose
|
|
1502
|
+
// declared package scope is the directory; JS/TS/Python files in one
|
|
1503
|
+
// directory remain distinct modules, and Java imports remain class-exact.
|
|
1504
|
+
for (const srcPath of [...sourceAbsPaths]) {
|
|
1505
|
+
const srcEntry = index.files.get(srcPath);
|
|
1506
|
+
if (langTraits(srcEntry?.language)?.packageScope !== 'directory') continue;
|
|
1507
|
+
const srcDir = path.dirname(srcPath);
|
|
1508
|
+
for (const [candidatePath, candidateEntry] of index.files) {
|
|
1509
|
+
if (candidateEntry.language === srcEntry.language &&
|
|
1510
|
+
path.dirname(candidatePath) === srcDir &&
|
|
1511
|
+
!isTestFile(candidateEntry.relativePath, candidateEntry.language)) {
|
|
1512
|
+
sourceAbsPaths.add(candidatePath);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1307
1517
|
// BFS through the export graph: walk from source files outward through
|
|
1308
1518
|
// re-export chains. At each hop, verify the intermediate file actually
|
|
1309
1519
|
// exports the target symbol (prevents overmatching barrels that import
|
|
@@ -1352,10 +1562,21 @@ function _buildSourceFileImporters(index, defs) {
|
|
|
1352
1562
|
candidateDirs.add(path.join(srcDir, td));
|
|
1353
1563
|
}
|
|
1354
1564
|
// Java convention: src/main/java/com/pkg → src/test/java/com/pkg
|
|
1565
|
+
let javaTestPackageDir = null;
|
|
1355
1566
|
if (srcDir.includes(path.sep + 'main' + path.sep)) {
|
|
1356
|
-
|
|
1567
|
+
javaTestPackageDir = srcDir.replace(path.sep + 'main' + path.sep, path.sep + 'test' + path.sep);
|
|
1568
|
+
candidateDirs.add(javaTestPackageDir);
|
|
1357
1569
|
}
|
|
1358
1570
|
|
|
1571
|
+
// Cargo integration tests may be nested (`tests/testsuite/foo.rs`).
|
|
1572
|
+
// Derive the crate root from its conventional src/ segment; a
|
|
1573
|
+
// workspace can contain many independent crate-local test trees.
|
|
1574
|
+
const rustSrcMarker = path.sep + 'src' + path.sep;
|
|
1575
|
+
const rustSrcAt = srcEntry.language === 'rust' ? srcPath.lastIndexOf(rustSrcMarker) : -1;
|
|
1576
|
+
const rustIntegrationRoot = rustSrcAt >= 0
|
|
1577
|
+
? path.join(srcPath.slice(0, rustSrcAt), 'tests')
|
|
1578
|
+
: null;
|
|
1579
|
+
|
|
1359
1580
|
for (const [absPath, fe] of index.files) {
|
|
1360
1581
|
if (importers.has(absPath)) continue; // already included
|
|
1361
1582
|
if (!isTestFile(fe.relativePath, fe.language) &&
|
|
@@ -1380,21 +1601,22 @@ function _buildSourceFileImporters(index, defs) {
|
|
|
1380
1601
|
continue;
|
|
1381
1602
|
}
|
|
1382
1603
|
|
|
1383
|
-
// Java: same-package tests need no import statement either
|
|
1384
|
-
//
|
|
1385
|
-
//
|
|
1386
|
-
//
|
|
1387
|
-
if (srcEntry.language === 'java' &&
|
|
1604
|
+
// Java: same-package tests need no import statement either. The
|
|
1605
|
+
// source and test packages can be physical mirrors under
|
|
1606
|
+
// src/main and src/test, and ANY test class in that package may
|
|
1607
|
+
// exercise the target (not only TargetNameTest.java).
|
|
1608
|
+
if (srcEntry.language === 'java' &&
|
|
1609
|
+
(testDir === srcDir || testDir === javaTestPackageDir)) {
|
|
1388
1610
|
importers.add(absPath);
|
|
1389
1611
|
continue;
|
|
1390
1612
|
}
|
|
1391
1613
|
|
|
1392
|
-
// Rust: Cargo integration tests live
|
|
1393
|
-
// import via the crate's PACKAGE name,
|
|
1394
|
-
// does not map to project files
|
|
1614
|
+
// Rust: Cargo integration tests live anywhere below
|
|
1615
|
+
// <crate-root>/tests/ and import via the crate's PACKAGE name,
|
|
1616
|
+
// which the import resolver does not map to project files.
|
|
1395
1617
|
if (srcEntry.language === 'rust' &&
|
|
1396
|
-
|
|
1397
|
-
|
|
1618
|
+
rustIntegrationRoot &&
|
|
1619
|
+
(absPath === rustIntegrationRoot || absPath.startsWith(rustIntegrationRoot + path.sep))) {
|
|
1398
1620
|
importers.add(absPath);
|
|
1399
1621
|
continue;
|
|
1400
1622
|
}
|
package/core/tracing.js
CHANGED
|
@@ -23,7 +23,7 @@ const path = require('path');
|
|
|
23
23
|
const { escapeRegExp, codeUnitCompare, inlineTestRanges, lineInRanges, classDispatchNames } = require('./shared');
|
|
24
24
|
const { isTestFile } = require('./discovery');
|
|
25
25
|
const { getCachedCalls } = require('./callers');
|
|
26
|
-
const {
|
|
26
|
+
const { getLanguageModule } = require('../languages');
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Contract-mode caller expansion for the tree commands. Memoizes the full
|
|
@@ -1244,7 +1244,7 @@ function affectedTests(index, name, options = {}) {
|
|
|
1244
1244
|
// functions (#[test] fns in Rust's #[cfg(test)] mods, Go Test*)
|
|
1245
1245
|
// live in production-path FILES but are tests themselves — the
|
|
1246
1246
|
// language's getEntryPointKind says so; they need no coverage.
|
|
1247
|
-
for (const [
|
|
1247
|
+
for (const [, fe] of index.files) {
|
|
1248
1248
|
if (isTestFile(fe.relativePath, fe.language)) continue;
|
|
1249
1249
|
const langModule = getLanguageModule(fe.language);
|
|
1250
1250
|
const kindOf = langModule?.getEntryPointKind;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Command trust matrix — executable inventory of what proves each command.
|
|
5
|
+
*
|
|
6
|
+
* This is intentionally not a table of accuracy percentages. It records the
|
|
7
|
+
* independent evidence or invariant that is capable of falsifying each
|
|
8
|
+
* command's documented claim. test/trust-matrix.test.js fails closed when a
|
|
9
|
+
* canonical command is added without an entry or when a referenced proof
|
|
10
|
+
* artifact disappears.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const PROOF_CATALOG = Object.freeze({
|
|
14
|
+
'oracle-callers': {
|
|
15
|
+
kind: 'external-oracle',
|
|
16
|
+
artifact: 'eval/run-oracle-eval.js',
|
|
17
|
+
claim: 'Compiler/LSP references score confirmed/unverified caller edges and semantic recall.',
|
|
18
|
+
},
|
|
19
|
+
'oracle-callees': {
|
|
20
|
+
kind: 'external-oracle',
|
|
21
|
+
artifact: 'eval/run-oracle-eval.js',
|
|
22
|
+
claim: 'The same compiler/LSP edges are re-read from caller scope to score callee answers.',
|
|
23
|
+
},
|
|
24
|
+
'oracle-symbols': {
|
|
25
|
+
kind: 'external-oracle',
|
|
26
|
+
artifact: 'eval/run-oracle-eval.js',
|
|
27
|
+
claim: 'Compiler/LSP symbols gate definition discovery, pinning, and extraction.',
|
|
28
|
+
},
|
|
29
|
+
'oracle-references': {
|
|
30
|
+
kind: 'external-oracle',
|
|
31
|
+
artifact: 'eval/run-oracle-eval.js',
|
|
32
|
+
claim: 'Compiler/LSP code references gate usages, direct tests, and confirmed examples.',
|
|
33
|
+
},
|
|
34
|
+
'oracle-deadcode': {
|
|
35
|
+
kind: 'external-oracle',
|
|
36
|
+
artifact: 'eval/run-deadcode-eval.js',
|
|
37
|
+
claim: 'Every sampled dead-code claim is challenged with compiler/LSP references.',
|
|
38
|
+
},
|
|
39
|
+
conservation: {
|
|
40
|
+
kind: 'accounting-invariant',
|
|
41
|
+
artifact: 'test/conservation.test.js',
|
|
42
|
+
claim: 'Observed text occurrences partition without unexplained loss.',
|
|
43
|
+
},
|
|
44
|
+
'cross-language-fixtures': {
|
|
45
|
+
kind: 'independent-fixture',
|
|
46
|
+
artifact: 'test/cross-language.test.js',
|
|
47
|
+
claim: 'Equivalent language fixtures assert the command contract across supported parsers.',
|
|
48
|
+
},
|
|
49
|
+
'command-fixtures': {
|
|
50
|
+
kind: 'independent-fixture',
|
|
51
|
+
artifact: 'test/command-coverage.test.js',
|
|
52
|
+
claim: 'Behavioral fixtures assert output semantics and negative cases.',
|
|
53
|
+
},
|
|
54
|
+
'graph-invariants': {
|
|
55
|
+
kind: 'algebraic-invariant',
|
|
56
|
+
artifact: 'test/command-coverage.test.js',
|
|
57
|
+
claim: 'Dependency duality, traversal depth, cycle, and export invariants are checked.',
|
|
58
|
+
},
|
|
59
|
+
'surface-parity': {
|
|
60
|
+
kind: 'architecture-invariant',
|
|
61
|
+
artifact: 'test/parity-test.js',
|
|
62
|
+
claim: 'CLI, MCP, and interactive surfaces dispatch the same canonical handler.',
|
|
63
|
+
},
|
|
64
|
+
'systematic-options': {
|
|
65
|
+
kind: 'combinatorial-invariant',
|
|
66
|
+
artifact: 'test/systematic-test.js',
|
|
67
|
+
claim: 'Supported command/flag combinations execute with stable envelopes.',
|
|
68
|
+
},
|
|
69
|
+
'git-fixtures': {
|
|
70
|
+
kind: 'integration-fixture',
|
|
71
|
+
artifact: 'test/command-coverage.test.js',
|
|
72
|
+
claim: 'Temporary Git histories assert diff attribution and composed checks.',
|
|
73
|
+
},
|
|
74
|
+
'framework-fixtures': {
|
|
75
|
+
kind: 'adversarial-fixture',
|
|
76
|
+
artifact: 'test/feature.test.js',
|
|
77
|
+
claim: 'Positive and confusable framework shapes test advisory detection.',
|
|
78
|
+
},
|
|
79
|
+
'protocol-fixtures': {
|
|
80
|
+
kind: 'protocol-invariant',
|
|
81
|
+
artifact: 'test/regression-mcp.test.js',
|
|
82
|
+
claim: 'Stateful expansion and MCP envelopes are checked end-to-end.',
|
|
83
|
+
},
|
|
84
|
+
'performance-budget': {
|
|
85
|
+
kind: 'performance-gate',
|
|
86
|
+
artifact: 'eval/run-performance-gate.js',
|
|
87
|
+
claim: 'Cold indexing, warm cache, semantic-query p95, and memory stay within budgets.',
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
function row(claim, proofs, performanceClass, decisionSafety, oracleReason = null) {
|
|
92
|
+
return Object.freeze({ claim, proofs: Object.freeze(proofs), performanceClass, decisionSafety, oracleReason });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const COMMAND_TRUST_MATRIX = Object.freeze({
|
|
96
|
+
about: row('tiered-semantic', ['oracle-callers', 'oracle-callees', 'conservation', 'surface-parity'], 'semantic-query', 'review-required'),
|
|
97
|
+
context: row('tiered-semantic', ['oracle-callers', 'oracle-callees', 'conservation', 'surface-parity'], 'semantic-query', 'review-required'),
|
|
98
|
+
impact: row('tiered-semantic', ['oracle-callers', 'conservation', 'surface-parity'], 'semantic-query', 'review-required'),
|
|
99
|
+
blast: row('tiered-derived', ['oracle-callers', 'conservation', 'command-fixtures', 'surface-parity'], 'graph-query', 'review-required'),
|
|
100
|
+
smart: row('tiered-derived', ['oracle-callees', 'oracle-symbols', 'command-fixtures', 'surface-parity'], 'semantic-query', 'review-required'),
|
|
101
|
+
trace: row('tiered-derived', ['oracle-callees', 'conservation', 'command-fixtures', 'surface-parity'], 'graph-query', 'review-required'),
|
|
102
|
+
reverseTrace: row('tiered-derived', ['oracle-callers', 'conservation', 'command-fixtures', 'surface-parity'], 'graph-query', 'review-required'),
|
|
103
|
+
example: row('confirmed-or-abstain-advisory', ['oracle-references', 'command-fixtures', 'surface-parity'], 'semantic-query', 'advisory-only'),
|
|
104
|
+
related: row('advisory-ranking', ['oracle-callers', 'oracle-callees', 'command-fixtures', 'surface-parity'], 'semantic-query', 'advisory-only'),
|
|
105
|
+
brief: row('ast-fact-summary', ['oracle-symbols', 'command-fixtures', 'surface-parity'], 'source-query', 'navigation'),
|
|
106
|
+
|
|
107
|
+
find: row('exact-indexed-definition', ['oracle-symbols', 'cross-language-fixtures', 'surface-parity'], 'symbol-query', 'navigation'),
|
|
108
|
+
usages: row('literal-code-reference-inventory', ['oracle-references', 'cross-language-fixtures', 'surface-parity'], 'project-scan', 'review-required'),
|
|
109
|
+
toc: row('indexed-symbol-accounting', ['cross-language-fixtures', 'command-fixtures', 'surface-parity'], 'project-scan', 'navigation', 'No compiler exposes a portable table-of-contents contract; symbol totals and filters use fixture/accounting invariants.'),
|
|
110
|
+
search: row('literal-or-structural-match', ['cross-language-fixtures', 'command-fixtures', 'systematic-options', 'surface-parity'], 'project-scan', 'navigation', 'Literal results are text-ground exact; structural filters are AST fixture-checked rather than semantic identity claims.'),
|
|
111
|
+
tests: row('tiered-direct-test-evidence', ['oracle-references', 'cross-language-fixtures', 'surface-parity'], 'semantic-query', 'review-required'),
|
|
112
|
+
affectedTests: row('tiered-transitive-test-evidence', ['oracle-callers', 'oracle-references', 'conservation', 'command-fixtures', 'surface-parity'], 'graph-query', 'review-required'),
|
|
113
|
+
deadcode: row('candidate-not-safe-delete', ['oracle-deadcode', 'cross-language-fixtures', 'command-fixtures', 'surface-parity'], 'project-scan', 'advisory-only'),
|
|
114
|
+
entrypoints: row('framework-advisory', ['framework-fixtures', 'cross-language-fixtures', 'surface-parity'], 'project-scan', 'advisory-only', 'Framework registration/reflection has no universal compiler oracle; positive and confusable framework fixtures define the static claim.'),
|
|
115
|
+
endpoints: row('framework-advisory', ['framework-fixtures', 'cross-language-fixtures', 'surface-parity'], 'project-scan', 'advisory-only', 'Route/client matching is framework-specific and explicitly advisory.'),
|
|
116
|
+
|
|
117
|
+
fn: row('exact-source-extraction', ['oracle-symbols', 'cross-language-fixtures', 'surface-parity'], 'source-query', 'navigation'),
|
|
118
|
+
class: row('exact-source-extraction', ['oracle-symbols', 'cross-language-fixtures', 'surface-parity'], 'source-query', 'navigation'),
|
|
119
|
+
lines: row('exact-file-slice', ['command-fixtures', 'systematic-options', 'surface-parity'], 'source-query', 'navigation', 'The file bytes and validated line bounds are the direct oracle.'),
|
|
120
|
+
expand: row('stateful-protocol', ['protocol-fixtures', 'surface-parity'], 'source-query', 'navigation', 'Expansion is a cache/protocol contract, not a semantic-analysis claim.'),
|
|
121
|
+
|
|
122
|
+
imports: row('static-import-inventory', ['graph-invariants', 'cross-language-fixtures', 'surface-parity'], 'graph-query', 'navigation', 'File import syntax/resolution is checked with language fixtures and graph duality; dynamic imports remain explicit blind spots.'),
|
|
123
|
+
exporters: row('reverse-static-import-inventory', ['graph-invariants', 'cross-language-fixtures', 'surface-parity'], 'graph-query', 'navigation', 'Reverse edges are proven by import/export graph duality.'),
|
|
124
|
+
fileExports: row('static-export-inventory', ['graph-invariants', 'cross-language-fixtures', 'surface-parity'], 'graph-query', 'navigation', 'Static visibility/export shapes use language fixtures; runtime mutation remains outside the claim.'),
|
|
125
|
+
graph: row('static-dependency-traversal', ['graph-invariants', 'cross-language-fixtures', 'surface-parity'], 'graph-query', 'navigation', 'Traversal is an algebraic derivative of the static import graph.'),
|
|
126
|
+
circularDeps: row('static-cycle-detection', ['graph-invariants', 'command-fixtures', 'surface-parity'], 'graph-query', 'navigation', 'Cycle output is checked against known graph fixtures and traversal invariants.'),
|
|
127
|
+
|
|
128
|
+
verify: row('tiered-signature-check', ['oracle-callers', 'cross-language-fixtures', 'command-fixtures', 'surface-parity'], 'semantic-query', 'review-required'),
|
|
129
|
+
plan: row('refactor-preview', ['oracle-callers', 'cross-language-fixtures', 'command-fixtures', 'surface-parity'], 'semantic-query', 'review-required'),
|
|
130
|
+
diffImpact: row('git-diff-derived-impact', ['oracle-callers', 'git-fixtures', 'command-fixtures', 'surface-parity'], 'git-query', 'review-required'),
|
|
131
|
+
check: row('composed-precommit-diagnostic', ['oracle-callers', 'oracle-references', 'git-fixtures', 'command-fixtures', 'surface-parity'], 'git-query', 'review-required'),
|
|
132
|
+
|
|
133
|
+
typedef: row('exact-indexed-type', ['oracle-symbols', 'cross-language-fixtures', 'surface-parity'], 'symbol-query', 'navigation'),
|
|
134
|
+
stacktrace: row('advisory-frame-resolution', ['command-fixtures', 'surface-parity'], 'source-query', 'advisory-only', 'Stack formats and source-map/runtime availability are environment-specific; unresolved frames remain visible.'),
|
|
135
|
+
api: row('static-public-surface', ['graph-invariants', 'cross-language-fixtures', 'command-fixtures', 'surface-parity'], 'project-scan', 'review-required', 'Static exports are the claim; reflection and external consumers are explicitly outside the universe.'),
|
|
136
|
+
stats: row('index-accounting', ['command-fixtures', 'systematic-options', 'surface-parity'], 'project-scan', 'navigation', 'Counts are checked against the built index and deterministic fixture totals.'),
|
|
137
|
+
doctor: row('diagnostic-not-accuracy', ['command-fixtures', 'systematic-options', 'surface-parity'], 'project-scan', 'advisory-only', 'Doctor reports index/evidence limitations and never presents itself as an accuracy oracle.'),
|
|
138
|
+
auditAsync: row('async-advisory', ['cross-language-fixtures', 'command-fixtures', 'surface-parity'], 'project-scan', 'advisory-only', 'Missing-await semantics depend on framework/type flow; findings are advisory and fixture-tested.'),
|
|
139
|
+
orient: row('diagnostic-composition', ['command-fixtures', 'systematic-options', 'surface-parity'], 'project-scan', 'navigation', 'Orient composes index counts, entrypoint hints, and doctor limitations.'),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
function summarizeCommandTrust() {
|
|
143
|
+
const commands = Object.entries(COMMAND_TRUST_MATRIX);
|
|
144
|
+
const oracleBacked = commands.filter(([, spec]) =>
|
|
145
|
+
spec.proofs.some(id => PROOF_CATALOG[id]?.kind === 'external-oracle')).length;
|
|
146
|
+
const advisoryOnly = commands.filter(([, spec]) => spec.decisionSafety === 'advisory-only').length;
|
|
147
|
+
return {
|
|
148
|
+
commands: commands.length,
|
|
149
|
+
classified: commands.length,
|
|
150
|
+
oracleBacked,
|
|
151
|
+
invariantOrFixtureBacked: commands.length - oracleBacked,
|
|
152
|
+
advisoryOnly,
|
|
153
|
+
unclassified: 0,
|
|
154
|
+
contract: 'proof-classification-not-runtime-accuracy',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
module.exports = { PROOF_CATALOG, COMMAND_TRUST_MATRIX, summarizeCommandTrust };
|
package/core/verify.js
CHANGED
package/eslint.config.js
CHANGED
|
@@ -6,7 +6,7 @@ module.exports = [
|
|
|
6
6
|
js.configs.recommended,
|
|
7
7
|
{
|
|
8
8
|
languageOptions: {
|
|
9
|
-
ecmaVersion:
|
|
9
|
+
ecmaVersion: 'latest',
|
|
10
10
|
sourceType: 'commonjs',
|
|
11
11
|
globals: {
|
|
12
12
|
// Node.js globals
|
|
@@ -31,7 +31,7 @@ module.exports = [
|
|
|
31
31
|
},
|
|
32
32
|
rules: {
|
|
33
33
|
'no-undef': 'error',
|
|
34
|
-
'no-unused-vars': ['warn', { args: 'none', caughtErrors: 'none' }],
|
|
34
|
+
'no-unused-vars': ['warn', { args: 'none', caughtErrors: 'none', ignoreRestSiblings: true }],
|
|
35
35
|
'no-redeclare': 'error',
|
|
36
36
|
'eqeqeq': ['warn', 'smart'],
|
|
37
37
|
'no-constant-condition': ['error', { checkLoops: false }],
|