c64bridge 1.0.7 → 1.1.0

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 (132) hide show
  1. package/.github/skills/assembly-program/SKILL.md +37 -0
  2. package/.github/skills/basic-program/SKILL.md +40 -0
  3. package/.github/skills/cross-platform-demo/SKILL.md +32 -0
  4. package/.github/skills/drive-manager/SKILL.md +33 -0
  5. package/.github/skills/graphics-demo/SKILL.md +30 -0
  6. package/.github/skills/hello-world/SKILL.md +36 -0
  7. package/.github/skills/memory-debug/SKILL.md +32 -0
  8. package/.github/skills/pr-converge/SKILL.md +33 -0
  9. package/.github/skills/printer-job/SKILL.md +30 -0
  10. package/.github/skills/sid-music/SKILL.md +35 -0
  11. package/.github/skills/software-launch/SKILL.md +31 -0
  12. package/.github/skills/stream-control/SKILL.md +29 -0
  13. package/.github/skills/system-control/SKILL.md +32 -0
  14. package/AGENTS.md +28 -7
  15. package/README.md +69 -14
  16. package/data/assembly/assembly-spec.md +2 -0
  17. package/dist/c64Client.js +238 -60
  18. package/dist/config.js +48 -26
  19. package/dist/device.js +92 -37
  20. package/dist/diagnostics.js +62 -2
  21. package/dist/hostPaths.js +128 -0
  22. package/dist/loggingHttpClient.js +59 -1
  23. package/dist/mcp-server.js +409 -286
  24. package/dist/numericArg.js +55 -0
  25. package/dist/petscii.js +13 -1
  26. package/dist/platform.js +12 -3
  27. package/dist/prompts/registry.js +6 -1
  28. package/dist/tools/access.js +66 -0
  29. package/dist/tools/debug.js +6 -0
  30. package/dist/tools/developer.js +67 -34
  31. package/dist/tools/errors.js +54 -3
  32. package/dist/tools/input.js +23 -6
  33. package/dist/tools/machineControl.js +112 -20
  34. package/dist/tools/memory.js +135 -30
  35. package/dist/tools/meta/artifacts.js +5 -5
  36. package/dist/tools/meta/background.js +216 -54
  37. package/dist/tools/meta/compilation.js +6 -5
  38. package/dist/tools/meta/config.js +15 -6
  39. package/dist/tools/meta/filesystem.js +16 -14
  40. package/dist/tools/meta/graphics.js +48 -20
  41. package/dist/tools/meta/memory.js +45 -19
  42. package/dist/tools/meta/program.js +10 -7
  43. package/dist/tools/meta/screen.js +116 -25
  44. package/dist/tools/pauseGuard.js +23 -0
  45. package/dist/tools/pollValidator.js +53 -8
  46. package/dist/tools/programRunners.js +62 -18
  47. package/dist/tools/registry/batch.js +16 -4
  48. package/dist/tools/registry/config.js +9 -2
  49. package/dist/tools/registry/disk.js +12 -2
  50. package/dist/tools/registry/drive.js +3 -0
  51. package/dist/tools/registry/extract.js +3 -0
  52. package/dist/tools/registry/graphics.js +59 -6
  53. package/dist/tools/registry/index.js +17 -1
  54. package/dist/tools/registry/memory.js +11 -1
  55. package/dist/tools/registry/platform.js +4 -1
  56. package/dist/tools/registry/printer.js +2 -1
  57. package/dist/tools/registry/program.js +5 -1
  58. package/dist/tools/registry/rag.js +1 -0
  59. package/dist/tools/registry/sound.js +12 -1
  60. package/dist/tools/registry/stream.js +2 -1
  61. package/dist/tools/registry/system.js +8 -1
  62. package/dist/tools/schema.js +7 -7
  63. package/dist/tools/streaming.js +34 -4
  64. package/dist/tools/translation/assembler.js +247 -24
  65. package/dist/tools/translation/basicDetokenizer.js +80 -0
  66. package/dist/tools/translation/basicTokenizer.js +8 -3
  67. package/dist/tools/translation/disassembler.js +14 -0
  68. package/dist/tools/types.js +258 -7
  69. package/dist/tools/vice.js +5 -16
  70. package/dist/vicBitmap.js +1 -1
  71. package/dist/vice/mockServer.js +3 -0
  72. package/dist/vice/process.js +19 -0
  73. package/dist/vice/resourcePolicy.js +36 -0
  74. package/dist/vice/viceClient.js +22 -5
  75. package/doc/vice/support-matrix.md +17 -11
  76. package/mcp/schemas/c64_batch.schema.json +2 -1
  77. package/mcp/schemas/c64_config.schema.json +25 -475
  78. package/mcp/schemas/c64_debug.schema.json +3 -602
  79. package/mcp/schemas/c64_disk.schema.json +17 -344
  80. package/mcp/schemas/c64_drive.schema.json +2 -157
  81. package/mcp/schemas/c64_extract.schema.json +10 -364
  82. package/mcp/schemas/c64_graphics.schema.json +19 -426
  83. package/mcp/schemas/c64_input.schema.json +5 -318
  84. package/mcp/schemas/c64_memory.schema.json +23 -491
  85. package/mcp/schemas/c64_printer.schema.json +3 -258
  86. package/mcp/schemas/c64_program.schema.json +16 -491
  87. package/mcp/schemas/c64_rag.schema.json +1 -81
  88. package/mcp/schemas/c64_select_backend.schema.json +2 -36
  89. package/mcp/schemas/c64_sound.schema.json +20 -822
  90. package/mcp/schemas/c64_stream.schema.json +4 -75
  91. package/mcp/schemas/c64_system.schema.json +28 -348
  92. package/mcp/schemas/c64_vice.schema.json +2 -71
  93. package/mcp/server.json +1 -1
  94. package/mcp/tools.json +1983 -6910
  95. package/mcp.json +21 -1
  96. package/package.json +16 -8
  97. package/scripts/mockC64Server.mjs +44 -0
  98. package/scripts/run-coverage.mjs +27 -1
  99. package/scripts/smoke-run.sh +3 -2
  100. package/scripts/start.mjs +23 -1
  101. package/server.json +35 -2
  102. package/dist/.tsbuildinfo +0 -1
  103. package/doc/agent-state-spec.md +0 -243
  104. package/doc/audit/secret-handling.md +0 -61
  105. package/doc/plans/bun/baseline-metrics.md +0 -16
  106. package/doc/plans/bun/migration-checklist.md +0 -55
  107. package/doc/plans/bun/migration.md +0 -83
  108. package/doc/plans/improve-knowledge/notes.md +0 -43
  109. package/doc/plans/improve-knowledge/plan.md +0 -121
  110. package/doc/plans/improve-knowledge/rollout.md +0 -110
  111. package/doc/plans/meta-tools/README.md +0 -110
  112. package/doc/plans/meta-tools/analysis.md +0 -272
  113. package/doc/plans/meta-tools/catalog.md +0 -435
  114. package/doc/plans/meta-tools/notes.md +0 -192
  115. package/doc/plans/meta-tools/rollout.md +0 -368
  116. package/doc/plans/prompts/README.md +0 -96
  117. package/doc/plans/prompts/user-prompt-ideas.md +0 -147
  118. package/doc/plans/tool-consolidation/notes.md +0 -28
  119. package/doc/plans/tool-consolidation/plan.md +0 -169
  120. package/doc/plans/tool-consolidation/rollout.md +0 -104
  121. package/doc/plans/vice-backend-switch/plan.md +0 -486
  122. package/doc/plans/vice-backend-switch-2/plan.md +0 -48
  123. package/doc/plans/vice-support/notes.md +0 -63
  124. package/doc/plans/vice-support/plan.md +0 -88
  125. package/doc/plans/vice-support/rollout.md +0 -99
  126. package/doc/reviews/audit.md +0 -143
  127. package/doc/reviews/hardening/01-fable/prompt-handover.md +0 -125
  128. package/doc/reviews/hardening/01-fable/prompt.md +0 -98
  129. package/doc/reviews/hardening/01-fable/review-prompt.md +0 -468
  130. package/doc/reviews/hardening/01-fable/review.md +0 -1234
  131. package/doc/reviews/vice-support-review.md +0 -185
  132. package/scripts/compare-rest/rest-compare-2025-11-01T00-25-21-830Z.json +0 -2513
