@henols/vice-mcp 0.2.5 → 1.1.0
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/THIRD-PARTY-NOTICES.md +6 -4
- package/anno-cli.ts +1 -1
- package/anno-store.ts +1 -1
- package/backend-detect.mts +143 -19
- package/build.ts +42 -1
- package/evid-ingest.ts +7 -3
- package/host-tool-client.ts +7 -3
- package/package.json +3 -1
- package/prerequisites.json +416 -0
- package/prg-image.ts +1 -1
- package/repo-root.ts +19 -8
- package/resources/backend-detect.mjs +90 -18
- package/resources/broker-launch.mjs +11 -2
- package/resources/ghidra-project.mjs +2 -2
- package/resources/host-tool.mjs +306 -82
- package/resources/prerequisites.json +416 -0
- package/resources/tool-location.mjs +750 -0
- package/resources/vice-broker.mjs +85 -12
- package/stock-checkpoints.ts +2 -2
- package/stock-connect.ts +12 -10
- package/stock-execution.ts +4 -2
- package/stock-memory-search.ts +3 -2
- package/stock-protocol.ts +60 -28
- package/stock-registers.ts +3 -1
- package/text-protocol.ts +6 -3
- package/textmon-profile.ts +8 -3
- package/version.ts +34 -246
- package/vice-proxy.ts +23 -23
package/version.ts
CHANGED
|
@@ -1,248 +1,49 @@
|
|
|
1
|
-
//
|
|
2
|
-
// algorithm (D-5). Before this file existed the repo carried FOUR
|
|
3
|
-
// hand-maintained version strings and none of them were true:
|
|
4
|
-
// `src/mcp/vice/package.json` and `installer/package.json` were stale
|
|
5
|
-
// placeholders CI never touched between releases, `.claude-plugin/plugin.json`
|
|
6
|
-
// was bumped by NO automation at all, and `vice-proxy.ts`'s own
|
|
7
|
-
// `PROXY_VERSION` literal -- advertised to every MCP client over
|
|
8
|
-
// `initialize` -- sat twelve patches behind npm's actual `latest`. This file
|
|
9
|
-
// exists so there is exactly one place that (a) parses the repo-root
|
|
10
|
-
// `VERSION` template, (b) resolves it against a published version per D-2's
|
|
11
|
-
// four rules, and (c) answers "what version am I, right now, at runtime"
|
|
12
|
-
// (D-4) -- every other consumer (the CLI in `scripts/version.mjs`,
|
|
13
|
-
// `vice-proxy.ts`'s `PROXY_VERSION`, CI's `release-on-merge` job) calls INTO
|
|
14
|
-
// this module rather than re-deriving any part of the algorithm locally.
|
|
1
|
+
// Answers one question: what version is this server, right now, at runtime.
|
|
15
2
|
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
// `
|
|
19
|
-
// `
|
|
20
|
-
//
|
|
21
|
-
// regression.
|
|
3
|
+
// WHY THIS FILE EXISTS: `vice-proxy.ts` advertises a version to every MCP
|
|
4
|
+
// client over `initialize`. That string used to be a hand-maintained
|
|
5
|
+
// `PROXY_VERSION` literal and it sat twelve patches behind npm's actual
|
|
6
|
+
// `latest`, because nothing bumped it. One function now derives it from the
|
|
7
|
+
// package.json that ships beside the code, so it cannot drift again.
|
|
22
8
|
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* manifests); never hand-edited. */
|
|
9
|
+
// This file once also carried a bespoke version-RESOLUTION algorithm (a
|
|
10
|
+
// `VERSION` template parsed against the published version under four named
|
|
11
|
+
// rules) used only by the auto-patch-on-merge release job. That job and its
|
|
12
|
+
// template are gone: the git tag is the version now, and the publish workflow
|
|
13
|
+
// reads it straight from the ref. Do not reintroduce a second version
|
|
14
|
+
// algorithm here -- if you need to know what a release will be called, look
|
|
15
|
+
// at the tag.
|
|
16
|
+
//
|
|
17
|
+
// Do NOT import `repo-root.ts` from this file, and do not reintroduce a
|
|
18
|
+
// `repoRoot` option. That module's `repoRoot()` fires
|
|
19
|
+
// `ensureResourcesInstalled()` at module-load time, and asking what version
|
|
20
|
+
// something is must never deploy host launcher resources as a side effect.
|
|
21
|
+
// The option existed to locate a `VERSION` template that no longer exists.
|
|
22
|
+
import { readFileSync } from "node:fs";
|
|
23
|
+
|
|
24
|
+
/** The placeholder both publishable `package.json` `.version` fields carry in
|
|
25
|
+
* the working tree. Valid semver -- `npm pack` accepts it -- but unmistakably
|
|
26
|
+
* not a release. `npm version` overwrites it at publish time from the git tag;
|
|
27
|
+
* never hand-edited. */
|
|
43
28
|
export const DEV_PLACEHOLDER = "0.0.0-dev";
|
|
44
29
|
|
|
45
|
-
export type ResolveRule = "pinned" | "no-published" | "prefix-differs" | "prefix-matches";
|
|
46
|
-
|
|
47
|
-
export interface ResolveResult {
|
|
48
|
-
version: string;
|
|
49
|
-
rule: ResolveRule;
|
|
50
|
-
template: string;
|
|
51
|
-
published: string | null;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Leading zeros are rejected (bare "0" is the only zero-value exception) --
|
|
55
|
-
// SemVer 2.0.0 SS2 forbids leading-zero numeric identifiers, and this
|
|
56
|
-
// component is echoed VERBATIM into the resolved string for literal (non-`-`)
|
|
57
|
-
// slots, so "1.00.0" must be rejected here rather than surfacing later as an
|
|
58
|
-
// npm-publish-time failure (MED-2).
|
|
59
|
-
const TEMPLATE_COMPONENT = /^(0|[1-9]\d*|-)$/;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Parse a version TEMPLATE (not a resolved version) into its three
|
|
63
|
-
* dot-separated components. Each component must be either a non-negative
|
|
64
|
-
* integer literal or the literal string `-` (an auto-managed slot, D-2).
|
|
65
|
-
* Throws on anything else: wrong component count, a non-numeric/non-`-`
|
|
66
|
-
* component, a blank/whitespace-only string, OR a literal component
|
|
67
|
-
* appearing after a `-` component (MED-1) -- D-2's "literal prefix" wording,
|
|
68
|
-
* and every row of CONTEXT.md's worked-example table, only ever describe
|
|
69
|
-
* dashes trailing literals, never leading or interleaved. A shape like
|
|
70
|
-
* "-.2.3" or "0.-.2" has no defined resolution semantics and must be
|
|
71
|
-
* rejected at the one seam that owns validating the hand-edited VERSION
|
|
72
|
-
* file, not silently resolved into something CONTEXT.md never specified.
|
|
73
|
-
*/
|
|
74
|
-
export function parseTemplate(raw: string): string[] {
|
|
75
|
-
const trimmed = raw.trim();
|
|
76
|
-
if (trimmed === "") {
|
|
77
|
-
throw new Error(`version.ts: malformed template ${JSON.stringify(raw)} -- empty`);
|
|
78
|
-
}
|
|
79
|
-
const parts = trimmed.split(".");
|
|
80
|
-
if (parts.length !== 3) {
|
|
81
|
-
throw new Error(
|
|
82
|
-
`version.ts: malformed template ${JSON.stringify(raw)} -- expected exactly 3 dot-separated components, got ${parts.length}`
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
let sawDash = false;
|
|
86
|
-
for (const part of parts) {
|
|
87
|
-
if (part === "-") {
|
|
88
|
-
sawDash = true;
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
if (sawDash) {
|
|
92
|
-
throw new Error(
|
|
93
|
-
`version.ts: malformed template ${JSON.stringify(raw)} -- literal component ${JSON.stringify(part)} cannot follow a "-" component; dashes only trail literals (D-2)`
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
if (!TEMPLATE_COMPONENT.test(part)) {
|
|
97
|
-
throw new Error(
|
|
98
|
-
`version.ts: malformed template ${JSON.stringify(raw)} -- component ${JSON.stringify(part)} is neither an integer (no leading zeros) nor "-"`
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return parts;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Parse a PUBLISHED version string into a 3-tuple of numbers, stripping any
|
|
107
|
-
* `-prerelease` / `+build` suffix first. Returns null (never throws) when
|
|
108
|
-
* the input is null, not parseable, or does not have exactly 3 numeric
|
|
109
|
-
* dot-separated components -- callers treat null exactly like "nothing is
|
|
110
|
-
* published" (D-2 rule 4).
|
|
111
|
-
*/
|
|
112
|
-
function parsePublished(published: string | null): number[] | null {
|
|
113
|
-
if (published == null) return null;
|
|
114
|
-
const core = published.split(/[-+]/, 1)[0];
|
|
115
|
-
const parts = core.split(".");
|
|
116
|
-
if (parts.length !== 3) return null;
|
|
117
|
-
// Strict plain-decimal-digit validation (MED-4) -- deliberately NOT
|
|
118
|
-
// `Number(p)` + `Number.isInteger`, which also accepts hex-like literals
|
|
119
|
-
// ("0x2" -> 2), exponential notation ("5e2" -> 500), and whitespace-padded
|
|
120
|
-
// numbers. Those would silently coerce malformed `--published` input or an
|
|
121
|
-
// unexpected `npm view` response into a number that doesn't reflect the
|
|
122
|
-
// original text; this must instead fall back to null (== "no-published",
|
|
123
|
-
// D-2 rule 4) exactly like any other unparseable input, matching the
|
|
124
|
-
// strict digit validation `parseTemplate`'s `TEMPLATE_COMPONENT` already
|
|
125
|
-
// uses for the same kind of input.
|
|
126
|
-
if (!parts.every((p) => /^\d+$/.test(p))) return null;
|
|
127
|
-
return parts.map((p) => Number(p));
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* The D-2 resolution algorithm, in full. `template` must already be
|
|
132
|
-
* well-formed (call `parseTemplate` first, or pass through unchanged --
|
|
133
|
-
* this function calls it internally so a malformed template always throws
|
|
134
|
-
* here too). `published` is the raw published-version string (or null);
|
|
135
|
-
* this function does its own stripping/parsing via `parsePublished`.
|
|
136
|
-
*/
|
|
137
|
-
export function resolveVersion(template: string, published: string | null): ResolveResult {
|
|
138
|
-
const components = parseTemplate(template);
|
|
139
|
-
|
|
140
|
-
if (!components.includes("-")) {
|
|
141
|
-
return { version: components.join("."), rule: "pinned", template, published };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const pub = parsePublished(published);
|
|
145
|
-
|
|
146
|
-
if (pub === null) {
|
|
147
|
-
const resolved = components.map((c) => (c === "-" ? "0" : c));
|
|
148
|
-
return { version: resolved.join("."), rule: "no-published", template, published };
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const prefixMatches = components.every((c, i) => c === "-" || Number(c) === pub[i]);
|
|
152
|
-
|
|
153
|
-
if (!prefixMatches) {
|
|
154
|
-
const resolved = components.map((c) => (c === "-" ? "0" : c));
|
|
155
|
-
return { version: resolved.join("."), rule: "prefix-differs", template, published };
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
let firstDashSeen = false;
|
|
159
|
-
const resolved = components.map((c, i) => {
|
|
160
|
-
if (c !== "-") return c;
|
|
161
|
-
if (!firstDashSeen) {
|
|
162
|
-
firstDashSeen = true;
|
|
163
|
-
// Guard (LOW-1): `pub[i] + 1` on a published component at
|
|
164
|
-
// Number.MAX_SAFE_INTEGER would silently lose precision to float
|
|
165
|
-
// rounding and could fail to actually increment, violating this
|
|
166
|
-
// seam's "always publishes something new" invariant. Purely
|
|
167
|
-
// theoretical for real-world semver (patch counts in the billions are
|
|
168
|
-
// not realistic) but the arithmetic is load-bearing enough to fail
|
|
169
|
-
// loud rather than silently miscompute.
|
|
170
|
-
if (pub[i] >= Number.MAX_SAFE_INTEGER) {
|
|
171
|
-
throw new Error(
|
|
172
|
-
`version.ts: published component ${pub[i]} at index ${i} is at or beyond Number.MAX_SAFE_INTEGER -- refusing to increment (would lose precision)`
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
return String(pub[i] + 1);
|
|
176
|
-
}
|
|
177
|
-
return "0";
|
|
178
|
-
});
|
|
179
|
-
return { version: resolved.join("."), rule: "prefix-matches", template, published };
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Compare two plain, fully-resolved (no `-`, no prerelease) semver-shaped
|
|
184
|
-
* version strings as a numeric 3-tuple. Returns -1/0/1. Throws if either
|
|
185
|
-
* side does not parse as 3 numeric components -- this is for comparing
|
|
186
|
-
* RESOLVED versions, not templates.
|
|
187
|
-
*/
|
|
188
|
-
export function compareVersions(a: string, b: string): -1 | 0 | 1 {
|
|
189
|
-
const pa = parsePublished(a);
|
|
190
|
-
const pb = parsePublished(b);
|
|
191
|
-
if (pa === null) throw new Error(`version.ts: compareVersions() cannot parse ${JSON.stringify(a)}`);
|
|
192
|
-
if (pb === null) throw new Error(`version.ts: compareVersions() cannot parse ${JSON.stringify(b)}`);
|
|
193
|
-
for (let i = 0; i < 3; i++) {
|
|
194
|
-
if (pa[i] < pb[i]) return -1;
|
|
195
|
-
if (pa[i] > pb[i]) return 1;
|
|
196
|
-
}
|
|
197
|
-
return 0;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Read `<repoRootDir>/VERSION`, trimmed. Returns the empty string for an
|
|
202
|
-
* existing-but-blank (or whitespace-only) file, and null ONLY when the file
|
|
203
|
-
* is absent (never throws on either outcome) -- do not treat `=== null` as
|
|
204
|
-
* the sole "no template" signal; an empty string is also "no usable
|
|
205
|
-
* template", and every current caller checks for it via truthiness (LOW-3).
|
|
206
|
-
* Does NOT validate the template shape -- `resolveVersion`/`parseTemplate`
|
|
207
|
-
* do that; this function's only job is the filesystem read.
|
|
208
|
-
*/
|
|
209
|
-
export function readTemplate(repoRootDir: string): string | null {
|
|
210
|
-
const path = join(repoRootDir, "VERSION");
|
|
211
|
-
if (!existsSync(path)) return null;
|
|
212
|
-
try {
|
|
213
|
-
return readFileSync(path, "utf8").trim();
|
|
214
|
-
} catch {
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
30
|
export interface RuntimeVersionOptions {
|
|
31
|
+
/** Path to the `package.json` shipped beside the running code. */
|
|
220
32
|
pkgJsonPath?: string;
|
|
221
|
-
/** LAZY -- see this file's header and the precedence note below. Only
|
|
222
|
-
* called when `pkgJsonPath`'s own version is absent or is
|
|
223
|
-
* DEV_PLACEHOLDER, so a published tarball (which has a real version and
|
|
224
|
-
* no repo-root VERSION file) never calls this and never risks the
|
|
225
|
-
* stderr note `repoRoot()` implementations may emit. */
|
|
226
|
-
repoRoot?: () => string | undefined;
|
|
227
33
|
}
|
|
228
34
|
|
|
229
35
|
/**
|
|
230
|
-
*
|
|
36
|
+
* Runtime precedence, synchronous and never throwing:
|
|
231
37
|
*
|
|
232
38
|
* 1. `pkgJsonPath`'s own `.version`, when present and not DEV_PLACEHOLDER
|
|
233
|
-
* -- the published-tarball path: `npm version`
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
* returns verbatim; a template containing `-` resolves every `-` to 0
|
|
237
|
-
* and appends a `-dev` prerelease tag, so a dev checkout never claims
|
|
238
|
-
* to be a release build.
|
|
239
|
-
* 3. Otherwise DEV_PLACEHOLDER.
|
|
39
|
+
* -- the published-tarball path: `npm version` stamped it from the git
|
|
40
|
+
* tag at publish time.
|
|
41
|
+
* 2. Otherwise DEV_PLACEHOLDER, which is what a git checkout reports.
|
|
240
42
|
*
|
|
241
|
-
* The
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
* standalone MCP server must never fail to start over a version string.
|
|
43
|
+
* The body is wrapped in try/catch: a missing or malformed package.json
|
|
44
|
+
* degrades to DEV_PLACEHOLDER rather than crashing the caller. This is the
|
|
45
|
+
* one runtime-facing entry point in this file, and a standalone MCP server
|
|
46
|
+
* must never fail to start over a version string.
|
|
246
47
|
*/
|
|
247
48
|
export function runtimeVersion(opts: RuntimeVersionOptions = {}): string {
|
|
248
49
|
try {
|
|
@@ -257,19 +58,6 @@ export function runtimeVersion(opts: RuntimeVersionOptions = {}): string {
|
|
|
257
58
|
// No readable/parseable package.json at pkgJsonPath -- fall through.
|
|
258
59
|
}
|
|
259
60
|
}
|
|
260
|
-
|
|
261
|
-
const root = opts.repoRoot?.();
|
|
262
|
-
if (root) {
|
|
263
|
-
const template = readTemplate(root);
|
|
264
|
-
if (template) {
|
|
265
|
-
const components = parseTemplate(template);
|
|
266
|
-
if (!components.includes("-")) {
|
|
267
|
-
return components.join(".");
|
|
268
|
-
}
|
|
269
|
-
const resolved = components.map((c) => (c === "-" ? "0" : c));
|
|
270
|
-
return `${resolved.join(".")}-dev`;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
61
|
} catch {
|
|
274
62
|
// Degrade to the placeholder below -- see this function's own doc
|
|
275
63
|
// comment for why nothing here may throw.
|
package/vice-proxy.ts
CHANGED
|
@@ -302,21 +302,23 @@ const HERE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
|
302
302
|
//
|
|
303
303
|
// `RESOLVED_BINARY.binPath` is what `vice_ping`'s `resolvedBinaryPath` field
|
|
304
304
|
// reports (see stock-dispatch.ts's `handlePing()`). It is resolved exactly
|
|
305
|
-
// ONCE here, at MCP-server process startup
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
// that
|
|
311
|
-
//
|
|
312
|
-
//
|
|
313
|
-
//
|
|
305
|
+
// ONCE here, at MCP-server process startup -- Phase 60 (LOC-01/LOC-02) routes
|
|
306
|
+
// that resolution through the tool-location seam (a `.c64-re-tools/tools.json`
|
|
307
|
+
// entry for `x64sc`, then a bare `x64sc` `$PATH` probe, in THIS process's own
|
|
308
|
+
// environment) rather than a `$PATH` probe alone -- it is NOT re-probed per
|
|
309
|
+
// request and has no connection to the broker, a separate, already-running
|
|
310
|
+
// process that leases whichever instance it chose to whatever request comes
|
|
311
|
+
// in. On a host where the seam resolves to one build, `resolvedBinaryPath`
|
|
312
|
+
// reports that build's path on every ping, even when the broker actually
|
|
313
|
+
// launched (or later recycled to) a different one. The authoritative
|
|
314
|
+
// per-instance answer lives in the broker's own launch record (`epoch.json`'s
|
|
315
|
+
// `vice_bin` field, written by `broker-epoch.mts`) -- Phase 8.2 plan 04's own
|
|
314
316
|
// walkthrough had to route around this field entirely and prove backend
|
|
315
317
|
// identity from that launch record plus a live `ps -o args=` read instead
|
|
316
318
|
// (2026-08-19 finding, closed as a documentation fix by Phase 15 plan 15-09
|
|
317
319
|
// rather than a per-request requery, which would be a behavioural change out
|
|
318
320
|
// of a disposition phase's remit).
|
|
319
|
-
const RESOLVED_BINARY = backendDetect.resolvedBackend();
|
|
321
|
+
const RESOLVED_BINARY = backendDetect.resolvedBackend({ toolsDir: toolsDir({ from: HERE_DIR }), projectRoot: repoRoot({ from: HERE_DIR }) });
|
|
320
322
|
|
|
321
323
|
// -------------------------------------------------------------- JSON-RPC
|
|
322
324
|
//
|
|
@@ -395,16 +397,14 @@ process.stdout.on("error", (err) => {
|
|
|
395
397
|
// repo-root `VERSION` template -- rendered as `<resolved>-dev` -- only in a
|
|
396
398
|
// git checkout, degrading to `0.0.0-dev` if neither is available. Reused,
|
|
397
399
|
// unchanged, as MCPServer's own `version` field below.
|
|
398
|
-
const PROXY_VERSION = runtimeVersion({
|
|
399
|
-
pkgJsonPath: join(HERE_DIR, "package.json"),
|
|
400
|
-
repoRoot: () => repoRoot(),
|
|
401
|
-
});
|
|
400
|
+
const PROXY_VERSION = runtimeVersion({ pkgJsonPath: join(HERE_DIR, "package.json") });
|
|
402
401
|
|
|
403
402
|
// --------------------------------------------------------------- tools/list
|
|
404
403
|
//
|
|
405
404
|
// A pure, offline read of the committed schema snapshot (decision D-C).
|
|
406
|
-
//
|
|
407
|
-
// never fetches, never awaits a network
|
|
405
|
+
// Nothing regenerates that file from a live host -- it is committed and
|
|
406
|
+
// changed by hand. This handler never fetches, never awaits a network
|
|
407
|
+
// call, and never throws. Any problem
|
|
408
408
|
// with the snapshot (absent, unparseable, wrong shape) degrades to a
|
|
409
409
|
// well-formed empty `tools` array plus one stderr line naming the path and
|
|
410
410
|
// the reason, never a fetch and never a hang.
|
|
@@ -503,8 +503,8 @@ const RESULT_CONTINUE_TOOL: ToolDefinition = {
|
|
|
503
503
|
|
|
504
504
|
// The recycle tool (plan 01.3-01, task 1): the only new HOST-SIDE ACTION
|
|
505
505
|
// this phase adds. Served entirely proxy-local -- like RESULT_CONTINUE_TOOL
|
|
506
|
-
// above, it is never in tools-manifest.json (RESEARCH Key Finding 3),
|
|
507
|
-
// manifest
|
|
506
|
+
// above, it is never in tools-manifest.stock.json (RESEARCH Key Finding 3),
|
|
507
|
+
// so a manifest edit can never drop it. Deliberately split from
|
|
508
508
|
// vice_diagnose (D-03): this tool NEVER gates on a verdict, so there is no
|
|
509
509
|
// "confirm"/"mode" argument and no shared state between the two tools to
|
|
510
510
|
// keep in sync -- the separation itself is the safety.
|
|
@@ -1584,11 +1584,11 @@ tools[DIAGNOSE_TOOL.name] = buildViceTool(stockDispatch.resolveAdvertisedToolDef
|
|
|
1584
1584
|
// Backend-INDEPENDENT by construction (plan 29-01): the anno_* family never
|
|
1585
1585
|
// touches VICE at all -- it reaches a PROXY-LOCAL SQLite annotation store
|
|
1586
1586
|
// this repo owns, opened and closed inside the runner itself, so there is no
|
|
1587
|
-
// fork/stock distinction to make. The family is
|
|
1588
|
-
// tools-manifest.
|
|
1589
|
-
//
|
|
1590
|
-
//
|
|
1591
|
-
//
|
|
1587
|
+
// fork/stock distinction to make. The family is deliberately absent from
|
|
1588
|
+
// tools-manifest.stock.json, which records what a live HOST VICE server
|
|
1589
|
+
// answers and never a proxy-local store file. Registering the family here,
|
|
1590
|
+
// rather than listing it there, keeps that manifest an honest record of the
|
|
1591
|
+
// emulator surface.
|
|
1592
1592
|
// Registered here via buildViceTool() directly (the SAME pattern
|
|
1593
1593
|
// RESULT_CONTINUE_TOOL above uses), so no anno_* runner ever reaches
|
|
1594
1594
|
// stockDispatch: there is no generic-dispatch surface left anywhere in this
|