Stride
By midnight, two completely different problems had been worked the same way. That was the thing worth noticing.
One was an A2L file rejection in a closed-source ECU tuning tool that ships only to professionals. The other was whether a 20mm chrome ball would sit cleanly in a 3D-printed race at R=75mm, D=25mm. The two problems have nothing in common. By the end of the night they'd used the same method.
Track one: the data side
The ECU work is a partnership — eight years of experience on the tuning side, a few weeks of code-side groundwork from this end. The need is a tool that takes a map-pack format and emits ASAP2-standard A2L files that work in Swiftec, the editor used on the other side of the partnership.
The first round emitted A2L files that passed every spec validator we could find. They imported only two of two hundred maps in Swiftec. Silent rejection on the other 199.
The day was spent systematically eliminating every spec-legal axis description pattern A2L permits. FIX_AXIS_PAR_DIST — rejected. VAL_BLK with MATRIX_DIM — rejected. FIX_AXIS_PAR_LIST with explicit per-point values — rejected. STANDARD_AXIS with axes inline in the record layout — rejected. FIX_AXIS_PAR with offset and shift — rejected.
The breakthrough wasn't a fix. It came from the tuning side: clone the two surviving maps' shape across all two hundred addresses and see what happens. Built a Python script that did exactly that. Loaded the result in Swiftec. Two hundred maps imported. Maps displayed at completely wrong sizes — every clone was 18×18 because that was the template — but the imports landed. That meant the rejection wasn't about addresses or memory regions or count caps. It was specifically about how we handled maps without binary-resident axes.
The production fix went out as version 0_4_0: when no real axis address exists, point the axis at the map's own address. The axis values read from there are garbage bytes, but the structure is valid, and Swiftec accepts the import with correct dimensions. Pending verification in the morning.
The work that mattered wasn't the fix. It was the discipline of testing one variable at a time, in cheap material, before committing to a production change.
Track two: the bench side
While the ECU iteration ran in one window, the workshop ran in another. The propulsion prototype — a pulsed offset gyro demonstrator covered in earlier posts — is moving from CAD into plastic. Before any full prototype print could start, four interfaces needed tolerance checks. Each one printed small. Each one took minutes, not hours.
The bearing seat block went first. A real bearing dropped in, pressed clean, no slack. Next, the NEMA 17 motor mount — 31×31mm M3 hole pattern, motor sat flush. Next, the drive axis coupling — a flat-on-D-shaft joint built with what was on hand, expected to break under torque later, holding fine in fit testing now. Last and most critical: a slice of the drive disk with one of the three radial slots cut, with a 20mm chrome ball dropped into the geometry at R=75, D=25.
The verdict on the slot fit: good enough for a rough and ready prototype.
That's the right bar. This isn't a precision instrument. It's a visceral demonstrator — the simulation already has the rigour, what the bench needs is a thing in the hand that walks across the surface when the motor spins up. Each interface tested in cheap material before committing to a full-print material budget that could easily be twenty-four hours of plate time and a substantial amount of filament.
Same method as the ECU track, in completely different material.
What linked them
The same shape twice:
- Build a cheap probe — Python script, small printed jig
- Test one variable at a time
- Let the evidence eliminate options, not the assumption
- Only commit to the larger move once the probe has landed
On the ECU track the cheap material was a few Python scripts and ~100KB A2L files. On the workshop side it was a few grams of PLA and an hour of print time. The cost of each probe was negligible. The cost of NOT running them would have been hours of rebuild work and material wasted on the wrong design.
It is not a new pattern. It is the working method any maker eventually develops. What was different today was running it on two completely unrelated problems at the same time, neither one blocking the other, both ending the day with cleared gates ready for the next phase.
That is the stride.
One more thing built tonight
The night closed with a small desktop GUI for a call-transcription tool that has been sitting in CLI-only form for a week. Tomorrow morning the verification call may come in. The tool that captures it needs to exist before the call lands.
Built in customtkinter with a dark theme matching the blog brand, threaded so the UI stays responsive while faster-whisper processes audio on the GPU. End-to-end pipeline tested with a forty-three-second recording. The CUDA dependency that bit on first run — cublas64_12.dll missing from the Python process's DLL search path — got patched by registering the pip-installed NVIDIA wheel bin directories with os.add_dll_directory() before importing faster-whisper.
Tool ready. Not because it is a finished product. Because the moment that needs it is coming.
Stride is not loud. It is the bench still standing in the morning, ready to keep going.
Co-written with my co-engineer Claude (Opus 4.7) at the end of a long evening.