@henols/vice-mcp 0.2.3 → 0.2.4

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/anno-graphics.ts CHANGED
@@ -1,19 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  // anno-graphics.ts
3
3
  //
4
- // Phase 37, plan 37-07 (AUTO-06): derives the VIC-II graphics areas -- screen
4
+ // Derives the VIC-II graphics areas -- screen
5
5
  // matrix, character set or bitmap, and sprite POINTERS -- from RECOVERED VIC
6
6
  // register VALUES, never from cross-references. The chip fetches its
7
7
  // character/bitmap/screen data by direct memory access, so a character set
8
8
  // may be referenced by NO INSTRUCTION ANYWHERE IN THE PROGRAM -- exactly the
9
9
  // case a cross-reference-driven join structurally cannot find. That is
10
- // AUTO-06's entire reason to exist (`37-RESEARCH.md` SS E: no existing code in
10
+ // This module's entire reason to exist: no existing code in
11
11
  // this repository decodes these three registers for this purpose -- the
12
12
  // stock-backend modules that name them read LIVE emulator state for a
13
- // different question).
13
+ // different question.
14
14
  //
15
15
  // THIS MODULE TAKES PLAIN REGISTER-WRITE VALUES AND RETURNS PLAIN RANGES --
16
- // AND NOTHING ELSE (D-37-26). It never receives a store handle, never opens
16
+ // AND NOTHING ELSE. It never receives a store handle, never opens
17
17
  // or names `node:sqlite`, never imports `anno-store.ts`, `anno-join.ts`, or
18
18
  // any module that reads the stored cross-reference graph, and never imports
19
19
  // `hostpath.ts`/`containerpath.ts` or the emulator backend
@@ -22,13 +22,13 @@
22
22
  // very case this derivation exists to cover -- so its import list is scanned
23
23
  // structurally (`anno-graphics.test.ts`) rather than merely reviewed.
24
24
  //
25
- // SPRITE BITMAP LOCATIONS ARE OUT OF SCOPE (D-37-29): this module derives the
25
+ // SPRITE BITMAP LOCATIONS ARE OUT OF SCOPE: this module derives the
26
26
  // eight-byte sprite POINTER TABLE range only. The pointer VALUES themselves
27
27
  // are program data, usually written at run time, and are not register values
28
28
  // a static analysis recovers -- this module must never appear to promise
29
29
  // them.
30
30
  //
31
- // A MISSING REGISTER IS A STATED ABSENCE, NEVER A DEFAULT (D-37-28): where a
31
+ // A MISSING REGISTER IS A STATED ABSENCE, NEVER A DEFAULT: where a
32
32
  // required register value was never recovered, the ranges that depend on it
33
33
  // are omitted from that map and the map names the missing register in
34
34
  // `missingRegisters`. Power-on defaults exist and are well known, which is
@@ -37,10 +37,10 @@
37
37
  // recovered value, and this phase exists to prevent exactly that class of
38
38
  // confident wrong output.
39
39
  //
40
- // SEVERAL COMBINATIONS ARE SEVERAL MAPS, NEVER ONE MERGED MAP (D-37-27): a
40
+ // SEVERAL COMBINATIONS ARE SEVERAL MAPS, NEVER ONE MERGED MAP: a
41
41
  // program that reprograms bank, screen or charset per raster split has
42
42
  // several valid graphics maps, and a single derived map would be wrong for
43
- // all but one -- the same path-dependence limit AUTO-05 names for the
43
+ // all but one -- the same path-dependence limit this project's own processor-port derivation names for the
44
44
  // processor port. `deriveGraphicsRanges()` returns one map per DISTINCT
45
45
  // combination of the three registers' own recovered values: the cross
46
46
  // product of each register's own distinct-value set, with a register that
@@ -49,7 +49,7 @@
49
49
  // tuple order (missing sorts first), so two runs over the same facts return
50
50
  // deeply equal arrays.
51
51
  //
52
- // NO GRAPHICS-SPECIFIC DATA TYPE EXISTS (D-37-30): the store's `DataType`
52
+ // NO GRAPHICS-SPECIFIC DATA TYPE EXISTS: the store's `DataType`
53
53
  // vocabulary (`anno-types.ts`) is a frozen twelve with no graphics-specific
