weavatrix 0.2.5 → 0.2.7

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 (41) hide show
  1. package/README.md +77 -14
  2. package/SECURITY.md +14 -0
  3. package/docs/agent-task-benchmark.md +58 -0
  4. package/docs/releases/v0.2.7.md +93 -0
  5. package/package.json +6 -1
  6. package/scripts/run-agent-task-benchmark.mjs +119 -0
  7. package/skill/SKILL.md +31 -10
  8. package/src/analysis/allowed-test-runner.js +59 -0
  9. package/src/analysis/data-flow-evidence.js +114 -0
  10. package/src/analysis/dead-code-review.js +51 -0
  11. package/src/analysis/dep-check.js +42 -3
  12. package/src/analysis/duplicate-groups.js +84 -0
  13. package/src/analysis/graph-analysis.aggregate.js +3 -1
  14. package/src/analysis/graph-analysis.refs.js +19 -11
  15. package/src/analysis/hot-path-review.js +20 -2
  16. package/src/analysis/internal-audit.run.js +6 -1
  17. package/src/analysis/task-retrieval.js +116 -0
  18. package/src/bounds.js +4 -0
  19. package/src/graph/builder/lang-js.js +52 -17
  20. package/src/graph/freshness-probe.js +4 -2
  21. package/src/graph/incremental-refresh.js +4 -2
  22. package/src/graph/internal-builder.barrels.js +39 -2
  23. package/src/graph/internal-builder.build.js +58 -12
  24. package/src/mcp/catalog.mjs +28 -6
  25. package/src/mcp/graph-context.mjs +5 -1
  26. package/src/mcp/graph-diff.mjs +1 -1
  27. package/src/mcp/sync-payload.mjs +1 -1
  28. package/src/mcp/tools-actions.mjs +3 -1
  29. package/src/mcp/tools-context.mjs +164 -0
  30. package/src/mcp/tools-graph.mjs +48 -3
  31. package/src/mcp/tools-health.mjs +15 -28
  32. package/src/mcp/tools-impact-change.mjs +1 -0
  33. package/src/mcp/tools-source.mjs +7 -6
  34. package/src/mcp/tools-verified-change.mjs +169 -0
  35. package/src/precision/lsp-client.js +1 -1
  36. package/src/precision/lsp-overlay.js +1 -5
  37. package/src/precision/symbol-query.js +1 -5
  38. package/src/security/malware-heuristics.exclusions.js +3 -3
  39. package/src/security/malware-heuristics.roots.js +4 -1
  40. package/src/security/malware-heuristics.scan.js +4 -2
  41. package/src/security/malware-scoring.js +22 -5
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  Grep sees text. Weavatrix sees structure. It builds a dependency graph of any local repository —
6
6
  files, symbols, and the imports/calls/inheritance connecting them — and serves it to Claude Code,
7
7
  Codex, or any MCP client: change impact, transitive dependents, health audit, clone detection,
8
- coverage mapping. **34 tools available; 31 enabled by the default offline profile. Local-first: with
8
+ coverage mapping. **36 tools available; 33 enabled by the default offline profile. Local-first: with
9
9
  the defaults, no repository data leaves your machine.**
10
10
 
