@henols/c64-re-tools 0.2.3 → 0.2.5
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/bin/cli.mjs +7 -4
- package/package.json +2 -2
- package/skills/acme-build/SKILL.md +22 -22
- package/skills/c64-disk-access/SKILL.md +49 -46
- package/skills/c64-memory-mapping/SKILL.md +71 -70
- package/skills/c64-petcat/SKILL.md +11 -11
- package/skills/c64-program-recon/SKILL.md +109 -105
- package/skills/c64-program-recon/references/control-flow.md +13 -13
- package/skills/c64-program-recon/references/graphics.md +6 -6
- package/skills/c64-program-recon/references/observation-hazards.md +7 -7
- package/skills/c64-program-recon/references/reconstruction.md +6 -6
- package/skills/c64-program-recon/references/sound-and-input.md +8 -8
- package/skills/c64-program-recon/references/tool-selection.md +11 -11
- package/skills/c64-program-recon/templates/memory-map.template.md +16 -12
- package/skills/c64-provenance-diff/SKILL.md +43 -43
- package/skills/c64-ram-capture/SKILL.md +96 -45
- package/skills/c64-ram-capture/scripts/compare-cross-binary.mjs +525 -0
- package/skills/c64-ram-capture/templates/capture-record.template.md +11 -11
- package/skills/c64-ram-capture/transients/README.md +11 -10
- package/skills/routine-queue-walker/SKILL.md +205 -176
- package/skills/routine-queue-walker/scripts/completeness-report.mjs +9 -7
- package/skills/vice-wedge-triage/SKILL.md +48 -48
|
@@ -6,37 +6,38 @@ description: Drive an existing C64 annotation store's backlog of undocumented ro
|
|
|
6
6
|
# Walking the routine and symbol queue to closure
|
|
7
7
|
|
|
8
8
|
**Do not start annotating whatever is in front of you.** The expensive failure
|
|
9
|
-
here is not slow work
|
|
9
|
+
here is not slow work. It is a pass that *looks* finished while a hundred
|
|
10
10
|
`p_XXXX` labels are still nameless and nobody wrote down which ones. Build the
|
|
11
11
|
queue first, from data, then walk it to the end.
|
|
12
12
|
|
|
13
13
|
This playbook assumes block classification has already happened and an
|
|
14
|
-
annotation store already exists.
|
|
15
|
-
where it starts, which vector is live, which regions are code
|
|
16
|
-
`c64-program-recon` first. That skill answers *what is this
|
|
17
|
-
answers *what is still undocumented in it, and how do I
|
|
14
|
+
annotation store already exists. You may not yet know what the program is —
|
|
15
|
+
where it starts, which vector is live, which regions are code. If so, stop
|
|
16
|
+
and run `c64-program-recon` first. That skill answers *what is this
|
|
17
|
+
program*. This one answers *what is still undocumented in it, and how do I
|
|
18
|
+
finish*.
|
|
18
19
|
|
|
19
20
|
## The one rule that makes this different from upstream's version
|
|
20
21
|
|
|
21
22
|
**Work the queue one entry at a time.** Not as a throughput compromise — as an
|
|
22
|
-
accurate model of the store underneath. One `.annostore` is one writer
|
|
23
|
+
accurate model of the store underneath. One `.annostore` is one writer. Every
|
|
23
24
|
mutating call opens it, commits and closes inside the call, and every one of
|
|
24
|
-
them accepts an optional `base_revision` compare-and-swap
|
|
25
|
-
computed against a revision the store has already moved past. Fanning
|
|
26
|
-
writers at one store therefore buys **zero** extra throughput and
|
|
27
|
-
correctness
|
|
28
|
-
have to re-derive and replay. Reading fan-out — several agents *thinking*
|
|
29
|
-
|
|
30
|
-
I/O.
|
|
31
|
-
|
|
32
|
-
**Every call names its own store.** There is no ambient "current store" on
|
|
33
|
-
surface: pass `store` (a `.annostore` path) on every call
|
|
34
|
-
|
|
35
|
-
from the annotations
|
|
36
|
-
`anno_disassemble`, `anno_get_cross_references`,
|
|
37
|
-
`anno_get_address_details`. The store holds annotations and
|
|
38
|
-
omitted image would read as a plausible success against
|
|
39
|
-
last.
|
|
25
|
+
them accepts an optional `base_revision` compare-and-swap. That REFUSES a
|
|
26
|
+
write computed against a revision the store has already moved past. Fanning
|
|
27
|
+
several writers at one store therefore buys **zero** extra throughput and
|
|
28
|
+
costs correctness. The losers come back as named stale-revision refusals you
|
|
29
|
+
then have to re-derive and replay. Reading fan-out — several agents *thinking*
|
|
30
|
+
over answers they already got — is fine, and its value is reasoning
|
|
31
|
+
bandwidth, never I/O.
|
|
32
|
+
|
|
33
|
+
**Every call names its own store.** There is no ambient "current store" on
|
|
34
|
+
this surface: pass `store` (a `.annostore` path) on every call. Also pass
|
|
35
|
+
`image` on every call that derives its answer from the program's bytes rather
|
|
36
|
+
than from the annotations. Those calls are `anno_get_binary_info`,
|
|
37
|
+
`anno_read_region`, `anno_disassemble`, `anno_get_cross_references`,
|
|
38
|
+
`anno_search` and `anno_get_address_details`. The store holds annotations and
|
|
39
|
+
never bytes, so an omitted image would read as a plausible success against
|
|
40
|
+
whatever was recorded last.
|
|
40
41
|
|
|
41
42
|
## Phase 0 — context, and the packed-binary gate
|
|
42
43
|
|
|
@@ -45,22 +46,23 @@ last.
|
|
|
45
46
|
quotes them.
|
|
46
47
|
2. Read the returned `entropy` against the threshold of **7.5** carried in that
|
|
47
48
|
tool's own description. At or above it, the bytes are very likely packed.
|
|
48
|
-
3. If the binary looks packed, **stop and say so.** Do not annotate a
|
|
49
|
-
image
|
|
50
|
-
thrown away the moment the real image
|
|
51
|
-
an unpacked image is `c64-ram-capture
|
|
52
|
-
capture RAM at a checkpoint past the decrunch
|
|
53
|
-
finding in `c64-program-recon`, which names the packer
|
|
54
|
-
Come back with the captured image and start again at
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
3. If the binary looks packed, **stop and say so.** Do not annotate a
|
|
50
|
+
packed image. You would be documenting a decruncher. Every label you write
|
|
51
|
+
is thrown away the moment you recover the real image. This project's
|
|
52
|
+
route to an unpacked image is `c64-ram-capture`. Run the program in the
|
|
53
|
+
emulator and capture RAM at a checkpoint past the decrunch. Also run the
|
|
54
|
+
packer-identity finding in `c64-program-recon`, which names the packer
|
|
55
|
+
when an oracle can. Come back with the captured image and start again at
|
|
56
|
+
Phase 0.
|
|
57
|
+
|
|
58
|
+
Upstream's in-place `unpack_binary` step is deliberately not carried. It is
|
|
59
|
+
destructive: it clears the comments, labels and blocks already in the store.
|
|
60
|
+
This project has a non-destructive route to the same answer.
|
|
59
61
|
|
|
60
62
|
## Phase 1 — make sure blocks are classified
|
|
61
63
|
|
|
62
64
|
Region classification is a prerequisite for everything below: a routine
|
|
63
|
-
candidate is only meaningful once the bytes around it are
|
|
65
|
+
candidate is only meaningful once you know the bytes around it are code.
|
|
64
66
|
|
|
65
67
|
1. Follow `src/skills/c64-memory-mapping/SKILL.md` for the classification pass
|
|
66
68
|
and for what each region type means.
|
|
@@ -72,60 +74,62 @@ candidate is only meaningful once the bytes around it are known to be code.
|
|
|
72
74
|
### 2.1 Build the candidate list
|
|
73
75
|
|
|
74
76
|
A routine counts as **already documented** when its entry address carries a
|
|
75
|
-
line comment. That is the only test
|
|
77
|
+
line comment. That is the only test. Do not guess from the label name.
|
|
76
78
|
|
|
77
|
-
1. Call `anno_get_symbols` for all labels — user, system and external
|
|
78
|
-
an explicit `max_results` above the program's label count
|
|
79
|
-
REQUIRED on this surface and has no default
|
|
80
|
-
a ceiling you chose
|
|
79
|
+
1. Call `anno_get_symbols` for all labels — user, system and external. Use
|
|
80
|
+
an explicit `max_results` above the program's label count. `max_results`
|
|
81
|
+
is REQUIRED on this surface and has no default. So a truncated answer is
|
|
82
|
+
always a ceiling you chose. Keep the answer. Phase 3 reuses it.
|
|
81
83
|
2. Call `anno_get_comments`, again with an explicit `max_results`. Keep that
|
|
82
|
-
too
|
|
84
|
+
too. The true match count rides beside the list. So truncation is a fact
|
|
83
85
|
you are told rather than one you infer.
|
|
84
86
|
3. **Candidate source A — cross-reference and block-derived, checked FIRST and
|
|
85
87
|
independently of whatever `anno_get_symbols` returned.** Call
|
|
86
88
|
`anno_get_blocks` with `block_type: "code"` for every code-typed range,
|
|
87
89
|
read each one with `anno_disassemble`, and collect every `jsr` target
|
|
88
|
-
address. For each candidate target,
|
|
89
|
-
list with `anno_get_cross_references
|
|
90
|
-
also the call that fills in "called from"
|
|
91
|
-
Phase 2.2
|
|
92
|
-
of whether it carries any label at all**.
|
|
93
|
-
|
|
94
|
-
store carries ZERO labels of any shape
|
|
95
|
-
cross-references, never names
|
|
96
|
-
B below finds nothing to do on
|
|
97
|
-
|
|
98
|
-
depend on step 1 having
|
|
90
|
+
address. For each candidate target, check that it is a real routine. Also
|
|
91
|
+
gather its full caller list with `anno_get_cross_references`. Use a
|
|
92
|
+
generous `max_results`. This is also the call that fills in "called from"
|
|
93
|
+
when you write up the entry in Phase 2.2. Every one of these targets is a
|
|
94
|
+
routine candidate **regardless of whether it carries any label at all**.
|
|
95
|
+
A real measured derivation run (dxa disassemble, then Ghidra import) found
|
|
96
|
+
this. A purely dxa/Ghidra-derived store carries ZERO labels of any shape.
|
|
97
|
+
Derivation writes typed ranges and cross-references, never names. So a
|
|
98
|
+
queue built only from Candidate source B below finds nothing to do on
|
|
99
|
+
such a store. It silently reports a clean, empty queue on a program
|
|
100
|
+
nothing has been named in yet. Source A does not depend on step 1 having
|
|
101
|
+
found anything.
|
|
99
102
|
4. **Candidate source B — the label-prefix path, for a store that DOES carry
|
|
100
103
|
externally-imported auto-names.** Keep a label as a routine candidate when
|
|
101
104
|
any of these holds:
|
|
102
|
-
- its name starts with `s_` (an auto-generated subroutine label)
|
|
105
|
+
- its name starts with `s_` (an auto-generated subroutine label).
|
|
103
106
|
- it sits in a code region and is the target of at least one `JSR`
|
|
104
|
-
cross-reference (`anno_get_cross_references`)
|
|
107
|
+
cross-reference (`anno_get_cross_references`).
|
|
105
108
|
- it is a `p_XXXX` label sitting **inside a code region**. These come from
|
|
106
|
-
split lo/hi immediate loads and from address tables
|
|
109
|
+
split lo/hi immediate loads and from address tables. They are almost
|
|
107
110
|
always chained raster-IRQ handlers, hardware- or shadow-vector handlers,
|
|
108
111
|
or jump-table and callback targets. Treat every one of them as a
|
|
109
|
-
candidate rather than pattern-matching specific vector addresses
|
|
112
|
+
candidate rather than pattern-matching specific vector addresses.
|
|
110
113
|
- it is the label named exactly `start`.
|
|
111
114
|
5. **Union sources A and B by address** — a routine reachable both ways counts
|
|
112
115
|
once. A store may carry either shape, or both, so neither source alone is
|
|
113
116
|
sufficient.
|
|
114
117
|
6. Drop every candidate that already carries a line comment.
|
|
115
|
-
7. What
|
|
118
|
+
7. What remains is the routine queue.
|
|
116
119
|
8. **Order it with `start` first** when `start` is in it. The entry point sets
|
|
117
|
-
the context every other routine
|
|
120
|
+
the context you read every other routine against.
|
|
118
121
|
|
|
119
122
|
### 2.2 Walk it
|
|
120
123
|
|
|
121
124
|
Take **one** entry at a time, to completion, before starting the next — the
|
|
122
125
|
queue discipline this section owns. For each entry, run `c64-program-recon`
|
|
123
126
|
`SKILL.md`'s **"Documenting one routine, end to end"** procedure (steps 1-7)
|
|
124
|
-
against the entry's explicit address
|
|
125
|
-
`anno_read_region` cap
|
|
126
|
-
its tail-call
|
|
127
|
-
|
|
128
|
-
paraphrase that procedure
|
|
127
|
+
against the entry's explicit address. That procedure has a 4096-byte
|
|
128
|
+
`anno_read_region` cap. Above the cap, use consecutive ranges. Never use a
|
|
129
|
+
raised cap. That procedure also carries its tail-call and fall-through
|
|
130
|
+
bounds rules. A `JMP shared_epilogue` still ends the routine. No return may
|
|
131
|
+
mean fall-through — say so. Do not re-derive or paraphrase that procedure
|
|
132
|
+
here.
|
|
129
133
|
|
|
130
134
|
Record per entry, for Phase 4: the address, the old label, the new label, a
|
|
131
135
|
one-line summary, and any uncertainty.
|
|
@@ -133,62 +137,67 @@ one-line summary, and any uncertainty.
|
|
|
133
137
|
### 2.3 Refresh point
|
|
134
138
|
|
|
135
139
|
When the queue is empty, read the store's revision with `anno_save_project`.
|
|
136
|
-
**It performs no write, and it exists to say so
|
|
137
|
-
surface has already committed and fsynced its own write by the time it
|
|
138
|
-
returned
|
|
139
|
-
revision — it is the checkpoint this pass
|
|
140
|
+
**It performs no write, and it exists to say so.** Every mutating verb on
|
|
141
|
+
this surface has already committed and fsynced its own write by the time it
|
|
142
|
+
returned. So there is nothing for an explicit save to flush. Record the
|
|
143
|
+
revision — it is the checkpoint you measure this pass from. It is also the
|
|
140
144
|
`base_revision` a later compare-and-swap write would quote. Everything after
|
|
141
|
-
this point re-reads the store, because Phase 2 has just changed the label
|
|
142
|
-
Phase 3 filters on.
|
|
145
|
+
this point re-reads the store, because Phase 2 has just changed the label
|
|
146
|
+
names Phase 3 filters on.
|
|
143
147
|
|
|
144
148
|
## Phase 3 — the symbol queue
|
|
145
149
|
|
|
146
150
|
### 3.1 Build the candidate list
|
|
147
151
|
|
|
148
|
-
A symbol counts as **already documented** when it has a name a human chose
|
|
149
|
-
when it is a well-known system address (hardware
|
|
150
|
-
OS variable).
|
|
152
|
+
A symbol counts as **already documented** when it has a name a human chose.
|
|
153
|
+
It also counts as documented when it is a well-known system address (hardware
|
|
154
|
+
register, KERNAL entry point, OS variable).
|
|
151
155
|
|
|
152
156
|
1. Call `anno_get_symbols` **again** — Phase 2 renamed things.
|
|
153
157
|
2. **Candidate source A — cross-reference and block-derived, checked FIRST
|
|
154
158
|
and independently of whatever label population exists.** Call
|
|
155
159
|
`anno_get_blocks` (with `include: ["enum_usage"]` where useful) for every
|
|
156
|
-
typed range
|
|
157
|
-
|
|
158
|
-
table
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
160
|
+
typed range. Then use `anno_get_cross_references` to find every address
|
|
161
|
+
referenced by one half of a split lo/hi pair. Also find any address
|
|
162
|
+
referenced by an address table. An address table is a
|
|
163
|
+
`lo_hi_address`/`hi_lo_address`/`lo_hi_word`/`hi_lo_word` range. The
|
|
164
|
+
`_address` forms produce cross-references, the `_word` forms do not, per
|
|
165
|
+
that data type's own schema distinction. Also include any address
|
|
166
|
+
referenced from a code range while it does not sit inside one. Every one
|
|
167
|
+
of these is a symbol candidate **regardless of whether it carries any
|
|
168
|
+
label at all**. The same measured derivation run found this. A purely
|
|
169
|
+
dxa/Ghidra-derived store carries ZERO labels of any shape. So Candidate
|
|
170
|
+
source B below finds nothing to do on such a store. It silently reports
|
|
171
|
+
a clean, empty queue on a program nothing has been named in yet.
|
|
167
172
|
3. **Candidate source B — the label-prefix path, for a store that DOES carry
|
|
168
173
|
externally-imported auto-names.** Keep every label whose name still
|
|
169
|
-
matches an auto-generated pattern
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
+
matches an auto-generated pattern. In the zero page, that pattern is
|
|
175
|
+
`zpp_XX`, `zpf_XX` or `zpa_XX`. Outside the zero page, that pattern is
|
|
176
|
+
`p_XXXX`, `f_XXXX`, `a_XXXX` or `e_XXXX`.
|
|
177
|
+
4. Exclude, from BOTH sources:
|
|
178
|
+
- `s_XXXX` (Phase 2 handled those).
|
|
179
|
+
- `b_XXXX` (branch targets, not data symbols).
|
|
180
|
+
- any `p_XXXX`-shaped or xref-derived candidate inside a code region
|
|
181
|
+
(also Phase 2's).
|
|
174
182
|
5. **Union sources A and B by address** — a symbol reachable both ways counts
|
|
175
183
|
once.
|
|
176
|
-
6. What
|
|
184
|
+
6. What remains is the symbol queue.
|
|
177
185
|
|
|
178
186
|
### 3.2 Walk it
|
|
179
187
|
|
|
180
188
|
Same discipline as Phase 2: explicit address, one entry at a time, to
|
|
181
189
|
completion. For each symbol, use `anno_get_cross_references` to find who
|
|
182
|
-
touches it
|
|
183
|
-
and comment it. Classify it plainly: flag, counter, pointer, state
|
|
184
|
-
buffer, table.
|
|
190
|
+
touches it. A symbol's meaning is what its callers do with it. Then rename
|
|
191
|
+
it and comment it. Classify it plainly: flag, counter, pointer, state
|
|
192
|
+
variable, buffer, table.
|
|
185
193
|
|
|
186
|
-
**No premature
|
|
194
|
+
**No premature stopping.** The symbol queue is routinely far larger than the
|
|
187
195
|
routine queue — fifty, a hundred entries is normal. Do not truncate it, do not
|
|
188
196
|
skip "secondary" symbols, and do not stop early because it is long. Feeding the
|
|
189
197
|
whole queue through is the job. Stopping early and labelling the remainder
|
|
190
|
-
"skipped for review" is a failed pass, not a completed one
|
|
191
|
-
|
|
198
|
+
"skipped for review" is a failed pass, not a completed one. The only
|
|
199
|
+
exception is a remainder you report explicitly, in full, under Phase 4's
|
|
200
|
+
leftovers table.
|
|
192
201
|
|
|
193
202
|
For naming conventions and for what any given hardware or KERNAL address
|
|
194
203
|
means, follow `src/skills/c64-memory-mapping/SKILL.md` rather than guessing.
|
|
@@ -196,16 +205,16 @@ means, follow `src/skills/c64-memory-mapping/SKILL.md` rather than guessing.
|
|
|
196
205
|
### 3.3 Refresh point
|
|
197
206
|
|
|
198
207
|
Read the revision again with `anno_save_project` and record it. No write is
|
|
199
|
-
performed
|
|
208
|
+
performed. The writes already landed.
|
|
200
209
|
|
|
201
210
|
## Phase 4 — save and report
|
|
202
211
|
|
|
203
212
|
1. Read the revision one last time with `anno_save_project` and quote it in
|
|
204
|
-
the report
|
|
213
|
+
the report. This makes the pass attributable to an exact store state.
|
|
205
214
|
2. Write the report. Four sections, all of them required:
|
|
206
215
|
|
|
207
|
-
**Regions.** How many regions
|
|
208
|
-
notable — text at a
|
|
216
|
+
**Regions.** How many regions you classified, grouped by type, plus anything
|
|
217
|
+
notable — text at a constant address, a jump table, a sprite block.
|
|
209
218
|
|
|
210
219
|
**Routines.**
|
|
211
220
|
|
|
@@ -221,10 +230,10 @@ notable — text at a fixed address, a jump table, a sprite block.
|
|
|
221
230
|
|
|
222
231
|
**Leftovers — uncertain, skipped, or still unannotated.** This section is not
|
|
223
232
|
optional and it is not allowed to be empty when the queues were not emptied.
|
|
224
|
-
List every routine and every symbol
|
|
233
|
+
List every routine and every symbol you left undone, with its address and
|
|
225
234
|
the reason. Never report "no uncertain areas" or "nothing left" while a single
|
|
226
235
|
`f_XXXX` or `a_XXXX` label is still auto-named or a queued routine is still
|
|
227
|
-
uncommented
|
|
236
|
+
uncommented. Those must be listed by name for a human to pick up.
|
|
228
237
|
|
|
229
238
|
## Phase 5 — measure the pass instead of asserting it finished
|
|
230
239
|
|
|
@@ -236,55 +245,60 @@ node src/mcp/vice/vice-proxy.ts anno coverage game.prg --store game.annostore
|
|
|
236
245
|
```
|
|
237
246
|
|
|
238
247
|
**`--store` is REQUIRED and is a second path, not a spelling of the first.**
|
|
239
|
-
`<program>` supplies the payload bytes and the load origin
|
|
248
|
+
`<program>` supplies the payload bytes and the load origin. `--store` names the
|
|
240
249
|
annotation store holding the labels, comments and typed ranges. The store holds
|
|
241
250
|
annotations and never bytes, so the verb refuses to guess either path from the
|
|
242
251
|
other.
|
|
243
252
|
|
|
244
253
|
**Dated note, 2026-08-30 — the positional is a program IMAGE, and the command
|
|
245
254
|
above is now correct against the shipped verb.** `<program>` is a `.prg` (a
|
|
246
|
-
2-byte little-endian load address followed by the payload)
|
|
247
|
-
**exactly-65536-byte** flat capture with a `.raw` or `.bin` extension
|
|
248
|
-
|
|
249
|
-
`c64-ram-capture` produces
|
|
250
|
-
previously required has **no producer left in
|
|
251
|
-
so an existing project file keeps working
|
|
252
|
-
not go looking for a step that produces
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
255
|
+
2-byte little-endian load address followed by the payload). Or it is an
|
|
256
|
+
**exactly-65536-byte** flat capture with a `.raw` or `.bin` extension. Every
|
|
257
|
+
other verb and tool on this surface already reads these two forms.
|
|
258
|
+
`c64-ram-capture` also produces these same two forms. The intermediate
|
|
259
|
+
project-file format this verb previously required has **no producer left in
|
|
260
|
+
this repo**. It is still accepted so an existing project file keeps working.
|
|
261
|
+
But nothing here writes one, so do not go looking for a step that produces
|
|
262
|
+
it.
|
|
263
|
+
|
|
264
|
+
Dispatch is by **file extension first, length second**. A short flat capture
|
|
265
|
+
is therefore refused by name: `a flat 64K capture must be exactly 65536
|
|
266
|
+
bytes`. It is not misread as a `.prg` whose first two payload bytes become
|
|
267
|
+
the load address. If you get that refusal, the capture is truncated.
|
|
268
|
+
Re-capture it, do not rename it.
|
|
259
269
|
|
|
260
270
|
Add `--out coverage.json` to keep the machine-readable report, `--force` to
|
|
261
271
|
overwrite one, and `--sample N` to widen the reproducibility sample. The verb
|
|
262
|
-
reads the same store every call in this playbook writes to
|
|
263
|
-
when the numbers are bad** — a low measurement is a result, not a
|
|
264
|
-
Non-zero means a caller error, an image it could not read, or a
|
|
265
|
-
not read at all.
|
|
272
|
+
reads the same store every call in this playbook writes to. It exits **0
|
|
273
|
+
even when the numbers are bad** — a low measurement is a result, not a
|
|
274
|
+
failure. Non-zero means a caller error, an image it could not read, or a
|
|
275
|
+
store it could not read at all.
|
|
266
276
|
|
|
267
|
-
**Run it three times:**
|
|
268
|
-
|
|
269
|
-
|
|
277
|
+
**Run it three times:**
|
|
278
|
+
- once before Phase 2, so the pass has a starting point to be compared
|
|
279
|
+
against.
|
|
280
|
+
- once at Phase 2.3's refresh point.
|
|
281
|
+
- once at the end, after Phase 4's final save.
|
|
282
|
+
|
|
283
|
+
The last run is what goes in the report.
|
|
270
284
|
|
|
271
285
|
**Read the three numbers against each other. Never quote one of them alone.**
|
|
272
286
|
There is deliberately no single "percent documented" figure, because one
|
|
273
|
-
combined number lets a weak measure hide behind a strong one
|
|
274
|
-
claim unfalsifiable:
|
|
275
|
-
|
|
276
|
-
- **A high user fraction beside a large unreached count means
|
|
277
|
-
|
|
278
|
-
reached by the descent walk from any seed
|
|
279
|
-
easily-visible part of the program
|
|
280
|
-
Phase 0 and find more entry points (chained IRQ vectors, dispatch
|
|
281
|
-
not more labels.
|
|
287
|
+
combined number lets a weak measure hide behind a strong one. That combined
|
|
288
|
+
number also makes the claim unfalsifiable:
|
|
289
|
+
|
|
290
|
+
- **A high user fraction beside a large unreached count means you named the
|
|
291
|
+
wrong things.** Every label got a human name, but most of the image was
|
|
292
|
+
never reached by the descent walk from any seed. You worked the queue over
|
|
293
|
+
the easily-visible part of the program. The rest was never entered. Go back
|
|
294
|
+
to Phase 0 and find more entry points (chained IRQ vectors, dispatch
|
|
295
|
+
tables), not more labels.
|
|
282
296
|
- **A large divergence means the store and the bytes disagree about what is
|
|
283
297
|
code.** Bytes the census reached as instructions that the store does not call
|
|
284
298
|
`Code` are places where Phase 1's classification is behind the actual control
|
|
285
299
|
flow. The reverse direction (the store calls it `Code`, the census never
|
|
286
|
-
reached it) is ordinary on an image with unreachable filler
|
|
287
|
-
chase it.
|
|
300
|
+
reached it) is ordinary on an image with unreachable filler. Read it, do
|
|
301
|
+
not chase it.
|
|
288
302
|
- **A low distinct-comment ratio means the comments are filler.** Fifty
|
|
289
303
|
addresses carrying the same sentence counts once, not fifty times. That is
|
|
290
304
|
the number that catches a pass which renamed everything and explained
|
|
@@ -300,66 +314,81 @@ This is a DIFFERENT, non-overlapping measurement from the `anno coverage`
|
|
|
300
314
|
call above — neither replaces the other. `anno coverage` is the byte-census
|
|
301
315
|
and label-ratio instrument: a derived-from-bytes census this store's own
|
|
302
316
|
block table cannot move. `anno decomp-completeness` is the
|
|
303
|
-
disagreement-gated closure gate
|
|
304
|
-
classification and its own real,
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
317
|
+
disagreement-gated closure gate. It checks four things:
|
|
318
|
+
- this fixture's byte-derived block classification and its own real,
|
|
319
|
+
observed-execution evidence agree.
|
|
320
|
+
- every code entry point carries a name and a complete purpose comment.
|
|
321
|
+
- every referenced non-hardware address resolves to a name or a decline.
|
|
322
|
+
- no auto-named survivor remains in a code region.
|
|
323
|
+
|
|
324
|
+
The disagreement query itself is a required, non-defaultable input, not an
|
|
325
|
+
optional cross-check.
|
|
310
326
|
|
|
311
327
|
```
|
|
312
328
|
node src/mcp/vice/vice-proxy.ts anno decomp-completeness --store <fixture>.annostore --disagreements <fixture>-disagreements.json --manifest src/mcp/vice/fixtures/decomp-execution-manifest.json
|
|
313
329
|
```
|
|
314
330
|
|
|
315
|
-
All three arguments are REQUIRED, and none is derived from another:
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
331
|
+
All three arguments are REQUIRED, and none is derived from another:
|
|
332
|
+
- `--store` names the annotation store.
|
|
333
|
+
- `--disagreements` names the JSON `anno evid-disagreements --store <same
|
|
334
|
+
store> --json` wrote for THIS store's own run.
|
|
335
|
+
- `--manifest` names the committed execution manifest recording which of the
|
|
336
|
+
nine fixtures were actually run under the reproducible-run protocol. It also
|
|
337
|
+
records which fixtures were declared not-executed, and why.
|
|
338
|
+
|
|
339
|
+
Omitting any of the three refuses by name rather than rendering an
|
|
340
|
+
empty-disagreement report. "The query was never run" and "the query found
|
|
341
|
+
nothing" must never read the same.
|
|
323
342
|
|
|
324
343
|
**The stop condition is a measured exit code, not a belief.** The walk
|
|
325
|
-
described in Phases 2-4 above
|
|
344
|
+
described in Phases 2-4 above finishes for a fixture when `node
|
|
326
345
|
src/skills/routine-queue-walker/scripts/completeness-report.mjs --store
|
|
327
346
|
<fixture>.annostore --disagreements <fixture>-disagreements.json --manifest
|
|
328
|
-
src/mcp/vice/fixtures/decomp-execution-manifest.json` **exits 0
|
|
329
|
-
the agent believes the queue is empty. A non-zero exit
|
|
330
|
-
exactly which measure still fails
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
347
|
+
src/mcp/vice/fixtures/decomp-execution-manifest.json` **exits 0**. It never
|
|
348
|
+
finishes just because the agent believes the queue is empty. A non-zero exit
|
|
349
|
+
names, by address, exactly which measure still fails:
|
|
350
|
+
- an Undefined byte.
|
|
351
|
+
- a surviving auto-name.
|
|
352
|
+
- an entry point missing a name or a purpose-comment element.
|
|
353
|
+
- an unresolved referenced address.
|
|
354
|
+
- an unresolved disagreement.
|
|
355
|
+
|
|
356
|
+
Go back to the corresponding phase and close it, then re-run the gate. Do
|
|
357
|
+
not report a pass from reading the rendered text alone — read the process
|
|
358
|
+
exit code.
|
|
335
359
|
|
|
336
360
|
## When something fails
|
|
337
361
|
|
|
338
362
|
- A failed call is not a reason to drop a queue entry. Log the address, the
|
|
339
|
-
call and the error
|
|
363
|
+
call and the error. Put the entry back on the queue, and carry on with the
|
|
340
364
|
next one. Report every one of those in the leftovers table.
|
|
341
|
-
- A refused write is not silent and must not be treated as one.
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
365
|
+
- A refused write is not silent and must not be treated as one. Three
|
|
366
|
+
things all come back REFUSED and named, with nothing written:
|
|
367
|
+
- a stale-revision refusal (a `base_revision` that the store has moved
|
|
368
|
+
past).
|
|
369
|
+
- an illegal label name.
|
|
370
|
+
- a scope that overlaps an existing one.
|
|
371
|
+
|
|
372
|
+
Re-read, re-derive and replay that one entry. Never widen the range or drop
|
|
373
|
+
the `base_revision` to make the refusal go away.
|
|
374
|
+
- Never invent an answer to make a queue entry go away. In the leftovers
|
|
375
|
+
table, an honest "this looks like a table, callers unclear" beats a
|
|
376
|
+
confident wrong label. The next reader would have to un-learn a wrong
|
|
377
|
+
label like that.
|
|
350
378
|
- **A genuinely unresolvable target gets a `DECLINED:` comment, never a
|
|
351
379
|
fabricated name.** When a referenced address's target is truly
|
|
352
380
|
path-dependent or otherwise cannot be determined, record it with
|
|
353
|
-
`anno_set_comment
|
|
354
|
-
unknown and why
|
|
355
|
-
|
|
356
|
-
label is worse than an absent one.
|
|
357
|
-
- **An accepted disagreement gets a `DISAGREEMENT-ACCEPTED:` comment.**
|
|
358
|
-
|
|
381
|
+
`anno_set_comment`. Use the literal prefix `DECLINED:` naming what is
|
|
382
|
+
unknown and why. `.annostore`'s own importer already uses this same
|
|
383
|
+
convention for bank-state declines. This is never a second mechanism. A
|
|
384
|
+
confident wrong label is worse than an absent one.
|
|
385
|
+
- **An accepted disagreement gets a `DISAGREEMENT-ACCEPTED:` comment.** The
|
|
386
|
+
decomposition-completeness gate's disagreement census flags a byte the
|
|
359
387
|
byte-derived block table calls `data` but the runtime evidence shows
|
|
360
|
-
executing,
|
|
388
|
+
executing. Also, a review finds the runtime evidence correct. Or the
|
|
361
389
|
disagreement is otherwise a reviewed, accepted fact rather than a
|
|
362
|
-
classification bug
|
|
363
|
-
prefix `DISAGREEMENT-ACCEPTED:` naming why
|
|
364
|
-
itself as the resolution for that
|
|
365
|
-
existing `anno_set_comment` tool
|
|
390
|
+
classification bug. When either holds, record it with `anno_set_comment`.
|
|
391
|
+
Use the literal prefix `DISAGREEMENT-ACCEPTED:` naming why. This is
|
|
392
|
+
greppable, and the gate itself reads it as the resolution for that
|
|
393
|
+
address. Both conventions ride the existing `anno_set_comment` tool.
|
|
394
|
+
Neither is a new mechanism.
|
|
@@ -75,13 +75,15 @@ export class MissingDisagreementInputError extends Error {
|
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* The frozen survivor prefix set, MIRRORED from
|
|
78
|
-
* `src/mcp/vice/anno-cli.ts`'s own frozen set --
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* script's own
|
|
84
|
-
*
|
|
78
|
+
* `src/mcp/vice/anno-cli.ts`'s own frozen set -- frozen from a real
|
|
79
|
+
* derivation run (dxa disassemble, then Ghidra import) that measured ZERO
|
|
80
|
+
* labels written by import alone, confirming the eleven prefixes had
|
|
81
|
+
* nothing populated to positively test against rather than contradicting
|
|
82
|
+
* them. Exported here, separately from the verb's own copy, because this
|
|
83
|
+
* script's own tests must be able to assert on the predicate in isolation,
|
|
84
|
+
* without a live store or a subprocess -- and because this script's own
|
|
85
|
+
* header forbids it from reading a store directly, so it cannot import the
|
|
86
|
+
* verb's copy through anything but a duplicate literal.
|
|
85
87
|
*
|
|
86
88
|
* WHAT NOT TO DO: if the frozen set in `anno-cli.ts` ever changes, this copy
|
|
87
89
|
* moves in the SAME commit, or the two renderers silently disagree about
|