ucn 4.2.2 → 4.2.3
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 +23 -11
- package/core/build-worker.js +11 -2
- package/core/cache.js +21 -1
- package/core/callers.js +618 -43
- package/core/deadcode.js +25 -2
- package/core/entrypoints.js +9 -1
- package/core/output/analysis.js +4 -0
- package/core/project.js +13 -3
- package/core/search.js +149 -26
- package/languages/java.js +32 -3
- package/languages/javascript.js +257 -34
- package/languages/python.js +77 -10
- package/languages/rust.js +99 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ UCN is deliberately lightweight:
|
|
|
34
34
|
- **No language servers** - tree-sitter does the parsing, no compilation needed
|
|
35
35
|
- **MCP is optional** - only needed if you connect UCN to an AI agent, the CLI and Skill work on their own
|
|
36
36
|
|
|
37
|
-
And it's built for **auditable trust**. grep hands you raw text matches to verify yourself; UCN separates target-backed edges from possible edges, explains exclusions, and reconciles its observed text set. It does not turn a zero into a deletion claim. CI re-derives answers from real compilers and language servers (ts-morph, pyright, gopls, rust-analyzer, jdtls). Publishing is gated on a representative
|
|
37
|
+
And it's built for **auditable trust**. grep hands you raw text matches to verify yourself; UCN separates target-backed edges from possible edges, explains exclusions, and reconciles its observed text set. It does not turn a zero into a deletion claim. CI re-derives answers from real compilers and language servers (ts-morph, pyright, gopls, rust-analyzer, jdtls). Publishing is gated on a representative seven-repository board; the scheduled board covers nineteen pinned repositories plus rotating fresh repositories. See [Answers you can trust](#answers-you-can-trust).
|
|
38
38
|
|
|
39
39
|
### Same engine, different transport
|
|
40
40
|
|
|
@@ -175,19 +175,31 @@ This is a release gate, not a promise of universal program understanding. CI re-
|
|
|
175
175
|
|
|
176
176
|
Current pinned release-board results:
|
|
177
177
|
|
|
178
|
-
| Repository | Language oracle | Confirmed caller precision | Caller recall | Callee precision/recall | Command checks |
|
|
179
|
-
|
|
180
|
-
| preact-signals | ts-morph | 100% | 100% | 100% / 100% | 100% |
|
|
181
|
-
| httpx | pyright | 100% | 100% | 100% / 100% | 100% |
|
|
182
|
-
| cobra | gopls | 100% | 100% | 100% / 100% | 100% |
|
|
183
|
-
|
|
|
184
|
-
|
|
|
178
|
+
| Repository | Pinned commit | Language oracle | Confirmed caller precision | Caller recall | Callee precision/recall | Command checks |
|
|
179
|
+
|---|---|---|---:|---:|---:|---:|
|
|
180
|
+
| [preact-signals](https://github.com/preactjs/signals) | [`e0ce9fdf`](https://github.com/preactjs/signals/commit/e0ce9fdf92df7f0ece2c89d44554c39f36dc6882) | ts-morph | 100% | 100% | 100% / 100% | 100% |
|
|
181
|
+
| [httpx](https://github.com/encode/httpx) | [`b5addb64`](https://github.com/encode/httpx/commit/b5addb64f0161ff6bfe94c124ef76f6a1fba5254) | pyright | 100% | 100% | 100% / 100% | 100% |
|
|
182
|
+
| [cobra](https://github.com/spf13/cobra) | [`ad460ea8`](https://github.com/spf13/cobra/commit/ad460ea8f249db69c943a365fb84f3a59042d54e) | gopls | 100% | 100% | 100% / 100% | 100% |
|
|
183
|
+
| [viper](https://github.com/spf13/viper) | [`528f7416`](https://github.com/spf13/viper/commit/528f7416c4b56a4948673984b190bf8713f0c3c4) | gopls | 100% | 100% | 100% / 100% | 100% |
|
|
184
|
+
| [ripgrep](https://github.com/BurntSushi/ripgrep) | [`82313cf9`](https://github.com/BurntSushi/ripgrep/commit/82313cf95849bfe425109ad9506a52154879b1b1) | rust-analyzer | 100% | 100% | 100% / 100% | 100% |
|
|
185
|
+
| [clap](https://github.com/clap-rs/clap) | [`d3e59a9a`](https://github.com/clap-rs/clap/commit/d3e59a9ab214910b9dad02921b7ef42c6400de9b) | rust-analyzer | 100% | 100% | 100% / 100% | 100% |
|
|
186
|
+
| [javapoet](https://github.com/square/javapoet) | [`b9017a95`](https://github.com/square/javapoet/commit/b9017a9503b76e11b4ad4c1a9f050e2d29112cb0) | jdtls | 100% | 100% | 100% / 100% | 100% |
|
|
185
187
|
|
|
186
|
-
The command checks cover exact definition lookup, `find`, `fn`/`class`, `brief`, `typedef`, `usages`, `tests`, and `example` against
|
|
188
|
+
Each semantic run draws a deterministic, reference-count-stratified sample of up to 50 compiler/LSP symbols per repository. The command checks cover exact definition lookup, `find`, `fn`/`class`, `brief`, `typedef`, `usages`, `tests`, and `example` against that same external-oracle population. The dead-code arm audits up to 100 claims per release repository and currently has zero false-dead claims. The semantic gate caps configuration-unscored confirmed caller and callee evidence at 10%, so platform filtering cannot silently make a small scored precision subset look representative. Configuration-gated unverified abstentions are reported separately and never presented as confirmed evidence. The performance arm runs every repository in an isolated process, takes three fresh-cache startup samples, reports median and maximum first-query latency, and independently gates steady-state p50/p95 and peak memory.
|
|
189
|
+
|
|
190
|
+
Evidence: [semantic rollup](eval/reports/oracle-eval-rollup-2026-07-20.md), [dead-code rollup](eval/reports/deadcode-eval-rollup-2026-07-20.md), and [performance rollup](eval/reports/performance-gate-2026-07-20.md). Reproduce all three release gates with `npm run trust:gate`; the pinned source manifest is [`eval/lib/repos.js`](eval/lib/repos.js).
|
|
187
191
|
|
|
188
192
|
Unverified precision is reported separately and is intentionally much lower on dispatch-heavy code. Unverified entries are review candidates, not confirmed claims. Rust feature-gated sites that one compiler configuration cannot load are reported as unscored rather than counted as passes.
|
|
189
193
|
|
|
190
|
-
The scheduled board covers nineteen pinned repositories, multiple sampling seeds, and a rotating fresh-repository arm. It is broader than the
|
|
194
|
+
The scheduled board covers nineteen pinned repositories, multiple sampling seeds, and a rotating fresh-repository arm. It is broader than the seven-repository publish gate and is used to expose regressions and overfitting:
|
|
195
|
+
|
|
196
|
+
- JavaScript and TypeScript: zod, preact-signals, express, hono, zustand, fastify
|
|
197
|
+
- Python: httpx, rich, click
|
|
198
|
+
- Go: cobra, grpc-go, viper, chi
|
|
199
|
+
- Rust: ripgrep, cursive, clap
|
|
200
|
+
- Java: gson, javapoet, jsoup
|
|
201
|
+
|
|
202
|
+
Repos that expose a gap remain on this scheduled board; they are not removed to preserve a perfect release table. HTML has parser, integration, and cross-surface regression coverage, but no compiler/LSP real-repository oracle is claimed. The tree commands `trace`, `blast`, `reverse-trace`, and `affected-tests` follow the same evidence discipline. Run `ucn doctor --deep` for task-specific readiness on your repository.
|
|
191
203
|
|
|
192
204
|
## Change code without breaking things
|
|
193
205
|
|
|
@@ -426,7 +438,7 @@ function compareNames(a, b) {
|
|
|
426
438
|
- **Coverage** - every command, every supported language, every surface (CLI, MCP, interactive)
|
|
427
439
|
- **Systematic** - a harness exercises all command and flag combinations against real multi-language fixtures
|
|
428
440
|
- **Test types** - unit, integration, per-language regression, formatter, cache, MCP edge cases, architecture parity guards
|
|
429
|
-
- **Ground truth** - caller, callee, and oracle-judgable command behavior is measured against ts-morph, pyright, gopls, rust-analyzer, and jdtls. The publish gate uses
|
|
441
|
+
- **Ground truth** - caller, callee, and oracle-judgable command behavior is measured against ts-morph, pyright, gopls, rust-analyzer, and jdtls. The publish gate uses seven representative repositories; the scheduled board uses nineteen pinned repositories plus a rotating fresh-repository arm. Gates track confirmed precision, semantic recall, conservation, observed-zero agreement, oracle configuration coverage, dead-code false positives, isolated startup latency, steady-state latency, and peak memory (see [Answers you can trust](#answers-you-can-trust))
|
|
430
442
|
|
|
431
443
|
---
|
|
432
444
|
|
package/core/build-worker.js
CHANGED
|
@@ -49,12 +49,14 @@ function addSymbol(fileEntry, item, type) {
|
|
|
49
49
|
if (item.implements) symbol.implements = item.implements;
|
|
50
50
|
if (item.indent !== undefined) symbol.indent = item.indent;
|
|
51
51
|
if (item.isNested) symbol.isNested = item.isNested;
|
|
52
|
+
if (item.enclosingType) symbol.enclosingType = item.enclosingType;
|
|
52
53
|
if (item.isMethod) symbol.isMethod = item.isMethod;
|
|
53
54
|
if (item.receiver) symbol.receiver = item.receiver;
|
|
54
55
|
if (item.className) symbol.className = item.className;
|
|
55
56
|
if (item.memberType) symbol.memberType = item.memberType;
|
|
56
57
|
if (item.fieldType) symbol.fieldType = item.fieldType;
|
|
57
58
|
if (item.aliasOf) symbol.aliasOf = item.aliasOf;
|
|
59
|
+
if (item.derefTarget) symbol.derefTarget = item.derefTarget;
|
|
58
60
|
if (item.decorators && item.decorators.length > 0) symbol.decorators = item.decorators;
|
|
59
61
|
if (item.decoratorsWithArgs && item.decoratorsWithArgs.length > 0) symbol.decoratorsWithArgs = item.decoratorsWithArgs;
|
|
60
62
|
if (item.annotationsWithArgs && item.annotationsWithArgs.length > 0) symbol.annotationsWithArgs = item.annotationsWithArgs;
|
|
@@ -64,13 +66,15 @@ function addSymbol(fileEntry, item, type) {
|
|
|
64
66
|
if (item.traitName) symbol.traitName = item.traitName;
|
|
65
67
|
if (item.isSignature) symbol.isSignature = true;
|
|
66
68
|
if (item.memberAssigned) symbol.memberAssigned = true;
|
|
69
|
+
if (item.bodyScopedName) symbol.bodyScopedName = true;
|
|
67
70
|
if (item.registryMember) symbol.registryMember = true;
|
|
68
71
|
if (item.registryContainer) symbol.registryContainer = item.registryContainer;
|
|
69
72
|
|
|
70
73
|
fileEntry.symbols.push(symbol);
|
|
71
74
|
// Property-assignment defs declare no lexical name (fix #269) — kept in
|
|
72
75
|
// lockstep with project.js addSymbol (the parallel≡sequential guard).
|
|
73
|
-
|
|
76
|
+
// Named function expressions bind only inside their own body.
|
|
77
|
+
if (!item.memberAssigned && !item.bodyScopedName) {
|
|
74
78
|
fileEntry.bindings.push({
|
|
75
79
|
id: symbol.bindingId,
|
|
76
80
|
name: symbol.name,
|
|
@@ -165,7 +169,12 @@ function processFile(filePath) {
|
|
|
165
169
|
.map(n => {
|
|
166
170
|
// Rename pairing (fix #269) — lockstep with project.js.
|
|
167
171
|
const rn = (i.renames || []).find(r => r.original === n);
|
|
168
|
-
return {
|
|
172
|
+
return {
|
|
173
|
+
name: n,
|
|
174
|
+
module: i.module,
|
|
175
|
+
...(rn && { alias: rn.local }),
|
|
176
|
+
...(i.defaultLike && { defaultLike: true }),
|
|
177
|
+
};
|
|
169
178
|
})),
|
|
170
179
|
exports: exports.map(e => e.name),
|
|
171
180
|
exportDetails: exports,
|
package/core/cache.js
CHANGED
|
@@ -164,7 +164,27 @@ const UCN_VERSION = require('../package.json').version;
|
|
|
164
164
|
// (`m.get_many::<T>()`); v64 caches misclassified them as references.
|
|
165
165
|
// v66: Rust call records preserve receiverFlowInvalidated after a non-call
|
|
166
166
|
// lexical rebinding, preventing stale return types from excluding true calls.
|
|
167
|
-
|
|
167
|
+
// v67: JS/TS constructor calls preserve lexical-shadow evidence, and method
|
|
168
|
+
// calls on fresh constructions preserve the exact constructed receiver type.
|
|
169
|
+
// Both fields affect caller identity and must not be read from older shards.
|
|
170
|
+
// v68: unaliased dotted Python imports persist separate package and submodule
|
|
171
|
+
// ownership edges (`import pkg.sub` binds pkg and loads pkg.sub).
|
|
172
|
+
// v69: CommonJS simple-require bindings preserve defaultLike so callable
|
|
173
|
+
// module.exports values resolve exactly across caller and callee queries.
|
|
174
|
+
// v70: Python receiver records preserve exact constructor provenance and
|
|
175
|
+
// context-manager result bindings for conservative dispatch tiering.
|
|
176
|
+
// v71: structural method records preserve untyped local-receiver ownership;
|
|
177
|
+
// single-owner spelling alone cannot confirm a parameter/assigned value.
|
|
178
|
+
// v72: Rust type symbols preserve the compiler-declared Deref Target so
|
|
179
|
+
// wrapper receiver method lookup reaches the target type without guessing.
|
|
180
|
+
// v73: JS/TS call records preserve qualified-constructor provenance and bound
|
|
181
|
+
// local receivers; conditional reassignments no longer persist a definite
|
|
182
|
+
// inferred constructor type past a branch.
|
|
183
|
+
// Java nested type symbols preserve their enclosing type for exact constructor
|
|
184
|
+
// ownership across same-named top-level and inner classes, and cast receivers
|
|
185
|
+
// retain their compiler-declared type. Rust tuple fields are indexed by numeric
|
|
186
|
+
// position so `self.0.method()` participates in declared-field resolution.
|
|
187
|
+
const CACHE_FORMAT_VERSION = 73;
|
|
168
188
|
|
|
169
189
|
/**
|
|
170
190
|
* Save index to cache file
|