11
11
  - Website: [weavatrix.com](https://weavatrix.com)
@@ -22,12 +22,18 @@ answers grep can't produce:
22
22
  depends on them — with test coverage attached, so the **untested part of the blast radius** stands
23
23
  out before you ship.
24
24
  - *"Who calls this function?"* → `inspect_symbol` returns exact bounded TS/JS occurrences plus
25
- source context; `get_dependents` walks the symbol-level reverse graph transitively. Opt into
25
+ source context; `context_bundle` adds grouped graph relations, exact re-export sites, and a
26
+ smaller source workset. `get_dependents` walks the symbol-level reverse graph transitively. Opt into
26
27
  `include_container_importers:true` only when a broader module-import radius is intended.
27
28
  - *"Did my refactor actually decouple anything?"* → `graph_diff base_ref=HEAD~1` builds an immutable
28
29
  baseline graph without checking it out, then reports the structural delta: new module
29
30
  dependencies, broken or introduced import cycles, and symbols that lost their last caller.
30
31
 
32
+ - *"Is this change actually safe?"* -> `verified_change` accepts the task plus current diff/files and
33
+ returns one proof-carrying `PASS`, `BLOCKED`, or `UNKNOWN`: compact edit contexts, exact-symbol
34
+ impact, bounded call-argument flow, Git graph drift, architecture/duplicate/API ratchets, and
35
+ affected-test evidence.
36
+
31
37
  ## Quick start
32
38
 
33
39
  Requires Node ≥ 18. One command:
@@ -107,10 +113,11 @@ An agent skill with recipes ships in [skill/SKILL.md](skill/SKILL.md) — instal
107
113
 
108
114
  **graph** — `graph_stats`, `get_node`, `get_neighbors`, `query_graph`, `god_nodes`,
109
115
  `shortest_path`, `get_community`, `list_communities`, `module_map`, `get_dependents`,
110
- `change_impact`, `git_history`, `graph_diff`, `get_architecture_contract`,
116
+ `change_impact`, `verified_change`, `git_history`, `graph_diff`, `get_architecture_contract`,
111
117
  `prepare_change`. Runtime dependencies, TypeScript type-only coupling and language
112
118
  compile-only edges (Rust module/use, Java imports) are reported separately where that distinction
113
- changes the result.
119
+ changes the result. TypeScript type-space and value-space declarations keep distinct identities;
120
+ classes and enums that inhabit both spaces are labelled `both`.
114
121
 
115
122
  Every current edge carries versioned provenance. The parser emits `EXTRACTED`, `RESOLVED`, and
116
123
  `INFERRED`; the built-in bounded TypeScript/JavaScript precision overlay upgrades only references
@@ -118,7 +125,7 @@ confirmed by its bundled `typescript-language-server` + TypeScript runtime to `E
118
125
  `CONFLICT` means evidence disagrees. `graph_stats` reports the provenance breakdown and the semantic
119
126
  provider's `COMPLETE`, `PARTIAL`, `UNAVAILABLE`, or `OFF` state; `OFF` means precision was explicitly
120
127
  disabled and only static evidence is active. Java and Rust language-server providers are not bundled
121
- in 0.2.5: their edges never become `EXACT_LSP`, even when a mixed repository reports a complete
128
+ in 0.2.7: their edges never become `EXACT_LSP`, even when a mixed repository reports a complete
122
129
  TypeScript/JavaScript overlay.
123
130
 
124
131
  The bounded JS/TS provider is enabled by default for new graphs. Set `WEAVATRIX_PRECISION=off`
@@ -127,13 +134,15 @@ before starting the MCP server for parser-only operation from the first build, o
127
134
  choice as **TypeScript/JavaScript semantic precision**.
128
135
 
129
136
  **search / source** — `search_code` (ripgrep-backed, pure-Node fallback), `read_source` (a
130
- symbol's actual code in one hop), `inspect_symbol` (one exact bounded TS/JS reference query,
131
- logical containers, impact and source excerpts), `list_endpoints` (HTTP route inventory:
137
+ symbol's actual code in one hop), `context_bundle` (definition plus grouped inbound/outbound
138
+ containers, exact re-export sites and a few bounded source excerpts), `inspect_symbol` (one exact
139
+ bounded TS/JS reference query, logical containers, impact and source excerpts), `list_endpoints` (HTTP route inventory:
132
140
  Express/Fastify/Nest/Flask/FastAPI/Go mux/Rust axum and actix-web …)
133
141
 
134
142
  **health** — `find_dead_code` (bounded review queue for statically unreferenced and test-only files,
135
- functions, methods, and symbols, with confidence/evidence and explicit public/framework/dynamic caveats),
136
- `run_audit` (unused files/exports/dependencies with an explicit manifest-check summary,
143
+ functions, methods, and symbols, with evidence tiers, completed/remaining verification and explicit
144
+ public/framework/dynamic caveats),
145
+ `run_audit` (unused files/exports/dependencies with per-finding manifest/indexed-source/script/config verification,
137
146
  missing npm/Go/Python deps, runtime
138
147
  cycles, type-only/compile-only coupling, orphans, boundary rules, offline OSV vulnerabilities + typosquat +
139
148
  lockfile drift; accepts an immutable `base_ref`, `changed_files`, and `debt: new|existing|all` for
@@ -146,7 +155,9 @@ with separate graph/test risk), `verify_architecture`,
146
155
  `hot_path_review` ranks production symbols using parser-derived local time, memory, cyclomatic and
147
156
  call-site facts plus exact inside-loop allocation, copy, scan, sort and recursion evidence. Graph
148
157
  fan-in/fan-out and measured coverage (or explicitly labelled static test reachability) remain
149
- separate from local syntax cost. The ranking is not profiler data or interprocedural Big-O.
158
+ separate from local syntax cost. Its focused default uses `min_score:85` plus a narrow strong-local
159
+ fallback; pass `min_score:0` only when the full diagnostic queue is wanted. The ranking is not
160
+ profiler data or interprocedural Big-O.
150
161
 
151
162
  **build** — `rebuild_graph` (reports the structural delta, keeps the prior state as
152
163
  `graph.prev.json`)
@@ -157,7 +168,8 @@ use `kinds:["method"]` or a `path` prefix to narrow it. `min_confidence:"low"` e
157
168
  public APIs and framework/dynamic/reflection-sensitive candidates with their warnings. For repository
158
169
  maintenance or branch debt, pair it with `run_audit category=unused debt=all` (or add an immutable
159
170
  `base_ref` with `debt=new`) to cover unused files, exports and dependencies. Neither tool authorizes
160
- deletion: inspect `read_source`, `get_dependents`, exact search, manifests/configuration and tests first.
171
+ deletion: `STRONG_STATIC_EVIDENCE` means an exact zero-reference result, not permission to remove;
172
+ follow each candidate's `remainingChecks`, then inspect source/dependents/configuration and run tests.
161
173
 
162
174
  `graph_diff` accepts `base_ref` (`HEAD~1`, `main`, `origin/main`, or another local Git ref) for a
163
175
  fresh baseline comparison. Without it, the tool compares against `graph.prev.json` saved by the last
@@ -169,6 +181,14 @@ manifest, alias, ignore/config, non-JS/TS, or unsafe merge cases deliberately fa
169
181
  rebuild. The result says whether freshness was `none`, `incremental`, or `full`. Graph artifacts stay
170
182
  in the per-user cache and never need to be committed to Git.
171
183
 
184
+ `verified_change` is read-only by default. It may run only explicitly requested `package.json`
185
+ scripts whose names are allowlisted as test/check/verify scripts, and only when both
186
+ `run_tests:true` and `WEAVATRIX_ALLOW_TEST_RUNS=1` are present. It never accepts an arbitrary shell
187
+ command. Cross-repository API proof is used only when the active profile includes `crossrepo`;
188
+ without a configured architecture contract or complete evidence, the result is `UNKNOWN`, not a
189
+ cosmetic pass. Its data-flow section maps bounded JS/TS call arguments to callee parameters; it is
190
+ not CFG, value-propagation, or taint analysis.
191
+
172
192
  **retarget** *(included in `offline`; absent from `pinned`; every switch is an explicit local call)* —
173
193
  `open_repo`, `list_known_repos`; changes the active repository boundary
174
194
 
@@ -179,8 +199,11 @@ in the per-user cache and never need to be committed to Git.
179
199
  entry points. Resolved explicit seeds are exclusive by default; set `augment_seeds:true` only when
180
200
  question-derived seeds are also wanted. Broad bootstrap/tool-execution/routing questions now rank
181
201
  conventional executables and graph-declared production entry points ahead of site, documentation, benchmark
182
- and fixture matches. Broad ranking remains orientation evidence; use `seed_files` when the intended
183
- entry point is already known.
202
+ and fixture matches. Production-first classification also applies during traversal, so unrelated
203
+ tests/generated/docs/benchmarks do not leak back from a production seed; name a class in the
204
+ question or set `include_classified:true` to include it. Unreferenced unmatched constant/field leaves
205
+ are hidden unless `include_low_signal:true`. Broad ranking remains orientation evidence; use
206
+ `seed_files` when the intended entry point is already known.
184
207
 
185
208
  **advisories** *(network, explicit opt-in)* — `refresh_advisories`
186
209
 
@@ -193,6 +216,46 @@ disclosed instead of silently guessed; and the server **hot-reloads its watched
193
216
  modules and catalog** when those files change — other MCP helpers and analysis engines require a
194
217
  reconnect.
195
218
 
219
+ ### 0.2.7 verified-change workflow
220
+
221
+ - New `verified_change` composes task retrieval, exact symbol context, change impact, immutable Git
222
+ graph comparison, architecture/duplicate/API ratchets, and targeted-test evidence behind one
223
+ `PASS` / `BLOCKED` / `UNKNOWN` contract.
224
+ - Intent-expanded graph retrieval is combined with exact changed-symbol seeds. A bounded JS/TS
225
+ interprocedural layer records call arguments and their callee parameters without claiming CFG or
226
+ taint completeness.
227
+ - Test execution is a double opt-in and limited to existing test/check/verify package scripts;
228
+ default operation remains read-only.
229
+ - Malware scanning now sweeps installed npm package roots instead of package-manager caches or
230
+ hosted release snapshots. Static heuristic findings are capped at `high`, expose unverified
231
+ execution/origin/lockfile/exposure state, and no longer prescribe secret rotation unless execution
232
+ or credential exposure has actually been confirmed.
233
+ - `query_graph` and `verified_change` task retrieval now enforce production-first classification
234
+ through traversal/selection while retaining exact changed or pinned non-product symbols; query
235
+ output also suppresses unmatched constant/field leaves. `hot_path_review` defaults to a focused
236
+ 85-point queue, while `min_score:0` remains the explicit full-diagnostic mode.
237
+ - Dead-code candidates now carry evidence tiers and remaining checks; dependency findings carry
238
+ per-finding manifest plus indexed-source/script/config verification without authorizing removal.
239
+ - `npm run benchmark:agent` reports routing success, false positives, token estimate and latency.
240
+ Codebase Memory and Serena results stay explicitly `MISSING` until independently collected data is
241
+ supplied under the [blind agent-change protocol](docs/agent-task-benchmark.md);
242
+ `benchmark:agent:release` fails closed without same-task change success, FP, token, and time results.
243
+
244
+ Full patch notes: [docs/releases/v0.2.7.md](docs/releases/v0.2.7.md).
245
+
246
+ ### 0.2.6 compact-context and TypeScript identity patch
247
+
248
+ - New `context_bundle` turns a symbol into a bounded workset: definition, grouped inbound/outbound
249
+ containers, reference evidence, exact re-export locations and a small number of source excerpts.
250
+ - Re-export records retain their concrete file, line, alias, specifier, type-only flag and resolved
251
+ origin. Barrel propagation through `export *` stays exact and private declarations are not exposed.
252
+ - TypeScript interfaces and type aliases are first-class type-space nodes. Same-named runtime values
253
+ keep separate identities, while classes and enums are explicitly marked as inhabiting both spaces.
254
+ - Graph schema v5 and freshness gates rebuild older caches before these precision-sensitive results
255
+ are used. The changes remain local-only and add no runtime dependency.
256
+
257
+ Full patch notes: [docs/releases/v0.2.6.md](docs/releases/v0.2.6.md).
258
+
196
259
  ### 0.2.5 exact-symbol and graph-fidelity patch
197
260
 
198
261
  - New `inspect_symbol` spends a bounded LSP query on one requested TS/JS declaration, groups exact
@@ -450,7 +513,7 @@ vulnerability findings. This is where each capability comes from and how it is c
450
513
  | Capability alert | Why it exists | Activation and boundary |
451
514
  |---|---|---|
452
515
  | Network access | `refresh_advisories` sends pinned package names and versions to OSV; `pull_architecture_contract` sends an opaque repository UUID and receives an owner-approved contract; `sync_graph` sends a normalized repository label plus an allowlisted graph/evidence payload. Evidence is derived locally from source and manifests, but source bodies, snippets, absolute paths, environment values, credentials and Git remotes are excluded | `offline` and `pinned` expose no network tools. `osv` exposes only advisory refresh. `hosted`/`full` expose all three; every request still requires an explicit tool call, and hosted calls require `WEAVATRIX_SYNC_URL` |
453
- | Shell access | Local `git` powers staleness/change impact; `rg` accelerates search; the bundled TLS/tsserver process supplies bounded JS/TS semantic evidence; timed-out Windows child trees may be terminated | Used only by the corresponding local operation. The semantic provider is package-pinned, disables automatic type acquisition, and never invokes a repository binary, script, installer or `npx` |
516
+ | Shell access | Local `git` powers staleness/change impact; `rg` accelerates search; the bundled TLS/tsserver process supplies bounded JS/TS semantic evidence; timed-out Windows child trees may be terminated; `verified_change` can optionally run an existing package test/check/verify script | The semantic provider never invokes repository code. Test execution separately requires `run_tests:true` plus `WEAVATRIX_ALLOW_TEST_RUNS=1`, rejects arbitrary commands and shell-sensitive arguments, and should be enabled only for trusted repository scripts |
454
517
  | Debug / dynamic loading | Cache-busted `import()` hot-reloads watched MCP tool entry modules; `createRequire` loads package metadata and parser dependencies | Loads files from the installed package; no `eval` |
455
518
  | Environment access | Reads `WEAVATRIX_*` configuration; ordinary local helpers inherit a credential-stripped environment, while TLS/tsserver receives only allowlisted OS/temp/locale values and a constrained executable path | `WEAVATRIX_SYNC_TOKEN` is removed from every child-process and worker environment. TLS/tsserver also receives no registry/proxy/cloud credentials or `NODE_OPTIONS` |
456
519
  | Filesystem access | Reads the active repository, graph, lockfiles and coverage reports; writes derived graphs and advisory/architecture caches | Realpath containment blocks traversal and symlink/junction escapes. The `pinned` profile removes `open_repo`; the default `offline` profile permits only explicit local switching. The optional malware dependency scan may inspect installed dependency caches such as GOPATH |
package/SECURITY.md CHANGED
@@ -28,6 +28,11 @@ dependency caches such as GOPATH. `offline` permits repository switching only th
28
28
  local `open_repo` call; select `pinned` to remove that tool, the global repository listing, and
29
29
  cross-repository API tracing, holding a hard startup-repository boundary.
30
30
 
31
+ Installed-package malware pattern matching is static heuristic evidence. It cannot confirm execution,
32
+ package compromise, or credential exposure and therefore cannot emit `critical`; heuristic severity
33
+ is capped at `high` with explicit `NOT_VERIFIED` runtime/origin/lockfile/exposure fields. Independently
34
+ confirmed malicious-package advisories remain separate vulnerability evidence and may be critical.
35
+
31
36
  The JS/TS precision overlay is enabled by default for new graphs and runs the package-pinned `typescript-language-server` and
32
37
  TypeScript runtime as local child processes. It never resolves a repository executable, invokes
33
38
  `npx`, runs package scripts, or installs dependencies. Automatic type acquisition is disabled, and
@@ -46,6 +51,15 @@ bounded graph-work drain, and closes or tree-terminates TLS/tsserver before exit
46
51
  Set `WEAVATRIX_PRECISION=off` before server startup (or select `off` in the MCPB semantic-precision
47
52
  setting) to keep new graphs parser-only from their first build.
48
53
 
54
+ `verified_change` is read-only by default. Its optional targeted-test step can execute only an
55
+ existing `package.json` script whose name matches the bounded test/check/verify allowlist. The call
56
+ must set `run_tests:true` and the server operator must separately set
57
+ `WEAVATRIX_ALLOW_TEST_RUNS=1`; otherwise no repository script runs. The tool accepts no command or
58
+ shell string, caps scripts/arguments/time, rejects shell-sensitive argument characters, and launches
59
+ the selected package-manager script with the credential-stripped child environment. Repository test
60
+ scripts are repository-controlled code and may have arbitrary side effects, so enable this flag only
61
+ for repositories and scripts you trust.
62
+
49
63
  Network capabilities are split by purpose. `osv` adds only `refresh_advisories`, which sends pinned
50
64
  package names and versions to OSV.dev when called. `hosted` / `full` also expose `sync_graph` and
51
65
  `pull_architecture_contract`; both require a user-configured endpoint, and contract pull requires
@@ -0,0 +1,58 @@
1
+ # Independent agent-change benchmark
2
+
3
+ The 0.2.7 release includes two deliberately separate measurements.
4
+
5
+ `npm run benchmark:agent` is a deterministic local routing microbenchmark. It measures whether the
6
+ task retriever selects the expected symbol, plus false-positive selections, estimated output tokens,
7
+ and local latency. It is a regression test, not evidence that an autonomous code change succeeded.
8
+
9
+ The release comparison is an independently executed, blind agent-change benchmark. The evaluator
10
+ must run the same pinned tasks and repository commits three times: with Weavatrix, with Codebase
11
+ Memory, and with Serena. The product author must not adjudicate or repair a run while it is active.
12
+
13
+ ## Required task protocol
14
+
15
+ Each task must provide a repository commit, natural-language request, allowed commands, timeout, and
16
+ machine-verifiable acceptance tests. Keep task prompts and acceptance tests identical across systems;
17
+ randomize system order. Record the entire agent input/output token count and wall-clock duration.
18
+
19
+ A run is successful only when the requested behavior is implemented, all acceptance tests pass, and
20
+ the final diff stays inside the task scope. A false positive is an evidenced warning, impacted symbol,
21
+ or required edit that an evaluator confirms is unrelated to the task. Record a count, including zero;
22
+ do not silently discard noisy output.
23
+
24
+ Use a mix of at least:
25
+
26
+ - behavior changes with indirect callers;
27
+ - API route/client changes;
28
+ - refactors with a duplicate or architecture regression trap;
29
+ - changes whose nearest relevant test is not in the edited file;
30
+ - one negative task where the correct action is to refuse or return unknown.
31
+
32
+ ## Result contract
33
+
34
+ Save the blind evaluator output as JSON:
35
+
36
+ ```json
37
+ {
38
+ "schemaVersion": "weavatrix.agent-change-results.v1",
39
+ "evaluator": "independent organization or person",
40
+ "systems": {
41
+ "weavatrix": {"runs": [{"taskId": "change-auth", "success": true, "falsePositives": 0, "tokens": 1234, "durationMs": 4567}]},
42
+ "codebase-memory": {"runs": [{"taskId": "change-auth", "success": true, "falsePositives": 1, "tokens": 1400, "durationMs": 5000}]},
43
+ "serena": {"runs": [{"taskId": "change-auth", "success": false, "falsePositives": 2, "tokens": 1600, "durationMs": 7000}]}
44
+ }
45
+ }
46
+ ```
47
+
48
+ Every system must contain the same unique task IDs. Every run must report success, false positives,
49
+ tokens, and duration. Then run:
50
+
51
+ ```sh
52
+ npm run benchmark:agent -- --independent-results path/to/results.json
53
+ npm run benchmark:agent:release -- --independent-results path/to/results.json
54
+ ```
55
+
56
+ The first command reports change-success rate, false positives per task, median tokens, and median
57
+ duration for all three systems. The release command fails unless the independent comparison is
58
+ complete and valid. Missing or malformed competitor data is `INCOMPLETE`, never a green comparison.
@@ -0,0 +1,93 @@
1
+ # Weavatrix 0.2.7
2
+
3
+ ## Proof-carrying changes
4
+
5
+ `verified_change` is a new high-level MCP workflow for planning and verifying a code change. It
6
+ accepts a natural-language task, an optional unified diff or changed-file hints, and an immutable Git
7
+ baseline. Its stable result is one of:
8
+
9
+ - `PASS`: every applicable enabled proof completed and no ratchet failed;
10
+ - `BLOCKED`: a test failed, a new duplicate intersects changed code, a runtime cycle appeared, an
11
+ architecture ratchet failed, or bounded API evidence found an HTTP method mismatch;
12
+ - `UNKNOWN`: required evidence is missing, partial, disabled, or not configured.
13
+
14
+ The structured result contains intent-expanded task retrieval combined with exact changed-symbol
15
+ seeds, up to five compact LSP/graph edit contexts, symbol-aware blast radius, affected-test paths,
16
+ bounded JS/TS call-argument-to-parameter evidence, Git graph drift, architecture verification, a
17
+ new-duplicate baseline comparison, and optional cross-repository HTTP contract evidence.
18
+
19
+ The workflow respects the existing capability boundary. Exact source/LSP context requires `source`,
20
+ duplicate comparison and architecture verification require `health`, and API tracing requires
21
+ `crossrepo`. A narrow custom profile is never silently bypassed.
22
+
23
+ ## Safe targeted tests
24
+
25
+ The workflow does not accept shell commands. It can execute at most five existing `package.json`
26
+ scripts whose names match the test/check/verify allowlist. Execution requires both
27
+ `run_tests:true` on the tool call and `WEAVATRIX_ALLOW_TEST_RUNS=1` in the server environment. The
28
+ default remains read-only and returns the test plan or `UNKNOWN` when runtime evidence was requested
29
+ but not authorized.
30
+
31
+ ## Malware scanner correction
32
+
33
+ The npm content sweep now starts at actual installed package roots instead of the entire
34
+ `node_modules` container. Hidden package-manager caches and hosted release snapshots are therefore
35
+ not attributed as dependencies and cannot make Weavatrix flag its own malware-signature source.
36
+
37
+ Heuristic remediation no longer says to treat a package as compromised or rotate all reachable
38
+ secrets. It asks the operator to quarantine and inspect the package, verify origin and lockfile
39
+ integrity, and rotate secrets only after execution or credential exposure is confirmed.
40
+
41
+ Static heuristic findings now have a hard `high` severity ceiling. They expose
42
+ `confirmedExecution:false` plus explicit runtime/origin/lockfile/exposure verification states;
43
+ `critical` is reserved for independently confirmed malicious advisories or runtime evidence.
44
+
45
+ Package-lock install-script drift now compares only `preinstall`, `install`, and `postinstall`.
46
+ Development/publication hooks such as `prepare` no longer create false high-severity drift findings.
47
+
48
+ ## Signal-quality corrections
49
+
50
+ - `query_graph` applies production-first path classification to the traversal, not only seed
51
+ selection. Tests, generated code, fixtures, docs, benchmarks and excluded paths require an
52
+ explicit question class or `include_classified:true`. Exact non-product seed files remain usable.
53
+ - `verified_change` task retrieval uses the same production-first policy while always retaining
54
+ exact changed symbols. The bundled routing microbenchmark moved from one test-symbol false
55
+ positive (`0.25`) to zero (`0`) without losing any of its three expected production targets.
56
+ - Unreferenced constant/field leaves that do not match the query are hidden by default; use
57
+ `include_low_signal:true` for diagnostic expansion.
58
+ - `hot_path_review` now defaults to a focused score gate of 85 plus a narrow strong-local fallback.
59
+ On the 0.2.7 source tree this reduced the default queue from 717 diagnostic candidates to 116 of
60
+ 1,043 analyzed symbols; `min_score:0` restores the full diagnostic set.
61
+ - `find_dead_code` adds `STRONG_STATIC_EVIDENCE`, `BOUNDED_STATIC_EVIDENCE`, and
62
+ `HIGH_UNCERTAINTY` tiers, structured completed/remaining checks, and `autoDelete:false` on every
63
+ candidate. Strong static evidence means exact zero LSP references, not permission to delete.
64
+ - npm unused/missing/duplicate dependency findings now carry manifest section, indexed-source,
65
+ script/config, package-scope and unresolved dynamic/plugin verification state. Unused dependencies
66
+ remain `REVIEW_REQUIRED`; missing dependencies retain the exact importing-file evidence.
67
+
68
+ ## Benchmark contract
69
+
70
+ `npm run benchmark:agent` runs a deterministic task-to-symbol routing microbenchmark and reports task
71
+ success, false-positive rate, estimated output tokens, and duration. This is intentionally labelled
72
+ as a routing microbenchmark, not an autonomous end-to-end change benchmark.
73
+
74
+ Independent Codebase Memory and Serena results may be supplied with
75
+ `--independent-results <json>` using the blind evaluator contract in
76
+ [`docs/agent-task-benchmark.md`](../agent-task-benchmark.md). Without same-task results for all three
77
+ systems, comparison status is `INCOMPLETE`; `npm run benchmark:agent:release` fails closed. The
78
+ repository does not manufacture competitor scores.
79
+
80
+ ## Scope and limitations
81
+
82
+ - The interprocedural evidence maps call arguments to declared callee parameters for bounded JS/TS
83
+ graph call edges. It is not a CFG, value-propagation engine, or taint analysis.
84
+ - An absent architecture contract produces `UNKNOWN`; provisional budgets are guidance, not policy.
85
+ - A static path to a test is not proof that the test executed. A `PASS` that depends on runtime
86
+ behavior requires explicitly authorized test execution.
87
+ - API proof is optional and registry-scoped. No client match is `UNKNOWN`, never proof of no external
88
+ consumers.
89
+
90
+ ## Tool catalog
91
+
92
+ 0.2.7 exposes 36 MCP tools. The default offline profile exposes 33; pinned exposes 30. Network tools
93
+ remain absent from the offline and pinned profiles.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "weavatrix",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "type": "module",
5
5
  "description": "Weavatrix — code graph & blast-radius MCP server for AI coding agents: change impact, transitive dependents, health audit, duplicate detection, and coverage mapping over any local repository.",
6
6
  "author": "Sergii Ziborov <sergii.ziborov@gmail.com>",
@@ -24,6 +24,9 @@
24
24
  "bin",
25
25
  "src",
26
26
  "skill",
27
+ "scripts/run-agent-task-benchmark.mjs",
28
+ "docs/agent-task-benchmark.md",
29
+ "docs/releases/v0.2.7.md",
27
30
  "README.md",
28
31
  "SECURITY.md",
29
32
  "LICENSE"
@@ -34,6 +37,8 @@
34
37
  "benchmark:quick": "node scripts/run-benchmark.mjs --quick",
35
38
  "benchmark:real": "node scripts/run-real-benchmark.mjs",
36
39
  "benchmark:real:release": "node scripts/run-real-benchmark.mjs --require-all",
40
+ "benchmark:agent": "node scripts/run-agent-task-benchmark.mjs",
41
+ "benchmark:agent:release": "node scripts/run-agent-task-benchmark.mjs --require-independent",
37
42
  "build:mcpb": "node scripts/build-mcpb.mjs",
38
43
  "verify:release": "node scripts/verify-release.mjs"
39
44
  },
@@ -0,0 +1,119 @@
1
+ import {readFileSync} from 'node:fs'
2
+ import {pathToFileURL} from 'node:url'
3
+ import {performance} from 'node:perf_hooks'
4
+ import {retrieveTaskContext} from '../src/analysis/task-retrieval.js'
5
+
6
+ function graphFixture() {
7
+ const nodes = [
8
+ {id: 'src/auth/session.js#validateSession@10', label: 'validateSession()', source_file: 'src/auth/session.js', symbol_kind: 'function'},
9
+ {id: 'src/http/router.js#registerRoutes@20', label: 'registerRoutes()', source_file: 'src/http/router.js', symbol_kind: 'function'},
10
+ {id: 'src/cache/store.js#readCache@5', label: 'readCache()', source_file: 'src/cache/store.js', symbol_kind: 'function'},
11
+ {id: 'src/auth/session.test.js#sessionTest@3', label: 'sessionTest()', source_file: 'src/auth/session.test.js', symbol_kind: 'function'},
12
+ ]
13
+ return {nodes, byId: new Map(nodes.map((node) => [node.id, node])), out: new Map(), inn: new Map()}
14
+ }
15
+
16
+ const CASES = [
17
+ {task: 'validate authentication session', expected: 'src/auth/session.js#validateSession@10'},
18
+ {task: 'register HTTP API routes', expected: 'src/http/router.js#registerRoutes@20'},
19
+ {task: 'read values from cache store', expected: 'src/cache/store.js#readCache@5'},
20
+ ]
21
+
22
+ function competitorTemplate(name) {
23
+ return {name, status: 'MISSING', reason: 'supply same-task blind evaluator output with --independent-results <json>'}
24
+ }
25
+
26
+ function readCompetitors(path) {
27
+ if (!path) return {}
28
+ const parsed = JSON.parse(readFileSync(path, 'utf8'))
29
+ return parsed?.competitors && typeof parsed.competitors === 'object' ? parsed.competitors : {}
30
+ }
31
+
32
+ const SYSTEMS = ['weavatrix', 'codebase-memory', 'serena']
33
+ const median = (values) => {
34
+ const sorted = values.slice().sort((left, right) => left - right)
35
+ const middle = Math.floor(sorted.length / 2)
36
+ return sorted.length % 2 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2
37
+ }
38
+
39
+ function independentComparison(path) {
40
+ if (!path) return {status: 'MISSING', reason: 'supply blind evaluator output with --independent-results <json>'}
41
+ try {
42
+ const input = JSON.parse(readFileSync(path, 'utf8'))
43
+ if (input?.schemaVersion !== 'weavatrix.agent-change-results.v1') throw new Error('unsupported schemaVersion')
44
+ const taskSets = []
45
+ const metrics = {}
46
+ for (const name of SYSTEMS) {
47
+ const runs = input.systems?.[name]?.runs
48
+ if (!Array.isArray(runs) || !runs.length) throw new Error(`${name} has no runs`)
49
+ const ids = runs.map((run) => String(run.taskId || '')).sort()
50
+ if (ids.some((id) => !id) || new Set(ids).size !== ids.length) throw new Error(`${name} task IDs are missing or duplicated`)
51
+ for (const run of runs) {
52
+ if (typeof run.success !== 'boolean' || !Number.isFinite(run.falsePositives) || !Number.isFinite(run.tokens) || !Number.isFinite(run.durationMs)) {
53
+ throw new Error(`${name}/${run.taskId || '?'} has incomplete metrics`)
54
+ }
55
+ }
56
+ taskSets.push(ids.join('\0'))
57
+ metrics[name] = {
58
+ tasks: runs.length, changeSuccessRate: runs.filter((run) => run.success).length / runs.length,
59
+ falsePositiveRate: runs.reduce((sum, run) => sum + Math.max(0, run.falsePositives), 0) / runs.length,
60
+ medianTokens: median(runs.map((run) => run.tokens)), medianDurationMs: median(runs.map((run) => run.durationMs)),
61
+ }
62
+ }
63
+ if (new Set(taskSets).size !== 1) throw new Error('systems were not evaluated on the same task IDs')
64
+ return {status: 'COMPLETE', evaluator: String(input.evaluator || 'unspecified'), metrics}
65
+ } catch (error) {
66
+ return {status: 'INVALID', reason: error instanceof Error ? error.message : String(error)}
67
+ }
68
+ }
69
+
70
+ export function runAgentTaskBenchmark({competitorResults, independentResults} = {}) {
71
+ const graph = graphFixture()
72
+ const started = performance.now()
73
+ const cases = CASES.map((item) => {
74
+ const semanticSeeds = graph.nodes.filter((node) => item.task.toLowerCase().split(/\W+/).some((word) => word.length > 3 && String(node.id).toLowerCase().includes(word)))
75
+ const result = retrieveTaskContext(graph, {task: item.task, semanticSeeds, maxSymbols: 2})
76
+ const selected = result.selected.map((entry) => entry.id)
77
+ return {task: item.task, expected: item.expected, selected, success: selected[0] === item.expected, falsePositives: selected.filter((id) => id !== item.expected).length}
78
+ })
79
+ const durationMs = performance.now() - started
80
+ const output = JSON.stringify(cases)
81
+ const imported = readCompetitors(competitorResults)
82
+ const independent = independentComparison(independentResults)
83
+ const competitors = independent.status === 'COMPLETE' ? {
84
+ 'codebase-memory': {name: 'codebase-memory', status: 'COMPLETE', metrics: independent.metrics['codebase-memory']},
85
+ serena: {name: 'serena', status: 'COMPLETE', metrics: independent.metrics.serena},
86
+ } : {
87
+ 'codebase-memory': imported['codebase-memory'] || competitorTemplate('codebase-memory'),
88
+ serena: imported.serena || competitorTemplate('serena'),
89
+ }
90
+ return {
91
+ schemaVersion: 'weavatrix.agent-task-benchmark.v1',
92
+ scope: 'deterministic task-to-symbol routing microbenchmark; not an end-to-end autonomous change benchmark',
93
+ weavatrix: {
94
+ status: cases.every((item) => item.success) ? 'PASS' : 'FAIL', cases,
95
+ metrics: {
96
+ taskSuccessRate: cases.filter((item) => item.success).length / cases.length,
97
+ falsePositiveRate: cases.reduce((sum, item) => sum + item.falsePositives, 0) / Math.max(1, cases.reduce((sum, item) => sum + item.selected.length, 0)),
98
+ estimatedOutputTokens: Math.ceil(Buffer.byteLength(output) / 4), durationMs: Number(durationMs.toFixed(3)),
99
+ },
100
+ },
101
+ competitors, independentComparison: independent,
102
+ comparisonStatus: independent.status === 'COMPLETE' ? 'COMPLETE' : 'INCOMPLETE',
103
+ }
104
+ }
105
+
106
+ function argument(name) {
107
+ const index = process.argv.indexOf(name)
108
+ return index >= 0 ? process.argv[index + 1] : null
109
+ }
110
+
111
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
112
+ const report = runAgentTaskBenchmark({
113
+ competitorResults: argument('--competitor-results'),
114
+ independentResults: argument('--independent-results'),
115
+ })
116
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`)
117
+ if ((process.argv.includes('--require-independent') || process.argv.includes('--require-competitors')) && report.comparisonStatus !== 'COMPLETE') process.exitCode = 1
118
+ if (report.weavatrix.status !== 'PASS') process.exitCode = 1
119
+ }
package/skill/SKILL.md CHANGED
@@ -38,15 +38,20 @@ Start from the task, not from the complete tool list:
38
38
  - **Review a branch/diff**: `change_impact` for changed-file blast radius; add
