What If You Could Replay the Decisions Your Software-Defined Vehicle Makes?

6 minute read Published: 2026-10-26

TL;DR

In a Software-Defined Vehicle, software components continuously react to events from sensors, ECUs, and other parts of the vehicle system. Getting each event right is not enough: the order in which events arrive can change the decisions the software makes.

What if we could take a recorded execution, change the order of selected events, and replay them through the same software to see how its decisions changeβ€”and trace why?

This prototype explores that idea using a Rust-based vehicle Digital Twin built around Actors and explicit Finite State Machines (FSMs).

The Actor system models independently reacting components; the FSMs make their state-dependent decisions explicit. Together, they provide something more useful than a stream of log messages: a record of how incoming events drive state transitions, trigger actions, and influence subsequent decisions.

A simulated vehicle environment supplies the events. We can modify a copy of a recorded event-sequence and feed it back through the same decision logic, then compare the resulting execution with the original.

The goal is to make software decisions reproducible and inspectableβ€”even when changing the order of events, rather than the code itself, changes the outcome.

πŸ“Œ Note:

This is a working Linux-based prototype using SocketCAN's virtual CAN interface (vcan) to demonstrate a testing approach, not a production vehicle platform. The experiments show specific, reproducible behaviours; they are not a claim of exhaustive verification.


This work builds on an earlier milestone in the same SDV Digital Twin project, which describes the architecture and its evolution: Digital Twin: Designing an Actor-Based SDV Prototype in Rust (Milestone 1).


One change in event order, a different decision

Consider a car that is initially idle. Engine is idling at minimum RPM. Visibility is good. Headlamp is ready.

Two events are about to arrive: the engine-speed reading that indicates the car is moving, and a lux reading that indicates poor visibility. Rain-sensor is active too.

During a regular run, the low-visibility event arrives first. The twin requests that the headlamp be switched on. The Rain sensor sends its event. Then the engine-speed event arrives. The twin transitions to the state: Driving. The ledger tells us the story.

Now we make one deliberate change to the same ledger: we interchange the order of the low-visibility and engine-speed events, leaving the rain event in between.

This time, the engine-speed event arrives first. Under the prototype's simplified vehicle model, the Twin derives a speed of 171 km/h and enters DrivingDangerously, activating the buzzer. Only afterwards does the low-visibility event arrive, prompting the Twin to request the headlamp be switched on.

πŸ“Œ Note:

That 171 Km/h is the threshold that the Twin's decision engine recognizes: just an assumption that the prototype makes.

The important difference is the sequence of decisionst that the Twin makes.

Original execution:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     ╔══════╗     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     ╔══════╗
β”‚ Lux = 820 β”‚ --> β•‘ Idle β•‘ --> β”‚ Rain starts β”‚ --> β•‘ Idle β•‘
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β•šβ•β•β•β•β•β•β•     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β•šβ•β•β•β•β•β•β•
                                                       |
                                                       |
                                                       V 
                                ╔═════════╗     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                β•‘ Driving β•‘ <-- β”‚ RPM = 1500 β”‚
                                β•šβ•β•β•β•β•β•β•β•β•β•     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Deliberately modified execution:

     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     ╔════════════════════╗            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚ RPM = 1500 β”‚ --> β•‘ DrivingDangerously β•‘     -->    β”‚ Rain starts β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                                 |
                                                                 |
                                                                 V 
     ╔════════════════════╗        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         ╔════════════════════╗
     β•‘ DrivingDangerously β•‘  <--   β”‚ Lux = 820 β”‚   <--   β•‘ DrivingDangerously β•‘
     β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚        β”‚  single line box: an event just received
β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜
╔════════╗
β•‘        β•‘  double line box: the car's state after that event
β•šβ•β•β•β•β•β•β•β•β•

Using a tool (part of the prototype), two outcomes - original and modified - can be compared to produce a report like:

<snip..>
EXPERIMENT: Event-order sensitivity

RESULT
Original execution: Driving
Modified execution: DrivingDangerously

EVIDENCE β€” first event

<snip..>

Original: lux=820
state=Idle
action=request_front_headlamp_on

Modified: rpm=1500
state=DrivingDangerously
actions=publish_state_sync,start_buzzer

<snip..>

FINAL STATE
Original:  Off   # The vehicle reaches this state finally.
Modified: Off    # The vehicle reaches this state finally.

The experiment does not require a different twin or a new decision algorithm. We modify a copy of the recorded event sequence and feed it back through the same twin. The resulting ledger records the decisions made during this (re)execution.

There is one more revealing detail: both executions eventually reach standstill and switch off. Their final states are identical, but their paths to that ending are not. Therefore, the end-state is not enough to know what happened meanwhile. This tool makes that possible.


When the command and the confirmation disagree

A headlamp command is not the same as a headlamp response. The actuator may acknowledge the command, reject it, or fail to respond as expected.

By changing the recorded response and replaying the sequence through the same Actor/FSM logic, we can examine how the Twin handles these different outcomes. In the demonstrated experiment, a negative acknowledgement leaves the headlamp in Ready and triggers a warning; a positive acknowledgement moves it to On without the warning.

The same approach can help investigate how vehicle software responds when commands, confirmations, and subsequent events don't follow the expected sequence.


Where this could help engineering teams

Consider a few questions an engineer might ask while investigating vehicle software:

These are examples of how a Digital Twin could help bring certain investigations earlier into development. The prototype demonstrates the replay-and-inspect approach; applying it to a particular vehicle subsystem would require adapting the model, defining the scenarios and deciding what behaviour needs to be checked.

The useful question is: which decisions or failure scenarios in your own system would benefit from being reproducible and inspectable this way?


The technology used: a summary

A few (Linux) processes interact, using virtual CAN bus and Unix Domain Socket:

Car physics is a pure library layer; responsible for all calculations to derive car's response to physical world.

The state machine is pure Finite State Machine. Its job is to arrive at stable states, always deterministicatlly.

The actor system runs inside the gateway. A tree of actors represents the brain and zone vECUs.


Takeaway and possible next steps

The prototype demonstrates a way to make software decisions reproducible and inspectable: start with a controlled scenario, introduce a deliberate change, replay the execution, and examine the resulting decisions and state transitions.

The experiments here are handcrafted, not exhaustive verification. Applying this approach to a real vehicle subsystem would require further engineering, including defining the scenarios and the properties to be checked.

If you are dealing with an SDV engineering problem where event ordering, component responses or failure scenarios need closer investigation, I'd be interested in discussing how this prototype could be adapted and extended to address it. I am open to paid engineering assignments focused on specific vehicle subsystems or engineering problems. You can reach me through LinkedIn DM.