weavatrix 0.3.4 → 0.3.5
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 +24 -0
- package/docs/releases/v0.3.5.md +50 -0
- package/package.json +2 -1
- package/src/analysis/internal-audit.run.js +5 -2
- package/src/precision/lsp-overlay/budget-policy.js +78 -0
- package/src/precision/lsp-overlay/build.js +23 -15
- package/src/precision/lsp-overlay/source-session.js +15 -7
package/README.md
CHANGED
|
@@ -311,6 +311,17 @@ before starting the MCP server for parser-only operation from the first build, o
|
|
|
311
311
|
`precision:"off"` to `rebuild_graph` / `open_repo`. The MCPB installer exposes the same `lsp` / `off`
|
|
312
312
|
choice as **TypeScript/JavaScript semantic precision**.
|
|
313
313
|
|
|
314
|
+
The broad prewarm stays conservative by default: it queries 32 ranked symbols and never more than
|
|
315
|
+
64. A deliberate high-budget run can set `WEAVATRIX_PRECISION_MAX_SYMBOLS` above 64 (for example,
|
|
316
|
+
`1000`), or set `WEAVATRIX_PRECISION_PREWARM=full` to select every eligible target up to the hard
|
|
317
|
+
10,000-symbol safety ceiling. Expanded runs receive proportionate reference/link and deadline
|
|
318
|
+
budgets, still capped at 131,072 references/links, a 30-minute deadline, 1,024 open documents and
|
|
319
|
+
128 MiB of verified source. `WEAVATRIX_PRECISION_MAX_REFERENCES`,
|
|
320
|
+
`WEAVATRIX_PRECISION_MAX_LINKS`, and `WEAVATRIX_PRECISION_TIMEOUT_MS` can lower or explicitly tune
|
|
321
|
+
those budgets. Repositories that exceed any hard ceiling remain honestly `PARTIAL`; the default
|
|
322
|
+
memory/time profile is unchanged. On-demand `get_dependents`, `inspect_symbol`, and
|
|
323
|
+
`change_impact` queries remain revision-bound separate caches and do not require a full prewarm.
|
|
324
|
+
|
|
314
325
|
**search / source** — `search_code` (ripgrep-backed, pure-Node fallback, with repository-relative
|
|
315
326
|
path globs on Windows/macOS/Linux), `read_source` (a
|
|
316
327
|
symbol's actual code in one hop), `context_bundle` (definition plus grouped inbound/outbound
|
|
@@ -413,6 +424,19 @@ metrics are not persisted or transmitted by Weavatrix. If a source checkout's pa
|
|
|
413
424
|
while an old daemon remains alive, `initialize`, `tools/list`, and tool calls fail loudly with
|
|
414
425
|
`STALE_RUNTIME` until the client reconnects; the opt-out is reserved for deliberate development.
|
|
415
426
|
|
|
427
|
+
### 0.3.5 explicit full semantic prewarm
|
|
428
|
+
|
|
429
|
+
- Normal startup remains bounded to 32 ranked semantic targets. An explicit
|
|
430
|
+
`WEAVATRIX_PRECISION_PREWARM=full` run, or a requested symbol budget above
|
|
431
|
+
64, can now cover every eligible JS/TS target within hard time, source and
|
|
432
|
+
memory ceilings instead of being silently clamped to 64.
|
|
433
|
+
- Real Hosted dogfood queried all 800 eligible targets in about 49 seconds and
|
|
434
|
+
produced 4,127 `EXACT_LSP` edges with `COMPLETE` semantic precision.
|
|
435
|
+
- Unreferenced public exports are now reported as low-confidence module-surface
|
|
436
|
+
evidence, not as proof that their implementations are dead.
|
|
437
|
+
|
|
438
|
+
Full patch notes: [docs/releases/v0.3.5.md](docs/releases/v0.3.5.md).
|
|
439
|
+
|
|
416
440
|
### 0.3.4 self-audit precision and bounded dynamic imports
|
|
417
441
|
|
|
418
442
|
- JS/TS graph construction now resolves the existing local-file prefix in
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Weavatrix 0.3.5
|
|
2
|
+
|
|
3
|
+
0.3.5 removes the hidden 64-symbol ceiling from deliberately high-budget
|
|
4
|
+
TypeScript/JavaScript semantic prewarms while keeping the ordinary offline
|
|
5
|
+
startup profile unchanged.
|
|
6
|
+
|
|
7
|
+
## Explicit full semantic prewarm
|
|
8
|
+
|
|
9
|
+
- The default remains a 32-symbol ranked prewarm with the previous 64-symbol,
|
|
10
|
+
16,384-reference/link and 60-second hard ceilings.
|
|
11
|
+
- Setting `WEAVATRIX_PRECISION_MAX_SYMBOLS` above 64 now explicitly opts into
|
|
12
|
+
the expanded policy instead of being silently clamped back to 64.
|
|
13
|
+
- `WEAVATRIX_PRECISION_PREWARM=full` selects all eligible targets, bounded by a
|
|
14
|
+
10,000-symbol hard ceiling. It is intended for CI, release evidence and
|
|
15
|
+
manually requested deep repository audits, not implicit startup work.
|
|
16
|
+
- Expanded runs scale their default reference/link budget to 16 per requested
|
|
17
|
+
symbol and their deadline to one second per requested symbol. Hard limits
|
|
18
|
+
remain 131,072 references/links and 30 minutes.
|
|
19
|
+
- Verified source remains bounded to 4 MiB per file, 1,024 simultaneously open
|
|
20
|
+
documents and 128 MiB total. Crossing a ceiling remains `PARTIAL`; it never
|
|
21
|
+
becomes a false complete result or an unbounded allocation.
|
|
22
|
+
|
|
23
|
+
The point-query cache used by `get_dependents` and `inspect_symbol`, and the
|
|
24
|
+
bounded changed-symbol batch used by `change_impact`, remain separate from this
|
|
25
|
+
broad overlay. Existing default and on-demand behavior is unchanged.
|
|
26
|
+
|
|
27
|
+
The regression suite proves both sides of the contract: an ordinary build still
|
|
28
|
+
selects 32 of 80 candidates and reports `PARTIAL`, while an explicit 1,000-symbol
|
|
29
|
+
budget and `full` mode each query all 80 targets and report `COMPLETE` with a
|
|
30
|
+
cooperative provider.
|
|
31
|
+
|
|
32
|
+
## Dead-code verdict consistency
|
|
33
|
+
|
|
34
|
+
- `run_audit` now labels an unreferenced public export as low-confidence
|
|
35
|
+
`unused-export-surface` evidence and sets `deadCodeCandidate:false`.
|
|
36
|
+
- The audit explains that an unused module surface is not proof of a dead
|
|
37
|
+
implementation; downstream packages and runtime registries may be invisible.
|
|
38
|
+
- This matches `find_dead_code`, which keeps the same uncertain public surface
|
|
39
|
+
out of its default medium-confidence review queue while reporting the
|
|
40
|
+
suppressed low-confidence count.
|
|
41
|
+
|
|
42
|
+
## Consistent unused-export confidence
|
|
43
|
+
|
|
44
|
+
`run_audit` now labels an unused export as low-confidence export-surface
|
|
45
|
+
evidence, not a medium-confidence dead implementation. The finding explicitly
|
|
46
|
+
distinguishes removing an unnecessary export keyword from deleting the symbol,
|
|
47
|
+
which still requires the separate exact/reference-aware `find_dead_code`
|
|
48
|
+
workflow. This aligns Health with the default dead-code queue, where public and
|
|
49
|
+
framework-sensitive candidates are intentionally suppressed until
|
|
50
|
+
`min_confidence=low` is requested.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "weavatrix",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local repository intelligence MCP for AI coding agents: reusable architecture graph for fast application understanding, Health, dead code, clones, history, blast radius and architecture safeguards.",
|
|
6
6
|
"author": "Sergii Ziborov <sergii.ziborov@gmail.com>",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"docs/releases/v0.3.2.md",
|
|
39
39
|
"docs/releases/v0.3.3.md",
|
|
40
40
|
"docs/releases/v0.3.4.md",
|
|
41
|
+
"docs/releases/v0.3.5.md",
|
|
41
42
|
"docs/releases/v0.2.19.md",
|
|
42
43
|
"README.md",
|
|
43
44
|
"SECURITY.md",
|
|
@@ -148,13 +148,16 @@ export async function runInternalAudit(repoPath, {
|
|
|
148
148
|
category: "unused",
|
|
149
149
|
rule: "unused-export",
|
|
150
150
|
severity: "info",
|
|
151
|
-
confidence: "
|
|
151
|
+
confidence: "low",
|
|
152
152
|
title: `Unused export: ${symbol.label.replace(/\(\)$/, "")} — ${symbol.file}`,
|
|
153
|
-
detail: `${symbol.reason}.
|
|
153
|
+
detail: `${symbol.reason}. This is unused module-surface evidence, not proof that the implementation is dead: downstream packages and runtime registration can be invisible to the repository graph. Review external consumers first; remove only the export keyword when the symbol is still used internally, and delete the implementation only after separate dead-code verification.`,
|
|
154
154
|
file: symbol.file,
|
|
155
155
|
symbol: symbol.label,
|
|
156
156
|
graphNodeId: symbol.id,
|
|
157
157
|
source: "internal",
|
|
158
|
+
classification: "unused-export-surface",
|
|
159
|
+
deadCodeCandidate: false,
|
|
160
|
+
fixHint: "check downstream/runtime consumers, then remove only the export surface or run a separate exact dead-code review before deleting the implementation",
|
|
158
161
|
}));
|
|
159
162
|
unusedExportCount++;
|
|
160
163
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {boundedInteger} from '../../bounds.js'
|
|
2
|
+
|
|
3
|
+
const DEFAULT_MAX_SYMBOLS = 32
|
|
4
|
+
const BOUNDED_MAX_SYMBOLS = 64
|
|
5
|
+
const EXPANDED_MAX_SYMBOLS = 10_000
|
|
6
|
+
const DEFAULT_MAX_REFERENCES = 2_048
|
|
7
|
+
const BOUNDED_MAX_REFERENCES = 16_384
|
|
8
|
+
const EXPANDED_MAX_REFERENCES = 131_072
|
|
9
|
+
const DEFAULT_MAX_LINKS = 2_048
|
|
10
|
+
const BOUNDED_MAX_LINKS = 16_384
|
|
11
|
+
const EXPANDED_MAX_LINKS = 131_072
|
|
12
|
+
const DEFAULT_TIMEOUT_MS = 45_000
|
|
13
|
+
const BOUNDED_MAX_TIMEOUT_MS = 60_000
|
|
14
|
+
const EXPANDED_MAX_TIMEOUT_MS = 30 * 60_000
|
|
15
|
+
|
|
16
|
+
function configured(value, environmentName) {
|
|
17
|
+
if (value !== undefined && value !== null && String(value).trim() !== '') return value
|
|
18
|
+
const environmentValue = process.env[environmentName]
|
|
19
|
+
return environmentValue != null && String(environmentValue).trim() !== ''
|
|
20
|
+
? environmentValue : undefined
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function precisionPrewarmBudget(options = {}) {
|
|
24
|
+
const targetBatch = Array.isArray(options.targetIds) && options.targetIds.length > 0
|
|
25
|
+
const configuredMode = String(configured(
|
|
26
|
+
options.prewarmMode,
|
|
27
|
+
'WEAVATRIX_PRECISION_PREWARM',
|
|
28
|
+
) || 'bounded').trim().toLowerCase()
|
|
29
|
+
const full = configuredMode === 'full' && !targetBatch
|
|
30
|
+
const requestedSymbols = configured(options.maxSymbols, 'WEAVATRIX_PRECISION_MAX_SYMBOLS')
|
|
31
|
+
const numericSymbols = Number(requestedSymbols)
|
|
32
|
+
const expanded = full || (Number.isFinite(numericSymbols) && numericSymbols > BOUNDED_MAX_SYMBOLS)
|
|
33
|
+
const maxSymbols = full ? EXPANDED_MAX_SYMBOLS : boundedInteger(
|
|
34
|
+
requestedSymbols,
|
|
35
|
+
DEFAULT_MAX_SYMBOLS,
|
|
36
|
+
1,
|
|
37
|
+
expanded ? EXPANDED_MAX_SYMBOLS : BOUNDED_MAX_SYMBOLS,
|
|
38
|
+
)
|
|
39
|
+
const referenceFallback = expanded
|
|
40
|
+
? Math.min(EXPANDED_MAX_REFERENCES, Math.max(DEFAULT_MAX_REFERENCES, maxSymbols * 16))
|
|
41
|
+
: DEFAULT_MAX_REFERENCES
|
|
42
|
+
const linkFallback = expanded
|
|
43
|
+
? Math.min(EXPANDED_MAX_LINKS, Math.max(DEFAULT_MAX_LINKS, maxSymbols * 16))
|
|
44
|
+
: DEFAULT_MAX_LINKS
|
|
45
|
+
const timeoutFallback = expanded
|
|
46
|
+
? Math.min(15 * 60_000, Math.max(DEFAULT_TIMEOUT_MS, maxSymbols * 1_000))
|
|
47
|
+
: DEFAULT_TIMEOUT_MS
|
|
48
|
+
const maxReferences = boundedInteger(
|
|
49
|
+
configured(options.maxReferences, 'WEAVATRIX_PRECISION_MAX_REFERENCES'),
|
|
50
|
+
referenceFallback,
|
|
51
|
+
1,
|
|
52
|
+
expanded ? EXPANDED_MAX_REFERENCES : BOUNDED_MAX_REFERENCES,
|
|
53
|
+
)
|
|
54
|
+
const maxLinks = boundedInteger(
|
|
55
|
+
configured(options.maxLinks, 'WEAVATRIX_PRECISION_MAX_LINKS'),
|
|
56
|
+
linkFallback,
|
|
57
|
+
1,
|
|
58
|
+
expanded ? EXPANDED_MAX_LINKS : BOUNDED_MAX_LINKS,
|
|
59
|
+
)
|
|
60
|
+
const timeoutMs = boundedInteger(
|
|
61
|
+
configured(options.timeoutMs, 'WEAVATRIX_PRECISION_TIMEOUT_MS'),
|
|
62
|
+
timeoutFallback,
|
|
63
|
+
100,
|
|
64
|
+
expanded ? EXPANDED_MAX_TIMEOUT_MS : BOUNDED_MAX_TIMEOUT_MS,
|
|
65
|
+
)
|
|
66
|
+
return {
|
|
67
|
+
expanded,
|
|
68
|
+
full,
|
|
69
|
+
maxSymbols,
|
|
70
|
+
maxReferences,
|
|
71
|
+
maxLinks,
|
|
72
|
+
timeoutMs,
|
|
73
|
+
maxOpenDocuments: expanded ? 1_024 : 96,
|
|
74
|
+
maxOpenBytes: expanded ? 128 * 1024 * 1024 : 32 * 1024 * 1024,
|
|
75
|
+
maxClassificationDocuments: expanded ? 1_024 : 96,
|
|
76
|
+
maxClassificationBytes: expanded ? 128 * 1024 * 1024 : 32 * 1024 * 1024,
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {boundedInteger} from '../../bounds.js'
|
|
2
1
|
import {createTypeScriptLspClient} from '../typescript-lsp-provider.js'
|
|
2
|
+
import {precisionPrewarmBudget} from './budget-policy.js'
|
|
3
3
|
import {baseOverlay} from './contract.js'
|
|
4
4
|
import {precisionOverlayMatches} from './contract.js'
|
|
5
5
|
import {collectReferenceResults} from './reference-results.js'
|
|
@@ -106,17 +106,24 @@ function failedOverlay(session, error) {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
function createSession(options) {
|
|
109
|
-
const
|
|
110
|
-
const boundedReferences = boundedInteger(options.maxReferences, 2_048, 1, 16_384)
|
|
111
|
-
const boundedLinks = boundedInteger(options.maxLinks, 2_048, 1, 16_384)
|
|
112
|
-
const boundedTimeout = boundedInteger(options.timeoutMs, 45_000, 100, 60_000)
|
|
109
|
+
const budget = precisionPrewarmBudget(options)
|
|
113
110
|
return {
|
|
114
111
|
...options,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
112
|
+
expandedPrewarm: budget.expanded,
|
|
113
|
+
fullPrewarm: budget.full,
|
|
114
|
+
boundedMax: budget.maxSymbols,
|
|
115
|
+
boundedReferences: budget.maxReferences,
|
|
116
|
+
boundedLinks: budget.maxLinks,
|
|
117
|
+
maxOpenDocuments: budget.maxOpenDocuments,
|
|
118
|
+
maxOpenBytes: budget.maxOpenBytes,
|
|
119
|
+
maxClassificationDocuments: budget.maxClassificationDocuments,
|
|
120
|
+
maxClassificationBytes: budget.maxClassificationBytes,
|
|
121
|
+
deadline: Date.now() + budget.timeoutMs,
|
|
122
|
+
request: {
|
|
123
|
+
maxSymbols: budget.maxSymbols,
|
|
124
|
+
maxReferences: budget.maxReferences,
|
|
125
|
+
maxLinks: budget.maxLinks,
|
|
126
|
+
},
|
|
120
127
|
links: [],
|
|
121
128
|
seen: new Set(),
|
|
122
129
|
evidenceSeen: new Set(),
|
|
@@ -138,17 +145,18 @@ export async function buildLspPrecisionOverlay({
|
|
|
138
145
|
graph,
|
|
139
146
|
graphPath,
|
|
140
147
|
mode = 'lsp',
|
|
141
|
-
maxSymbols
|
|
142
|
-
maxReferences
|
|
143
|
-
maxLinks
|
|
144
|
-
timeoutMs
|
|
148
|
+
maxSymbols,
|
|
149
|
+
maxReferences,
|
|
150
|
+
maxLinks,
|
|
151
|
+
timeoutMs,
|
|
152
|
+
prewarmMode,
|
|
145
153
|
targetIds,
|
|
146
154
|
clientFactory,
|
|
147
155
|
} = {}) {
|
|
148
156
|
if (!graph || !repoRoot) throw new Error('precision overlay requires repoRoot and graph')
|
|
149
157
|
const session = createSession({
|
|
150
158
|
repoRoot, graph, graphPath, mode, maxSymbols, maxReferences, maxLinks,
|
|
151
|
-
timeoutMs, targetIds, clientFactory,
|
|
159
|
+
timeoutMs, prewarmMode, targetIds, clientFactory,
|
|
152
160
|
})
|
|
153
161
|
if (mode === 'off') {
|
|
154
162
|
return persist(session, baseOverlay(graph, 'OFF', {
|
|
@@ -62,11 +62,15 @@ export async function ensureOpen(session, relPath) {
|
|
|
62
62
|
const file = norm(relPath)
|
|
63
63
|
if (!file || session.opened.has(file)) return
|
|
64
64
|
ensureBudget(session)
|
|
65
|
-
|
|
66
|
-
const
|
|
65
|
+
const maxDocuments = Number(session.maxOpenDocuments) || 96
|
|
66
|
+
const maxOpenBytes = Number(session.maxOpenBytes) || 32 * 1024 * 1024
|
|
67
|
+
if (session.opened.size >= maxDocuments) {
|
|
68
|
+
throw new PrecisionLimitError('precision open-document limit reached')
|
|
69
|
+
}
|
|
70
|
+
const maxBytes = Math.min(4 * 1024 * 1024, maxOpenBytes - session.openedBytes)
|
|
67
71
|
const {bytes, text} = verifiedSource(session, file, maxBytes)
|
|
68
72
|
if (bytes > 4 * 1024 * 1024) throw new PrecisionLimitError('precision document exceeds 4 MiB limit')
|
|
69
|
-
if (session.openedBytes + bytes >
|
|
73
|
+
if (session.openedBytes + bytes > maxOpenBytes) {
|
|
70
74
|
throw new PrecisionLimitError('precision source-transfer budget reached')
|
|
71
75
|
}
|
|
72
76
|
await awaitWithBudget(session, () => session.client.openDocument(file, text))
|
|
@@ -79,13 +83,15 @@ export function sourceForClassification(session, relPath) {
|
|
|
79
83
|
const file = norm(relPath)
|
|
80
84
|
if (session.openedTexts.has(file)) return session.openedTexts.get(file)
|
|
81
85
|
if (session.classificationTexts.has(file)) return session.classificationTexts.get(file)
|
|
82
|
-
|
|
86
|
+
const maxDocuments = Number(session.maxClassificationDocuments) || 96
|
|
87
|
+
const maxClassificationBytes = Number(session.maxClassificationBytes) || 32 * 1024 * 1024
|
|
88
|
+
if (session.classificationTexts.size >= maxDocuments) return null
|
|
83
89
|
let source
|
|
84
90
|
try {
|
|
85
91
|
source = verifiedSource(
|
|
86
92
|
session,
|
|
87
93
|
file,
|
|
88
|
-
Math.min(4 * 1024 * 1024,
|
|
94
|
+
Math.min(4 * 1024 * 1024, maxClassificationBytes - session.classificationBytes),
|
|
89
95
|
)
|
|
90
96
|
} catch (error) {
|
|
91
97
|
if (error instanceof PrecisionLimitError) return null
|
|
@@ -100,7 +106,9 @@ export async function ensureFullUniverse(session) {
|
|
|
100
106
|
if (session.fullUniverseOpened) return true
|
|
101
107
|
if (!session.universe.complete) return false
|
|
102
108
|
const additional = session.universe.files.filter((file) => !session.opened.has(file))
|
|
103
|
-
|
|
109
|
+
const maxDocuments = Number(session.maxOpenDocuments) || 96
|
|
110
|
+
const maxOpenBytes = Number(session.maxOpenBytes) || 32 * 1024 * 1024
|
|
111
|
+
if (session.opened.size + additional.length > maxDocuments) {
|
|
104
112
|
session.truncated = true
|
|
105
113
|
return false
|
|
106
114
|
}
|
|
@@ -115,7 +123,7 @@ export async function ensureFullUniverse(session) {
|
|
|
115
123
|
let bytes
|
|
116
124
|
try { bytes = statSync(resolvedFile.path).size }
|
|
117
125
|
catch { throw new PrecisionStaleGraphError() }
|
|
118
|
-
if (bytes > 4 * 1024 * 1024 || projectedBytes + bytes >
|
|
126
|
+
if (bytes > 4 * 1024 * 1024 || projectedBytes + bytes > maxOpenBytes) {
|
|
119
127
|
session.truncated = true
|
|
120
128
|
return false
|
|
121
129
|
}
|