39
39
  `graph_diff base_ref=<merge-base>` for structural drift, then drill into exact candidates with
40
40
  `get_dependents` and `read_source`.
41
+ - **Plan or verify a serious change**: call `verified_change phase=plan` with the natural-language
42
+ task and current diff/files. After editing, call it again with `phase=verify` and an immutable
43
+ `base_ref`. Treat `UNKNOWN` as missing proof, not success; inspect its blockers and unknowns instead
44
+ of manually orchestrating the lower-level graph, context, impact, ratchet, and test tools.
41
45
  - **Trace an API across repositories**: `list_known_repos` -> `trace_api_contract` with an explicit
42
46
  backend and client list; inspect each `graphReconciliation.buildMode` before using the verdict.
43
- - **Inspect exact symbol references**: start with `graph_stats`, then call `inspect_symbol` with an
44
- exact node ID (or an unambiguous label). It spends a bounded point query beyond the broad overlay
45
- cap and returns occurrence containers plus source context. Use `get_dependents` for transitive
47
+ - **Inspect exact symbol references**: start with `graph_stats`, then call `context_bundle` with an
48
+ exact node ID (or an unambiguous label) for a compact definition, grouped relations, exact
49
+ re-export sites and source workset. Use `inspect_symbol` when the raw bounded point query is needed;
50
+ it spends evidence beyond the broad overlay cap. Use `get_dependents` for transitive
46
51
  graph impact and `find_dead_code` for bounded zero-reference