@@ -1,88 +0,0 @@
1
- # Vice Support Plan
2
-
3
- Objective: Provide first‑class VICE emulator support in the MCP server using the Binary Monitor (BM) protocol, achieving parity where feasible with the C64U REST experience, and making platform capabilities explicit to clients.
4
-
5
- ## Required Reading
6
-
7
- - doc/vice/vice-binary-monitor-spec.md — VICE BM protocol (framing, commands, errors).
8
- - doc/vice/vice-bm-vs-c64u-rest.md — Feature comparison and integration guidance.
9
- - Tip from recent work (kept in mind when reading): BM halts CPU while servicing requests; explicitly resume with `0xAA Exit` between polls so emulation progresses (we use this in `src/vice/readiness.ts` and the smoke test).
10
-
11
- ## Goals
12
-
13
- - Implement a robust, typed BM client with request multiplexing and error translation.
14
- - Enable practical day‑to‑day flows under VICE: program runs, screen capture, memory IO, pause/resume, soft reset.
15
- - Introduce optional debugger workflows (breakpoints, registers, stepping) as a distinct capability set.
16
- - Keep clear boundaries: do not emulate hardware‑only features (drives, firmware config, streaming) on VICE.
17
-
18
- ## Scope (What we will support on VICE)
19
-
20
- - Memory: read/write main memory (initially comp memspace, bank=`0`) with safe bounds.
21
- - Screen: `read_screen` via `$0400..$07E7` and ASCII conversion; polling via `wait_for_text`.
22
- - System: soft reset; pause/resume by entering/exiting monitor; version/info probe.
23
- - Runners: prefer RAM injection on a long‑lived VICE process (BASIC pointer patch + keyboard feed), with BM `0xDD` autostart as a fallback; per‑run `x64sc -autostart` only as a last resort. BASIC/ASM helpers preserved.
24
- - Debugging (later phase): breakpoints, step/next, execute‑until‑return, register read/write; optional framebuffer snapshot.
25
-
26
- ## Out of Scope (remain unsupported on VICE)
27
-
28
- - Drives and file image creation (D64/D71/D81/DNP) — REST only.
29
- - Hardware streaming (video/audio/debug) — REST only. For VICE, prefer ad‑hoc captures.
30
- - Firmware configuration (`/v1/configs` load/save/reset) — replace with VICE “resources” in a separate emulator‑specific module when needed.
31
-
32
- ## Technical Design
33
-
34
- 1) Vice Binary Monitor Client (`src/vice/viceClient.ts` + `src/vice/readiness.ts`)
35
- - Transport: TCP to `127.0.0.1:6502` (configurable), single session, queued requests.
36
- - Framing: STX `0x02`, API version `0x02`, body length (uint32 LE), request ID (uint32 LE), command ID, body.
37
- - Commands (initial):
38
- - `0x85` Info (for ping/version), `0xCC` Reset, `0x01` Memory Get, `0x02` Memory Set, `0x72` Keyboard Feed, `0xAA` Exit.
39
- - Error handling: translate BM error codes to structured errors; timeouts; socket lifecycle (connect, keepalive, backoff reconnect).
40
- - Concurrency: serialize (one outstanding request); buffer unsolicited events (e.g., `Stopped/Resumed`) for logging.
41
- - Readiness helpers: screen polling for any text and `READY.` pattern; optional resume hook between polls so emulation runs.
42
-
43
- 2) Process Manager (`src/vice/process.ts`)
44
- - Start a long‑lived `x64sc` with: `-binarymonitor`, `-binarymonitoraddress 127.0.0.1:6502`, `-sounddev dummy`, `-config /dev/null` (and `-warp` unless disabled).
45
- - Env integration: honor `VICE_MODE`, `VICE_LIMIT_CYCLES`, `VICE_BINARY`, `FORCE_XVFB` (see `mcp.json` and `src/viceRunner.ts`).
46
- - Lifecycle: start on demand; monitor stdout/stderr; auto‑restart policy with jitter; clean shutdown.
47
- - Optional: run under `xvfb-run` in headless/CI when needed.
48
-
49
- 3) Facade Integration (`src/device.ts`)
50
- - Replace the stub `ViceBackend` with implementations backed by the BM client:
51
- - `ping`, `version`, `info` (BM Info), `reset` (`0xCC`), `pause`/`resume` (connect vs. `0xAA Exit`),
52
- - `readMemory`/`writeMemory` (initially comp memspace, bank 0; extend later),
53
- - `runPrg`: prefer RAM injection on the long‑lived process (BASIC pointer patch + keyboard feed `RUN`), with BM `0xDD Autostart` as an optional fallback; keep per‑run spawn only as a last resort.
54
- - Keep hardware‑specific endpoints (`drives*`, `files*`, `config*`, `stream*`) returning `UNSUPPORTED` with crisp messages.
55
-
56
- 4) Client & Tools
57
- - `C64Client.readMemoryRaw()` and `writeMemory()` already prefer the facade; once `ViceBackend` supports them, memory tools work unchanged.
58
- - `read_screen` and `wait_for_text` start working under VICE without tool changes (screen ASCII exists in `src/petscii.ts`).
59
- - System tool (`reset_c64`, `pause`, `resume`) starts working under VICE via facade.
60
- - Optional `keyboard_feed` op (emulator‑specific) can be exposed later if needed for scripted sessions.
61
-
62
- 5) Platform Description (`src/platform.ts` and platform resource)
63
- - Update VICE features to include: `binary-monitor`, `memory-io`, `pause/resume`, `reset`, `screen-capture`, and later `debugger-primitives`.
64
- - Keep limited features explicit: no hardware drives, no firmware config, no REST streams.
65
-
66
- 6) Optional Debug Module (later phase)
67
- - New grouped tool `c64_debug` exposing BM features: set/list/delete/toggle breakpoints, conditionals, step/next/return, register get/set, banks list, display get.
68
- - Separate from generic memory/system tools to avoid confusing capabilities across platforms.
69
-
70
- 7) Test Harness & Stub (`src/vice/mockServer.ts` + helpers)
71
- - Provide a lightweight BM stub that responds to the limited command surface the smoke test and unit tests rely on (`info`, `reset`, `memGet`, `memSet`, `keyboardFeed`, screen snapshot).
72
- - Controlled via an opt-in flag (e.g., `VICE_TEST_TARGET=mock`), mirroring the C64U test flags. Default behavior: use real VICE when available.
73
- - Document detection order: if `VICE_TEST_TARGET=mock`, start the stub; otherwise attempt to start VICE (skip tests gracefully when neither is available).
74
-
75
- ## Acceptance Guidelines
76
-
77
- - Under `C64_MODE=vice`, the following must succeed without REST using the long‑lived process:
78
- - `c64_memory` ops: `read`, `write`, `read_screen`, and `wait_for_text`.
79
- - `c64_system` ops: `reset_c64`, `pause`, `resume`.
80
- - `c64_program` ops: `upload_run_basic` and `upload_run_asm` inject and run without respawning VICE.
81
- - Clear error messages for unsupported features when running on VICE (drives, files, streaming, firmware configs).
82
- - Logging includes BM command timings and error codes for troubleshooting.
83
-
84
- ## Notes on Existing Implementations
85
-
86
- - `src/vice/viceClient.ts` and `src/vice/readiness.ts` provide a production‑ready BM client and readiness helpers (screen polling with optional resume hooks).
87
- - `test/vice/viceSmokeTest.ts` demonstrates process launch, readiness, RAM injection, and screen verification.
88
- - `src/viceRunner.ts` (SID→WAV capture) remains useful for audio workflows, but it should not be used for general VICE control under the facade. Mark it as “audio capture utility”; avoid extending it for program runners.
@@ -1,99 +0,0 @@
1
- # Vice Support Rollout Plan
2
-
3
- Purpose: Deliver practical, high‑value VICE support quickly, with clear platform semantics and safe fallbacks. Each phase targets what can be completed in a single focused LLM session, avoiding fragmentation.
4
-
5
- ## Required Reading (before starting)
6
-
7
- - [ ] Read `doc/vice/vice-binary-monitor-spec.md` (BM framing, commands, errors).
8
- - [ ] Read `doc/vice/vice-bm-vs-c64u-rest.md` (feature comparison). The guidance remains correct, with one emphasis from our findings: the BM pauses CPU during monitor handling — explicitly call `0xAA Exit` between screen/memory polls so emulation progresses.
9
- - [ ] Read `doc/plans/vice-support/plan.md` and `doc/plans/vice-support/notes.md` (objectives, design decisions).
10
- - [ ] Skim code refs: `src/device.ts`, `src/c64Client.ts`, `src/petscii.ts`, `src/mcp-server.ts`, `src/platform.ts`, `src/viceRunner.ts` (audio only).
11
- - [ ] Run/understand `test/vice/viceSmokeTest.ts` (long‑lived VICE, readiness, injection, screen verification).
12
-
13
- ## Operator Rules
14
-
15
- - Execute phases strictly in order; do not parallelize across phases.
16
- - After any change, run `npm run check` and only proceed if it passes.
17
- - Keep changes minimal and focused on the current phase; update tests and docs alongside code changes.
18
- - Preserve hardware behaviors; do not introduce fake drive/config semantics under VICE.
19
- - Prefer using a real VICE binary when available; fall back to the BM stub only when explicitly requested (see Test Harness checklist).
20
-
21
- ## CI and Headless Testing
22
-
23
- - [ ] Configure CI to run any VICE‑dependent tests under X11 virtualization. Either:
24
- - Set `FORCE_XVFB=1` (or `CI=1`) so helpers spawn `Xvfb` automatically; or
25
- - Wrap commands in `xvfb-run`.
26
- - [ ] Add a CI step to run the smoke test headlessly: `FORCE_XVFB=1 npm run vice:smoke`.
27
- - [ ] Recommend the same locally during builds to avoid VICE windows popping up and to catch headless issues early: `FORCE_XVFB=1 npm run vice:smoke` (and any VICE‑dependent tests).
28
-
29
- ## Smoke Test and Test Strategy
30
-
31
- - [ ] Reuse the existing minimal BM client for smoke tests (`src/vice/viceClient.ts` with `src/vice/readiness.ts`), proven by `test/vice/viceSmokeTest.ts`.
32
- - [ ] Keep the smoke test simple: start VICE, wait for READY., inject a tiny BASIC program, RUN, verify the screen, clean up processes.
33
- - [ ] Do not grow the smoke test further; any new VICE features must have their own normal tests (unit/integration) alongside other prod features under `test/`.
34
- - [ ] Avoid UI‑sensitive/timing‑brittle assertions in the smoke test; prefer feature‑specific tests for deeper coverage.
35
- - [ ] Gate smoke-test execution behind the same flag used in phase deliverables (e.g., real VICE unless `VICE_TEST_TARGET=mock`).
36
-
37
- ## Phase 1 — Core client + screen/memory/system (Foundations)
38
-
39
- Checklist:
40
-
41
- - [x] Implement BM client and readiness helpers (`src/vice/viceClient.ts`, `src/vice/readiness.ts`): `info`, `reset`, `memGet`, `memSet`, `keyboardFeed`, `exitMonitor`, screen polling.
42
- - [x] Add smoke test (`test/vice/viceSmokeTest.ts`): launch VICE or stub, wait READY., inject BASIC, RUN, verify screen; include process cleanup; support visible and headless.
43
- - [x] Wire `src/device.ts` `ViceBackend` to use the client for: `ping`, `version`, `info`, `reset`, `readMemory`, `writeMemory`; `pause`/`resume` via monitor enter/exit.
44
- - [x] Update platform features (`src/platform.ts`) for VICE: `binary-monitor`, `memory-io`, `pause/resume`, `reset`, `screen-capture`.
45
- - [x] Introduce a BM stub (`src/vice/mockServer.ts`) for tests: respond to minimal command set; controlled via flag `VICE_TEST_TARGET=mock`. Default attempts real VICE.
46
- - [x] Document env detection order (developer guide): real VICE when available; stub only when explicitly requested; tests skip gracefully if neither is present.
47
-
48
- Acceptance:
49
-
50
- - [x] Under `C64_MODE=vice`, `c64_memory` (`read`, `write`, `read_screen`, `wait_for_text`) and `c64_system` (`reset_c64`, `pause`, `resume`) work end‑to‑end using BM (validated with the mock server; real VICE covered when available).
51
- - [x] Smoke test passes with the stub (`VICE_TEST_TARGET=mock`) and cleans up processes.
52
- - [ ] Smoke test passes with real VICE (default) on at least one platform (run when emulator available).
53
-
54
- ## Phase 2 — Long‑lived process + injection runners
55
-
56
- Checklist:
57
-
58
- - [x] Add supervisor (`src/vice/process.ts`) to spawn and supervise a single VICE process; honor `VICE_*` env; headless via Xvfb; clean shutdown.
59
- - [x] Extend `ViceBackend.runPrg(prg|file)` to inject into RAM on the supervised process (BASIC pointer patch + `keyboardFeed RUN`); keep BM `0xDD` as optional fallback; remove per-run spawn from the default path.
60
- - [x] Ensure resume/exit hooks are used between polls so emulation runs during readiness and screen waits.
61
- - [x] Add documentation to developer guide for visible vs headless runs; keep vice:smoke as a sanity command.
62
-
63
- Acceptance:
64
-
65
- - [x] `c64_program` (`upload_run_basic`, `upload_run_asm`) use the supervised VICE without respawning; hello world displays and is captured by `read_screen`.
66
-
67
- ## Phase 3 — Debugger capabilities (Breakpoints, stepping, registers)
68
-
69
- Checklist:
70
-
71
- - [x] Implement BM: `0x11/0x12/0x13/0x14/0x15` (breakpoints), `0x22` (conditions), `0x71/0x73` (step/return), `0x31/0x32` (registers), `0x82/0x83` (banks/registers metadata).
72
- - [x] Add grouped module `c64_debug` with ops for the above; mark `supportedPlatforms: ["vice"]`.
73
-
74
- Acceptance:
75
-
76
- - [x] Demo: set breakpoint, run PRG, verify stop, inspect registers, step a few instructions, resume to completion. (Covered via grouped tool regression tests under mock VICE.)
77
-
78
- ## Phase 4 — Emulator resources and display capture (Polish)
79
-
80
- Checklist:
81
-
82
- - [x] Implement `0x84` Display Get; expose minimal `c64_vice.display_get` returning geometry + pixels; optional PNG encoding.
83
- - [x] Implement `0x51/0x52` Resource get/set; expose `c64_vice.resource_get`/`resource_set` with safe namespacing.
84
- - [x] Update platform resource/README to reflect emulator‑specific tools and debugger status.
85
-
86
- Acceptance:
87
-
88
- - [x] `display_get` returns consistent geometry/pixels; resource get/set toggles a simple option end‑to‑end. (Validated by grouped tool regression coverage.)
89
-
90
- ## Done Definition
91
-
92
- - [ ] TypeScript builds cleanly; tests pass (`npm run check`).
93
- - [ ] VICE features appear in the platform resource with correct supported/unsupported tool lists.
94
- - [ ] Unsupported operations on VICE return informative errors without crashing the server.
95
- - [ ] README and platform docs call out feature parity and differences succinctly.
96
-
97
- ## Notes on Existing Code
98
-
99
- - `src/viceRunner.ts` (SID→WAV) is retained for audio capture only; do not extend it for program runners. All general VICE control lives behind the facade using `src/vice/viceClient.ts` + supervisor.
@@ -1,143 +0,0 @@
1
- # C64 Bridge MCP Server Audit
2
-
3
- ## Scope
4
-
5
- This audit reviews the C64 Bridge repository as an MCP server for C64 Ultimate/C64U REST devices and VICE emulator control. It covers server initialization, tool/resource/prompt registration, tool schemas and results, device-control safety, VICE integration, C64 Ultimate integration, documentation consistency, and test/coverage architecture.
6
-
7
- ## Assumptions
8
-
9
- - The local repository is the source of truth for supported behavior.
10
- - The current branch is `fix/readme`.
11
- - Pre-existing modified `data/**` files are user-owned unless a finding specifically requires editing them.
12
- - Real hardware is not required for fixes where mock or emulator tests cover the behavior.
13
- - Compatibility aliases for removed legacy resource URIs are not required; tests already assert legacy URI rejection.
14
-
15
- ## Methodology
16
-
17
- 1. Inspected repository layout, package scripts, README, AGENTS instructions, skill routing, and MCP generated artifacts.
18
- 2. Inspected `src/mcp-server.ts`, tool registries, schema utilities, knowledge resource registry, VICE backend, C64U backend, and client device-control helpers.
19
- 3. Ran baseline install/build/test/coverage commands.
20
- 4. Scanned for top-level tool schema combinators, stale resource URIs, broad validation gaps, and hidden-result patterns.
21
- 5. Classified findings with the requested severity model and mapped fixes to tests.
22
-
23
- ## Repository Areas Inspected
24
-
25
- - `src/mcp-server.ts`
26
- - `src/tools/**`
27
- - `src/rag/resourceUris.ts`
28
- - `src/rag/knowledgeIndex.ts`
29
- - `src/c64Client.ts`
30
- - `src/device.ts`
31
- - `src/vice/**`
32
- - `src/prompts/registry.ts`
33
- - `scripts/run-coverage.mjs`
34
- - `scripts/run-tests.ts`
35
- - `README.md`
36
- - `AGENTS.md`
37
- - `.github/skills/**`
38
- - `mcp/**`
39
- - `test/**`
40
-
41
- ## MCP Server Design Review
42
-
43
- The server uses the official TypeScript MCP SDK and registers `resources/list`, `resources/read`, `tools/list`, `tools/call`, `prompts/list`, and `prompts/get` handlers over stdio. Diagnostics are written to stderr/files, leaving stdout for the MCP protocol. Tool errors are returned as tool results with `isError`, which matches MCP expectations for model-visible tool failures.
44
-
45
- Confirmed non-issue: optional HTTP CLI parsing exists but current `main()` always connects stdio; the README describes stdio as canonical. No fix is required for this audit because no HTTP server path is advertised as the normal agent transport.
46
-
47
- ## Tool Design Review
48
-
49
- Most public tools are grouped under names such as `c64_program`, `c64_memory`, `c64_config`, and `c64_sound` with an `op` discriminator. The shared `discriminatedUnionSchema` intentionally flattens operations to avoid top-level `oneOf`, which is correct for common MCP/LLM client compatibility.
50
-
51
- Confirmed issue: `c64_input` is an outlier and still exposes top-level `oneOf` plus `discriminator`. Its metadata also references `c64://specs/assembly` and `c64://specs/memory-map`, which are not registered resources.
52
-
53
- ## Resource URI Design Review
54
-
55
- Canonical resources use the `c64://` scheme with concise namespaces such as `guide`, `basic`, `assembly`, `graphics`, `sound`, `memory`, `io`, `printer`, `vice`, and `platform`. The registered resource list is stable, discoverable, and documented in README-generated sections. Tests assert old legacy URI forms are rejected.
56
-
57
- Confirmed non-issue: legacy resource aliases do not need to be preserved because repository tests already reject legacy direct reads.
58
-
59
- Confirmed issue: tool metadata in `c64_input` points to stale `c64://specs/*` URIs outside the canonical resource set.
60
-
61
- ## Device-Control Safety Review
62
-
63
- The tool layer generally bounds address lengths, file image parameters, stream names, joystick values, and grouped operations. Memory mutation paths support verification and C64U pause/resume where applicable.
64
-
65
- Confirmed issue: lower-level client parsing accepts partial numeric literals through `parseInt`, so values such as `$04ZZ` can resolve to `$0004`. Hex byte parsing can also silently truncate through `Buffer.from(value, "hex")`, so malformed data such as `$AAZZ` can become `$AA`. Raw memory helpers do not consistently reject address ranges that cross `$FFFF` before calling a backend.
66
-
67
- ## VICE Integration Review
68
-
69
- VICE support is separated through `ViceBackend`, `ViceClient`, readiness helpers, and Binary Monitor guidance resources. Tests cover monitor framing, single-client behavior, process launch, readiness, resource get/set, mock and device smoke paths. Joystick simulation is correctly marked VICE-only at operation-platform level.
70
-
71
- Confirmed issue: `c64_input` schema compatibility affects VICE joystick discoverability in some clients.
72
-
73
- ## C64 Ultimate REST Integration Review
74
-
75
- C64U REST base URLs are normalized through config and environment handling. Network passwords are sent as `X-Password`. HTTP clients use timeouts. Tests cover mock REST endpoints, password behavior, memory, runners, drives, config, streams, and files.
76
-
77
- Possible issue requiring external confirmation: local option-list validation for arbitrary C64U configuration values is limited because firmware-provided option inventories are not fully represented in repository data. The current safe approach is to list/read config first and rely on firmware rejection for unknown values. This was not fixed without a reliable local option source.
78
-
79
- ## Error Handling Review
80
-
81
- Tool errors are normalized through `ToolValidationError`, `ToolExecutionError`, `toolErrorResult`, and `unknownErrorResult`. Tool-level failures are model-visible rather than protocol-level except for unknown tool lookup through registry invocation.
82
-
83
- Confirmed issue: memory parse failures for partial literals are not reliably raised today, reducing the value of this error-handling layer for device safety.
84
-
85
- ## Schema/Input Validation Review
86
-
87
- Most grouped tools use flattened schemas with enum-constrained `op`. String, number, boolean, array, object, and optional schema helpers enforce runtime validation.
88
-
89
- Confirmed issue: `c64_input` still has a top-level `oneOf`.
90
-
91
- Confirmed issue: C64Client numeric and hex parsing needs stricter validation below the tool layer so public methods and internal raw paths fail safely.
92
-
93
- ## Documentation Review
94
-
95
- README, AGENTS, skills, and generated `mcp/**` files are broadly consistent with current grouped tool names and canonical resource URIs. README resource lists are generated.
96
-
97
- Confirmed issue: generated `mcp/tools.json` and schema output inherit the stale `c64_input` resources and top-level `oneOf` until source is fixed and generation reruns.
98
-
99
- ## Test Architecture and Coverage Review
100
-
101
- The repository has broad unit, integration, MCP harness, VICE mock, and VICE device tests. `npm test` passes. Coverage is intended to be enforced by `scripts/run-coverage.mjs` and `.c8rc.json` with 90% thresholds.
102
-
103
- Confirmed issue: baseline `npm run coverage` failed after passing shards because the wrapper attempted to copy a missing `coverage/lcov.info` for the VICE device shard.
104
-
105
- ## Findings
106
-
107
- | ID | Severity | Area | Finding | Evidence | Impact | Recommended fix | Status |
108
- | --- | --- | --- | --- | --- | --- | --- | --- |
109
- | MCP-AUDIT-001 | High | Device-control safety | Numeric and hex parsing can accept partial malformed input, and raw memory access lacks consistent 64 KB range guards. | `src/c64Client.ts` uses `parseInt`; `hexStringToBuffer` and `src/tools/memory.ts` parse hex through `Buffer.from(..., "hex")`; raw reads/writes do not consistently validate range overflow. | Malformed addresses or bytes can target the wrong memory location or write truncated data before a device call fails. | Enforce full-literal parsing, strict hex validation, and range checks before memory reads/writes. Add regression tests. | Planned |
110
- | MCP-AUDIT-002 | High | MCP schema/resource metadata | `c64_input` exposes top-level `oneOf`/`discriminator` and stale `c64://specs/*` resources. | `src/tools/input.ts`; generated `mcp/tools.json` and `mcp/schemas/c64_input.schema.json`. | Some MCP/LLM clients may fail to call the tool, and metadata points agents to resources that cannot be read. | Use flattened `discriminatedUnionSchema` and canonical resources. Regenerate MCP artifacts and tests. | Planned |
111
- | MCP-AUDIT-003 | Medium | Coverage architecture | Coverage command fails with `ENOENT` copying `coverage/lcov.info` after VICE device shard. | Baseline `npm run coverage` failed in `scripts/run-coverage.mjs:181`. | Required coverage verification cannot complete reliably even when tests pass. | Make the wrapper robust to Bun temporary LCOV output and add script regression tests. | Planned |
112
- | MCP-AUDIT-004 | Low | Dependency hygiene | `npm install` reports 1 moderate and 1 high vulnerability. | Baseline install output. | Potential dependency risk, but not enough local evidence to safely upgrade during this audit. | Defer to a dependency/security review with package upgrade testing. | Deferred |
113
- | MCP-AUDIT-005 | Trivial | Formatting | Some inspected source has indentation inconsistencies. | `src/rag/knowledgeIndex.ts`, `src/tools/memory.ts`. | Cosmetic only. | Defer unless adjacent to required edits. | Deferred |
114
-
115
- ## Fix Plan Mapped To Findings
116
-
117
- - MCP-AUDIT-001: update strict parsers and range guards in `src/c64Client.ts` and `src/tools/memory.ts`; update `test/c64ClientInvalidInputs.test.mjs` and `test/memory.test.mjs`.
118
- - MCP-AUDIT-002: update `src/tools/input.ts`; update input/schema tests; regenerate `README.md` and `mcp/**` via `npm run build`.
119
- - MCP-AUDIT-003: update `scripts/run-coverage.mjs`; update `test/scripts/run-coverage.test.mjs`.
120
-
121
- ## Confirmed Issues
122
-
123
- - MCP-AUDIT-001
124
- - MCP-AUDIT-002
125
- - MCP-AUDIT-003
126
-
127
- ## Possible Issues Requiring External Confirmation
128
-
129
- - Full local option-list validation for C64U firmware configuration values depends on firmware-provided category/item/value metadata not present in repository evidence.
130
-
131
- ## Non-Issues
132
-
133
- - Legacy resource URI aliases are intentionally absent.
134
- - Stdio-first transport is consistent with repository instructions.
135
- - VICE Binary Monitor reference is present and routed through canonical resource and skills.
136
-
137
- ## Trivialities Intentionally Deferred
138
-
139
- - Formatting-only indentation cleanup not adjacent to required edits.
140
-
141
- ## Final Status After Implementation
142
-
143
- Implementation has not yet started. This section will be updated after fixes and final verification.
@@ -1,125 +0,0 @@
1
- # Hardening 01 — handover: finish the poll-truthfulness fix, then adversarially review and land the branch
2
-
3
- Repository: `/home/chris/dev/c64/c64bridge`
4
- Branch: `fix/hardening` (already pushed once as PR #143, previously brought to a green/merge-ready state)
5
- This file: `doc/reviews/hardening/01-fable/prompt-handover.md`
6
-
7
- This is a handover from a session that (1) got PR #143 to a fully green, merge-ready state addressing five Kilo Code review comments and raising patch coverage to ~96%, then (2) at the user's request, ran **real physical hardware tests** (C64U, Ultimate 64, Ultimate II+L) against the live MCP server, and (3) found a genuine, reproducible false-success bug during that testing. A fix is in progress but not finished, verified, or committed. You are picking this up cold — there is no prior conversation for you to recall.
8
-
9
- Do not consider this session's work "done" until Part 3's exit criteria are met. Do not claim completion while any part below remains unresolved.
10
-
11
- ## Part 0 — Orient yourself first
12
-
13
- 1. Read `AGENTS.md` in full.
14
- 2. Read `doc/reviews/hardening/01-fable/review.md` and `doc/reviews/hardening/01-fable/prompt.md` for the original 35-finding hardening context (HARD01-001..035) — this branch already implements all of them, plus five follow-up fixes from a Kilo Code automated review round, plus coverage work. That prior work is committed (`git log --oneline` back to `cfb7e4a`). Nothing in that history needs redoing.
15
- 3. Run `git status` and `git diff --stat` to see the exact uncommitted state you're inheriting (summarized precisely in Part 1 below — but verify it yourself, the working tree may have drifted).
16
- 4. Check `gh pr view 143 --repo chrisgleissner/c64bridge` and `gh pr checks 143 --repo chrisgleissner/c64bridge` for the PR's current state. As of this handover, PR #143 is green and all prior review threads are resolved — the uncommitted work described below is a **new, additional** round on top of that.
17
-
18
- ## Background: what real-hardware testing found
19
-
20
- The session tested the live MCP server against three genuinely separate physical devices (confirmed via distinct firmware `unique_id`s):
21
-
22
- - **C64U** — hostname `c64u`, product "C64 Ultimate", firmware 1.2.0.
23
- - **Ultimate 64 Elite** — hostname `u64`, firmware 3.15. Fully independent hardware (its own board), but uses the same `c64u` backend profile/config key as C64U (same REST API family). The c64bridge config format has exactly one `c64u` slot, and the running server caches whichever host was resolved into it the first time that backend is constructed in a process — editing `.c64bridge.json` and re-selecting the backend does **not** retarget an already-constructed facade. To test C64U and U64 as genuinely separate live connections, the session registered a second temporary MCP server entry, `c64bridge-u64`, in `.mcp.json`, pointed at a separate config file (`C64BRIDGE_CONFIG` env var) with `c64u.host = "u64"`. That second server entry is a legitimate, intentional part of the current uncommitted `.mcp.json` diff — keep it (or ask the user before removing it) rather than reverting it as accidental drift.
24
- - **Ultimate II+L (U2)** — hostname `u2`, firmware 3.15. Physically a cartridge plugged into the back of the C64U unit — separately networked (own IP), but shares the same underlying simulated C64 bus/CPU/screen as the C64U it's plugged into. **Never drive `c64u` and `u2` operations concurrently** — they are electrically the same target machine. U64 is fully independent and safe to run alongside either.
25
-
26
- Targeted verification (firmware `errors[]` truthfulness, drive-list shape, real ASM execution, `power_cycle` honesty) passed cleanly on **C64U and U64**. On **U2**, `upload_run_basic` / `upload_run_asm` reported `success: true, verified: true` while the program **never actually executed** — confirmed independently by bypassing the MCP client entirely and sending raw HTTP `POST /v1/runners:run_prg` requests directly to the device in both `application/octet-stream` and `multipart/form-data` encodings (matching the OpenAPI spec exactly). The U2 firmware returns `200 {"errors": []}` but memory at `$0801` never changes and the screen never advances past the boot banner. This was cross-checked against the *same* physical machine via the C64U side (which genuinely does execute, visible through the shared screen) — so it is a real gap in the U2 cartridge's own firmware implementation of the upload-and-run REST feature, **not** a client-side bug. That part needs no further code change; it's a hardware/firmware limitation to document, not fix.
27
-
28
- **What is a fixable bug**, independent of the U2 firmware issue: `pollBasicOutcome()` / `pollAsmOutcome()` in `src/tools/pollValidator.ts` were blindly reporting success whenever the literal text "RUN" or "ERROR" never appeared on screen within the poll window. Since `upload_run_basic` / `upload_run_asm` execute via the native REST `run_prg` endpoint (or a typed `SYS<entry>` command for ASM) rather than a user visibly typing `RUN`, this fallback was structurally unable to provide a real signal — it happened to look correct on C64U/U64 only because execution there also happens to produce visible screen text (a KERNAL LOAD/RUN echo) for unrelated reasons. On U2, where nothing executes at all, the same fallback silently laundered a real failure into a false `verified: true`. This is exactly the class of bug the original 35 HARD01 findings targeted, just not one of them by number — a natural HARD01-036 in spirit, though nothing in code should assume that ID is registered anywhere; use it only as a descriptive tag if useful (as already done in the new tests, see below).
29
-
30
- ## Part 1 — Finish the in-progress fix (do this first)
31
-
32
- ### 1a. Fix already applied to `src/tools/pollValidator.ts` — verify, don't redo
33
-
34
- - `pollBasicOutcome`: previously always fell through to `return { status: "ok", type: "BASIC" }` after the poll window if no BASIC error text was ever seen, regardless of whether `runDetected` was ever set. Now, if `runDetected` is still `false` after the full poll window, it returns `{ status: "error", type: "BASIC", message: "No execution activity detected: the firmware reported success, but the program does not appear to have run." }` instead.
35
- - `pollAsmOutcome`: previously returned `{ status: "ok", type: "ASM" }` immediately if `runDetected` was never set, **skipping** the hardware liveness check (screen-RAM CRC comparison) entirely. That early return is removed — execution now always falls through to the liveness check regardless of whether "RUN"/"ERROR" text was ever seen. Only the two log lines and a comment changed around this; the liveness-check logic itself (already correct, from the original HARD01-019/020 work) is untouched.
36
-
37
- Confirm this is still exactly what's in the working tree (`git diff src/tools/pollValidator.ts`) before proceeding — do not re-derive it from scratch, but do sanity-check it against the description above.
38
-
39
- ### 1b. Test updates already applied — verify, don't redo
40
-
41
- `test/pollValidator.test.mjs`:
42
- - Two tests that previously *encoded the bug as correct behavior* — `"pollForProgramOutcome BASIC returns ok if RUN not detected"` and `"pollForProgramOutcome ASM returns ok if RUN not detected"` (both used a mock whose `readScreen()` always returns `"READY.\n"` forever, and asserted `status: "ok"`) — were renamed and fixed to assert the corrected behavior: `"HARD01-036 pollForProgramOutcome BASIC reports an error instead of blind success when RUN is never detected"` (asserts `status: "error"`, message matches `/does not appear to have run/i`) and `"HARD01-036 pollForProgramOutcome ASM reports crashed instead of blind success when RUN is never detected"` (asserts `status: "crashed"`, `reason: "no program-visible screen progression within window"`).
43
- - A new positive-case test was added, `"HARD01-036 pollForProgramOutcome ASM still detects real liveness even when RUN text never appears"`, confirming the fix doesn't regress the case where RUN/ERROR text never shows but screen RAM genuinely changes (must still report `ok`).
44
-
45
- `test/programRunnersModule.test.mjs`:
46
- - `"upload_run_basic verify true annotates metadata"`: mock's `readScreen()` previously always returned `"READY.\n"`; now returns `"READY.\n"` on the first call (the tool's own pre-poll screen check) and `"RUN\n"` thereafter (matching how a real backend's LOAD/RUN echo actually behaves), so the poll genuinely observes execution instead of relying on the old blind-success fallback.
47
- - `"upload_run_basic verify tolerates repeated screen read failures"`: mock's `readScreen()` previously threw on *every* call, and the test asserted success. That was itself an instance of the bug (permanent failure ≠ "tolerated" success). Now the mock throws only on the first 3 calls, then returns `"RUN\n"` — genuinely testing transient-failure tolerance, not permanent-failure-as-success.
48
- - `"upload_run_asm returns structured content on success"`: the mock previously had no `readScreen`/`readMemoryRaw` at all (relying on the old shortcut to skip verification entirely, even though `executeUploadRunAsm` calls `pollForProgramOutcome` unconditionally regardless of the `verify` flag). Now includes a minimal `readScreen` (`"RUN\n"`) and `readMemoryRaw` (returns a changing byte pattern) so the now-mandatory liveness check has something real to observe.
49
- - `"upload_run_asm verify treats repeated screen read failures as instant execution"` — this test's *name* literally described the bug as a feature. Renamed to `"upload_run_asm verify tolerates transient screen read failures and still verifies via liveness"` and its mock changed from permanent to transient (3) failures followed by success, mirroring the BASIC fix above.
50
-
51
- Confirm `test/pollValidator.test.mjs` and `test/programRunnersModule.test.mjs` diffs match this description; run `timeout 120 node scripts/run-tests.mjs test/pollValidator.test.mjs test/programRunnersModule.test.mjs test/groupedToolsShims.test.mjs` and confirm all green (96/96 in the second file, 22/22 in the first, as of last check).
52
-
53
- ### 1c. Leftover test artifacts already cleaned up — verify, don't redo
54
-
55
- The hardware-testing session had left two untracked scratch files in the repo root — `.c64bridge.json` (containing `{"u2": {"host": "u2", "port": 80}}`) and `.c64bridge.u64.json`. These were interfering with `test/device.test.mjs`'s `"falls back to vice or c64u when no config"` test via `config.ts`'s "legacy package-adjacent config" fallback candidate (`dirname(import.meta.url of config.ts)/../.c64bridge.json`), which resolves relative to the *source file location*, not `process.cwd()` — so it bypasses that test's directory-isolation helper (`withConfigScenario`) entirely. Both files have already been deleted. Run `git status --porcelain` and confirm neither file is present; if either has reappeared (e.g. from a stray tool run), delete it again before running the full suite — it will cause spurious, hard-to-diagnose failures in `device.test.mjs` and possibly elsewhere.
56
-
57
- ### 1d. NOT yet fixed — this is the actual remaining work
58
-
59
- `scripts/mockC64Server.mjs` was modified to make the C64U mock server's simulated `injectKeyboardQueue` path (used by `upload_run_asm`'s typed `SYS<entry>` trigger) produce *some* observable screen-RAM change, since the mock runs no real 6502 code and the now-mandatory liveness check in `pollAsmOutcome` needs something to observe. The current approach: `simulateKeyboardDrain()` (which already existed, to drain the KERNAL NDX counter) now also detects when the queued/typed text matches `/SYS\s*\d+/i` and, if so, starts a `setInterval` "heartbeat" that increments `state.memory[0x0400]` every 20ms for up to 50 ticks (~1 second), simulating an alive, still-running program.
60
-
61
- **This introduced a real regression**: the heartbeat is a background timer with no cleanup tied to test boundaries. Because `test/c64Client.test.mjs` shares one mock server/client instance across many sequential `t.test()` subtests (no `resetState()`/reset call between them), an *earlier* subtest in that file — `"HARD01-018 uploadAndRunAsm loads the assembled PRG..."` (around line 456) or `"HARD01-018 uploadAndRunAsm keeps a non-$0801 origin intact..."` (around line 475), both of which trigger a `SYS...` pattern — leaves a heartbeat ticking for up to a second. A *later*, unrelated subtest a few lines down, `"readMemory returns hex string with prefix"` (around line 506), which expects to read back the exact bytes (`$AA55`) written by the immediately-preceding `"writeMemory writes bytes to mock memory"` subtest, instead observes `$AB55` — the first byte has been incremented by exactly 1 by the still-running heartbeat from the earlier subtest. Confirmed via direct code inspection; not yet re-confirmed by test run after the last edit (the fix attempt was mid-flight when this handover was written — you may find it still fails, or fails intermittently depending on timing).
62
-
63
- **Fix this properly before doing anything else.** Suggested directions, roughly in order of robustness (pick one, don't feel obligated to preserve the current heartbeat mechanism if a cleaner design fits better):
64
-
65
- 1. **Time-computed overlay instead of a live timer (recommended).** Instead of a `setInterval` that mutates `state.memory` in the background, record `state.heartbeat = { startedAt: Date.now(), durationMs, baseValue }` when a `SYS` command is detected, and have the `machine:readmem` handler (or wherever `$0400` reads are served) compute a *synthetic* byte value on read — e.g. `baseValue + Math.floor((Date.now() - startedAt) / 20) & 0xff` — only while `Date.now() - startedAt < durationMs`, otherwise return the real stored byte. This eliminates all background timers and the cross-test leakage entirely: nothing mutates `state.memory` asynchronously, so an unrelated `writeMemory`/`readMemory` pair immediately after a `SYS`-triggering subtest is unaffected (the overlay only applies to reads of that specific address, and only while a window is "active" for a specific triggering event — consider whether the overlay should apply narrowly enough that a real write to `$0400` by a later subtest correctly overrides/disables it, e.g. by clearing `state.heartbeat` on any explicit write to that address).
66
- 2. **Track and clear timers explicitly.** Keep the `setInterval` approach, but store the active heartbeat handle in `state` (e.g. `state.activeHeartbeat`), clear any previous one before starting a new one, and clear it in `resetState()`. This only fully solves leakage across resets — it does **not** solve leakage between sequential subtests in the *same* file that never call reset (the actual failure mode observed), so this alone is likely insufficient without also auditing whether `t.test()` sequences in shared-client test files should call `mock.reset()` between subtests (check `test/c64Client.test.mjs`'s setup/teardown and `test/helpers/mcpTestHarness.mjs`'s `withSharedMcpClient` for whether that's feasible without breaking other assumptions).
67
- 3. **Shrink the blast radius.** If you keep a live timer, drastically shorten its duration (a handful of ticks over ~40-60ms) so it's very unlikely to outlive the specific subtest that triggered it, while remaining long enough to intersect the poll windows used by tests that override `C64BRIDGE_POLL_MAX_MS`/`C64BRIDGE_POLL_INTERVAL_MS` (values as low as `maxMs: 50, intervalMs: 10` appear in the test suite — check the *smallest* configured poll window across all callers before picking a duration, not just the ones you happen to be looking at). This is the least robust option and only worth using if 1 and 2 both prove impractical.
68
-
69
- Whichever approach you take, the fix must satisfy **all** of:
70
- - `test/suites/mcpServerCallToolSuite.mjs`'s `"c64_program upload_run_asm assembles source and runs program"` test passes reliably (run it standalone at least 10 times in a loop to rule out flakiness, not just once).
71
- - `test/c64Client.test.mjs` passes with no cross-subtest interference (run the whole file standalone, and also as part of the full suite — the earlier failure only manifested in the full-suite/whole-file run, not a single-test run, so isolated single-test passes are not sufficient evidence).
72
- - No new timer/interval leaks the Node/Bun process past test completion (check for "open handle" warnings if your test runner surfaces them).
73
- - The underlying simulation stays scoped to the `SYS`-command pattern specifically — `injectKeyboardQueue` is also used by `src/tools/input.ts` for general `key`/`write_text` operations, and those must not trigger this heartbeat/overlay behavior.
74
-
75
- After fixing, `timeout 300 npm test` must show **0 failures** across the entire suite (as of last check before the mock-server regression, the count was 792 tests / 788 pass / 2 skip / 0 fail with the pollValidator fix alone, before the mock-server change was introduced — use that as your target, adjusting only for whatever new tests you may have added).
76
-
77
- ### 1e. Full validation gate for Part 1
78
-
79
- Once the full suite is green, run the complete validation gate used earlier in this branch's history (see recent commits `ad93c5d`, `417ec89` for the pattern):
80
-
81
- ```
82
- npx tsc -p tsconfig.json --noEmit
83
- npm test
84
- npm run test:vice:mock
85
- npm run build
86
- npm run check:package
87
- git diff --check
88
- ```
89
-
90
- All must pass cleanly. `npm run build` regenerates `mcp/*.json` and `README.md` from source/schema — if those come out changed, that's expected only if you touched schemas; otherwise investigate rather than blindly committing a diff you don't understand.
91
-
92
- Also reconsider **patch coverage**: this branch has an established pattern (see commit `ad93c5d`'s message and `.mcp.json`-adjacent `codecov.yml`) of keeping patch coverage above ~94% by, among other things, moving standalone explanatory comments onto the code line they document as trailing comments (since this project's bun-based coverage tooling counts every comment/blank line in a diff hunk as an uncovered patch line). Apply the same discipline to your new diff if it introduces many multi-line comments in changed regions — it's cheap and keeps the codecov gate comfortably clear.
93
-
94
- ## Part 2 — Adversarial review of the current branch
95
-
96
- Once Part 1 is fully green and validated (but not yet committed — or committed as a checkpoint if you prefer smaller commits, your call), perform a genuinely adversarial review of the **entire branch diff against `main`** (i.e. `git diff main...HEAD` plus your uncommitted changes) — not just the new pollValidator/mock-server work. This branch has already been through one automated review round (Kilo Code, five findings, all fixed) and one round of real-hardware testing (which is what surfaced the bug you just fixed) — the goal now is to find what *those* rounds missed, not to re-litigate what they already caught.
97
-
98
- Use whichever of these fits best; you have access to the same skills this session had available:
99
- - `/codex challenge` — adversarial mode specifically designed to try to break the code. Well-suited to this task by name; consider using it as a first pass.
100
- - `/review` or `/code-review` — structural pre-landing review (SQL safety, trust-boundary violations, conditional side effects, simplification opportunities).
101
- - Manual review, if you prefer full control or the automated tools are unavailable in your environment.
102
-
103
- Focus areas, in rough priority order:
104
-
105
- 1. **The same bug class elsewhere.** The pollValidator bug was "assume success when a specific text signal never appears, without checking whether that signal was ever achievable for this code path." Grep the codebase for similar patterns — any other poller, verifier, or "wait for X then assume success" loop that has a fallback branch reachable when X structurally cannot occur for some valid code path. `src/tools/programRunners.ts`, `src/c64Client.ts`, and `src/device.ts` are the highest-yield places to look, given they're where the original HARD01-011/018/019/020/021 truthfulness fixes lived.
106
- 2. **The mock-server fidelity gap you just fixed, generalized.** If `scripts/mockC64Server.mjs` didn't simulate keyboard-queue-triggered execution at all before your fix, ask whether other mock behaviors (in this file or `src/vice/mockServer.ts`) are similarly "accepts the request, does nothing, returns success" in ways that could mask a real truthfulness bug the same way the U2 firmware gap did. You don't need to fix speculative gaps, but flag anything concrete you find.
107
- 3. **Standard security review**: command injection, path traversal (especially around `src/tools/meta/filesystem.ts`, `src/tools/storage.ts`, and anywhere device filesystem paths are constructed — HARD01-010's fix should already cover segment encoding, but re-verify it holds against the newest code), credential/secret handling (HARD01-024's redaction — re-verify it's applied everywhere new logging was added, if any), and any newly-introduced regex (the `SYS_COMMAND_PATTERN` you may still be touching in Part 1) for ReDoS risk given it's small and simple but worth a two-second glance.
108
- 4. **Test-suite integrity**: any other test in the suite that, like the two `programRunnersModule.test.mjs` tests you fixed, encodes a *bug* as expected behavior rather than a genuine requirement. This is a real, demonstrated pattern in this codebase now (found twice in one session) — worth a deliberate second pass rather than assuming it was fully exhausted.
109
- 5. **The `.mcp.json` `c64bridge-u64` addition**: confirm it doesn't leak the hardware hostname `u64` or any credentials into anything that shouldn't have it (it shouldn't — no password was configured for these devices — but verify), and that it's harmless if a reader without access to that hardware tries to use this repo (the server entry will simply fail to connect, not error out the whole MCP client setup — confirm this assumption is actually true rather than taking it on faith).
110
-
111
- ## Part 3 — Fix all findings, then land the branch
112
-
113
- Fix everything Part 2 surfaces, following the same discipline as the rest of this branch: minimal, root-cause fixes; a regression test for anything that was a real bug; no speculative refactors. Re-run the full Part 1e validation gate after every batch of fixes.
114
-
115
- **Exit criteria — do not stop before all of these are true:**
116
-
117
- 1. `git status` is clean except for intentional, understood changes.
118
- 2. Full validation gate (Part 1e) passes with zero failures.
119
- 3. Changes are committed with a clear message (follow this branch's existing commit style — see `git log --oneline` — imperative, `fix:`/`test:`-prefixed, explaining *why* not just *what*).
120
- 4. Commits are pushed to `fix/hardening` on the `chrisgleissner/c64bridge` remote.
121
- 5. `gh pr checks 143 --repo chrisgleissner/c64bridge` shows every check green, including `codecov/patch` and `codecov/project`.
122
- 6. If your adversarial review or the push triggers new automated review comments (Kilo Code or otherwise) on PR #143, address every one — reply explaining the fix (or the reasoning if not applicable) and resolve the thread via the GitHub API, exactly as was done for the five Kilo Code findings earlier in this PR's history (see commit `ad93c5d` and its PR comment replies for the established pattern: reply via `gh api repos/chrisgleissner/c64bridge/pulls/143/comments/{id}/replies`, resolve via the GraphQL `resolveReviewThread` mutation).
123
- 7. No unresolved review threads remain on PR #143.
124
-
125
- Only once all seven are true is this work complete. Report back with: a summary of what the mock-server fix ended up being, what the adversarial review found (if anything) and how it was fixed, confirmation of the exit criteria, and the final CI/PR status.
@@ -1,98 +0,0 @@
1
- # Hardening 01 implementation
2
-
3
- Repository: `/home/chris/dev/c64/c64bridge`
4
-
5
- Implement every confirmed finding in [review.md](./review.md): **HARD01-001 through HARD01-035**. This is an implementation task, not a second review. The review's executive-summary/count prose is stale in places; the findings index and detailed findings are authoritative. Do not omit a finding because it is P3, Medium confidence, or shares a file with another finding.
6
-
7
- ## Outcome
8
-
9
- Deliver a coherent, production-safe hardening change that eliminates the false-success, wrong-target, lifecycle, input, file-system, SID, and packaging failures described in the review. Preserve the public MCP contract except where an existing success result is demonstrably false; in those cases, return a clear failure with actionable details. Add focused automated regression tests for every finding, update generated artifacts only when the source/contract change requires it, and leave the working tree with all relevant checks passing.
10
-
11
- Do not perform state-changing operations against real Ultimate hardware. Use unit tests, mock Ultimate HTTP servers, Binary Monitor stubs, and managed VICE only where the repository's existing test setup makes that safe and deterministic. Do not expose passwords or other secrets in logs, tests, fixtures, errors, or documentation.
12
-
13
- ## Start correctly
14
-
15
- 1. Read `AGENTS.md` and the entire adjacent `review.md` before editing. Follow the repository's `.github/skills/*/SKILL.md` instructions for any C64 execution workflow. In particular, read `c64://vice/binary-monitor-spec`/`doc/vice/vice-binary-monitor-spec.md` before changing VICE monitor framing or commands.
16
- 2. Inspect the existing test conventions, package scripts, generated-artifact workflow, public MCP schemas, and call sites before changing result shapes or shared facade APIs.
17
- 3. Keep changes narrow but solve root causes. Do not paper over a failure with logging, an unchecked cast, a longer timeout, or a test-only special case.
18
- 4. Work in logical batches, but run targeted tests after each batch and the complete relevant suite before handoff. Do not claim hardware validation unless it was actually performed with explicit authorization.
19
-
20
- ## Cross-cutting implementation rules
21
-
22
- - Treat Ultimate REST responses as unsuccessful when their JSON `errors` array is non-empty, even when HTTP status is 2xx. Preserve the firmware error strings in structured details. Missing `errors` remains compatible success.
23
- - Pin backend/facade identity for the full lifetime of a tool invocation and any spawned work. A backend switch may affect later invocations but must never retarget an operation already underway.
24
- - Never silently drop input, swallow a failed state-restoration operation, or report a physical action as verified when the required state cannot be observed. Report uncertainty/failure explicitly and include recovery guidance where useful.
25
- - Keep monitor queues and background work cancellable and recoverable. A timeout or shutdown must release subsequent work rather than permanently wedging a backend.
26
- - Use package-relative paths for assets shipped with the package and process-cwd paths only for deliberately user/project-local inputs. Use `fileURLToPath`, not raw URL pathnames.
27
- - Validate and contain all user-influenced host filesystem paths after resolution. Never rely solely on string prefix checks that permit sibling-prefix escapes; use `path.relative`/platform-aware containment.
28
- - Retain backward compatibility deliberately. If a formerly `void` method must signal a timeout, propagate an explicit typed result or error through every caller rather than silently discarding it.
29
-
30
- ## Required fixes and acceptance criteria
31
-
32
- ### HTTP transport, configuration, platform status, and package paths
33
-
34
- - **HARD01-001:** Make `--http [port]` real, or remove the dead parser/imports and the documented option together. Prefer wiring the existing parsed CLI options to a clearly factored transport-selection path using `StreamableHTTPServerTransport`; retain stdio as the default. Test that `--http 8080` selects/listens on the HTTP path and that default invocation selects stdio. Ensure MCP server/prompt registry wiring is actually used or remove the dead path.
35
- - **HARD01-004:** Capability probing must not cache a transient error as `unknown`. Cache confirmed availability/unavailability as appropriate, but evict/retry uncertain results. Test failure-once then success.
36
- - **HARD01-005, HARD01-006, HARD01-027:** Replace divergent configuration loading with one typed, authoritative loader and pass its parsed backend sections to all consumers/facade creation. Candidate precedence must match documented behavior: `C64BRIDGE_CONFIG`, then `process.cwd()/.c64bridge.json`, then home config, then defaults; retain a package-relative legacy fallback only if necessary and document its lower precedence. Resolve home via `os.homedir()` fallback and module paths through `fileURLToPath`. A malformed/unreadable candidate must produce a prominent redacted warning that identifies the file and problem, then continue to the next candidate/defaults rather than crashing or silently disagreeing. Test layered/contradictory configs, project-cwd config under an installed-style foreign package cwd, malformed config fallback, and identical host/port/password selection by startup and facade code.
37
- - **HARD01-016:** Establish an invocation-scoped backend snapshot/facade lease. Platform gating, all operations in a multi-step tool flow, verification, and background work must use that pinned facade. Make switching atomic with platform publication so no request is gated with one platform and sent to another. Avoid a global lock that deadlocks streams or long-running work; a reference-counted/read lease or a façade captured in tool context is preferable. Add a deterministic concurrent mock-facade test where a slow write/verify is interleaved with `c64_select_backend` and every write/read/resume step remains on the original backend.
38
- - **HARD01-025:** Resolve all shipped RAG data, docs, embeddings, AGENTS/prompt assets relative to the installed package/module root, not `process.cwd()`. If an override is supported, make it explicit and test it. Do not silently return an empty index merely because a bundled path was calculated incorrectly; surface a useful diagnostic when an expected bundled index cannot load. Test initialization from a foreign cwd.
39
- - **HARD01-031:** `c64://platform/status` must not block on a normal 10-second live REST probe. Return cached/static/not-yet-probed capability state quickly, or use a bounded short probe that cannot poison the cache. Test a never-resolving probe and assert a prompt resource result within the short bound.
40
-
41
- ### Ultimate REST, input, physical-state safety, drive shape, and logging
42
-
43
- - **HARD01-002 and HARD01-003:** Rework C64U/U64 `power_cycle` to be fail-closed. Before sending final RETURN, positively determine from the menu screen's actual selection/highlight encoding that `POWER CYCLE` is selected; visible text anywhere on screen is insufficient. Confirm firmware color-matrix semantics from repository/device contract and abort on uncertainty. On every post-menu-open failure, make a safe, best-effort, idempotent attempt to close/escape the menu and return cleanup status in failure details. Never report a selected power cycle unless verification succeeds. Test different highlighted rows, all navigation-step failures, no final RETURN on mismatch, and cleanup attempts.
44
- - **HARD01-009:** Do not overwrite an undrained KERNAL queue. Make keyboard queue injection return a typed completion result or throw a typed timeout/error, then update every input tool call site to report partial/non-delivery accurately. Test a permanently nonzero NDX, assert no subsequent chunk overwrite, and assert the user-visible result is failure rather than success.
45
- - **HARD01-010:** Encode Ultimate device filesystem paths segment-by-segment so nested slash separators remain separators while special characters in segments are encoded exactly once. Cover leading slash and spaces in an HTTP request-capture test.
46
- - **HARD01-011:** Centralize `ActionResponse` error-array handling in the C64U/U2 backend; apply it to every action, configuration, drive, program, file, and playback method rather than hand-patching a few call sites. Nonempty `errors` must result in `success: false` and retain useful response details. Mock 200+errors for mount, run, and config operations and assert the MCP/tool layer reports failure.
47
- - **HARD01-017:** If write verification paused a machine and resume fails, do not let the original write success escape as a clean success. Return/throw a failure that records write/verification outcome, `machinePaused: true`, the resume failure, and clear recovery instructions; an appropriately bounded retry is acceptable. Test return and thrown resume failures.
48
- - **HARD01-023:** Normalize `drivesList` at the facade boundary into one documented internal shape (at least `id`, `power`, `image`, `type`, plus any needed raw details), for both VICE and Ultimate/U2. Decode the actual Ultimate `{ errors, drives: [{ id: info }] }` response rather than leaking it. Make `drive_mount_and_verify` use that normalized contract and verify the correct image-path/power fields. Test the real firmware-shaped mock response.
49
- - **HARD01-024:** Redact sensitive HTTP request/response diagnostic metadata case-insensitively before it reaches stderr or diagnostics persistence. At minimum mask `X-Password` and authorization/cookie-equivalent secrets. Test that a known test secret never appears while the header key/placeholder may remain useful for diagnosis.
50
- - **HARD01-034:** Align printer module runtime gates with advertised U2 capability: support `c64u` and `u2` if device-4 BASIC printing is actually supported by the stated contract, otherwise remove the advertised U2 feature and explain it. The likely intended fix is explicit `supportedPlatforms: ["c64u", "u2"]` on both relevant modules. Add capability/gating coverage.
51
-
52
- ### VICE monitor, emulator lifecycle, input, and config
53
-
54
- - **HARD01-012:** Add per-request Binary Monitor response deadlines that reject, remove the pending request, and reset/destroy the unhealthy socket so the serialized monitor queue always releases. Use a considered timeout policy (normal versus large display transfer if needed), clear timers on response/error/close, and avoid unhandled rejections or request-ID reuse confusion. The parser must preserve framing correctness during resynchronization; do not scan arbitrary payload bytes as a new frame unless the protocol framing permits it. A stub server that accepts but never answers must prove bounded rejection and a subsequent operation must succeed after reconnect.
55
- - **HARD01-013:** Ensure managed VICE and Xvfb are stopped on normal exit and SIGINT/SIGTERM/SIGHUP. Since async work cannot complete in `exit`, expose/use a synchronous best-effort kill path there; signal handlers should coordinate bounded async shutdown where viable and then exit without recursive/double cleanup. Guard already-exited children. Add tests around registered handlers/child termination and, where reliable in CI, an integration check proving no managed child survives SIGTERM.
56
- - **HARD01-014:** Implement VICE Binary Monitor `0xA2` Joyport Set from the in-repo spec, including correctly encoded port and active-low control mask. Route VICE joystick press/release/tap through it; do not write CIA data-port registers to simulate input. Test exact BM bytes for press/release/tap and retain C64U physical-input behavior unchanged.
57
- - **HARD01-015:** Construct checkpoint masks so explicitly provided operations are exactly honored; default to execute only when no load/store/execute flag was requested. Test `{store:true}` serializes `0x02` and other combinations.
58
- - **HARD01-026:** Make VICE reset/reboot success contingent on `waitForBasicReady`'s required readiness signals. Return structured `success:false` with readiness diagnostics when it does not reach READY; update callers/tests accordingly.
59
- - **HARD01-035:** Stop coercing values solely because they look numeric. Determine VICE resource type from the monitor/resource API (or add an explicit value type where the public schema supports it) and send a string payload for numeric-looking string resources such as `"1541"`. Test both resource types and batched behavior.
60
-
61
- ### Program execution, BASIC generation, and validation
62
-
63
- - **HARD01-007:** Generate valid Commodore BASIC V2 printer output for strings containing quotes. Do not use doubled quotes. Split expressions and emit `CHR$(34)` (while preserving all other literal characters), maintain line-length/chunking correctness, close the printer channel on normal/error paths where feasible, and ensure the tool does not claim completed printing merely because load/run started. Add generator/tokenizer-level coverage for quoted text and a VICE-facing regression if the suite supports it.
64
- - **HARD01-008:** Make the PETSCII screen program actually wait for a key without immediately falling through to `READY.`. Use a valid GET loop and preserve intended termination behavior. Test generated source and, if possible, rendered-screen behavior before a key press.
65
- - **HARD01-018:** Make `upload_run_asm` execute assembled code on every supported backend. Establish one robust contract for entry address and PRG layout: either emit a valid BASIC SYS stub for applicable loads while keeping labels/address accounting correct, or inject/run the PRG then explicitly `SYS` the assembled entry in a backend-safe manner. Do not point VICE BASIC pointers at arbitrary non-BASIC ML. Test the published `$0801` example by observing its intended memory/screen effect and test a non-$0801 origin does not corrupt BASIC workspace. Keep symbol and verification metadata truthful.
66
- - **HARD01-019 and HARD01-020:** Replace the ASM liveness heuristic so it neither treats free-running VIC/CIA values as activity nor reads side-effectful CIA ICRs on hardware. Prefer program-specific expected effects/explicit verification where supplied; any fallback must restrict reads to side-effect-free addresses, mask volatile registers, and honestly describe its confidence. Test a memory stream differing only at `$D012` as non-alive/crashed and assert hardware polling never overlaps `$DC0D`/`$DD0D` (or the I/O ranges whose reads have side effects).
67
- - **HARD01-021:** Detect BASIC runtime errors only using actual BASIC error syntax/patterns, not arbitrary occurrences of the word `ERROR`. Test `0 ERRORS FOUND` as successful and a canonical `?SYNTAX ERROR IN 20` as failure.
68
-
69
- ### Background work, artifacts, and SID playback
70
-
71
- - **HARD01-022:** On reload, persisted tasks marked `running` must never remain zombie status. Either safely re-arm them once execution context is available or mark them `interrupted`/`stopped` with a restart explanation; choose and document one deterministic policy. Test persisted reload and task listing/state.
72
- - **HARD01-028:** Treat resolved `{ success:false }` operation results as failed task iterations, recording `lastError` and truthful status/failure counts. Decide whether retry continues; if it does, make the continued/retry status explicit. Test both thrown and returned failures.
73
- - **HARD01-029:** Reject unsafe `runId` and all artifact-derived filenames before writing. Resolve the output base and candidate then prove containment with `path.relative`; reject separators, traversal, absolute paths, empty normalized names, and unsafe range components. Test `../escape`, absolute/sibling-prefix escapes, and verify no out-of-tree file is created.
74
- - **HARD01-030:** Replace detached `music_generate` playback with tracked, cancellable work. Pin its facade/backend at creation, expose an existing-compatible stop/cancel path (integrate with the background task registry or a dedicated playback registry), stop/cancel on shutdown and before/when backend switches according to a documented policy, and do not monopolize VICE's queue beyond individual note operations. Preserve immediate scheduling response only if it truthfully says playback is scheduled/running rather than completed. Test that switching backends cannot redirect later notes and cancellation prevents future writes.
75
- - **HARD01-032:** Correct every SID frequency formula to the 24-bit phase accumulator: `round(hz * 16777216 / phi2)` with existing valid 16-bit clamp. Update both production implementations and knowledge/spec text, including PAL and NTSC handling. Test A4/PAL is approximately 7493 (`$1D45`) and that note-on writes low/high bytes correctly.
76
- - **HARD01-033:** Retrigger the SID envelope between generated notes by writing gate-low for an appropriate bounded interval before the next gate-high/note-on. Do not leave a permanently high gate except where an explicit legato contract asks for it. Test register-write sequence includes gate clearing between successive notes.
77
-
78
- ## Test and validation gate
79
-
80
- At a minimum, add/extend automated tests that demonstrate every numbered acceptance criterion above. Group them sensibly (config/path, Ultimate mock HTTP, VICE BM stub/lifecycle, program generation/validation, background/SID), but name cases with the HARD01 IDs or titles so coverage is auditable. Run:
81
-
82
- 1. formatter/linter/typecheck and the repository's normal test command;
83
- 2. focused tests for every changed subsystem;
84
- 3. package/build and generated-contract checks when applicable;
85
- 4. a final `git diff --check` and review of all changed public schemas/docs/skills.
86
-
87
- If an existing test harness cannot safely perform a desired managed-VICE or device-level assertion, retain a deterministic stub/unit test and report the exact unperformed HIL validation as a limitation. Do not weaken a requirement merely because production hardware is unavailable.
88
-
89
- ## Documentation and handoff
90
-
91
- Update docs, generated metadata, schemas, and `.github/skills` only where behavior, supported platform, failure/recovery behavior, or invocation semantics changed. In particular, keep `AGENTS.md`, README/developer docs, platform-status capabilities, and MCP descriptions consistent with the implementation. Do not edit `review.md` to hide findings.
92
-
93
- At handoff, provide:
94
-
95
- - a concise mapping of HARD01 IDs to the implemented change and tests;
96
- - commands run and their outcomes;
97
- - any intentionally deferred hardware-only validation, with reason;
98
- - an explicit statement that all 35 findings were implemented, or a precise list of any remaining IDs and the blocker. Do not claim completion while any HARD01-001..035 finding remains unresolved.