@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-cli.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- // anno-cli.ts -- the thin CLI ergonomics layer over the annotation store
3
- // (D-06). Reached as `vice-mcp anno <verb>` because that bin is the only
2
+ // anno-cli.ts -- the thin CLI ergonomics layer over the annotation store.
3
+ // Reached as `vice-mcp anno <verb>` because that bin is the only
4
4
  // surface that resolves identically across the Claude Code plugin route and
5
5
  // both npm-installer routes: `installer/bin/cli.mjs`'s `viceServerEntry()`
6
6
  // always launches this server via `npx` in BOTH npm-installer modes, and
@@ -8,18 +8,17 @@
8
8
  // consuming project for some other filesystem-path-resolving design to find.
9
9
  //
10
10
  // ---------------------------------------------------------------------------
11
- // FIVE VERBS. THAT IS THE WHOLE SURFACE (D-14, 2026-08-29 -- FOUR at the
12
- // time; third verb landed 2026-08-31; fourth verb, `evid-disagreements`,
13
- // landed by phase 43 plan 43-06 -- the CLI route for EVID-03's disagreement
14
- // query, so a planted test has three RENDERED, textually-distinguishable
15
- // states to compare rather than only the MCP tool's JSON, which a test can
16
- // only inspect structurally; fifth verb, `decomp-completeness`, landed by
17
- // phase 45 plan 45-01 -- the CLI route for the decomposition-completeness
18
- // report's ONLY data path into a real store (D-07). D-14's original
19
- // "FOUR VERBS. THAT IS THE WHOLE SURFACE" framing is DELIBERATELY
20
- // SUPERSEDED by D-07, exactly as plan 43-06 (three -> four) already
21
- // superseded it once before -- this is the second raise over the same
22
- // decision, not a silent reopening of it).
11
+ // FIVE VERBS. THAT IS THE WHOLE SURFACE -- narrowed to two, then grown back
12
+ // one verb at a time as each earned a real route over this project's own
13
+ // annotation store: a third verb landed first; a fourth, `evid-disagreements`,
14
+ // followed as the CLI route for the disagreement query, so a planted test has
15
+ // three RENDERED, textually-distinguishable states to compare rather than
16
+ // only the MCP tool's JSON, which a test can only inspect structurally; a
17
+ // fifth, `decomp-completeness`, followed as the CLI route for the
18
+ // decomposition-completeness report's ONLY data path into a real store. Each
19
+ // raise of the count DELIBERATELY SUPERSEDES the prior "THAT IS THE WHOLE
20
+ // SURFACE" framing rather than silently reopening it -- this is the second
21
+ // raise over that framing, not the first.
23
22
  // ---------------------------------------------------------------------------
24
23
  // This file used to carry eight. Six were removed in one commit because they
25
24
  // were delivery paths for the retired external analyser this project used to
@@ -43,15 +42,14 @@
43
42
  // requirement and no success criterion of the phase that rebuilt
44
43
  // `export-asm` covers any of them, and NO PHASE CURRENTLY OWNS THEM, so
45
44
  // the symbol round trip still has NO route at all. That is recorded as a
46
- // withdrawal in `.planning/PROJECT.md`'s shipped-capability list rather
47
- // than left for a reader to discover by running it. The exact wording of
48
- // those withdrawal notices across both skill trees is re-pointed in one
49
- // place, by the plan that owns the tree-wide sweep (30-06); this file
50
- // states the code fact and does not restate their text, so the two edits
51
- // cannot contradict each other.
45
+ // withdrawal in this project's own capability record rather than left for
46
+ // a reader to discover by running it. The exact wording of those
47
+ // withdrawal notices across both skill trees is kept in exactly one
48
+ // place; this file states the code fact and does not restate their text,
49
+ // so the two edits cannot contradict each other.
52
50
  //
53
51
  // WHAT NOT TO DO, named concretely:
54
- // - Never auto-pick an input when the caller does not name one (D-02). A
52
+ // - Never auto-pick an input when the caller does not name one. A
55
53
  // silent auto-pick would happily analyse a cracktro or loader stub's
56
54
  // bytes instead of the actual game -- precisely the failure
57
55
  // `c64-provenance-diff` exists to prevent elsewhere in this project.
@@ -67,9 +65,9 @@
67
65
  // confinement escape waiting to be written.
68
66
  //
69
67
  // THIS PARAGRAPH WAS FALSE WHEN IT WAS FIRST WRITTEN, and that is why it
70
- // now names the mechanism that keeps it. `29-VERIFICATION.md` gap 3 /
71
- // `29-REVIEW.md` CR-02 and CR-03 reproduced three escapes on this very
72
- // tree, on arguments the shipped playbooks tell an agent to compose in a
68
+ // now names the mechanism that keeps it. An independent verification pass
69
+ // reproduced three escapes on this very tree, on arguments the shipped
70
+ // playbooks tell an agent to compose in a
73
71
  // Bash invocation: `render-memmap --out` and `coverage --out` reached
74
72
  // `writeFileSync` as raw caller strings (the first silently replacing a
75
73
  // pre-existing file OUTSIDE the workspace root and exiting 0), and
@@ -95,7 +93,7 @@
95
93
  // noticing.
96
94
  //
97
95
  // AND WHAT IT DOES NOT CHECK, stated in terms so the limit can be closed
98
- // deliberately rather than discovered (WR-02): it does not associate a
96
+ // deliberately rather than discovered: it does not associate a
99
97
  // particular argument with a particular call site. "Six arguments each
100
98
  // confined once" and "five confined with one of them confined twice" read
101
99
  // the same to it. That association needs per-argument dataflow through
@@ -119,16 +117,16 @@
119
117
  //
120
118
  // Import nothing from `hostpath.ts` or `containerpath.ts`. Every path this
121
119
  // CLI handles is already container-side, and translating any of these
122
- // arguments would be the mirror image of the DERIV-07 screenshot-path trap,
123
- // where a client-side-derived path was wrongly translated a second time.
124
- // This absence is asserted structurally by `hostpath-consumers.test.ts`
125
- // (D-08), not merely stated here.
120
+ // arguments would be the mirror image of a screenshot-path trap this project
121
+ // hit before, where a client-side-derived path was wrongly translated a
122
+ // second time. This absence is asserted structurally by
123
+ // `hostpath-consumers.test.ts`, not merely stated here.
126
124
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
127
125
  import { basename, dirname, extname, join, sep } from "node:path";
128
126
  import { fileURLToPath } from "node:url";
129
127
 
130
128
  import { renderMemoryMap, checkRenderedMemoryMap } from "./anno-memmap-render.ts";
131
- // The ACME source emitter (EXPORT-01). It reads the store and the image and
129
+ // The ACME source emitter. It reads the store and the image and
132
130
  // returns text plus counts; it starts no assembler and knows nothing about
133
131
  // one. `acme-verify.ts` -- the module that DOES spawn ACME -- is deliberately
134
132
  // NOT imported here and must never be: it is test-only (it is absent from
@@ -136,7 +134,7 @@ import { renderMemoryMap, checkRenderedMemoryMap } from "./anno-memmap-render.ts
136
134
  // would drag it into the published closure `check-npm-packages.mjs` walks.
137
135
  import { exportAsmTree } from "./anno-export-asm.ts";
138
136
  import type { ExportAsmTreeResult } from "./anno-export-asm.ts";
139
- // The coverage instrument (COV-01/COV-02). It declares its own input shapes
137
+ // The coverage instrument. It declares its own input shapes
140
138
  // and never reads a store, a file or a tool on its own behalf -- a caller
141
139
  // fetches and hands the data in, which is exactly what makes the store
142
140
  // re-point below a CALLER-side change and nothing more.