47
52
  candidates. Treat `PARTIAL`, `UNAVAILABLE`, `OFF`, or zero exact edges as incomplete evidence, not
48
53
  a compiler-exact result; `OFF` means the caller explicitly selected static-only mode. Java and Rust
49
- providers are not bundled in 0.2.5, so their edges never become `EXACT_LSP` even when a mixed
54
+ providers are not bundled in 0.2.7, so their edges never become `EXACT_LSP` even when a mixed
50
55
  repository has a complete TypeScript/JavaScript overlay.
51
56
  - **Explore an architectural question**: use broad `query_graph` when entry points are unknown; its
52
57
  bootstrap/tool-execution ranking prefers production executables over docs, sites and fixtures.
@@ -86,16 +91,20 @@ Start from the task, not from the complete tool list:
86
91
  `.weavatrix.json` `classify.product`, for example `{"classify":{"product":["benchmarks/core/**"]}}`.
87
92
  - **Architectural queries**: broad bootstrap/tool-execution/routing questions rank conventional
88
93
  production and graph-declared entry points ahead of classified docs, sites, benchmarks and
89
- fixtures. Inspect the returned seeds before trusting the traversal. Pass exact repository-relative
90
- `seed_files` to `query_graph` when the intended entry points are already known.
94
+ fixtures. Production-first classification also applies during traversal. A class term in the
95
+ question enables only that class; `include_classified=true` enables all classified paths. Exact
96
+ non-product `seed_files` remain pinned. Unmatched unreferenced constant/field leaves are suppressed
97
+ unless `include_low_signal=true`. Inspect the returned seeds/policy before trusting the traversal.
91
98
  - **Coverage**: `coverage_map` reads an existing report. `unavailable` means no supported report was
