@henols/c64-re-tools 1.0.0 → 2.0.0-rc.10

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.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -37
  3. package/distribution/package.ts +21 -0
  4. package/distribution/plugin.ts +122 -0
  5. package/package.json +34 -40
  6. package/skills/c64-assembler/SKILL.md +76 -0
  7. package/skills/c64-assembler/scripts/assemble.ts +110 -0
  8. package/skills/c64-assembler/scripts/compare.ts +84 -0
  9. package/skills/c64-basic/SKILL.md +66 -0
  10. package/skills/c64-basic/scripts/basic.ts +46 -0
  11. package/skills/c64-disk/SKILL.md +93 -0
  12. package/skills/c64-disk/scripts/containers.ts +124 -0
  13. package/skills/c64-disk/scripts/disk.ts +120 -0
  14. package/skills/c64-emulator/SKILL.md +134 -0
  15. package/skills/c64-knowledge/SKILL.md +166 -0
  16. package/skills/c64-knowledge/scripts/knowledge.ts +492 -0
  17. package/skills/c64-memory-map/SKILL.md +79 -0
  18. package/skills/c64-memory-map/scripts/memmap.ts +92 -0
  19. package/skills/c64-provenance/SKILL.md +110 -0
  20. package/skills/c64-provenance/scripts/compare.ts +152 -0
  21. package/skills/c64-provenance/scripts/provenance.ts +334 -0
  22. package/skills/c64-provenance/scripts/runtime.ts +191 -0
  23. package/skills/c64-provenance/scripts/separate.ts +140 -0
  24. package/skills/c64-reverse-engineering/SKILL.md +144 -0
  25. package/skills/c64-source/SKILL.md +126 -0
  26. package/skills/c64-source/scripts/generate.ts +490 -0
  27. package/skills/c64-source/scripts/loader.ts +571 -0
  28. package/skills/c64-source/scripts/opcodes.ts +61 -0
  29. package/skills/c64-source/scripts/source.ts +595 -0
  30. package/skills/c64-static-analysis/SKILL.md +119 -0
  31. package/skills/c64-static-analysis/scripts/analyze.ts +275 -0
  32. package/skills/c64-static-analysis/scripts/coverage.ts +78 -0
  33. package/skills/c64-static-analysis/scripts/findings.ts +75 -0
  34. package/skills/c64-static-analysis/scripts/idioms.ts +149 -0
  35. package/skills/c64-static-analysis/scripts/instructions.ts +105 -0
  36. package/skills/c64-testing/SKILL.md +103 -0
  37. package/skills/c64-testing/references/scenario-format.md +116 -0
  38. package/skills/c64-testing/scripts/checklist.ts +51 -0
  39. package/skills/c64-testing/scripts/run.ts +189 -0
  40. package/skills/c64-testing/scripts/scenario.ts +493 -0
  41. package/skills/c64-testing/scripts/test.ts +65 -0
  42. package/skills/c64-unpacker/SKILL.md +135 -0
  43. package/skills/c64-unpacker/scripts/evidence.ts +207 -0
  44. package/skills/c64-unpacker/scripts/loads.ts +209 -0
  45. package/skills/c64-unpacker/scripts/machine.ts +80 -0
  46. package/skills/c64-unpacker/scripts/stages.ts +130 -0
  47. package/skills/c64-unpacker/scripts/unpack.ts +683 -0
  48. package/src/c64.ts +411 -0
  49. package/src/cli/ap-sdk.ts +44 -0
  50. package/src/cli/cleanup.ts +194 -0
  51. package/src/cli/main.ts +125 -0
  52. package/src/cli/status.ts +61 -0
  53. package/src/host/main.ts +134 -0
  54. package/src/host/server.ts +392 -0
  55. package/src/host/tools/c1541.ts +284 -0
  56. package/src/host/tools/index.ts +35 -0
  57. package/src/host/tools/petcat.ts +77 -0
  58. package/src/host/tools/status.ts +47 -0
  59. package/src/host/tools/version.ts +41 -0
  60. package/src/host/vice/adapter.ts +804 -0
  61. package/src/host/vice/binary-monitor.ts +389 -0
  62. package/src/host/vice/chips.ts +118 -0
  63. package/src/host/vice/connect.ts +34 -0
  64. package/src/host/vice/process.ts +413 -0
  65. package/src/host/vice/screen.ts +184 -0
  66. package/src/host/vice/session.ts +1426 -0
  67. package/src/host/vice/text-monitor.ts +223 -0
  68. package/src/host-client/autostart.ts +55 -0
  69. package/src/host-client/connect.ts +241 -0
  70. package/src/host-client/coverage.ts +63 -0
  71. package/src/host-client/steps.ts +170 -0
  72. package/src/host-client/tools.ts +89 -0
  73. package/src/host-client/transfer.ts +126 -0
  74. package/src/host-client/vice-session.ts +316 -0
  75. package/src/knowledge/database.ts +132 -0
  76. package/src/knowledge/history.ts +163 -0
  77. package/src/knowledge/import.ts +391 -0
  78. package/src/knowledge/read.ts +342 -0
  79. package/src/knowledge/schema.ts +229 -0
  80. package/src/knowledge/write.ts +663 -0
  81. package/src/mcp/main.ts +134 -0
  82. package/src/mcp/output.ts +32 -0
  83. package/src/mcp/server.ts +316 -0
  84. package/src/mcp/tools/debug.ts +327 -0
  85. package/src/mcp/tools/execution.ts +117 -0
  86. package/src/mcp/tools/input.ts +72 -0
  87. package/src/mcp/tools/machine.ts +100 -0
  88. package/src/mcp/tools/media.ts +129 -0
  89. package/src/mcp/tools/memory.ts +250 -0
  90. package/src/mcp/tools/video.ts +271 -0
  91. package/src/native/acme.ts +116 -0
  92. package/src/native/discover.ts +179 -0
  93. package/src/native/dxa.ts +166 -0
  94. package/src/native/ghidra/analyze.ts +86 -0
  95. package/src/native/ghidra/index.ts +173 -0
  96. package/src/native/ghidra/language/c64rt_6510.cspec +34 -0
  97. package/src/native/ghidra/language/c64rt_6510.ldefs +15 -0
  98. package/src/native/ghidra/language/c64rt_6510.pspec +15 -0
  99. package/src/native/ghidra/language/c64rt_6510.slaspec +596 -0
  100. package/src/native/ghidra/scripts/C64Export.java +274 -0
  101. package/src/native/ghidra/scripts/C64Prepare.java +60 -0
  102. package/src/native/local.ts +34 -0
  103. package/src/native/processes.ts +350 -0
  104. package/src/native/run.ts +172 -0
  105. package/src/native/staging.ts +83 -0
  106. package/src/native/status.ts +113 -0
  107. package/src/native/types.ts +257 -0
  108. package/src/native/watchdog.ts +78 -0
  109. package/src/package.ts +21 -0
  110. package/src/platform.ts +441 -0
  111. package/src/project.ts +39 -0
  112. package/src/protocol/framing.ts +132 -0
  113. package/src/protocol/messages.ts +259 -0
  114. package/src/protocol/tools.ts +266 -0
  115. package/src/protocol/vice.ts +1348 -0
  116. package/src/script.ts +60 -0
  117. package/src/time.ts +15 -0
  118. package/src/trace.ts +194 -0
  119. package/THIRD-PARTY-NOTICES.md +0 -26
  120. package/bin/cli.mjs +0 -283
  121. package/skills/acme-build/SKILL.md +0 -260
  122. package/skills/acme-build/scripts/acme.mjs +0 -341
  123. package/skills/acme-build/template.a +0 -53
  124. package/skills/c64-disk-access/SKILL.md +0 -159
  125. package/skills/c64-disk-access/scripts/c1541.mjs +0 -569
  126. package/skills/c64-memory-mapping/SKILL.md +0 -631
  127. package/skills/c64-memory-mapping/memmap.json +0 -8800
  128. package/skills/c64-memory-mapping/scripts/driver.mjs +0 -553
  129. package/skills/c64-petcat/SKILL.md +0 -87
  130. package/skills/c64-petcat/scripts/petcat.mjs +0 -221
  131. package/skills/c64-program-recon/SKILL.md +0 -706
  132. package/skills/c64-program-recon/references/control-flow.md +0 -180
  133. package/skills/c64-program-recon/references/graphics.md +0 -79
  134. package/skills/c64-program-recon/references/observation-hazards.md +0 -143
  135. package/skills/c64-program-recon/references/reconstruction.md +0 -133
  136. package/skills/c64-program-recon/references/sound-and-input.md +0 -74
  137. package/skills/c64-program-recon/references/tool-selection.md +0 -78
  138. package/skills/c64-program-recon/scripts/derive.mjs +0 -364
  139. package/skills/c64-program-recon/scripts/packer-finding.mjs +0 -709
  140. package/skills/c64-program-recon/templates/memory-map.template.md +0 -101
  141. package/skills/c64-provenance-diff/SKILL.md +0 -292
  142. package/skills/c64-provenance-diff/scripts/diff-images.mjs +0 -984
  143. package/skills/c64-provenance-diff/scripts/recovery-schema.mjs +0 -395
  144. package/skills/c64-ram-capture/RELEASES.json.example +0 -17
  145. package/skills/c64-ram-capture/SKILL.md +0 -474
  146. package/skills/c64-ram-capture/scripts/compare-cross-binary.mjs +0 -525
  147. package/skills/c64-ram-capture/scripts/compare.mjs +0 -258
  148. package/skills/c64-ram-capture/scripts/derive-transients.mjs +0 -575
  149. package/skills/c64-ram-capture/scripts/dump-artifacts.mjs +0 -317
  150. package/skills/c64-ram-capture/scripts/mcp-module.mjs +0 -174
  151. package/skills/c64-ram-capture/scripts/project-paths.mjs +0 -81
  152. package/skills/c64-ram-capture/scripts/releases.mjs +0 -109
  153. package/skills/c64-ram-capture/scripts/vsf-slice.mjs +0 -147
  154. package/skills/c64-ram-capture/scripts/watch-loads.mjs +0 -581
  155. package/skills/c64-ram-capture/templates/capture-record.template.md +0 -99
  156. package/skills/c64-ram-capture/transients/README.md +0 -137
  157. package/skills/routine-queue-walker/SKILL.md +0 -394
  158. package/skills/routine-queue-walker/scripts/completeness-report.mjs +0 -465
  159. package/skills/vice-wedge-triage/SKILL.md +0 -269
