ucn 5.5.3 → 5.6.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.
Files changed (77) hide show
  1. package/.claude/skills/ucn/SKILL.md +37 -12
  2. package/.claude/skills/ucn/references/commands.md +18 -14
  3. package/.claude/skills/ucn/references/resolution-rules.md +4 -0
  4. package/.claude/skills/ucn/references/trust-contract.md +4 -4
  5. package/CHANGELOG.md +485 -0
  6. package/README.md +66 -29
  7. package/cli/flags.js +184 -0
  8. package/cli/index.js +137 -324
  9. package/core/account.js +44 -87
  10. package/core/analysis.js +274 -108
  11. package/core/ast-analysis.js +2 -0
  12. package/core/bridge.js +14 -0
  13. package/core/cache.js +103 -16
  14. package/core/call-fit.js +130 -0
  15. package/core/callers.js +353 -71
  16. package/core/capture.js +363 -0
  17. package/core/change-kinds.js +1107 -0
  18. package/core/check.js +1150 -139
  19. package/core/command-contracts.js +8 -7
  20. package/core/constructed-class.js +1195 -0
  21. package/core/contract-membership.js +142 -0
  22. package/core/deadcode.js +455 -106
  23. package/core/deletion.js +421 -0
  24. package/core/discovery.js +104 -9
  25. package/core/entrypoint-toml.js +110 -0
  26. package/core/entrypoints.js +12 -6
  27. package/core/evidence-bands.js +68 -0
  28. package/core/evidence-bands.json +44 -0
  29. package/core/execute.js +172 -161
  30. package/core/hot-summary.js +241 -0
  31. package/core/index-ir.js +5 -3
  32. package/core/ir.js +6 -2
  33. package/core/member-signatures.js +789 -0
  34. package/core/outgoing-check.js +116 -0
  35. package/core/output/analysis-ext.js +20 -10
  36. package/core/output/analysis.js +98 -60
  37. package/core/output/check.js +101 -37
  38. package/core/output/doctor.js +34 -1
  39. package/core/output/find.js +2 -1
  40. package/core/output/lines.js +63 -3
  41. package/core/output/public.js +29 -11
  42. package/core/output/refactoring.js +32 -0
  43. package/core/output/reporting.js +19 -2
  44. package/core/output/search.js +14 -2
  45. package/core/output/tracing.js +36 -8
  46. package/core/output-budget.js +146 -61
  47. package/core/parallel-build.js +3 -3
  48. package/core/project.js +106 -23
  49. package/core/provenance-type.js +12 -5
  50. package/core/public-command.js +35 -9
  51. package/core/python-binding-identity.js +707 -0
  52. package/core/python-modules.js +6 -0
  53. package/core/reflection.js +248 -31
  54. package/core/registry.js +623 -72
  55. package/core/reporting.js +69 -34
  56. package/core/review-items.js +75 -0
  57. package/core/runtime-entrypoints.js +159 -0
  58. package/core/search.js +148 -164
  59. package/core/shared.js +0 -45
  60. package/core/snapshot.js +350 -0
  61. package/core/source-universe.js +707 -0
  62. package/core/test-units.js +3698 -0
  63. package/core/tracing.js +270 -658
  64. package/core/verify.js +63 -22
  65. package/languages/adapter.js +8 -3
  66. package/languages/c-family.js +12 -0
  67. package/languages/csharp.js +634 -0
  68. package/languages/go.js +120 -0
  69. package/languages/index.js +466 -4
  70. package/languages/java.js +712 -0
  71. package/languages/javascript.js +1498 -6
  72. package/languages/lexical-scope.js +68 -0
  73. package/languages/python.js +1708 -3
  74. package/languages/rust.js +209 -104
  75. package/languages/static-js.js +88 -0
  76. package/mcp/server.js +44 -110
  77. package/package.json +11 -5
