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
@@ -0,0 +1,110 @@
1
+ 'use strict';
2
+
3
+ // A data-only TOML reader for the PEP 621 entrypoint subtree. Other values
4
+ // remain opaque; malformed or nonliteral entrypoint values return unknown.
5
+ function entrypointToml(source) {
6
+ const tokens = [];
7
+ let line = 1;
8
+ let offset = 0;
9
+ while (offset < source.length) {
10
+ const ch = source[offset];
11
+ if (ch === '\n') { tokens.push({ kind: 'newline', line }); offset++; line++; continue; }
12
+ if (/\s/.test(ch)) { offset++; continue; }
13
+ if (ch === '#') { while (offset < source.length && source[offset] !== '\n') offset++; continue; }
14
+ if ('[]=.,{}'.includes(ch)) { tokens.push({ kind: ch, line }); offset++; continue; }
15
+ if (ch === '"' || ch === "'") {
16
+ const startLine = line;
17
+ const triple = source.slice(offset, offset + 3) === ch.repeat(3);
18
+ const delimiter = triple ? ch.repeat(3) : ch;
19
+ offset += delimiter.length;
20
+ let value = '';
21
+ let valid = true;
22
+ let closed = false;
23
+ while (offset < source.length) {
24
+ if (source.slice(offset, offset + delimiter.length) === delimiter) {
25
+ offset += delimiter.length; closed = true; break;
26
+ }
27
+ const current = source[offset++];
28
+ if (current === '\n') line++;
29
+ if (current !== '\\' || ch === "'") { value += current; continue; }
30
+ const escaped = source[offset++];
31
+ const simple = { b: '\b', t: '\t', n: '\n', f: '\f', r: '\r', '"': '"', '\\': '\\' };
32
+ if (Object.hasOwn(simple, escaped)) value += simple[escaped];
33
+ else if (escaped === 'u' || escaped === 'U') {
34
+ const width = escaped === 'u' ? 4 : 8;
35
+ const digits = source.slice(offset, offset + width);
36
+ if (!/^[0-9a-fA-F]+$/.test(digits) || digits.length !== width || parseInt(digits, 16) > 0x10ffff) valid = false;
37
+ else value += String.fromCodePoint(parseInt(digits, 16));
38
+ offset += width;
39
+ } else if (triple && /\s/.test(escaped || '')) {
40
+ if (escaped === '\n') line++;
41
+ while (offset < source.length && /\s/.test(source[offset])) {
42
+ if (source[offset++] === '\n') line++;
43
+ }
44
+ } else valid = false;
45
+ }
46
+ tokens.push({ kind: 'string', value: valid && closed ? value.replace(/^\n/, '') : null, line: startLine });
47
+ continue;
48
+ }
49
+ const start = offset;
50
+ while (offset < source.length && !/[\s[\]=.,{}#]/.test(source[offset])) offset++;
51
+ if (offset === start) offset++;
52
+ tokens.push({ kind: 'bare', value: source.slice(start, offset), line });
53
+ }
54
+ const rows = [];
55
+ let at = 0;
56
+ let section = [];
57
+ const keys = () => {
58
+ const parts = [];
59
+ while (tokens[at] && ['bare', 'string'].includes(tokens[at].kind)) {
60
+ parts.push(tokens[at++].value);
61
+ if (tokens[at]?.kind !== '.') break;
62
+ at++;
63
+ }
64
+ return parts;
65
+ };
66
+ const entryPath = parts => parts[0] === 'project' &&
67
+ (['scripts', 'gui-scripts'].includes(parts[1]) || parts[1] === 'entry-points');
68
+ const readValue = (parts, depth = 0) => {
69
+ const token = tokens[at++];
70
+ if (!token || depth > 32) { if (entryPath(parts)) rows.push({ unknown: true, line: token?.line }); return; }
71
+ if (token.kind === '{') {
72
+ while (tokens[at] && tokens[at].kind !== '}') {
73
+ if (['newline', ','].includes(tokens[at].kind)) { at++; continue; }
74
+ const nested = keys();
75
+ if (!nested.length || tokens[at++]?.kind !== '=') { if (entryPath(parts)) rows.push({ unknown: true, line: token.line }); break; }
76
+ readValue([...parts, ...nested], depth + 1);
77
+ }
78
+ if (tokens[at]?.kind === '}') at++;
79
+ } else if (token.kind === '[') {
80
+ let balance = 1;
81
+ while (tokens[at] && balance) {
82
+ const item = tokens[at++];
83
+ if (item.kind === '[') balance++;
84
+ if (item.kind === ']') balance--;
85
+ if (parts.join('.') === 'project.dynamic' && item.kind === 'string' &&
86
+ ['scripts', 'gui-scripts', 'entry-points'].includes(item.value)) rows.push({ unknown: true, line: item.line });
87
+ }
88
+ if (entryPath(parts)) rows.push({ unknown: true, line: token.line });
89
+ } else if (entryPath(parts)) {
90
+ rows.push(token.kind === 'string' && token.value != null
91
+ ? { value: token.value, line: token.line } : { unknown: true, line: token.line });
92
+ }
93
+ };
94
+ while (at < tokens.length) {
95
+ if (tokens[at].kind === 'newline') { at++; continue; }
96
+ if (tokens[at].kind === '[') {
97
+ at++;
98
+ section = keys();
99
+ while (tokens[at] && ![']', 'newline'].includes(tokens[at].kind)) at++;
100
+ if (tokens[at]?.kind === ']') at++;
101
+ continue;
102
+ }
103
+ const parts = keys();
104
+ if (parts.length && tokens[at]?.kind === '=') { at++; readValue([...section, ...parts]); }
105
+ else while (tokens[at] && tokens[at].kind !== 'newline') at++;
106
+ }
107
+ return rows;
108
+ }
109
+
110
+ module.exports = { entrypointToml };
@@ -595,6 +595,12 @@ const FRAMEWORK_PATTERNS = [
595
595
 
596
596
  ];
597
597
 
598
+ // What the `type` and `framework` filters accept: the kinds and the labels a
599
+ // pattern above carries, and nothing else.
600
+ const ENTRYPOINT_TYPES = Object.freeze([...new Set(FRAMEWORK_PATTERNS.map(p => p.type))].sort());
601
+ const ENTRYPOINT_FRAMEWORKS = Object.freeze(
602
+ [...new Set(FRAMEWORK_PATTERNS.map(p => p.framework.toLowerCase()))].sort());
603
+
598
604
  // ============================================================================
599
605
  // DETECTION
600
606
  // ============================================================================
@@ -864,11 +870,10 @@ function detectEntrypoints(index, options = {}) {
864
870
  // Validate --type against the pattern registry up front — an unknown
865
871
  // value used to fall through to the filter and silently return nothing.
866
872
  if (options.type) {
867
- const validTypes = new Set(FRAMEWORK_PATTERNS.map(p => p.type));
868
- if (!validTypes.has(options.type)) {
873
+ if (!ENTRYPOINT_TYPES.includes(options.type)) {
869
874
  return {
870
875
  error: 'invalid-type',
871
- message: `Unknown type "${options.type}". Valid: ${[...validTypes].sort().join(', ')}.`,
876
+ message: `Unknown type "${options.type}". Valid: ${ENTRYPOINT_TYPES.join(', ')}.`,
872
877
  };
873
878
  }
874
879
  }
@@ -876,13 +881,12 @@ function detectEntrypoints(index, options = {}) {
876
881
  // Same discipline for --framework (fix #243) — a typo like 'flsk'
877
882
  // silently filtered everything to an empty result.
878
883
  if (options.framework) {
879
- const validFrameworks = new Set(FRAMEWORK_PATTERNS.map(p => p.framework.toLowerCase()));
880
884
  const wanted = String(options.framework).split(',').map(s => s.trim().toLowerCase()).filter(Boolean);
881
- const unknown = wanted.filter(f => !validFrameworks.has(f));
885
+ const unknown = wanted.filter(f => !ENTRYPOINT_FRAMEWORKS.includes(f));
882
886
  if (unknown.length > 0) {
883
887
  return {
884
888
  error: 'invalid-framework',
885
- message: `Unknown framework "${unknown.join('", "')}". Valid: ${[...validFrameworks].sort().join(', ')}.`,
889
+ message: `Unknown framework "${unknown.join('", "')}". Valid: ${ENTRYPOINT_FRAMEWORKS.join(', ')}.`,
886
890
  };
887
891
  }
888
892
  }
@@ -1462,6 +1466,8 @@ function isFrameworkEntrypoint(symbol, index) {
1462
1466
  }
1463
1467
 
1464
1468
  module.exports = {
1469
+ ENTRYPOINT_TYPES,
1470
+ ENTRYPOINT_FRAMEWORKS,
1465
1471
  FRAMEWORK_PATTERNS,
1466
1472
  detectEntrypoints,
1467
1473
  isFrameworkEntrypoint,
@@ -0,0 +1,68 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * How often an unverified reason was a real edge on the release board.
5
+ *
6
+ * Display only. The table labels the per-reason summary line of an
7
+ * unverified section and orders that line; it decides no tier, filter,
8
+ * score or account, and nothing outside the output layer reads it (guarded
9
+ * by test/evidence-bands.test.js). Entries of a section keep their own
10
+ * order.
11
+ *
12
+ * The table (core/evidence-bands.json) is written by
13
+ * `node eval/run-unverified-census.js --bands` from the release board's
14
+ * oracle reports: per reason, the candidates the language's own tool scored
15
+ * and how many of them were real edges. A reason scored fewer times than
16
+ * the table's minimum has no row and is labelled "not measured".
17
+ */
18
+
19
+ const DEFAULT_TABLE = require('./evidence-bands.json');
20
+
21
+ const BAND_RANK = Object.freeze({ often: 0, sometimes: 1, rarely: 2 });
22
+ const UNMEASURED_RANK = 3;
23
+
24
+ const codeUnitCompare = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
25
+
26
+ /** The measured row of a reason: { band, scored, real }, or null. */
27
+ function evidenceBand(reason, table = DEFAULT_TABLE) {
28
+ const row = table?.reasons?.[reason];
29
+ if (!row || BAND_RANK[row.band] === undefined) return null;
30
+ return { band: row.band, scored: row.scored, real: row.real };
31
+ }
32
+
33
+ /**
34
+ * The reasons of a list of unverified entries (or a `{ reason: count }`
35
+ * map) with their counts and bands: most often real first, then the larger
36
+ * count, then the name.
37
+ */
38
+ function summarizeReasons(entries, table = DEFAULT_TABLE) {
39
+ const counts = new Map();
40
+ if (Array.isArray(entries)) {
41
+ for (const entry of entries) {
42
+ const reason = entry?.reason || 'unknown';
43
+ counts.set(reason, (counts.get(reason) || 0) + 1);
44
+ }
45
+ } else {
46
+ for (const [reason, count] of Object.entries(entries || {})) {
47
+ if (count > 0) counts.set(reason, count);
48
+ }
49
+ }
50
+ const rows = [...counts].map(([reason, count]) => ({ reason, count, ...evidenceBand(reason, table) }));
51
+ const rank = row => (row.band ? BAND_RANK[row.band] : UNMEASURED_RANK);
52
+ return rows.sort((a, b) => rank(a) - rank(b) || b.count - a.count || codeUnitCompare(a.reason, b.reason));
53
+ }
54
+
55
+ /**
56
+ * The summary line of an unverified section, or null when it would add
57
+ * nothing: one reason that is not measured.
58
+ */
59
+ function reasonSummaryLine(entries, table = DEFAULT_TABLE) {
60
+ const rows = summarizeReasons(entries, table);
61
+ if (rows.length === 0 || (rows.length === 1 && !rows[0].band)) return null;
62
+ const parts = rows.map(row => (row.band
63
+ ? `${row.reason} ${row.count} (${row.band}, ${row.real}/${row.scored})`
64
+ : `${row.reason} ${row.count} (not measured)`));
65
+ return ` by reason, with how often each was a real call on the release board: ${parts.join('; ')}`;
66
+ }
67
+
68
+ module.exports = { evidenceBand, summarizeReasons, reasonSummaryLine };
@@ -0,0 +1,44 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "source": "unverified candidates of the release oracle board, scored by each language's own tool",
4
+ "boardDate": "2026-11-13",
5
+ "repositories": [
6
+ "cjson",
7
+ "clap",
8
+ "cobra",
9
+ "fmt",
10
+ "httpx",
11
+ "javapoet",
12
+ "newtonsoft-json",
13
+ "preact-signals",
14
+ "ripgrep",
15
+ "viper"
16
+ ],
17
+ "minimumScored": 30,
18
+ "thresholds": {
19
+ "often": 0.5,
20
+ "sometimes": 0.15
21
+ },
22
+ "reasons": {
23
+ "method-ambiguous": {
24
+ "scored": 62,
25
+ "real": 21,
26
+ "band": "sometimes"
27
+ },
28
+ "overload-ambiguous": {
29
+ "scored": 591,
30
+ "real": 393,
31
+ "band": "often"
32
+ },
33
+ "possible-dispatch": {
34
+ "scored": 782,
35
+ "real": 59,
36
+ "band": "rarely"
37
+ },
38
+ "single-owner": {
39
+ "scored": 45,
40
+ "real": 45,
41
+ "band": "often"
42
+ }
43
+ }
44
+ }