Skip to content

projects/ janelia/ Mouse Joystick/

Verification Evidence

The record of how the mouse-joystick-pcb v1.0 boards and this firmware were verified, from the first v1.0 power-up on 2026-10-05 to the installed rig's first observed experiment on 2026-10-09, the desk rig's bring-up on 2026-10-12 and its reflash on 2026-10-14: one directory per bench or rig session, named by date, time and subject. Each holds the console logs, protocol transcripts, pyControl session outputs, trace analyses and small logic-analyzer captures of that session, with meta.txt naming the firmware commit and build, the board serial and the setup. The measurements these files support are written up, with their numbers, in the board repository's bring-up log (mouse-joystick-pcb/docs/bringup-log.md), which cites the files here as mouse-joystick-firm/evidence/<session>/<file>.

The sessions were driven from the sibling repository qp-arduino-example, whose evidence/ directory keeps the raw material in full (about 835 MB of .sr and .vcd logic-analyzer captures). This copy keeps every text file, the captures up to 1 MB and the four first-board captures the bring-up log cites by name; each session's OMITTED.sha256 lists what was left behind with its size and hash so the raw files can be identified and requested. bench-scripts/ holds the shell scripts that drove the sessions, verbatim.

The Boards

Serial (RP2040 flash unique id) Board Rig position Pull sign
E4654C159F274534 board 6, pcb v1.0 unit A right joystick −1
E4654C159F404534 board 7, pcb v1.0 unit B left joystick −1
E4654C159F171E34 board 4, pcb v0.3 bench, not in the rig −1
E4654C159F504534 board 8, pcb v1.0 unit C desk rig, left joystick (port 1) −1
E4654C159F234234 board 9, pcb v1.0 unit D desk rig, right joystick (port 4) −1

The pull sign is the only per-board calibration the firmware carries (src/BoardCalibration.hpp); on all five boards a pull is the decreasing-angle direction, found by a hand pull classified PUSH under the default sign (2026-10-07-0855-unitA-solenoid-pull/pilot/pull1_60s.txt, 2026-10-07-0908-unitB-bringup/pilot/pull_left_60s.txt, 2026-10-12-1355-deskrig-bringup/pull_right_60s.log and pull_left_60s.log). The left joysticks' torque sensors read a pull negative where the right ones read positive; the firmware uses the magnitude.

What Was Verified

  • Every bring-up stage on both v1.0 boards: stage 1 self test (20 checks), stage 2 TMC5130A register reads with the switched rail power-cycled, A3 the angle DAC over I2C, A6 motion, encoder tracking and homing, A4 the brake current sink (linear to 49 mA) and A5 the torque bridge (2026-10-05-1142-board4-regression/v1/, 2026-10-06-0848-unitA-brake-torque/, 2026-10-07-0908-unitB-bringup/).
  • Gate E: pyControl 2.1.1 running the bring-up task on the pyboard against a board over the RJ-45, through pyControl's own board API (2026-10-06-1019-unitA-gate-e/ and the runs after it; unit B on Breakout port 1 in 2026-10-07-0908-unitB-bringup/pilot/gate_e_left.txt).
  • The sampler's tick budget attributed on the bus and on the pin, and the flash stage-2 change that brought it back to 1 kHz (2026-10-06-1104-unitA-tmc-spi-timing/, 2026-10-06-1132-unitA-tick-attribution/, 2026-10-06-1140/1147/1355-unitA-gate-e-fastflash/).
  • Positioning: the park error measured and attributed to lost motion on reversal, then the encoder settle that bounds every TARGET to 0.18° (2026-10-06-1407-unitA-park-error/, 2026-10-07-0807-unitA-encoder-settle/).
  • The rig's own task: eight trials dry, the reward path with a hand on the lever, both analog outputs on an oscilloscope, then 400 trials unattended with no fault (2026-10-07-0840-…, -0855-…, -0950-…, -1040-unitA-soak/).
  • The installed rig: the laptop observer identifying which analyzer sits on which board and watching the desktop's mock experiment (2026-10-09-0919-rig1-which-analyzer/, 2026-10-09-0920-rig1-mock-experiment/).
  • The desk rig: two further v1.0 boards (units C and D) from the ROM bootloader through the stage 1 self test and the delivery build, id over both RJ-45 ports, one trial of the rig's task per board through the headless runner, and both pull signs by hand (2026-10-12-1355-deskrig-bringup/).
  • Link loss on hardware: the task's reply timeouts exercised by unplugging the RJ-45 before a session (three resends, session ended at 2.0 s) and during a watch (ended at the deadline, board latched host_timeout with its rail off, next session recovered) (2026-10-12-1355-deskrig-bringup/).

