@henols/vice-mcp 0.2.2 → 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/README.md +2 -2
- package/THIRD-PARTY-NOTICES.md +422 -1
- package/anno-bank.ts +171 -0
- package/anno-cli.ts +1736 -163
- package/anno-confidence.ts +2 -2
- package/anno-derive.ts +6 -6
- package/anno-details.ts +4 -4
- package/anno-enum-gen.ts +416 -30
- package/anno-export-asm.ts +1211 -126
- package/anno-graphics.ts +338 -0
- package/anno-hazard-report.ts +1367 -0
- package/anno-import.ts +495 -0
- package/anno-index.ts +8 -8
- package/anno-join.ts +480 -0
- package/anno-memmap-render.ts +22 -21
- package/anno-provenance-ledger.ts +472 -0
- package/anno-regbits-gen.ts +13 -13
- package/anno-register.ts +159 -0
- package/anno-store-export.ts +661 -0
- package/anno-store.ts +635 -124
- package/anno-symbols.ts +7 -7
- package/anno-tools.ts +1169 -16
- package/anno-types.ts +313 -40
- package/backend-detect.mts +124 -312
- package/build.ts +3 -1
- package/capture-predicate.ts +597 -0
- package/channel-lock.ts +349 -0
- package/evid-ingest.ts +217 -0
- package/evid-reconcile.ts +316 -0
- package/host-tool-client.ts +430 -0
- package/incident-record.ts +23 -12
- package/install-resources.ts +29 -13
- package/memmap-lookup.ts +285 -0
- package/package.json +27 -8
- package/prg-image.ts +1 -2
- package/repo-root.ts +87 -3
- package/resources/backend-detect.mjs +98 -236
- package/resources/broker-control.mjs +220 -54
- package/resources/broker-epoch.mjs +7 -8
- package/resources/broker-kill.mjs +36 -31
- package/resources/broker-launch.mjs +511 -374
- package/resources/broker-state.mjs +69 -24
- package/resources/container-guard.mjs +1 -1
- package/resources/ghidra-project.mjs +790 -0
- package/resources/host-tool.mjs +2533 -0
- package/resources/vice-broker.mjs +434 -290
- package/resources/vice-launcher.sh +127 -9
- package/stock-address.ts +1 -1
- package/stock-condition.ts +1 -1
- package/stock-connect.ts +9 -5
- package/stock-derived.ts +29 -37
- package/stock-diagnose.ts +200 -36
- package/stock-dispatch.ts +179 -77
- package/stock-handler.ts +1 -1
- package/stock-paths.ts +18 -14
- package/stock-petscii.ts +1 -1
- package/stock-protocol.ts +1 -1
- package/stock-recycle.ts +83 -2
- package/stock-reproducible-run.ts +811 -0
- package/stock-run-until.ts +100 -1
- package/stock-symbols.ts +4 -4
- package/stock-timing.ts +1 -1
- package/stop-oracle.ts +167 -0
- package/text-capability-probe.ts +660 -0
- package/text-connect.ts +157 -0
- package/text-protocol.ts +810 -0
- package/text-tools.ts +778 -0
- package/textmon-backtrace.ts +385 -0
- package/textmon-cpuhistory.ts +335 -0
- package/textmon-memmap.ts +494 -0
- package/textmon-profile.ts +458 -0
- package/textmon-registers.ts +748 -0
- package/tools-manifest.stock.json +864 -3
- package/vice-broker-client.ts +253 -108
- package/vice-errors.ts +268 -0
- package/vice-proxy.ts +339 -2144
- package/vsf-slice.ts +640 -0
- package/anno-d64.ts +0 -310
- package/capability-registry.ts +0 -390
- package/refresh-manifest.ts +0 -124
- package/tools-manifest.json +0 -1223
- package/vice-probe.ts +0 -278
- package/vice-sync.ts +0 -336
- package/vice.ts +0 -772
package/anno-types.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
// The ONE place that writes down the annotation store's data-type vocabulary,
|
|
5
5
|
// its range row shape, and every validator the store runs before a caller's
|
|
6
|
-
// argument is allowed anywhere near SQL
|
|
6
|
+
// argument is allowed anywhere near SQL.
|
|
7
7
|
//
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
// WHY THIS FILE EXISTS
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
// path lands outside the root once symbolic links are followed, and that
|
|
59
59
|
// is a filesystem question that no string comparison can answer -- the
|
|
60
60
|
// earlier pure-string version accepted a symlinked subdirectory and let a
|
|
61
|
-
// store file be created outside the workspace root
|
|
62
|
-
//
|
|
61
|
+
// store file be created outside the workspace root, confirmed by a live
|
|
62
|
+
// symlink reproduction. Every OTHER export is still
|
|
63
63
|
// a pure function of its arguments and still unit-testable with no file
|
|
64
64
|
// on disk. The exception is named here, in `storePathWithinWorkspace`'s
|
|
65
65
|
// own doc comment, and in `anno-types.test.ts`'s mutable-state assertion
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
// `parseStoreAddress()` takes an integer, a `$hex` string and a
|
|
69
69
|
// `0x`/`0X` string, and refuses `"1024"`. This is a REAL, user-visible
|
|
70
70
|
// divergence from `stock-address.ts:155-160`, which accepts the bare
|
|
71
|
-
// decimal form AS DECIMAL
|
|
71
|
+
// decimal form AS DECIMAL and says so at
|
|
72
72
|
// `:89-105`. The reason the store diverges: a mis-based address written
|
|
73
73
|
// into the store is PERSISTENT and silently wrong -- every later reader
|
|
74
74
|
// inherits it -- whereas a mis-based memory read is transient and the
|
|
@@ -92,26 +92,28 @@
|
|
|
92
92
|
// sanitized or quoted."
|
|
93
93
|
// 8. NEVER restate the eleven auto-generated-name prefixes here. They live in
|
|
94
94
|
// exactly one place, `anno-coverage.ts`'s `AUTO_NAME_PREFIX_RE`, and
|
|
95
|
-
//
|
|
96
|
-
// five-prefix copy silently under-counts, which breaks the
|
|
95
|
+
// a short reimplementation causes the exact failure this project has
|
|
96
|
+
// already measured: a five-prefix copy silently under-counts, which breaks the
|
|
97
97
|
// `routine-queue-walker` skill's backlog construction while every test
|
|
98
98
|
// keeps passing. The store separates the two namespaces with its label
|
|
99
99
|
// `kind` field, not with a name pattern.
|
|
100
100
|
// 9. NEVER build a mnemonic-to-access-kind classifier here. Nothing derivable
|
|
101
101
|
// is stored (see `anno-store.ts`'s `putXref`), `OpcodeEntry` carries no
|
|
102
|
-
// access/reads/writes field at all, and
|
|
103
|
-
//
|
|
102
|
+
// access/reads/writes field at all, and analysis built on such a field is
|
|
103
|
+
// deliberately deferred until a named future consumer exists: the field
|
|
104
|
+
// is stored because it is free now and unrecoverable later, but no
|
|
105
|
+
// shipped caller reads it yet. A
|
|
104
106
|
// classifier written now would have no caller and no way to be wrong
|
|
105
107
|
// observably.
|
|
106
|
-
// 10. NEVER measure comment length in code units. `String.length` counts
|
|
107
|
-
// code units, so a multi-byte comment passes a code-unit check and then
|
|
108
|
+
// 10. NEVER measure comment length in code units. `String.length` counts
|
|
109
|
+
// 16-bit code units, so a multi-byte comment passes a code-unit check and then
|
|
108
110
|
// exceeds the byte bound on disk. `assertCommentText()` measures with a
|
|
109
111
|
// `TextEncoder`.
|
|
110
112
|
import { existsSync, lstatSync, readlinkSync, realpathSync } from "node:fs";
|
|
111
113
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
112
114
|
|
|
113
115
|
import { OPCODES } from "./disasm-opcodes.ts";
|
|
114
|
-
import { ViceError, type ViceErrorOptions } from "./vice.ts";
|
|
116
|
+
import { ViceError, type ViceErrorOptions } from "./vice-errors.ts";
|
|
115
117
|
|
|
116
118
|
/**
|
|
117
119
|
* The on-disk schema version every store file carries in `anno_meta`. A
|
|
@@ -133,26 +135,26 @@ import { ViceError, type ViceErrorOptions } from "./vice.ts";
|
|
|
133
135
|
*
|
|
134
136
|
* * TWO STORES IN ONE DIRECTORY SHARED ONE RING under the same
|
|
135
137
|
* `r<revision>.db` filenames, so `revertTo` on one store restored the
|
|
136
|
-
* OTHER store's whole database, silently and with no error
|
|
138
|
+
* OTHER store's whole database, silently and with no error.
|
|
137
139
|
* * RENAMING THE CONTAINING DIRECTORY invalidated every persisted absolute
|
|
138
140
|
* path at once, after which `retainedRevisions()` reported none and the
|
|
139
|
-
* next accepted write's prune destroyed the entire revert history
|
|
141
|
+
* next accepted write's prune destroyed the entire revert history.
|
|
140
142
|
*
|
|
141
143
|
* Version 2 drops `anno_snapshot.path` -- there is no persisted string left for
|
|
142
144
|
* a second namespace to disagree with -- and derives the location from the
|
|
143
145
|
* handle at every read and every delete via `snapshotDirFor()`.
|
|
144
146
|
*
|
|
145
147
|
* A VERSION-1 STORE IS REFUSED, NOT UPGRADED, and the reason is that the
|
|
146
|
-
* version-1 ring's OWNERSHIP is not recoverable:
|
|
148
|
+
* version-1 ring's OWNERSHIP is not recoverable: two stores may
|
|
147
149
|
* both have written into `<dir>/snapshots`, and nothing recorded which file
|
|
148
150
|
* belonged to which store. Any migration would have to guess, attributing one
|
|
149
|
-
* store's history to another --
|
|
150
|
-
* watching. The legacy directory is therefore left on disk untouched: never
|
|
151
|
+
* store's history to another -- the same failure shape recurring with a new
|
|
152
|
+
* cause and no test watching. The legacy directory is therefore left on disk untouched: never
|
|
151
153
|
* adopted, never migrated, never deleted, so the bytes stay recoverable by
|
|
152
154
|
* hand.
|
|
153
155
|
*
|
|
154
156
|
* ---------------------------------------------------------------------------
|
|
155
|
-
* VERSION 3, 2026-08-29
|
|
157
|
+
* VERSION 3, 2026-08-29 -- AND THE COST IS NAMED HERE RATHER THAN LEFT
|
|
156
158
|
* IN A PLANNING DIRECTORY, because a version number whose rationale lives
|
|
157
159
|
* somewhere else is a number the next reader has no way to weigh.
|
|
158
160
|
*
|
|
@@ -182,8 +184,158 @@ import { ViceError, type ViceErrorOptions } from "./vice.ts";
|
|
|
182
184
|
* exist, and it would put new code into a module six hardening rounds went
|
|
183
185
|
* into. Stating the difference is the point: this one is a choice, not an
|
|
184
186
|
* impossibility.
|
|
187
|
+
*
|
|
188
|
+
* ---------------------------------------------------------------------------
|
|
189
|
+
* VERSION 4, 2026-09-10 -- THE DECISION IS `reaffirm-refusal`, AND
|
|
190
|
+
* THE FACTUAL BASIS IS TRANSCRIBED HERE RATHER THAN LEFT IN A PLANNING
|
|
191
|
+
* DIRECTORY, for the same reason VERSION 3's paragraph gives.
|
|
192
|
+
*
|
|
193
|
+
* WHAT THE BUMP BUYS: `anno_evid_exec`, the durable, run-identity-keyed table
|
|
194
|
+
* that records ONE fact per row -- an address was OBSERVED EXECUTING during a
|
|
195
|
+
* specific run -- so a later session can query accumulated runtime evidence
|
|
196
|
+
* instead of re-running the program. See `anno-store.ts`'s `DDL` and its
|
|
197
|
+
* `insertExecObservations` / `listExecObservations` / `listObservedRuns` /
|
|
198
|
+
* `deleteExecObservationsForRun`.
|
|
199
|
+
*
|
|
200
|
+
* THE OPTION SELECTED, BY NAME: `reaffirm-refusal`. The strict-equality
|
|
201
|
+
* refusal inside `openStore` stays exactly as VERSION 3 left it -- a
|
|
202
|
+
* version-3 store does not open under this `SCHEMA_VERSION`, no migration arm
|
|
203
|
+
* is written, and the single-witness property (one comparison site, no
|
|
204
|
+
* second write of `anno_meta.schema_version`) is unchanged.
|
|
205
|
+
*
|
|
206
|
+
* THE FACTUAL CHECK, RUN 2026-09-10, SCOPE ONE DEVELOPMENT MACHINE:
|
|
207
|
+
* 1. `find / -xdev -name '*.annostore'`, with `.git` directories excluded --
|
|
208
|
+
* empty. The broader `find "$HOME" -name '*.annostore'` (closing the `-xdev`
|
|
209
|
+
* mount-boundary gap) found five hits, all inside a dated (2026-08-27)
|
|
210
|
+
* scratch probe cache directory, with
|
|
211
|
+
* fixture-pattern names (`t-half`, `t-zero`, `t-tail-100`, `p`, `proj`)
|
|
212
|
+
* and one file zero bytes -- synthetic test fixtures, not a real
|
|
213
|
+
* project's store.
|
|
214
|
+
* 2. `git log --oneline --diff-filter=A -- '*.annostore'` -- empty. No
|
|
215
|
+
* `.annostore` has ever been added to this repository's history.
|
|
216
|
+
* 3. Release tags versus when the store landed: `anno-store.ts` was added
|
|
217
|
+
* by `4c9cea3c` (2026-08-27). `v0.7.0` (2026-09-01) and `v0.8.0`
|
|
218
|
+
* (2026-09-06) BOTH POSTDATE that landing and the VERSION 3 bump
|
|
219
|
+
* itself. **THIS CORRECTS THE VERSION 3 PARAGRAPH ABOVE**: its stated
|
|
220
|
+
* justification -- "the last release tag (`v0.5.0`, 2026-08-25)
|
|
221
|
+
* PREDATES the store entirely -- so no tagged release has ever shipped
|
|
222
|
+
* a store at all" -- was true when written (2026-08-29) and is STALE
|
|
223
|
+
* now. Two tagged releases since then could, in principle, have shipped
|
|
224
|
+
* a working store to a real user.
|
|
225
|
+
* 4. Nothing outside that dated scratch/probe cache directory, and nothing
|
|
226
|
+
* tracked in git, was found on this machine.
|
|
227
|
+
*
|
|
228
|
+
* THE RE-AFFIRMATION IS MADE DESPITE THAT CORRECTION, on the strength of the
|
|
229
|
+
* observed NULL RESULT (zero real stores found), not on the strength of the
|
|
230
|
+
* now-stale "no release ever shipped one" framing. What changed since VERSION
|
|
231
|
+
* 3 is the *possibility* of a field store, not an observation of one, and the
|
|
232
|
+
* two directions' costs are asymmetric in reversibility: re-affirming refusal
|
|
233
|
+
* STAYS REVERSIBLE -- a migration arm can be added later, at version 5,
|
|
234
|
+
* against real evidence -- while shipping a mutating open path now adds a
|
|
235
|
+
* half-reasoned migration to a module that also has a corruption-refusal
|
|
236
|
+
* path, exactly where silent corruption lives, inside a plan whose actual job
|
|
237
|
+
* is an evidence table rather than a schema-migration redesign. House style
|
|
238
|
+
* settles the shape too: this project's standing pattern for a mismatched or
|
|
239
|
+
* missing external state is DETECT, THEN REFUSE BY NAME WITH THE REMEDY IN
|
|
240
|
+
* THE MESSAGE (`CLAUDE.md`'s "external tools are never auto-installed"
|
|
241
|
+
* constraint states the same discipline for a different kind of missing
|
|
242
|
+
* state) -- `openStore`'s refusal message is extended to name that remedy
|
|
243
|
+
* explicitly and to say the refused file is left untouched, so a user who
|
|
244
|
+
* hits it is not left inferring either fact.
|
|
245
|
+
*
|
|
246
|
+
* THE FACTUAL BASIS IS ONE MACHINE ONLY, and the limit travels with the
|
|
247
|
+
* decision: this is not a global guarantee that no field store exists --
|
|
248
|
+
* exactly the scope VERSION 3's own paragraph already accepted once.
|
|
249
|
+
*
|
|
250
|
+
* THE VERSION 1 PARAGRAPH ABOVE IS STILL THE PRECEDENT THIS IS MEASURED
|
|
251
|
+
* AGAINST, and this refusal is, again, a CHOICE and not an impossibility:
|
|
252
|
+
* version 4 COULD be given a migration arm and deliberately is not, for the
|
|
253
|
+
* reasons above.
|
|
254
|
+
*
|
|
255
|
+
* THE REVERSAL CONDITION, RECORDED SO THIS DOES NOT QUIETLY HARDEN INTO
|
|
256
|
+
* PRECEDENT: a migration arm at version 5 is justified if a real, non-scratch
|
|
257
|
+
* `.annostore` file is found in the field -- a user's own project, a bug
|
|
258
|
+
* report attaching one, or a support request -- or if a documented
|
|
259
|
+
* distribution channel is shown to have shipped a version-3 or version-4
|
|
260
|
+
* store to users who have not yet upgraded past it. Absent that evidence, the
|
|
261
|
+
* refusal stays the default.
|
|
262
|
+
*
|
|
263
|
+
* ---------------------------------------------------------------------------
|
|
264
|
+
* VERSION 5, 2026-09-11 -- THE DECISION IS `reaffirm-refusal`, THE
|
|
265
|
+
* SAME OPTION VERSION 4 SELECTED, AND THE FACTUAL BASIS IS TRANSCRIBED HERE
|
|
266
|
+
* RATHER THAN LEFT IN A PLANNING DIRECTORY, for the same reason every prior
|
|
267
|
+
* version's paragraph gives.
|
|
268
|
+
*
|
|
269
|
+
* WHAT THE BUMP BUYS: `anno_excluded_range`, the durable record of a
|
|
270
|
+
* user-requested exclusion -- its extent AND the reason the user gave -- so
|
|
271
|
+
* the export can emit the range's real bytes tagged with a visible marker
|
|
272
|
+
* comment instead of a hole, which is this feature's whole invariant: an
|
|
273
|
+
* exclusion is a RECORDED, VISIBLE fact, never a silent drop. See
|
|
274
|
+
* `anno-store.ts`'s `DDL` and its three verbs, `addExcludedRange` /
|
|
275
|
+
* `listExcludedRanges` / `removeExcludedRange`.
|
|
276
|
+
*
|
|
277
|
+
* THE OPTION SELECTED, BY NAME: `reaffirm-refusal`. The strict-equality
|
|
278
|
+
* refusal inside `openStore` stays EXACTLY as VERSION 4 left it -- this bump
|
|
279
|
+
* does not touch that comparison site at all: a version-4 store does not open
|
|
280
|
+
* under this `SCHEMA_VERSION`, no migration arm is written, and the
|
|
281
|
+
* single-witness property (one comparison site, no second write of
|
|
282
|
+
* `anno_meta.schema_version`) is unchanged.
|
|
283
|
+
*
|
|
284
|
+
* THE FACTUAL CHECK, RUN 2026-09-11, SCOPE ONE DEVELOPMENT MACHINE -- the
|
|
285
|
+
* same three checks VERSION 4 ran, re-run rather than inherited, with the
|
|
286
|
+
* REAL results transcribed as of today:
|
|
287
|
+
* 1. `find "$HOME" -name '*.annostore'`, with `.git` directories excluded --
|
|
288
|
+
* SIX hits, not VERSION 4's five. The same five fixture-pattern files
|
|
289
|
+
* inside the dated (2026-08-27) scratch probe cache (`t-half`, `t-zero`,
|
|
290
|
+
* `t-tail-100`, `p`, `proj`) are still present, UNCHANGED. The SIXTH is
|
|
291
|
+
* NEW since VERSION 4's check: `.c64-re-tools/phase45-scratch/
|
|
292
|
+
* tracer.annostore`, inside THIS repository's own gitignored scratch
|
|
293
|
+
* directory, timestamped 2026-09-10 -- its path and name identify it as
|
|
294
|
+
* this project's own tracer/scratch fixture, not a real consuming project's
|
|
295
|
+
* store. Six real files, zero real stores.
|
|
296
|
+
* 2. `git log --oneline --diff-filter=A -- '*.annostore'` -- still empty.
|
|
297
|
+
* No `.annostore` has ever been added to this repository's tracked
|
|
298
|
+
* history.
|
|
299
|
+
* 3. Release tags versus when the store landed: `anno-store.ts` was added
|
|
300
|
+
* by `4c9cea3c` (2026-08-27). Since VERSION 4's check, ONE MORE tagged
|
|
301
|
+
* release has been cut -- `v0.9.0` (2026-09-10) -- joining `v0.7.0`
|
|
302
|
+
* (2026-09-01) and `v0.8.0` (2026-09-06). All THREE now postdate the
|
|
303
|
+
* store's landing and VERSION 3's original bump, so three tagged
|
|
304
|
+
* releases, not two, could in principle have shipped a working store to
|
|
305
|
+
* a real user. No result differs in KIND from VERSION 4's; the count of
|
|
306
|
+
* possible-exposure releases has grown by one, consistent with time
|
|
307
|
+
* having passed rather than with any new evidence of a real store.
|
|
308
|
+
*
|
|
309
|
+
* THE RE-AFFIRMATION IS MADE ON THE SAME BASIS VERSION 4 USED: the observed
|
|
310
|
+
* NULL RESULT (zero real, non-scratch stores found across six candidate
|
|
311
|
+
* files), not on a claim that no release could have shipped one. The new
|
|
312
|
+
* sixth file changes nothing about that null result -- it is this project's
|
|
313
|
+
* OWN scratch output, in a directory this project's own tooling writes to and
|
|
314
|
+
* that ships nothing.
|
|
315
|
+
*
|
|
316
|
+
* WHY A NEW TABLE WAS CHOSEN OVER A NULLABLE `excluded`/`exclusion_reason`
|
|
317
|
+
* COLUMN PAIR ON `anno_range`, recorded because it is the one part of this
|
|
318
|
+
* decision that is structural rather than stylistic: an exclusion's extent is
|
|
319
|
+
* chosen by the USER and has no reason to coincide with a typed range's
|
|
320
|
+
* boundaries, so a column on `anno_range` would force `retype()`'s carve to
|
|
321
|
+
* split, duplicate, or lose an exclusion's reason every time a range boundary
|
|
322
|
+
* moved underneath it -- `retype()` knows nothing about exclusions today and
|
|
323
|
+
* a column would force it to. A separate table keeps the user's own words
|
|
324
|
+
* independent of the store's typing churn entirely. This also happens to
|
|
325
|
+
* match the project's existing preference (`anno_evid_exec` at VERSION 4 over
|
|
326
|
+
* widening `anno_range`), but that precedent is the WEAKER of the two
|
|
327
|
+
* arguments -- the structural one above is the one that would still hold even
|
|
328
|
+
* if this project had no such precedent.
|
|
329
|
+
*
|
|
330
|
+
* THE REVERSAL CONDITION, RECORDED SO THIS DOES NOT QUIETLY HARDEN INTO
|
|
331
|
+
* PRECEDENT: a migration arm at version 6 is justified if a real, non-scratch
|
|
332
|
+
* `.annostore` is found in the field -- a user's own project, a bug report
|
|
333
|
+
* attaching one, or a support request -- or if a documented distribution
|
|
334
|
+
* channel is shown to have shipped a version-4 or version-5 store to users
|
|
335
|
+
* who have not yet upgraded past it. Absent that evidence the refusal stays
|
|
336
|
+
* the default.
|
|
185
337
|
*/
|
|
186
|
-
export const SCHEMA_VERSION =
|
|
338
|
+
export const SCHEMA_VERSION = 5;
|
|
187
339
|
|
|
188
340
|
/** The 6510's address space, inclusive at both ends. */
|
|
189
341
|
export const ADDRESS_MIN = 0x0000;
|
|
@@ -193,8 +345,7 @@ export const ADDRESS_MAX = 0xffff;
|
|
|
193
345
|
* (`anno-store.ts`'s `snapshotDirFor()` -- a sibling named after the store
|
|
194
346
|
* FILE, not the fixed `<dir>/snapshots` version 1 used) may hold before the
|
|
195
347
|
* oldest is pruned. Declared here because the bound is a property of the
|
|
196
|
-
* store's format; the pruning that enforces it belongs to the revert surface
|
|
197
|
-
* (`STORE-04`). */
|
|
348
|
+
* store's format; the pruning that enforces it belongs to the revert surface. */
|
|
198
349
|
export const MAX_SNAPSHOT_REVISIONS = 32;
|
|
199
350
|
|
|
200
351
|
/**
|
|
@@ -301,7 +452,8 @@ export type LabelKind = (typeof LABEL_KINDS)[number];
|
|
|
301
452
|
* is written down, and its provenance needs stating precisely so a later reader
|
|
302
453
|
* does not over-trust it: these four spellings are CITED from an external
|
|
303
454
|
* analyser's reference documentation, flowed through this project's own research
|
|
304
|
-
* notes, and fixed by
|
|
455
|
+
* notes, and fixed by this project's own requirement that cross-reference rows
|
|
456
|
+
* carry their access kind. They are NOT read from any
|
|
305
457
|
* code in this repository, and no comment may present them as verified project
|
|
306
458
|
* vocabulary.
|
|
307
459
|
*
|
|
@@ -370,7 +522,7 @@ export interface CommentRow {
|
|
|
370
522
|
}
|
|
371
523
|
|
|
372
524
|
/**
|
|
373
|
-
* One stored comment that a retype has just made FALSE
|
|
525
|
+
* One stored comment that a retype has just made FALSE.
|
|
374
526
|
*
|
|
375
527
|
* The caller needs all four facts to act on the report without a second query:
|
|
376
528
|
* WHERE the comment is, WHAT it says, WHICH grade fired, and WHICH data type
|
|
@@ -389,7 +541,7 @@ export interface ContradictedComment {
|
|
|
389
541
|
}
|
|
390
542
|
|
|
391
543
|
/**
|
|
392
|
-
* One entry-address pairing of a split table
|
|
544
|
+
* One entry-address pairing of a split table.
|
|
393
545
|
*
|
|
394
546
|
* `pairs[i]` is the two ADDRESSES whose bytes form entry `i`, in table order:
|
|
395
547
|
* the first-half address and its second-half partner. `entryCount` is
|
|
@@ -402,8 +554,8 @@ export interface SplitEntryPairs {
|
|
|
402
554
|
}
|
|
403
555
|
|
|
404
556
|
/**
|
|
405
|
-
* One surviving fragment of a split table that a partial overwrite left behind
|
|
406
|
-
*
|
|
557
|
+
* One surviving fragment of a split table that a partial overwrite left behind.
|
|
558
|
+
* `entryPairs` is what that fragment reads NOW -- not what the addresses
|
|
407
559
|
* in it used to be paired with.
|
|
408
560
|
*/
|
|
409
561
|
export interface SplitTableSurvivor {
|
|
@@ -415,7 +567,7 @@ export interface SplitTableSurvivor {
|
|
|
415
567
|
|
|
416
568
|
/**
|
|
417
569
|
* What one accepted partial overwrite of a split table COST, reported as data on
|
|
418
|
-
* a successful `setDataType()` result
|
|
570
|
+
* a successful `setDataType()` result. Documented in the same
|
|
419
571
|
* register as `ContradictedComment` above, and for the same reason: a caller
|
|
420
572
|
* needs every fact it would otherwise have to re-query for.
|
|
421
573
|
*
|
|
@@ -464,6 +616,23 @@ export interface ScopeRow {
|
|
|
464
616
|
endInclusive: number;
|
|
465
617
|
}
|
|
466
618
|
|
|
619
|
+
/** One user-requested exclusion as the store holds it, added at
|
|
620
|
+
* `SCHEMA_VERSION` 5. Both ends are INCLUSIVE, matching every
|
|
621
|
+
* other range-shaped row this store persists. There is no `bank` column,
|
|
622
|
+
* because an exclusion is a statement about a span of the SUBJECT PROGRAM the
|
|
623
|
+
* user asked to leave out of the rebuild -- not a memory view -- and there is
|
|
624
|
+
* no confidence, verdict, grade or severity column, because this row holds
|
|
625
|
+
* WHAT THE USER ASKED FOR and nothing the tool concluded about it. A
|
|
626
|
+
* judgement column here would be the exact "tool is the decider" shape
|
|
627
|
+
* this project's own 2026-09-10 requirement rewording removed: the store answers "what did the
|
|
628
|
+
* user record", never "should this range be excluded". */
|
|
629
|
+
export interface ExcludedRangeRow {
|
|
630
|
+
id: number;
|
|
631
|
+
start: number;
|
|
632
|
+
endInclusive: number;
|
|
633
|
+
reason: string;
|
|
634
|
+
}
|
|
635
|
+
|
|
467
636
|
/** One project-local enum as the store holds it. `variants` is keyed by the
|
|
468
637
|
* numeric-string forms the schema names -- decimal, `0x`/`$` hex, `0b`/`%`
|
|
469
638
|
* binary -- and its values are variant names. */
|
|
@@ -476,7 +645,7 @@ export interface ProjectEnumRow {
|
|
|
476
645
|
|
|
477
646
|
/**
|
|
478
647
|
* One enum usage as the store holds it: the association between ONE address
|
|
479
|
-
* and ONE project enum, added at `SCHEMA_VERSION` 3
|
|
648
|
+
* and ONE project enum, added at `SCHEMA_VERSION` 3.
|
|
480
649
|
*
|
|
481
650
|
* `enumId` IS WHAT THE STORE PERSISTS; `enumName` is resolved through the join
|
|
482
651
|
* at read time and is never a second on-disk copy of the name. A row that
|
|
@@ -505,6 +674,80 @@ export interface XrefRow {
|
|
|
505
674
|
bank: number | null;
|
|
506
675
|
}
|
|
507
676
|
|
|
677
|
+
/**
|
|
678
|
+
* The three memory regions `memmapshow` reports an execute observation
|
|
679
|
+
* against, `SCHEMA_VERSION` 4. This is the ONE place this
|
|
680
|
+
* vocabulary is written down.
|
|
681
|
+
*
|
|
682
|
+
* ALL THREE ARE INCLUDED DELIBERATELY, not for symmetry: `AccessFlags`
|
|
683
|
+
* (`textmon-memmap.ts`) carries `read`/`write`/`execute` as three
|
|
684
|
+
* INDEPENDENT bits per region, and dropping any one of the three regions
|
|
685
|
+
* here would discard a genuine positive observation the wire already
|
|
686
|
+
* reported -- `io.execute` is exactly as real a fact as `ram.execute`.
|
|
687
|
+
*/
|
|
688
|
+
export const EVID_SOURCE_BANKS = Object.freeze(["ram", "rom", "io"] as const);
|
|
689
|
+
|
|
690
|
+
/** One of the three evidence source banks. */
|
|
691
|
+
export type EvidSourceBank = (typeof EVID_SOURCE_BANKS)[number];
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* The runtime evidence layer's own classification of an address, `SCHEMA_VERSION`
|
|
695
|
+
* 4. This is a TYPE-LEVEL control, not a runtime check: the union has
|
|
696
|
+
* exactly two members and NO `"data"` member exists for a caller to return,
|
|
697
|
+
* mistakenly or otherwise.
|
|
698
|
+
*
|
|
699
|
+
* THIS IS THE SAME DISCIPLINE `textmon-memmap.ts` STATES FOR ITSELF, carried
|
|
700
|
+
* forward rather than re-derived: that module's own header forbids adding
|
|
701
|
+
* "a field, key, label or enum member anywhere in this module or its answer
|
|
702
|
+
* types that classifies an address as DATA on the strength of never having
|
|
703
|
+
* been observed" (`textmon-memmap.ts:32-38`). `BlockClass`'s three-valued
|
|
704
|
+
* union (`"code" | "data" | "undefined"`, `block-class.ts`) is the byte-derived
|
|
705
|
+
* classifier's own vocabulary and is DELIBERATELY NOT REUSED here: that union
|
|
706
|
+
* exists to name a byte-derived guess, and this one exists to name only what
|
|
707
|
+
* was actually witnessed executing. `"unobserved"` means exactly "no
|
|
708
|
+
* execution was ever recorded at this address" -- never "this address is
|
|
709
|
+
* data" -- and no code path in this layer may treat the two as
|
|
710
|
+
* interchangeable (see the ban on deriving `data` from absence, Pitfall 3 in
|
|
711
|
+
* this phase's research).
|
|
712
|
+
*/
|
|
713
|
+
export type RuntimeExecClass = "code" | "unobserved";
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* One runtime-execution observation as the store holds it (`anno_evid_exec`,
|
|
717
|
+
* `SCHEMA_VERSION` 4). Keyed by the bare run-identity triple
|
|
718
|
+
* `(imageSha256, argvDigest, seed)` plus `address` plus `sourceBank` -- there is
|
|
719
|
+
* no `run_class` discriminator column: a live A/B measured `no-perturbation`
|
|
720
|
+
* from instrumentation at anchor hit depths 10 and 50, so a run-class
|
|
721
|
+
* distinction would encode a difference nothing observed.
|
|
722
|
+
*
|
|
723
|
+
* A row here asserts exactly ONE fact: this address was observed executing,
|
|
724
|
+
* in this source bank, during this run. There is no `bank`-reserved column
|
|
725
|
+
* (unlike the annotation tables) and no nullable column -- every field is a
|
|
726
|
+
* fact this table is licensed to assert, or the row does not exist.
|
|
727
|
+
*/
|
|
728
|
+
export interface EvidExecRow {
|
|
729
|
+
id: number;
|
|
730
|
+
imageSha256: string;
|
|
731
|
+
argvDigest: string;
|
|
732
|
+
seed: string;
|
|
733
|
+
address: number;
|
|
734
|
+
sourceBank: EvidSourceBank;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* One distinct run identity's accumulated observation count, as
|
|
739
|
+
* `listObservedRuns()` reports it. `observationCount` is a COUNT, never a
|
|
740
|
+
* percentage or rate -- see `listObservedRuns`'s own doc comment in
|
|
741
|
+
* `anno-store.ts` for the denominator this count is a fraction of (a count
|
|
742
|
+
* with no denominator invites the reading "the rest is data").
|
|
743
|
+
*/
|
|
744
|
+
export interface ObservedRunRow {
|
|
745
|
+
imageSha256: string;
|
|
746
|
+
argvDigest: string;
|
|
747
|
+
seed: string;
|
|
748
|
+
observationCount: number;
|
|
749
|
+
}
|
|
750
|
+
|
|
508
751
|
/** What `resolveSplitTargets()` returns: the entry count, the resolved 16-bit
|
|
509
752
|
* targets in table order, and whether this layout produces cross-references at
|
|
510
753
|
* all. Nothing here is ever written to disk. */
|
|
@@ -586,7 +829,7 @@ export interface AnnoRevisionArgumentErrorOptions {
|
|
|
586
829
|
* path is built, so nothing has been read and nothing has been written.
|
|
587
830
|
*
|
|
588
831
|
* WHY THIS IS NOT `AnnoStoreCorruptError`, WHICH IS THE WHOLE REASON THE CLASS
|
|
589
|
-
* EXISTS
|
|
832
|
+
* EXISTS. Until this class existed, `revertTo(handle, "0001")` matched
|
|
590
833
|
* revision 1's pointer row through SQLite's INTEGER affinity on a bound TEXT
|
|
591
834
|
* operand, while `snapshotPathFor` built `r0001.db` from the raw string -- so
|
|
592
835
|
* the two disagreed and the caller was told its snapshot was "not a readable
|
|
@@ -840,7 +1083,8 @@ export interface AnnoCommentGradeErrorOptions {
|
|
|
840
1083
|
*
|
|
841
1084
|
* It is NEVER correct to swallow the original and treat the comment as ungraded:
|
|
842
1085
|
* that would quietly exempt a malformed comment from contradiction reporting,
|
|
843
|
-
* which is the same silent
|
|
1086
|
+
* which is the same silent-un-documenting failure this store's contradiction
|
|
1087
|
+
* reporting exists to prevent.
|
|
844
1088
|
*/
|
|
845
1089
|
export class AnnoCommentGradeError extends AnnoStoreError {
|
|
846
1090
|
comment?: string;
|
|
@@ -974,7 +1218,7 @@ const MAX_SYMLINK_HOPS = 40;
|
|
|
974
1218
|
* Does the path ENTRY `p` exist -- that is, does this NAME exist in its
|
|
975
1219
|
* directory?
|
|
976
1220
|
*
|
|
977
|
-
*
|
|
1221
|
+
* THE WHOLE OF THE DISTINCTION, IN TWO SENTENCES. `existsSync` answers a
|
|
978
1222
|
* different question: "does this path RESOLVE to something?", which follows
|
|
979
1223
|
* symbolic links and therefore reports `false` for a dangling one. `lstat`
|
|
980
1224
|
* answers "does this NAME exist?", which does not follow the link. The two
|
|
@@ -984,8 +1228,7 @@ const MAX_SYMLINK_HOPS = 40;
|
|
|
984
1228
|
*
|
|
985
1229
|
* `throwIfNoEntry: false` makes the ABSENT case a value rather than an
|
|
986
1230
|
* exception, so the caller has one branch instead of a `try` around a
|
|
987
|
-
* predicate. That option suppresses `ENOENT` AND NOTHING ELSE
|
|
988
|
-
* whole of `WR-12`.
|
|
1231
|
+
* predicate. That option suppresses `ENOENT` AND NOTHING ELSE.
|
|
989
1232
|
*
|
|
990
1233
|
* REVERSED 2026-08-28, and the reversal is the record rather than a deletion
|
|
991
1234
|
* (this module's header discipline, 28-07 P3). The premise that was RIGHT and
|
|
@@ -1005,7 +1248,7 @@ const MAX_SYMLINK_HOPS = 40;
|
|
|
1005
1248
|
* * `ELOOP` -- a symlink cycle in an ANCESTOR position, where the kernel
|
|
1006
1249
|
* refuses at `lstat` before the manual hop counter below ever runs.
|
|
1007
1250
|
*
|
|
1008
|
-
*
|
|
1251
|
+
* The `try` restores the
|
|
1009
1252
|
* family WITHOUT restoring the old blindness: the absent case is still a value
|
|
1010
1253
|
* and still one branch, and everything else is a decision naming both the entry
|
|
1011
1254
|
* the walk stopped on and the path being confined.
|
|
@@ -1052,8 +1295,7 @@ function pathEntryExists(p: string, resolved: string): boolean {
|
|
|
1052
1295
|
* /tmp/annosym-XXXX/ws/p.annostore`) and `openStore` created the store file
|
|
1053
1296
|
* OUTSIDE the workspace root (`A) file created OUTSIDE workspace: true`);
|
|
1054
1297
|
* separately, a dangling DIRECTORY link was accepted at the predicate
|
|
1055
|
-
* (`B dangling dir -> ACCEPTED`).
|
|
1056
|
-
* CR-04. The walk now stops on `pathEntryExists`, which is `lstat` and does not
|
|
1298
|
+
* (`B dangling dir -> ACCEPTED`). The walk now stops on `pathEntryExists`, which is `lstat` and does not
|
|
1057
1299
|
* follow the link.
|
|
1058
1300
|
*
|
|
1059
1301
|
* THE DANGLING STOPPING ENTRY IS RESOLVED BY HAND, because nothing else will:
|
|
@@ -1162,8 +1404,8 @@ function realpathOfNearestExisting(p: string): string {
|
|
|
1162
1404
|
* * The PATH must be a real path, because `resolve()` normalises `..` but
|
|
1163
1405
|
* does NOT follow symbolic links. The pure-string version accepted a
|
|
1164
1406
|
* symlinked subdirectory inside the workspace and the store file was
|
|
1165
|
-
* created outside the root
|
|
1166
|
-
*
|
|
1407
|
+
* created outside the root, confirmed by a live symlink reproduction.
|
|
1408
|
+
* A confinement check has to compare what the filesystem will
|
|
1167
1409
|
* actually do.
|
|
1168
1410
|
* * The ROOT must go through the SAME walk, for two independent reasons. A
|
|
1169
1411
|
* workspace root that does not exist is a legitimate input -- the pinned
|
|
@@ -1212,8 +1454,7 @@ export function storePathWithinWorkspace(path: string, workspaceRoot: string): s
|
|
|
1212
1454
|
* WHY THIS IS A SEAM RATHER THAN AN INLINE `relative()` AT ITS ONE CALL SITE.
|
|
1213
1455
|
* The spelling this returns goes into a **compared** artifact: the memory
|
|
1214
1456
|
* map's banner is re-rendered and diffed BYTE FOR BYTE by
|
|
1215
|
-
* `checkRenderedMemoryMap()`. The defect it closes
|
|
1216
|
-
* `.planning/phases/29-the-mcp-surface/29-VERIFICATION.md`) is that machine
|
|
1457
|
+
* `checkRenderedMemoryMap()`. The defect it closes is that machine
|
|
1217
1458
|
* identity leaked into that content comparison: the banner recorded the
|
|
1218
1459
|
* absolute realpaths, so the same store, the same sidecar and the same
|
|
1219
1460
|
* rendered file reported `drifted` as soon as the checkout sat at a different
|
|
@@ -1300,6 +1541,37 @@ export function assertAccessKind(value: unknown): XrefAccessKind {
|
|
|
1300
1541
|
return assertMember(value, XREF_ACCESS_KINDS, "access kind");
|
|
1301
1542
|
}
|
|
1302
1543
|
|
|
1544
|
+
/** Narrows an unvalidated argument to an `EvidSourceBank`, or throws
|
|
1545
|
+
* `AnnoTypeError` carrying the offending value and all three valid members. */
|
|
1546
|
+
export function assertEvidSourceBank(value: unknown): EvidSourceBank {
|
|
1547
|
+
return assertMember(value, EVID_SOURCE_BANKS, "source bank");
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
/** Exactly 64 lowercase hex characters -- the shape `argvDigest()`
|
|
1551
|
+
* (`capture-predicate.ts`) and a sha256 image hash both already produce.
|
|
1552
|
+
* Refuses anything else BY NAME, including a correctly-shaped but
|
|
1553
|
+
* UPPERCASE digest, rather than lower-casing it silently: two callers
|
|
1554
|
+
* disagreeing on case is exactly the kind of divergence a store write must
|
|
1555
|
+
* not launder into agreement. */
|
|
1556
|
+
export function assertRunIdentityDigest(value: unknown, what: string): string {
|
|
1557
|
+
if (typeof value === "string" && /^[0-9a-f]{64}$/.test(value)) {
|
|
1558
|
+
return value;
|
|
1559
|
+
}
|
|
1560
|
+
throw new AnnoTypeError(
|
|
1561
|
+
`${what} ${JSON.stringify(value)} is not exactly 64 lowercase hex characters -- expected a sha256 hex digest such as argvDigest() or an image hash produces`,
|
|
1562
|
+
{ dataType: value },
|
|
1563
|
+
);
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
/** A non-empty string. `seed` is not a digest and carries no shape beyond
|
|
1567
|
+
* that -- this store's run-identity composite treats it as an opaque token. */
|
|
1568
|
+
export function assertRunIdentitySeed(value: unknown): string {
|
|
1569
|
+
if (typeof value === "string" && value.length > 0) {
|
|
1570
|
+
return value;
|
|
1571
|
+
}
|
|
1572
|
+
throw new AnnoTypeError(`seed ${JSON.stringify(value)} is not a non-empty string`, { dataType: value });
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1303
1575
|
/** The legal-identifier shape, quoted from the schema's own sentence
|
|
1304
1576
|
* (`anno-tools.ts:246-251`): "starts with a letter or underscore, followed by
|
|
1305
1577
|
* letters/digits/underscores only". Used for label names and for project-enum
|
|
@@ -1573,7 +1845,8 @@ export function resolveSplitTargets(bytes: Uint8Array | readonly number[], dataT
|
|
|
1573
1845
|
*
|
|
1574
1846
|
* A resolver and a writer that each kept their own copy of this arithmetic could
|
|
1575
1847
|
* disagree about what an entry IS, and the disagreement would be silent: both
|
|
1576
|
-
* copies produce legal, decodable rows. That is
|
|
1848
|
+
* copies produce legal, decodable rows. That is exactly the class of bug a
|
|
1849
|
+
* second implementation would risk, so the rule
|
|
1577
1850
|
* has one home. `anno-types.test.ts`'s worked-arithmetic pin is the control --
|
|
1578
1851
|
* changing the couples here to an interleaved `[2i, 2i + 1]` reddens it.
|
|
1579
1852
|
*
|