{"id":11564,"date":"2026-07-23T14:57:38","date_gmt":"2026-07-23T13:57:38","guid":{"rendered":"https:\/\/purplemeanie.co.uk\/?p=11564"},"modified":"2026-07-23T14:57:38","modified_gmt":"2026-07-23T13:57:38","slug":"rewriting-my-evs-vehicle-control-software-with-an-agentic-workflow","status":"publish","type":"post","link":"https:\/\/purplemeanie.co.uk\/index.php\/2026\/07\/23\/rewriting-my-evs-vehicle-control-software-with-an-agentic-workflow\/","title":{"rendered":"Rewriting My EV\u2019s Vehicle Control Software with an Agentic Workflow"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">It has been a while since I gave a proper written update on the Purple Meanie project. That doesn\u2019t mean progress has stopped\u2014quite the opposite. I\u2019ve been working on it almost full-time. I even creep downstairs in the middle of the night to kick off another vibe when I think of something I should have taken into account.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <a href=\"https:\/\/purplemeanie.co.uk\/index.php\/2026\/07\/23\/project-seven-pt-10-project-update-and-software-re-write-with-ai\/\" data-type=\"post\" data-id=\"11558\">latest video<\/a> sticks my head rather firmly above the parapet: I have rewritten the vehicle-control software from a clean sheet, using ChatGPT Codex as an agentic coding partner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Before anyone asks: no, the new code isn\u2019t open source yet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It remains experimental, incomplete and tailored specifically to my car. If it reaches a point where I think it&#8217;s useful and responsible to release it, I intend to publish the parts that aren\u2019t restricted by supplier NDAs. It isn\u2019t ready for that today.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The New Video<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/purplemeanie.co.uk\/index.php\/2026\/07\/23\/project-seven-pt-10-project-update-and-software-re-write-with-ai\/\" data-type=\"post\" data-id=\"11558\">Project sEVen pt 10: Project Update and Software re-write with AI<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The the last video is a broad bench update but also video explains the new VCU code I&#8217;ve been working on:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"2048\" height=\"1152\" data-attachment-id=\"11567\" data-permalink=\"https:\/\/purplemeanie.co.uk\/pt10-bench-setup\/\" data-orig-file=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Pt10-Bench-Setup-scaled.jpg\" data-orig-size=\"2560,1440\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;1&quot;,&quot;alt&quot;:&quot;&quot;}\" data-image-title=\"Pt10 Bench Setup\" data-image-description=\"\" data-image-caption=\"&lt;p&gt;Project sEVen Pt10 Bench Setup&lt;\/p&gt;\n\" data-large-file=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Pt10-Bench-Setup-2048x1152.jpg\" src=\"http:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Pt10-Bench-Setup-2048x1152.jpg\" alt=\"Project sEVen Pt10 Bench Setup\" class=\"wp-image-11567\" srcset=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Pt10-Bench-Setup-2048x1152.jpg 2048w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Pt10-Bench-Setup-600x338.jpg 600w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Pt10-Bench-Setup-768x432.jpg 768w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Pt10-Bench-Setup-1536x864.jpg 1536w\" sizes=\"auto, (max-width: 2048px) 100vw, 2048px\" \/><figcaption class=\"wp-element-caption\">Project sEVen Pt10 Bench Setup<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Why start again?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">My Caterham has an unusual high-voltage architecture. It has both a low-side and a high-side HV bus, connected by a bidirectional traction DC-to-DC converter.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I had incorporated this into the open-source ZombieVerter software and had it substantially working. The problem wasn\u2019t that the existing software couldn\u2019t support it. The problem was that I didn\u2019t like the architecture I was creating as my project diverged further from the original system.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">My folly was then while I was away for a weekend, I decided to find out what Codex could do with a clean sheet of paper. I&#8217;d done over a dozen AI related coding projects over the previous 6 months and finally thought it was time to see if a safety critical, high speed, don&#8217;t chuck John in a ditch project could be vibe coded&#8230; what could possibly go wrong!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To be clear, the new implementation doesn\u2019t copy the existing ZombieVerter application software or its old web interface. The ZombieVerter hardware remains, and the proven behaviour of the original system is an important reference\u2014particularly for safety-related sequencing\u2014but the new application architecture and implementation started from scratch.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After four weeks, 28 development phases and a fairly spectacular number of tokens, I had the system shown in the video running on the bench.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That is both exciting and slightly terrifying.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What&#8217;s been built?<\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"1080\" data-attachment-id=\"11568\" data-permalink=\"https:\/\/purplemeanie.co.uk\/project-seven-pt10-dashboard\/\" data-orig-file=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Dashboard.jpg\" data-orig-size=\"1920,1080\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;1&quot;,&quot;alt&quot;:&quot;&quot;}\" data-image-title=\"Project sEVen Pt10 Dashboard\" data-image-description=\"\" data-image-caption=\"&lt;p&gt;Project sEVen Pt10 &amp;#8211; Web Dashboard&lt;\/p&gt;\n\" data-large-file=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Dashboard.jpg\" src=\"http:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Dashboard.jpg\" alt=\"Project sEVen Pt10 - Web Dashboard\" class=\"wp-image-11568\" srcset=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Dashboard.jpg 1920w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Dashboard-600x338.jpg 600w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Dashboard-768x432.jpg 768w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Dashboard-1536x864.jpg 1536w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><figcaption class=\"wp-element-caption\">Project sEVen Pt10 &#8211; Web Dashboard<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The system uses the STM32F107 on the ZombieVerter board as the authoritative vehicle controller. An ESP32-S3 daughterboard provides the web interface, command-line terminal, logging and other communications facilities.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The STM32 must remain capable of controlling the vehicle safely if the ESP32 is absent, rebooting, overloaded or being reprogrammed. The browser and ESP32 are therefore observation and request surfaces, never the authority for vehicle behaviour.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The STM32 software is organised into four broad layers:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Drivers deal with pins, ADCs, CAN controllers, timers and communications hardware.<\/li>\n\n\n\n<li>Devices interpret hardware data and publish facts about components such as the IVT-S shunt, throttle and drive selector.<\/li>\n\n\n\n<li>Services own functions such as high-voltage sequencing, torque decisions, fault handling and configuration.<\/li>\n\n\n\n<li>The Vehicle finite state machine owns the overall operating mode.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Time-sensitive hardware input uses interrupts and DMA where appropriate, but the interrupt handlers do not run vehicle policy. They capture, timestamp, queue and notify. Safety decisions remain in deterministic task-context code where they can be tested and reviewed.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"2048\" height=\"1280\" data-attachment-id=\"11566\" data-permalink=\"https:\/\/purplemeanie.co.uk\/driver_device_service_overview_highres\/\" data-orig-file=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/driver_device_service_overview_highres-scaled.png\" data-orig-size=\"2560,1600\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;,&quot;alt&quot;:&quot;&quot;}\" data-image-title=\"driver_device_service_overview_highres\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/driver_device_service_overview_highres-2048x1280.png\" src=\"http:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/driver_device_service_overview_highres-2048x1280.png\" alt=\"\" class=\"wp-image-11566\" srcset=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/driver_device_service_overview_highres-2048x1280.png 2048w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/driver_device_service_overview_highres-600x375.png 600w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/driver_device_service_overview_highres-768x480.png 768w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/driver_device_service_overview_highres-1536x960.png 1536w\" sizes=\"auto, (max-width: 2048px) 100vw, 2048px\" \/><figcaption class=\"wp-element-caption\">Project sEVen Pt10 &#8211; current software architecture<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The ESP32 runs its own FreeRTOS tasks and provides two main ways to observe the system.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The first is a web interface containing the Dashboard and detailed Drivers, Devices, Services and Vehicle views. It also provides configuration, signals, plotting, logging, system diagnostics, events and fault information.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The second is a command-line terminal. This allows me to inspect the system in much greater depth, including task execution, processor use, link performance, subscriptions, inputs, outputs and service state.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"1080\" data-attachment-id=\"11569\" data-permalink=\"https:\/\/purplemeanie.co.uk\/project-seven-pt10-terminal-interface\/\" data-orig-file=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Terminal-Interface.jpg\" data-orig-size=\"1920,1080\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;1&quot;,&quot;alt&quot;:&quot;&quot;}\" data-image-title=\"Project sEVen Pt10 Terminal Interface\" data-image-description=\"\" data-image-caption=\"&lt;p&gt;Project sEVen Pt10 Terminal Interface&lt;\/p&gt;\n\" data-large-file=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Terminal-Interface.jpg\" src=\"http:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Terminal-Interface.jpg\" alt=\"\" class=\"wp-image-11569\" srcset=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Terminal-Interface.jpg 1920w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Terminal-Interface-600x338.jpg 600w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Terminal-Interface-768x432.jpg 768w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Terminal-Interface-1536x864.jpg 1536w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><figcaption class=\"wp-element-caption\">Project sEVen Pt10 Terminal Interface<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Compact binary link<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The STM32 and ESP32 communicate over a one-megabit serial connection using a bounded binary protocol.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At startup, the STM32 reports the checksum and generation of its operator catalogue. If the ESP32 doesn\u2019t have the matching catalogue in its cache, it retrieves a new copy.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The catalogue describes the public fields, identifiers, types, units, views and commands that the ESP32 needs. Once that metadata is known, normal telemetry can be transferred using compact identifiers and binary values rather than repeatedly sending field names or JSON.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Telemetry is subscription-based. The ESP32 maintains a small operational baseline and requests additional data according to what an operator or connected browser needs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Continuously changing values\u2014voltages, currents, ADC measurements and throttle position\u2014use bounded sampled streams. State transitions and status changes can be sent on change. Ordered events, where sequence and loss matter, remain separate from latest-value telemetry.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If there is no connected browser, the higher-detail browser subscriptions aren\u2019t needed. If the ESP32 is completely absent, none of this prevents the STM32 from operating the vehicle safely.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Safety and diagnostic behaviour<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Every device fact has explicit validity and freshness.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A voltage of zero is not used to mean \u201cmissing,\u201d \u201cinvalid\u201d or \u201cstale.\u201d These are separate states. If the IVT-S shunt stops communicating, for example, its data becomes stale and the owning services can remove permission or initiate the appropriate safe response.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The High Voltage Manager owns contactor sequencing and the low-side and high-side precharge process. The Fault Manager independently evaluates faults and can veto unsafe operation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Torque service calculates the requested torque, while a separate Torque Monitor checks that decision using an independent set of bounds and safety conditions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Real inverter torque production is not enabled yet. The current torque path is diagnostic and zero-safe until the real inverter, speed, availability, BMS limits and other required facts have proper owners and validation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The IVT-S installation also provides the measurements required for future weld detection. The algorithm exists, but it isn\u2019t yet part of the active control loop.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Testing something this large<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The project has a host-side test harness that runs the shared control logic on a Mac without requiring the vehicle hardware.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This lets me test state machines, contactor sequencing, stale and invalid device data, queue pressure, request rejection, fault behaviour and other safety-related cases deterministically.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Selected tests also generate timing diagrams. These aren\u2019t attractive diagrams drawn afterwards to explain what I hoped the software would do. They are generated from structured test traces, which means I can inspect the ordering of inputs, decisions, state transitions and output intent produced by the test.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1002\" height=\"1058\" data-attachment-id=\"11565\" data-permalink=\"https:\/\/purplemeanie.co.uk\/torque_path_timing_contract\/\" data-orig-file=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/torque_path_timing_contract.png\" data-orig-size=\"1002,1058\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;,&quot;alt&quot;:&quot;&quot;}\" data-image-title=\"torque_path_timing_contract\" data-image-description=\"\" data-image-caption=\"&lt;p&gt;Project sEVen Pt10 &amp;#8211; simple timing verification diagram&lt;\/p&gt;\n\" data-large-file=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/torque_path_timing_contract.png\" src=\"http:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/torque_path_timing_contract.png\" alt=\"\" class=\"wp-image-11565\" srcset=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/torque_path_timing_contract.png 1002w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/torque_path_timing_contract-568x600.png 568w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/torque_path_timing_contract-768x811.png 768w\" sizes=\"auto, (max-width: 1002px) 100vw, 1002px\" \/><figcaption class=\"wp-element-caption\">Project sEVen Pt10 &#8211; simple timing verification diagram<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Automated tests are only one part of the evidence. Firmware builds, browser tests and structural checks are also run during development. Codex can flash the boards, access the terminal and probe the web interface, so a surprising amount of integration diagnosis can be automated.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hardware validation remains my responsibility. I review the results and perform the staged tests on the bench. Anything involving contactors, high voltage, real CAN devices or physical output behaviour requires hardware evidence before I trust it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">My agentic development workflow<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u201cAI wrote it\u201d doesn\u2019t adequately describe what is happening.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nor do I give Codex a one-line instruction to \u201cbuild me a VCU\u201d and return later to find a finished vehicle controller waiting for me.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The process is structured, supervised and deliberately evidence-heavy. It evolves as I learn what works, but the current version looks like this.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s the workflow as a flowchart (it&#8217;ll change&#8230; of course)&#8230;<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1630\" height=\"1181\" data-attachment-id=\"11572\" data-permalink=\"https:\/\/purplemeanie.co.uk\/purple-meanie-agentic-workflow\/\" data-orig-file=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/purple-meanie-agentic-workflow.png\" data-orig-size=\"1630,1181\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;,&quot;alt&quot;:&quot;&quot;}\" data-image-title=\"purple-meanie-agentic-workflow\" data-image-description=\"\" data-image-caption=\"&lt;p&gt;The Purple Meanie agentic workflow. Specialist roles are invoked when needed, while standard Codex implements one approved slice at a time. Automated checks support\u2014but never replace\u2014John\u2019s architecture decisions, bench validation and final acceptance.&lt;\/p&gt;\n\" data-large-file=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/purple-meanie-agentic-workflow.png\" src=\"http:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/purple-meanie-agentic-workflow.png\" alt=\"\" class=\"wp-image-11572\" srcset=\"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/purple-meanie-agentic-workflow.png 1630w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/purple-meanie-agentic-workflow-600x435.png 600w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/purple-meanie-agentic-workflow-768x556.png 768w, https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/purple-meanie-agentic-workflow-1536x1113.png 1536w\" sizes=\"auto, (max-width: 1630px) 100vw, 1630px\" \/><figcaption class=\"wp-element-caption\">The Purple Meanie agentic workflow. Specialist roles are invoked when needed, while standard Codex implements one approved slice at a time. Automated checks support\u2014but never replace\u2014John\u2019s architecture decisions, bench validation and final acceptance.<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">What&#8217;s it Cost?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Lots and lots and lots of tokens.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I started off on the free ChatGPT plan a year ago then upgraded to the $20\/mo plan. But then Codex got released at the end of last year and things changed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I was doing more and more AI coding projects. Stuff I&#8217;d had in the back of my mind for years, but I didn&#8217;t want to spend 6 months developing. But Codex changed all that.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And to be clear Claude Code will do just as good a job. It and Codex are good at their own specialities. I like Codex, but that&#8217;s not to say you couldn&#8217;t do what I&#8217;ve done with Clause Code. Pick your poison!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Back to the costs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After burning through my $20\/mo tokens, I soon jumped to the $100\/mo plan. That might sound a lot to some people. But you have to remember that a High Voltage Amphenol connector can easily cost that. Spening $100 a month on having a whole software development team at my beck-and-call is a bargain. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I did also bump up to the $200\/mo plan for a while. Just to get some thorny issues resolved on a tight timescale (even retired people have timescales). But I could drop back to the $100 plan once that was over. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then there&#8217;s the time cost. In the 4 weeks in question, I did a bunch of vibing. Because I could do it at the test-bench or on the sofa, I was at it every waking second. Seven days a week. I could set it off doing something simple and leave running while I did chores around the house or went for a walk. But it was doing something while I was off doing life. So it was way more than 40 hours a week &#8211; just like running a start-up again! \ud83e\udd23 <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">A Note on Current AI<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">There&#8217;s lots to be said about AI coding at the moment. And I don&#8217;t have space to do that justice here. But one thing I will say is that vibe coding (agentic coding with AI) is not a fix-all problem. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For sure some people have got good results from vibing even when they&#8217;re not software engineers. But the key thing is that you need to understand your problem-space. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I&#8217;ve been coding real time software for over 45 years and running software development teams for more than 35 years. To me AI coding is like being a CTO &#8211; you guide the project at that level. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">AI goes and does the work of a team of dozens of engineers, but you have to have the critical problem-space questions that you can apply to the AI as it works its problems. It&#8217;s just like being a CTO.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Persistent project memory<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The project doesn\u2019t rely on one enormous chat history.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A compact\u00a0<code>HANDOVER.md<\/code>\u00a0records the current state, active phase, next action and important warnings.\u00a0<code>README.md<\/code> provides the human entry point.\u00a0<code>AGENTS.md<\/code>\u00a0tells a new Codex task which documents to read and which working rules take precedence.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Stable architecture and engineering decisions live under&nbsp;<code>docs\/<\/code>. Detailed work is broken into phase plans under&nbsp;<code>plans\/<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This lets a fresh task build the necessary context from the repository rather than depending on what an earlier conversation happened to remember.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2. A phase begins with a bounded objective<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">I decide on the next coherent piece of work: support a device, change a task boundary, add a diagnostic path, or address a structural problem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When a genuine planning pass is needed, I explicitly invoke the Planner role. It turns the objective into a proposed phase with bounded slices, tasks, validation requirements, exclusions and exit criteria.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A future inverter phase, for example, would not simply be called \u201cmake the motor spin.\u201d It would identify the real inverter facts, command ownership, safety gates, staged bench tests and conditions under which torque output must remain inhibited.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The plan is then reviewed and revised with me. I decide whether the phase is accepted and whether it should begin.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There&#8217;s a real phase markdown file attached at the end of this post.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3. Architecture is treated as a gate where necessary<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Material changes may start with an architecture gate.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Architect role examines ownership, dependency direction, task boundaries, public contracts, test seams and where the code should physically live. It can propose documentation and planning changes, but it doesn\u2019t silently expand the project or start implementing production code.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Not every small change needs an architecture ceremony. The role is invoked when the decision genuinely concerns architecture.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4. Standard Codex implements one approved slice<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Once a plan is accepted, a normal Codex task carries out the implementation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It works through one approved slice at a time. It inspects the existing owners, makes the smallest coherent change, runs the required checks and records the evidence in the plan.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Codex is already good at normal software implementation, so I don\u2019t wrap that work in an elaborate fictional \u201ccoding agent\u201d persona. The repository rules, active plan and local architecture provide the harness.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Source structure is enforceable. Broad composition files aren\u2019t allowed to become the default home for new behaviour. Structural checks flag oversized files, legacy hotspots and selected dependency violations.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">5. Automated evidence is gathered as part of the slice<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Depending on the work, Codex may run:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>host unit and regression tests;<\/li>\n\n\n\n<li>deterministic replay or fault-injection tests;<\/li>\n\n\n\n<li>STM32 and ESP32 firmware builds;<\/li>\n\n\n\n<li>browser tests;<\/li>\n\n\n\n<li>source-structure checks;<\/li>\n\n\n\n<li>protocol and contract fixtures;<\/li>\n\n\n\n<li>resource and firmware-size comparisons;<\/li>\n\n\n\n<li>live terminal and web probes; and<\/li>\n\n\n\n<li>carefully bounded board flashing and bench diagnostics.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">A failed check isn\u2019t quietly described as \u201cprobably fine.\u201d It remains a failed check or a recorded limitation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6. I perform the manual validation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Once the automated work is complete, I review what changed and carry out the required bench testing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The agents don\u2019t mark manual bench validation complete on my behalf. I own that evidence, along with slice acceptance and phase closure.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The system stops after each slice. It doesn\u2019t autonomously continue into the next area of work just because more checkboxes exist.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">7. Independent review is genuinely separate<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For selected slice or phase gates, I invoke a Reviewer in a separate Codex task.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Reviewer doesn\u2019t inherit the implementation discussion and isn\u2019t supposed to rationalise what the implementation task intended. It examines the result against the plan, architecture, safety rules and evidence.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It reports required findings separately from optional improvements. Any required findings go back through the normal implementation and validation process.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Reviewer doesn\u2019t decide that the project is accepted. That remains my decision.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">8. Commit, hand over and repeat<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">After a coherent slice is complete and accepted, I authorise a focused local Git commit. That creates a checkpoint I can inspect or return to if later work goes wrong.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The plan and handover documentation are updated, and only then do I authorise the next slice.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When all slices are complete, the phase receives its full regression and review pass. I then decide whether to accept and close it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Suggested image: a simple workflow graphic showing: Idea \u2192 Plan \u2192 Architecture gate \u2192 Implement \u2192 Automated checks \u2192 Bench validation \u2192 Independent review \u2192 Accept and commit. Place \u201cJohn\u201d across the whole flow to emphasise continued human control.<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What do I actually do?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I\u2019m involved throughout.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I choose the objectives, review the plans, make the architecture and safety trade-offs, watch the work, answer questions, examine the evidence, perform the bench testing and decide whether each slice is accepted.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I often watch Codex work. A chunk usually takes minutes rather than hours, so it isn\u2019t like watching paint dry. Seeing which files it examines, what assumptions it makes and how it reacts to test results is informative, and I can steer it while it works.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The AI produces code much faster than I could type it, but typing speed isn\u2019t the difficult part of vehicle-control software. The difficult parts are deciding what the system should do, assigning authority, controlling failure behaviour, preserving determinism, proving the important paths and knowing what hasn\u2019t yet been validated.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Those responsibilities have not been delegated.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Is 81,000 lines of code completely bonkers?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Possibly. Probably. Definitely!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That figure includes the STM32, ESP32 and web application, and reflects a large amount of diagnostic and operator functionality. There is at least as much written material again in plans and durable technical documentation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A high line count is not automatically evidence of either quality or waste. It does, however, increase the cost of inspection and maintenance.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For instance, the phase following the video release is going to be a behaviour-preserving source-boundary cleanup. Its purpose is to reduce duplicated lifecycle logic and move accumulated responsibilities out of large integration files without changing the vehicle, protocol or safety behaviour.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Even that cleanup has a baseline slice, frozen contracts, structural evidence and an architecture gate before production files are rearranged.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why not simply stay with ZombieVerter?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The established ZombieVerter codebase has years of community experience and testing behind it. My new implementation doesn\u2019t.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That is a serious limitation, not something that AI makes disappear.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, my vehicle architecture and software were diverging substantially from the original project. Much of the code I would eventually have created around that divergence would also have required its own testing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Starting again gave me an architecture I understand and which directly represents this car. It also gave me the opportunity to build host testing, replay, explicit validity and freshness, diagnostics and documentation into the project from the beginning.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I\u2019m not proposing this as a replacement for ZombieVerter, and I\u2019m not currently building a generic controller into which anyone can drop arbitrary components. The architecture may allow that direction in the future, but today the implementation is deliberately specific to my vehicle.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What happens next?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The video captures a significant milestone, but not a finished VCU.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The immediate work includes more testing and source-boundary cleanup. After that come the real inverter integration, motor feedback, BMS and energy limits, final torque publication and carefully staged attempts to make the motor turn.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There is still a very long distance between \u201cworks on my bench\u201d and \u201ctrusted in the car.\u201d<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For now, though, the contactors sequence, the traction DC-to-DC converter produces its 260-volt high side (which is what I&#8217;m asking for at the moment &#8211; it will be 800V in the finished bench and vehicle), the VCU observes the system through its new web and terminal interfaces, and the clean-sheet architecture has proved that it can run on the real hardware.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Four weeks earlier, it was a blank page on a laptop at a campsite.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I\u2019m looking forward to seeing where it goes next.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Am I crazy? Probably.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Is it over-engineered? Of course. That\u2019s what I do.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let me know what you think\u2014and happy blatting.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Bonus Material<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Completed Phase Plan<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s a recent phase plan to re-work the way the web interface was serving content. This isn&#8217;t meant to be read verbatim, it&#8217;s to give a flavour of the work that goes into creating a plan and working through it&#8230; this is NOT me issuing a fire-and-forget order for Codex to go and implement some random output.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># Phase 28: Web Panel Information Design<br><br>Status: complete as of 2026-07-14. The 13 bounded Services, Devices, and<br>Drivers migrations in Slices 28.41-28.53, the Vehicle-page follow-on in Slices<br>28.60-28.63, Dashboard rework, and CAN hardening are implemented and reviewed.<br>Post-close live-data regressions found on 2026-07-15 were resolved without<br>reopening the phase: ESP32 WebSocket send recovery was hardened and STM32<br>contactor intent\/applied changes now trigger `output_gate_state` publication.<br><br>## Goal<br><br>Make browser panels easier to scan without hiding the distinction between<br>inputs, internal decisions, outputs, faults, freshness, validity, and detailed<br>diagnostics.<br><br>The phase should produce a small reusable presentation system, prove it on the<br>Torque \/ Drive Permissions, Drive Selector, and Traction DCDC panels, and leave<br>a clear bug\/deferred-work record for later expansion. It is deliberately a<br>quick web-polish phase, not a site-wide rewrite.<br><br>## Why This Phase Exists<br><br>Phases 26 and 27 made the browser faster, denser, and substantially more<br>complete. As more real facts have appeared, however, panels have accumulated<br>many visually equivalent chips. A categorical state, a measured voltage, an<br>input request, an applied output, a stale source, and a fault reason can all<br>look like peer values even though they mean different things.<br><br>The browser needs a predictable visual grammar. An operator should be able to<br>look at any device or service panel and quickly answer:<br><br>- what came in;<br>- what the STM32 decided or is doing internally;<br>- what went out;<br>- whether the evidence is valid and fresh;<br>- what is blocking, degraded, or faulty;<br>- where to find raw counters, timestamps, source, and transport details.<br><br>## North Star<br><br>Each detailed live panel follows the same hierarchy where the underlying data<br>supports it:<br><br>```text<br>Panel header: name | overall condition | freshness\/age | optional source<br><br>Inputs              Internal state \/ decision        Outputs<br>requested facts  -> validation, mode, permission -> applied\/published facts<br><br>Condition strip: faults | vetoes | inhibits | stale\/invalid evidence<br><br>Diagnostics drawer: raw values | counters | sequence | timestamps | cache\/feed<br>```<br><br>Not every panel needs every region. Empty structure must not be rendered merely<br>for symmetry, and unavailable data must not be invented.<br><br>## Scope<br><br>Included:<br><br>- Audit the current Torque \/ Drive Permissions, Drive Selector, and Traction<br>  DCDC panels and map each displayed fact to input, internal state\/decision,<br>  output, condition, freshness\/validity, source, or detailed diagnostics.<br>- Define reusable browser components and CSS for panel headers, stage groups,<br>  compact metric rows, signal rows, condition strips, freshness\/age indicators,<br>  source tags, permission matrices, counter pairs, and diagnostics drawers.<br>- Retain chips for categorical state such as mode, permission, validity,<br>  freshness, and fault reason; render numeric values as aligned dense metrics<br>  rather than large chips.<br>- Use state rails only for real state machines, not for arbitrary collections<br>  of enum values.<br>- Prototype the shared grammar on:<br>  - Services -> Torque \/ Drive Permissions;<br>  - Devices -> Drive Selector;<br>  - Devices -> Traction DCDC.<br>- Preserve live stream\/cache updates, initial hydration, source\/freshness<br>  meaning, unavailable\/no-feed behavior, tooltips, and mobile layout.<br>- Add or update local browser tests for the shared components and the three<br>  prototype panels.<br>- Run browser\/board smoke checks and record rendering, update, and regression<br>  evidence.<br>- Maintain a short Phase 28 bug\/deferred-work list. Fix small defects in the<br>  touched presentation path; defer unrelated data-source, firmware, and broad<br>  page issues to an explicitly named later phase or follow-up.<br><br>Out of scope:<br><br>- A site-wide migration of every panel before the prototype is reviewed.<br>- A new browser framework, build system, navigation model, or visual brand.<br>- Changes to STM32 authority, control policy, safety decisions, or CAN\/device<br>  behavior.<br>- New STM32 instrumentation or new data feeds solely to make a prototype look<br>  complete. Missing data should be shown honestly and recorded for later work.<br>- Reworking configuration, logger, files, Wi-Fi, update, or other workflow<br>  pages that do not use the live device\/service panel grammar.<br>- Making colour the only indication of state.<br>- Broad backend optimisation or stream-rate work unless a regression in the<br>  touched panels demonstrates a concrete need.<br><br>## Information And Colour Rules<br><br>- Panel headers summarize overall condition; they do not repeat every detail<br>  shown in the body.<br>- Inputs, decisions, and outputs use stable placement and explicit labels.<br>- Numeric values use aligned label\/value\/unit rows. Chips are reserved for<br>  categorical facts and short, meaningful states.<br>- Requested intent and applied\/published result must remain distinguishable.<br>- Faults, vetoes, inhibits, stale state, and invalid evidence remain visible<br>  without opening a drawer.<br>- Raw protocol values, counters, sequence numbers, timestamps, cache details,<br>  and low-level source evidence may move into the diagnostics drawer.<br>- Green means healthy\/valid; blue or cyan means active\/selected\/energised;<br>  amber means inhibited\/stale\/degraded; red means fault\/unsafe; grey means<br>  inactive\/unavailable\/not implemented; purple may distinguish requested<br>  intent from applied state.<br>- Every coloured state also has a text label, icon, shape, or accessible title.<br>- `live`, `snapshot`, `static`, `unavailable`, and `not implemented` retain the<br>  Phase 26 meanings everywhere.<br>- A benign inactive state must not look like missing data, and unavailable<br>  evidence must not look healthy merely because no fault is asserted.<br><br>## Slice 28.00 Existing Panel Audit And Prototype Contract<br><br>- [x] Inventory the three current panels, their render functions, shared UI<br>  helpers, CSS, endpoints\/topics, hydrate sources, and live-update paths.<br>- [x] Classify every displayed field by information role and identify duplicate,<br>  ambiguous, incorrectly prominent, and drawer-worthy facts.<br>- [x] Record the proposed shared component API and responsive layout contract.<br>- [x] Capture before-state screenshots or equivalent browser evidence at<br>  representative desktop and narrow widths.<br>- [x] Confirm that the prototype requires no new STM32 instrumentation.<br><br>Exit criteria:<br><br>- The three panels have an explicit field\/role map and an agreed implementation<br>  shape before presentation code is generalized.<br><br>## Slice 28.10 Shared Panel Grammar And Components<br><br>- [x] Implement the smallest reusable HTML\/JavaScript helpers needed for the<br>  header, input\/decision\/output stages, dense metrics, condition strip, and<br>  diagnostics drawer.<br>- [x] Implement shared CSS tokens and responsive behavior without duplicating<br>  panel-specific styling.<br>- [x] Preserve semantic labels, keyboard access, tooltips, and colour-independent<br>  status meaning.<br>- [x] Add component-level browser assertions where practical.<br><br>Exit criteria:<br><br>- The primitives can express all three prototypes without hard-coding one<br>  panel's field names or hiding unavailable\/error states.<br><br>## Slice 28.15 Torque State And Sampled Stream Split<br><br>- [x] Replace the overloaded `torque` on-change stream with distinct<br>  `torque.state` and `torque.sampled` streams while retaining the existing<br>  structured Torque snapshot for explicit terminal\/API reads.<br>- [x] Keep service\/monitor modes, decisions, reasons, effective limits, regen<br>  gates, and published-intent availability in the on-change state stream;<br>  carry continuously changing counters and timestamps with the sampled values.<br>- [x] Publish raw, monitor-checked, monitor-accepted, and published torque<br>  values through a bounded latest-value sampled stream at 20 Hz by default.<br>- [x] Merge both compact topics into one browser torque model so Dashboard,<br>  Services, Signals, Plot\/Gauge, and terminal stream diagnostics retain their<br>  current field names and distinguish accepted request from published intent.<br>- [x] Raise the per-browser subscription capacity from 12 to 16 so both torque<br>  topics remain part of the normal profile without displacing diagnostics or<br>  other device streams.<br>- [x] Verify initial subscription hydration, independent state\/sample<br>  freshness, bounded UART\/WebSocket traffic, and recovery after both firmware<br>  endpoints restart.<br>- [x] Bench-verify live Forward pedal movement through `torque.sampled` on both<br>  shared signed graphs.<br>- [x] Record the forward rule that newly integrated devices with both<br>  low-rate state and continuously changing measurements receive separate<br>  `.state` and `.sampled` streams at design time; do not retrofit a combined<br>  stream after browser surfaces depend on it.<br><br>Exit criteria:<br><br>- Live Forward bench torque movement reaches both shared signed graphs through<br>  `torque.sampled`, state\/reason changes remain event-driven through<br>  `torque.state`, and neither stream is mistaken for inverter-applied torque.<br><br>Bench evidence, 2026-07-13:<br><br>- STM32 and ESP32 production firmware flashed with operator catalogue<br>  generation 41; browser assets synchronized over STA.<br>- `torque.state` hydrated once and remained `current` after 45 seconds quiet;<br>  `torque.sampled` was accepted at 50 ms and delivered all 11 numeric fields.<br>- Sixteen STM32 subscriptions were active with zero publisher drops,<br>  coalescing, malformed requests, sampled sequence gaps, or malformed torque<br>  updates after restart.<br>- In live Run\/HV-ready\/Forward operation the terminal snapshot and sampled<br>  cache agreed at 418 deci-Nm accepted with a 21 ms cache age. The remaining<br>  graph issue was traced to the browser startup list omitting both torque<br>  topics; automatic `torque.state` and `torque.sampled` subscription is now<br>  covered by the browser regression test and deployed with a new asset version.<br>- Operator verification confirmed live torque movement on both the Dashboard<br>  and Services graphs after the automatic-subscription fix.<br>- Host tests: 35\/35 passed. Local browser tests: 9\/9 passed. Production STM32<br>  and ESP32 builds passed; the live Services page loaded without application<br>  asset failures or page errors (the existing optional `\/favicon.ico` request<br>  remains absent).<br><br>## Slice 28.20 Three-Panel Prototype<br><br>- [x] Convert Torque \/ Drive Permissions so raw\/requested torque and permission<br>  inputs, service\/monitor decisions, effective limits, published intent,<br>  compile\/runtime regen gates, and inhibit\/fault reasons have distinct roles.<br>- [x] Convert Drive Selector so request, acceptance decision, accepted mode and<br>  direction, response\/counter evidence, freshness, and decline\/stale reasons<br>  read as one input-to-output flow.<br>- [x] Convert Traction DCDC so measured inputs, requested\/actual operating state,<br>  commands\/feedback, high\/low-side values, warnings\/faults, freshness, and raw<br>  diagnostics are grouped by meaning rather than rendered as equal chips.<br>- [x] Preserve initial hydration and granular live update behavior on all three<br>  panels.<br>- [x] Record bugs found during the conversion and fix only those inside the<br>  touched presentation\/data-binding path unless a wider fix is clearly small<br>  and safe.<br><br>Exit criteria:<br><br>- The three panels use the shared grammar, remain live, and show all previously<br>  useful facts either in the main hierarchy or the diagnostics drawer.<br><br>## Slice 28.30 Browser, Bench, And Responsive Evidence<br><br>- [x] Run local web tests and update stable DOM\/semantic assertions for the<br>  prototype panels.<br>- [x] Build the ESP32 web\/firmware target as required by the touched files.<br>- [x] Sync or flash the board and run the existing web probe against the live<br>  surface.<br>- [x] Verify hydrate then live-update behavior for the three panels without<br>  full-page refreshes or multi-second action lag.<br>- [x] Inspect desktop and narrow\/mobile layouts for clipping, unstable height,<br>  excessive empty space, and unreadable density.<br>- [x] Confirm faults, inhibits, invalid\/stale state, and no-feed\/unavailable<br>  cases remain conspicuous and text-labelled.<br>- [x] Check WebSocket\/link\/CPU diagnostics for an obvious regression caused by<br>  presentation changes.<br><br>Exit criteria:<br><br>- Local and live-board evidence shows the prototypes are readable, responsive,<br>  semantically correct, and do not regress the existing dataflow.<br><br>## Slice 28.40 Remaining Panel Migration Audit<br><br>- [x] Audit the Services, Devices, and Drivers browser pages for remaining<br>  chip-based panels that could use the adopted information grammar.<br>- [x] Produce one explicit inventory entry per candidate panel, including its<br>  page and displayed title, render function\/component, hydrate source, live<br>  topic or cache path, and principal information roles currently shown.<br>- [x] Exclude the three adopted prototypes and distinguish true panel-grammar<br>  candidates from workflow forms, navigation\/status summaries, tables, logs,<br>  plots, and panels that already have a more suitable presentation.<br>- [x] Identify duplicate, obsolete, unavailable-only, or overlapping panels<br>  that should be removed or consolidated instead of mechanically migrated.<br>- [x] Note known data\/feed gaps separately from presentation work so a panel<br>  migration does not invent facts or silently expand into device integration.<br>- [x] Estimate the final candidate count (currently expected to be roughly 16),<br>  group the list by browser page, and propose a sensible review order.<br>- [x] Review the inventory with the operator and agree the exact bounded list<br>  of panels to address.<br>- [x] After that agreement, add one numbered Phase 28 slice per accepted panel;<br>  do not create or begin those implementation slices during this audit.<br><br>Exit criteria:<br><br>- The remaining web surface has been audited, each migration candidate is<br>  named and scoped, exclusions and consolidations are explicit, and the<br>  operator has agreed the list from which individual panel slices will be<br>  created.<br><br>### Audit Result<br><br>The page DOM contains exactly 16 remaining surfaces after excluding the three<br>adopted prototypes: Services -> Torque Service, Devices -> Drive Selector, and<br>Devices -> Traction DCDC. The common live panels hydrate from `\/api\/dashboard`<br>and merge the compact topics named below through `stm32Status()` or<br>`stm32Shunt()`. Configuration and UART summaries instead use<br>`\/api\/stm32\/link`; Dashboard Stream is browser\/WebSocket client state rather<br>than an STM32 domain model.<br><br>| Page | Panel | Current renderer and data path | Principal roles | Proposed disposition |<br>| --- | --- | --- | --- | --- |<br>| Services | HVM | `stateRailHtml()` + `hvmDetailPanelHtml()`; dashboard hydrate; `vehicle`, `outputs`, `traction_dcdc.state`, `traction_dcdc.sampled`, and `shunt.sampled` | HVM state\/intent, output gate, sequence, safe-open, high\/low-side voltage | Migrate; strong input\/state\/output\/condition candidate. |<br>| Services | Fault Manager | `stateRailHtml()` + `faultPanelHtml()`; dashboard hydrate; `vehicle`, `permissions`, and `outputs` | clear\/active\/latched state, class, action\/owner, shunt validity | Migrate; keep active conditions visible and move ownership detail into diagnostics. |<br>| Services | Charging | charging rail + `unavailablePanelHtml()`; no implemented charging feed | intended state, path, enable, inhibit | Defer until charging integration publishes facts; an unavailable-only restyle adds little value. |<br>| Services | Configuration Service | `setKeyValueTable(\"configState\")`; `\/api\/stm32\/link` summary cache | cache health, schema\/generation, key\/storage counts, last error | Adapt to the grammar as an authority\/state\/condition summary, not a forced three-stage flow. |<br>| Services | Dashboard Stream | `renderDashboardStreamState()`; local WebSocket\/dashboard state and `liveSignalStats` | client mode, correlation, age, heap, sent\/drop or topic count | Adapt to compact transport status\/metrics\/diagnostics; it is not a vehicle input\/output flow. |<br>| Devices | Throttle | `throttlePanelHtml()`; dashboard hydrate; `throttle.sampled`, `analog_inputs.sampled`, and digital-input cache | channel\/fused input, validity\/freshness, released\/deadband state, fault reason | Migrate; retain the fused-position bar and expose both channels as measurements. |<br>| Devices | BMS | `bmsPanelHtml()`; dashboard hydrate; BMS facts currently carried by `digital_inputs.state` | source, freshness, discharge\/charge permissions, coverage | Migrate honestly as partial\/fake-source coverage; pack data and faults remain integration work. |<br>| Devices | Shunt | `shuntPanelHtml()`; dashboard hydrate; `shunt.sampled` | V1\/V2\/V3, current, power, completeness\/validity | Migrate; dense measurements plus freshness\/validity and raw diagnostics. |<br>| Devices | Inverter | `inverterCommandPanelHtml()`; dashboard hydrate; `torque.state`, `torque.sampled`, and `outputs` | VCU intent\/demand, monitor decision, inhibit intent\/applied, missing feedback | Migrate as a clearly partial VCU-to-inverter path; do not imply device feedback exists. |<br>| Devices | Chargers | `unavailablePanelHtml()`; no OBC\/DCFC feed | intended charger states and limits | Defer until charger integration publishes facts; keep the current honest placeholder meanwhile. |<br>| Devices | PDM | `pdmSelectorPanelHtml()`; dashboard hydrate; `digital_inputs.state` | selector-only source\/freshness, request\/acceptance, RX counters, coverage | Migrate as partial PDM coverage while keeping it distinct from the VCU Drive Selector decision panel. |<br>| Drivers | CAN | `setKeyValueTable(\"driverCanState\")`; dashboard CAN hydrate, no granular CAN live topic | interface\/IRQ state, RX\/TX, FIFO\/ring\/dispatch, decoder and selector-TX counters | Migrate; use state\/traffic\/health groups with counters in engineering details. |<br>| Drivers | GPIO | `analogInputPanelHtml()`; dashboard hydrate; `digital_inputs.state`, `digital_inputs.diagnostics`, `analog_inputs.sampled`, and `analog_inputs.diagnostics` | digital inputs, raw ADC measurements, capture\/DMA diagnostics | Migrate; retain analogue bars and separate live inputs from capture health. |<br>| Drivers | Output Drivers | output-gate rail + `unavailablePanelHtml()`; dashboard hydrate; `outputs` | runtime\/physical gate, feedback mismatch, request activity | Consolidate with Intent \/ Applied Feedback into one migrated Output Drivers panel. |<br>| Drivers | STM32 \/ ESP32 UART | `setKeyValueTable(\"driverUartState\")`; `\/api\/stm32\/link` | link state, RX\/TX frames and bytes, rejected frames, API error | Adapt to link-state\/traffic\/condition\/diagnostic groups rather than a three-stage device flow. |<br>| Drivers | Intent \/ Applied Feedback | `setCompactChips()`; dashboard hydrate; `outputs` | contactor and torque-inhibit intent\/applied pairs, safe-open | Merge into Output Drivers; it is the missing command\/feedback body of that panel, not a separate device. |<br><br>Known data gaps are deliberately outside these presentation slices: charging<br>service state, OBC\/DCFC state and limits, real BMS pack measurements\/faults,<br>inverter ready\/enable\/actual-torque\/fault feedback, wider PDM channels\/loads\/<br>faults, and output physical-application facts not yet published. The CAN panel<br>also lacks a granular live topic and currently follows dashboard hydration;<br>that does not block its visual migration and should not trigger new<br>instrumentation in this phase.<br><br>Recommended bounded implementation list: 13 slices covering HVM, Fault<br>Manager, Configuration Service, Dashboard Stream, Throttle, BMS, Shunt,<br>Inverter, PDM, CAN, GPIO, combined Output Drivers, and STM32 \/ ESP32 UART.<br>Defer the two unavailable-only Charging and Chargers surfaces, and fold Intent \/<br>Applied Feedback into Output Drivers. Suggested execution order is: (1) HVM,<br>Throttle, Shunt, CAN, and GPIO as the richest live-data examples; (2) Fault<br>Manager, combined Output Drivers, Inverter, BMS, and PDM as decision\/partial<br>device panels; then (3) Configuration Service, Dashboard Stream, and UART as<br>non-flow status adaptations.<br><br>Operator decision, 2026-07-13: accepted the recommended 13-slice list and its<br>two deferrals\/one consolidation. Services -> Charging and Devices -> Chargers<br>remain unchanged until their owning integrations publish real facts. Drivers<br>-> Intent \/ Applied Feedback will be removed as a separate surface when its<br>content is folded into Drivers -> Output Drivers.<br><br>### Migration Implementation Contract<br><br>This contract applies to every slice from 28.41 through 28.53:<br><br>- Panel-specific JavaScript may classify and map authoritative facts into the<br>  shared panel model, but must not reproduce layout scaffolding or responsive<br>  behavior.<br>- Use and extend the existing shared primitives (`informationPanelHtml()`,<br>  `informationStageHtml()`, metrics, condition strips, state rails, disclosure<br>  handling, analogue bars, and signed graphs) rather than creating parallel<br>  panel-local markup systems.<br>- Shared presentation behavior belongs in `core.js` and shared styling belongs<br>  in `styles.source.css`; generated bundle\/CSS files remain build outputs.<br>- Do not add a panel-named CSS selector for spacing, columns, chip sizing,<br>  colour, diagnostics, or breakpoints when the requirement can be expressed by<br>  an existing role or a small reusable modifier.<br>- If a panel exposes a genuinely new visual relationship, add the smallest<br>  reusable primitive or role\/modifier and demonstrate that it is not tied to<br>  one panel's field names.<br>- Keep per-panel renderers declarative and limited to labels, values, roles,<br>  states, source\/freshness meaning, diagnostics, and optional shared visuals.<br>- During each slice, check for duplicated markup\/style rules and remove retired<br>  panel helpers or selectors once their final consumer has migrated.<br>- Browser tests should assert the common semantic roles and responsive<br>  contract, with panel-specific assertions limited to data mapping and safety-<br>  meaningful labels\/states.<br><br>## Slice 28.41 Services HVM Panel<br><br>- [x] Recast HVM state, intent, output gate, sequence, and safe-open evidence<br>  into the shared meta\/state\/output\/condition hierarchy.<br>- [x] Present high- and low-side voltages as dense live measurements with<br>  source, freshness, and validity meaning preserved.<br>- [x] Move raw sequence, stream\/cache, shunt cross-check, and other engineering<br>  evidence into the persistent diagnostics drawer.<br>- [ ] Verify hydrate, live HVM transitions, unavailable\/stale rendering, and<br>  narrow-width layout without changing HVM authority or policy.<br><br>Exit criteria: HVM reads as one state-and-output flow, retains every useful<br>fact, and remains live and responsive at desktop and mobile widths.<br><br>Local evidence, 2026-07-13: Services HVM now maps facts declaratively into the<br>existing shared `informationPanelHtml()` primitives; no new CSS or layout<br>helper was added. Compact `vehicle`, `outputs`, `permissions`, and Traction<br>DCDC updates exercised the state, intent, gate, contactor intent\/applied,<br>fault\/blocker, and high\/low-voltage paths. The 390 px regression confirms the<br>shared flow stacks to one column without overflow. Local browser tests passed<br>9\/9 and the production LittleFS image build passed. Board hydrate\/transition\/<br>stale review remains before the slice is complete.<br><br>Initial board evidence: the cache-bundled asset was synchronized without a<br>firmware flash; all 4 applicable board tests passed with 5 fixture-only tests<br>skipped, and the live Services probe had no page errors or failed application<br>resources. Visual review in Vehicle Off\/HVM Open confirmed a clear condition,<br>fresh on-change source, valid shunt evidence, Open rail state, Not ready state,<br>armed output gate, safe-open output, and no HVM blockers. That review also<br>caught and fixed an initial semantic leak where the generic vehicle helper<br>treated torque inhibition as an HVM blocker. Live HVM transitions and a stale<br>source still require operator\/bench review.<br><br>A subsequent T15-off bench check exposed that the headline voltages were<br>sourced from cached Traction DCDC values. The HVM panel now preserves all four<br>distinct measurements in electrical-path order: battery pack from shunt V2,<br>low side from shunt V1, Traction DCDC low side, and Traction DCDC high side.<br>Each source carries its own availability presentation; inactive DCDC readings<br>are `n\/a` rather than stale values. Shunt V3 (the future low-side-negative<br>node) remains an engineering detail. Six live board samples at two-second<br>intervals showed both shunt-backed values changing with shunt freshness live.<br><br>## Slice 28.42 Devices Throttle Panel<br><br>- [x] Recast both throttle channels, fused demand, released\/deadband state,<br>  freshness, validity, and fault reason into the shared hierarchy.<br>- [x] Retain the responsive fused-position bar and show both normalized channel<br>  measurements without promoting raw ADC counts over operator facts.<br>- [x] Place raw ADC, timestamps, stream\/cache, and capture detail in engineering<br>  diagnostics while keeping disagreement\/invalid state conspicuous.<br>- [ ] Verify hydrate and live pedal movement plus released, stale, invalid, and<br>  narrow-width cases.<br><br>Exit criteria: channel plausibility and fused pedal demand are immediately<br>clear without losing the raw evidence needed for bench diagnosis.<br><br>Implementation evidence, 2026-07-13: the Devices Throttle panel now uses the<br>shared information-panel hierarchy and the shared analogue bar, extended only<br>with a reusable display-label option. Channel and fused demand are operator-<br>facing percentages; per-mille values, raw ADC counts, timestamps, stream<br>sources, and ADC capture counters remain in Engineering Details. Local stream<br>fixtures cover released\/deadband, disagreement\/invalid, stale, and narrow-<br>width rendering. All 10 local browser tests pass. A live T15-off board probe<br>confirmed fresh valid channels, active released deadband, a zero fused output,<br>live raw ADC\/capture diagnostics, and no browser errors. Live pedal travel and<br>physical invalid-channel review remain for bench verification.<br><br>## Slice 28.43 Devices Shunt Panel<br><br>- [x] Present V1\/V2\/V3, current, and power as aligned live measurements rather<br>  than equal categorical chips.<br>- [x] Make source, freshness, completeness, and validity visible in consistent<br>  meta\/condition positions.<br>- [x] Move raw values, timestamps, and stream\/cache evidence into engineering<br>  diagnostics.<br>- [x] Verify dashboard hydrate, `shunt.sampled` updates, stale\/unavailable<br>  behavior, signed values, units, and narrow layout.<br><br>Exit criteria: the shunt panel is measurement-dense, semantically honest, and<br>updates cleanly from its existing sampled stream.<br><br>Completion evidence, 2026-07-13: the Devices Shunt panel now uses the shared<br>information-panel hierarchy with aligned Voltage and Electrical Flow stages.<br>Condition, freshness, validity, source, completeness, and sample currency use<br>the common semantic positions; raw units and stream\/cache counters are in<br>Engineering Details. Local fixtures cover unavailable hydrate, signed values,<br>invalid\/incomplete data, the six-second stale transition, and 390 px layout.<br>All 11 local browser tests pass. After runtime asset sync, six live board<br>samples showed V1\/V2\/V3\/current updating from the push stream with valid,<br>complete, current state, signed values, no failed resources, and no page<br>errors.<br><br>## Slice 28.44 Drivers CAN Panel<br><br>- [x] Group interface\/initialization state, traffic, queue\/ring health, dispatch,<br>  decoder, and selector-response evidence by role.<br>- [x] Keep bus-off\/error\/overflow\/failure conditions conspicuous and move bulk<br>  counters and high-water details into engineering diagnostics.<br>- [x] Preserve the current dashboard hydrate path without adding a granular CAN<br>  stream or implying more live cadence than exists.<br>- [x] Verify available\/unavailable states, counter updates, and responsive<br>  density using local and board evidence.<br><br>Exit criteria: CAN health and traffic can be scanned quickly while detailed<br>driver counters remain reachable and accurately sourced.<br><br>Completion evidence, 2026-07-13: the Drivers CAN panel now uses the shared<br>information-panel hierarchy with Interface, Traffic, and Routing roles.<br>Present interface readiness is kept distinct from cumulative TX\/FIFO\/overflow<br>failure history, while queue, ring, dispatch, decoder, and selector-response<br>counters remain available under Engineering Details. Missing selector-response<br>counters are shown as `n\/a` rather than healthy zeroes, and the source is<br>explicitly identified as a dashboard snapshot rather than a granular CAN<br>stream. The full 13-test local browser suite passes with focused available and<br>unavailable CAN coverage, including the 390 px layout check. After runtime<br>asset sync, six board samples showed focused-dashboard<br>rehydration from a stale 2617-second snapshot to 40 ms and subsequent RX, TX,<br>dispatch, and shunt-decoder counter movement, with no failed resources or page<br>errors.<br><br>Cadence refinement, 2026-07-14: all page fallback rows now request one update<br>per second, while the Balanced profile requests 200 ms for its faster live<br>topics, including `torque.sampled`. Page navigation explicitly subscribes the<br>matching Dashboard, Services, Devices, Drivers, Faults, or System fallback row.<br>View leases now express demand only and no longer introduce a hidden producer<br>period; STM32 sampled and snapshot periods are derived from accepted web<br>subscriptions. The live profile smoke confirmed 200 ms accepted periods for<br>digital, analogue, throttle, torque, shunt, and Traction DCDC sampled producers,<br>with Traction DCDC state correctly remaining edge-driven at period zero.<br><br>## Slice 28.45 Drivers GPIO Panel<br><br>- [x] Separate digital input states, analogue measurements, and capture\/driver<br>  health using the shared panel roles.<br>- [x] Retain responsive ADC bars and clearly distinguish active, inactive,<br>  invalid, stale, and unavailable inputs.<br>- [x] Move DMA\/capture sequence, queue, overflow, cadence, timestamp, and stream<br>  evidence into engineering diagnostics.<br>- [x] Verify hydrate and all four existing digital\/analogue topics at desktop<br>  and narrow widths.<br><br>Exit criteria: physical I\/O state is dense and live, while capture health is<br>available without competing with the primary inputs.<br><br>Completion evidence, 2026-07-13: the Drivers GPIO panel now uses shared Digital<br>Inputs, Analogue Inputs, and Capture Health roles. T15, start, brake, brake<br>validity, and both ADC bars retain their push updates; capture overflow,<br>cadence, queue, and DMA errors are conditions, while sequences, timestamps,<br>raw counts, and all four stream sources are in Engineering Details. The shared<br>stage primitive now supports reusable embedded visuals rather than a GPIO-only<br>layout. Focused live-topic and 390 px tests pass. Board sampling showed digital<br>state and analogue samples arriving by push, ADC counts and DMA sequence<br>advancing, and unavailable on-change diagnostic facts rendered grey rather<br>than as healthy zeroes, with no failed resources or page errors.<br><br>Review refinement, 2026-07-14: the two throttle ADC bars now sit directly below<br>the panel metadata, matching the Throttle and Torque treatment, and a grey<br>Brake Pressure bar reserves the future analogue input without implying a live<br>source. The primary Capture Health stage was removed: ADC configuration and<br>capture-ring occupancy are engineering facts excluded from the compact<br>30-record physical-input stream, so showing two `n\/a` chips was misleading.<br>Unavailable capture conditions are likewise suppressed; available DMA\/capture<br>faults remain visible and all lower-level fields remain in Engineering details.<br><br>## Slice 28.46 Services Fault Manager Panel<br><br>- [x] Recast clear\/active\/latched state, fault class, action\/owner, permissions,<br>  and relevant validity evidence into the shared hierarchy.<br>- [x] Keep active\/latched faults, vetoes, and unsafe or invalid evidence visible<br>  outside engineering details.<br>- [x] Move ownership\/source, cache\/stream, timestamps, and secondary diagnostic<br>  evidence into the drawer without duplicating the dedicated Faults page.<br>- [x] Verify clear, active\/latched, stale\/unavailable, and responsive cases from<br>  existing facts only.<br><br>Exit criteria: the service panel communicates the current fault-manager outcome<br>and action immediately, with deeper evidence still available elsewhere.<br><br>Completion evidence, 2026-07-13: Fault Manager now uses the shared meta, state<br>rail, Fault State, Response, Permissions, Conditions, and diagnostics grammar.<br>Faults, vetoed permissions, and invalid source evidence remain visible; action<br>ownership and stream\/source detail are in Engineering Details with a pointer to<br>the dedicated Faults page. Local tests exercise clear and active\/latched states,<br>permission vetoes, live updates, and 390 px layout. Board inspection confirmed<br>the clear state, torque veto, live permission source, valid throttle evidence,<br>and honest grey rendering for unpublished action, clear-gate, and shunt facts,<br>with no failed resources or page errors.<br><br>Review refinement, 2026-07-14: the redundant Clear\/Active\/Latched state rail was<br>removed. Fault State remains the single primary presentation of active and<br>latched state, while clear permission stays in Engineering details.<br><br>## Slice 28.47 Drivers Output Drivers Panel<br><br>- [x] Merge Intent \/ Applied Feedback into Output Drivers and remove the<br>  redundant standalone panel from the Drivers page.<br>- [x] Show contactor and torque-inhibit intent\/applied pairs as explicit command<br>  flows alongside physical\/runtime gate state and safe-open evidence.<br>- [x] Make feedback mismatch and unavailable physical-application facts honest<br>  conditions; place request\/cache\/stream detail in engineering diagnostics.<br>- [x] Verify live `outputs` updates, mismatch\/unavailable rendering, no lost<br>  fields, page order, and narrow layout.<br><br>Exit criteria: one Output Drivers panel owns gate, command, applied-feedback,<br>and mismatch evidence with no duplicate Drivers surface.<br><br>Completion evidence, 2026-07-13: Output Drivers now owns the runtime gate,<br>safe-open state, physical-application evidence, POS\/PRE\/NEG\/torque-inhibit<br>command intents, applied feedback, mismatch, and structured-request state. The<br>standalone Intent \/ Applied Feedback article has been removed. Focused tests<br>exercise live `outputs` updates, armed state, intent\/applied pairs, unavailable<br>physical facts, an injected mismatch, page order, and narrow layout. Board<br>inspection confirmed the four-panel Drivers layout, armed rail, safe-open and<br>physical evidence, honest unavailable intent\/applied facts in the current<br>bench state, and no page exceptions; the browser recovered from one transient<br>WebSocket reset immediately after runtime asset sync.<br><br>## Slice 28.48 Devices Inverter Panel<br><br>- [x] Recast VCU torque intent, monitor-accepted demand, inhibit intent\/applied,<br>  and missing inverter feedback as a clearly labelled partial command path.<br>- [x] Reuse the signed torque visualization where useful without presenting<br>  monitor or VCU intent as inverter-reported torque.<br>- [x] Keep missing ready\/enable\/actual-torque\/fault feedback explicit and defer<br>  those facts to inverter integration work.<br>- [x] Verify hydrate, `torque.state`, `torque.sampled`, `outputs`, unavailable<br>  feedback, and mobile behavior.<br><br>Exit criteria: the panel clearly distinguishes what the VCU knows or commands<br>from inverter facts that do not yet exist.<br><br>Completion evidence, 2026-07-13: the Inverter panel now presents the VCU raw<br>request, Torque Monitor decision and accepted demand, published intent, and<br>inhibit intent\/applied state as a partial command path. The shared signed<br>torque graph is explicitly labelled Monitor Accepted; inverter ready, enabled,<br>actual torque, and fault feedback remain grey `not published` conditions.<br>Focused stream tests cover `torque.state`, `torque.sampled`, and `outputs`, plus<br>missing feedback and narrow layout. Live board inspection in the safe bench<br>state confirmed honest unavailable feedback and no page errors; it also caught<br>and corrected cramped metric-label wrapping at the normal two-panel width.<br><br>Review refinement, 2026-07-14: the legacy Diagnostics appendix was removed so<br>the panel has one disclosure, consistently named Engineering details.<br><br>## Slice 28.49 Devices BMS Panel<br><br>- [x] Recast source, freshness, discharge\/charge permissions, and coverage into<br>  the shared hierarchy.<br>- [x] Make compile-time fake versus real-device source unmistakable and retain<br>  stale\/unavailable permission semantics.<br>- [x] Keep absent pack measurements, limits, and BMS faults explicit without<br>  inventing placeholders that appear healthy.<br>- [x] Verify current hydrate\/live behavior and narrow layout using the existing<br>  BMS facts only.<br><br>Exit criteria: the partial BMS surface is useful and honest about its source,<br>permissions-only coverage, and missing integration data.<br><br>Completion evidence, 2026-07-13: BMS now uses the shared hierarchy with an<br>amber synthetic\/compile-time-fake source, explicit permissions-only coverage,<br>and a dedicated permission stage. Pack measurements, power limits, and BMS<br>faults remain grey `not published` conditions. Local stream tests cover fresh<br>fake facts, discharge allowed\/charge blocked, stale permission suppression,<br>and narrow layout. Live board inspection confirmed the synthetic source and<br>fresh permission facts without page errors or layout overflow.<br><br>## Slice 28.50 Devices PDM Panel<br><br>- [x] Recast selector-source freshness, request\/acceptance, CAN source, counters,<br>  and selector-only coverage into the shared grammar.<br>- [x] Keep the PDM device\/transport perspective distinct from the adopted Drive<br>  Selector panel's VCU decision perspective.<br>- [x] Move raw request, sequence\/counters, timestamps, and stream\/cache evidence<br>  into engineering diagnostics and state wider PDM gaps explicitly.<br>- [x] Verify hydrate, live selector changes, stale source, unavailable data, and<br>  responsive layout.<br><br>Exit criteria: the PDM panel adds device\/source evidence without duplicating or<br>contradicting Drive Selector decisions.<br><br>Completion evidence, 2026-07-13: PDM now presents the `0x210` receive source,<br>freshness, requested mode, raw value, VCU receipt state, and receive counters<br>as a selector-link view. The authoritative VCU decision remains in Drive<br>Selector; wider PDM channels, loads, and faults are grey `not published`<br>conditions. Local fixtures cover live accepted input, stale suppression,<br>unavailable wider facts, and narrow layout. Live board inspection confirmed<br>the selector facts and lifetime counters; an initial counter-label wrap was<br>corrected by using the shared one-chip-wide stage treatment.<br><br>Review refinement, 2026-07-14: the legacy Diagnostics appendix was removed so<br>the panel has one disclosure, consistently named Engineering details.<br><br>## Slice 28.51 Services Configuration Service Panel<br><br>- [x] Adapt cache health, authority, schema\/generation, record\/storage counts,<br>  and last error to the shared meta\/metric\/condition grammar.<br>- [x] Do not force the non-flow summary into artificial input\/state\/output<br>  stages or duplicate the full Configuration workflow page.<br>- [x] Put lower-level cache\/source and endpoint evidence in engineering details<br>  while preserving `\/api\/stm32\/link` semantics.<br>- [x] Verify initial hydrate, post-action refresh, error\/unavailable state, and<br>  responsive layout without reintroducing configuration UI lag.<br><br>Exit criteria: the compact service summary is consistent with Phase 28 while<br>remaining distinct from the configuration editing workflow.<br><br>Completion evidence, 2026-07-13: Configuration Service now uses shared meta,<br>metric, condition, and diagnostic primitives without an artificial flow. It<br>shows STM32 authority, cache\/summary health, schema, generation, record counts,<br>stored bytes, lifetime validation failures, and last error; the full action<br>workflow remains on Configuration. A reusable `informationMetricsHtml()`<br>helper removes panel-specific metric markup. Local tests cover valid hydrate,<br>absent-cache\/error state, Services refresh behavior, and narrow layout. Live<br>board inspection confirmed schema 1.4, generation 22, 39 cached records, no<br>validation failures, and no page errors.<br><br>## Slice 28.52 Services Dashboard Stream Panel<br><br>- [x] Adapt browser stream mode, age\/freshness, connection state, and aggregate<br>  delivery health to shared meta\/condition presentation.<br>- [x] Render heap, sent\/drop, correlation, and topic counts as dense metrics or<br>  engineering diagnostics rather than equal state chips.<br>- [x] Preserve the distinction between browser\/WebSocket client state and<br>  STM32 vehicle\/device state.<br>- [x] Verify reconnect, idle\/live\/stale, hydrate, update, and narrow-width cases<br>  without changing subscription behavior.<br><br>Exit criteria: stream health is compact and legible without masquerading as a<br>vehicle input\/output panel.<br><br>Completion evidence, 2026-07-13: Dashboard Stream now reports browser-client<br>condition, WebSocket connection, split-topic versus aggregate mode, freshness,<br>received topics, server sent\/drop counters, and free heap without using a<br>vehicle flow. Correlation, client ID, last message, active subscriptions, and<br>the browser\/STM32 boundary live in Engineering Details. Local tests exercise<br>idle hydrate, topic update, disconnect\/reconnect, server counters, and narrow<br>layout. Live board inspection showed connected split-topic delivery, fresh<br>updates, five browser topic messages, server lifetime sent\/drop counters, and<br>no page errors.<br><br>Review refinement, 2026-07-14: Last Result is no longer a chatty primary<br>condition and remains available in Engineering details. Engineering details<br>uses the shared single-column metric layout for more reliable panel-width and<br>mobile rendering.<br><br>## Slice 28.53 Drivers STM32 \/ ESP32 UART Panel<br><br>- [x] Adapt link state, RX\/TX traffic, rejected frames, bytes, and API error to<br>  link-state\/metric\/condition\/diagnostic groups.<br>- [x] Preserve `\/api\/stm32\/link` source and cadence, and do not conflate UART<br>  transport health with structured API or vehicle-state health.<br>- [x] Keep errors\/rejections conspicuous and move secondary counters\/source<br>  evidence into engineering details.<br>- [x] Verify connected, quiet, error\/unavailable, updating, and responsive cases.<br><br>Exit criteria: UART transport status can be scanned quickly and retains the<br>engineering evidence needed to diagnose the STM32\/ESP32 link.<br><br>Completion evidence, 2026-07-13: STM32 \/ ESP32 UART now separates UART enable<br>and handshake state, frame\/byte traffic, and structured-API summary\/catalogue\/<br>request state. Rejected frames and API error remain conspicuous conditions;<br>reload and source evidence move to Engineering Details. Local tests cover<br>active traffic, quiet link, rejected\/API-error state, unavailable link, and<br>narrow layout. Live board inspection confirmed active traffic, `wait` link<br>state, a not-fresh handshake, valid generation-41 API catalogue, current<br>lifetime rejected count, and no page errors. Those transport counters are<br>presented without inferring vehicle health.<br><br>## Slice 28.60 Vehicle Page Information Architecture<br><br>- [x] Make the Vehicle page a deliberate single-column operational narrative<br>  rather than allowing its horizontally rich panels to pair unpredictably.<br>- [x] Order the page as Vehicle FSM, Inhibitions \/ Readiness, and Powertrain.<br>- [x] Remove the standalone Freshness panel: retain domain freshness\/source in<br>  each migrated panel and leave HTTP, WebSocket, catalogue, cache, and endpoint<br>  diagnostics on their owning system\/link surfaces.<br>- [x] Add stable desktop and narrow-width ordering\/no-overflow assertions.<br><br>Exit criteria: the Vehicle page has one clear top-to-bottom hierarchy, no<br>standalone transport-diagnostics panel, and no loss of relevant per-domain<br>freshness evidence.<br><br>Completion evidence, 2026-07-14: the Vehicle section now uses the shared<br>single-column view class and contains only Vehicle FSM, Inhibitions \/ Readiness,<br>and Powertrain in that order. The obsolete transport\/cache freshness renderer<br>and DOM surface were removed; each remaining domain panel retains its own<br>source\/freshness contract. The ESP32 web filesystem build passed and the<br>focused local layout\/order regression passed.<br><br>## Slice 28.61 Vehicle FSM Panel<br><br>- [x] Recast the Vehicle FSM using the shared information grammar and retain<br>  the genuine Off -> Ignition -> Precharge -> Run -> Shutdown -> Fault -><br>  Charge state rail.<br>- [x] Separate input\/request facts, the current FSM decision\/state, and its<br>  high-level HVM\/permission outputs without implying ownership of HVM internals.<br>- [x] Keep transition blockers and faults visible in Conditions; move sequence,<br>  timestamps, stream source, and raw reasons into Engineering details.<br>- [x] Verify hydrate\/live state transitions, unavailable\/fault\/inhibited cases,<br>  and narrow layout without adding STM32 instrumentation.<br><br>Exit criteria: the panel answers what whole-vehicle mode is active, what the<br>FSM is trying to do, and what prevents its next transition while respecting<br>the documented Vehicle-FSM\/HVM boundary.<br><br>Completion evidence, 2026-07-14: Vehicle FSM now uses shared metadata, the<br>genuine vehicle state rail, request\/guard, FSM decision, and service-intent<br>stages, visible blocker\/fault conditions, and one Engineering details drawer.<br>The panel labels HVM state as a coarse service result and explicitly records<br>that HVM owns HV sequencing. Focused tests cover active and fault\/inhibited<br>permission updates, the active Run rail, the three roles, and narrow stacking.<br><br>## Slice 28.62 Vehicle Inhibitions And Readiness Panel<br><br>- [x] Recast readiness evidence, decision\/blockers, and resulting precharge,<br>  contactor, motoring, and regen permissions into the shared hierarchy.<br>- [x] Distinguish source facts such as HVM readiness, BMS discharge permission,<br>  shunt validity, selector acceptance, and fault state from the permissions<br>  derived from them.<br>- [x] Keep the primary active blocker visible and collapse the healthy state to<br>  a concise `Blockers: none`; retain raw reason\/source evidence in Engineering<br>  details.<br>- [x] Verify permission-topic updates, initial hydrate, unavailable\/stale\/fault<br>  states, and responsive layout without duplicating the Faults or Torque pages.<br><br>Exit criteria: the panel explains why the vehicle may or may not proceed and<br>shows the resulting permissions without presenting every fact as a peer chip.<br><br>Completion evidence, 2026-07-14: Inhibitions \/ Readiness now separates HVM,<br>BMS, shunt, and selector source facts from the readiness decision and resulting<br>precharge, contactor, motoring, and regen permissions. Active blockers and<br>evidence problems remain visible; the clear case reduces to `Blockers: none`.<br>Focused live permission fixtures cover fault\/inhibited and recovered-ready<br>states, and the shared mobile flow stacks without overflow.<br><br>## Slice 28.63 Vehicle Powertrain Synoptic<br><br>- [x] Replace the duplicated Torque-Service-plus-DCDC chip wall with a<br>  full-width two-lane powertrain summary: electrical energy flow and drive<br>  demand flow.<br>- [x] Show the energy path as Battery Pack -> Low-Side Bus -> Traction DCDC -><br>  High-Side Bus -> Inverter, using only authoritative available measurements<br>  and explicit unavailable inverter feedback.<br>- [x] Show the drive path as accepted direction -> fused throttle -> requested\/<br>  accepted torque -> published inverter intent, reusing the responsive signed<br>  torque graph where it helps rather than duplicating the full Torque Service.<br>- [x] Implement only small reusable flow-lane\/node helpers and generic CSS;<br>  avoid panel-specific layout markup or new subscriptions outside the existing<br>  link-subscription model.<br>- [x] Keep faults, vetoes, stale\/invalid evidence visible and move detailed<br>  DCDC flags, monitor reasons, timestamps, and stream evidence to Engineering<br>  details or their owning Services\/Devices panels.<br>- [x] Verify initial hydrate, granular live updates, honest partial\/unavailable<br>  states, desktop flow, and vertically stacked mobile behavior.<br><br>Exit criteria: Powertrain provides a compact vehicle-level energy-and-demand<br>story without duplicating the dedicated HVM, Torque Service, Traction DCDC, or<br>Inverter panels and without inferring missing feedback.<br><br>Completion evidence, 2026-07-14: Powertrain is now a full-width two-lane<br>synoptic built from shared flow-lane and flow-node helpers plus generic CSS.<br>The energy lane distinguishes shunt Battery Pack and Low-Side Bus evidence,<br>Traction DCDC low\/high-side measurements, and explicitly unavailable inverter<br>feedback. The demand lane carries accepted direction through fused throttle,<br>raw and monitor-accepted torque, and VCU-published inverter intent; the shared<br>signed torque graph remains the primary analogue summary. Dedicated service<br>detail is not duplicated, and the obsolete one-off Torque\/DCDC vehicle<br>renderers were removed. The ESP32 filesystem build and all 21 local web tests<br>passed; focused fixtures cover DCDC and torque live updates plus narrow stacked<br>layout. The image was uploaded to the board and all four applicable board smoke<br>tests passed. Follow-up board verification advanced both web asset<br>cache-busters to `v=228-phase28-vehicle-panels`; the served bundle contains the<br>new FSM, readiness, and Powertrain renderers and no longer contains the<br>obsolete Vehicle Torque\/DCDC chip-wall renderer.<br><br>## Slice 28.64 Vehicle Powertrain Energy-Flow Refinement<br><br>- [x] Stack metric labels above their values inside Electrical Energy nodes at<br>  mobile widths while preserving the compact horizontal desktop presentation.<br>- [x] Add the HVM\/precharge stage between Battery Pack and Low-Side Bus using<br>  existing HVM state, intent, readiness, sequence, and contactor intent\/applied<br>  facts.<br>- [x] Enrich the Traction DCDC flow node with its published command, high-side<br>  target, and fault evidence without duplicating the full Devices panel.<br>- [x] Trace low\/high-side DCDC voltage and current through the same sampled<br>  stream path and capture direct live-topic evidence for their actual cadence.<br>- [x] Rebuild, upload, and verify desktop\/mobile behavior on the board.<br><br>Exit criteria: the electrical flow remains compact on desktop, is legible on<br>mobile, includes the HVM\/precharge transition, and does not imply that stable<br>source values are stale UI values.<br><br>Completion evidence, 2026-07-14: Electrical Energy metrics retain their<br>horizontal desktop rows and stack label above value below 760 px. The flow now<br>includes HVM \/ Precharge between Battery Pack and Low-Side Bus with HVM state,<br>intent, readiness, and POS\/PRE\/NEG contactor evidence; the Traction DCDC node<br>adds command result, high-side target, and fault state. A direct 20-message<br>`traction_dcdc.sampled` capture showed complete payloads every 200 ms: both<br>voltages were present in every message and remained exactly 218 mV \/ 375 mV<br>while currents changed, confirming source stability rather than stale browser<br>plumbing. The filesystem build and all 21 local web tests passed, the<br>cache-busted `v=229-phase28-powertrain-energy` image was uploaded, and all four<br>applicable board smoke tests passed.<br><br>## Slice 28.65 Dashboard Vehicle Overview<br><br>- [x] Recast Dashboard as an operator overview: concise Vehicle, Readiness,<br>  HVM, Faults, and Data summary followed by gauges and primary dynamic state.<br>- [x] Add shared responsive Speed, Motor RPM, and Pack Temperature gauges with<br>  full scales of 130 mph, 24,000 rpm, and 50 degrees C respectively.<br>- [x] Keep all three gauges explicitly grey\/unavailable until authoritative<br>  feeds exist, while providing a clearly labelled demonstration sweep between<br>  approximately 25% and 75% of full scale.<br>- [x] Lay out Direction, Torque, and Contactors at equal desktop widths; retain<br>  F\/N\/R and fold T15, Start, and Brake into Direction as secondary context.<br>- [x] Remove the low-value Domains panel and the duplicated\/low-level Vehicle<br>  Status card wall; do not move their engineering detail into the overview.<br>- [x] Stack gauges and dynamic panels cleanly on mobile, preserving honest<br>  unavailable styling, readable units, and no horizontal overflow.<br>- [x] Rebuild, upload, and verify hydrate\/live dashboard behavior on desktop<br>  and mobile without adding STM32 instrumentation or proxy telemetry.<br><br>Exit criteria: Dashboard answers vehicle mode, readiness, key faults, driver<br>direction, torque, contactor state, and future primary measurements at a glance<br>without duplicating the Vehicle, Services, Devices, or system-health pages.<br><br>Completion evidence: the Dashboard now presents five compact summary facts,<br>followed by a responsive three-gauge primary-measurement row and an equal-width<br>Direction \/ Torque \/ Contactors row. Speed, Motor RPM, and Pack Temperature are<br>truthfully marked unavailable while their grey demonstration needles sweep from<br>approximately 25% to 75% of 130 mph, 24,000 rpm, and 50 \u00b0C full scales. Driver<br>inputs now sit inside Direction; Domains and the former Vehicle Status card wall<br>have been removed. The cache-busted `v=230-phase28-dashboard-overview` LittleFS<br>image built and uploaded successfully. All 22 local web tests passed, and all<br>five board-applicable tests passed against `http:\/\/10.0.1.127`, including the<br>desktop\/mobile dashboard layout, animation, unavailable-state, and overflow<br>checks.<br><br>## Slice 28.70 CAN1 Transmit Backpressure And Completion Hardening<br><br>The CAN1 path already has a 16-entry software request ring in front of the<br>STM32 bxCAN controller's three hardware transmit mailboxes. It currently<br>removes a request from that ring before establishing mailbox availability and<br>then performs immediate, no-time-passes retry calls. A temporarily full set of<br>hardware mailboxes can therefore turn normal backpressure into a permanent<br>request failure. The published `tx_accepted` value also means only \"admitted to<br>a hardware mailbox\", not confirmed on-bus completion.<br><br>Bench investigation on 2026-07-14 found identical lifetime totals of 33,225<br>for low-level CAN1 TX admission failures and selector-response TX failures, but<br>no new failures across matched T15-off, T15-on, high-side-ready, or<br>Forward\/throttle windows. The powered BDC668 increased receive traffic by<br>roughly eight times without reproducing the failure. This slice therefore<br>hardens the structural weakness and corrects the diagnostics without treating<br>the historical cumulative total as a currently active fault.<br><br>- [x] Preserve the oldest queued CAN1 TX request while all three hardware<br>  mailboxes are occupied; dequeue it only after mailbox admission or explicit<br>  deadline expiry.<br>- [x] Replace immediate busy-loop `attempts` with deferred service-pass retry<br>  semantics, retaining the 1 ms CAN task cadence and bounded per-pass work.<br>- [x] Keep the shared software queue bounded and deterministic under mixed<br>  selector-response and three-frame Traction DCDC bursts; document its usable<br>  capacity and define overflow, fairness, and deadline behaviour.<br>- [x] Track the owner and correlation of each in-flight hardware mailbox and<br>  distinguish queue admission, mailbox deferral\/admission, deadline expiry,<br>  successful bus completion, arbitration loss, and transmit error.<br>- [x] Return owner results to Drive Selector and Traction DCDC only with the<br>  correct semantics; do not report mailbox admission as confirmed bus<br>  transmission.<br>- [x] Publish bounded CAN TX queue depth\/high-water, deferred, expired,<br>  completed, and failed evidence through the existing CAN operator view and<br>  terminal command, without adding polling outside the structured API.<br>- [x] Update Drivers -> CAN so current conditions use active\/recent failure or<br>  counter-delta evidence. Keep lifetime totals in Engineering Details and use<br>  precise labels such as mailbox admission, deadline expiry, and bus TX error.<br>- [x] Add host coverage for all-mailboxes-busy then recovery, deadline expiry,<br>  queue overflow, DCDC burst plus selector response, result ownership, and<br>  transmit-completion\/error handling.<br>- [ ] From a safe reset bench state, verify T15 off, T15 on, high-side ready,<br>  Forward selection, and throttle exercise with `0x210` requests and `0x220`<br>  responses visible, no queue\/ring overflow, and no false current-fault display<br>  from historical counters.<br><br>Exit criteria: temporary bxCAN mailbox occupancy causes bounded queuing or an<br>explicit deadline expiry rather than an immediate false failure; selector and<br>DCDC owners receive truthful completion results; and terminal\/web surfaces<br>separate current CAN health from cumulative history.<br><br>Implementation evidence, 2026-07-14: the shared ring remains bounded at 16<br>storage slots \/ 15 usable requests and is serviced as a strict FIFO. The head<br>request remains queued when all three bxCAN mailboxes are occupied; one<br>deferral is recorded per service pass and later requests cannot bypass it.<br>Requests leave the FIFO only on mailbox admission or deadline expiry. Admitted<br>requests retain owner, correlation, context, deadline, and mailbox until bxCAN<br>reports completion; owners now receive transmitted, expired, arbitration-lost,<br>or transmit-error results rather than mailbox-admission results. Scheduler<br>access is serialized across the CAN and application tasks.<br><br>The CAN structured view now spans two protocol pages and publishes 39 fields,<br>including queue depth\/high-water\/overflow, mailbox deferrals, in-flight count,<br>confirmed completions, deadline expiry, arbitration loss, and transmit error.<br>The ESP32 dashboard hydrator fetches and combines both pages. Drivers -> CAN<br>labels admission and completion separately, derives its current TX condition<br>from five-second counter-change evidence, and leaves cumulative totals in<br>Engineering Details.<br><br>Verification on the safe T15-off bench completed with the output-enabled BDC668<br>release build flashed to both controllers. The final `can` sample showed 570<br>mailbox admissions and 570 confirmed completions, TX queue depth 0\/high-water<br>3, in-flight 0, and zero queue overflow, expiry, arbitration loss, transmit<br>error, aggregate TX failure, or selector-response failure. `\/api\/dashboard`<br>reported the same two-page view with `field_count = 39`; all five applicable<br>board browser tests passed. The focused scheduler test covers mixed three-frame<br>DCDC plus selector traffic, busy-mailbox recovery, FIFO ownership\/order,<br>15-entry overflow, queued and in-flight expiry, transmit error, and arbitration<br>loss. The full host suite passed 36\/36 and the local browser suite passed<br>22\/22. The remaining T15-on, high-side-ready, Forward, throttle, and physical<br>`0x210`\/`0x220` exercise stays unchecked for the operator's next powered bench<br>session.<br><br>## Slice 28.71 CAN Transport, Router, And Device Ownership Boundary<br><br>The 28.70 investigation confirmed that the low-level CAN1 transport is mostly<br>device-agnostic, but the current `can1_vehicle_dispatch_service` combines<br>subscription routing with Shunt and Drive Selector protocol ownership. The CAN<br>operator view also mixes global bus health, generic delivery evidence, and<br>device-specific decoded\/response counters. This conflicts with the durable CAN<br>bus device safety contract and will become increasingly costly as inverter,<br>BMS, charger, and further PDM integrations arrive.<br><br>- [x] Keep the CAN1 hardware transport limited to controller setup, filters,<br>  ISR\/raw RX buffering, generic TX queue\/mailbox\/deadline handling, and global<br>  bus-pressure\/error diagnostics.<br>- [x] Replace device-named TX scheduler ownership with an opaque result endpoint<br>  token plus correlation\/context; keep all endpoint interpretation outside the<br>  scheduler and hardware transport.<br>- [x] Recast CAN1 dispatch as a generic static subscription router which only<br>  matches descriptors, invokes bounded owner delivery endpoints, and records<br>  generic matched\/delivered\/rejected\/unhandled evidence.<br>- [x] Move Shunt subscription declarations, decode state, validation, packet<br>  counters, measurement publication, and snapshot access into a Shunt-owned CAN<br>  service.<br>- [x] Move Drive Selector subscription declaration, decode\/state, response-frame<br>  construction, TX-result interpretation, stale handling, packet counters, and<br>  event\/record publication into a Drive Selector-owned CAN service.<br>- [x] Keep Traction DCDC frame decode and command-result interpretation in its<br>  existing device service; expose its subscription descriptors through the<br>  same device-owned composition contract used by the other CAN devices.<br>- [x] Compose the reviewed static CAN1 subscription registry at the application<br>  boundary without adding device branches or decoders to the transport\/router.<br>- [x] Separate global CAN transport\/router diagnostics from per-device packet<br>  and protocol diagnostics in the structured API and Drivers -> CAN surface;<br>  retain device counters in their owning terminal\/web views where currently<br>  published.<br>- [x] Add host coverage proving generic routing, opaque TX-result ownership,<br>  Shunt decode\/publication, Drive Selector request\/response\/stale behavior, and<br>  unchanged Traction DCDC delivery under the new ownership boundary.<br>- [ ] Re-run host, STM32\/ESP32, local browser, and safe-bench CAN verification;<br>  leave powered T15\/high-side\/Forward\/throttle evidence with the remaining<br>  28.70 operator bench step.<br><br>Exit criteria: neither the CAN hardware transport nor generic router names or<br>decodes a vehicle device; subscription and protocol facts are owned by device<br>services; TX results return through opaque endpoints; and CAN versus device<br>diagnostics remain truthful without regressing current bench behavior.<br><br>Implementation evidence, 2026-07-14: CAN RX subscriptions now contain an<br>opaque `CanDeliveryEndpoint`, and TX requests\/results contain an opaque<br>`Can1TxResultEndpoint`. The generic dispatch service only matches the composed<br>registry, calls the selected endpoint, and counts generic dispatch, match,<br>delivery, rejection, ignored, and unhandled outcomes. It contains no vehicle<br>IDs, device headers, decoders, response construction, or stale logic.<br><br>Drive Selector and Shunt now have explicit CAN service modules which export<br>their own subscription descriptors and own decode state, event publication,<br>protocol counters, selector response construction\/completion, and selector<br>stale handling. The existing Traction DCDC service exports its four BDC668<br>descriptors and owns its opaque TX completion endpoint. `main.cpp` is the<br>composition root which concatenates the reviewed 13 descriptors and maps the<br>three endpoint tokens to their services.<br><br>The CAN operator view is again a single 30-field page containing only bus,<br>ring, generic-router, and generic-TX scheduler evidence. Shunt counters remain<br>in the Shunt view; selector response completion counters moved to the Drive<br>Selector group in the paged Inputs view; Traction DCDC counters remain in its<br>device view. Drivers -> CAN now presents only generic routing and transport<br>health. The ESP32 snapshot hydrator checks the protocol `has more` flag, so it<br>works with both single- and multi-page views.<br><br>Host coverage now includes generic endpoint routing, opaque scheduler result<br>return, selector-owned request\/response\/result behavior, Shunt-owned<br>decode\/publication\/rejection behavior, device-owned descriptor declarations,<br>and the existing Traction DCDC service tests. The host suite, STM32 release<br>build, ESP32 firmware\/web builds, and 22-test local browser suite pass. Safe<br>bench flashing and live CAN verification remain open with the final checklist<br>item and the powered portion of Slice 28.70.<br><br>## Slice 28.90 Review, Bug Ledger, And Close-Out<br><br>- [x] Review the prototypes with the operator and record adopt, revise, or<br>  reject decisions for each shared pattern.<br>- [x] Classify discovered issues as Phase 28 fixes, later page migrations,<br>  missing-data work, or unrelated bugs.<br>- [x] If adopted, record a bounded follow-up migration list rather than silently<br>  expanding Phase 28 across every page.<br>- [x] Update the Phase 28 brief, top-level status, evidence, and close-out notes.<br>- [x] Run final relevant tests and builds and commit the completed phase.<br><br>Exit criteria:<br><br>- The project has a reviewed panel grammar, three proven prototypes, the 13<br>  agreed migrations completed or explicitly deferred, and a clear bug ledger<br>  without leaving Phase 28 indefinitely open.<br><br>Prototype adoption and follow-up ledger:<br><br>- Adopted: the shared input -> decision\/state -> output hierarchy, visible<br>  condition strip, dense numeric metrics, categorical state rails\/chips,<br>  text-backed colour key, and persistent engineering-details drawer.<br>- Adopted prototypes: Services -> Torque Service, Devices -> Drive Selector,<br>  and Devices -> Traction DCDC. Operator review confirmed the direction and<br>  accepted all three after the recorded refinements and live torque check.<br>- Fixed in the prototype path: disclosure state\/click races, DCDC measurement<br>  colour and rail consistency, narrow-stage chip overflow, duplicate Torque<br>  Service presentation, signed torque plumbing and units, torque state\/sample<br>  stream separation, automatic browser torque subscription, and redundant<br>  Vehicle-page panels.<br>- Agreed bounded migrations are recorded in Slices 28.41-28.53 and the later<br>  Vehicle-page follow-on in Slices 28.60-28.63. Charging and<br>  Chargers are deferred until real feeds exist; Intent \/ Applied Feedback will<br>  be consolidated into Output Drivers. Other browser pages are not part of<br>  this migration run.<br>- Missing-data work remains separate from presentation migration. Inverter,<br>  charger, broader BMS\/PDM facts, and other not-yet-published device data must<br>  be added by their owning integration work rather than invented by Phase 28.<br>- Phase 28 closed on 2026-07-14 after operator review of the migrated surfaces,<br>  live motoring\/shutdown presentation, CAN ownership cleanup, and final build<br>  and test evidence. Further presentation nuances are normal follow-up work,<br>  not a reason to keep the phase active.<br><br>Close-out evidence, 2026-07-14: the host suite passed 38 tests; STM32 release,<br>ESP32 firmware, and ESP32 web filesystem builds passed; the 22-test local<br>browser suite and focused Powertrain regression passed; and the live board<br>served the final bundle with separate PRE, NEG, and POS HVM\/Precharge rows.<br>The operator confirmed the completed surface was working well through the<br>available bench states. The remaining powered CAN selector edge cases recorded<br>in Slices 28.70\/28.71 and Phase 27 remain narrow follow-up evidence and do not<br>keep Phase 28 open.<br><br>Post-close maintenance evidence, 2026-07-15: sustained compact-stream and<br>headless-browser probes confirmed live throttle, torque, shunt, and Traction<br>DCDC updates after the ESP32 recovery fix; the ESP32 release build and flash<br>passed. The STM32 output-capable BDC668 release build and all 38 host tests<br>passed after adding output-state change detection, the image flashed and<br>reconnected cleanly, and the safe-open `output_gate_state` baseline was current.<br><br>## Verification<br><br>Minimum expected evidence:<br><br>- local web test suite;<br>- ESP32 build when bundled\/deployed assets or firmware integration are touched;<br>- live-board web probe and browser inspection after asset sync or flash;<br>- desktop and narrow-width review of every panel touched by the active slice;<br>- hydrate and WebSocket update checks;<br>- representative healthy, inactive, unavailable\/stale, inhibited, and fault<br>  rendering where the current bench can safely produce them;<br>- no obvious new WebSocket, link, heap, or CPU pressure regression.<br><br>## Stop Rules<br><br>- Stop if presentation code begins deciding vehicle state, permission, fault,<br>  validity, or freshness instead of rendering authoritative facts.<br>- Stop if numeric or diagnostic detail is removed without remaining reachable<br>  in the panel or its diagnostics drawer.<br>- Stop if colour becomes the only state discriminator.<br>- Stop if the prototype requires speculative STM32 facts or new instrumentation.<br>- Stop and record a deferred bug if a fix would materially expand into control<br>  firmware, protocol redesign, or unrelated page work.<br>- Stop any migration not named in Slices 28.41-28.53 or 28.60-28.63 until it is separately<br>  reviewed and added to the plan.<br><br>## Completion Criteria<br><br>Phase 28 is complete when:<br><br>- a reusable input -> decision\/state -> output panel grammar exists;<br>- Torque \/ Drive Permissions, Drive Selector, and Traction DCDC use it;<br>- all 13 agreed Services, Devices, and Drivers migrations are completed or<br>  explicitly deferred with their reason recorded;<br>- the Vehicle page is single-column and its FSM, readiness, and powertrain<br>  surfaces complete Slices 28.60-28.63;<br>- categorical chips, dense numeric metrics, conditions, freshness\/validity,<br>  and diagnostics have consistent roles;<br>- all migrated panels retain correct hydration and live updates;<br>- local and live-board browser evidence passes at desktop and narrow widths;<br>- defects and further migration candidates are recorded and classified; and<br>- the operator has reviewed the completed migration set and the phase is<br>  closed or revised explicitly.<br><br>## Investigation Notes<br><br>- 2026-07-13: Phase created after Phase 27 close-out. Initial direction agreed:<br>  consistent panel header; three-column input\/state\/output flow; always-visible<br>  condition strip; expandable diagnostics; chips for categorical facts;<br>  aligned rows for numeric facts; and stable, text-backed colour semantics.<br>- 2026-07-13: Slice 28.00 audit found no STM32 instrumentation gap. All three<br>  panels hydrate from `\/api\/dashboard` and update from existing typed caches and<br>  granular topics: `permissions`\/`torque`, `digital_inputs.state`, and<br>  `traction_dcdc.state` plus `traction_dcdc.sampled`. Drive Selector and<br>  Traction DCDC were flattening primary state, measurements, conditions,<br>  counters, and timestamps into equal chips. Torque \/ Drive Permissions was<br>  rendering only the permission matrix although cached torque service\/monitor\/<br>  intent facts were already available.<br>- 2026-07-13: Added reusable `informationPanelHtml`, stage, and metric helpers.<br>  The responsive contract is three equal stages above 760 px and one stacked<br>  column at or below 760 px. Requested\/input facts use purple, internal active<br>  state uses cyan, accepted\/healthy output uses green, inhibited\/degraded uses<br>  amber, fault uses red, and unavailable remains grey; every state retains a<br>  text label.<br>- 2026-07-13: Prototype conversion complete for the three selected panels.<br>  Engineering details now hold selector counters\/timestamps, DCDC TX\/update\/<br>  feed evidence, and torque reasons\/inhibit evidence. Conditions remain visible<br>  outside the drawer. One presentation regression found by tests was fixed:<br>  permission values retain authoritative `yes`\/`no` rather than changing to<br>  `allowed`\/`blocked`, and absent runtime-regen detail is `unavailable` rather<br>  than `No feed` while only the permission topic is present.<br>- 2026-07-13: Generated assets rebuilt; local Playwright suite passed 9\/9,<br>  including a 390 px stacked-flow\/no-overflow regression for all prototypes;<br>  `make esp32-build` passed at 55.7% RAM and 86.6% application flash; web sync<br>  uploaded only `app.bundle.js` and `styles.css`; board browser suite passed all<br>  4 applicable tests with 4 fixture-only tests skipped; and<br>  `esp32_web_probe.mjs --ws-dashboard` confirmed live HTTP\/WebSocket status and<br>  the existing cached permission, selector\/throttle, and DCDC facts. In-app<br>  visual inspection could not be captured because the browser-control runtime<br>  failed to start, so before\/after screenshots and manual desktop\/narrow panel<br>  review remain open.<br>- 2026-07-13: Operator review accepted the direction and found that engineering<br>  disclosure controls appeared inert. Root cause was the live render loop<br>  replacing each panel's DOM and therefore resetting native `&lt;details>` state<br>  to closed. Shared panel replacement now preserves open disclosures across<br>  refreshes; a regression opens DCDC engineering details, applies a sampled<br>  stream update, and confirms the drawer remains open. The richer panel grammar<br>  also established a practical two-panel-wide limit: general `.view` pages now<br>  use two desktop columns and retain the existing one-column mobile layout.<br>  Services now has one consolidated `Torque Service` panel using the richer<br>  former Torque \/ Drive Permissions content; the redundant older Torque<br>  Service card was removed. Revised assets were synced to the board and all 4<br>  applicable board tests passed, with 5 fixture-only tests skipped.<br>- 2026-07-13: Follow-up operator review found opening a disclosure could still<br>  require several clicks. A deliberately slow pointer regression reproduced<br>  the remaining race: live rendering could replace the summary between pointer<br>  down and native click activation. Disclosure state is now explicit browser<br>  presentation state, toggled on pointer-down (or keyboard click), while the<br>  interacted panel is briefly held stable. The slow-click test holds the mouse<br>  down across several render periods, confirms the drawer opens on the first<br>  interaction, applies a stream update, and confirms it stays open. Local suite<br>  passed 9\/9; revised cache-busted assets were synced and all 4 applicable board<br>  tests passed with 5 fixture-only tests skipped.<br>- 2026-07-13: Added a persistent, text-backed colour key below every page<br>  heading for requested, live, healthy, inhibited, fault, and unavailable<br>  states. Review also identified that Traction DCDC low\/high-side measurements<br>  were incorrectly using the purple requested-input treatment. The measurement<br>  stage and its voltage\/current values now use the cyan live-data treatment;<br>  the purple treatment remains reserved for requested or commanded intent.<br>- 2026-07-13: DCDC panel review standardized the state rail: every inactive<br>  state now uses the same subdued grey treatment and one-pixel chip border,<br>  ordinary active states use green, and active Blocked\/Fault states retain<br>  amber\/red safety meaning. Blocked and Fault remain on the rail because they<br>  are state-machine outcomes, distinct from the independent condition flags.<br>  Chips inside information-flow stages now form a one-column stack so long<br>  enumerated values cannot compete for half of a narrow sub-panel.<br>- 2026-07-13: Torque review found the Dashboard torque indicator still read<br>  retired `torque_request_nm`\/`torque_applied_nm` fields and therefore did not<br>  move with the current compact torque stream. Added one shared responsive<br>  signed-torque graph for Dashboard and Torque Service, now driven by diagnostic<br>  published intent in deci-Nm. Its negative\/positive halves scale from the<br>  active cached regen and motoring configuration limits, with safe project<br>  defaults while configuration is still loading. Raw request and monitor-<br>  accepted values remain visible alongside published output so an inhibited<br>  zero is distinguishable from missing accelerator demand. Browser regression<br>  work also exposed and fixed a tenfold unit error: formatted Nm was being<br>  reparsed as raw deci-Nm because compact-stream raw values were not preserved<br>  through the shared status merge. Torque reason enums now retain their text<br>  labels through the same path.<br>- 2026-07-13: Live forward bench evidence confirmed Torque Service and Torque<br>  Monitor produced and accepted a nonzero request while the deliberately<br>  deferred diagnostic published intent remained unavailable\/zero. The shared<br>  signed graph now visualizes the monitor-accepted request on Dashboard and<br>  Services, using an explicit red `Monitor Accepted` label and red bar so it<br>  cannot be mistaken for a published inverter command. Published intent stays<br>  separately visible in the output stage.<br><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I rewrote my Caterham Seven EV\u2019s vehicle-control software from a clean sheet using ChatGPT Codex. Here\u2019s what the system now does, how my supervised agentic workflow operates, and why AI-generated code still requires architecture, testing, independent review and real bench evidence.<\/p>\n","protected":false},"author":1,"featured_media":11568,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"jetpack_seo_schema_type":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[51,38,56],"tags":[66,64],"class_list":["post-11564","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-caterham-blog","category-electric-vehicles","category-software","tag-caterham","tag-project-seven"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-Pt10-Dashboard.jpg","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p8yl38-30w","jetpack-related-posts":[{"id":11308,"url":"https:\/\/purplemeanie.co.uk\/index.php\/2026\/04\/25\/spectrogram-an-agentic-coding-side-quest-into-final-cut-pro-audio-analysis\/","url_meta":{"origin":11564,"position":0},"title":"Spectrogram: An Agentic Coding Side Quest Into Final Cut Pro Audio Analysis","author":"John Martin","date":"April 25, 2026","format":false,"excerpt":"If you already live and breathe audio analysis, feel free to skip this bit and go straight to the tale of plugin architecture, notarization and mild host-induced despair. A spectrogram is a way of visualising sound over time. Instead of just showing amplitude, like a waveform does, it shows: Time\u2026","rel":"","context":"In &quot;Caterham Blog&quot;","block_context":{"text":"Caterham Blog","link":"https:\/\/purplemeanie.co.uk\/index.php\/category\/caterham-blog\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/04\/FCP-Full-scaled.jpeg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/04\/FCP-Full-scaled.jpeg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/04\/FCP-Full-scaled.jpeg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/04\/FCP-Full-scaled.jpeg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/04\/FCP-Full-scaled.jpeg?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/04\/FCP-Full-scaled.jpeg?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":11558,"url":"https:\/\/purplemeanie.co.uk\/index.php\/2026\/07\/23\/project-seven-pt-10-project-update-and-software-re-write-with-ai\/","url_meta":{"origin":11564,"position":1},"title":"Project sEVen pt 10: Project Update and Software re-write with AI","author":"John Martin","date":"July 23, 2026","format":false,"excerpt":"A detailed July update on the Purple Meanie Caterham Seven EV conversion, including the rebuilt VCU software, ESP32 web interface, updated bench hardware and live precharge demonstration.","rel":"","context":"In &quot;Caterham Blog&quot;","block_context":{"text":"Caterham Blog","link":"https:\/\/purplemeanie.co.uk\/index.php\/category\/caterham-blog\/"},"img":{"alt_text":"Project sEVen part 10 VCU rewrite thumbnail","src":"https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-pt10-002-scaled.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-pt10-002-scaled.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-pt10-002-scaled.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-pt10-002-scaled.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-pt10-002-scaled.jpg?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/07\/Project-sEVen-pt10-002-scaled.jpg?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":11273,"url":"https:\/\/purplemeanie.co.uk\/index.php\/2026\/04\/24\/project-seven-pt9-traction-dc2dc-converter-bring-up\/","url_meta":{"origin":11564,"position":2},"title":"Project sEVen Pt9: Traction DC2DC converter Bring Up","author":"John Martin","date":"April 24, 2026","format":false,"excerpt":"Project sEVen update covering traction DC-DC converter bring-up, high-voltage testing, low-voltage integration, CAN bus work, and the Caterham EV testbench.","rel":"","context":"In &quot;Caterham Blog&quot;","block_context":{"text":"Caterham Blog","link":"https:\/\/purplemeanie.co.uk\/index.php\/category\/caterham-blog\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/04\/Project-sEVen-pt9-001_2-scaled.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/04\/Project-sEVen-pt9-001_2-scaled.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/04\/Project-sEVen-pt9-001_2-scaled.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/04\/Project-sEVen-pt9-001_2-scaled.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/04\/Project-sEVen-pt9-001_2-scaled.jpg?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2026\/04\/Project-sEVen-pt9-001_2-scaled.jpg?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":5086,"url":"https:\/\/purplemeanie.co.uk\/index.php\/2019\/09\/16\/ecu-diagnostics-part-12-osi-7-layers-for-caterham-diagnostics\/","url_meta":{"origin":11564,"position":3},"title":"ECU Diagnostics &#8211; part 12 : OSI 7 Layers for Caterham Diagnostics","author":"John Martin","date":"September 16, 2019","format":false,"excerpt":"Maps the Caterham diagnostics stack onto the OSI model, from CAN bus hardware through MBE ISO-TP layers up to Python tooling and applications.","rel":"","context":"In &quot;ECU Diagnostics&quot;","block_context":{"text":"ECU Diagnostics","link":"https:\/\/purplemeanie.co.uk\/index.php\/category\/caterham-blog\/ecu-diagnostics\/"},"img":{"alt_text":"Layered diagram mapping the MBE ECU protocol stack to OSI layers and CAN bus.","src":"https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2019\/09\/ECU-OSI-Layers.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2019\/09\/ECU-OSI-Layers.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2019\/09\/ECU-OSI-Layers.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2019\/09\/ECU-OSI-Layers.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2019\/09\/ECU-OSI-Layers.png?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":4782,"url":"https:\/\/purplemeanie.co.uk\/index.php\/2019\/09\/07\/ecu-diagnostics-part-4-wireshark-patching-and-obd-ii-results\/","url_meta":{"origin":11564,"position":4},"title":"ECU Diagnostics &#8211; part 4 : Wireshark Patching and OBD-II Results","author":"John Martin","date":"September 7, 2019","format":false,"excerpt":"Uses patched Wireshark decoding to inspect Caterham CAN bus captures and compare the useful diagnostic data available through OBD-II.","rel":"","context":"In &quot;Caterham Blog&quot;","block_context":{"text":"Caterham Blog","link":"https:\/\/purplemeanie.co.uk\/index.php\/category\/caterham-blog\/"},"img":{"alt_text":"Wireshark capture with OBD-II packets decoded after the dissector fix.","src":"https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2019\/08\/cano7-OBD-II-working.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2019\/08\/cano7-OBD-II-working.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2019\/08\/cano7-OBD-II-working.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2019\/08\/cano7-OBD-II-working.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2019\/08\/cano7-OBD-II-working.png?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2019\/08\/cano7-OBD-II-working.png?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":8125,"url":"https:\/\/purplemeanie.co.uk\/index.php\/2022\/09\/30\/seven-index\/","url_meta":{"origin":11564,"position":5},"title":"Putting the EV in sEVen &#8211; Index","author":"John Martin","date":"September 30, 2022","format":false,"excerpt":"Project sEVen is the electric conversion thread for the Caterham, covering mechanical packaging, EV power electronics, CAN bus diagnostics, 3D scanning, CAD references, and systems integration.","rel":"","context":"In &quot;Caterham Blog&quot;","block_context":{"text":"Caterham Blog","link":"https:\/\/purplemeanie.co.uk\/index.php\/category\/caterham-blog\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2022\/09\/Cartoon-Seven-Vector-with-EV-Purple-Bumper.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2022\/09\/Cartoon-Seven-Vector-with-EV-Purple-Bumper.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2022\/09\/Cartoon-Seven-Vector-with-EV-Purple-Bumper.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2022\/09\/Cartoon-Seven-Vector-with-EV-Purple-Bumper.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2022\/09\/Cartoon-Seven-Vector-with-EV-Purple-Bumper.png?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/purplemeanie.co.uk\/wp-content\/uploads\/2022\/09\/Cartoon-Seven-Vector-with-EV-Purple-Bumper.png?resize=1400%2C800&ssl=1 4x"},"classes":[]}],"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/purplemeanie.co.uk\/index.php\/wp-json\/wp\/v2\/posts\/11564","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/purplemeanie.co.uk\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/purplemeanie.co.uk\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/purplemeanie.co.uk\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/purplemeanie.co.uk\/index.php\/wp-json\/wp\/v2\/comments?post=11564"}],"version-history":[{"count":9,"href":"https:\/\/purplemeanie.co.uk\/index.php\/wp-json\/wp\/v2\/posts\/11564\/revisions"}],"predecessor-version":[{"id":11579,"href":"https:\/\/purplemeanie.co.uk\/index.php\/wp-json\/wp\/v2\/posts\/11564\/revisions\/11579"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/purplemeanie.co.uk\/index.php\/wp-json\/wp\/v2\/media\/11568"}],"wp:attachment":[{"href":"https:\/\/purplemeanie.co.uk\/index.php\/wp-json\/wp\/v2\/media?parent=11564"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/purplemeanie.co.uk\/index.php\/wp-json\/wp\/v2\/categories?post=11564"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/purplemeanie.co.uk\/index.php\/wp-json\/wp\/v2\/tags?post=11564"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}