@@ -1,269 +0,0 @@
1
- ---
2
- name: vice-wedge-triage
3
- description: Decide whether a VICE emulator that has stopped responding is genuinely wedged, stopped itself at your own checkpoint, crashed and respawned, or merely paused — and what is safe to do about each. Use when asked why the emulator is stuck, frozen, hung, wedged, dead or not advancing, when a cycle bracket reads zero, when vice_ping says running but nothing happens, when a checkpoint never fires, when deciding whether to recycle or restart VICE, or when a run has to be voided and its evidence recorded.
4
- ---
5
-
6
- # Triage a VICE that stopped moving
7
-
8
- **Five states look identical from outside, and the intuitive correction destroys a healthy machine
9
- in more than one of them.** Work the order below. Do not start with a remedy.
10
-
11
- | State | Cheap tell | Safe action |
12
- |---|---|---|
13
- | **Merely paused** | Any state read pauses the machine and does not resume it | Resume once. Nothing is wrong |
14
- | **Stopped itself at your checkpoint** | An armed *stopping* checkpoint on the live IRQ path | Delete/disable the checkpoint. **Never recycle** |
15
- | **Crashed and respawned** | The proxy raises epoch drift on the next forwarded call | Void the run, reboot from scratch. Already handled for you |
16
- | **Genuinely wedged** | Two consecutive cycle brackets read exactly `0` | `vice_recycle` with a reason, as a last resort |
17
- | **Contended between the two channels** | Two brackets would read zero, but `evidence.channelContention.held` is true and `bracketsRun` is 0 | Wait for the other channel's operation to finish, or find its holder. **Never recycle** — the instance is healthy and is answering |
18
- | **Monitor held elsewhere** | A second client already holds this instance's single binary-monitor socket | Find the other holder. **Never recycle** — the instance is healthy, just claimed elsewhere |
19
-
20
- ```
21
- mcp__plugin_c64-re-tools_vice__vice_diagnose # one call, no arguments, answers which state it is
22
- ```
23
-
24
- `vice_diagnose`'s verdict vocabulary is `restarted`, `checkpoint_trap`, `wedged`,
25
- `monitor_held_elsewhere`, `live`. The binary monitor services exactly one client, so a second
26
- connection sits unserviced in the backlog with no reply and no EOF — a state that looks exactly
27
- like a wedge unless `vice_diagnose` names it as `monitor_held_elsewhere`. Read the tool's own
28
- schema for the exact contract: `tools/list`'s advertised schema is the real manifest entry.
29
- `vice_diagnose` can also answer a `diagnosis_unavailable` outcome when no verdict could be
30
- established at all. That is **not** a sixth verdict. See the table below.
31
-
32
- ## The order
33
-
34
- 1. **Call `vice_diagnose` first.** It runs the checks in the cheap-to-expensive order and stops at
35
- the first that fires: the epoch comparison costs zero emulator calls, the checkpoint-trap check
36
- costs three reads and **no resume**, and only then does it measure a cycle bracket.
37
- 2. **Read the verdict, not the vibe.** Each verdict has exactly one correct response — the table
38
- below. A verdict is not a suggestion to try things.
39
- 3. **`diagnose` leaves the machine paused** when it ran a bracket. Resuming is your own next call.
40
- Do not treat "still paused afterwards" as a symptom. Every established verdict also reports
41
- `machinePaused` plus `machinePausedSource` (07-15). These fields let you tell an actual
42
- observation from an inference. `observed` means a wire `stopped`/`resumed`/`jam` event directly
43
- reported the state. `structural` means the diagnosis inferred the state. It used the fact that
44
- every stock read stops the machine, not a specific event. `no_session` means no session was
45
- ever obtained — for example, the `monitor_held_elsewhere` verdict, or a `diagnosis_unavailable`
46
- acquisition failure. In that case, the answer makes no claim about pause state at all.
47
- 4. **If the verdict is `wedged`, capture evidence before recovering.** `vice_recycle` requires a
48
- `reason`. It writes that string verbatim into a permanent incident record under
49
- `.c64-re-tools/incidents/` **before the recycle kills anything**. That record is the evidence
50
- capture — there is no separate ritual to perform, and a lazy `reason` is a lost incident.
51
- 5. **Recycling changes the restart epoch.** Any run in flight is void. Resume from the last
52
- recorded milestone snapshot, never from where the wedge happened.
53
-
54
- ## Verdict → response
55
-
56
- | Verdict | What it means | Do |
57
- |---|---|---|
58
- | `live` | Cycles advanced | Resume and carry on. Suspect your own checkpoint conditions, not the emulator — **unless `evidence.jamObserved` is true, or `evidence.channelContention.held` is true** (both below) |
59
- | `checkpoint_trap` | The machine stopped **itself** at an armed checkpoint | `vice_checkpoint_delete` or `vice_checkpoint_toggle` it, or `vice_execution_step` past it, then re-run `diagnose`. **Recycling here destroys a healthy instance** |
60
- | `restarted` | The epoch changed — a crash-and-respawn already happened | The run is void. `c64-ram-capture` § Void a run gives the artifact procedure. Reboot from `vice_disk_attach` |
61
- | `monitor_held_elsewhere` | A different client already holds this instance's single binary-monitor slot | Release or identify the other holder. **Never a reason to recycle** — recycling here destroys an instance that is not even wedged |
62
- | `wedged` | Two brackets, zero cycles, no epoch change | Last resort: `vice_recycle` with a real reason — **but check `evidence.jamObserved` first** (below). `wedged` is structurally unreachable while `evidence.channelContention.held` is true — reaching `wedged` at all means no monitor channel held halt authority when the brackets ran |
63
- | `diagnosis_unavailable` **(non-verdict outcome — not one of the five)** | No verdict could be established at all. The message starts `vice_diagnose: diagnosis_unavailable (<reason>)`. The machine's state is **UNKNOWN**, not any of the five above | **Do not recycle on this answer alone.** Read the reason class in the message and act on it — see below |
64
-
65
- ### `evidence.jamObserved` — read it before acting on `wedged` *or* `live`
66
-
67
- Every `vice_diagnose` verdict carries `evidence.jamObserved` (always present, never omitted).
68
- It is `true` once a `JAM` (0x61) event has arrived on this instance's wire — the 6510 executed an
69
- illegal opcode and **the CPU is dead regardless of the verdict above**. The flag latches: it stays
70
- true for the rest of the session.
71
-
72
- It cuts across two verdicts in opposite directions, which is exactly why it is separate evidence
73
- rather than a sixth verdict:
74
-
75
- | `jamaction` | What `vice_diagnose` answers | Why | What to actually do |
76
- |---|---|---|---|
77
- | `-jamaction 2` (Monitor) | `wedged` | The machine stopped, so both brackets read zero advance | **`vice_machine_reset`, not `vice_recycle`.** Recycling destroys an instance a reset recovers — the same trap as `checkpoint_trap` |
78
- | default (continue) | `live` | The emulator keeps burning cycles refetching the same opcode, so both brackets **advance** | **`vice_machine_reset`.** "Cycles advanced" is true and irrelevant: the machine will never execute another instruction |
79
-
80
- **`jamObserved: true` is never a reason to recycle.** A reset recovers a jam. The instance
81
- itself is healthy. Treat a `live` verdict with `jamObserved: true` as a false negative on liveness,
82
- and a `wedged` verdict with it as a false positive on wedging.
83
-
84
- ### `evidence.channelContention` — read it before acting on `wedged` or `live`
85
-
86
- Every `vice_diagnose` verdict carries `evidence.channelContention` (always present, never
87
- omitted). `held` is `true` when the **other** monitor channel — the binary monitor or the
88
- `-remotemonitor` text channel, whichever this call is not — currently holds this instance's halt
89
- authority. When `held` is `true` the verdict is always `live`, with `evidence.bracketsRun: 0` —
90
- meaning **no cycle bracket ran at all**, not that a bracket ran and read zero. The four detail
91
- fields name the holder: `channel` (`"binary"` or `"text"`), `operation`, `grantId`, and `heldMs` (a
92
- non-negative whole-millisecond duration) — all four are `null` when `held` is `false`.
93
-
94
- **`channelContention.held: true` is never a reason to call `vice_recycle`.** The instance is
95
- healthy and is answering. The remedy is to wait for the other channel's operation to finish, or to
96
- find its holder, exactly as the opening table's contention row says.
97
-
98
- Deliberate asymmetry with `jamObserved`, worth stating so a later reader does not "correct" it:
99
- `jamObserved` qualifies `wedged` in this prose above and still lets that verdict return — the caller
100
- has to read the evidence and choose not to recycle. `vice_diagnose`'s own handler guards
101
- `channelContention` in **code** and makes `wedged` structurally unreachable while a foreign hold is
102
- live — the only route to `wedged` is through a liveness bracket, and a bracket only runs when this
103
- instance's own diagnose call actually held the lock. The two are different on purpose: the cost of
104
- being wrong about contention is a destructive recycle of a healthy instance, not merely a
105
- misdiagnosis.
106
-
107
- ### `diagnosis_unavailable` — reason classes and response (07-15)
108
-
109
- `diagnosis_unavailable` is what `vice_diagnose` answers, on the `isError:true` channel, when it
110
- could not reach any of the five verdicts above — including a protocol-decode failure. It is
111
- never added to the verdict enum and is never grounds to `vice_recycle` by itself: the message says
112
- so explicitly. **Every** `isError:true` answer this tool can produce carries this prefix — there is
113
- no unclassified no-verdict path left. Eight reason classes exist, each with
114
- its own next move:
115
-
116
- | Reason | What it means | Do |
117
- |---|---|---|
118
- | `connection_lost` | The socket died mid-session | Retry once. If it recurs, treat as a real transport problem, not a wedge |
119
- | `request_timeout` | The wire went silent past the request bound | Retry once. If it recurs, fall to the manual cycle bracket below |
120
- | `monitor_acquisition_timeout` | Another client holds the monitor and the wait bound expired | Wait for the current holder to release, then retry — this is the bounded sibling of `monitor_held_elsewhere`, not a wedge. **The abandoned acquisition is not cancelled**: a session may be established moments after this answer, so a later-appearing held session is not a ghost. The system writes its real outcome to stderr |
121
- | `session_refused` | The broker/lease itself refused the session | Read the raw detail in the message. This is a broker-level problem, not an emulator state |
122
- | `protocol_decode_failure` | This build answered a frame the client cannot decode | Report it as a tool defect. Check `docs/stock-vice-parity.md`'s `CPUHISTORY_GET` history for a known class of this. Then fall back to the manual cycle bracket below |
123
- | `evidence_gathering_failed` | `vice_diagnose` obtained a session, but a read needed to build the verdict failed | `vice_execution_run` may be needed to unstick a stalled read path, then retry |
124
- | `liveness_unmeasurable` | The liveness bracket could not be **measured at all** — no `CPUHISTORY_GET` (needs VICE ≥ 3.10) and no `LIN`/`CYC` enumerated. **The expected outcome on a stock 3.9-class build**, e.g. every current Debian/Ubuntu package | **Not a wedge and not a tool defect.** A bracket that cannot measure is not one that measured zero. Judge liveness from outside the monitor (screenshot, process state). Retrying will produce the same answer |
125
- | `unknown` | None of the above classified the failure | Read the raw detail in the message. Retry once before escalating |
126
-
127
- ## What is not recoverable
128
-
129
- **A checkpoint trap may be the onset without being the whole story.** In the one recorded
130
- incident of this kind, checkpoint delete, then a soft reset, then a hard reset, then an explicit single step **all** left
131
- the machine frozen, in sequence. Deleting the checkpoint is not guaranteed to unfreeze anything.
132
-
133
- If a bracket still reads zero after the checkpoint is gone, the verdict becomes `wedged` and
134
- recycle is the fallback after all.
135
-
136
- **A stalled session cannot be corrected from inside.** The instance is granted on the session's
137
- first forwarded call and is that session's for its whole life, so a subagent inherits the same
138
- stalled instance. Before `vice_recycle` existed, the only exit was abandoning the session. That
139
- is still the exit if recycle itself cannot land.
140
-
141
- ## Two traps that read as a wedge and are not
142
-
143
- **`vice_ping`'s `execution` field is not a liveness signal.** A stalled host VICE answers
144
- `status: "ok", execution: "running"` continuously and indefinitely. So does a machine that stopped
145
- at a checkpoint — VICE's flag flips before the trap fires. Checkpoint bookkeeping
146
- (`vice_checkpoint_add`/`list`/`delete`) also keeps returning healthy, self-consistent responses
147
- throughout a real wedge, so "the tools respond" proves nothing.
148
-
149
- **A `vice_run_until` on an address that is never reached looks exactly like a wedge.** Its
150
- `cycles` parameter is *"not yet implemented"*, and the tool REFUSES it rather than ignoring it —
151
- including alongside `address`, where it used to be silently dropped while the answer still
152
- reported `reached: true`. The tool refuses unexpected argument names by name too, so a
153
- `timeoutMs`/`timeout_ms` typo can no longer run with the default bound in silence. **This is
154
- bounded:** pass `timeout_ms` (default 30000, clamped to a ceiling of 600000). An unreachable
155
- address returns an explicit, bounded `timedOut: true` answer — with the temporary checkpoint
156
- already cleaned up — rather than looking like a wedge. **Two further behaviours:** every
157
- non-error answer, hit or timeout, carries `machineHalted` plus a `machineHaltedNote` naming the
158
- resume call — the tool halts the machine on every read and says so explicitly. **`machineHalted`
159
- is `true` on a hit and on a timeout whose cleanup delete got an answer** (`cleanup: "deleted"` /
160
- `"already_gone"`). **It is `false` when `cleanup: "delete_failed"` or the socket is already gone**
161
- and the run-state projection does not say `"stopped"`. **Do not read `machineHalted: false` as
162
- "still running"** — it means nothing here could establish the state, `machineHaltedNote` says so,
163
- and the next call should be `vice_diagnose`, not `vice_execution_run` (which may not reach the
164
- instance at all). And a timeout whose cleanup delete lands on an already-gone race no longer
165
- asserts `reached: false` outright: it reads the program counter and resolves the race
166
- (`raceResolved: "pc_at_address"` / `"pc_elsewhere"`), or, if the PC read itself fails, omits
167
- `reached` entirely and reports `reachedUnknown: true` (`raceResolved: "unresolved"`). **An absent
168
- `reached` is not "false"** — check `reachedUnknown` before assuming a miss. The underlying
169
- judgement stays the same and is still the right first question: before concluding anything, check
170
- whether you asked the machine to run to an address it cannot reach. **Confidence: HIGH for the
171
- reach/timeout mechanism** — live-checked against genuine, unmodified `/usr/bin/x64sc` (VICE
172
- 3.9) and `/usr/local/bin/x64sc` (VICE 3.10): a real KERNAL address ($EA31) reached within its
173
- timeout, an unreached one ($C000) timing out with the checkpoint deleted. **MEDIUM for the
174
- honesty fields above** (`machineHalted`, `raceResolved`, `reachedUnknown`) — unit-proven
175
- (`stock-run-until.test.ts`, 21/21) but not independently re-exercised against a real emulator.
176
-
177
- **A second binary-monitor client is contention, not a wedge, and it has a cheap tell.** The
178
- binary monitor services exactly one client. A second `connect()` sits
179
- unserviced in the backlog with no reply and no EOF. The discriminator: a socket that *accepts* the
180
- connection but never answers is contention, not a hung emulator — and the broker itself already
181
- knows whether it holds a lease on that port, which is the thing a human or agent can actually go
182
- check instead of guessing. Named causes, so a reader knows where to look: a hand-run `nc` session
183
- left open against the port, a second Claude Code session driving the same instance, VICE's own
184
- `-remotemonitor`, and any other 6502 debugger that dials in. **This plugin's own annotation route
185
- can never be one of them:** the `anno_*` tool surface and the `anno` CLI are pure store-and-image
186
- readers — they open a SQLite annotation store and decode bytes out of a file on disk, and there is
187
- no emulator connection anywhere on that path to contend for the port. That is a structural
188
- property of what those calls do, not a documented promise, so a user chasing a silent emulator can
189
- rule this project's own annotation tooling out immediately rather than suspecting it. The standing
190
- advice does not change: contention is **never** a reason to recycle — the instance is healthy,
191
- merely claimed elsewhere.
192
-
193
- ## The manual fallback, when `vice_diagnose` cannot answer
194
-
195
- `vice_diagnose` needs the host broker running. When it reports that no `broker.json` record
196
- exists, that is a **host action for a human** — say so and stop. Nothing container-side may reach
197
- the emulator by another route.
198
-
199
- When the broker is up but you want the raw measurement, the cycle bracket is the only trustworthy
200
- liveness test. There is no non-pausing call at all — any inbound byte halts the machine, so a
201
- `vice_ping` poll during the wait would measure nothing. The procedure is four calls with **zero
202
- calls during the wait**:
203
-
204
- 1. `vice_cycles_stopwatch` `{action: "reset"}`
205
- 2. `vice_execution_run`
206
- 3. A real wall-clock wait, with **no calls at all** during it
207
- 4. `vice_cycles_stopwatch` `{action: "read"}`
208
-
209
- **Exactly `0`, twice in a row, is a wedge.** Cycles advancing but far below the expected rate for
210
- a real wait is a third thing — merely slow, a separate documented hazard measured at ~6,000/s
211
- when a loop polls without re-resuming instead of waiting clean. Never poll with a state read.
212
- Those pause and do not resume.
213
-
214
- `vice_diagnose` already runs exactly this bracket internally, so the manual fallback above is
215
- only for when the broker itself is unreachable and `vice_diagnose` cannot be called at all.
216
-
217
- **Enumerate your own checkpoints before running any bracket.** `vice_checkpoint_list`, then
218
- resolve the live IRQ handler (`$0314/$0315`, or `$FFFE/$FFFF` when `$01` has the ROMs banked out).
219
- An armed stopping checkpoint at or inside the live IRQ path, with the PC pinned at or just past
220
- it, is the trap signature — and reaching that verdict needs **no `vice_execution_run`**, which
221
- matters because `vice_execution_run` is this project's leading crash suspect (six outages in one
222
- session, the last three all on that call).
223
-
224
- ## Provenance
225
-
226
- | Claim | Evidence | Confidence |
227
- |---|---|---|
228
- | The wedge signature: zero cycles, `ping` says running, PC byte-identical across pause/resume/step | Four live incidents across two disk images and two sessions. Three independent zero-cycle brackets in one | HIGH |
229
- | `vice_ping`'s `execution` field is not liveness | Checked twice independently | HIGH |
230
- | The cycle bracket is the only trustworthy liveness test | Measured both ways — 21,551,860 cycles on a healthy instance, exactly 0 twice on a stalled one | HIGH |
231
- | Epoch drift is surfaced automatically, and self-heals on the next call | Live, twice. Both incidents self-healed within the session | HIGH |
232
- | Checkpoint delete / reset / step can all fail to recover | One recorded incident, all four attempts in sequence | HIGH, single incident |
233
- | A checkpoint trap explains all three recorded "silent stalls" | Cross-read, 3/3 correlation, mechanism consistent with every symptom — **not reproduced** | MEDIUM |
234
- | `vice_diagnose`'s five-verdict path behaves as its schema says | Schema read, and cross-checked against the tracked implementation's own report builders. **Not exercised end to end** | MEDIUM |
235
- | Stock's five-verdict path (`restarted`, `checkpoint_trap`, `wedged`, `monitor_held_elsewhere`, `live`) and its bounded `vice_run_until` | Unit-proven (40/40 `stock-diagnose.test.ts`, 21/21 `stock-run-until.test.ts`). **Live-proven** against genuine `/usr/bin/x64sc` (VICE 3.9) and `/usr/local/bin/x64sc` (VICE 3.10) for `live`, `checkpoint_trap`, and `wedged`, and also for `restarted`. The first three were checked on both capability routes — `frame_position` on 3.9, `cpu_history` on 3.10. `monitor_held_elsewhere`'s **socket-level** contention bound is live-proven (~1501-1502ms against a 1500ms bound). This was **UPDATED 2026-08-18**, using `stock-live-broker-monitor.test.ts`, command `VICE_LIVE_BROKER_BIN=/usr/bin/x64sc` (or `/usr/local/bin/x64sc`) `node --test stock-live-broker-monitor.test.ts`. Both remaining unit-only residuals are now ALSO live-proven, on both binaries, in one real run. The first is the **broker-mediated** `monitor_held_elsewhere` verdict. It is a real second `claimMonitor()` refusal from a genuine host broker daemon, naming the other real grant's id. It settled in 1ms against the 10000ms bound. The second is the **broker-supervised** (not test-performed) `restarted` respawn. The host broker's OWN crash supervision relaunched the killed instance. `vice_diagnose` answered `restarted` with `baselineEpoch:1`/`currentEpoch:2` at zero-to-minimal emulator cost. `vice_run_until`'s reach/timeout mechanism is live-proven against both binaries. Its honesty fields (`machineHalted`, `raceResolved`, `reachedUnknown`) remain unit-proven only — NOT re-exercised live, no blanket claim made here | HIGH for the five verdicts (including both the broker-mediated `monitor_held_elsewhere` path and the broker-supervised `restarted` path, both now live-proven) and the run_until reach/timeout mechanism. MEDIUM for the run_until honesty fields only, which stay unit-only |
236
- | `evidence.channelContention`: always present, `wedged` unreachable while contended, verdict `live` with `bracketsRun:0` while a foreign hold is live | Unit-proven in `stock-diagnose.test.ts` (65/65) — the always-present field on every verdict, including both `session === null` paths. This covers the `live`-with-`bracketsRun:0` answer on a foreign hold, and the fact that `wedged` is never reached while contended. It also covers the discriminating-power case, where an uncontended double-zero still answers `wedged`. **Live-reproduced against genuine stock `/usr/bin/x64sc` (VICE 3.9) only** — 2026-09-09, using command `VICE_LIVE_STOCK_BIN=/usr/bin/x64sc node --test text-monitor-live.test.ts`. With the text channel holding a real `device c:` command, a concurrent `vice_diagnose` answered `verdict=live, evidence={"bracketsRun":0,"jamObserved":false,"channelContention":{"held":true,"channel":"text","operation":"device c:","grantId":"unknown","heldMs":3499}}`. After release, the same call answered `verdict=live, evidence={"bracketsRun":1,...,"channelContention":{"held":false,"channel":null,"operation":null,"grantId":null,"heldMs":null}}`, showing a real bracket ran once uncontended | **MEDIUM** — a single binary's basis: the text channel is a stock-backend-only capability, so it is proven where it ships (genuine stock `/usr/bin/x64sc`, VICE 3.9). It is not proven against a second binary, the way the neighbouring HIGH row above is |
237
-
238
- **Log a new incident in your own project notes at the moment you hit it**, graded the same way
239
- (`Evidence:` and `Confidence:`). Promote a grade by re-logging, never by editing the old entry in
240
- place. A defect in the `vice` MCP tools themselves is worth filing as a bug rather than working
241
- around inline — the workaround outlives the memory of why it was needed.
242
-
243
- ## Which skill does what
244
-
245
- This one owns "is the machine alive, and what may I do to it". It does not restate what the
246
- others carry.
247
-
248
- | Need | Go to |
249
- |---|---|
250
- | Every way a live *read* gives a wrong answer | `c64-program-recon` — `references/observation-hazards.md` |
251
- | Which address to read next, and what the answer rules out | `c64-program-recon` |
252
- | Voiding a capture, and the artifact rename procedure | `c64-ram-capture` — § Void a run |
253
- | What a specific address or bit means | `c64-memory-mapping` |
254
- | **Whether the emulator is wedged, and whether to recycle** | here |
255
-
256
- ## Troubleshooting
257
-
258
- | Symptom | Correct |
259
- |---|---|
260
- | `vice_diagnose` reports no `broker.json` record exists | The host broker was never started. A human must start it on the host. There is no container-side workaround |
261
- | The machine is still paused after `vice_diagnose` | Expected — it leaves it paused after a bracket. Resume with `vice_execution_run` |
262
- | `vice_ping` says `running`, nothing advances | Not liveness. Run a bracket, or call `vice_diagnose` |
263
- | The checkpoint never fired | Most state reads pause the emulator. Resume exactly once after every read |
264
- | Zero cycles, and a checkpoint is armed on the IRQ handler | `checkpoint_trap`, not a wedge. Do not recycle |
265
- | Zero cycles, nothing armed, epoch unchanged | A wedge. `vice_recycle` with a reason that names the evidence |
266
- | A run "survived a reset" | Distrust it. You cannot read the epoch to check. But an unintended respawn inside the bracket would have raised a drift error on the next forwarded call. So the absence of that error is the only evidence available |
267
- | `vice_recycle` refused for a missing reason | It is required, by design — the reason *is* the incident record |
268
- | `vice_diagnose` answers `monitor_held_elsewhere`, or a call hangs with no reply and no EOF | Not a wedge. Find the other client holding this instance's single binary-monitor slot |
269
- </content>