@@ -147,14 +145,14 @@ import type { CoverageReport, LoadedProject, AnnoComment, AnnoCrossReference, An
147
145
  import type { BlockEntry } from "./block-class.ts";
148
146
  import { openStore, closeStore, listLabels, listComments, listRanges, listExecObservations, listObservedRuns, listXrefs } from "./anno-store.ts";
149
147
  import type { AnnoStoreHandle } from "./anno-store.ts";
150
- // The shared 6502/6510 decoder (DISASM-01 et al.). `decomp-completeness`'s
151
- // entry-point and referenced-address censuses (phase 45 plan 45-04) walk the
148
+ // The shared 6502/6510 decoder. `decomp-completeness`'s
149
+ // entry-point and referenced-address censuses walk the
152
150
  // SAME code-range decode `anno_disassemble` and `anno-enum-gen.ts`'s
153
151
  // `fetchRegisterSearchRows()` already use -- never a second decoder, never a
154
152
  // regex over rendered text.
155
153
  import { decode } from "./disasm-decoder.ts";
156
154
  import type { Instruction } from "./disasm-decoder.ts";
157
- // The disagreement query's own pure join (EVID-03/EVID-04, plan 43-06). This
155
+ // The disagreement query's own pure join. This
158
156
  // is the SAME reconcileObservedExecution() the anno_evid_disagreements MCP
159
157
  // tool calls -- reached here directly (a static import, never lazy) because
160
158
  // this module IS the CLI, not a startup-cost-sensitive MCP server entry
@@ -166,7 +164,7 @@ import { reconcileObservedExecution } from "./evid-reconcile.ts";
166
164
  import { buildHazardReport } from "./anno-hazard-report.ts";
167
165
  import type { HazardReport } from "./anno-hazard-report.ts";
168
166
  import type { EvidReconciliation } from "./evid-reconcile.ts";
169
- // The derived half of STORE-06: cross-references are DERIVED from the bytes
167
+ // The derived half of the cross-reference union: cross-references are DERIVED from the bytes
170
168
  // plus the store's typed ranges plus the few rows that cannot be recovered
171
169
  // from bytes at all. There is exactly one definition of that union and this
172
170
  // file calls it rather than restating it.
@@ -174,9 +172,9 @@ import { crossReferencesTo } from "./anno-derive.ts";
174
172
  import { storePathWithinWorkspace, isSplitDataType } from "./anno-types.ts";
175
173
  import type { CommentRow, LabelRow, RangeRow, DataType } from "./anno-types.ts";
176
174
  import { repoRoot } from "./repo-root.ts";
177
- // D-03's three comment-text conventions (plan 45-02), declared once in
175
+ // The three comment-text conventions, declared once in
178
176
  // anno-store-export.ts and imported everywhere they are matched -- never
179
- // restated as a second literal (T-45-15's own mitigation).
177
+ // restated as a second literal.
180
178
  import { DECLINE_COMMENT_PREFIX, DISAGREEMENT_ACCEPTED_COMMENT_PREFIX, AUTHORED_PROVENANCE_COMMENT_PREFIX } from "./anno-store-export.ts";
181
179
 
182
180
  const HERE = dirname(fileURLToPath(import.meta.url));
@@ -189,7 +187,7 @@ usage (plugin/in-repo): ${PLUGIN_INVOCATION}
189
187
  verbs:
190
188
  render-memmap <store> --provenance FILE [--out FILE] [--force] [--check]
191
189
  Generates the Markdown memory map from an annotation store plus a
192
- validated provenance sidecar (D-24: the store is canonical, this
190
+ validated provenance sidecar (the store is canonical; this
193
191
  output is a GENERATED VIEW -- never hand-edit it). Without --check,
194
192
  writes --out (default: memory-map.md beside the STORE -- in the
195
193
  store's own directory), refusing to overwrite an existing file there
@@ -213,7 +211,7 @@ verbs:
213
211
 
214
212
  coverage <image> --store FILE [--out FILE] [--force] [--sample N]
215
213
  Measures how far a program has actually been reverse-engineered
216
- (COV-01/COV-02), through anno-coverage.ts. <image> supplies the
214
+ through anno-coverage.ts. <image> supplies the
217
215
  PAYLOAD BYTES and the load origin; --store names the ANNOTATION STORE
218
216
  holding the labels, comments and typed ranges. Those are two separate
219
217
  files on purpose: the store holds annotations and never bytes, so a
@@ -222,14 +220,14 @@ verbs:
222
220
  <image> is dispatched IN THIS ORDER, and the order is load-bearing:
223
221
  first, a .raw or .bin is read as a flat capture BY EXTENSION, before
224
222
  any length check, so a truncated capture is refused BY NAME instead
225
- of falling through to the .prg parser (WR-07: a 4096-byte .raw once
223
+ of falling through to the .prg parser (a 4096-byte .raw once
226
224
  had its first two bytes read as a load address and reported a
227
225
  complete-looking measurement); then any file that is NOT a .prg and
228
226
  is exactly 65536 bytes is read as a flat capture, which is the one
229
227
  branch that does dispatch on byte length; then a .prg, whose first
230
228
  two bytes are the load address. The retired JSON project form
231
229
  survives as a TRAILING LEGACY branch, reached only when none of
232
- those matched -- its only producer was deleted (D-14) and it is kept
230
+ those matched -- its only producer was deleted, and it is kept
233
231
  solely so an existing file on disk is not broken.
234
232
  Prints three separately named measures -- the structural byte census,
235
233
  the two label figures, and the sampled reproducibility result -- plus
@@ -279,7 +277,7 @@ verbs:
279
277
  room.
280
278
  --ledger names c64-provenance-diff's generated recovery/PROVENANCE.md.
281
279
  Supplying it makes the export carry each covered range's recorded
282
- Verdict and Confidence as inline comments (BUILD-05). It is OPTIONAL:
280
+ Verdict and Confidence as inline comments. It is OPTIONAL:
283
281
  omitting it exports exactly as before. The flag changes COMMENT TEXT
284
282
  ONLY -- it never changes which bytes or which blocks are emitted, and a
285
283
  range the supplied ledger does not cover is refused by name rather than
@@ -318,15 +316,15 @@ verbs:
318
316
 
319
317
  decomp-completeness --store FILE --disagreements FILE --manifest FILE [--json]
320
318
  The decomposition-closure completeness answer for ONE
321
- per-fixture store (D-07). Three REQUIRED arguments, none defaulted from
319
+ per-fixture store. Three REQUIRED arguments, none defaulted from
322
320
  another: --store names the annotation store; --disagreements names the
323
321
  JSON "anno evid-disagreements --store <same store> --json" wrote for
324
- THIS store's own run; --manifest names the execution manifest (D-13)
322
+ THIS store's own run; --manifest names the execution manifest
325
323
  recording which committed fixtures were actually run. Omitting ANY of
326
324
  the three refuses BY NAME with exit 1 -- there is no default and no
327
325
  empty-array substitute for a missing disagreement input, because an
328
326
  omitted query and a query that found nothing must never render the
329
- same report (D-09 mechanism 1).
327
+ same report.
330
328
  The supplied --disagreements document is refused, by name, when it is
331
329
  missing any EvidReconciliation field, and when its own recorded
332
330
  runIdentity (image_sha256/argv_digest/seed) matches no row in the
@@ -342,9 +340,9 @@ verbs:
342
340
  candidate queue uses), the fixture's own execution disposition read
343
341
  from --manifest (a NOT EXECUTED fixture renders that fact by name,
344
342
  never a clean bill of health), and the disagreement input verbatim.
345
- Never prints a percentage, rate or combined figure (D-05's own rule,
346
- applied here too). --json prints the raw JSON answer instead of the
347
- rendered report.
343
+ Never prints a percentage, rate or combined figure -- the same rule
344
+ this CLI applies to every verb's own report. --json prints the raw
345
+ JSON answer instead of the rendered report.
348
346
  Requires an EXISTING annotation store, an EXISTING --disagreements
349
347
  document and an EXISTING --manifest file; creates none and writes
350
348
  nothing.
@@ -370,7 +368,7 @@ verbs:
370
368
 
371
369
  Every verb requires inputs that already exist. None creates a project, a
372
370
  store or a sidecar, and none derives one path from another -- this CLI
373
- never guesses (D-02).
371
+ never guesses.
374
372
  `;
375
373
 
376
374
  function errMsg(err: unknown): string {
@@ -378,7 +376,7 @@ function errMsg(err: unknown): string {
378
376
  }
379
377
 
380
378
  /**
381
- * IN-06 (D-11.1-04): the ONE declared verb-to-accepted-options fact in this
379
+ * The ONE declared verb-to-accepted-options fact in this
382
380
  * file. Every option every verb's own code actually reads is listed here --
383
381
  * ground truth, not merely what USAGE happens to say.
384
382
  *
@@ -396,8 +394,8 @@ function errMsg(err: unknown): string {
396
394
  *
397
395
  * `export-asm` deliberately carries NO assembler-facing option. It writes
398
396
  * source and runs no assembler, so there is no binary to name, no exit status
399
- * to surface and no flag that could imply either. `--ledger` (BUILD-05,
400
- * phase 46 plan 01) does not weaken that claim: it is an EVIDENCE-CARRYING
397
+ * to surface and no flag that could imply either. `--ledger` does not weaken
398
+ * that claim: it is an EVIDENCE-CARRYING
401
399
  * INPUT, exactly like `--store`, never an assembler-facing option -- it names
402
400
  * a file to READ, not a way to run or configure an assembler.
403
401
  */
@@ -411,7 +409,7 @@ export const VERB_OPTIONS: Readonly<Record<string, readonly string[]>> = Object.
411
409
  });
412
410
 
413
411
  /**
414
- * The one shared refusal check IN-06 generalises to every verb (WR-08's
412
+ * The one shared refusal check generalises to every verb (the same
415
413
  * closed-option-set posture, applied uniformly rather than verb by verb).
416
414
  * Scans `rest` for any `--flag`-shaped token not in `verb`'s accepted set
417
415
  * from `VERB_OPTIONS` and returns a one-line refusal naming the flag and the
@@ -421,8 +419,8 @@ export const VERB_OPTIONS: Readonly<Record<string, readonly string[]>> = Object.
421
419
  * message). Never throws -- this file's never-throw posture applies here
422
420
  * too.
423
421
  *
424
- * THE LOOKUP IS AN OWN-PROPERTY READ, AND THAT IS THE WHOLE POINT (30-REVIEW
425
- * CR-01, fixed 2026-08-31). `VERB_OPTIONS` is an object literal, so it
422
+ * THE LOOKUP IS AN OWN-PROPERTY READ, AND THAT IS THE WHOLE POINT (fixed
423
+ * 2026-08-31, after a real crash reproduced it). `VERB_OPTIONS` is an object literal, so it
426
424
  * inherits from `Object.prototype`; a bare `VERB_OPTIONS[verb]` resolved
427
425
  * `hasOwnProperty`, `toString`, `constructor`, `valueOf` and `__proto__` to
428
426
  * TRUTHY inherited FUNCTIONS. Those sailed past the `if (!accepted) return
@@ -461,17 +459,17 @@ export function checkAcceptedOptions(verb: string, rest: string[]): string | und
461
459
  * passed `--force`. Called by the TWO verbs that write a single output FILE
462
460
  * -- `cmdRenderMemmap()` (non-`--check` branch only; `--check` never writes)
463
461
  * and `cmdCoverage()` -- so overwrite safety is uniform across both rather
464
- * than one verb accreting a check the other lacks (CR-01/CR-02).
462
+ * than one verb accreting a check the other lacks.
465
463
  *
466
464
  * "SHARED BY EVERY VERB THAT WRITES AN OUTPUT FILE" IS WHAT THIS DOC USED TO
467
465
  * SAY, AND IT WAS NOT TRUE. `render-memmap` wrote an output file and had
468
466
  * neither `--force` in its option set nor a call to this function anywhere on
469
- * its path; `29-REVIEW.md` CR-02 reproduced it destroying a pre-existing file
467
+ * its path; an independent review reproduced it destroying a pre-existing file
470
468
  * silently, exit code 0. The claim is now stated as the TWO call sites it
471
469
  * actually has, because a count is checkable where "every" is not.
472
470
  *
473
471
  * "THE TWO CALL SITES" IS WHAT THIS SENTENCE SAID UNTIL 2026-08-31, AFTER
474
- * `cmdExportAsm()` BECAME THE THIRD (30-REVIEW WR-08). The paragraph directly
472
+ * `cmdExportAsm()` BECAME THE THIRD. The paragraph directly
475
473
  * above had been updated to name all three; this one, whose entire point is
476
474
  * that a COUNT is checkable where "every" is not, was left carrying a stale
477
475
  * count -- the failure mode it exists to argue against, reproduced in
@@ -480,10 +478,10 @@ export function checkAcceptedOptions(verb: string, rest: string[]): string | und
480
478
  * confinement seam, so the next verb to write an output file cannot leave this
481
479
  * number behind again.
482
480
  *
483
- * BACK DOWN TO TWO, phase 47 plan 47-05: `export-asm`'s `--out` was promoted
484
- * from a FILE to a DIRECTORY (D47-A). A directory's overwrite question --
481
+ * BACK DOWN TO TWO, after `export-asm`'s `--out` was promoted
482
+ * from a FILE to a DIRECTORY. A directory's overwrite question --
485
483
  * does this directory already hold something, and may `force` replace it --
486
- * is `exportAsmTree()`'s own output-directory contract (phase 47 plan 47-02),
484
+ * is `exportAsmTree()`'s own output-directory contract,
487
485
  * never this function's single-file question, so `cmdExportAsm()` dropped its
488
486
  * call here rather than reshaping a file-shaped check to fit a directory. The
489
487
  * count this doc states, and the count `anno-cli.test.ts` checks mechanically,
@@ -506,7 +504,7 @@ function refuseOverwrite(outPath: string, force: boolean | undefined, verbLabel:
506
504
 
507
505
  /**
508
506
  * THE ONE "is this token a value, or the next flag?" TEST, shared by all THREE
509
- * option parsers below (30-REVIEW WR-09, fixed 2026-08-31).
507
+ * option parsers below (fixed 2026-08-31, after a real failure reproduced it).
510
508
  *
511
509
  * The `*MissingValue` mechanism exists precisely to avoid "silently swallowing
512
510
  * the next token" when an option is given without its value. Until this
@@ -545,7 +543,7 @@ interface RenderMemmapParsedArgs {
545
543
  }
546
544
 
547
545
  /** Fixed, closed option set for render-memmap -- exactly `--provenance`,
548
- * `--out`, `--force` and `--check`. Per WR-08's posture (do not silently
546
+ * `--out`, `--force` and `--check`. Per this file's closed-option-set posture (do not silently
549
547
  * accept a flag a verb does not implement, or a flag missing its value), any
550
548
  * OTHER `--flag`-shaped token is refused as `unknownOption`, and
551
549
  * `--provenance`/`--out` with no value (or a flag-shaped "value") is refused
@@ -597,25 +595,25 @@ function parseRenderMemmapArgs(rest: string[]): RenderMemmapParsedArgs {
597
595
 
598
596
  /**
599
597
  * `render-memmap <store> --provenance FILE [--out FILE] [--force] [--check]`
600
- * -- D-24's generated-view verb, via `anno-memmap-render.ts`'s
598
+ * -- the generated-view verb, via `anno-memmap-render.ts`'s
601
599
  * `renderMemoryMap()`/`checkRenderedMemoryMap()`. Never writes a file when
602
600
  * `--check` is given -- that mode only reads and reports.
603
601
  *
604
602
  * ALL THREE OF THIS VERB'S PATHS ARE CONFINED, and the reason each one is
605
603
  * named here rather than left to a reader to infer is that two of them were
606
- * NOT, and shipped that way. `29-VERIFICATION.md` gap 3 / `29-REVIEW.md`
607
- * CR-02 and CR-03 reproduced both on this tree:
604
+ * NOT, and shipped that way. An independent verification pass
605
+ * reproduced both on this tree:
608
606
  *
609
607
  * - `--out` reached `writeFileSync` as the RAW caller string. Pointed
610
608
  * outside the workspace root it exited 0, printed `wrote /tmp/.../
611
609
  * PRECIOUS.md` and replaced that pre-existing file's bytes. `--force`
612
610
  * was not in this verb's option set at all, so `refuseOverwrite()` --
613
611
  * whose own doc claims the safety is uniform across every verb that
614
- * writes an output file -- was never reached from here (CR-02).
612
+ * writes an output file -- was never reached from here.
615
613
  * - `--provenance` reached `readFileSync` as the RAW caller string, making
616
614
  * it an arbitrary-file read oracle; the sidecar parse failure then
617
615
  * interpolated Node's own parse error, which carries a snippet of the
618
- * file, so the oracle DISCLOSED CONTENT (CR-03). Confining it here also
616
+ * file, so the oracle DISCLOSED CONTENT. Confining it here also
619
617
  * confines it for `anno-memmap-render.ts`, which reads it with no check
620
618
  * of its own.
621
619
  *
@@ -635,7 +633,7 @@ function parseRenderMemmapArgs(rest: string[]): RenderMemmapParsedArgs {
635
633
  * fails when the inventory and the surface disagree in either direction, or
636
634
  * when this file's confinement call sites number fewer than the inventory's
637
635
  * entries. It does not associate a particular argument with a particular call
638
- * site (WR-02), so six arguments confined once each and five confined with one
636
+ * site, so six arguments confined once each and five confined with one
639
637
  * of them confined twice read the same to it; that limit is named here rather
640
638
  * than papered over. A header that asserts a property must point at the
641
639
  * mechanism that keeps it, and must claim no more than the mechanism checks.
@@ -699,7 +697,7 @@ async function cmdRenderMemmap(rest: string[]): Promise<number> {
699
697
  return 1;
700
698
  }
701
699
 
702
- // CR-03. The sidecar is confined BEFORE the existence check, so a path
700
+ // The sidecar is confined BEFORE the existence check, so a path
703
701
  // outside the workspace root never reaches the filesystem at all -- not as
704
702
  // an `existsSync` probe (which is itself an oracle: it answers "does this
705
703
  // file exist" for any path the process can stat) and not as the
@@ -718,7 +716,7 @@ async function cmdRenderMemmap(rest: string[]): Promise<number> {
718
716
  return 1;
719
717
  }
720
718
 
721
- // CR-02. The default is applied FIRST and the result confined AFTER, so the
719
+ // The default is applied FIRST and the result confined AFTER, so the
722
720
  // derived path and a caller-supplied one are confined by the same rule --
723
721
  // rather than the default being trusted because this verb computed it.
724
722
  let outPath: string;
@@ -751,7 +749,7 @@ async function cmdRenderMemmap(rest: string[]): Promise<number> {
751
749
  return 1;
752
750
  }
753
751
 
754
- // CR-02, the second half. `--check` never writes, so the overwrite refusal
752
+ // The second half of that same fix. `--check` never writes, so the overwrite refusal
755
753
  // belongs on THIS branch only -- and it runs against the CONFINED path, so
756
754
  // the file it protects is the file that would actually be written.
757
755
  if (!refuseOverwrite(outPath, force, "render-memmap")) {
@@ -768,7 +766,7 @@ async function cmdRenderMemmap(rest: string[]): Promise<number> {
768
766
  try {
769
767
  writeFileSync(outPath, rendered.markdown);
770
768
  } catch (err) {
771
- // WR-09 (D-11.1-04): the same shape as bootstrapProject()'s write above,
769
+ // The same shape as bootstrapProject()'s write above,
772
770
  // one verb over -- an ordinary write failure (missing parent directory,
773
771
  // permissions, full disk) must not throw past this verb's own
774
772
  // never-throw contract.
@@ -795,7 +793,7 @@ interface CoverageParsedArgs {
795
793
  }
796
794
 
797
795
  /** Fixed, closed option set for coverage -- exactly `--store`, `--out`,
798
- * `--force` and `--sample`. Same WR-08 posture as `parseRenderMemmapArgs()`
796
+ * `--force` and `--sample`. Same closed-option-set posture as `parseRenderMemmapArgs()`
799
797
  * above: an unimplemented flag is refused as `unknownOption`, and
800
798
  * `--store`/`--out`/`--sample` with a missing or flag-shaped value are refused
801
799
  * through their own `*MissingValue` fields rather than silently swallowing the
@@ -986,7 +984,7 @@ function addressList(addresses: readonly number[], cap = 12): string {
986
984
  /**
987
985
  * Renders the report as separately-headed sections.
988
986
  *
989
- * THE ONE RULE THIS FUNCTION EXISTS TO HOLD (COV-01, and the reason the
987
+ * THE ONE RULE THIS FUNCTION EXISTS TO HOLD (and the reason the
990
988
  * rendering lives here rather than being a generic pretty-printer): print
991
989
  * every measure's own numbers under its own heading, and never compute a
992
990
  * combined figure at the point of display. `anno-coverage.ts`'s report
@@ -1106,21 +1104,21 @@ function printCoverageReport(report: CoverageReport): void {
1106
1104
 
1107
1105
  /**
1108
1106
  * `coverage <image> --store FILE [--out FILE] [--force] [--sample N]` --
1109
- * COV-01's delivery path: the instrument from `anno-coverage.ts`, run against
1107
+ * This verb's delivery path: the instrument from `anno-coverage.ts`, run against
1110
1108
  * a real program and a real annotation store.
1111
1109
  *
1112
1110
  * TWO PATHS, NEITHER DERIVED FROM THE OTHER. `<image>` carries the payload
1113
1111
  * bytes and the load origin; `--store` names the annotation store holding the
1114
1112
  * labels, comments and typed ranges. The store holds annotations and never
1115
1113
  * bytes, so a derived measure has to be told which bytes it is measuring, and
1116
- * guessing one path from the other is exactly the auto-pick D-02 forbids.
1114
+ * guessing one path from the other is exactly the auto-pick this file forbids.
1117
1115
  *
1118
1116
  * Two properties this function must keep:
1119
1117
  * - NO SECOND PATH VALIDATOR (T-19-22 / T-29-28), over ALL THREE of this
1120
1118
  * verb's caller-supplied paths -- the positional, `--store` and `--out`.
1121
1119
  * The count is stated because it was WRONG: this doc said "both" and meant
1122
1120
  * it, while `--out` reached `refuseOverwrite()` and `writeFileSync()` as
1123
- * the raw caller string. `29-REVIEW.md` CR-02 reproduced the escape --
1121
+ * the raw caller string. An independent review reproduced the escape --
1124
1122
  * `coverage <project> --store <store> --out /tmp/...` wrote the report
1125
1123
  * outside the workspace root. All three now go through
1126
1124
  * `storePathWithinWorkspace()` against `repoRoot()` -- the one seam, the
@@ -1133,7 +1131,7 @@ function printCoverageReport(report: CoverageReport): void {
1133
1131
  * synopsis line -- and fails when that inventory and the surface disagree
1134
1132
  * either way, or when this file's confinement call sites number fewer
1135
1133
  * than the inventory's entries. It does not associate a given argument
1136
- * with a given call site (WR-02), so it cannot tell six arguments
1134
+ * with a given call site, so it cannot tell six arguments
1137
1135
  * confined once each from five confined with one confined twice.
1138
1136
  * - THE STORE IS OPENED ONCE, read-only, for the whole verb, and closed in a
1139
1137
  * `finally`. `mustExist` is what makes "the annotations are gone" and
@@ -1189,10 +1187,10 @@ async function cmdCoverage(rest: string[]): Promise<number> {
1189
1187
  return 1;
1190
1188
  }
1191
1189
 
1192
- // T-19-22 / T-29-28 / CR-02: the ONE confinement seam, for ALL THREE
1190
+ // The ONE confinement seam, for ALL THREE
1193
1191
  // caller-supplied paths. Never a second hand-rolled one, and never a
1194
1192
  // different rule for the store than for the program it annotates -- or, as
1195
- // CR-02 found, no rule at all for the report this verb writes.
1193
+ // an earlier review found, no rule at all for the report this verb writes.
1196
1194
  const workspaceRoot = repoRoot();
1197
1195
  let projectPath: string;
1198
1196
  let storePath: string;
@@ -1284,7 +1282,7 @@ async function cmdCoverage(rest: string[]): Promise<number> {
1284
1282
  if (!report.project.payloadDecoded) {
1285
1283
  // Not a low measurement -- an unreadable payload means every byte-side
1286
1284
  // measure above was computed over nothing. Reported as the caller-facing
1287
- // failure it is, AFTER the report, so the reason is on screen (COV-02).
1285
+ // failure it is, AFTER the report, so the reason is on screen.
1288
1286
  console.error(`coverage: the project's payload was UNAVAILABLE -- ${report.project.reason ?? "reason not recorded"}`);
1289
1287
  return 1;
1290
1288
  }
@@ -1297,7 +1295,7 @@ interface ExportAsmParsedArgs {
1297
1295
  storeMissingValue?: boolean;
1298
1296
  out?: string;
1299
1297
  outMissingValue?: boolean;
1300
- /** BUILD-05 (phase 46 plan 01): the ledger `c64-provenance-diff` generates
1298
+ /** The ledger `c64-provenance-diff` generates
1301
1299
  * (`recovery/PROVENANCE.md`). OPTIONAL -- see `ExportAsmOptions.ledgerPath`
1302
1300
  * in `anno-export-asm.ts` for why. */
1303
1301
  ledger?: string;
@@ -1307,7 +1305,7 @@ interface ExportAsmParsedArgs {
1307
1305
  }
1308
1306
 
1309
1307
  /** Fixed, closed option set for export-asm -- exactly `--store`, `--out`,
1310
- * `--ledger` and `--force`. The SAME WR-08 posture, and deliberately the same
1308
+ * `--ledger` and `--force`. The SAME closed-option-set posture, and deliberately the same
1311
1309
  * SHAPE, as `parseRenderMemmapArgs()` and `parseCoverageArgs()` above rather
1312
1310
  * than a third convention: an unimplemented flag is refused as
1313
1311
  * `unknownOption`, and `--store`/`--out`/`--ledger` with a missing or
@@ -1370,8 +1368,8 @@ function parseExportAsmArgs(rest: string[]): ExportAsmParsedArgs {
1370
1368
  * is a GENERATED VIEW of the annotations, so it belongs beside the artefact it
1371
1369
  * was generated from. The image is an input this verb only reads.
1372
1370
  *
1373
- * NO EXTENSION, on purpose (phase 47 plan 47-05: `--out` was promoted from a
1374
- * FILE to a DIRECTORY, D47-A). This names a directory the tree is written
1371
+ * NO EXTENSION, on purpose (`--out` was promoted from a
1372
+ * FILE to a DIRECTORY). This names a directory the tree is written
1375
1373
  * INTO, never a file -- a name ending in `.a` would read as a file to every
1376
1374
  * human and every tool that inspects it, and the tree this verb writes is
1377
1375
  * not one. The stem is derived the same way it always was (whatever
@@ -1410,15 +1408,15 @@ function pathIsOrContains(containerPath: string, candidate: string): boolean {
1410
1408
  /**
1411
1409
  * `export-asm <image> --store FILE [--out DIR] [--ledger FILE] [--force]` --
1412
1410
  * a TREE of ACME source files for a program, emitted from its annotation
1413
- * store by `anno-export-asm.ts`'s `exportAsmTree()` (phase 47 plan 47-05,
1414
- * D47-A: `--out` promoted from a FILE to a DIRECTORY, decided at this plan's
1415
- * own checkpoint rather than left to fall out of implementation).
1411
+ * store by `anno-export-asm.ts`'s `exportAsmTree()` (`--out` promoted from a
1412
+ * FILE to a DIRECTORY, a decision made deliberately at a checkpoint rather
1413
+ * than left to fall out of implementation).
1416
1414
  *
1417
1415
  * EVERY ONE OF THIS VERB'S PATHS IS CONFINED, and the ORDER each step happens
1418
1416
  * in is the load-bearing part rather than the mere presence of the calls. It
1419
1417
  * follows `cmdRenderMemmap()`'s chain deliberately, because that chain is the
1420
- * corrected shape of three reproduced escapes (`29-VERIFICATION.md` gap 3 /
1421
- * `29-REVIEW.md` CR-02 and CR-03) on exactly the argument shapes this verb
1418
+ * corrected shape of three reproduced escapes (an independent verification
1419
+ * pass) on exactly the argument shapes this verb
1422
1420
  * has:
1423
1421
  *
1424
1422
  * - `<image>` and `--store` go through `storePathWithinWorkspace()` BEFORE
@@ -1426,12 +1424,12 @@ function pathIsOrContains(containerPath: string, candidate: string): boolean {
1426
1424
  * this file exist" for any path this process can reach -- so probing first
1427
1425
  * and confining second would leak that answer for a path the seam is about
1428
1426
  * to refuse.
1429
- * - `--ledger` (BUILD-05, phase 46 plan 01) joins that SAME confinement
1427
+ * - `--ledger` joins that SAME confinement
1430
1428
  * block, on the SAME terms, WHEN SUPPLIED -- it is a third input this run
1431
1429
  * reads, not a second-class one confined later or not at all.
1432
1430
  * - `--out`'s DEFAULT is applied FIRST and the result confined AFTER, so a
1433
1431
  * path this verb computed is confined by the same rule as one a caller
1434
- * supplied, rather than trusted because this verb computed it (CR-02).
1432
+ * supplied, rather than trusted because this verb computed it.
1435
1433
  * This is unchanged by the file-to-directory promotion: the confined
1436
1434
  * result now NAMES A DIRECTORY rather than a file, but it is confined by
1437
1435
  * the exact same call.
@@ -1441,8 +1439,9 @@ function pathIsOrContains(containerPath: string, candidate: string): boolean {
1441
1439
  * and `exportAsmTree()` -- so every printed line names the file or
1442
1440
  * directory that is actually on disk.
1443
1441
  * - The output directory may not BE, and may not CONTAIN, any of the three
1444
- * inputs (T-47-14, generalised from the single-file version's plain
1445
- * equality check, 30-REVIEW WR-05). `pathIsOrContains()` runs against
1442
+ * inputs -- generalised from the single-file version's plain
1443
+ * equality check, once this verb started writing a directory rather than
1444
+ * a file. `pathIsOrContains()` runs against
1446
1445
  * the CONFINED destination and each CONFINED input, so what it protects
1447
1446
  * is the input that would actually be read and the directory that would
1448
1447
  * actually be written into -- and `--force` does not lift this refusal,
@@ -1451,7 +1450,7 @@ function pathIsOrContains(containerPath: string, candidate: string): boolean {
1451
1450
  * writing".
1452
1451
  * - The output-directory's own overwrite question -- does it already hold
1453
1452
  * something, and may `--force` replace it -- is `exportAsmTree()`'s own
1454
- * contract (phase 47 plan 47-02), not a second check grown here. This
1453
+ * contract, not a second check grown here. This
1455
1454
  * verb adds no overwrite rule of its own for the directory as a whole.
1456
1455
  *
1457
1456
  * WHAT THIS VERB DOES NOT DO, stated here as well as in `USAGE` because a
@@ -1502,7 +1501,7 @@ async function cmdExportAsm(rest: string[]): Promise<number> {
1502
1501
  return 1;
1503
1502
  }
1504
1503
 
1505
- // T-30-15 / CR-03: the ONE confinement seam, on both input paths, BEFORE any
1504
+ // The ONE confinement seam, on both input paths, BEFORE any
1506
1505
  // filesystem probe. `openStore()` downstream is handed this same workspace
1507
1506
  // root, so its own confinement agrees by construction rather than by a
1508
1507
  // second rule. `--ledger` joins this SAME block, WHEN SUPPLIED -- confined
@@ -1541,7 +1540,7 @@ async function cmdExportAsm(rest: string[]): Promise<number> {
1541
1540
  return 1;
1542
1541
  }
1543
1542
 
1544
- // T-30-02 / CR-02. The default is applied FIRST and the RESULT confined,
1543
+ // The default is applied FIRST and the RESULT confined,
1545
1544
  // so the derived path and a caller-supplied one are confined by the same
1546
1545
  // rule.
1547
1546
  let outPath: string;
@@ -1553,8 +1552,8 @@ async function cmdExportAsm(rest: string[]): Promise<number> {
1553
1552
  }
1554
1553
 
1555
1554
  // THE OUTPUT DIRECTORY MAY NOT BE, AND MAY NOT CONTAIN, AN INPUT, AND
1556
- // `--force` DOES NOT OVERRIDE THIS (T-47-14, generalising 30-REVIEW WR-05's
1557
- // plain-equality refusal to containment now that `--out` names a directory
1555
+ // `--force` DOES NOT OVERRIDE THIS (generalising an earlier plain-equality
1556
+ // refusal to containment now that `--out` names a directory
1558
1557
  // a whole tree is written into). The single-file version of this refusal
1559
1558
  // existed because `outPath` was confined and overwrite-checked but never
1560
1559
  // COMPARED to the inputs, so `anno export-asm game.raw --store g.annostore
@@ -1563,7 +1562,7 @@ async function cmdExportAsm(rest: string[]): Promise<number> {
1563
1562
  // same mistake from one file to everything the directory would hold, so the
1564
1563
  // check widens from equality to containment with it: the directory may not
1565
1564
  // itself BE an input's own path, and no input may live INSIDE it.
1566
- // `--ledger` (BUILD-05, phase 46 plan 01) joins this SAME check: it is a
1565
+ // `--ledger` joins this SAME check: it is a
1567
1566
  // THIRD input this run reads, and `--force` must not lift the refusal for
1568
1567
  // it any more than it lifts it for the store or the image.
1569
1568
  //
@@ -1594,8 +1593,8 @@ async function cmdExportAsm(rest: string[]): Promise<number> {
1594
1593
 
1595
1594
  // The output-directory contract itself -- create when missing, refuse a
1596
1595
  // non-empty directory without `--force`, and with `--force` replace only
1597
- // the names this export produces -- lives entirely in `exportAsmTree()`
1598
- // (phase 47 plan 47-02). This verb adds no second overwrite rule of its
1596
+ // the names this export produces -- lives entirely in `exportAsmTree()`.
1597
+ // This verb adds no second overwrite rule of its
1599
1598
  // own: it forwards the request and reports the library's own refusal
1600
1599
  // through this same single-line error path every other exporter refusal
1601
1600
  // already takes.
@@ -1638,7 +1637,7 @@ interface EvidDisagreementsParsedArgs {
1638
1637
  }
1639
1638
 
1640
1639
  /** Fixed, closed option set for evid-disagreements -- exactly `--store` and
1641
- * `--json`. Same WR-08 posture as every other verb's own parser: an
1640
+ * `--json`. Same closed-option-set posture as every other verb's own parser: an
1642
1641
  * unimplemented flag is refused as `unknownOption`, and `--store` with a
1643
1642
  * missing or flag-shaped value is refused through its own `*MissingValue`
1644
1643
  * field rather than silently swallowing the next token. `--json` is a plain
@@ -1709,8 +1708,8 @@ function printEvidDisagreementsReport(storePath: string, r: EvidReconciliation):
1709
1708
  }
1710
1709
 
1711
1710
  /**
1712
- * `evid-disagreements --store FILE [--json]` -- the CLI route for EVID-03's
1713
- * disagreement query (`43-RESEARCH.md` Open Question 3): the criterion that
1711
+ * `evid-disagreements --store FILE [--json]` -- the CLI route for the
1712
+ * disagreement query: the criterion that
1714
1713
  * settles the question is that a planted test needs the disagreement,
1715
1714
  * agreement and silence states rendered as three DIFFERENT pieces of TEXT
1716
1715
  * it can tell apart, which an MCP tool's JSON answer can only be inspected
@@ -1769,10 +1768,10 @@ async function cmdEvidDisagreements(rest: string[]): Promise<number> {
1769
1768
  return 1;
1770
1769
  }
1771
1770
  let reconciliation: EvidReconciliation;
1772
- // Rule 2 (missing critical functionality), phase 45 plan 45-01: `runIdentity`
1771
+ // Rule 2 (missing critical functionality): `runIdentity`
1773
1772
  // is NOT an `EvidReconciliation` field -- it rides alongside the spread
1774
1773
  // reconciliation in the JSON envelope, exactly like `store` already does.
1775
- // Added so `decomp-completeness` (D-09 mechanism 1/2) has a run identity to
1774
+ // Added so `decomp-completeness` has a run identity to
1776
1775
  // validate this document against the SAME store's own `anno_evid_runs`
1777
1776
  // table, rather than accepting a fabricated or foreign empty document as
1778
1777
  // this run's own answer. `null` when the store holds zero or more than one
@@ -1804,15 +1803,15 @@ async function cmdEvidDisagreements(rest: string[]): Promise<number> {
1804
1803
  }
1805
1804
 
1806
1805
  // ---------------------------------------------------------------------------
1807
- // decomp-completeness (D-07, phase 45 plan 45-01) -- the fifth verb.
1806
+ // decomp-completeness -- the fifth verb.
1808
1807
  // ---------------------------------------------------------------------------
1809
1808
 
1810
1809
  /**
1811
- * The frozen survivor prefix set (phase 45 plan 45-01 task 2), measured
1810
+ * The frozen survivor prefix set, measured
1812
1811
  * against a real dxa+Ghidra-derived store rather than against roadmap prose
1813
- * alone -- see docs/phase45-wave0-measurements.md for the MEASURED label
1814
- * population (zero labels; derivation writes typed ranges and xrefs, never
1815
- * names) and the reasoning this set was frozen against. `AUTO_NAME_PREFIX_RE`
1812
+ * alone -- MEASURED against a zero-label population (derivation writes typed
1813
+ * ranges and xrefs, never names) and the reasoning this set was frozen
1814
+ * against. `AUTO_NAME_PREFIX_RE`
1816
1815
  * (imported from anno-coverage.ts, NEVER restated as a second literal here --
1817
1816
  * a census over this file for any of its own eleven prefix strings returns
1818
1817
  * zero, proving that) covers the eleven upstream-analyser-shaped
@@ -1837,8 +1836,8 @@ function isSurvivorLabelName(name: string): boolean {
1837
1836
  return AUTO_NAME_PREFIX_RE.test(name) || SURVIVOR_EXTRA_RE.test(name);
1838
1837
  }
1839
1838
 
1840
- /** One row of the manifest `anno decomp-completeness --manifest FILE` reads
1841
- * (D-13). `path` is relative to `src/mcp/vice/fixtures`; `reason` is
1839
+ /** One row of the manifest `anno decomp-completeness --manifest FILE` reads.
1840
+ * `path` is relative to `src/mcp/vice/fixtures`; `reason` is
1842
1841
  * required (non-empty) when `execution` is `"not-executed"` and `null`
1843
1842
  * otherwise. */
1844
1843
  interface DecompExecutionManifestEntry {
@@ -1863,16 +1862,16 @@ function fixtureStem(path: string): string {
1863
1862
  return base.replace(/\.[^./]+$/, "");
1864
1863
  }
1865
1864
 
1866
- /** The subset of `EvidReconciliation` (verbatim field names, never renamed --
1867
- * D-10) that a `--disagreements` document must carry for
1865
+ /** The subset of `EvidReconciliation` (verbatim field names, never renamed)
1866
+ * that a `--disagreements` document must carry for
1868
1867
  * `decomp-completeness` to accept it as real, plus the `runIdentity` this
1869
1868
  * verb (via `cmdEvidDisagreements`'s own `--json` branch) adds alongside it.
1870
1869
  * `disagreementInput` in the `--json` answer below is exactly this shape. */
1871
1870
  interface DecompDisagreementInput extends EvidReconciliation {
1872
- // Phase 45, plan 45-06 (Rule 1 fix, disclosed): `null` is a THIRD, LEGITIMATE
1871
+ // (Rule 1 fix, disclosed): `null` is a THIRD, LEGITIMATE
1873
1872
  // value here -- `anno evid-disagreements --json`'s own `runIdentity` field
1874
1873
  // reads `null` when the store holds zero observed runs (listObservedRuns()),
1875
- // which is exactly the real, non-fabricated answer a D-13 non-executed
1874
+ // which is exactly the real, non-fabricated answer a non-executed
1876
1875
  // fixture's store produces. Refusing null unconditionally made a real
1877
1876
  // `anno evid-disagreements --json` answer for a non-executed fixture
1878
1877
  // unusable by this verb, contradicting this phase's own must_haves ("a
@@ -1899,28 +1898,28 @@ const EVID_RECONCILIATION_FIELDS = [
1899
1898
  /**
1900
1899
  * Validates a parsed `--disagreements` document has every `EvidReconciliation`
1901
1900
  * field AND a complete `runIdentity` -- refusing BY NAME, never silently
1902
- * treating a missing field as an empty answer (D-09 mechanism 2: a required
1901
+ * treating a missing field as an empty answer (a required
1903
1902
  * output-schema field only the real `--disagreements` input can populate).
1904
1903
  * Returns the validated document (typed as `DecompDisagreementInput`) or a
1905
1904
  * refusal message string. Never throws.
1906
1905
  */
1907
1906
  function validateDisagreementDocumentShape(doc: unknown): DecompDisagreementInput | string {
1908
1907
  if (typeof doc !== "object" || doc === null) {
1909
- return "decomp-completeness: the --disagreements document is not a JSON object -- refusing to render (D-09)";
1908
+ return "decomp-completeness: the --disagreements document is not a JSON object -- refusing to render";
1910
1909
  }
1911
1910
  const bag = doc as Record<string, unknown>;
1912
1911
  for (const field of EVID_RECONCILIATION_FIELDS) {
1913
1912
  if (!(field in bag)) {
1914
1913
  return (
1915
1914
  `decomp-completeness: the --disagreements document is missing the "${field}" field -- ` +
1916
- "this is not a real anno evid-disagreements --json answer, refusing to render (D-09)"
1915
+ "this is not a real anno evid-disagreements --json answer, refusing to render"
1917
1916
  );
1918
1917
  }
1919
1918
  }
1920
1919
  const runIdentity = bag.runIdentity;
1921
- // Rule 1 fix (disclosed, plan 45-06): `null` is accepted HERE as a
1920
+ // Rule 1 fix (disclosed): `null` is accepted HERE as a
1922
1921
  // well-formed shape -- it is `anno evid-disagreements --json`'s own real
1923
- // answer for a store with zero observed runs (a D-13 non-executed
1922
+ // answer for a store with zero observed runs (a non-executed
1924
1923
  // fixture). It is NOT yet accepted as a legitimate ANSWER: cmdDecompCompleteness's
1925
1924
  // own match-check below still refuses a null identity unless the store's
1926
1925
  // evid-runs table is ALSO genuinely empty, so a store that DOES carry real
@@ -1935,7 +1934,7 @@ function validateDisagreementDocumentShape(doc: unknown): DecompDisagreementInpu
1935
1934
  return (
1936
1935
  "decomp-completeness: the --disagreements document carries no complete runIdentity " +
1937
1936
  "(image_sha256/argv_digest/seed) -- an empty or ambiguous-run document is refused rather than " +
1938
- "rendered as \"no disagreements\" (D-09 mechanism 2, RESEARCH.md Pitfall 9)"
1937
+ "rendered as \"no disagreements\""
1939
1938
  );
1940
1939
  }
1941
1940
  }
@@ -1943,12 +1942,12 @@ function validateDisagreementDocumentShape(doc: unknown): DecompDisagreementInpu
1943
1942
  }
1944
1943
 
1945
1944
  // ---------------------------------------------------------------------------
1946
- // The full measure set (phase 45 plan 45-04, task 1) -- rangeProvenance
1947
- // (D-10), entryPoints, referencedAddresses (criterion 4) and
1948
- // disagreementResolution (D-09's own gate-vs-bulletin distinction).
1945
+ // The full measure set -- rangeProvenance
1946
+ // (typed by evidence, never inferred), entryPoints, referencedAddresses and
1947
+ // disagreementResolution (the gate-vs-bulletin distinction).
1949
1948
  // ---------------------------------------------------------------------------
1950
1949
 
1951
- /** One typed range's provenance classification (D-10 mechanism 2). Always
1950
+ /** One typed range's provenance classification. Always
1952
1951
  * one of the three named values -- never a fourth, never a boolean. */
1953
1952
  type RangeTypedBy = "observed-executing" | "byte-derived" | "authored";
1954
1953
 
@@ -1957,7 +1956,7 @@ interface RangeProvenanceRow {
1957
1956
  endInclusive: number;
1958
1957
  dataType: DataType;
1959
1958
  /** `dataType` unless it is one of the four `SPLIT_DATA_TYPES` members, in
1960
- * which case it renders as `"table"` (D-11) -- read from `anno-types.ts`'s
1959
+ * which case it renders as `"table"` -- read from `anno-types.ts`'s
1961
1960
  * own `isSplitDataType()`, NEVER a restated literal, so the four split
1962
1961
  * spellings never appear in this file's own source as strings. */
1963
1962
  renderedType: string;
@@ -2100,7 +2099,7 @@ function commentReasonAfterPrefix(comments: readonly CommentRow[], address: numb
2100
2099
  return found ? found.text.slice(prefix.length).trim() : null;
2101
2100
  }
2102
2101
 
2103
- /** D-10 mechanism 2: how ONE typed range was typed. Evidence beats
2102
+ /** How ONE typed range was typed. Evidence beats
2104
2103
  * inference, stated as a fixed precedence that must never be reordered:
2105
2104
  * `observed-executing` (at least one real execute observation falls inside
2106
2105
  * the range) beats `authored` (the range's start address carries an
@@ -2112,7 +2111,7 @@ function typedByFor(hasObservation: boolean, hasAuthoredComment: boolean): Range
2112
2111
  return "byte-derived";
2113
2112
  }
2114
2113
 
2115
- /** Builds `rangeProvenance` (D-10 mechanism 2): one row per typed range,
2114
+ /** Builds `rangeProvenance`: one row per typed range,
2116
2115
  * sorted ascending by `start` then `endInclusive` (ranges never overlap, so
2117
2116
  * this is already the input order once `ranges` itself is pre-sorted, but
2118
2117
  * the sort is restated here so this function's OWN output contract does not
@@ -2145,7 +2144,7 @@ function buildRangeProvenance(
2145
2144
  * address (`image.origin`) -- the fixture's own natural entry point.
2146
2145
  * Sorted ascending by address.
2147
2146
  *
2148
- * `image === null` (45-REVIEW WR-02, fixed 2026-09-11) means the fixture's
2147
+ * `image === null` (fixed 2026-09-11) means the fixture's
2149
2148
  * own bytes could not be located: no instructions are decoded and NO
2150
2149
  * `image.origin` candidate is added -- a missing image degrades this to
2151
2150
  * whatever the store's own stored `xrefs` already establish, never a
@@ -2204,7 +2203,7 @@ function buildEntryPoints(
2204
2203
  * decline's own reason), or `unresolved` (neither) -- sorted ascending by
2205
2204
  * address within each bucket.
2206
2205
  *
2207
- * `image === null` (45-REVIEW WR-02, fixed 2026-09-11): no instructions are
2206
+ * `image === null` (fixed 2026-09-11): no instructions are
2208
2207
  * decoded, so this degrades to whatever the store's own stored `xrefs`
2209
2208
  * establish -- never fabricated from a placeholder image's bytes. */
2210
2209
  function buildReferencedAddresses(
@@ -2243,7 +2242,7 @@ function buildReferencedAddresses(
2243
2242
  return { resolved, declined, unresolved, denominator: resolved.length + declined.length + unresolved.length };
2244
2243
  }
2245
2244
 
2246
- /** Builds `disagreementResolution` (D-09's gate-vs-bulletin distinction,
2245
+ /** Builds `disagreementResolution` (the gate-vs-bulletin distinction,
2247
2246
  * criterion 2): one row per disagreement the supplied `--disagreements`
2248
2247
  * document carries, `accepted` when the address carries a
2249
2248
  * `DISAGREEMENT_ACCEPTED_COMMENT_PREFIX` comment, `resolved` identically (the
@@ -2331,8 +2330,7 @@ function parseDecompCompletenessArgs(rest: string[]): DecompCompletenessParsedAr
2331
2330
  * unknown option -> refuse missing value -> refuse missing required argument
2332
2331
  * BY NAME -> `storePathWithinWorkspace()` every caller-supplied path -> open
2333
2332
  * the store `mustExist: true` -> gather -> `--json` branch or rendered
2334
- * branch. Three required arguments, none defaulted from another (D-09
2335
- * mechanism 1).
2333
+ * branch. Three required arguments, none defaulted from another.
2336
2334
  */
2337
2335
  async function cmdDecompCompleteness(rest: string[]): Promise<number> {
2338
2336
  const { store, storeMissingValue, disagreements, disagreementsMissingValue, manifest, manifestMissingValue, json, unknownOption } =
@@ -2366,7 +2364,7 @@ async function cmdDecompCompleteness(rest: string[]): Promise<number> {
2366
2364
  if (!disagreements) {
2367
2365
  console.error(
2368
2366
  "decomp-completeness: --disagreements FILE is required -- there is no default and no empty-array " +
2369
- "substitute; omitting the disagreement input must never render the same report as a real, empty answer (D-09).\n",
2367
+ "substitute; omitting the disagreement input must never render the same report as a real, empty answer.\n",
2370
2368
  );
2371
2369
  console.log(USAGE);
2372
2370
  return 1;
@@ -2374,7 +2372,7 @@ async function cmdDecompCompleteness(rest: string[]): Promise<number> {
2374
2372
  if (!manifest) {
2375
2373
  console.error(
2376
2374
  "decomp-completeness: --manifest FILE is required -- a fixture absent from the manifest is refused, " +
2377
- "never defaulted to \"executed\" (D-13).\n",
2375
+ "never defaulted to \"executed\".\n",
2378
2376
  );
2379
2377
  console.log(USAGE);
2380
2378
  return 1;
@@ -2445,7 +2443,7 @@ async function cmdDecompCompleteness(rest: string[]): Promise<number> {
2445
2443
  if (!manifestEntry) {
2446
2444
  console.error(
2447
2445
  `decomp-completeness: no fixture matching store ${JSON.stringify(basename(storePath))} (stem ${JSON.stringify(stem)}) ` +
2448
- `is listed in the manifest ${manifestPath} -- an unlisted fixture is refused, never defaulted to "executed" (D-13).`,
2446
+ `is listed in the manifest ${manifestPath} -- an unlisted fixture is refused, never defaulted to "executed".`,
2449
2447
  );
2450
2448
  return 1;
2451
2449
  }
@@ -2466,7 +2464,7 @@ async function cmdDecompCompleteness(rest: string[]): Promise<number> {
2466
2464
  byteCensus: { byType: Record<string, number>; undefinedCount: number; denominator: number; undefinedRanges: { start: number; endInclusive: number }[] };
2467
2465
  survivors: { address: number; name: string }[];
2468
2466
  rangeProvenance: RangeProvenanceRow[];
2469
- // 45-REVIEW WR-02 (fixed 2026-09-11): true when the fixture's own image
2467
+ // Fixed 2026-09-11: true when the fixture's own image
2470
2468
  // bytes could not be located -- see the fallback below. `entryPoints`/
2471
2469
  // `referencedAddresses` are DEGRADED (never fabricated) when this is
2472
2470
  // true: no synthetic `$0000` entry point is manufactured from a
@@ -2480,9 +2478,9 @@ async function cmdDecompCompleteness(rest: string[]): Promise<number> {
2480
2478
  try {
2481
2479
  const ranges = listRanges(handle);
2482
2480
  const { runs } = listObservedRuns(handle);
2483
- // Rule 1 fix (disclosed, plan 45-06): a `null` runIdentity is accepted
2481
+ // Rule 1 fix (disclosed): a `null` runIdentity is accepted
2484
2482
  // ONLY when the store's own evid-runs table is ALSO genuinely empty --
2485
- // the real, honest answer for a D-13 non-executed fixture. A store that
2483
+ // the real, honest answer for a non-executed fixture. A store that
2486
2484
  // DOES carry real runs must still supply a real, matching identity; the
2487
2485
  // anti-vacuity property this whole check exists for is unaffected.
2488
2486
  if (disagreementInput.runIdentity === null) {
@@ -2543,14 +2541,14 @@ async function cmdDecompCompleteness(rest: string[]): Promise<number> {
2543
2541
  .map((l) => ({ address: l.address, name: l.name }))
2544
2542
  .sort((a, b) => a.address - b.address);
2545
2543
 
2546
- // The full measure set (phase 45 plan 45-04). All four use the SAME
2544
+ // The full measure set. All four use the SAME
2547
2545
  // fixture bytes the derivation route itself read -- the fixtures-relative
2548
2546
  // manifest path, resolved beside this module (`fixtures/<manifestEntry.path>`),
2549
2547
  // never a second guess at where the image lives. An image that cannot be
2550
2548
  // located (never expected for a committed fixture, but never fabricated
2551
2549
  // either) degrades entryPoints/referencedAddresses to EMPTY -- never a
2552
2550
  // synthetic zero-length placeholder whose own `origin` (0) would read as
2553
- // a real `$0000` entry point (45-REVIEW WR-02, fixed 2026-09-11: the
2551
+ // a real `$0000` entry point (fixed 2026-09-11: the
2554
2552
  // placeholder's origin was previously unioned into the candidate set
2555
2553
  // unconditionally, fabricating a plausible-looking but fictitious
2556
2554
  // finding). `imageUnavailable` reports the condition BY NAME instead.
@@ -2598,8 +2596,8 @@ async function cmdDecompCompleteness(rest: string[]): Promise<number> {
2598
2596
  * Copies `printEvidDisagreementsReport()`'s rendering discipline exactly:
2599
2597
  * every measure under its own heading, disagreements first, `denominator`
2600
2598
  * beside every count, an explicit sentence stating what absence does NOT
2601
- * prove, and never a percentage, rate or combined figure (D-05's own rule,
2602
- * applied here too). This is the FALLBACK text renderer for a direct CLI
2599
+ * prove, and never a percentage, rate or combined figure -- the same rule
2600
+ * this CLI applies everywhere else. This is the FALLBACK text renderer for a direct CLI
2603
2601
  * invocation without `--json`; `completeness-report.mjs`'s
2604
2602
  * `renderCompletenessReport()` is the report the routine-queue-walker skill
2605
2603
  * actually reads, built from this same verb's `--json` answer.
@@ -2623,7 +2621,7 @@ function printDecompCompletenessReport(r: {
2623
2621
  if (r.executionDisposition === "not-executed") {
2624
2622
  console.log(` NOT EXECUTED: ${r.notExecutedReason ?? "(no reason recorded)"}`);
2625
2623
  } else {
2626
- console.log(" EXECUTED: this fixture was run under the reproducible-run protocol (REPRO-02).");
2624
+ console.log(" EXECUTED: this fixture was run under the reproducible-run protocol.");
2627
2625
  }
2628
2626
  console.log("");
2629
2627
  console.log(` BYTE CENSUS (denominator ${r.byteCensus.denominator})`);
@@ -2674,7 +2672,7 @@ function printDecompCompletenessReport(r: {
2674
2672
  }
2675
2673
  console.log("");
2676
2674
 
2677
- // 45-REVIEW WR-02 (fixed 2026-09-11): named BY NAME, not inferred from a
2675
+ // Fixed 2026-09-11: named BY NAME, not inferred from a
2678
2676
  // suspiciously-empty entryPoints/referencedAddresses census.
2679
2677
  if (r.imageUnavailable) {
2680
2678
  console.log(" IMAGE UNAVAILABLE: the fixture's own image bytes could not be located -- entryPoints and referencedAddresses below are degraded to what the store's own stored xrefs establish, never fabricated from a placeholder image.");
@@ -2730,7 +2728,7 @@ interface HazardReportParsedArgs {
2730
2728
  }
2731
2729
 
2732
2730
  /** Fixed, closed option set for hazard-report -- exactly `--store`,
2733
- * `--image` and `--json`. Same WR-08 posture as every other verb's own
2731
+ * `--image` and `--json`. Same closed-option-set posture as every other verb's own
2734
2732
  * parser: an unimplemented flag is refused as `unknownOption`, and an
2735
2733
  * option with a missing or flag-shaped value is refused through its own
2736
2734
  * `*MissingValue` field rather than silently swallowing the next token. */
@@ -2820,7 +2818,7 @@ function printHazardReport(storePath: string, imagePath: string, r: HazardReport
2820
2818
  else for (const region of unclassified) console.log(` ${hexAddr(region.start)}..${hexAddr(region.endInclusive)} (${region.reason ?? "no reason recorded"})`);
2821
2819
  console.log("");
2822
2820
 
2823
- // WR-03: rendered here so an operator reading ONLY the human-readable
2821
+ // Rendered here so an operator reading ONLY the human-readable
2824
2822
  // report (never --json) still sees the declined dispatch candidates
2825
2823
  // HazardReport's own doc comment insists must never be silently dropped --
2826
2824
  // "an honest decline indistinguishable from an absence" is exactly the
@@ -2981,13 +2979,13 @@ export async function runAnnoCli(argv: string[]): Promise<number> {
2981
2979
  }
2982
2980
 
2983
2981
  try {
2984
- // IN-06 (D-11.1-04): the single call site for the shared verb-options
2982
+ // The single call site for the shared verb-options
2985
2983
  // check, run BEFORE dispatch so a refused option never reaches any cmd*
2986
2984
  // function -- one place enforces the closed option set for every verb,
2987
2985
  // rather than seven places each doing (or, as `verify` proved, NOT doing)
2988
2986
  // it themselves.
2989
2987
  //
2990
- // INSIDE the try since 2026-08-31 (30-REVIEW CR-01, defence in depth).
2988
+ // INSIDE the try since 2026-08-31, as defence in depth.
2991
2989
  // It used to sit above this block, so a throw from it escaped
2992
2990
  // `runAnnoCli()` entirely -- which is exactly what a prototype-key verb
2993
2991
  // did. `checkAcceptedOptions()` is now own-property-safe and cannot
@@ -3015,12 +3013,12 @@ export async function runAnnoCli(argv: string[]): Promise<number> {
3015
3013
  case "hazard-report":
3016
3014
  return await cmdHazardReport(rest);
3017
3015
  default:
3018
- // WR-14 site 2, corrected 2026-08-30 (plan 29-16). This prefix read
3019
- // `anno:` -- the subcommand renamed to `anno` on 2026-08-29 (29-09)
3016
+ // Corrected 2026-08-30. This prefix read
3017
+ // `anno:` -- the subcommand renamed to `anno` on 2026-08-29
3020
3018
  // -- so a user who mistyped a verb was answered by a subcommand that
3021
3019
  // no longer dispatches. Only the STRING moved: the enclosing function
3022
3020
  // keeps its current name, so no consumer, test or record entry moves
3023
- // with it (see the plan's <wr14_scope_decision>).
3021
+ // with it.
3024
3022
  console.error(
3025
3023
  `anno: unknown verb "${verb}" -- this CLI has exactly six: render-memmap, coverage, export-asm, ` +
3026
3024
  "evid-disagreements, decomp-completeness and hazard-report\n",
@@ -3032,8 +3030,8 @@ export async function runAnnoCli(argv: string[]): Promise<number> {
3032
3030
  // A last-resort net: every expected failure path above already returns its
3033
3031
  // own code with its own message, so anything arriving here is unexpected
3034
3032
  // and is reported verbatim rather than swallowed. The loud failure is the
3035
- // point (D-07).
3036
- // WR-14 site 2, second half -- same correction, same reason.
3033
+ // point.
3034
+ // Second half of that same fix -- same correction, same reason.
3037
3035
  console.error(`anno: ${errMsg(err)}`);
3038
3036
  return 1;
3039
3037
  }