Sessions

Most recent first. The firmware column names the commit git describe reported (-dirty means uncommitted changes in the working tree at build time) and the PlatformIO environment.

Session What it shows Firmware
2026-10-14-0910-deskrig-reflash/ the desk rig reflashed from Peter's laptop, the practice run for Rig 1: both boards took the 1200 baud touch through pixi run upload (board 8 in 7.6 s, board 9 in 4.3 s, the board that had refused the touch on 2026-10-12), picotool verified each image, and id;1 over each RJ-45 from the pyboard's REPL (uart_id_probe.log) answered fw=v1.0.0-2-g109169f on both; the delivery build's USB banner cannot be caught after a reboot (the CDC port appears ~1 s later), so the RJ-45 is the only read of the version. No 36 V, no measurements; meta.txt has the laptop's CONDA_OVERRIDE_GLIBC requirement firmware v1.0.0-2-g109169f mouse_joystick_qp_rev2 (the code of tag v1.0.0)
2026-10-12-1355-deskrig-bringup/ Peter's desk rig, first bring-up: two never-powered v1.0 boards (8 E4654C159F504534 on port 1, 9 E4654C159F234234 on port 4) found in the ROM bootloader, flashed with the stage 1 self test by picotool load -x --bus 1 --address N (20 checks, 0 failed each), then the delivery build (board 9 needed a direct picotool load after pixi run upload's picotool step failed as on unit B); id over each RJ-45 from the pyboard's REPL; the second pyboard found on MicroPython 1.22.2, its 18 files copied off through the REPL (pyboard-flash-before/), upgraded to 1.26.1 by dfu-util (the ST bootloader jumps only after a download with -s :leave); the rig's joystick_trial.py with the new reply timeouts run once per board through run_task_headless.py: home 1.3–1.4 s, present 0.36 s, RELEASED, NOMOVE, TRIAL, park within 0.1°, session complete, no resends; then the pull sign of both boards by hand pull (PUSH under +1, PULL at −10.08° after −1 went into BoardCalibration.hpp), board 9 needing manual BOOTSEL because it ignores the 1200 baud touch, and the pyboard wedged once by an overlapping REPL probe; finally the lost-reply case on hardware: RJ-45 unplugged at start (three resends, session ended at 2.0 s) and pulled during the watch (ended at the 15 s deadline, board latched host_timeout with its rail off, next session recovered). Numbers in the PCB bring-up log, "Desk rig" firmware 316f802 mouse_joystick_qp_rev2 (first boards with the Janelia Research Campus USB string); pyControl 2.1.1 on MicroPython 1.26.1
2026-10-09-0920-rig1-mock-experiment/ the laptop observer's first run, watching the desktop's mock experiment (two joystick_trial trials on the left board at 09:23:18, two on the right at 09:23:33, from the joystick-rig branch files through pycontrol_run.py): lai monitor live on all three LA5032s from the laptop, 15 s windows armed on marker0, 2 MSa/s; unit B (1-5.4) ok 2 NOMOVE, unit A (1-5.1) ok 2 NOMOVE, heartbeat 24.3 Hz, 0 naks / faults / framing errors, camera board ok with 150 Hz triggers and every follower matched, one degraded that is the health model's count-over-span rate across the 5 s pause between the tasks (its README explains; instrument-control PLANS Phase 3). Also the laptop's setup record: firmware from the nonfree artifacts, udev rule, qspy built with CONDA_OVERRIDE_GLIBC=2.41 firmware 51322e7-dirty mouse_joystick_qp_rev2; pyControl 2.1.1; instrument-control f350621 on the laptop
2026-10-09-0919-rig1-which-analyzer/ which laptop analyzer sits on which kind of board, the 3 s identification capture before the run above: 1-5.1 and 1-5.4 trace alive (silent, joystick boards), 1-5.3 dead (the camera board, no MCU); the serials came from the ok windows' identity acks as above
2026-10-07-1040-unitA-soak/ 400-trial soak of the rig task on the right joystick, nobody at the lever, ~35 min (pilot/soak400.txt): 400 NOMOVE, 0 nak, 0 FAULT, 33 min 53 s; every present and park within one encoder count (park 0.0 × 399, −0.091 × 1), trial period 5.081 ± 0.023 s with no drift, 53 190 link lines; numbers in the PCB bring-up log firmware 51322e7-dirty mouse_joystick_qp_rev2; pyControl 2.1.1
2026-10-07-1035-unitA-rig-files-verbatim/ the rig's pyControl files as the researcher's GUI uploads them: the originals failed (33.8 KB free after "Load framework", 22.3 KB of drivers, the 15.5 KB hardware definition did not fit); after the prose moved out (definition 2.1 KB, task 6.9 KB, code unchanged) the GUI path leaves 7.7 KB free and two trials ran clean from the verbatim files (pilot/verbatim2.txt) firmware 51322e7-dirty mouse_joystick_qp_rev2; pyControl 2.1.1
2026-10-07-0950-unitA-scope-hold/ both analog outputs of the right board on Peter's scope with the joystick in the hand: the bring-up task's new hold/angout knobs keep the board RELEASED with the brake at 1.0 N·cm and the angle output on; run 1 (pilot/hold.txt) lost the angout line to a badframe behind the blocking home (angle BNC flat, torque BNC live) and ended host_timeout when its host was killed (USB backpressure stalls the pyboard); run 2 (pilot/hold2.txt, the lines behind the move, scale −28.125..0°, clrfault first) — both traces follow the hand firmware 51322e7-dirty mouse_joystick_qp_rev2; pyControl 2.1.1
2026-10-07-0908-unitB-bringup/ pcb v1.0 unit B (E4654C159F404534), the LEFT joystick, stages 1 through A5 in one morning: after the capacitor rework the board enumerated as the ROM bootloader and took RP2040SelfTest by UF2 copy, 20 checks, 0 failed (s1_selftest_rev2_boardB.log); stage 2 two clean direct-LATCH power-ups, 5/0 register checks, 29.9 mA idle (s2_tmc_rev2_boardB.log); A3 the angle DAC at 0x48 with GAIN read back, monitor 0.829/1.649 V at mid/full scale (a3_angleoutput_rev2_boardB.log); A6 the left joystick's switch at the positive end, active high, encoder tracking, homed, homing spread 9 counts (runs 3–5 within one), live loop 350 µs (a6a_gates_unitB.log, a6b_motion_unitB.log, captures on the second LA5032); A4 brake sweep linear to 49.11 mA, full-scale step 128.5 % overshoot without ~{BRAKE_FAULT} as on unit A (a4_brake_unitB.log); A5 torque 103 µV rms, 1000.1 Hz at 137 µs worst, shunt-cal inert, rest 1.55 N·cm above the nominal zero (a mechanical preload), −0.006 N·cm brake coupling (a5_torque_unitB.log). captures/la2_quiet_all32.sr, la2_beacon.sr, la1_during_unitB_beacon.sr: the second adapter straight, the analyser map by beacon. Then pilot/gate_e_left.txt: Gate E through pyControl on Breakout port 1 (captures/gate_e_left.sr, CH0–CH3), and the pull direction: pull_left_60s.txt PUSH with the default sign, pull_left_60s_signed.txt PULL after {"E4654C159F404534", -1.0} went into the calibration table; the left torque sensor reads the pull negative. The uploader's bootloader step failed on this unit every time after the first; each flash finished by UF2 copy. Numbers in the PCB bring-up log firmware 51322e7-dirty mouse_joystick_rev2
2026-10-07-0855-unitA-solenoid-pull/ the reward path, with the solenoid on Breakout port 5 and a hand on the right joystick (the rig's trial task, lean copies): pilot/pull2.txt two 25 s windows missed (the pull came late; one TOUCH 15 ms before the timeout); pilot/pull1_60s.txt the pull from −4.95 to −9.991 classified PUSH (pull sign +1 wrong for unit A), then the park against the held lever faulted not_following and one faults request was answered badverb before its retry succeeded; pilot/pull1_60s_signed.txt after flashing pull_sign −1 for E4654C159F274534: PULL at −10.083, the 50 ms reward pulse, TRIAL outcome=PULL a_peak=5.041, park at 0.0, session complete. Numbers in the PCB bring-up log firmware 51322e7-dirty mouse_joystick_qp_rev2 (BoardCalibration: unit A pull sign); pyControl 2.1.1
2026-10-07-0840-unitA-rig-trial-dryrun/ the rig's own trial task, eight trials without homing between them (joystick-rig branch feat/mouse-joystick: hardware_definition_joystick.py and tasks/rig1/joystick_trial.py, home_each_trial=0, uploaded as comment-free copies, pilot/*.lean.py) on unit A, right joystick, nobody at the rig: every present within one count of −5° (−4.95 ×7, −5.041 ×1), every park at 0.0 or +0.084, eight NOMOVE, no fault, 5.1 s a trial with a 2 s watch and 1 s ITI (pilot/dryrun8.txt). The first attempt failed on a full pyboard flash: stale MCP.py/rotary_encoder.py from 2026-10-06 and a truncated frame_trigger.py (pyControl never removes device files) firmware bd9bcbb-dirty mouse_joystick_qp_rev2; pyControl 2.1.1
2026-10-07-0807-unitA-encoder-settle/ the encoder settle after a move (uart-protocol.md V1.13): the same probe chain as …-1407-unitA-park-error/ on the firmware that commands moves relative to the encoder and corrects the residual after position_reached (pilot/settle_park3.txt; settle_park3_trace.txt with the QS trace in captures/, uart/, qspy/): every TARGET within 0.18° of its target (before: up to 0.99°), the park at 0.0 and +0.084; MOVE_SETTLE shows the reversals arriving 0.63–0.99° short, one correction removing most of it, and 1–2 counts left that a correction at IRUN 8 cannot move against static friction (the next same-direction move then overshoots by it); a reversal costs 150–260 ms more, an uncorrected move 10 ms. Numbers in the PCB bring-up log firmware bd9bcbb-dirty mouse_joystick_qp_rev2 (quad stage 2, TMC51X0 4.2.0, IRUN 8); qp-arduino v7.3.4
2026-10-06-1407-unitA-park-error/ the park error measured with the bring-up task's park knob (pilot/park*.txt): at RELEASED the encoder reads −4.774° against a ramp generator at −4.999 (the rotor relaxed 0.27° when the drive let go under the brake); leaving RELEASED with stop alone and waiting 300 ms leaves both frames at −4.774 with the brake off; the park move then still lands at −0.991. A chain of six probe moves (−5, −2.5, 0, −2.5, −5, 0) loses 0.35–0.77° at every direction reversal and ≤ 0.07° on a same-direction move: lost motion on reversal, not an encoder or step loss. park3_irun17.txt: the same chain at IRUN 17 (0.42 A RMS) instead of 8: the after-positive state −0.633 three times out of three (from −0.82…−0.99), hysteresis a third smaller — about 0.3° is the stepper's torque angle under friction, 0.3–0.6° is drive-train lost motion firmware mouse_joystick_qp_rev2 (quad stage 2, TMC51X0 4.2.0); the IRUN 17 run on a temporary build
2026-10-06-1355-unitA-gate-e-fastflash/ Gate E run 10: run 9's configuration built against the released TMC51X0 4.2.0 tag instead of the symlinked checkout; identical result (1 kHz throughout, 70 overrun skips and 23 coalesced ticks in 50 s outside the rail transients, snapshot max 171–213 µs). The build the board was left with firmware 9b6d2ef-dirty mouse_joystick_qp_rev2_fastflash, TMC51X0 4.2.0; qp-arduino v7.3.4
2026-10-06-1147-unitA-gate-e-fastflash/ Gate E run 9: the sampler at 1 kHz for the whole run, trial included — the quad-lane flash stage 2 plus the sampler budget raised from 400 to the measured 700 µs; never decimated, 988–1013 polls/s, handler max 798 µs in RELEASED and 371 in ARMED, 79 overrun skips and 47 coalesced ticks in 50 s (the morning's runs: ≈1900 and ≈1500); the sequence unchanged to the millisecond. The configuration the board was left in firmware 9b6d2ef-dirty mouse_joystick_qp_rev2_fastflash, TMC51X0 working copy; qp-arduino v7.3.4
2026-10-06-1140-unitA-gate-e-fastflash/ Gate E run 8 on the quad-lane flash stage 2 (mouse_joystick_qp_rev2_fastflash, boot2_w25q128jvxq_4 instead of the generic 03h): the sequence identical, the sampler at 1 kHz in ARMED (1003–1008 polls/s, handler max 330–370 µs), decimated only inside the trial by the 400 µs budget constant; POLL_STAT maxima 2–3× lower, a ping 0.5 ms (LINK_WORK). The proof that the inflation was instruction fetch from flash firmware 9b6d2ef-dirty, TMC51X0 working copy; qp-arduino v7.3.4
2026-10-06-1132-unitA-tick-attribution/ the TICK handler measured on the pin (MJ_TICK_LEVEL build, CH0 level with CH1 trace and CH2/CH3 UART at 2 MSa/s, tools/la/tick_level.py) and two trace runs at 25 and 2.5 host lines/s: in RELEASED the handler is 758 µs median (p90 1.09 ms), in the ARMED idle 268 µs (p90 465), a host line adds ~300 µs to the tick after it; LINK_WORK puts a ping at 1.2–1.9 ms of handler time. The time is code execution, not bus traffic; see the bring-up log. tick_level_fastflash/: the same probe on the quad-lane flash stage 2 firmware 9b6d2ef-dirty mouse_joystick_qp_rev2 (+ -DMJ_TICK_LEVEL=1), TMC51X0 working copy
2026-10-06-1124-unitA-gate-e/ Gate E run 7: the same sequence on the build with the TMC51X0 working copy (one buffer transfer per datagram, the six-datagram pipelined snapshot), the watchdog running from hv on with blocked time credited (V1.12) and the task taring before watch (t_touch empty, torque 0.12 N·cm after the tare). The sampler is still decimated with the rail up and its POLL_STAT maxima did not move although the bus time fell 37 %: the inflation is not in the SPI path firmware 9b6d2ef-dirty mouse_joystick_qp_rev2 + TMC51X0 working copy; qp-arduino v7.3.4
2026-10-06-1115-unitA-gate-e/, 2026-10-06-1120-unitA-gate-e/ Gate E run 7, first two passes: run 6's once-per-advance poll gate plus the TMC51X0 working copy (buffer-transfer datagrams, the pipelined six-datagram snapshot), the watchdog armed by hv on (V1.12) and the task taring before watch; 1120 adds the protocol clock synced to the wall after blocking commands. pilot/gate_e.txt and .json each; the completed pass is 2026-10-06-1124-unitA-gate-e/ firmware 9b6d2ef-dirty mouse_joystick_qp_rev2 + TMC51X0 working copy; qp-arduino v7.3.4
2026-10-06-1104-unitA-tmc-spi-timing/ the TMC5130 SPI bus itself (CH8 SCK, CH9 ~CS, CH10 COPI, CH11 CIPO at 50 MSa/s, 1 s each) during the task's ARMED idle: before.sr TMC51X0 4.1.0 (ten 17 µs datagrams per snapshot, 11.6 µs between them, 269 µs per snapshot, 60 µs per encoder read), after.sr the in-place buffer transfer and the pipelined snapshot (six datagrams, 3.5 µs between, 167 µs per snapshot, 38 µs per read), after_instr_off.sr the same with the trace off (no change), after_slow_hb.sr with the host heartbeat at 2.5/s instead of 25/s: the sampler ran at 1 kHz (1004 polls/s), so the host lines are the load; after2.sr the two-buffer core transfer (clocks back to back) firmware 9b6d2ef-dirty mouse_joystick_qp_rev2; TMC51X0 4.1.0 then the working copy
2026-10-06-1043-unitA-gate-e/ Gate E, third pass, the poll gate right: the same task against the build whose hardware backends read their buses once per advance of the protocol clock (beginAdvance()) — bus polls exactly one per handled tick (488/s in RELEASED, decimated), overrun skips ≈30/s (from ≈310), worst handler 1.27 ms (from 2.45); still decimated with the rail up because one poll set costs 550–1040 µs: every primitive at 2–4× its console cost (POLL_STAT) firmware 9b6d2ef-dirty mouse_joystick_qp_rev2 with the once-per-advance gate; qp-arduino v7.3.4
2026-10-06-1037-unitA-gate-e/ Gate E, second pass, the poll limit keyed to the wall clock (500 µs): the sequence identical, but 720–734 bus polls/s against ≈400 handled ticks — the second catch-up stop of a late tick polled again once the first poll had outlasted the window; handlers to 2.8 ms firmware 9b6d2ef-dirty mouse_joystick_qp_rev2, wall-clock limit; qp-arduino v7.3.4
2026-10-06-1019-unitA-gate-e/ Gate E passed: pyControl 2.1.1's own Pycboard path (tools/pycontrol_run.py in the firmware repo, no GUI) uploaded the lean device files, mouse_joystick_bringup and the task mouse_joystick_check to the PYBV11 and ran the whole sequence against v1.0 unit A over the RJ-45 (Breakout 1.2 port 4): identify, rail, home, arm, move to −5°, BRAKE 1.0 N·cm → RELEASED, TOUCH, NOMOVE, TRIAL, park, stop/disarm/hv off. The trace's SAMPLE_STAT/POLL_STAT show the bus polled 1000×/s with the sampler decimated to 500 ticks/s in RELEASED and handlers of 1–2.4 ms: the once-per-tick limit was keyed to the protocol clock firmware 9b6d2ef-dirty mouse_joystick_qp_rev2 (UART0); qp-arduino v7.3.4
2026-10-06-1007-unitA-gate-e/, 2026-10-06-1010-unitA-gate-e/, 2026-10-06-1017-unitA-gate-e/ the three Gate E attempts before it: pyControl's device-file discovery recursing to a RecursionError on the MouseJoystick/MouseJoystickProtocol prefix (1007), the pyboard's flash too small for the commented sources (1010), and the task crashing on a get_current_state() the task API does not have, one step after hv on (1017; the rail stayed up until the next run's hv on/hv off) same
2026-10-06-1001-unitA-uart0-protocol/ protocol v1 over the real link from the pyboard's REPL (machine.UART(3)), 36 V on: hv on, tq zero, home, arm, brake 1.0 → RELEASED, watch → NOMOVE/TRIAL, fetch; then host_timeout because the REPL script waited 150 ms on the fetch — the board's correct response firmware 9b6d2ef mouse_joystick_qp_rev2; qp-arduino v7.3.4
2026-10-06-0959-unitA-uart0-first-contact/ first bytes over UART0: id from the pyboard REPL on Breakout 1.2 port 4 (UART(3)), the request on CH3 and the 170-character reply on CH2 decoded on the Digital-50 taps same
2026-10-06-0920-unitA-qp-brake-torque/, 2026-10-06-0925-unitA-qp-brake-torque/, 2026-10-06-0931-unitA-qp-brake-torque/, 2026-10-06-0938-unitA-qp-brake-torque/, 2026-10-06-0942-unitA-qp-brake-torque/ the brake and torque verbs of protocol v1 on the USB bench build (tools/brake_torque_steps.py): 0920 faulted host_timeout because the script's first version starved its own heartbeat, 0925 clean, 0931 with the ADS8689 transport on a busy wait (no change to the tick), 0938 and 0942 with the new POLL_STAT record attributing the tick cost to interrupt inflation of every primitive firmware 80fdf3b-dirty mouse_joystick_qp_rev2_usb; qp-arduino v7.3.4
2026-10-06-0848-unitA-brake-torque/, 2026-10-06-0853-unitA-brake-steps/ pcb v1.0 unit A stages A4 and A5 through examples/BrakeAndTorque over USB CDC (bench-scripts/unitA_brake_torque.sh, unitA_brake_steps.sh): bus-switch glitch at reboot, brake sweep 0–48.80 mA, step responses at several amplitudes with ~{BRAKE_FAULT} sampled (overshoot above ~60 % FS, fault line silent), torque statistics, the inert shunt-cal switch, the live loop; CH16–CH21 of the adapter verified firmware 80fdf3b-dirty mouse_joystick_rev2 (not QP)
2026-10-05-1142-board4-regression/ HEAD ec3b36d on board 4 (pcb v0.3): every stage tool, no firmware regression; then v1/ — the same stages on pcb v1.0 unit A, the first v1.0 board on the bench, with 19 Digital-50 channels verified through the v2.0 adapter firmware ec3b36d; qp-arduino v7.3.4

bench-scripts/ holds the shell scripts and the Python step drivers that ran these sessions, verbatim, with the bench machine's paths as they were; its README says what each one did. Transcripts and tool logs likewise record the bench machine's file paths and the boards' USB strings as they were at the time (Orb Weaver was the USB manufacturer string until 2026-10-12).