54
54
  // member, and widening it is a schema decision no plan in this phase makes --
55
55
  // widening it would require re-auditing every consumer of that frozen list,
@@ -61,7 +61,7 @@
61
61
  // (READ-ONLY, NEVER IMPORTED): this module's bank/screen/charset/bitmap
62
62
  // arithmetic was checked by hand against those two files' existing, tested
63
63
  // decode of the SAME three registers for the live-emulator backend, and
64
- // found to agree exactly -- see the plan 37-07 SUMMARY for the comparison.
64
+ // found to agree exactly.
65
65
  // This module REIMPLEMENTS that arithmetic rather than importing it, because
66
66
  // importing either file would reach the emulator backend's own transport
67
67
  // seam, which this module must never touch.
@@ -126,7 +126,7 @@ export const SPRITE_POINTER_OFFSET = 0x3f8;
126
126
  const SPRITE_POINTER_SIZE = 8;
127
127
 
128
128
  /** The existing, byte-shaped `DataType` every range this module emits
129
- * carries (D-37-30). */
129
+ * carries. */
130
130
  const GRAPHICS_DATA_TYPE: DataType = "byte";
131
131
 
132
132
  /** One recovered register write, in the same shape `ConstWriteFact` in
@@ -212,7 +212,7 @@ function distinctValuesFor(facts: readonly GraphicsConstWriteFact[], targetAddre
212
212
  }
213
213
 
214
214
  /** One combination of the three registers' own recovered values --
215
- * `undefined` names a register with zero recovered facts (D-37-28). */
215
+ * `undefined` names a register with zero recovered facts. */
216
216
  interface RegisterCombo {
217
217
  bankSelect: number | undefined;
218
218
  memoryControl: number | undefined;
@@ -220,7 +220,7 @@ interface RegisterCombo {
220
220
  }
221
221
 
222
222
  /** Ascending, with `undefined` (missing) sorting first -- a stated,
223
- * deterministic order over the tuple (D-37-27), not "whatever `Set`
223
+ * deterministic order over the tuple, not "whatever `Set`
224
224
  * iteration happened to produce". */
225
225
  function compareSlot(a: number | undefined, b: number | undefined): number {
226
226
  const av = a ?? -1;
@@ -304,13 +304,13 @@ function buildGraphicsMap(combo: RegisterCombo): GraphicsMap {
304
304
 
305
305
  /**
306
306
  * Derives the graphics areas -- screen matrix, character set or bitmap, and
307
- * sprite pointers -- from recovered VIC register values ALONE (D-37-26).
307
+ * sprite pointers -- from recovered VIC register values ALONE.
308
308
  * Returns one `GraphicsMap` per distinct combination of the three registers'
309
- * own recovered values (D-37-27), in ascending tuple order, so two calls
309
+ * own recovered values, in ascending tuple order, so two calls
310
310
  * with the same `facts` return deeply equal arrays. A register with zero
311
311
  * recovered facts contributes a single "missing" combination slot rather
312
312
  * than an axis; every range depending on it is omitted from every map, and
313
- * the map names it in `missingRegisters` (D-37-28). `facts` carrying values
313
+ * the map names it in `missingRegisters`. `facts` carrying values
314
314
  * for addresses other than the three this module watches (e.g. the
315
315
  * processor port, $0001) are ignored -- this module only ever reads writes
316
316
  * to `BANK_SELECT_ADDRESS`/`MEMORY_CONTROL_ADDRESS`/`CONTROL_REGISTER_1_ADDRESS`.
@@ -233,8 +233,8 @@ export interface HazardLimit {
233
233
  }
234
234
 
235
235
  /**
236
- * The two limits this plan's slice seeds. Plan 48-03 and plan 48-05 add the
237
- * remaining entries; this array only ever grows.
236
+ * The two limits seeded here; later work adds the
237
+ * remaining entries -- this array only ever grows.
238
238
  */
239
239
  export const HAZARD_LIMITS: readonly HazardLimit[] = Object.freeze([
240
240
  {
package/anno-import.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  // anno-import.ts
3
3
  //
4
- // Phase 37, plan 37-01 (IMP-01, IMP-02): the container-side parser and
5
- // importer for `GhidraStructExport.java`'s `## `-delimited transfer file.
4
+ // The container-side parser and importer for `GhidraStructExport.java`'s
5
+ // `## `-delimited transfer file.
6
6
  //
7
7
  // THIS MODULE RECEIVES AN ALREADY-OPEN STORE HANDLE. It never opens or closes
8
8
  // a store itself -- there is no second store session anywhere in this file.
@@ -18,9 +18,9 @@
18
18
  // WHAT THIS IS THE ONE AUTHORITATIVE PLACE FOR: parsing the export's fixed
19
19
  // `## `-delimited section format, mapping Ghidra's `Reference.getReferenceType()`
20
20
  // vocabulary onto the store's frozen four-member `XrefAccessKind`
21
- // (`GHIDRA_REFTYPE_TO_ACCESS_KIND`, D-37-05), and the digest-then-delete
21
+ // (`GHIDRA_REFTYPE_TO_ACCESS_KIND`), and the digest-then-delete
22
22
  // discipline that makes the transfer file transient evidence rather than a
23
- // second on-disk model (IMP-02).
23
+ // second on-disk model.
24
24
  //
25
25
  // WHAT NOT TO DO:
26
26
  // - Never write a row before the WHOLE document has parsed successfully.
@@ -37,7 +37,7 @@
37
37
  // member of `XREF_ACCESS_KINDS`. It is dropped and COUNTED in
38
38
  // `kindsSeenNotImported`, never silently absorbed and never refused --
39
39
  // refusing on an ordinary `JSR`/`JMP` reference would make the importer
40
- // unusable against a real corpus binary (D-37-05).
40
+ // unusable against a real corpus binary.
41
41
 
42
42
  import { createHash } from "node:crypto";
43
43
  // Namespace import, deliberately: a later acceptance gate greps this file for
@@ -95,11 +95,11 @@ export interface GhidraExportDocument {
95
95
  * hand-written test fixture using either convention keeps working.
96
96
  *
97
97
  * Discovered as a LIVE, previously-untested defect in the REFERENCES import
98
- * path (plan 37-01): every prior test used a hand-written, `$`-prefixed
98
+ * path: every prior test used a hand-written, `$`-prefixed
99
99
  * transfer file, never a token shaped exactly as Ghidra itself renders one
100
100
  * -- `importGhidraExport()` would refuse EVERY real captured export outright.
101
101
  * Fixed here, in the SAME function `## CONST_WRITES`'s own tokens (which are
102
- * ALSO bare hex, D-37-06) need the identical treatment for.
102
+ * ALSO bare hex) need the identical treatment for.
103
103
  */
104
104
  function parseGhidraAddressToken(token: string, what: string): number {
105
105
  if (token.startsWith("$") || /^0[xX]/.test(token)) {
@@ -245,7 +245,7 @@ export function parseGhidraExport(text: string): GhidraExportDocument {
245
245
  };
246
246
  checkTrailerCount("REFERENCES", "REFERENCE_COUNT");
247
247
  checkTrailerCount("CLASSIFICATION", "CLASSIFICATION_LINES");
248
- // Plan 37-02: the SAME generic trailer-count check, extended to
248
+ // The SAME generic trailer-count check, extended to
249
249
  // `## CONST_WRITES` / `## CONST_WRITES_COUNT`. Note what this generic
250
250
  // grammar already does with the exporter's own `## CONST_WRITES_NONE`
251
251
  // marker line: because every `## `-prefixed line with no following space
@@ -262,7 +262,7 @@ export function parseGhidraExport(text: string): GhidraExportDocument {
262
262
  }
263
263
 
264
264
  /**
265
- * D-37-05's frozen mapping from Ghidra's `Reference.getReferenceType()`
265
+ * The frozen mapping from Ghidra's `Reference.getReferenceType()`
266
266
  * vocabulary onto the store's four-member `XrefAccessKind`. A token absent
267
267
  * from this table is dropped and COUNTED (`kindsSeenNotImported`), never
268
268
  * refused and never guessed -- a real corpus binary carries ordinary jump and
@@ -277,7 +277,7 @@ export const GHIDRA_REFTYPE_TO_ACCESS_KIND: Readonly<Record<string, XrefAccessKi
277
277
  COMPUTED_CALL: "COMPUTED_JUMP",
278
278
  });
279
279
 
280
- /** D-37-06's watched-address set, mirroring the Java constant
280
+ /** The watched-address set, mirroring the Java constant
281
281
  * `CONST_WRITE_WATCHED_ADDRESSES` in `GhidraStructExport.java` byte-for-byte
282
282
  * -- the two MUST be kept in step. `parseConstWrites()` below does NOT
283
283
  * filter its own output against this list: a `## CONST_WRITES` line for an
@@ -299,8 +299,8 @@ export interface ConstWriteFact {
299
299
 
300
300
  /**
301
301
  * Turns the `## CONST_WRITES` section `parseGhidraExport()` already parsed
302
- * into `(storeAddress, targetAddress, value)` facts (Phase 37 plan 37-02,
303
- * `AUTO-04`/`AUTO-05`). `parseGhidraExport()` has ALREADY refused, before
302
+ * into `(storeAddress, targetAddress, value)` facts. `parseGhidraExport()`
303
+ * has ALREADY refused, before
304
304
  * this function is ever called, if a declared `## CONST_WRITES_COUNT`
305
305
  * trailer disagrees with the section's own parsed body-line count -- the
306
306
  * SAME generic check `## REFERENCE_COUNT`/`## CLASSIFICATION_LINES` already
@@ -357,10 +357,10 @@ export interface ImportCounts {
357
357
  * still reported as a success with this reason attached, never as a
358
358
  * failure after a durable write. */
359
359
  transferDeleteError?: string;
360
- /** CR-01 fix: the `## CONST_WRITES` section's own facts, parsed by
360
+ /** The `## CONST_WRITES` section's own facts, parsed by
361
361
  * `parseConstWrites()` BEFORE the transfer file is deleted below -- the
362
362
  * ONE artifact carrying them. `importGhidraExport()` never persists these
363
- * facts in the store (the reserved `bank` column stays null, IMP-01/D-37-25);
363
+ * facts in the store (the reserved `bank` column stays null);
364
364
  * they ride on THIS return value instead, so a caller can hand the SAME
365
365
  * array straight to `anno_join_memmap`'s own `const_writes` argument in a
366
366
  * following call, closing the loop `anno-tools.ts`'s `dispatchJoinMemmap()`
@@ -429,7 +429,7 @@ export function importGhidraExport(handle: AnnoStoreHandle, args: ImportGhidraEx
429
429
 
430
430
  const doc = parseGhidraExport(contents.toString("utf8"));
431
431
  const referenceLines = doc.sections.get("REFERENCES") ?? [];
432
- // CR-01/WR-02 fix: parsed here, from the SAME document, before the
432
+ // Parsed here, from the SAME document, before the
433
433
  // transfer file is deleted below -- `parseConstWrites()` was previously
434
434
  // exercised only by test code (`anno-join.test.ts`/`ghidra-live.test.ts`),
435
435
  // never by this, the only production entry point that reads a transfer
package/anno-index.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  //
4
4
  // The ONE place that answers "which annotated range owns this address" --
5
5
  // a pure, narrowest-range-wins paint index over the whole 64K address space,
6
- // rebuilt from rows and never maintained incrementally (STORE-03).
6
+ // rebuilt from rows and never maintained incrementally.
7
7
  //
8
8
  // ---------------------------------------------------------------------------
9
9
  // WHY THIS FILE EXISTS
@@ -37,14 +37,14 @@
37
37
  // an argument is the only shape that can be tested for it.
38
38
  // 2. NEVER cache this index on disk. A cached index is a second truth that
39
39
  // can disagree with the range table -- exactly the failure the
40
- // derived-from-bytes coverage census (`COV-01`) exists to make impossible.
40
+ // derived-from-bytes coverage census exists to make impossible.
41
41
  // Rebuild it; see the measurement above.
42
- // 3. NEVER add an adjacency, coalescing or merge pass. `STORE-02` stores
43
- // ranges AS ranges and never merges them, so there is no splitter
44
- // primitive here to introduce. Merging is the named blocker behind
45
- // `DECOMP-01` and `BUILD-02` and the predicted over-merge bias behind
46
- // `COV-01`; all three are designed out by construction here, not
47
- // documented as hazards to remember.
42
+ // 3. NEVER add an adjacency, coalescing or merge pass. This project's own
43
+ // store stores ranges AS ranges and never merges them, so there is no
44
+ // splitter primitive here to introduce. Merging is a named blocker for
45
+ // decomposition-to-closure work and the predicted over-merge bias
46
+ // behind the coverage census; all three are designed out by
47
+ // construction here, not documented as hazards to remember.
48
48
  // 4. NEVER maintain the index incrementally. An incremental update has to
49
49
  // know what the row it is removing was covering UNDERNEATH, which the
50
50
  // index cannot say -- a painted cell records the winner, not the losers.
package/anno-join.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  // anno-join.ts
3
3
  //
4
- // Phase 37, plan 37-01 (AUTO-01): the mechanical join between stored
4
+ // The mechanical join between stored
5
5
  // cross-references and `memmap.json`, with no agent call, no queue walk and
6
6
  // no skill invocation anywhere in the loop.
7
7
  //
@@ -17,9 +17,9 @@
17
17
  // WHAT THIS IS THE ONE AUTHORITATIVE PLACE FOR: reading `listXrefs()`'s
18
18
  // distinct target addresses, classifying each one (inside the loaded image,
19
19
  // no `memmap.json` entry, or annotated) and writing the resulting comment
20
- // through `setComment()`. `AUTO-01`'s own criterion -- no agent, no queue
20
+ // through `setComment()`. This module's own criterion -- no agent, no queue
21
21
  // walk, no skill invocation -- is checked STRUCTURALLY over this module's own
22
- // source in `anno-join.test.ts` (plan 37 task 3), not merely asserted here in
22
+ // source in `anno-join.test.ts`, not merely asserted here in
23
23
  // prose.
24
24
  //
25
25
  // WHAT NOT TO DO:
@@ -33,14 +33,14 @@
33
33
  // that decodes to more than one region, DECLINES -- see the bank-state
34
34
  // block below.
35
35
  //
36
- // Phase 37, plan 37-06 (AUTO-04/AUTO-05): `runMemmapJoin()` gains an
36
+ // `runMemmapJoin()` gains an
37
37
  // OPTIONAL `constWrites` argument. When `undefined` (every pre-37-06 call
38
38
  // site, and every existing test in this file), the bank-state machinery
39
39
  // below is a complete no-op and every address resolves EXACTLY as it did
40
40
  // before this plan -- the candidate-constraint argument must not change any
41
41
  // unconstrained selection's answer. Only when a caller explicitly supplies
42
42
  // an array (even an empty one) does the reaching-values/decline logic
43
- // activate for addresses inside `BANK_CONDITIONAL_RANGES`. D-37-24: the
43
+ // activate for addresses inside `BANK_CONDITIONAL_RANGES`. The
44
44
  // reaching-values computation is deliberately conservative -- it is NOT a
45
45
  // dataflow analysis. It uses only what the export gives: the recovered
46
46
  // constant stores to the processor port (each with its own address), and
@@ -49,7 +49,7 @@
49
49
  // `constWrites` whose OWN store address can reach that address, walked
50
50
  // forward over that same graph. Where the graph does not connect a store to
51
51
  // the address at all, that value is simply not in the reaching set --
52
- // D-37-23's decline-on-empty-or-disagreement rule (below) is what turns
52
+ // the decline-on-empty-or-disagreement rule (below) is what turns
53
53
  // "nothing reaches this point" into a stated absence rather than a silent
54
54
  // default.
55
55
 
@@ -66,30 +66,29 @@ import type { ConstWriteFact } from "./anno-import.ts";
66
66
  import type { ContradictedComment, SplitTableReinterpretation, XrefRow } from "./anno-types.ts";
67
67
  import { loadMemmap, memmapDigest, PROVENANCE_TOKEN_PREFIX, selectMemmapEntry } from "./memmap-lookup.ts";
68
68
  import type { MemmapEntry, MemmapSelection } from "./memmap-lookup.ts";
69
- // Phase 37, plan 37-08 (AUTO-07): the graphics write-back. deriveGraphicsRanges()
69
+ // The graphics write-back. deriveGraphicsRanges()
70
70
  // is structurally typed against ConstWriteFact -- GraphicsConstWriteFact's own
71
71
  // shape is identical ({storeAddress, targetAddress, value}) -- so THIS module's
72
- // own constWrites argument, already threaded for D-37-24's bank-state block, is
73
- // handed straight through with no translation layer (37-07-SUMMARY.md's own
74
- // "Next Phase Readiness" note names this directly).
72
+ // own constWrites argument, already threaded for the bank-state block, is
73
+ // handed straight through with no translation layer.
75
74
  import { deriveGraphicsRanges } from "./anno-graphics.ts";
76
75
  import type { GraphicsMap } from "./anno-graphics.ts";
77
76
 
78
- /** D-37-25: the axis-qualified provenance marker an annotated bank-conditional
77
+ /** The axis-qualified provenance marker an annotated bank-conditional
79
78
  * comment carries, ALWAYS before `PROVENANCE_TOKEN_PREFIX`'s own digest
80
- * token (which stays last -- D-37-13). Names the axis ("processor-port")
79
+ * token (which stays last). Names the axis ("processor-port")
81
80
  * explicitly, so a later phase adding the VIC banking axis is additive
82
81
  * rather than ambiguous about which axis a given token names. */
83
82
  const BANK_PROVENANCE_PREFIX = "[processor-port:";
84
83
 
85
84
  /**
86
- * The reaching-values computation's own result shape (D-37-24): exactly one
85
+ * The reaching-values computation's own result shape: exactly one
87
86
  * recovered value reaches the address; several do (an array -- length 0
88
87
  * means none reach at all, length 2+ means genuine disagreement-or-agreement
89
88
  * to resolve); or the computation cannot decide at all. Nothing in this
90
89
  * project's current data model (a plain directed graph of already-resolved
91
90
  * `XrefRow`s) can currently produce `"unknown"` -- there is no signal here
92
- * for a dropped or unresolved reference (D-37-24's own stated limit) -- but
91
+ * for a dropped or unresolved reference (a stated limit of this computation) -- but
93
92
  * the shape is kept complete for a future importer that DOES emit such a
94
93
  * signal, rather than silently folding that case into "empty".
95
94
  */
@@ -126,7 +125,7 @@ function canReach(from: number, target: number, adjacency: Map<number, number[]>
126
125
  return false;
127
126
  }
128
127
 
129
- /** D-37-24: the set of recovered processor-port values whose OWN store
128
+ /** The set of recovered processor-port values whose OWN store
130
129
  * address can reach `targetAddress`, over `adjacency`. Never a dataflow
131
130
  * analysis -- purely "does the stored cross-reference graph connect this
132
131
  * store to this address". */
@@ -156,8 +155,8 @@ export class AnnoJoinError extends Error {
156
155
  }
157
156
 
158
157
  /** What one `runMemmapJoin()` call reports. `addressesConsidered` is always
159
- * the sum of the next four fields. The three `graphics*` fields (plan 37-08,
160
- * AUTO-07) are always present and `0` when `constWrites` is omitted or when
158
+ * the sum of the next four fields. The three `graphics*` fields are always
159
+ * present and `0` when `constWrites` is omitted or when
161
160
  * the selected map derives zero ranges -- never absent, so a caller reads
162
161
  * them unconditionally instead of guarding on them, mirroring
163
162
  * `SetDataTypeResult`'s own "always present, often empty" convention for
@@ -175,14 +174,14 @@ export interface JoinCounts {
175
174
  }
176
175
 
177
176
  /**
178
- * What the graphics write-back (plan 37-08, AUTO-07) reports, in full --
177
+ * What the graphics write-back reports, in full --
179
178
  * `JoinCounts`'s own `graphics*` fields are the COUNTS of these same
180
179
  * `contradictedComments`/`reinterpretedSplitTables` arrays; this record
181
180
  * carries the disclosures themselves so neither is dropped (must_haves.truths:
182
181
  * "the join's returned counts include the contradicted-comment and
183
182
  * fragmented-split-table disclosures the range write reported; neither is
184
183
  * dropped"). `mapIndex` records WHICH of `deriveGraphicsRanges()`'s several
185
- * maps was written -- D-37-27's own rule (several valid combinations are
184
+ * maps was written -- this project's own rule (several valid combinations are
186
185
  * several maps, never one merged map) means writing more than one would
187
186
  * write mutually-contradicting ranges into the SAME store, so exactly one is
188
187
  * ever written and this field is the record of which. */
@@ -206,7 +205,7 @@ export interface JoinDecision {
206
205
  export interface RunMemmapJoinArgs {
207
206
  imageOrigin: number;
208
207
  imageByteLength: number;
209
- /** D-37-24/AUTO-04/AUTO-05: the recovered `$01` const-write facts this run
208
+ /** The recovered `$01` const-write facts this run
210
209
  * has evidence for, typically `parseConstWrites()`'s own output over one
211
210
  * imported export. `undefined` (every pre-37-06 call site) means "this run
212
211
  * carries no bank-state evidence at all" -- the bank-state machinery is a
@@ -214,7 +213,7 @@ export interface RunMemmapJoinArgs {
214
213
  * An explicit array (even `[]`) activates it for addresses inside
215
214
  * `BANK_CONDITIONAL_RANGES`. */
216
215
  constWrites?: readonly ConstWriteFact[];
217
- /** Plan 37-08 (AUTO-07, D-37-27): which of `deriveGraphicsRanges()`'s
216
+ /** Which of `deriveGraphicsRanges()`'s
218
217
  * several maps to write back, when `constWrites` derives more than one
219
218
  * distinct register-value combination. Defaults to `0`. Consulted ONLY
220
219
  * when `constWrites` is supplied AT ALL (the SAME gate that activates the
@@ -230,8 +229,9 @@ export interface RunMemmapJoinArgs {
230
229
  * in ascending address order. An address inside the caller's own loaded
231
230
  * image range is a program address, not a hardware/memory-map feature, and
232
231
  * is skipped WITHOUT a `memmap.json` lookup -- the membership test runs
233
- * BEFORE `selectEntry()` is called at all (D-37-12), so the guard is a
234
- * control-flow fact rather than a result-filtering one: `AUTO-03` says an
232
+ * BEFORE `selectEntry()` is called at all, so the guard is a
233
+ * control-flow fact rather than a result-filtering one: this file's own
234
+ * contract says an
235
235
  * in-image address is "never looked up in memmap.json", a claim about what
236
236
  * runs, not merely about what the caller sees back. An address with no
237
237
  * containing `memmap.json` entry is skipped for that reason instead;
@@ -262,7 +262,7 @@ export function runMemmapJoin(
262
262
  );
263
263
  }
264
264
 
265
- // D-37-13: computed ONCE per join run and reused for every annotated row,
265
+ // Computed ONCE per join run and reused for every annotated row,
266
266
  // never recomputed per row -- two comments written in the same run are
267
267
  // therefore GUARANTEED to carry byte-identical tokens, not merely likely
268
268
  // to (the file cannot change mid-run, but a per-row recompute would still
@@ -272,7 +272,7 @@ export function runMemmapJoin(
272
272
  const xrefs = listXrefs(handle);
273
273
  const targets = [...new Set(xrefs.map((xref) => xref.toAddress))].sort((a, b) => a - b);
274
274
 
275
- // D-37-24: built ONCE per run, over the SAME xref graph the unconstrained
275
+ // Built ONCE per run, over the SAME xref graph the unconstrained
276
276
  // path already reads via `listXrefs()` above -- reused for every address's
277
277
  // own reachability walk below. `undefined` `args.constWrites` means the
278
278
  // bank-state block is never entered at all, so this adjacency map is built
@@ -282,7 +282,7 @@ export function runMemmapJoin(
282
282
  const bankAdjacency = buildAdjacency(xrefs);
283
283
 
284
284
  // The inclusive image range, computed ONCE from the LoadedImage's own body
285
- // bytes (D-37-12) -- never from `totalBytes` (the file's own byte count,
285
+ // bytes -- never from `totalBytes` (the file's own byte count,
286
286
  // which on the .prg route includes the two-byte load-address header and
287
287
  // would shift this whole range by two bytes) and never a caller-supplied
288
288
  // number pair that could silently widen or narrow the program's own
@@ -300,7 +300,7 @@ export function runMemmapJoin(
300
300
  for (const address of targets) {
301
301
  // THE GUARD: one early-return, before selectEntry() is ever called. A
302
302
  // single textual deletion of this block removes it cleanly -- that
303
- // deletion is plan 37-05's own observed-red control (row 4).
303
+ // deletion is this file's own observed-red control.
304
304
  if (address >= imageStart && address <= imageEnd) {
305
305
  skippedInImage += 1;
306
306
  decisions.push({
@@ -313,13 +313,13 @@ export function runMemmapJoin(
313
313
  continue;
314
314
  }
315
315
 
316
- // THE BANK-STATE BLOCK (D-37-22/D-37-23/D-37-24, AUTO-04/AUTO-05). Only
316
+ // THE BANK-STATE BLOCK. Only
317
317
  // entered when the caller supplied `constWrites` AT ALL (`undefined`
318
318
  // skips this whole block, falling through to the unconstrained path
319
319
  // below exactly as pre-37-06) AND the address is inside one of the
320
320
  // three bank-conditional ranges -- outside them, bank state is
321
321
  // irrelevant and the candidate set stays unconstrained regardless of
322
- // `constWrites` (D-37-22).
322
+ // `constWrites`.
323
323
  if (args.constWrites !== undefined && isBankConditionalAddress(address)) {
324
324
  const reaching = computeReachingValues(address, args.constWrites, bankAdjacency);
325
325
 
@@ -335,7 +335,7 @@ export function runMemmapJoin(
335
335
 
336
336
  // Resolves ONE region (or refuses) for a single reaching value, applies
337
337
  // it as a candidate constraint BEFORE selection runs (never a
338
- // post-filter -- D-37-22), and pushes the matching decision. Shared by
338
+ // post-filter), and pushes the matching decision. Shared by
339
339
  // both the single-value and the several-values-same-region branches
340
340
  // below, so the annotate path is written exactly once.
341
341
  const annotateUnderRegion = (region: Exclude<BankedRegion, "not_applicable">, bankNote: string): void => {
@@ -412,7 +412,7 @@ export function runMemmapJoin(
412
412
 
413
413
  // The full comment text: the selected entry's label, one space, the
414
414
  // provenance prefix, then the full 64-character digest -- always LAST,
415
- // never truncated (D-37-13). setComment() -> assertCommentText() refuses
415
+ // never truncated. setComment() -> assertCommentText() refuses
416
416
  // (never truncates) a text that overflows MAX_COMMENT_BYTES; that
417
417
  // refusal is left to propagate here rather than being pre-checked and
418
418
  // silently worked around, because a truncated provenance token would be
@@ -424,7 +424,7 @@ export function runMemmapJoin(
424
424
  decisions.push({ address, outcome: "annotated", label: selection.entry.label });
425
425
  }
426
426
 
427
- // THE GRAPHICS WRITE-BACK (D-37-32/D-37-33, AUTO-07). Runs AFTER the main
427
+ // THE GRAPHICS WRITE-BACK. Runs AFTER the main
428
428
  // per-address loop above, as its own step -- graphics ranges are derived
429
429
  // from register VALUES, never from the cross-reference targets the loop
430
430
  // above walks, so there is no reason to interleave the two. Gated on the
@@ -443,10 +443,10 @@ export function runMemmapJoin(
443
443
  );
444
444
  }
445
445
 
446
- // D-37-27: write ONLY the selected map's own ranges -- never every map
446
+ // Write ONLY the selected map's own ranges -- never every map
447
447
  // deriveGraphicsRanges() returned. Several distinct register-value
448
- // combinations describe MUTUALLY CONTRADICTING layouts (D-37-27's own
449
- // reason several maps exist at all); writing more than one into the
448
+ // combinations describe MUTUALLY CONTRADICTING layouts (the reason
449
+ // several maps exist at all); writing more than one into the
450
450
  // same store would write ranges that disagree with each other by
451
451
  // construction.
452
452
  let rangesWritten = 0;