92
99
  found, not 0% coverage; do not rank testing risk from that state.
93
100
  - **Local hot paths**: `hot_path_review` ranks parser-derived local syntax cost and reports graph
94
101
  fan-in/fan-out plus test evidence separately. `actualCoverage: NOT_AVAILABLE` is not zero coverage,
95
- and the score is not profiler data or an interprocedural Big-O proof. Inspect its line evidence and
96
- measure runtime before scheduling a performance rewrite.
102
+ and the score is not profiler data or an interprocedural Big-O proof. The default queue uses
103
+ `min_score=85` plus a narrow strong-local fallback; use `min_score=0` only for full diagnostics.
104
+ Inspect its line evidence and measure runtime before scheduling a performance rewrite.
97
105
  - **Audit completeness**: read `dependencyReport.status` plus its unused/missing counts, then
98
- `checks.osv.status` and `checks.malware.status`. A dependency result from a partial graph is not a
106
+ each npm finding's `verification` (manifest, indexed source, scripts/config, unresolved dynamic
107
+ usage), then `checks.osv.status` and `checks.malware.status`. A dependency result from a partial graph is not a
99
108
  repository-wide clean zero. For OSV, `OK` is
100
109
  complete for the recorded dependency fingerprint; `PARTIAL` is incomplete or stale,
