@henols/vice-mcp 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD-PARTY-NOTICES.md +23 -0
- package/package.json +15 -1
- package/r2000-acme-ident.ts +97 -0
- package/r2000-cli.ts +1103 -0
- package/r2000-confidence.ts +233 -0
- package/r2000-d64.ts +310 -0
- package/r2000-enum-gen.ts +574 -0
- package/r2000-launch.ts +357 -0
- package/r2000-mcp-client.ts +596 -0
- package/r2000-memmap-render.ts +531 -0
- package/r2000-project.ts +190 -0
- package/r2000-regbits-gen.ts +416 -0
- package/r2000-regbits.json +1370 -0
- package/r2000-symbols.ts +388 -0
- package/r2000-tools.ts +914 -0
- package/r2000-verify.ts +184 -0
- package/stock-symbols.ts +33 -9
- package/vice-proxy.ts +146 -0
package/r2000-symbols.ts
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// r2000-symbols.ts -- the ONE authoritative place in this repo for the
|
|
3
|
+
// symbol round trip between regenerator2000's annotation store and stock
|
|
4
|
+
// VICE's symbol table (R2000-14/R2000-15, ARCHITECTURE.md Rule A20).
|
|
5
|
+
//
|
|
6
|
+
// WHY THIS FILE EXISTS: static-analysis symbols going OUT to VICE
|
|
7
|
+
// (`exportLabels()`) and live-discovered symbols coming IN from VICE
|
|
8
|
+
// (`importLabels()`) must flow through explicit adapter code -- neither side
|
|
9
|
+
// may parse the other's internal representation (Rule A20). This module is
|
|
10
|
+
// that adapter. It reuses `stock-symbols.ts`'s existing `al C:xxxx .Name`
|
|
11
|
+
// parser (`parseViceLabelFile()`, exported there for exactly this reuse)
|
|
12
|
+
// rather than adding this repo's THIRD copy of that format --
|
|
13
|
+
// `stock-symbols.ts` and `acme-build/scripts/acme.mjs`'s `curateLabels()`
|
|
14
|
+
// are the two that already exist.
|
|
15
|
+
//
|
|
16
|
+
// MEASURED FACTS this module's behaviour depends on:
|
|
17
|
+
// - `--export_lbl` exports USER labels only. Measured (Phase 9, and
|
|
18
|
+
// re-confirmed by this plan's own round-trip test): an annotated
|
|
19
|
+
// project emits exactly the labels a caller set via
|
|
20
|
+
// `r2000_set_label_name`/`--import_lbl` -- the auto-generated `a_D011` /
|
|
21
|
+
// `a_D020` / `e_FFD2` externals are NOT exported. A test asserting an
|
|
22
|
+
// `a_`-prefixed name appears in an `exportLabels()` result is testing
|
|
23
|
+
// the wrong thing.
|
|
24
|
+
// - `--import_lbl` under plain `--headless` DISCARDS. `main.rs:800-806` is
|
|
25
|
+
// `if headless && !mcp_server { return Ok(()) }`: an argv of
|
|
26
|
+
// `--import_lbl <path> --headless <proj>` imports the labels into
|
|
27
|
+
// memory and then hits that early return WITHOUT ever calling save, so
|
|
28
|
+
// the import is silently discarded -- measured live: two names imported
|
|
29
|
+
// that way, and a subsequent `--export_lbl` read back from disk
|
|
30
|
+
// returned only the pre-existing label. `r2000-launch.ts`'s
|
|
31
|
+
// `buildImportLblArgs()` makes this combination unbuildable by always
|
|
32
|
+
// pairing `--mcp-server-stdio` (which sets both `headless` AND
|
|
33
|
+
// `mcp_server`, `main.rs:709-711`, skipping the early return) --
|
|
34
|
+
// `r2000-symbol-roundtrip.test.ts` pins the trap itself with a
|
|
35
|
+
// hand-built argv, so that builder's pairing stays provably load-bearing
|
|
36
|
+
// rather than merely assumed.
|
|
37
|
+
//
|
|
38
|
+
// WHAT NOT TO DO, named concretely:
|
|
39
|
+
// - Never add a second `al C:xxxx .Name` regex anywhere in this file.
|
|
40
|
+
// Every read of a label file's TEXT goes through
|
|
41
|
+
// `stock-symbols.ts`'s exported `parseViceLabelFile()`.
|
|
42
|
+
// - Never call `vice_symbols_load` (`stock-symbols.ts`'s handlers)
|
|
43
|
+
// incrementally. `regenerateAndReload()` below regenerates the WHOLE
|
|
44
|
+
// `.lbl` and returns its path for the CALLER to hand to
|
|
45
|
+
// `vice_symbols_load` exactly once -- `vice_symbols_load` is
|
|
46
|
+
// deliberately replace-not-merge (T-05-02-05), so a full regeneration is
|
|
47
|
+
// what keeps that semantics correct rather than a limitation. A merge
|
|
48
|
+
// mode on `vice_symbols_load` itself was rejected: it would reopen a
|
|
49
|
+
// v0.2.0 decision and make a tool advertised on both backends diverge in
|
|
50
|
+
// semantics.
|
|
51
|
+
// - Never build `--import_lbl`'s argv by hand in this file. It comes ONLY
|
|
52
|
+
// from `r2000-launch.ts`'s `buildImportLblArgs()` -- there is no literal
|
|
53
|
+
// `"--import_lbl"` string anywhere below.
|
|
54
|
+
// - Never report an import as persisted on the strength of a
|
|
55
|
+
// no-error response alone. `importLabels()` below proves persistence
|
|
56
|
+
// TWICE: once via `saveAndVerify()`'s content-hash check (inside the
|
|
57
|
+
// import session), and independently via a fresh `exportLabels()` from
|
|
58
|
+
// disk, in a BRAND NEW process, after the import session has fully
|
|
59
|
+
// closed. `ImportLabelsResult` is a discriminated union
|
|
60
|
+
// (`diskVerified: true | false`) specifically so a caller cannot
|
|
61
|
+
// mistake "the import call returned no error" for "the names are
|
|
62
|
+
// actually on disk".
|
|
63
|
+
// - Never let an illegal label name from a `.lbl` file reach a spawned
|
|
64
|
+
// child (T-11-NAME-INJECT, closed). `importLabels()` validates every
|
|
65
|
+
// name against `r2000-acme-ident.ts`'s `assertLegalAcmeIdentifier()`
|
|
66
|
+
// BEFORE `buildImportLblArgs()` is ever called -- REJECT, never
|
|
67
|
+
// sanitize, matching `r2000-tools.ts`'s `r2000_set_label_name` posture.
|
|
68
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, statSync } from "node:fs";
|
|
69
|
+
import { tmpdir } from "node:os";
|
|
70
|
+
import { join } from "node:path";
|
|
71
|
+
|
|
72
|
+
import { buildExportLblArgs, buildImportLblArgs, runR2000 } from "./r2000-launch.ts";
|
|
73
|
+
import { withR2000Session, saveAndVerify } from "./r2000-mcp-client.ts";
|
|
74
|
+
import { runR2000Tool } from "./r2000-tools.ts";
|
|
75
|
+
import { parseViceLabelFile, MAX_LABEL_FILE_BYTES } from "./stock-symbols.ts";
|
|
76
|
+
import { assertLegalAcmeIdentifier } from "./r2000-acme-ident.ts";
|
|
77
|
+
|
|
78
|
+
/** This module's own error class, following `r2000-tools.ts`'s
|
|
79
|
+
* `R2000StorePathError` / `r2000-launch.ts`'s `R2000ViceFlagError` minimal
|
|
80
|
+
* shape (message-only, `.name` set to the class name). Never thrown for a
|
|
81
|
+
* ceiling violation on a `.lbl` file's TEXT -- those come from
|
|
82
|
+
* `stock-symbols.ts`'s `StockSymbolsError`, surfaced verbatim, never
|
|
83
|
+
* re-wrapped as this class. Reserved for this module's OWN failure modes:
|
|
84
|
+
* a nonzero regenerator2000 exit, a missing output file despite a zero
|
|
85
|
+
* exit, an oversized file caught before `parseViceLabelFile()` is ever
|
|
86
|
+
* called, or (T-11-NAME-INJECT, closed) an illegal label name caught in
|
|
87
|
+
* `importLabels()` before any child is spawned -- naming the offending
|
|
88
|
+
* name, its 1-based line number, and that line's own text. */
|
|
89
|
+
export class R2000SymbolsError extends Error {
|
|
90
|
+
constructor(message: string) {
|
|
91
|
+
super(message);
|
|
92
|
+
this.name = "R2000SymbolsError";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface LabelEntry {
|
|
97
|
+
name: string;
|
|
98
|
+
address: number;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface ExportLabelsOptions {
|
|
102
|
+
projectPath: string;
|
|
103
|
+
outPath: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface ExportLabelsResult {
|
|
107
|
+
path: string;
|
|
108
|
+
symbolCount: number;
|
|
109
|
+
symbols: LabelEntry[];
|
|
110
|
+
skippedLines: number;
|
|
111
|
+
duplicateNames: number;
|
|
112
|
+
lineCount: number;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The export leg (R2000-14). Runs `buildExportLblArgs()` through
|
|
117
|
+
* `runR2000()`, then READS THE PRODUCED FILE BACK through
|
|
118
|
+
* `stock-symbols.ts`'s existing parser to validate it and return the parsed
|
|
119
|
+
* symbol list plus a count.
|
|
120
|
+
*
|
|
121
|
+
* The read-back is not optional, for two independent reasons: a
|
|
122
|
+
* regenerator2000 exit code has lied before (`r2000-verify.ts`'s founding
|
|
123
|
+
* incident, D-10 -- a zero exit alongside content that should have failed),
|
|
124
|
+
* and the parse is the only thing that proves the produced file is in the
|
|
125
|
+
* format `vice_symbols_load` actually accepts. Ceiling violations from
|
|
126
|
+
* `parseViceLabelFile()` (`StockSymbolsError`) are surfaced VERBATIM, never
|
|
127
|
+
* re-wrapped -- only this function's OWN failure modes (nonzero exit,
|
|
128
|
+
* missing file, oversized file) throw `R2000SymbolsError`.
|
|
129
|
+
*/
|
|
130
|
+
export async function exportLabels({ projectPath, outPath }: ExportLabelsOptions): Promise<ExportLabelsResult> {
|
|
131
|
+
const argv = buildExportLblArgs({ projectPath, outPath });
|
|
132
|
+
const result = runR2000(argv);
|
|
133
|
+
if (result.status !== 0) {
|
|
134
|
+
throw new R2000SymbolsError(
|
|
135
|
+
`exportLabels: regenerator2000 exited ${result.status} for "${projectPath}" -- stderr: ${result.stderr || "(empty)"}`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
if (!existsSync(outPath)) {
|
|
139
|
+
throw new R2000SymbolsError(
|
|
140
|
+
`exportLabels: regenerator2000 exited 0 but did not produce "${outPath}" -- a lying zero exit code has ` +
|
|
141
|
+
"happened before (r2000-verify.ts's founding incident, D-10); refusing to trust the exit code alone.",
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let size: number;
|
|
146
|
+
try {
|
|
147
|
+
size = statSync(outPath).size;
|
|
148
|
+
} catch (err) {
|
|
149
|
+
throw new R2000SymbolsError(
|
|
150
|
+
`exportLabels: could not stat "${outPath}" (${err instanceof Error ? err.message : String(err)})`,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
if (size > MAX_LABEL_FILE_BYTES) {
|
|
154
|
+
throw new R2000SymbolsError(
|
|
155
|
+
`exportLabels: "${outPath}" is ${size} bytes, which exceeds the ${MAX_LABEL_FILE_BYTES}-byte ceiling`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const text = readFileSync(outPath, "utf8");
|
|
160
|
+
// Reused verbatim -- stock-symbols.ts's ONE parser, never a second regex.
|
|
161
|
+
// A ceiling violation here (StockSymbolsError) propagates unmodified.
|
|
162
|
+
const parsed = parseViceLabelFile(text);
|
|
163
|
+
|
|
164
|
+
const symbols: LabelEntry[] = Array.from(parsed.table.byName.entries()).map(([name, address]) => ({ name, address }));
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
path: outPath,
|
|
168
|
+
symbolCount: parsed.symbolCount,
|
|
169
|
+
symbols,
|
|
170
|
+
skippedLines: parsed.skippedLines,
|
|
171
|
+
duplicateNames: parsed.duplicateNames,
|
|
172
|
+
lineCount: parsed.lineCount,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface ImportLabelsOptions {
|
|
177
|
+
projectPath: string;
|
|
178
|
+
lblPath: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface ImportLabelsVerified {
|
|
182
|
+
diskVerified: true;
|
|
183
|
+
importedNames: string[];
|
|
184
|
+
/** The fresh, independent `exportLabels()` result used to prove
|
|
185
|
+
* persistence -- a caller can inspect it without re-exporting itself. */
|
|
186
|
+
exported: ExportLabelsResult;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export interface ImportLabelsUnverified {
|
|
190
|
+
diskVerified: false;
|
|
191
|
+
importedNames: string[];
|
|
192
|
+
/** Names present in the imported `.lbl` file that a fresh export from
|
|
193
|
+
* disk did NOT contain. Always non-empty when `diskVerified` is `false`. */
|
|
194
|
+
missingNames: string[];
|
|
195
|
+
reason: string;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* A discriminated union, deliberately -- `diskVerified: true` and
|
|
200
|
+
* `diskVerified: false` are structurally distinct shapes, so a caller
|
|
201
|
+
* cannot read `result.importedNames` and mistake "the import call returned
|
|
202
|
+
* no error" for "the names are actually on disk" (the plan's own required
|
|
203
|
+
* distinction). Only the `true` variant carries `exported`; only the
|
|
204
|
+
* `false` variant carries `missingNames`/`reason`.
|
|
205
|
+
*/
|
|
206
|
+
export type ImportLabelsResult = ImportLabelsVerified | ImportLabelsUnverified;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* The import leg (R2000-15, the D-28 path). Runs `buildImportLblArgs()` --
|
|
210
|
+
* which ALWAYS carries `--mcp-server-stdio` -- and, over that SAME stdio
|
|
211
|
+
* session, calls `r2000_save_project` through `r2000-mcp-client.ts`'s
|
|
212
|
+
* `saveAndVerify()`. `--import_lbl` mutates only IN-MEMORY state
|
|
213
|
+
* (`main.rs:800-806` is why the save must be explicit); `saveAndVerify()`
|
|
214
|
+
* proves the save changed the project file's own content hash on disk.
|
|
215
|
+
*
|
|
216
|
+
* That alone is `r2000-mcp-client.ts`'s own T-11-FALSESUCCESS proof, not
|
|
217
|
+
* THIS module's. `importLabels()` proves persistence a SECOND, independent
|
|
218
|
+
* way: after the import session has fully closed, a BRAND NEW process
|
|
219
|
+
* (`exportLabels()`, a fresh `runR2000()` child) re-reads the project from
|
|
220
|
+
* disk and re-exports its labels. Only when every name in the caller's
|
|
221
|
+
* `.lbl` file appears in that fresh export does this function report
|
|
222
|
+
* `diskVerified: true`.
|
|
223
|
+
*
|
|
224
|
+
* The caller-supplied `.lbl` file itself is ceiling-checked BEFORE any
|
|
225
|
+
* child process is spawned (T-11-LBL-SIZE): a byte-size check against
|
|
226
|
+
* `stock-symbols.ts`'s own `MAX_LABEL_FILE_BYTES`, then a full
|
|
227
|
+
* `parseViceLabelFile()` pass, whose `StockSymbolsError` ceiling violations
|
|
228
|
+
* (`MAX_LABEL_FILE_LINES`/`MAX_SYMBOLS`) propagate verbatim -- an oversized
|
|
229
|
+
* or over-populated `.lbl` never reaches regenerator2000 at all.
|
|
230
|
+
*
|
|
231
|
+
* Every discovered name is then validated against `r2000-acme-ident.ts`'s
|
|
232
|
+
* `assertLegalAcmeIdentifier()`, also BEFORE any spawn (T-11-NAME-INJECT,
|
|
233
|
+
* closed): an illegal name throws `R2000SymbolsError` naming the offending
|
|
234
|
+
* name, its 1-based line number, and that line's own text -- REJECT, never
|
|
235
|
+
* sanitize, the same posture `r2000-tools.ts`'s `r2000_set_label_name`
|
|
236
|
+
* takes on the tool-surface entry route.
|
|
237
|
+
*/
|
|
238
|
+
export async function importLabels({ projectPath, lblPath }: ImportLabelsOptions): Promise<ImportLabelsResult> {
|
|
239
|
+
let size: number;
|
|
240
|
+
try {
|
|
241
|
+
size = statSync(lblPath).size;
|
|
242
|
+
} catch (err) {
|
|
243
|
+
throw new R2000SymbolsError(
|
|
244
|
+
`importLabels: could not stat "${lblPath}" (${err instanceof Error ? err.message : String(err)})`,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
if (size > MAX_LABEL_FILE_BYTES) {
|
|
248
|
+
throw new R2000SymbolsError(
|
|
249
|
+
`importLabels: "${lblPath}" is ${size} bytes, which exceeds the ${MAX_LABEL_FILE_BYTES}-byte ceiling`,
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const inputText = readFileSync(lblPath, "utf8");
|
|
254
|
+
// Reused verbatim -- same parser as exportLabels(). A ceiling violation
|
|
255
|
+
// here (StockSymbolsError) propagates unmodified, before any spawn.
|
|
256
|
+
const inputParsed = parseViceLabelFile(inputText);
|
|
257
|
+
const importedNames = Array.from(inputParsed.table.byName.keys());
|
|
258
|
+
|
|
259
|
+
// T-11-NAME-INJECT (route B, closed): every discovered label name is
|
|
260
|
+
// validated against the one ACME identifier seam (r2000-acme-ident.ts)
|
|
261
|
+
// BEFORE any child is spawned -- REJECT, never sanitize, matching
|
|
262
|
+
// r2000-tools.ts's r2000_set_label_name posture. The offending line is
|
|
263
|
+
// located by a substring search over the already-read inputText, never a
|
|
264
|
+
// second `al C:` regex (this module's own header forbids a third parser
|
|
265
|
+
// for that format, T-11-LBL-PARSER-DUP).
|
|
266
|
+
for (const name of importedNames) {
|
|
267
|
+
try {
|
|
268
|
+
assertLegalAcmeIdentifier(name, "importLabels label name");
|
|
269
|
+
} catch (err) {
|
|
270
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
271
|
+
const inputLines = inputText.split(/\r?\n/);
|
|
272
|
+
const lineIndex = inputLines.findIndex((line) => line.includes(name));
|
|
273
|
+
const lineNumber = lineIndex === -1 ? 0 : lineIndex + 1;
|
|
274
|
+
const lineText = lineIndex === -1 ? "(line not found)" : inputLines[lineIndex];
|
|
275
|
+
throw new R2000SymbolsError(
|
|
276
|
+
`importLabels: "${lblPath}" line ${lineNumber} carries an illegal label name "${name}" (${reason}) -- ` +
|
|
277
|
+
`line text: ${JSON.stringify(lineText)}. REJECTED, never sanitized or quoted, before any child is spawned.`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// buildImportLblArgs() is the ONLY producer of this argv -- there is no
|
|
283
|
+
// literal "--import_lbl" string anywhere in this file.
|
|
284
|
+
const argv = buildImportLblArgs({ projectPath, lblPath });
|
|
285
|
+
await withR2000Session(projectPath, (call) => saveAndVerify(projectPath, call), { argv });
|
|
286
|
+
|
|
287
|
+
// Independent proof #2: a brand-new process, after the import session has
|
|
288
|
+
// fully closed, re-reads the project from disk and re-exports its labels.
|
|
289
|
+
const verifyDir = mkdtempSync(join(tmpdir(), "r2000-symbols-verify-"));
|
|
290
|
+
try {
|
|
291
|
+
const reExportPath = join(verifyDir, "reexport.lbl");
|
|
292
|
+
const exported = await exportLabels({ projectPath, outPath: reExportPath });
|
|
293
|
+
const exportedNames = new Set(exported.symbols.map((s) => s.name));
|
|
294
|
+
const missingNames = importedNames.filter((n) => !exportedNames.has(n));
|
|
295
|
+
|
|
296
|
+
if (missingNames.length > 0) {
|
|
297
|
+
return {
|
|
298
|
+
diskVerified: false,
|
|
299
|
+
importedNames,
|
|
300
|
+
missingNames,
|
|
301
|
+
reason:
|
|
302
|
+
`importLabels: saveAndVerify() reported a changed content hash for "${projectPath}", but a fresh ` +
|
|
303
|
+
`exportLabels() from disk is missing ${missingNames.length} of ${importedNames.length} imported ` +
|
|
304
|
+
`name(s): ${missingNames.join(", ")}`,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return { diskVerified: true, importedNames, exported };
|
|
309
|
+
} finally {
|
|
310
|
+
rmSync(verifyDir, { recursive: true, force: true });
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export interface RegenerateAndReloadOptions {
|
|
315
|
+
projectPath: string;
|
|
316
|
+
outPath: string;
|
|
317
|
+
address: number;
|
|
318
|
+
name: string;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export interface RegenerateAndReloadResult {
|
|
322
|
+
path: string;
|
|
323
|
+
symbolCount: number;
|
|
324
|
+
symbols: LabelEntry[];
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* D-29: the store is the merge point for a live-discovered name. Writes
|
|
329
|
+
* `name` at `address` into the store FIRST (via the curated
|
|
330
|
+
* `r2000_set_label_name`, `r2000-tools.ts`'s `runR2000Tool()` -- which
|
|
331
|
+
* auto-saves internally before its session exits), THEN regenerates the
|
|
332
|
+
* WHOLE `.lbl` with `exportLabels()`, and returns the path for the CALLER
|
|
333
|
+
* to hand to `vice_symbols_load` EXACTLY ONCE.
|
|
334
|
+
*
|
|
335
|
+
* This function deliberately does NOT call `vice_symbols_load` itself --
|
|
336
|
+
* see this module's header for why an incremental/repeated load would
|
|
337
|
+
* violate `vice_symbols_load`'s replace-not-merge semantics (T-05-02-05).
|
|
338
|
+
*
|
|
339
|
+
* Note (T-11-NAME-INJECT, closed): an illegal `name` now REJECTS this
|
|
340
|
+
* function's returned promise -- `r2000_set_label_name`'s own pre-spawn
|
|
341
|
+
* `assertLegalLabelArg()` gate (`r2000-tools.ts`) fires inside
|
|
342
|
+
* `runR2000Tool()`'s `assertCuratedTool()` call, before `runR2000Tool()`'s
|
|
343
|
+
* own `try` block, rather than surfacing as `setResult.isError` the way a
|
|
344
|
+
* regenerator2000-side failure would. This function had no production
|
|
345
|
+
* caller when that note was written, so no existing behaviour depended on
|
|
346
|
+
* the old `isError`-shaped outcome -- superseded in place by the formal
|
|
347
|
+
* status below rather than left as an aside.
|
|
348
|
+
*
|
|
349
|
+
* LIBRARY-ONLY (Phase 11 IN-02, D-11.1-06): as of this phase,
|
|
350
|
+
* `regenerateAndReload()` is a library-only export -- available for
|
|
351
|
+
* programmatic use, with NO PRODUCTION CALLER anywhere in this repo
|
|
352
|
+
* (`.claude/mcp/vice/`, `.claude/skills/`, `scripts/`). A future phase must
|
|
353
|
+
* not assume it is wired into any real workflow just because it is the
|
|
354
|
+
* named D-29 live-discovery merge point.
|
|
355
|
+
*
|
|
356
|
+
* The proven live path for the symbol round trip is NOT this function --
|
|
357
|
+
* it is `r2000 export-lbl` -> `vice_symbols_load` -> live discovery ->
|
|
358
|
+
* `r2000_set_label_name` -> `r2000 import-lbl`, documented as one closed
|
|
359
|
+
* loop in `c64-program-recon/SKILL.md`. `R2000-15` is satisfied through
|
|
360
|
+
* that sequence, not through this convenience wrapper. Giving this
|
|
361
|
+
* function a caller would mean inventing a new CLI verb or skill entry
|
|
362
|
+
* point it was never actually proven through -- out of this phase's scope
|
|
363
|
+
* fence (11.1-CONTEXT.md).
|
|
364
|
+
*
|
|
365
|
+
* ADOPTION CONDITION: if `regenerateAndReload()` ever acquires a real
|
|
366
|
+
* production caller, the `LIBRARY-ONLY` marker above must be deleted in
|
|
367
|
+
* the SAME commit that adds the caller. `r2000-symbol-roundtrip.test.ts`
|
|
368
|
+
* enforces this as a biconditional, in both directions: zero callers
|
|
369
|
+
* requires the marker present; one or more callers requires the marker
|
|
370
|
+
* absent. The two can never legally disagree.
|
|
371
|
+
*/
|
|
372
|
+
export async function regenerateAndReload({
|
|
373
|
+
projectPath,
|
|
374
|
+
outPath,
|
|
375
|
+
address,
|
|
376
|
+
name,
|
|
377
|
+
}: RegenerateAndReloadOptions): Promise<RegenerateAndReloadResult> {
|
|
378
|
+
const setResult = await runR2000Tool("r2000_set_label_name", { project: projectPath, address, name });
|
|
379
|
+
if (setResult.isError) {
|
|
380
|
+
throw new R2000SymbolsError(
|
|
381
|
+
`regenerateAndReload: r2000_set_label_name failed for "${name}" at ${address}: ` +
|
|
382
|
+
`${setResult.content.map((c) => c.text).join(" ")}`,
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const exported = await exportLabels({ projectPath, outPath });
|
|
387
|
+
return { path: exported.path, symbolCount: exported.symbolCount, symbols: exported.symbols };
|
|
388
|
+
}
|