Racket port phases 1-2: core runtime + ISO-TP/UDS/DoIP diagnostics stack - #29
Merged
Merged
Conversation
Core runtime: frozen wire-contract records with typed camelCase serializers, evidence pipeline with bounded stores and per-target context rings, the runtime state machine (target-first mutation gates with rollback, first-cause-wins cancellation, wall-clock deadline rejection of late successes, bounded history, drain), and the target-oriented facade (power/serial/flash/diagnostics operations, preflight, real-ECU readiness). Diagnostics: ISO 15765-2 codec and endpoint over an in-process simulated CAN bus with per-port pump threads, the ISO 14229 UDS client (P2/P2* timing, NRC 0x78 pending), the behavioral simulated ECU (sessions with S3 timeout, security access attempt counting, DID read/write, erase/verify routines, RequestDownload/TransferData/Exit block sequencing), the transport-independent flash engine (CRC32 verify, audited steps) and the simulator bench (power/serial/flash sharing virtual board state). 42 ported/contract tests green locally; port bugs found by the port itself: escaped-string corruption in tolerant JSON reading (regression test added), '() used as empty-queue marker (truthy in Racket), left-shifts where C# encoders right-shift.
ISO 13400-2 frames over TCP: routing activation handshake, alive-check answering, buffered frame reassembly; UDP vehicle discovery with loopback-then-broadcast; the simulated DoIP entity serving the shared behavioral UDS processor; and the DoIP UDS channel behind the same diag-channel contract, so the flash engine drives CAN and DoIP unchanged. DoIP workflow and channel tests green; 44 ported tests total green locally.
benchpilotd: loopback HTTP/1.1 JSON API over the runtime with per-user
token auth, admit/stopping middleware, route table over flat thunks,
bounded-drain shutdown and autostart quiet mode (stdio detach).
benchpilot CLI: the full command surface (status/doctor, operations and
observations with evidence/cancel, preflight/validate, power, flash,
serial, UDS, DoIP discover, shutdown) with the stable exit-code table,
one-retry daemon autostart and indented/compact JSON output per --json.
End-to-end through real HTTP: doctor, validate, power on, flash,
serial wait, UDS DID read, power check, history, power off, shutdown —
all green through the CLI against the daemon. Port-side findings:
hasheq never matches dynamically-built string keys (equal?-hash the
header table), Racket 0/1 integers are truthy like everything else,
case only matches symbol datums, and #rx lacks \d and {n} (use #px).
44 unit tests + the CLI/daemon flow green locally.
Generic raw-TCP SCPI power supply with profile command templates, IO-gated connection lifecycle, safety shutoff on transport/protocol errors, and bounded response timeouts (TCP input ports as sync events). ScpiPowerTests ported: the full profile-driven flow against a fake SCPI server and the timeout-triggers-safety-off path. 46 tests green. Port finding: hasheq's eq?-based keys silently fail for dynamically built strings (the fake server's command table never matched); use equal-based hash for anything with runtime-built keys.
SEGGER J-Link Commander backend: generated command files through the installed JLink.exe/JLinkExe with process timeout, probe enumeration that never touches the target MCU (ShowEmuList only), executable resolution across Program Files/SEGGER and /opt/SEGGER paths, and factory validation parity (device required, speed/timeout ranges, binAddress hex-string format). J-Link and SCPI drivers complete phase 4's TCP/process-backed drivers; SocketCAN/PCAN (Linux/Win FFI) and system-serial (POSIX/Win32 FFI) follow when hardware is available per ADR 0002's Phase 4 gate.
The derivers table was hasheq (eq?-based), so the plan's dynamically built 'xor0x5a' key never matched the table entry on Linux CI. Use equal-based hash for any table with runtime-built string keys.
Deriver names arrive as plan strings from other modules; hasheq's eq?
lookup misses cross-module string literals, so CI failed 3 flash/DoIP
tests that passed locally only by compiled-literal-sharing luck. Also
name the fake SCPI server's silence sentinel correctly ('no-reply)
instead of relying on the same lookup miss.
turinglambdaai
added a commit
that referenced
this pull request
Oct 2, 2026
main holds the same phases 0-2 content as this branch (PR #29 squash); conflicts resolve to this branch, which is a strict superset built on the same tree.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Continues the port plan from ADR 0002 (PR #28) with the core runtime and the complete diagnostics protocol stack. 44 ported/contract tests green locally; CI gains no new gates — the existing racket-port job runs the new tree on Windows + Linux.
What lands here
Port bugs the port itself caught
"\n"became"n\""); regression test added againstprofiles/real-ecu.example.json;'()used as the empty-queue marker — truthy in Racket, so reassembly queues returned phantom empty payloads;Not in this PR
Phases 3-5 (HTTP daemon, client/CLI, drivers, packaging, docs) follow on this branch as they land.