package/languages/go.js CHANGED
@@ -719,6 +719,9 @@ function extractInterfaceMembers(interfaceNode, codeOrLines) {
719
719
  endLine,
720
720
  sourceSpan,
721
721
  memberType: 'method',
722
+ // An interface method element is a requirement of
723
+ // every type used as the interface.
724
+ requiredMember: true,
722
725
  ...(paramsText !== null && { params: paramsText }),
723
726
  ...(paramsNode && { paramsStructured: parseStructuredParams(paramsNode, 'go') }),
724
727
  ...(returnType && { returnType })
@@ -3841,6 +3844,121 @@ function selectorReceiverIsLocal(tree, line, column) {
3841
3844
  return !!scope && typeof scope !== 'string';
3842
3845
  }
3843
3846
 
3847
+
3848
+ /**
3849
+ * The tests a Go file declares, as "go test" finds them: in a _test.go
3850
+ * file, a function (never a method) named Test or Fuzz followed by nothing
3851
+ * or by a rune that is not lower case; TestMain is the harness, not a
3852
+ * test; an Example runs only when its body ends in an output comment. A
3853
+ * Benchmark runs only when asked for, so it is not one of the tests.
3854
+ */
3855
+ /**
3856
+ * Does a Go test file hold a TestMain that may keep the package's tests
3857
+ * from running. A TestMain decides nothing when the first statement of
3858
+ * its body that can end it or run the tests is a plain statement holding
3859
+ * `m.Run()` (`os.Exit(m.Run())`, `code := m.Run()`): nothing before it
3860
+ * returns or exits. One that exits or returns in a branch first, never
3861
+ * calls Run, or hands `m` to something else, decides.
3862
+ */
3863
+ function testGate(fileEntry, helpers) {
3864
+ if (!/_test\.go$/.test(fileEntry.relativePath)) return false;
3865
+ if (!(fileEntry.symbols || []).some(symbol => symbol.name === 'TestMain' && !symbol.className)) return false;
3866
+ const tree = helpers.tree();
3867
+ if (!tree) return true;
3868
+ const main = tree.rootNode.namedChildren.find(node => node.type === 'function_declaration' &&
3869
+ node.childForFieldName('name')?.text === 'TestMain');
3870
+ if (!main) return true;
3871
+ const body = main.childForFieldName('body');
3872
+ const list = main.childForFieldName('parameters');
3873
+ const first = list && list.namedChildren[0];
3874
+ const handle = first && first.childForFieldName('name') ? first.childForFieldName('name').text : null;
3875
+ if (!body || !handle) return true;
3876
+ const calls = (node, test) => (node.type === 'call_expression' && test(node)) || node.namedChildren.some(child => calls(child, test));
3877
+ const runs = node => {
3878
+ const callee = node.childForFieldName('function');
3879
+ return !!callee && callee.type === 'selector_expression' && callee.childForFieldName('operand')?.text === handle &&
3880
+ callee.childForFieldName('field')?.text === 'Run';
3881
+ };
3882
+ const ends = node => {
3883
+ const callee = node.childForFieldName('function');
3884
+ const text = callee ? callee.text : '';
3885
+ const args = node.childForFieldName('arguments');
3886
+ return /^(os\.Exit|panic|runtime\.Goexit|log\.(Fatal|Fatalf|Fatalln|Panic|Panicf|Panicln))$/.test(text) ||
3887
+ (args ? args.namedChildren : []).some(argument => argument.type === 'identifier' && argument.text === handle);
3888
+ };
3889
+ const returns = node => node.type === 'return_statement' || (node.type !== 'func_literal' && node.namedChildren.some(returns));
3890
+ const statements = body.namedChildren.filter(node => node.type !== 'comment');
3891
+ for (const statement of statements.length === 1 && statements[0].type === 'statement_list' ? statements[0].namedChildren : statements) {
3892
+ if (statement.type === 'comment') continue;
3893
+ const running = calls(statement, runs);
3894
+ if (!running && !calls(statement, ends) && !returns(statement)) continue;
3895
+ const plain = ['expression_statement', 'short_var_declaration', 'assignment_statement', 'var_declaration'].includes(statement.type);
3896
+ return !(running && plain && !calls(statement, node => !runs(node) && ends(node) && !/^os\.Exit$/.test(node.childForFieldName('function')?.text || '')));
3897
+ }
3898
+ return true;
3899
+ }
3900
+
3901
+ /**
3902
+ * Does an Example function end in an output comment? go runs an example
3903
+ * only then. The comment is a comment of the function's body (vet insists
3904
+ * it is the last one): text inside a string is not one.
3905
+ */
3906
+ function exampleHasOutput(tree, symbol) {
3907
+ if (!tree) return false;
3908
+ const line = (symbol.nameLine || symbol.startLine) - 1;
3909
+ const declaration = tree.rootNode.namedChildren.find(node => node.type === 'function_declaration' &&
3910
+ node.startPosition.row <= line && node.endPosition.row >= line &&
3911
+ node.childForFieldName('name')?.text === symbol.name);
3912
+ const body = declaration && declaration.childForFieldName('body');
3913
+ if (!body) return false;
3914
+ let found = false;
3915
+ const scan = node => {
3916
+ if (found) return;
3917
+ if (node.type === 'comment') {
3918
+ const text = node.text.replace(/^\/\/|^\/\*/, '');
3919
+ if (/^\s*(unordered )?output:/i.test(text)) found = true;
3920
+ return;
3921
+ }
3922
+ for (const child of node.namedChildren) scan(child);
3923
+ };
3924
+ scan(body);
3925
+ return found;
3926
+ }
3927
+
3928
+ const GO_PLATFORM_WORDS = new Set(['aix', 'android', 'darwin', 'dragonfly', 'freebsd', 'hurd', 'illumos', 'ios',
3929
+ 'js', 'linux', 'nacl', 'netbsd', 'openbsd', 'plan9', 'solaris', 'wasip1', 'windows', 'zos',
3930
+ '386', 'amd64', 'amd64p32', 'arm', 'armbe', 'arm64', 'arm64be', 'loong64', 'mips', 'mipsle', 'mips64',
3931
+ 'mips64le', 'mips64p32', 'mips64p32le', 'ppc', 'ppc64', 'ppc64le', 'riscv', 'riscv64', 's390', 's390x',
3932
+ 'sparc', 'sparc64', 'wasm']);
3933
+
3934
+ function testUnits(fileEntry, helpers) {
3935
+ if (!/_test\.go$/.test(fileEntry.relativePath)) return { units: [], inheriting: false, complete: true };
3936
+ // name_GOOS_test.go, name_GOARCH_test.go, name_GOOS_GOARCH_test.go
3937
+ const words = fileEntry.relativePath.split('/').pop().replace(/_test\.go$/, '').split('_').slice(1);
3938
+ const platform = words.length > 0 && GO_PLATFORM_WORDS.has(words[words.length - 1]);
3939
+ const units = [];
3940
+ for (const symbol of fileEntry.symbols || []) {
3941
+ if (symbol.type !== 'function' || symbol.isMethod || symbol.receiver || symbol.className) continue;
3942
+ const match = /^(Test|Fuzz|Example)([\s\S]*)$/.exec(symbol.name);
3943
+ if (!match) continue;
3944
+ const first = match[2] ? String.fromCodePoint(match[2].codePointAt(0)) : '';
3945
+ if (first && first.toLowerCase() === first && first.toUpperCase() !== first) continue;
3946
+ if (symbol.name === 'TestMain' && /testing\s*\.\s*M\b/.test(String(symbol.params || ''))) continue;
3947
+ if (helpers.enclosing(symbol).length > 0) continue;
3948
+ const form = match[1].toLowerCase();
3949
+ if (form === 'example' && !exampleHasOutput(helpers.tree(), symbol)) continue;
3950
+ units.push({
3951
+ kind: 'test', name: symbol.name, suite: [], form,
3952
+ // A file under a build constraint is compiled only sometimes.
3953
+ ...((fileEntry.buildConstraint || platform) && { conditional: true, notInEveryBuild: true }),
3954
+ line: symbol.nameLine || symbol.startLine, startLine: symbol.startLine,
3955
+ endLine: symbol.endLine ?? symbol.startLine, sourceSpan: symbol.sourceSpan,
3956
+ });
3957
+ }
3958
+ // Go has no test that is not a declaration: no unit, no test.
3959
+ return { units, inheriting: false, complete: true };
3960
+ }
3961
+
3844
3962
  /**
3845
3963
  * Classify a Go symbol as a runtime entry point of a specific kind.
3846
3964
  * Returns 'test' | 'main' | null.
@@ -3902,5 +4020,7 @@ module.exports = {
3902
4020
  isPlatformConcreteType,
3903
4021
  isEntryPoint,
3904
4022
  getEntryPointKind,
4023
+ testUnits,
4024
+ testGate,
3905
4025
  parse
3906
4026
  };