101
110
  `NOT_CHECKED` has no repository-specific result, and `ERROR` means the local check failed. Treat
@@ -120,8 +129,18 @@ Start from the task, not from the complete tool list:
120
129
  `offline`, absent from `pinned`, and available in a custom registration only when `retarget` is
121
130
  named. Concurrent non-mutating calls retain the graph/root snapshot with which they started.
122
131
 
132
+ - **Test execution is separately authorized**: `verified_change` only executes named
133
+ `package.json` test/check/verify scripts when the call has `run_tests:true` and the server was
134
+ started with `WEAVATRIX_ALLOW_TEST_RUNS=1`. It never accepts arbitrary shell commands. Without
135
+ both gates it returns a plan or `UNKNOWN` evidence state.
136
+
123
137
  ## Recipes
124
138
 
139
+ - **Proof-carrying refactor**: `verified_change task="..." phase=plan base_ref=<merge-base>` -> edit ->
140
+ `verified_change task="..." phase=verify base_ref=<same-ref> tests=[{"script":"test","args":[...]}]`.
141
+ Add `run_tests:true` only when test execution was authorized. `BLOCKED` means an evidenced ratchet
142
+ or test failure; `UNKNOWN` means at least one required proof is incomplete.
143
+
125
144
  - **Orient in the configured repo**: `module_map` → `list_communities` → `god_nodes`. Hub ranking
126
145
  is production-only by default; use `include_classified:true` only when tests/generated/build
127
146
  surfaces are deliberately part of the question.
@@ -151,7 +170,9 @@ Start from the task, not from the complete tool list:
151
170
  `run_audit category=unused debt=all` (or `base_ref=<merge-base> debt=new`) for unused exports and
152
171
  dependencies. Before deletion, run `read_source`, `get_dependents`, exact `search_code`, inspect
153
172
  framework discovery/annotations, package scripts, CLI/Docker/manifests, generated consumers and
154
- test-only use, then run the repository tests. `REVIEW_REQUIRED` and `autoDelete:false` are hard
173
+ test-only use, then run the repository tests. Use `evidenceTier` and `remainingChecks` to order the
174
+ review; even `STRONG_STATIC_EVIDENCE` is exact zero-reference evidence, not deletion permission.
175
+ `REVIEW_REQUIRED` and `autoDelete:false` are hard
155
176
  safety semantics, not boilerplate. Use `.weavatrix-deps.json` entrypoints/nonRuntimeRoots only for
156
177
  verified conventions.
157
178
  - **API inventory**: `list_endpoints` (including Next.js App Router, Rust axum and actix-web).