Skip to content

Racket port phases 1-2: core runtime + ISO-TP/UDS/DoIP diagnostics stack - #29

Merged
turinglambdaai merged 7 commits into
mainfrom
racket-port-2
Oct 1, 2026
Merged

turinglambdaai merged 7 commits into
mainfrom
racket-port-2

Conversation

@turinglambdaai

Copy link
Copy Markdown
Owner

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

  • Core runtime: frozen wire-contract records with typed camelCase serializers; evidence pipeline (bounded stores, per-target context rings, failure scopes); the runtime state machine (target-first mutation gates with rollback, first-cause-wins cancellation, wall-clock deadline rejection of late successes, bounded history, drain); the target-oriented facade (power/serial/flash/diagnostics operations, preflight, real-ECU readiness).
  • Diagnostics: ISO 15765-2 codec + endpoint over the in-process simulated CAN bus (per-port pump threads); ISO 14229 UDS client (P2/P2*, NRC 0x78 pending); behavioral simulated ECU (S3 session timeout, security access attempt counting, erase/verify routines, block sequencing); transport-independent flash engine (CRC32 verify, audited steps); simulated power/serial/flash bench; ISO 13400-2 DoIP path (TCP routing activation, alive checks, UDP discovery, simulated entity).
  • The full UDS flash workflow runs hardware-free in Racket, over both ISO-TP/CAN and DoIP, against the same behavioral ECU the C# simulator uses.

Port bugs the port itself caught

  • tolerant JSON reading corrupted escaped string literals ("\n" became "n\""); regression test added against profiles/real-ecu.example.json;
  • '() used as the empty-queue marker — truthy in Racket, so reassembly queues returned phantom empty payloads;
  • encoder shifts used positive amounts where C# right-shifts, zeroing flash addresses on the wire.

Not in this PR

Phases 3-5 (HTTP daemon, client/CLI, drivers, packaging, docs) follow on this branch as they land.

ren.ji and others added 7 commits October 1, 2026 14:54
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
turinglambdaai merged commit be74878 into main Oct 1, 2026
4 checks passed
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant