Repository: NREL/hive (language: python)

Issue:
TaskIds for VehicleState equality
in hive-distributed, we compare VehicleStates for near-sameness. in a [proposed PR](https://github.nrel.gov/MBAP/hive-distributed/pull/178), there is a note pointing out that this is possibly flawed.

as an alternative, a VehicleState could have a simple unique TaskId which could be idempotent through VehicleState.update calls. then task sameness could be a simple test of 

```python
TaskId = str
def same_task(v0, v1):
  return v0.vehicle_state.task_id == v1.vehicle_state.task_id
```

some thoughts:
- this id could be generated using python's built-in [uuid generator](https://docs.python.org/3/library/uuid.html) (using [uuid4](https://docs.python.org/3/library/uuid.html#uuid.uuid4))
- uuid.uuid4() creates a "random uuid"; we should confirm that importing the uuid module in different files does not produce duplicate results due to seed values
- VehicleState should have a `@property @abstractmethod def task_id`
- all VehicleState.default_update methods would need to make sure they use ._replace when updating local state, to retain this TaskId. if not possible, they should explicitly copy the TaskId into a new VehicleState object (unless the new object is a "different task", such as moving from Repositioning to Idle)




Discussion / hints:
oh, btw, `TaskId` is up for discussion as a name here for certain. maybe something less general, like, `VehicleStateInstanceId` if that's not too verbose
yeah I like this proposal, and UUID seems like a good fit. 

Fix patch:
diff --git a/hive/dispatcher/instruction/instruction_ops.py b/hive/dispatcher/instruction/instruction_ops.py
index 52222e73..fceaff9b 100644
--- a/hive/dispatcher/instruction/instruction_ops.py
+++ b/hive/dispatcher/instruction/instruction_ops.py
@@ -37,8 +37,7 @@ def trip_plan_covers_previous(previous_state: ServicingPoolingTrip,
 
 
 def trip_plan_ordering_is_valid(new_trip_plan: Tuple[Tuple[RequestId, TripPhase], ...],
-                                previous_state: Optional[ServicingPoolingTrip] = None
-                                ) -> bool:
+                                previous_state: Optional[ServicingPoolingTrip] = None) -> bool:
     """
     checks that the incoming trip plan has a logical pickup and dropoff ordering and that
     no passengers are left on the vehicle after all steps in the trip plan.
@@ -82,8 +81,9 @@ def _test(acc: Tuple[bool, FrozenSet[str]],
     return has_valid_order and no_passengers_at_end_of_trip_plan
 
 
-def trip_plan_all_requests_allow_pooling(sim: 'SimulationState',
-                                         trip_plan: Tuple[Tuple[RequestId, TripPhase], ...]) -> Optional[str]:
+def trip_plan_all_requests_allow_pooling(
+        sim: 'SimulationState', trip_plan: Tuple[Tuple[RequestId, TripPhase],
+                                                 ...]) -> Optional[str]:
     """
     confirm that each request in the trip plan allows pooling
 
@@ -99,7 +99,7 @@ def _test_req(test_errors: Tuple[Tuple[str, ...], Tuple[str, ...]],
             updated_sim_error_ids = sim_error_ids + (r_id, )
             return updated_sim_error_ids, pool_error_ids
         elif not req.allows_pooling:
-            updated_pool_error_ids = sim_error_ids + (r_id,)
+            updated_pool_error_ids = sim_error_ids + (r_id, )
             return sim_error_ids, updated_pool_error_ids
         else:
             return test_errors
@@ -160,7 +160,7 @@ def _test_req(test_errors: Tuple[Tuple[str, ...], Tuple[str, ...]],
 #     reqs = immutables.Map({r_id: sim.requests.get(r_id) for r_id in req_ids_unique})
 #     num_passengers = sum([len(r.passengers) for r in reqs.values()])
 #
-#     state = ServicingPoolingTrip(
+#     state = ServicingPoolingTrip.build(
 #         vehicle_id=vehicle.id,
 #         trip_plan=trip_plan,
 #         trips=reqs,
diff --git a/hive/dispatcher/instruction/instructions.py b/hive/dispatcher/instruction/instructions.py
index 80c8403f..fc58a9f5 100644
--- a/hive/dispatcher/instruction/instructions.py
+++ b/hive/dispatcher/instruction/instructions.py
@@ -6,7 +6,8 @@
 from hive.dispatcher.instruction.instruction import Instruction
 from hive.dispatcher.instruction.instruction_ops import (
     trip_plan_ordering_is_valid,
-    trip_plan_all_requests_allow_pooling, trip_plan_covers_previous,
+    trip_plan_all_requests_allow_pooling,
+    trip_plan_covers_previous,
 )
 from hive.dispatcher.instruction.instruction_result import InstructionResult
 from hive.model.roadnetwork.link import Link, EntityPosition
@@ -43,19 +44,18 @@ class IdleInstruction(NamedTuple, Instruction):
     vehicle_id: VehicleId
 
     def apply_instruction(
-        self, sim_state: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
+            self, sim_state: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
         vehicle = sim_state.vehicles.get(self.vehicle_id)
         if not vehicle:
             return (
                 SimulationStateError(
-                    f"vehicle {vehicle} not found; context: applying idle instruction."
-                ),
+                    f"vehicle {vehicle} not found; context: applying idle instruction."),
                 None,
             )
         else:
             prev_state = vehicle.vehicle_state
-            next_state = Idle(self.vehicle_id)
+            next_state = Idle.build(self.vehicle_id)
             return None, InstructionResult(prev_state, next_state)
 
 
@@ -64,8 +64,8 @@ class DispatchTripInstruction(NamedTuple, Instruction):
     request_id: RequestId
 
     def apply_instruction(
-        self, sim_state: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
+            self, sim_state: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
         vehicle = sim_state.vehicles.get(self.vehicle_id)
         request = sim_state.requests.get(self.request_id)
         if not vehicle:
@@ -87,7 +87,7 @@ def apply_instruction(
             end = request.origin_position
             route = sim_state.road_network.route(start, end)
             prev_state = vehicle.vehicle_state
-            next_state = DispatchTrip(self.vehicle_id, self.request_id, route)
+            next_state = DispatchTrip.build(self.vehicle_id, self.request_id, route)
 
             return None, InstructionResult(prev_state, next_state)
 
@@ -97,18 +97,18 @@ class DispatchPoolingTripInstruction(NamedTuple, Instruction):
     trip_plan: Tuple[Tuple[RequestId, TripPhase], ...]
 
     def apply_instruction(
-        self, sim_state: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
+            self, sim_state: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
         # see https://github.com/NREL/hive/issues/9 for implementation plan
         vehicle = sim_state.vehicles.get(self.vehicle_id)
         v_state = vehicle.vehicle_state
 
         req_allow_pooling_error_msg = trip_plan_all_requests_allow_pooling(
-            sim_state, self.trip_plan
-        )
+            sim_state, self.trip_plan)
         # seating_error = check_if_vehicle_has_seats(sim_state, vehicle, self.trip_plan)
 
-        if isinstance(v_state, ServicingPoolingTrip) and not trip_plan_covers_previous(v_state, self.trip_plan):
+        if isinstance(v_state, ServicingPoolingTrip) and not trip_plan_covers_previous(
+                v_state, self.trip_plan):
             msg = "DispatchPoolingTripInstruction updates an active pooling state but doesn't include all previous requests"
             error = InstructionError(msg)
             return error, None
@@ -135,8 +135,7 @@ def apply_instruction(
             return error, None
         else:
             error, next_state = dispatch_ops.begin_or_replan_dispatch_pooling_state(
-                sim_state, self.vehicle_id, self.trip_plan
-            )
+                sim_state, self.vehicle_id, self.trip_plan)
             if error is not None:
                 return error, None
             else:
@@ -149,23 +148,26 @@ class DispatchStationInstruction(NamedTuple, Instruction):
     charger_id: ChargerId
 
     def apply_instruction(
-        self, sim_state: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
+            self, sim_state: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
         vehicle = sim_state.vehicles.get(self.vehicle_id)
         station = sim_state.stations.get(self.station_id)
         if not vehicle:
-            return SimulationStateError(f"vehicle {vehicle} not found: context: applying dispatch station instruction to station {self.station_id}"), None
+            return SimulationStateError(
+                f"vehicle {vehicle} not found: context: applying dispatch station instruction to station {self.station_id}"
+            ), None
         elif not station:
-            return SimulationStateError(f"station {station} not found: context: applying dispatch station instruction for vehicle {self.vehicle_id}"), None
+            return SimulationStateError(
+                f"station {station} not found: context: applying dispatch station instruction for vehicle {self.vehicle_id}"
+            ), None
         else:
             start = vehicle.position
             end = station.position
             route = sim_state.road_network.route(start, end)
 
             prev_state = vehicle.vehicle_state
-            next_state = DispatchStation(
-                self.vehicle_id, self.station_id, route, self.charger_id
-            )
+            next_state = DispatchStation.build(self.vehicle_id, self.station_id, route,
+                                               self.charger_id)
 
             return None, InstructionResult(prev_state, next_state)
 
@@ -176,16 +178,16 @@ class ChargeStationInstruction(NamedTuple, Instruction):
     charger_id: ChargerId
 
     def apply_instruction(
-        self, sim_state: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
+            self, sim_state: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
         vehicle = sim_state.vehicles.get(self.vehicle_id)
         if not vehicle:
-            return SimulationStateError(f"vehicle {vehicle} not found: context: applying charge station instruction at {self.station_id} with {self.charger_id} charger"), None
+            return SimulationStateError(
+                f"vehicle {vehicle} not found: context: applying charge station instruction at {self.station_id} with {self.charger_id} charger"
+            ), None
         else:
             prev_state = vehicle.vehicle_state
-            next_state = ChargingStation(
-                self.vehicle_id, self.station_id, self.charger_id
-            )
+            next_state = ChargingStation.build(self.vehicle_id, self.station_id, self.charger_id)
 
             return None, InstructionResult(prev_state, next_state)
 
@@ -196,14 +198,16 @@ class ChargeBaseInstruction(NamedTuple, Instruction):
     charger_id: ChargerId
 
     def apply_instruction(
-        self, sim_state: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
+            self, sim_state: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
         vehicle = sim_state.vehicles.get(self.vehicle_id)
         if not vehicle:
-            return SimulationStateError(f"vehicle {vehicle} not found; context: applying charge base instruction at base {self.base_id} with {self.charger_id} charger"), None
+            return SimulationStateError(
+                f"vehicle {vehicle} not found; context: applying charge base instruction at base {self.base_id} with {self.charger_id} charger"
+            ), None
         else:
             prev_state = vehicle.vehicle_state
-            next_state = ChargingBase(self.vehicle_id, self.base_id, self.charger_id)
+            next_state = ChargingBase.build(self.vehicle_id, self.base_id, self.charger_id)
 
             return None, InstructionResult(prev_state, next_state)
 
@@ -213,21 +217,25 @@ class DispatchBaseInstruction(NamedTuple, Instruction):
     base_id: BaseId
 
     def apply_instruction(
-        self, sim_state: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
+            self, sim_state: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
         vehicle = sim_state.vehicles.get(self.vehicle_id)
         base = sim_state.bases.get(self.base_id)
         if not vehicle:
-            return SimulationStateError(f"vehicle {self.vehicle_id} not found; context: applying dispatch base instruction to {self.base_id}"), None
+            return SimulationStateError(
+                f"vehicle {self.vehicle_id} not found; context: applying dispatch base instruction to {self.base_id}"
+            ), None
         if not base:
-            return SimulationStateError(f"base {self.base_id} not found; context: applying dispatch base instruction for {self.vehicle_id}"), None
+            return SimulationStateError(
+                f"base {self.base_id} not found; context: applying dispatch base instruction for {self.vehicle_id}"
+            ), None
         else:
             start = vehicle.position
             end = base.position
             route = sim_state.road_network.route(start, end)
 
             prev_state = vehicle.vehicle_state
-            next_state = DispatchBase(self.vehicle_id, self.base_id, route)
+            next_state = DispatchBase.build(self.vehicle_id, self.base_id, route)
 
             return None, InstructionResult(prev_state, next_state)
 
@@ -237,23 +245,27 @@ class RepositionInstruction(NamedTuple, Instruction):
     destination: LinkId
 
     def apply_instruction(
-        self, sim_state: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
+            self, sim_state: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
         vehicle = sim_state.vehicles.get(self.vehicle_id)
         if not vehicle:
-            return SimulationStateError(f"vehicle {self.vehicle_id} not found; context: applying reposition instruction to link {self.destination}"), None
+            return SimulationStateError(
+                f"vehicle {self.vehicle_id} not found; context: applying reposition instruction to link {self.destination}"
+            ), None
         else:
             start = vehicle.position
 
             link = sim_state.road_network.link_from_link_id(self.destination)
             if not link:
-                return SimulationStateError(f"link {self.destination} not found; context: applying reposition instruction for vehicle {self.vehicle_id}"), None
+                return SimulationStateError(
+                    f"link {self.destination} not found; context: applying reposition instruction for vehicle {self.vehicle_id}"
+                ), None
             else:
                 destination_position = EntityPosition(link.link_id, link.end)
                 route = sim_state.road_network.route(start, destination_position)
 
                 prev_state = vehicle.vehicle_state
-                next_state = Repositioning(self.vehicle_id, route)
+                next_state = Repositioning.build(self.vehicle_id, route)
 
                 return None, InstructionResult(prev_state, next_state)
 
@@ -263,13 +275,15 @@ class ReserveBaseInstruction(NamedTuple, Instruction):
     base_id: BaseId
 
     def apply_instruction(
-        self, sim_state: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
+            self, sim_state: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[InstructionResult]]:
         vehicle = sim_state.vehicles.get(self.vehicle_id)
         if not vehicle:
-            return SimulationStateError(f"vehicle {self.vehicle_id} not found; context: applying reserve base instruction at base {self.base_id}"), None
+            return SimulationStateError(
+                f"vehicle {self.vehicle_id} not found; context: applying reserve base instruction at base {self.base_id}"
+            ), None
 
         prev_state = vehicle.vehicle_state
-        next_state = ReserveBase(self.vehicle_id, self.base_id)
+        next_state = ReserveBase.build(self.vehicle_id, self.base_id)
 
         return None, InstructionResult(prev_state, next_state)
diff --git a/hive/initialization/sample_vehicles.py b/hive/initialization/sample_vehicles.py
index 9ca6db4c..f8bd55d3 100644
--- a/hive/initialization/sample_vehicles.py
+++ b/hive/initialization/sample_vehicles.py
@@ -21,13 +21,13 @@
 
 
 def sample_vehicles(
-        count: int,
-        sim: SimulationState,
-        env: Environment,
-        location_sampling_function: Callable[[SimulationState], Link],
-        soc_sampling_function: Callable[[], Ratio],
-        total_seats: int = 4,
-        offset: int = 0,
+    count: int,
+    sim: SimulationState,
+    env: Environment,
+    location_sampling_function: Callable[[SimulationState], Link],
+    soc_sampling_function: Callable[[], Ratio],
+    total_seats: int = 4,
+    offset: int = 0,
 ) -> Result[SimulationState, Exception]:
     """
     creates {count} vehicles using the provided sampling functions
@@ -54,7 +54,6 @@ def _add_sample(i: int):
             :param i: the number to associate with this sampled vehicle
             :return: a function that adds vehicle i to the SimulationState
             """
-
             def _inner(s: SimulationState) -> Result[SimulationState, Exception]:
                 """
                 attempts to add the i'th vehicle to this simulation state
@@ -68,17 +67,15 @@ def _inner(s: SimulationState) -> Result[SimulationState, Exception]:
                     energy = mechatronics.initial_energy(initial_soc)
                     link = location_sampling_function(s)
                     position = EntityPosition(link.link_id, link.start)
-                    vehicle_state = Idle(vehicle_id)
+                    vehicle_state = Idle.build(vehicle_id)
                     driver_state = AutonomousAvailable(AutonomousDriverAttributes(vehicle_id))
-                    vehicle = Vehicle(
-                        id=vehicle_id,
-                        mechatronics_id=mechatronics_id,
-                        energy=energy,
-                        position=position,
-                        vehicle_state=vehicle_state,
-                        driver_state=driver_state,
-                        total_seats=total_seats
-                    )
+                    vehicle = Vehicle(id=vehicle_id,
+                                      mechatronics_id=mechatronics_id,
+                                      energy=energy,
+                                      position=position,
+                                      vehicle_state=vehicle_state,
+                                      driver_state=driver_state,
+                                      total_seats=total_seats)
                     add_result = add_vehicle_returns(s, vehicle)
                     return add_result
                 except Exception as e:
@@ -87,15 +84,14 @@ def _inner(s: SimulationState) -> Result[SimulationState, Exception]:
             return _inner
 
         log.info(
-            f"sampling vehicles {offset} through {offset + count - 1} ({count} vehicles) with mechatronics id {mechatronics_id}")
+            f"sampling vehicles {offset} through {offset + count - 1} ({count} vehicles) with mechatronics id {mechatronics_id}"
+        )
 
         # sample i vehicles, adding each to the sim
         # fail fast if an exception is encountered
-        result: Result[SimulationState, Exception] = ft.reduce(
-            lambda acc, i: acc.bind(_add_sample(i)),
-            range(offset, offset + count),
-            Success(sim)
-        )
+        result: Result[SimulationState,
+                       Exception] = ft.reduce(lambda acc, i: acc.bind(_add_sample(i)),
+                                              range(offset, offset + count), Success(sim))
 
         return result
 
@@ -113,7 +109,8 @@ def build_default_location_sampling_fn(seed: int = 0) -> Callable[[], Link]:
 
     def _inner(sim: SimulationState) -> Link:
         if not isinstance(sim.road_network, OSMRoadNetwork):
-            raise NotImplementedError(f"this sampling function is only implemented for the OSMRoadNetwork")
+            raise NotImplementedError(
+                f"this sampling function is only implemented for the OSMRoadNetwork")
         links = list(sim.road_network.link_helper.links.values())
         if len(links) == 0:
             raise AssertionError(f"must have at least one link to sample from")
@@ -123,8 +120,9 @@ def _inner(sim: SimulationState) -> Link:
     return _inner
 
 
-def build_default_soc_sampling_fn(lower_bound: Ratio = 1.0, upper_bound: Ratio = 1.0, seed: int = 0) -> Callable[
-    [], Ratio]:
+def build_default_soc_sampling_fn(lower_bound: Ratio = 1.0,
+                                  upper_bound: Ratio = 1.0,
+                                  seed: int = 0) -> Callable[[], Ratio]:
     """
     constructs an SoC sampling function that uniformly samples between a lower and upper bound value
 
@@ -135,10 +133,14 @@ def build_default_soc_sampling_fn(lower_bound: Ratio = 1.0, upper_bound: Ratio =
     """
     assert lower_bound <= upper_bound, ArithmeticError(
         f"lower bound {lower_bound} must be less than or equal to upper bound {upper_bound}")
-    assert lower_bound >= 0, ArithmeticError(f"lower bound {lower_bound} must be in the range [0, 1]")
-    assert lower_bound <= 1, ArithmeticError(f"lower bound {lower_bound} must be in the range [0, 1]")
-    assert upper_bound >= 0, ArithmeticError(f"upper bound {upper_bound} must be in the range [0, 1]")
-    assert upper_bound <= 1, ArithmeticError(f"upper bound {upper_bound} must be in the range [0, 1]")
+    assert lower_bound >= 0, ArithmeticError(
+        f"lower bound {lower_bound} must be in the range [0, 1]")
+    assert lower_bound <= 1, ArithmeticError(
+        f"lower bound {lower_bound} must be in the range [0, 1]")
+    assert upper_bound >= 0, ArithmeticError(
+        f"upper bound {upper_bound} must be in the range [0, 1]")
+    assert upper_bound <= 1, ArithmeticError(
+        f"upper bound {upper_bound} must be in the range [0, 1]")
     random.seed(seed)
 
     def _inner() -> Ratio:
diff --git a/hive/model/vehicle/vehicle.py b/hive/model/vehicle/vehicle.py
index c7dc453c..3b39aa0f 100644
--- a/hive/model/vehicle/vehicle.py
+++ b/hive/model/vehicle/vehicle.py
@@ -56,7 +56,8 @@ def geoid(self):
         return self.position.geoid
 
     @classmethod
-    def from_row(cls, row: Dict[str, str], road_network: RoadNetwork, environment: Environment) -> Vehicle:
+    def from_row(cls, row: Dict[str, str], road_network: RoadNetwork,
+                 environment: Environment) -> Vehicle:
         """
         reads a csv row from file to generate a Vehicle
 
@@ -88,18 +89,23 @@ def from_row(cls, row: Dict[str, str], road_network: RoadNetwork, environment: E
                 if not mechatronics:
                     found = set(environment.mechatronics.keys())
                     raise IOError(
-                        f"was not able to find mechatronics '{mechatronics_id}' for vehicle {vehicle_id} in environment: found {found}")
+                        f"was not able to find mechatronics '{mechatronics_id}' for vehicle {vehicle_id} in environment: found {found}"
+                    )
                 energy = mechatronics.initial_energy(float(row['initial_soc']))
 
                 schedule_id = row.get(
-                    'schedule_id')  # if None, it signals an autonomous vehicle, otherwise, human with schedule
+                    'schedule_id'
+                )  # if None, it signals an autonomous vehicle, otherwise, human with schedule
                 home_base_id = row.get('home_base_id')
                 if schedule_id and not schedule_id in environment.schedules.keys():
                     raise IOError(
-                        f"was not able to find schedule '{schedule_id}' in environment for vehicle {vehicle_id}")
-                allows_pooling = bool(row['allows_pooling']) if row.get('allows_pooling') is not None else False
+                        f"was not able to find schedule '{schedule_id}' in environment for vehicle {vehicle_id}"
+                    )
+                allows_pooling = bool(
+                    row['allows_pooling']) if row.get('allows_pooling') is not None else False
                 available_seats = int(row.get('available_seats', 0))
-                driver_state = DriverState.build(vehicle_id, schedule_id, home_base_id, allows_pooling)
+                driver_state = DriverState.build(vehicle_id, schedule_id, home_base_id,
+                                                 allows_pooling)
 
                 geoid = h3.geo_to_h3(lat, lon, road_network.sim_h3_resolution)
                 start_position = road_network.position_from_geoid(geoid)
@@ -109,7 +115,7 @@ def from_row(cls, row: Dict[str, str], road_network: RoadNetwork, environment: E
                     mechatronics_id=mechatronics_id,
                     energy=energy,
                     position=start_position,
-                    vehicle_state=Idle(vehicle_id),
+                    vehicle_state=Idle.build(vehicle_id),
                     driver_state=driver_state,
                     total_seats=available_seats,
                     # available_seats=available_seats
diff --git a/hive/resources/mock_lobster.py b/hive/resources/mock_lobster.py
index e1a2784c..7508e7fd 100644
--- a/hive/resources/mock_lobster.py
+++ b/hive/resources/mock_lobster.py
@@ -53,7 +53,6 @@
 
 
 class DefaultIds:
-
     @classmethod
     def mock_request_id(cls) -> RequestId:
         return "r0"
@@ -96,19 +95,26 @@ def somewhere_else() -> GeoId:
 
 
 def mock_geojson() -> Dict:
-    return {'type': 'Feature',
-            'properties': {'id': None},
-            'geometry': {'type': 'Polygon',
-                         'coordinates': [[[-105.00029227609865, 39.74962517224048],
-                                          [-104.98738065320869, 39.73994639686878],
-                                          [-104.97341667234025, 39.74000864414065],
-                                          [-104.97337619703339, 39.767951988786585],
-                                          [-104.97511663522859, 39.769196417473],
-                                          [-105.00029227609865, 39.74962517224048]]]}}
+    return {
+        'type': 'Feature',
+        'properties': {
+            'id': None
+        },
+        'geometry': {
+            'type':
+            'Polygon',
+            'coordinates': [[[-105.00029227609865, 39.74962517224048],
+                             [-104.98738065320869, 39.73994639686878],
+                             [-104.97341667234025, 39.74000864414065],
+                             [-104.97337619703339, 39.767951988786585],
+                             [-104.97511663522859, 39.769196417473],
+                             [-105.00029227609865, 39.74962517224048]]]
+        }
+    }
 
 
 def mock_membership():
-    return Membership().from_tuple((DefaultIds.mock_membership_id(),))
+    return Membership().from_tuple((DefaultIds.mock_membership_id(), ))
 
 
 def mock_geofence(geojson: Dict = mock_geojson(), resolution: H3Resolution = 10) -> GeoFence:
@@ -142,13 +148,14 @@ def mock_base(
         road_network: RoadNetwork = mock_network(),
         membership: Membership = Membership(),
 ) -> Base:
-    return Base.build(base_id,
-                      h3.geo_to_h3(lat, lon, h3_res),
-                      road_network,
-                      station_id,
-                      stall_count,
-                      membership,
-                      )
+    return Base.build(
+        base_id,
+        h3.geo_to_h3(lat, lon, h3_res),
+        road_network,
+        station_id,
+        stall_count,
+        membership,
+    )
 
 
 def mock_base_from_geoid(
@@ -176,8 +183,8 @@ def mock_station(
         chargers = immutables.Map({mock_l2_charger_id(): 1, mock_dcfc_charger_id(): 1})
     if on_shift_access_chargers is None:
         on_shift_access_chargers = frozenset(chargers.keys())
-    return Station.build(station_id, h3.geo_to_h3(lat, lon, h3_res), road_network, chargers, on_shift_access_chargers,
-                         membership)
+    return Station.build(station_id, h3.geo_to_h3(lat, lon, h3_res), road_network, chargers,
+                         on_shift_access_chargers, membership)
 
 
 def mock_station_from_geoid(
@@ -194,14 +201,13 @@ def mock_station_from_geoid(
         chargers = immutables.Map(chargers)
     if on_shift_access_chargers is None:
         on_shift_access_chargers = frozenset(chargers.keys())
-    return Station.build(station_id, geoid, road_network, chargers, on_shift_access_chargers, membership)
+    return Station.build(station_id, geoid, road_network, chargers, on_shift_access_chargers,
+                         membership)
 
 
-def mock_rate_structure(
-        base_price: Currency = 2.2,
-        price_per_mile: Currency = 1.6,
-        minimum_price: Currency = 5
-) -> RequestRateStructure:
+def mock_rate_structure(base_price: Currency = 2.2,
+                        price_per_mile: Currency = 1.6,
+                        minimum_price: Currency = 5) -> RequestRateStructure:
     return RequestRateStructure(
         base_price=base_price,
         price_per_mile=price_per_mile,
@@ -209,53 +215,45 @@ def mock_rate_structure(
     )
 
 
-def mock_request(
-        request_id: RequestId = DefaultIds.mock_request_id(),
-        o_lat: float = 39.7539,
-        o_lon: float = -104.974,
-        d_lat: float = 39.7579,
-        d_lon: float = -104.978,
-        h3_res: int = 15,
-        road_network: RoadNetwork = mock_network(),
-        departure_time: SimTime = 0,
-        passengers: int = 1,
-        fleet_id: Optional[MembershipId] = None,
-        allows_pooling: bool = False
-) -> Request:
-    return Request.build(
-        request_id=request_id,
-        origin=h3.geo_to_h3(o_lat, o_lon, h3_res),
-        destination=h3.geo_to_h3(d_lat, d_lon, h3_res),
-        road_network=road_network,
-        departure_time=departure_time,
-        passengers=passengers,
-        fleet_id=fleet_id,
-        allows_pooling=allows_pooling
-    )
-
-
-def mock_request_from_geoids(
-        request_id: RequestId = DefaultIds.mock_request_id(),
-        origin: GeoId = h3.geo_to_h3(39.7539, -104.974, 15),
-        destination: GeoId = h3.geo_to_h3(39.7579, -104.978, 15),
-        road_network: RoadNetwork = mock_network(),
-        departure_time: SimTime = SimTime(0),
-        passengers: int = 1,
-        value: Currency = 0,
-        fleet_id: Optional[MembershipId] = None,
-        allows_pooling: bool = False
-) -> Request:
-    return Request.build(
-        request_id=request_id,
-        origin=origin,
-        destination=destination,
-        road_network=road_network,
-        departure_time=departure_time,
-        passengers=passengers,
-        value=value,
-        fleet_id=fleet_id,
-        allows_pooling=allows_pooling
-    )
+def mock_request(request_id: RequestId = DefaultIds.mock_request_id(),
+                 o_lat: float = 39.7539,
+                 o_lon: float = -104.974,
+                 d_lat: float = 39.7579,
+                 d_lon: float = -104.978,
+                 h3_res: int = 15,
+                 road_network: RoadNetwork = mock_network(),
+                 departure_time: SimTime = 0,
+                 passengers: int = 1,
+                 fleet_id: Optional[MembershipId] = None,
+                 allows_pooling: bool = False) -> Request:
+    return Request.build(request_id=request_id,
+                         origin=h3.geo_to_h3(o_lat, o_lon, h3_res),
+                         destination=h3.geo_to_h3(d_lat, d_lon, h3_res),
+                         road_network=road_network,
+                         departure_time=departure_time,
+                         passengers=passengers,
+                         fleet_id=fleet_id,
+                         allows_pooling=allows_pooling)
+
+
+def mock_request_from_geoids(request_id: RequestId = DefaultIds.mock_request_id(),
+                             origin: GeoId = h3.geo_to_h3(39.7539, -104.974, 15),
+                             destination: GeoId = h3.geo_to_h3(39.7579, -104.978, 15),
+                             road_network: RoadNetwork = mock_network(),
+                             departure_time: SimTime = SimTime(0),
+                             passengers: int = 1,
+                             value: Currency = 0,
+                             fleet_id: Optional[MembershipId] = None,
+                             allows_pooling: bool = False) -> Request:
+    return Request.build(request_id=request_id,
+                         origin=origin,
+                         destination=destination,
+                         road_network=road_network,
+                         departure_time=departure_time,
+                         passengers=passengers,
+                         value=value,
+                         fleet_id=fleet_id,
+                         allows_pooling=allows_pooling)
 
 
 def mock_ev_powertrain(nominal_watt_hour_per_mile) -> TabularPowertrain:
@@ -267,8 +265,8 @@ def mock_ev_powertrain(nominal_watt_hour_per_mile) -> TabularPowertrain:
 
 
 def mock_powercurve(
-        nominal_max_charge_kw=50,
-        battery_capacity_kwh=50,
+    nominal_max_charge_kw=50,
+    battery_capacity_kwh=50,
 ) -> TabularPowercurve:
     powercurve_file = resource_filename("hive.resources.powercurve", "normalized.yaml")
     with Path(powercurve_file).open() as f:
@@ -281,11 +279,11 @@ def mock_powercurve(
 
 
 def mock_bev(
-        battery_capacity_kwh=50,
-        idle_kwh_per_hour=0.8,
-        nominal_watt_hour_per_mile=225,
-        nominal_max_charge_kw=50,
-        charge_taper_cutoff_kw=10,
+    battery_capacity_kwh=50,
+    idle_kwh_per_hour=0.8,
+    nominal_watt_hour_per_mile=225,
+    nominal_max_charge_kw=50,
+    charge_taper_cutoff_kw=10,
 ) -> BEV:
     return BEV(
         mechatronics_id='bev',
@@ -307,9 +305,9 @@ def mock_ice_powertrain(nominal_miles_per_gallon) -> TabularPowertrain:
 
 
 def mock_ice(
-        tank_capacity_gallons=15,
-        idle_gallons_per_hour=0.2,
-        nominal_miles_per_gallon=30,
+    tank_capacity_gallons=15,
+    idle_gallons_per_hour=0.2,
+    nominal_miles_per_gallon=30,
 ) -> ICE:
     # source: https://www.energy.gov/eere/vehicles/
     #   fact-861-february-23-2015-idle-fuel-consumption-selected-gasoline-and-diesel-vehicles
@@ -322,68 +320,62 @@ def mock_ice(
     )
 
 
-def mock_vehicle(
-        vehicle_id: VehicleId = DefaultIds.mock_vehicle_id(),
-        lat: float = 39.7539,
-        lon: float = -104.974,
-        h3_res: int = 15,
-        mechatronics: MechatronicsInterface = mock_bev(),
-        vehicle_state: Optional[VehicleState] = None,
-        soc: Ratio = 1,
-        driver_state: Optional[DriverState] = None,
-        membership: Membership = Membership(),
-        total_seats: int = 999
-) -> Vehicle:
-    v_state = vehicle_state if vehicle_state else Idle(vehicle_id)
+def mock_vehicle(vehicle_id: VehicleId = DefaultIds.mock_vehicle_id(),
+                 lat: float = 39.7539,
+                 lon: float = -104.974,
+                 h3_res: int = 15,
+                 mechatronics: MechatronicsInterface = mock_bev(),
+                 vehicle_state: Optional[VehicleState] = None,
+                 soc: Ratio = 1,
+                 driver_state: Optional[DriverState] = None,
+                 membership: Membership = Membership(),
+                 total_seats: int = 999) -> Vehicle:
+    v_state = vehicle_state if vehicle_state else Idle.build(vehicle_id)
     road_network = mock_network(h3_res)
     initial_energy = mechatronics.initial_energy(soc)
     geoid = h3.geo_to_h3(lat, lon, road_network.sim_h3_resolution)
-    d_state = driver_state if driver_state else AutonomousAvailable(AutonomousDriverAttributes(vehicle_id))
+    d_state = driver_state if driver_state else AutonomousAvailable(
+        AutonomousDriverAttributes(vehicle_id))
     position = road_network.position_from_geoid(geoid)
-    return Vehicle(
-        id=vehicle_id,
-        mechatronics_id=mechatronics.mechatronics_id,
-        energy=initial_energy,
-        position=position,
-        vehicle_state=v_state,
-        driver_state=d_state,
-        membership=membership,
-        total_seats=total_seats
-    )
-
-
-def mock_vehicle_from_geoid(
-        vehicle_id: VehicleId = DefaultIds.mock_vehicle_id(),
-        geoid: GeoId = h3.geo_to_h3(39.7539, -104.974, 15),
-        mechatronics: MechatronicsInterface = mock_bev(),
-        vehicle_state: Optional[VehicleState] = None,
-        soc: Ratio = 1,
-        driver_state: Optional[DriverState] = None,
-        membership: Membership = Membership(),
-        total_seats: int = 999
-) -> Vehicle:
-    state = vehicle_state if vehicle_state else Idle(vehicle_id)
+    return Vehicle(id=vehicle_id,
+                   mechatronics_id=mechatronics.mechatronics_id,
+                   energy=initial_energy,
+                   position=position,
+                   vehicle_state=v_state,
+                   driver_state=d_state,
+                   membership=membership,
+                   total_seats=total_seats)
+
+
+def mock_vehicle_from_geoid(vehicle_id: VehicleId = DefaultIds.mock_vehicle_id(),
+                            geoid: GeoId = h3.geo_to_h3(39.7539, -104.974, 15),
+                            mechatronics: MechatronicsInterface = mock_bev(),
+                            vehicle_state: Optional[VehicleState] = None,
+                            soc: Ratio = 1,
+                            driver_state: Optional[DriverState] = None,
+                            membership: Membership = Membership(),
+                            total_seats: int = 999) -> Vehicle:
+    state = vehicle_state if vehicle_state else Idle.build(vehicle_id)
     initial_energy = mechatronics.initial_energy(soc)
-    d_state = driver_state if driver_state else AutonomousAvailable(AutonomousDriverAttributes(vehicle_id))
+    d_state = driver_state if driver_state else AutonomousAvailable(
+        AutonomousDriverAttributes(vehicle_id))
     position = mock_network().position_from_geoid(geoid)
-    return Vehicle(
-        id=vehicle_id,
-        mechatronics_id=mechatronics.mechatronics_id,
-        energy=initial_energy,
-        position=position,
-        vehicle_state=state,
-        driver_state=d_state,
-        membership=membership,
-        total_seats=total_seats
-    )
+    return Vehicle(id=vehicle_id,
+                   mechatronics_id=mechatronics.mechatronics_id,
+                   energy=initial_energy,
+                   position=position,
+                   vehicle_state=state,
+                   driver_state=d_state,
+                   membership=membership,
+                   total_seats=total_seats)
 
 
 def mock_human_driver(available: bool = True,
                       schedule_id: ScheduleId = DefaultIds.mock_schedule_id(),
                       home_base_id: BaseId = DefaultIds.mock_base_id(),
-                      allows_pooling: bool = True
-                      ):
-    attr = HumanDriverAttributes(DefaultIds.mock_vehicle_id(), schedule_id, home_base_id, allows_pooling)
+                      allows_pooling: bool = True):
+    attr = HumanDriverAttributes(DefaultIds.mock_vehicle_id(), schedule_id, home_base_id,
+                                 allows_pooling)
     state = HumanAvailable(attr) if available else HumanUnavailable(attr)
     return state
 
@@ -414,7 +406,6 @@ def add_or_throw(fn: Callable):
         :return: the updated sim
         ;raises: Exception when an add fails
         """
-
         def _inner(s: SimulationState, to_add):
             error, result = fn(s, to_add)
             if error:
@@ -424,20 +415,22 @@ def _inner(s: SimulationState, to_add):
 
         return _inner
 
-    sim_v = ft.reduce(add_or_throw(simulation_state_ops.add_vehicle), vehicles, sim) if vehicles else sim
-    sim_s = ft.reduce(add_or_throw(simulation_state_ops.add_station), stations, sim_v) if stations else sim_v
+    sim_v = ft.reduce(add_or_throw(simulation_state_ops.add_vehicle), vehicles,
+                      sim) if vehicles else sim
+    sim_s = ft.reduce(add_or_throw(simulation_state_ops.add_station), stations,
+                      sim_v) if stations else sim_v
     sim_b = ft.reduce(add_or_throw(simulation_state_ops.add_base), bases, sim_s) if bases else sim_s
 
     return sim_b
 
 
 def mock_config(
-        start_time: Union[str, int] = 0,
-        end_time: Union[str, int] = 100,
-        timestep_duration_seconds: Seconds = 1,
-        sim_h3_location_resolution: int = 15,
-        sim_h3_search_resolution: int = 9,
-        input: Dict = None,
+    start_time: Union[str, int] = 0,
+    end_time: Union[str, int] = 100,
+    timestep_duration_seconds: Seconds = 1,
+    sim_h3_location_resolution: int = 15,
+    sim_h3_search_resolution: int = 9,
+    input: Dict = None,
 ) -> HiveConfig:
     if not input:
         input = {
@@ -467,17 +460,18 @@ def mock_config(
             "network": {},
             "dispatcher": {}
         })
-    updated_global = conf_without_temp_dir.global_config._replace(output_base_directory=test_output_directory.name)
+    updated_global = conf_without_temp_dir.global_config._replace(
+        output_base_directory=test_output_directory.name)
 
     return conf_without_temp_dir._replace(global_config=updated_global)
 
 
 def mock_env(
-        config: HiveConfig = mock_config(),
-        mechatronics: Optional[Dict[MechatronicsId, MechatronicsInterface]] = None,
-        chargers: Optional[Dict[ChargerId, Charger]] = None,
-        schedules: Optional[Dict[ScheduleId, Callable[['SimulationState', VehicleId], bool]]] = None,
-        fleet_ids: FrozenSet[MembershipId] = frozenset([DefaultIds.mock_membership_id()])
+    config: HiveConfig = mock_config(),
+    mechatronics: Optional[Dict[MechatronicsId, MechatronicsInterface]] = None,
+    chargers: Optional[Dict[ChargerId, Charger]] = None,
+    schedules: Optional[Dict[ScheduleId, Callable[['SimulationState', VehicleId], bool]]] = None,
+    fleet_ids: FrozenSet[MembershipId] = frozenset([DefaultIds.mock_membership_id()])
 ) -> Environment:
     if mechatronics is None:
         mechatronics = {
@@ -492,21 +486,18 @@ def mock_env(
         }
 
     if schedules is None:
+
         def always_on_schedule(a, b):
             return True
 
-        schedules = {
-            DefaultIds.mock_schedule_id(): always_on_schedule
-        }
+        schedules = {DefaultIds.mock_schedule_id(): always_on_schedule}
 
-    initial_env = Environment(
-        config=config,
-        reporter=mock_reporter(),
-        mechatronics=immutables.Map(mechatronics),
-        chargers=immutables.Map(chargers),
-        schedules=immutables.Map(schedules),
-        fleet_ids=fleet_ids
-    )
+    initial_env = Environment(config=config,
+                              reporter=mock_reporter(),
+                              mechatronics=immutables.Map(mechatronics),
+                              chargers=immutables.Map(chargers),
+                              schedules=immutables.Map(schedules),
+                              fleet_ids=fleet_ids)
 
     return initial_env
 
@@ -528,13 +519,11 @@ def close(self):
     return MockReporter()
 
 
-def mock_haversine_zigzag_route(
-        n: int = 3,
-        lat_step_size: int = 5,
-        lon_step_size: int = 5,
-        speed_kmph: Kmph = 40,
-        h3_res: int = 15
-) -> Route:
+def mock_haversine_zigzag_route(n: int = 3,
+                                lat_step_size: int = 5,
+                                lon_step_size: int = 5,
+                                speed_kmph: Kmph = 40,
+                                h3_res: int = 15) -> Route:
     """
     "zigs" lat steps and "zags" lon steps. all your base belong to us.
 
@@ -549,7 +538,6 @@ def mock_haversine_zigzag_route(
     :param h3_res: h3 resolution
     :return: a route
     """
-
     def step(acc: Tuple[Link, ...], i: int) -> Tuple[Link, ...]:
         """
         constructs the next Link
@@ -559,8 +547,10 @@ def step(acc: Tuple[Link, ...], i: int) -> Tuple[Link, ...]:
         :param i: what link we are making
         :return: the route with another link added
         """
-        lat_pos, lon_pos = math.floor(i / 2.0) * lat_step_size, math.floor((i + 1) / 2.0) * lon_step_size
-        lat_dest, lon_dest = math.floor((i + 1) / 2.0) * lat_step_size, math.floor((i + 2) / 2.0) * lon_step_size
+        lat_pos, lon_pos = math.floor(i / 2.0) * lat_step_size, math.floor(
+            (i + 1) / 2.0) * lon_step_size
+        lat_dest, lon_dest = math.floor((i + 1) / 2.0) * lat_step_size, math.floor(
+            (i + 2) / 2.0) * lon_step_size
         start = h3.geo_to_h3(lat_pos, lon_pos, h3_res)
         end = h3.geo_to_h3(lat_dest, lon_dest, h3_res)
         distance_km = H3Ops.great_circle_distance(start, end)
@@ -571,15 +561,12 @@ def step(acc: Tuple[Link, ...], i: int) -> Tuple[Link, ...]:
             distance_km=distance_km,
             speed_kmph=speed_kmph,
         )
-        return acc + (link,)
+        return acc + (link, )
 
     return ft.reduce(step, range(0, n), ())
 
 
-def mock_route_from_geoids(
-        src: GeoId,
-        dst: GeoId,
-        speed_kmph: Kmph = 1) -> Tuple[Link, ...]:
+def mock_route_from_geoids(src: GeoId, dst: GeoId, speed_kmph: Kmph = 1) -> Tuple[Link, ...]:
     link = Link.build("1", src, dst, speed_kmph=speed_kmph)
     return link,
 
@@ -591,19 +578,23 @@ def mock_graph_links(h3_res: int = 15, speed_kmph: Kmph = 1) -> Dict[str, Link]:
     """
 
     links = {
-        "1": Link.build("1",
-                        h3.geo_to_h3(37, 122, h3_res),
-                        h3.geo_to_h3(37.008994, 122, h3_res),
-                        speed_kmph=speed_kmph,
-                        ),
-        "2": Link.build("2",
-                        h3.geo_to_h3(37.008994, 122, h3_res),
-                        h3.geo_to_h3(37.017998, 122, h3_res),
-                        speed_kmph=speed_kmph),
-        "3": Link.build("3",
-                        h3.geo_to_h3(37.017998, 122, h3_res),
-                        h3.geo_to_h3(37.026992, 122, h3_res),
-                        speed_kmph=speed_kmph),
+        "1":
+        Link.build(
+            "1",
+            h3.geo_to_h3(37, 122, h3_res),
+            h3.geo_to_h3(37.008994, 122, h3_res),
+            speed_kmph=speed_kmph,
+        ),
+        "2":
+        Link.build("2",
+                   h3.geo_to_h3(37.008994, 122, h3_res),
+                   h3.geo_to_h3(37.017998, 122, h3_res),
+                   speed_kmph=speed_kmph),
+        "3":
+        Link.build("3",
+                   h3.geo_to_h3(37.017998, 122, h3_res),
+                   h3.geo_to_h3(37.026992, 122, h3_res),
+                   speed_kmph=speed_kmph),
     }
 
     return links
@@ -616,9 +607,7 @@ def mock_route(h3_res: int = 15, speed_kmph: Kmph = 1) -> Tuple[Link, ...]:
 def mock_forecaster(forecast: int = 1) -> ForecasterInterface:
     class MockForecaster(NamedTuple, ForecasterInterface):
         def generate_forecast(
-                self,
-                simulation_state: SimulationState
-        ) -> Tuple[ForecasterInterface, Forecast]:
+                self, simulation_state: SimulationState) -> Tuple[ForecasterInterface, Forecast]:
             f = Forecast(type=ForecastType.DEMAND, value=forecast)
             return self, f
 
@@ -626,16 +615,16 @@ def generate_forecast(
 
 
 def mock_instruction_generators(
-        config: HiveConfig = mock_config(),
-) -> Tuple[InstructionGenerator, ...]:
+        config: HiveConfig = mock_config(), ) -> Tuple[InstructionGenerator, ...]:
     return (
         ChargingFleetManager(config.dispatcher),
         Dispatcher(config.dispatcher),
     )
 
 
-def mock_update(config: Optional[HiveConfig] = None,
-                instruction_generators: Optional[Tuple[InstructionGenerator, ...]] = None) -> Update:
+def mock_update(
+        config: Optional[HiveConfig] = None,
+        instruction_generators: Optional[Tuple[InstructionGenerator, ...]] = None) -> Update:
     if config and instruction_generators:
         return Update.build(config, instruction_generators)
     elif config:
@@ -663,19 +652,31 @@ def mock_dcfc_charger_id():
 
 
 def mock_l1_charger():
-    return Charger(mock_l1_charger_id(), energy_type=EnergyType.ELECTRIC, rate=3.3, units='kilowatts')
+    return Charger(mock_l1_charger_id(),
+                   energy_type=EnergyType.ELECTRIC,
+                   rate=3.3,
+                   units='kilowatts')
 
 
 def mock_l2_charger():
-    return Charger(mock_l2_charger_id(), energy_type=EnergyType.ELECTRIC, rate=7.2, units='kilowatts')
+    return Charger(mock_l2_charger_id(),
+                   energy_type=EnergyType.ELECTRIC,
+                   rate=7.2,
+                   units='kilowatts')
 
 
 def mock_dcfc_charger():
-    return Charger(mock_dcfc_charger_id(), energy_type=EnergyType.ELECTRIC, rate=50.0, units='kilowatts')
+    return Charger(mock_dcfc_charger_id(),
+                   energy_type=EnergyType.ELECTRIC,
+                   rate=50.0,
+                   units='kilowatts')
 
 
 def mock_gasoline_pump():
     gal_per_minute = 10  # source: https://en.wikipedia.org/wiki/Gasoline_pump
     gal_per_second = gal_per_minute / 60
 
-    return Charger("gas_pump", energy_type=EnergyType.GASOLINE, rate=gal_per_second, units='gal_gasoline')
+    return Charger("gas_pump",
+                   energy_type=EnergyType.GASOLINE,
+                   rate=gal_per_second,
+                   units='gal_gasoline')
diff --git a/hive/state/vehicle_state/charge_queueing.py b/hive/state/vehicle_state/charge_queueing.py
index 424ac464..ea8cdd71 100644
--- a/hive/state/vehicle_state/charge_queueing.py
+++ b/hive/state/vehicle_state/charge_queueing.py
@@ -1,15 +1,21 @@
+from __future__ import annotations
+
 import logging
-from typing import NamedTuple, Tuple, Optional
+from typing import NamedTuple, Tuple, Optional, TYPE_CHECKING
+from uuid import uuid4
 
 from hive.model.sim_time import SimTime
 from hive.runner.environment import Environment
 from hive.state.simulation_state import simulation_state_ops
 from hive.state.vehicle_state.charging_station import ChargingStation
-from hive.state.vehicle_state.vehicle_state import VehicleState
+from hive.state.vehicle_state.vehicle_state import VehicleState, VehicleStateInstanceId
 from hive.state.vehicle_state.vehicle_state_type import VehicleStateType
 from hive.util.exception import SimulationStateError
 from hive.util.typealiases import VehicleId, StationId, ChargerId
 
+if TYPE_CHECKING:
+    from hive.state.simulation_state.simulation_state import SimulationState
+
 log = logging.getLogger(__name__)
 
 
@@ -24,22 +30,35 @@ class ChargeQueueing(NamedTuple, VehicleState):
     charger_id: ChargerId
     enqueue_time: SimTime
 
+    instance_id: VehicleStateInstanceId
+
+    @classmethod
+    def build(cls, vehicle_id: VehicleId, station_id: StationId, charger_id: ChargerId,
+              enqueue_time: SimTime) -> ChargeQueueing:
+        return ChargeQueueing(vehicle_id=vehicle_id,
+                              station_id=station_id,
+                              charger_id=charger_id,
+                              enqueue_time=enqueue_time,
+                              instance_id=uuid4())
+
     @property
     def vehicle_state_type(cls) -> VehicleStateType:
         return VehicleStateType.CHARGE_QUEUEING
 
-    def enter(self, sim: 'SimulationState', env: 'Environment') -> Tuple[
-        Optional[Exception], Optional['SimulationState']]:
+    def enter(self, sim: SimulationState,
+              env: 'Environment') -> Tuple[Optional[Exception], Optional[SimulationState]]:
+        """
+        entering a charge queueing state requires being at that station
+
+        :param sim: the simulation state
+        :param env: the simulation environment
+        :return: an exception due to failure or an optional updated simulation
         """
-                entering a charge queueing state requires being at that station
 
-                :param sim: the simulation state
-                :param env: the simulation environment
-                :return: an exception due to failure or an optional updated simulation
-                """
         vehicle = sim.vehicles.get(self.vehicle_id)
         station = sim.stations.get(self.station_id)
-        has_available_charger = station.available_chargers.get(self.charger_id, 0) > 0 if station else False
+        has_available_charger = station.available_chargers.get(self.charger_id,
+                                                               0) > 0 if station else False
         context = f"vehicle {self.vehicle_id} entering queueing at station {self.station_id}"
         if not vehicle:
             return SimulationStateError(f"vehicle not found; context: {context}"), None
@@ -64,15 +83,12 @@ def enter(self, sim: 'SimulationState', env: 'Environment') -> Tuple[
             else:
                 return VehicleState.apply_new_vehicle_state(updated_sim, self.vehicle_id, self)
 
-    def update(self, sim: 'SimulationState', env: 'Environment') -> Tuple[
-        Optional[Exception], Optional['SimulationState']]:
+    def update(self, sim: SimulationState,
+               env: 'Environment') -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return VehicleState.default_update(sim, env, self)
 
-    def exit(self,
-             next_state: VehicleState,
-             sim: 'SimulationState',
-             env: 'Environment'
-             ) -> Tuple[Optional[Exception], Optional['SimulationState']]:
+    def exit(self, next_state: VehicleState, sim: SimulationState,
+             env: 'Environment') -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         remove agent from queue before exiting this state
 
@@ -95,7 +111,7 @@ def exit(self,
                 return response, None
             return None, updated_sim
 
-    def _has_reached_terminal_state_condition(self, sim: 'SimulationState', env: Environment) -> bool:
+    def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Environment) -> bool:
         """
         vehicle has reached a terminal state if the station disappeared
         or if it has at least one charger_id of the correct type
@@ -110,9 +126,9 @@ def _has_reached_terminal_state_condition(self, sim: 'SimulationState', env: Env
         else:
             return station.available_chargers.get(self.charger_id, 0) > 0
 
-    def _default_terminal_state(self,
-                                sim: 'SimulationState',
-                                env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
+    def _default_terminal_state(
+            self, sim: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
         """
         gets the default terminal state for this state which should be transitioned to
         once it reaches the end of the current task.
@@ -122,7 +138,8 @@ def _default_terminal_state(self,
         """
         vehicle = sim.vehicles.get(self.vehicle_id)
         station = sim.stations.get(self.station_id)
-        has_no_charger = station.available_chargers.get(self.charger_id, 0) == 0 if station else False
+        has_no_charger = station.available_chargers.get(self.charger_id,
+                                                        0) == 0 if station else False
         context = f"vehicle {self.vehicle_id} entering default terminal state for charge queueing at station {self.station_id}"
         if not vehicle:
             return SimulationStateError(f"vehicle not found; context: {context}"), None
@@ -131,11 +148,11 @@ def _default_terminal_state(self,
         elif has_no_charger:
             return SimulationStateError(f"no charger_id found; context: {context}"), None
         else:
-            next_state = ChargingStation(self.vehicle_id, self.station_id, self.charger_id)
+            next_state = ChargingStation.build(self.vehicle_id, self.station_id, self.charger_id)
             return next_state
 
-    def _perform_update(self, sim: 'SimulationState', env: Environment) -> Tuple[
-        Optional[Exception], Optional['SimulationState']]:
+    def _perform_update(self, sim: SimulationState,
+                        env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         similarly to the idle state, we incur an idling penalty here
 
@@ -146,11 +163,14 @@ def _perform_update(self, sim: 'SimulationState', env: Environment) -> Tuple[
         vehicle = sim.vehicles.get(self.vehicle_id)
         context = f"vehicle {self.vehicle_id} performing update for charge queueing at station {self.station_id}"
         if not vehicle:
-            return SimulationStateError(f"vehicle {self.vehicle_id} not found; context: {context}"), None
+            return SimulationStateError(
+                f"vehicle {self.vehicle_id} not found; context: {context}"), None
         else:
             mechatronics = env.mechatronics.get(vehicle.mechatronics_id)
             if not mechatronics:
-                return SimulationStateError(f"cannot find {vehicle.mechatronics_id} in environment; context: {context}"), None
+                return SimulationStateError(
+                    f"cannot find {vehicle.mechatronics_id} in environment; context: {context}"
+                ), None
             less_energy_vehicle = mechatronics.idle(vehicle, sim.sim_timestep_duration_seconds)
 
             return simulation_state_ops.modify_vehicle(sim, less_energy_vehicle)
diff --git a/hive/state/vehicle_state/charging_base.py b/hive/state/vehicle_state/charging_base.py
index ea495cbc..0affaf92 100644
--- a/hive/state/vehicle_state/charging_base.py
+++ b/hive/state/vehicle_state/charging_base.py
@@ -2,10 +2,11 @@
 
 import logging
 from typing import Tuple, Optional, NamedTuple, TYPE_CHECKING
+from uuid import uuid4
 
 from hive.state.simulation_state import simulation_state_ops
 from hive.state.vehicle_state.reserve_base import ReserveBase
-from hive.state.vehicle_state.vehicle_state import VehicleState
+from hive.state.vehicle_state.vehicle_state import VehicleState, VehicleStateInstanceId
 from hive.state.vehicle_state.vehicle_state_ops import charge
 from hive.state.vehicle_state.vehicle_state_type import VehicleStateType
 from hive.util.exception import SimulationStateError
@@ -27,13 +28,18 @@ class ChargingBase(NamedTuple, VehicleState):
     base_id: BaseId
     charger_id: ChargerId
 
+    instance_id: VehicleStateInstanceId
+
+    @classmethod
+    def build(cls, vehicle_id: VehicleId, base_id: BaseId, charger_id: ChargerId) -> ChargingBase:
+        return ChargingBase(vehicle_id, base_id, charger_id, instance_id=uuid4())
+
     @property
     def vehicle_state_type(cls) -> VehicleStateType:
         return VehicleStateType.CHARGING_BASE
 
-    def enter(
-            self, sim: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def enter(self, sim: SimulationState,
+              env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         entering a charge event requires attaining a charger_id from the station situated at the base
 
@@ -41,6 +47,7 @@ def enter(
         :param env: the simulation environment
         :return: an exception due to failure or an optional updated simulation, or (None, None) if not possible
         """
+
         base = sim.bases.get(self.base_id)
         vehicle = sim.vehicles.get(self.vehicle_id)
         context = f"vehicle {self.vehicle_id} entering charging base state at base {self.base_id} with charger {self.charger_id}"
@@ -70,7 +77,8 @@ def enter(
             else:
                 err1, sim2 = simulation_state_ops.modify_base(sim, updated_base)
                 if err1:
-                    response = SimulationStateError(f"failure during ChargingBase.enter for vehicle {self.vehicle_id}")
+                    response = SimulationStateError(
+                        f"failure during ChargingBase.enter for vehicle {self.vehicle_id}")
                     response.__cause__ = err1
                     return response, None
                 else:
@@ -86,29 +94,23 @@ def enter(
                             )
                             return None, None
                         else:
-                            err2, sim3 = simulation_state_ops.modify_station(
-                                sim2, updated_station
-                            )
+                            err2, sim3 = simulation_state_ops.modify_station(sim2, updated_station)
                             if err2:
                                 response = SimulationStateError(
-                                    f"failure during ChargingBase.enter for vehicle {self.vehicle_id}")
+                                    f"failure during ChargingBase.enter for vehicle {self.vehicle_id}"
+                                )
                                 response.__cause__ = err2
                                 return response, None
                             else:
                                 return VehicleState.apply_new_vehicle_state(
-                                    sim3, self.vehicle_id, self
-                                )
+                                    sim3, self.vehicle_id, self)
 
-    def update(
-            self, sim: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def update(self, sim: SimulationState,
+               env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return VehicleState.default_update(sim, env, self)
 
-    def exit(self,
-             next_state: VehicleState,
-             sim: SimulationState,
-             env: Environment
-             ) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def exit(self, next_state: VehicleState, sim: SimulationState,
+             env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         exiting a charge event requires returning the charger_id to the station at the base
 
@@ -131,13 +133,15 @@ def exit(self,
         else:
             err1, updated_base = base.return_stall()
             if err1:
-                response = SimulationStateError(f"failure during ChargingBase.exit for vehicle {self.vehicle_id}")
+                response = SimulationStateError(
+                    f"failure during ChargingBase.exit for vehicle {self.vehicle_id}")
                 response.__cause__ = err1
                 return response, None
             else:
                 err2, sim2 = simulation_state_ops.modify_base(sim, updated_base)
                 if err2:
-                    response = SimulationStateError(f"failure during ChargingBase.exit for vehicle {self.vehicle_id}")
+                    response = SimulationStateError(
+                        f"failure during ChargingBase.exit for vehicle {self.vehicle_id}")
                     response.__cause__ = err2
                     return response, None
                 else:
@@ -149,9 +153,7 @@ def exit(self,
                         return response, None
                     return simulation_state_ops.modify_station(sim2, updated_station)
 
-    def _has_reached_terminal_state_condition(
-            self, sim: SimulationState, env: Environment
-    ) -> bool:
+    def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Environment) -> bool:
         """
         test if charging is finished
 
@@ -166,8 +168,9 @@ def _has_reached_terminal_state_condition(
         else:
             return mechatronics.is_full(vehicle)
 
-    def _default_terminal_state(self, sim: SimulationState, env: Environment) -> Tuple[
-        Optional[Exception], Optional[VehicleState]]:
+    def _default_terminal_state(
+            self, sim: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
         """
         give the default state to transition to after having met a terminal condition
 
@@ -175,12 +178,11 @@ def _default_terminal_state(self, sim: SimulationState, env: Environment) -> Tup
         :param env: the simulation environment
         :return: an exception due to failure or the next_state after finishing a task
         """
-        next_state = ReserveBase(self.vehicle_id, self.base_id)
+        next_state = ReserveBase.build(self.vehicle_id, self.base_id)
         return None, next_state
 
-    def _perform_update(
-            self, sim: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def _perform_update(self, sim: SimulationState,
+                        env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         apply any effects due to a vehicle being advanced one discrete time unit in this VehicleState
 
diff --git a/hive/state/vehicle_state/charging_station.py b/hive/state/vehicle_state/charging_station.py
index 9c781751..5c11a8c9 100644
--- a/hive/state/vehicle_state/charging_station.py
+++ b/hive/state/vehicle_state/charging_station.py
@@ -1,15 +1,22 @@
+from __future__ import annotations
+
 import logging
-from typing import Tuple, Optional, NamedTuple
+from typing import Tuple, Optional, NamedTuple, TYPE_CHECKING
+import uuid
 
 from hive.runner.environment import Environment
 from hive.state.simulation_state import simulation_state_ops
 from hive.state.vehicle_state.idle import Idle
-from hive.state.vehicle_state.vehicle_state import VehicleState
+from hive.state.vehicle_state.vehicle_state import VehicleState, VehicleStateInstanceId
 from hive.state.vehicle_state.vehicle_state_ops import charge
 from hive.state.vehicle_state.vehicle_state_type import VehicleStateType
 from hive.util.exception import SimulationStateError
 from hive.util.typealiases import StationId, VehicleId, ChargerId
 
+if TYPE_CHECKING:
+    from hive.state.simulation_state.simulation_state import SimulationState
+    from hive.runner.environment import Environment
+
 log = logging.getLogger(__name__)
 
 
@@ -21,12 +28,30 @@ class ChargingStation(NamedTuple, VehicleState):
     station_id: StationId
     charger_id: ChargerId
 
+    instance_id: VehicleStateInstanceId
+
+    @classmethod
+    def build(cls, vehicle_id: VehicleId, station_id: StationId,
+              charger_id: ChargerId) -> ChargingStation:
+        """
+        build a charging station state
+
+        :param vehicle_id: the vehicle id
+        :param station_id: the station id
+        :param charger_id: the charger id
+
+        :return: a charging station state 
+        """
+        return cls(vehicle_id=vehicle_id,
+                   station_id=station_id,
+                   charger_id=charger_id,
+                   instance_id=uuid.uuid4())
+
     @property
     def vehicle_state_type(cls) -> VehicleStateType:
         return VehicleStateType.CHARGING_STATION
 
-    def enter(self,
-              sim: 'SimulationState',
+    def enter(self, sim: 'SimulationState',
               env: Environment) -> Tuple[Optional[Exception], Optional['SimulationState']]:
         """
         entering a charge event requires attaining a charger_id from the station
@@ -35,6 +60,9 @@ def enter(self,
         :param env: the simulation environment
         :return: an exception due to failure or an optional updated simulation
         """
+
+        self = self._replace(instance_id=uuid.uuid4())
+
         # ok, we want to enter a charging state.
         # we attempt to claim a charger_id from the station of this self.charger_id type
         # what if we can't? is that an Exception, or, is that simply rejected?
@@ -70,15 +98,12 @@ def enter(self,
                 else:
                     return VehicleState.apply_new_vehicle_state(updated_sim, self.vehicle_id, self)
 
-    def update(self, sim: 'SimulationState', env: Environment) -> Tuple[
-        Optional[Exception], Optional['SimulationState']]:
+    def update(self, sim: 'SimulationState',
+               env: Environment) -> Tuple[Optional[Exception], Optional['SimulationState']]:
         return VehicleState.default_update(sim, env, self)
 
-    def exit(self,
-             next_state: VehicleState,
-             sim: 'SimulationState',
-             env: 'Environment'
-             ) -> Tuple[Optional[Exception], Optional['SimulationState']]:
+    def exit(self, next_state: VehicleState, sim: 'SimulationState',
+             env: 'Environment') -> Tuple[Optional[Exception], Optional['SimulationState']]:
         """
         exiting a charge event requires returning the charger_id to the station
 
@@ -103,8 +128,7 @@ def exit(self,
                 return response, None
             return simulation_state_ops.modify_station(sim, updated_station)
 
-    def _has_reached_terminal_state_condition(self,
-                                              sim: 'SimulationState',
+    def _has_reached_terminal_state_condition(self, sim: 'SimulationState',
                                               env: Environment) -> bool:
         """
         test if charging is finished
@@ -121,8 +145,8 @@ def _has_reached_terminal_state_condition(self,
             return mechatronics.is_full(vehicle)
 
     def _default_terminal_state(
-        self, sim: 'SimulationState', env: Environment
-    ) -> Tuple[Optional[Exception], Optional[VehicleState]]:
+            self, sim: 'SimulationState',
+            env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
         """
         give the default state to transition to after having met a terminal condition
 
@@ -130,12 +154,12 @@ def _default_terminal_state(
         :param env: the simulation environment
         :return: an exception due to failure or the next_state after finishing a task
         """
-        next_state = Idle(self.vehicle_id)
+        next_state = Idle.build(self.vehicle_id)
         return None, next_state
 
-    def _perform_update(self,
-                        sim: 'SimulationState',
-                        env: Environment) -> Tuple[Optional[Exception], Optional['SimulationState']]:
+    def _perform_update(
+            self, sim: 'SimulationState',
+            env: Environment) -> Tuple[Optional[Exception], Optional['SimulationState']]:
         """
         apply any effects due to a vehicle being advanced one discrete time unit in this VehicleState
 
diff --git a/hive/state/vehicle_state/dispatch_base.py b/hive/state/vehicle_state/dispatch_base.py
index c1991837..c5f10a6a 100644
--- a/hive/state/vehicle_state/dispatch_base.py
+++ b/hive/state/vehicle_state/dispatch_base.py
@@ -1,11 +1,12 @@
 from __future__ import annotations
 import logging
 from typing import NamedTuple, Tuple, Optional, TYPE_CHECKING
+from uuid import uuid4
 
 from hive.model.roadnetwork.route import Route, route_cooresponds_with_entities
 from hive.runner.environment import Environment
 from hive.state.simulation_state import simulation_state_ops
-from hive.state.vehicle_state.vehicle_state import VehicleState
+from hive.state.vehicle_state.vehicle_state import VehicleState, VehicleStateInstanceId
 from hive.state.vehicle_state import vehicle_state_ops
 from hive.state.vehicle_state.idle import Idle
 from hive.state.vehicle_state.reserve_base import ReserveBase
@@ -24,17 +25,27 @@ class DispatchBase(NamedTuple, VehicleState):
     base_id: BaseId
     route: Route
 
+    instance_id: VehicleStateInstanceId
+
+    @classmethod
+    def build(cls, vehicle_id: VehicleId, base_id: BaseId, route: Route) -> DispatchBase:
+        return cls(vehicle_id=vehicle_id, base_id=base_id, route=route, instance_id=uuid4())
+
     @property
     def vehicle_state_type(cls) -> VehicleStateType:
         return VehicleStateType.DISPATCH_BASE
 
-    def update(self, sim: SimulationState, env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def update(self, sim: SimulationState,
+               env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return VehicleState.default_update(sim, env, self)
 
-    def enter(self, sim: SimulationState, env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def enter(self, sim: SimulationState,
+              env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+
         base = sim.bases.get(self.base_id)
         vehicle = sim.vehicles.get(self.vehicle_id)
-        is_valid = route_cooresponds_with_entities(self.route, vehicle.position, base.position) if vehicle and base else False
+        is_valid = route_cooresponds_with_entities(self.route, vehicle.position,
+                                                   base.position) if vehicle and base else False
         context = f"vehicle {self.vehicle_id} entering dispatch base state at base {self.base_id}"
         if not base:
             msg = f"base not found; context: {context}"
@@ -51,11 +62,8 @@ def enter(self, sim: SimulationState, env: Environment) -> Tuple[Optional[Except
             result = VehicleState.apply_new_vehicle_state(sim, self.vehicle_id, self)
             return result
 
-    def exit(self,
-             next_state: VehicleState,
-             sim: SimulationState,
-             env: Environment
-             ) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def exit(self, next_state: VehicleState, sim: SimulationState,
+             env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return None, sim
 
     def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Environment) -> bool:
@@ -69,8 +77,8 @@ def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Envir
         return len(self.route) == 0
 
     def _default_terminal_state(
-        self, sim: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[VehicleState]]:
+            self, sim: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
         """
         give the default state to transition to after having met a terminal condition
 
@@ -90,13 +98,12 @@ def _default_terminal_state(
             return SimulationStateError(message), None
         else:
             if base.available_stalls > 0:
-                next_state = ReserveBase(self.vehicle_id, self.base_id)
+                next_state = ReserveBase.build(self.vehicle_id, self.base_id)
             else:
-                next_state = Idle(self.vehicle_id)
+                next_state = Idle.build(self.vehicle_id)
             return None, next_state
 
-    def _perform_update(self,
-                        sim: SimulationState,
+    def _perform_update(self, sim: SimulationState,
                         env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         take a step along the route to the base
diff --git a/hive/state/vehicle_state/dispatch_ops.py b/hive/state/vehicle_state/dispatch_ops.py
index ad4f55a5..ebf30fd9 100644
--- a/hive/state/vehicle_state/dispatch_ops.py
+++ b/hive/state/vehicle_state/dispatch_ops.py
@@ -12,7 +12,8 @@
 from hive.util import VehicleId, RequestId, iterators, SimulationStateError, TupleOps
 
 
-def requests_exist_and_match_membership(sim: SimulationState, vehicle: Vehicle, requests: Tuple[RequestId, ...]) -> bool:
+def requests_exist_and_match_membership(sim: SimulationState, vehicle: Vehicle,
+                                        requests: Tuple[RequestId, ...]) -> bool:
     """
     confirm that all requests currently exist in the system
 
@@ -20,23 +21,24 @@ def requests_exist_and_match_membership(sim: SimulationState, vehicle: Vehicle,
     :param requests: the requests
     :return: True if all requests currently exist
     """
-
     def exists_and_match_membership(req_id):
         req = sim.requests.get(req_id)
         if req is None:
             return False
         else:
-            membership_ok = req.membership.grant_access_to_membership(vehicle.membership) if req else False
+            membership_ok = req.membership.grant_access_to_membership(
+                vehicle.membership) if req else False
             return membership_ok
 
     all_exist = all(map(exists_and_match_membership, requests))
     return all_exist
 
 
-def modify_vehicle_assignment(sim: SimulationState,
-                              vehicle_id: VehicleId,
-                              requests: Tuple[RequestId, ...],
-                              unassign: bool = False) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+def modify_vehicle_assignment(
+        sim: SimulationState,
+        vehicle_id: VehicleId,
+        requests: Tuple[RequestId, ...],
+        unassign: bool = False) -> Tuple[Optional[Exception], Optional[SimulationState]]:
     """
     assign/un-assign this vehicle to each request. if a request does not exist, it is ignored.
 
@@ -46,7 +48,6 @@ def modify_vehicle_assignment(sim: SimulationState,
     :param assign if true, un-assign the request, otherwise, assign
     :return: either an error, or, the updated simulation
     """
-
     def _modify(acc, req_id):
         err, sim = acc
         req = sim.requests.get(req_id) if sim is not None else None
@@ -65,8 +66,8 @@ def _modify(acc, req_id):
     return result
 
 
-def create_routes(sim: SimulationState,
-                  trip_plan: Tuple[Tuple[RequestId, TripPhase], ...]) -> Tuple[Route, ...]:
+def create_routes(sim: SimulationState, trip_plan: Tuple[Tuple[RequestId, TripPhase],
+                                                         ...]) -> Tuple[Route, ...]:
     """
     creates routes between each phase of a trip plan. requires at least 2
     entries in the trip plan, which in the base case, would be a PICKUP followed
@@ -97,7 +98,8 @@ def route_between(pair):
         return routes
 
 
-def get_position_for_phase(sim: SimulationState, req_id: RequestId, trip_phase: TripPhase) -> Optional[EntityPosition]:
+def get_position_for_phase(sim: SimulationState, req_id: RequestId,
+                           trip_phase: TripPhase) -> Optional[EntityPosition]:
     """
     gets the EntityPosition for the request based on the trip phase
 
@@ -117,10 +119,9 @@ def get_position_for_phase(sim: SimulationState, req_id: RequestId, trip_phase:
         return None
 
 
-def begin_or_replan_dispatch_pooling_state(sim: SimulationState,
-                                            vehicle_id: VehicleId,
-                                            trip_plan: Tuple[Tuple[RequestId, TripPhase], ...]
-                                            ) -> Tuple[Optional[Exception], Optional[DispatchPoolingTrip]]:
+def begin_or_replan_dispatch_pooling_state(
+    sim: SimulationState, vehicle_id: VehicleId, trip_plan: Tuple[Tuple[RequestId, TripPhase], ...]
+) -> Tuple[Optional[Exception], Optional[DispatchPoolingTrip]]:
     """
     create a DispatchPoolingTrip state. if the vehicle is currently in a ServicingPoolingTrip
     state, then carry over that state information here as well in the construction of the new state.
@@ -134,7 +135,8 @@ def begin_or_replan_dispatch_pooling_state(sim: SimulationState,
     first_trip = TupleOps.head_optional(trip_plan)
 
     if vehicle is None:
-        error = SimulationStateError(f"attempting to dispatch vehicle {vehicle_id} that does not exist")
+        error = SimulationStateError(
+            f"attempting to dispatch vehicle {vehicle_id} that does not exist")
         return error, None
     elif first_trip is None:
         error = SimulationStateError(f"attempting to dispatch pooling trip with empty trip plan")
@@ -143,7 +145,9 @@ def begin_or_replan_dispatch_pooling_state(sim: SimulationState,
         first_req_id, first_phase = first_trip
         first_req_pos = get_position_for_phase(sim, first_req_id, first_phase)
         if first_req_pos is None:
-            error = SimulationStateError(f"attempting to dispatch pooling trip to request {first_req_id} that does not exist in the sim")
+            error = SimulationStateError(
+                f"attempting to dispatch pooling trip to request {first_req_id} that does not exist in the sim"
+            )
             return error, None
         else:
             route = sim.road_network.route(vehicle.position, first_req_pos)
@@ -153,16 +157,12 @@ def begin_or_replan_dispatch_pooling_state(sim: SimulationState,
             #  - maybe we need a TripPhase.REPLANNING for the leg that is the interruption?
             if vehicle.vehicle_state.vehicle_state_type == VehicleStateType.SERVICING_POOLING_TRIP:
                 # already servicing pooling - copy over passenger state
-                next_state = DispatchPoolingTrip(
-                    vehicle_id,
-                    trip_plan,
-                    route,
-                    vehicle_state.boarded_requests,
-                    vehicle_state.departure_times,
-                    vehicle_state.num_passengers
-                )
+                next_state = DispatchPoolingTrip.build(vehicle_id, trip_plan, route,
+                                                       vehicle_state.boarded_requests,
+                                                       vehicle_state.departure_times,
+                                                       vehicle_state.num_passengers)
                 return None, next_state
             else:
                 # not already servicing pooling - just dispatch, don't carry over passenger state
-                next_state = DispatchPoolingTrip(vehicle_id, trip_plan, route)
+                next_state = DispatchPoolingTrip.build(vehicle_id, trip_plan, route)
                 return None, next_state
\ No newline at end of file
diff --git a/hive/state/vehicle_state/dispatch_pooling_trip.py b/hive/state/vehicle_state/dispatch_pooling_trip.py
index 0b470c1d..e48e60c8 100644
--- a/hive/state/vehicle_state/dispatch_pooling_trip.py
+++ b/hive/state/vehicle_state/dispatch_pooling_trip.py
@@ -2,6 +2,7 @@
 
 import logging
 from typing import NamedTuple, Tuple, Optional, TYPE_CHECKING
+from uuid import uuid4
 
 import immutables
 
@@ -13,7 +14,7 @@
 from hive.state.simulation_state import simulation_state_ops
 from hive.state.vehicle_state import vehicle_state_ops, dispatch_ops, servicing_ops
 from hive.state.vehicle_state.servicing_pooling_trip import ServicingPoolingTrip
-from hive.state.vehicle_state.vehicle_state import VehicleState
+from hive.state.vehicle_state.vehicle_state import VehicleState, VehicleStateInstanceId
 from hive.state.vehicle_state.vehicle_state_type import VehicleStateType
 from hive.util import TupleOps
 from hive.util.exception import SimulationStateError
@@ -33,18 +34,38 @@ class DispatchPoolingTrip(NamedTuple, VehicleState):
     route: Route
 
     # if we are re-planning a current ServicingPoolingTrip, we include this state
-    boarded_requests: immutables.Map[RequestId, Request] = immutables.Map()
-    departure_times: immutables.Map[RequestId, SimTime] = immutables.Map()
-    num_passengers: int = 0
+    boarded_requests: immutables.Map[RequestId, Request]
+    departure_times: immutables.Map[RequestId, SimTime]
+    num_passengers: int
+
+    instance_id: VehicleStateInstanceId
+
+    @classmethod
+    def build(cls,
+              vehicle_id: VehicleId,
+              trip_plan: Tuple[Tuple[RequestId, TripPhase], ...],
+              route: Route,
+              boarded_requests: immutables.Map[RequestId, Request] = immutables.Map(),
+              departure_times: immutables.Map[RequestId, SimTime] = immutables.Map(),
+              num_passengers: int = 0) -> DispatchPoolingTrip:
+        return cls(vehicle_id=vehicle_id,
+                   trip_plan=trip_plan,
+                   route=route,
+                   boarded_requests=boarded_requests,
+                   departure_times=departure_times,
+                   num_passengers=num_passengers,
+                   instance_id=uuid4())
 
     @property
     def vehicle_state_type(cls) -> VehicleStateType:
         return VehicleStateType.DISPATCH_POOLING_TRIP
 
-    def update(self, sim: SimulationState, env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def update(self, sim: SimulationState,
+               env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return VehicleState.default_update(sim, env, self)
 
-    def enter(self, sim: 'SimulationState', env: 'Environment') -> Tuple[Optional[Exception], Optional['SimulationState']]:
+    def enter(self, sim: 'SimulationState',
+              env: 'Environment') -> Tuple[Optional[Exception], Optional['SimulationState']]:
         """
         checks that all requests exist. updates all requests to know that this vehicle is on it's way
 
@@ -52,6 +73,7 @@ def enter(self, sim: 'SimulationState', env: 'Environment') -> Tuple[Optional[Ex
         :param env: the sim environment
         :return: an exception, or a sim state, or (None, None) if the request isn't there anymore
         """
+
         first_stop = TupleOps.head_optional(self.trip_plan)
         if first_stop is None:
             log.debug(f"DispatchPoolingTrip.enter called with empty trip_plan")
@@ -59,10 +81,13 @@ def enter(self, sim: 'SimulationState', env: 'Environment') -> Tuple[Optional[Ex
         else:
             req_ids, _ = tuple(zip(*self.trip_plan))
             vehicle = sim.vehicles.get(self.vehicle_id)
-            reqs_exist_and_match_membership = dispatch_ops.requests_exist_and_match_membership(sim, vehicle, req_ids)
+            reqs_exist_and_match_membership = dispatch_ops.requests_exist_and_match_membership(
+                sim, vehicle, req_ids)
             first_req_id, first_phase = first_stop
             first_req = sim.requests.get(first_req_id)
-            is_valid = route_cooresponds_with_entities(self.route, vehicle.position, first_req.origin_position) if vehicle and first_req else False
+            is_valid = route_cooresponds_with_entities(
+                self.route, vehicle.position,
+                first_req.origin_position) if vehicle and first_req else False
 
             context = f"vehicle {self.vehicle_id} entering dispatch pooling state"
             if not vehicle:
@@ -75,21 +100,20 @@ def enter(self, sim: 'SimulationState', env: 'Environment') -> Tuple[Optional[Ex
                 log.debug(f"bad route to connect vehicle {vehicle.id} to request {first_req.id}")
                 return None, None
             else:
-                error, updated_sim = dispatch_ops.modify_vehicle_assignment(sim, self.vehicle_id, req_ids)
+                error, updated_sim = dispatch_ops.modify_vehicle_assignment(
+                    sim, self.vehicle_id, req_ids)
                 if error:
                     response = SimulationStateError(
                         f"failure during DispatchPoolingTrip.enter for vehicle {self.vehicle_id}")
                     response.__cause__ = error
                     return response, None
                 else:
-                    result = VehicleState.apply_new_vehicle_state(updated_sim, self.vehicle_id, self)
+                    result = VehicleState.apply_new_vehicle_state(updated_sim, self.vehicle_id,
+                                                                  self)
                     return result
 
-    def exit(self,
-             next_state: VehicleState,
-             sim: SimulationState,
-             env: Environment
-             ) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def exit(self, next_state: VehicleState, sim: SimulationState,
+             env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         release the vehicle from the requests it was dispatched to
 
@@ -98,7 +122,10 @@ def exit(self,
         :return: an error, or, the updated simulation state, where the requests are no longer awaiting this vehicle
         """
         req_ids, _ = tuple(zip(*self.trip_plan))
-        result = dispatch_ops.modify_vehicle_assignment(sim, self.vehicle_id, req_ids, unassign=True)
+        result = dispatch_ops.modify_vehicle_assignment(sim,
+                                                        self.vehicle_id,
+                                                        req_ids,
+                                                        unassign=True)
         return result
 
     def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Environment) -> bool:
@@ -112,8 +139,8 @@ def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Envir
         return len(self.route) == 0
 
     def _default_terminal_state(
-        self, sim: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[VehicleState]]:
+            self, sim: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
         """
         give the default state to transition to after having met a terminal condition
 
@@ -125,18 +152,15 @@ def _default_terminal_state(
         # create servicing state, with first request PICKUP event consumed
         routes = dispatch_ops.create_routes(sim, self.trip_plan)
 
-        servicing_pooling_state = ServicingPoolingTrip(
-            vehicle_id=self.vehicle_id,
-            trip_plan=self.trip_plan,
-            routes=routes,
-            boarded_requests=self.boarded_requests,
-            departure_times=self.departure_times,
-            num_passengers=self.num_passengers
-        )
+        servicing_pooling_state = ServicingPoolingTrip.build(vehicle_id=self.vehicle_id,
+                                                             trip_plan=self.trip_plan,
+                                                             routes=routes,
+                                                             boarded_requests=self.boarded_requests,
+                                                             departure_times=self.departure_times,
+                                                             num_passengers=self.num_passengers)
         return None, servicing_pooling_state
 
-    def _perform_update(self,
-                        sim: SimulationState,
+    def _perform_update(self, sim: SimulationState,
                         env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         take a step along the route to the first request
diff --git a/hive/state/vehicle_state/dispatch_station.py b/hive/state/vehicle_state/dispatch_station.py
index d348ffbf..e0a0cfc1 100644
--- a/hive/state/vehicle_state/dispatch_station.py
+++ b/hive/state/vehicle_state/dispatch_station.py
@@ -2,13 +2,14 @@
 
 import logging
 from typing import NamedTuple, Tuple, Optional, TYPE_CHECKING
+from uuid import uuid4
 
 from hive.model.roadnetwork.route import Route, route_cooresponds_with_entities
 from hive.runner.environment import Environment
 from hive.state.simulation_state import simulation_state_ops
 from hive.state.vehicle_state.charge_queueing import ChargeQueueing
 from hive.state.vehicle_state.charging_station import ChargingStation
-from hive.state.vehicle_state.vehicle_state import VehicleState
+from hive.state.vehicle_state.vehicle_state import VehicleState, VehicleStateInstanceId
 from hive.state.vehicle_state.vehicle_state_ops import move
 from hive.state.vehicle_state.vehicle_state_type import VehicleStateType
 from hive.util.exception import SimulationStateError
@@ -26,18 +27,32 @@ class DispatchStation(NamedTuple, VehicleState):
     route: Route
     charger_id: ChargerId
 
+    instance_id: VehicleStateInstanceId
+
+    @classmethod
+    def build(cls, vehicle_id: VehicleId, station_id: StationId, route: Route,
+              charger_id: ChargerId) -> DispatchStation:
+        return DispatchStation(vehicle_id=vehicle_id,
+                               station_id=station_id,
+                               charger_id=charger_id,
+                               route=route,
+                               instance_id=uuid4())
+
     @property
     def vehicle_state_type(cls) -> VehicleStateType:
         return VehicleStateType.DISPATCH_STATION
 
-    def update(self, sim: SimulationState, env: Environment) -> Tuple[
-        Optional[Exception], Optional[SimulationState]]:
+    def update(self, sim: SimulationState,
+               env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return VehicleState.default_update(sim, env, self)
 
-    def enter(self, sim: SimulationState, env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def enter(self, sim: SimulationState,
+              env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+
         station = sim.stations.get(self.station_id)
         vehicle = sim.vehicles.get(self.vehicle_id)
-        is_valid = route_cooresponds_with_entities(self.route, vehicle.position, station.position) if vehicle and station else False
+        is_valid = route_cooresponds_with_entities(
+            self.route, vehicle.position, station.position) if vehicle and station else False
         context = f"vehicle {self.vehicle_id} entering dispatch station state for station {self.station_id} with charger {self.charger_id}"
         if not vehicle:
             return SimulationStateError(f"vehicle not found; context: {context}"), None
@@ -45,7 +60,7 @@ def enter(self, sim: SimulationState, env: Environment) -> Tuple[Optional[Except
             return SimulationStateError(f"station not found; context: {context}"), None
         elif station.geoid == vehicle.geoid:
             # already there!
-            next_state = ChargingStation(self.vehicle_id, self.station_id, self.charger_id)
+            next_state = ChargingStation.build(self.vehicle_id, self.station_id, self.charger_id)
             return next_state.enter(sim, env)
         elif not is_valid:
             return None, None
@@ -56,11 +71,8 @@ def enter(self, sim: SimulationState, env: Environment) -> Tuple[Optional[Except
             result = VehicleState.apply_new_vehicle_state(sim, self.vehicle_id, self)
             return result
 
-    def exit(self,
-             next_state: VehicleState,
-             sim: SimulationState,
-             env: Environment
-             ) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def exit(self, next_state: VehicleState, sim: SimulationState,
+             env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return None, sim
 
     def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Environment) -> bool:
@@ -74,8 +86,8 @@ def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Envir
         return len(self.route) == 0
 
     def _default_terminal_state(
-        self, sim: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[VehicleState]]:
+            self, sim: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
         """
         give the default state to transition to after having met a terminal condition
 
@@ -97,16 +109,13 @@ def _default_terminal_state(
             return SimulationStateError(message), None
         else:
             has_chargers = available_chargers > 0
-            next_state = ChargingStation(self.vehicle_id,
-                                         self.station_id,
-                                         self.charger_id) if has_chargers else ChargeQueueing(self.vehicle_id,
-                                                                                              self.station_id,
-                                                                                              self.charger_id,
-                                                                                              sim.sim_time)
+            next_state = ChargingStation.build(
+                self.vehicle_id, self.station_id,
+                self.charger_id) if has_chargers else ChargeQueueing.build(
+                    self.vehicle_id, self.station_id, self.charger_id, sim.sim_time)
             return None, next_state
 
-    def _perform_update(self,
-                        sim: SimulationState,
+    def _perform_update(self, sim: SimulationState,
                         env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         take a step along the route to the station
diff --git a/hive/state/vehicle_state/dispatch_trip.py b/hive/state/vehicle_state/dispatch_trip.py
index 616837d7..90801d12 100644
--- a/hive/state/vehicle_state/dispatch_trip.py
+++ b/hive/state/vehicle_state/dispatch_trip.py
@@ -2,6 +2,7 @@
 
 import logging
 from typing import NamedTuple, Tuple, Optional, TYPE_CHECKING
+from uuid import uuid4
 
 import immutables
 
@@ -14,7 +15,7 @@
 from hive.state.vehicle_state.idle import Idle
 from hive.state.vehicle_state.servicing_pooling_trip import ServicingPoolingTrip
 from hive.state.vehicle_state.servicing_trip import ServicingTrip
-from hive.state.vehicle_state.vehicle_state import VehicleState
+from hive.state.vehicle_state.vehicle_state import VehicleState, VehicleStateInstanceId
 from hive.state.vehicle_state.vehicle_state_type import VehicleStateType
 from hive.util.exception import SimulationStateError
 from hive.util.typealiases import RequestId, VehicleId
@@ -30,15 +31,22 @@ class DispatchTrip(NamedTuple, VehicleState):
     request_id: RequestId
     route: Route
 
+    instance_id: VehicleStateInstanceId
+
+    @classmethod
+    def build(cls, vehicle_id: VehicleId, request_id: RequestId, route: Route) -> DispatchTrip:
+        return cls(vehicle_id=vehicle_id, request_id=request_id, route=route, instance_id=uuid4())
+
     @property
     def vehicle_state_type(cls) -> VehicleStateType:
         return VehicleStateType.DISPATCH_TRIP
 
-    def update(self, sim: SimulationState, env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def update(self, sim: SimulationState,
+               env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return VehicleState.default_update(sim, env, self)
 
-    def enter(self, sim: SimulationState, env: Environment) -> Tuple[
-        Optional[Exception], Optional[SimulationState]]:
+    def enter(self, sim: SimulationState,
+              env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         checks that the request exists and if so, updates the request to know that this vehicle is on it's way
 
@@ -46,9 +54,11 @@ def enter(self, sim: SimulationState, env: Environment) -> Tuple[
         :param env: the sim environment
         :return: an exception, or a sim state, or (None, None) if the request isn't there anymore
         """
+
         vehicle = sim.vehicles.get(self.vehicle_id)
         request = sim.requests.get(self.request_id)
-        is_valid = route_cooresponds_with_entities(self.route, vehicle.position, request.origin_position) if vehicle and request else False
+        is_valid = route_cooresponds_with_entities(
+            self.route, vehicle.position, request.origin_position) if vehicle and request else False
         context = f"vehicle {self.vehicle_id} entering dispatch trip for request {self.request_id}"
         if not vehicle:
             return SimulationStateError(f"vehicle not found; context: {context}"), None
@@ -72,11 +82,8 @@ def enter(self, sim: SimulationState, env: Environment) -> Tuple[
                 result = VehicleState.apply_new_vehicle_state(updated_sim, self.vehicle_id, self)
                 return result
 
-    def exit(self,
-             next_state: VehicleState,
-             sim: SimulationState,
-             env: Environment
-             ) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def exit(self, next_state: VehicleState, sim: SimulationState,
+             env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         release the vehicle from the request it was dispatched to
 
@@ -105,8 +112,8 @@ def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Envir
         return len(self.route) == 0
 
     def _default_terminal_state(
-        self, sim: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[VehicleState]]:
+            self, sim: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
         """
         give the default state to transition to after having met a terminal condition
 
@@ -122,7 +129,7 @@ def _default_terminal_state(
             return SimulationStateError(message), None
         elif not request:
             # request already got picked up or was cancelled; go an Idle state
-            next_state = Idle(self.vehicle_id)
+            next_state = Idle.build(self.vehicle_id)
             return None, next_state
         else:
             # request exists: pick up the trip and enter a ServicingTrip state
@@ -130,28 +137,25 @@ def _default_terminal_state(
             # apply next state
             # generate the data to describe the trip for this request
             # where the pickup phase is currently happening + doesn't need to be added to the trip plan
-            trip_plan: Tuple[Tuple[RequestId, TripPhase], ...] = ((request.id, TripPhase.DROPOFF),)
+            trip_plan: Tuple[Tuple[RequestId, TripPhase], ...] = ((request.id, TripPhase.DROPOFF), )
             departure_time = sim.sim_time
 
             # create the state (pooling, or, standard servicing trip, depending on the sitch)
             pooling_trip = vehicle.driver_state.allows_pooling and request.allows_pooling
-            next_state = ServicingPoolingTrip(
+            next_state = ServicingPoolingTrip.build(
                 vehicle_id=vehicle.id,
                 trip_plan=trip_plan,
                 boarded_requests=immutables.Map({request.id: request}),
                 departure_times=immutables.Map({request.id, departure_time}),
-                routes=(route,),
-                num_passengers=len(request.passengers)
-            ) if pooling_trip else ServicingTrip(
-                vehicle_id=vehicle.id,
-                request=request,
-                departure_time=departure_time,
-                route=route
-            )
+                routes=(route, ),
+                num_passengers=len(request.passengers)) if pooling_trip else ServicingTrip.build(
+                    vehicle_id=vehicle.id,
+                    request=request,
+                    departure_time=departure_time,
+                    route=route)
             return None, next_state
 
-    def _perform_update(self,
-                        sim: SimulationState,
+    def _perform_update(self, sim: SimulationState,
                         env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         take a step along the route to the request
diff --git a/hive/state/vehicle_state/idle.py b/hive/state/vehicle_state/idle.py
index d021677a..7f2d5da5 100644
--- a/hive/state/vehicle_state/idle.py
+++ b/hive/state/vehicle_state/idle.py
@@ -1,37 +1,50 @@
-from typing import NamedTuple, Tuple, Optional
+from __future__ import annotations
+
+from typing import NamedTuple, Tuple, Optional, TYPE_CHECKING
+from uuid import uuid4
 
 from hive.runner.environment import Environment
 from hive.state.simulation_state import simulation_state_ops
 from hive.state.vehicle_state.out_of_service import OutOfService
-from hive.state.vehicle_state.vehicle_state import VehicleState
+from hive.state.vehicle_state.vehicle_state import VehicleState, VehicleStateInstanceId
 from hive.state.vehicle_state.vehicle_state_type import VehicleStateType
 from hive.util.exception import SimulationStateError
 from hive.util.typealiases import VehicleId
 from hive.util.units import Seconds
 
+if TYPE_CHECKING:
+    from hive.state.simulation_state.simulation_state import SimulationState
+
 
 class Idle(NamedTuple, VehicleState):
     vehicle_id: VehicleId
+    instance_id: VehicleStateInstanceId
+
     idle_duration: Seconds = 0
 
+    @classmethod
+    def build(cls, vehicle_id: VehicleId) -> Idle:
+        return Idle(vehicle_id=vehicle_id, instance_id=uuid4())
+
     @property
     def vehicle_state_type(cls) -> VehicleStateType:
         return VehicleStateType.IDLE
 
-    def update(self, sim: 'SimulationState', env: Environment) -> Tuple[Optional[Exception], Optional['SimulationState']]:
+    def update(self, sim: SimulationState,
+               env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return VehicleState.default_update(sim, env, self)
 
-    def enter(self, sim: 'SimulationState', env: Environment) -> Tuple[Optional[Exception], Optional['SimulationState']]:
-        return VehicleState.apply_new_vehicle_state(sim, self.vehicle_id, self)
+    def enter(self, sim: SimulationState,
+              env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+
+        new_state = VehicleState.apply_new_vehicle_state(sim, self.vehicle_id, self)
+        return new_state
 
-    def exit(self,
-             next_state: VehicleState,
-             sim: 'SimulationState',
-             env: Environment
-             ) -> Tuple[Optional[Exception], Optional['SimulationState']]:
+    def exit(self, next_state: VehicleState, sim: SimulationState,
+             env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return None, sim
 
-    def _has_reached_terminal_state_condition(self, sim: 'SimulationState', env: Environment) -> bool:
+    def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Environment) -> bool:
         """
         If energy has run out, we will move to OutOfService
 
@@ -44,8 +57,8 @@ def _has_reached_terminal_state_condition(self, sim: 'SimulationState', env: Env
         return not vehicle or mechatronics.is_empty(vehicle)
 
     def _default_terminal_state(
-        self, sim: 'SimulationState', env: Environment
-    ) -> Tuple[Optional[Exception], Optional[VehicleState]]:
+            self, sim: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
         """
         give the default state to transition to after having met a terminal condition
 
@@ -53,12 +66,11 @@ def _default_terminal_state(
         :param env: the simulation environment
         :return: an exception due to failure or the next_state after finishing a task
         """
-        next_state = OutOfService(self.vehicle_id)
+        next_state = OutOfService.build(self.vehicle_id)
         return None, next_state
 
-    def _perform_update(self,
-                        sim: 'SimulationState',
-                        env: Environment) -> Tuple[Optional[Exception], Optional['SimulationState']]:
+    def _perform_update(self, sim: SimulationState,
+                        env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         incur an idling cost
 
@@ -72,7 +84,8 @@ def _perform_update(self,
         if not vehicle:
             return SimulationStateError(f"vehicle not found; context: {context}"), None
         elif not mechatronics:
-            return SimulationStateError(f"cannot find {vehicle.mechatronics_id} in environment"), None
+            return SimulationStateError(
+                f"cannot find {vehicle.mechatronics_id} in environment"), None
         else:
             less_energy_vehicle = mechatronics.idle(vehicle, sim.sim_timestep_duration_seconds)
 
diff --git a/hive/state/vehicle_state/out_of_service.py b/hive/state/vehicle_state/out_of_service.py
index 7cc9fb2f..8bcc05ec 100644
--- a/hive/state/vehicle_state/out_of_service.py
+++ b/hive/state/vehicle_state/out_of_service.py
@@ -1,32 +1,52 @@
-from typing import NamedTuple, Tuple, Optional
+from __future__ import annotations
+
+from typing import NamedTuple, Tuple, Optional, TYPE_CHECKING
+
+from uuid import uuid4
 
 from hive.runner.environment import Environment
-from hive.state.vehicle_state.vehicle_state import VehicleState
+from hive.state.vehicle_state.vehicle_state import VehicleState, VehicleStateInstanceId
 from hive.state.vehicle_state.vehicle_state_type import VehicleStateType
 from hive.util.typealiases import VehicleId
 
+if TYPE_CHECKING:
+    from hive.state.simulation_state.simulation_state import SimulationState
+
 
 class OutOfService(NamedTuple, VehicleState):
     vehicle_id: VehicleId
 
+    instance_id: VehicleStateInstanceId
+
+    @classmethod
+    def build(cls, vehicle_id: VehicleId) -> OutOfService:
+        """
+        build an OutOfService state
+
+        :param vehicle_id: the vehicle id
+        :return: an OutOfService state
+        """
+        return cls(vehicle_id=vehicle_id, instance_id=uuid4())
+
     @property
     def vehicle_state_type(cls) -> VehicleStateType:
         return VehicleStateType.OUT_OF_SERVICE
 
-    def update(self, sim: 'SimulationState', env: Environment) -> Tuple[Optional[Exception], Optional['SimulationState']]:
+    def update(self, sim: SimulationState,
+               env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return VehicleState.default_update(sim, env, self)
 
-    def enter(self, sim: 'SimulationState', env: Environment) -> Tuple[Optional[Exception], Optional['SimulationState']]:
-        return VehicleState.apply_new_vehicle_state(sim, self.vehicle_id, self)
+    def enter(self, sim: SimulationState,
+              env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+
+        new_state = VehicleState.apply_new_vehicle_state(sim, self.vehicle_id, self)
+        return new_state
 
-    def exit(self,
-             next_state: VehicleState,
-             sim: 'SimulationState',
-             env: Environment
-             ) -> Tuple[Optional[Exception], Optional['SimulationState']]:
+    def exit(self, next_state: VehicleState, sim: SimulationState,
+             env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return None, sim
 
-    def _has_reached_terminal_state_condition(self, sim: 'SimulationState', env: Environment) -> bool:
+    def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Environment) -> bool:
         """
         There is no terminal state for OutOfService
 
@@ -37,8 +57,8 @@ def _has_reached_terminal_state_condition(self, sim: 'SimulationState', env: Env
         return False
 
     def _default_terminal_state(
-        self, sim: 'SimulationState', env: Environment
-    ) -> Tuple[Optional[Exception], Optional[VehicleState]]:
+            self, sim: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
         """
         give the default state to transition to after having met a terminal condition
 
@@ -48,9 +68,8 @@ def _default_terminal_state(
         """
         return None, self
 
-    def _perform_update(self,
-                        sim: 'SimulationState',
-                        env: Environment) -> Tuple[Optional[Exception], Optional['SimulationState']]:
+    def _perform_update(self, sim: SimulationState,
+                        env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         as of now, there is no update for being OutOfService
 
diff --git a/hive/state/vehicle_state/repositioning.py b/hive/state/vehicle_state/repositioning.py
index 4f7f8f4b..f80695b3 100644
--- a/hive/state/vehicle_state/repositioning.py
+++ b/hive/state/vehicle_state/repositioning.py
@@ -1,30 +1,52 @@
-from typing import NamedTuple, Tuple, Optional
+from __future__ import annotations
+from typing import NamedTuple, Tuple, Optional, TYPE_CHECKING
+from uuid import uuid4
 
 from hive.model.roadnetwork.route import Route, route_cooresponds_with_entities
 from hive.runner.environment import Environment
 from hive.state.simulation_state import simulation_state_ops
 from hive.state.vehicle_state import vehicle_state_ops
 from hive.state.vehicle_state.idle import Idle
-from hive.state.vehicle_state.vehicle_state import VehicleState
+from hive.state.vehicle_state.vehicle_state import VehicleState, VehicleStateInstanceId
 from hive.state.vehicle_state.vehicle_state_type import VehicleStateType
 from hive.util.exception import SimulationStateError
 from hive.util.typealiases import VehicleId
 
+if TYPE_CHECKING:
+    from hive.state.simulation_state.simulation_state import SimulationState
+
 
 class Repositioning(NamedTuple, VehicleState):
     vehicle_id: VehicleId
     route: Route
 
+    instance_id: VehicleStateInstanceId
+
+    @classmethod
+    def build(cls, vehicle_id: VehicleId, route: Route) -> Repositioning:
+        """
+        build a repositioning state
+
+        :param vehicle_id: the vehicle id
+        :param route: the route to the new location 
+        :return: a repositioning state
+        """
+        return cls(vehicle_id=vehicle_id, route=route, instance_id=uuid4())
+
     @property
     def vehicle_state_type(cls) -> VehicleStateType:
         return VehicleStateType.REPOSITIONING
 
-    def update(self, sim: 'SimulationState', env: Environment) -> Tuple[Optional[Exception], Optional['SimulationState']]:
+    def update(self, sim: SimulationState,
+               env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return VehicleState.default_update(sim, env, self)
 
-    def enter(self, sim: 'SimulationState', env: Environment) -> Tuple[Optional[Exception], Optional['SimulationState']]:
+    def enter(self, sim: SimulationState,
+              env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+
         vehicle = sim.vehicles.get(self.vehicle_id)
-        is_valid = route_cooresponds_with_entities(self.route, vehicle.position) if vehicle else False
+        is_valid = route_cooresponds_with_entities(self.route,
+                                                   vehicle.position) if vehicle else False
         context = f"vehicle {self.vehicle_id} entering repositioning state"
         if not vehicle:
             return SimulationStateError(f"vehicle not found; context: {context}"), None
@@ -34,14 +56,11 @@ def enter(self, sim: 'SimulationState', env: Environment) -> Tuple[Optional[Exce
             result = VehicleState.apply_new_vehicle_state(sim, self.vehicle_id, self)
             return result
 
-    def exit(self,
-             next_state: VehicleState,
-             sim: 'SimulationState',
-             env: Environment
-             ) -> Tuple[Optional[Exception], Optional['SimulationState']]:
+    def exit(self, next_state: VehicleState, sim: SimulationState,
+             env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return None, sim
 
-    def _has_reached_terminal_state_condition(self, sim: 'SimulationState', env: Environment) -> bool:
+    def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Environment) -> bool:
         """
         this terminates when we reach a base
 
@@ -52,8 +71,8 @@ def _has_reached_terminal_state_condition(self, sim: 'SimulationState', env: Env
         return len(self.route) == 0
 
     def _default_terminal_state(
-        self, sim: 'SimulationState', env: Environment
-    ) -> Tuple[Optional[Exception], Optional[VehicleState]]:
+            self, sim: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
         """
         give the default state to transition to after having met a terminal condition
 
@@ -61,12 +80,11 @@ def _default_terminal_state(
         :param env: the simulation environment
         :return: an exception due to failure or the next_state after finishing a task
         """
-        next_state = Idle(self.vehicle_id)
+        next_state = Idle.build(self.vehicle_id)
         return None, next_state
 
-    def _perform_update(self,
-                        sim: 'SimulationState',
-                        env: Environment) -> Tuple[Optional[Exception], Optional['SimulationState']]:
+    def _perform_update(self, sim: SimulationState,
+                        env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         take a step along the route to the base
 
@@ -85,7 +103,8 @@ def _perform_update(self,
             response.__cause__ = move_error
             return response, None
         elif not moved_vehicle:
-            return SimulationStateError(f"vehicle {self.vehicle_id} not found; context: {context}"), None
+            return SimulationStateError(
+                f"vehicle {self.vehicle_id} not found; context: {context}"), None
         elif moved_vehicle.vehicle_state.vehicle_state_type == VehicleStateType.OUT_OF_SERVICE:
             return None, move_result.sim
         else:
diff --git a/hive/state/vehicle_state/reserve_base.py b/hive/state/vehicle_state/reserve_base.py
index c5865b79..c924b828 100644
--- a/hive/state/vehicle_state/reserve_base.py
+++ b/hive/state/vehicle_state/reserve_base.py
@@ -2,10 +2,11 @@
 
 import logging
 from typing import NamedTuple, Tuple, Optional, TYPE_CHECKING
+from uuid import uuid4
 
 from hive.runner.environment import Environment
 from hive.state.simulation_state import simulation_state_ops
-from hive.state.vehicle_state.vehicle_state import VehicleState
+from hive.state.vehicle_state.vehicle_state import VehicleState, VehicleStateInstanceId
 from hive.state.vehicle_state.vehicle_state_type import VehicleStateType
 from hive.util.exception import SimulationStateError
 from hive.util.typealiases import VehicleId, BaseId
@@ -20,16 +21,22 @@ class ReserveBase(NamedTuple, VehicleState):
     vehicle_id: VehicleId
     base_id: BaseId
 
+    instance_id: VehicleStateInstanceId
+
+    @classmethod
+    def build(cls, vehicle_id: VehicleId, base_id: BaseId) -> ReserveBase:
+        return cls(vehicle_id, base_id, instance_id=uuid4())
+
     @property
     def vehicle_state_type(cls) -> VehicleStateType:
         return VehicleStateType.RESERVE_BASE
 
-    def update(self, sim: SimulationState, env: Environment) -> Tuple[
-        Optional[Exception], Optional[SimulationState]]:
+    def update(self, sim: SimulationState,
+               env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return VehicleState.default_update(sim, env, self)
 
-    def enter(self, sim: SimulationState, env: Environment) -> Tuple[
-        Optional[Exception], Optional[SimulationState]]:
+    def enter(self, sim: SimulationState,
+              env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         to enter this state, the base must have a stall for the vehicle
 
@@ -37,6 +44,7 @@ def enter(self, sim: SimulationState, env: Environment) -> Tuple[
         :param env: the sim environment
         :return: an exception, an updated SimulationState, or (None, None) when the base has no stalls
         """
+
         vehicle = sim.vehicles.get(self.vehicle_id)
         base = sim.bases.get(self.base_id)
         context = f"vehicle {self.vehicle_id} entering reserve base at base {self.base_id}"
@@ -45,7 +53,8 @@ def enter(self, sim: SimulationState, env: Environment) -> Tuple[
         elif not base:
             return SimulationStateError(f"{context}; base not found"), None
         elif base.geoid != vehicle.geoid:
-            log.warning(f"ReserveBase.enter(): vehicle {vehicle.id} not at same location as {base.id}")
+            log.warning(
+                f"ReserveBase.enter(): vehicle {vehicle.id} not at same location as {base.id}")
             return None, None
         elif not base.membership.grant_access_to_membership(vehicle.membership):
             msg = f"ReserveBase.enter(): vehicle {vehicle.id} does not have access to base {base.id}"
@@ -65,11 +74,8 @@ def enter(self, sim: SimulationState, env: Environment) -> Tuple[
                 else:
                     return VehicleState.apply_new_vehicle_state(updated_sim, self.vehicle_id, self)
 
-    def exit(self,
-             next_state: VehicleState,
-             sim: SimulationState,
-             env: Environment
-             ) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def exit(self, next_state: VehicleState, sim: SimulationState,
+             env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         releases the stall that this vehicle occupied
 
@@ -101,8 +107,8 @@ def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Envir
         return False
 
     def _default_terminal_state(
-        self, sim: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[VehicleState]]:
+            self, sim: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
         """
         give the default state to transition to after having met a terminal condition
 
@@ -112,8 +118,7 @@ def _default_terminal_state(
         """
         return None, self
 
-    def _perform_update(self,
-                        sim: SimulationState,
+    def _perform_update(self, sim: SimulationState,
                         env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         as of now, there is no update for being ReserveBase
diff --git a/hive/state/vehicle_state/servicing_pooling_trip.py b/hive/state/vehicle_state/servicing_pooling_trip.py
index 6a9e661c..532a994d 100644
--- a/hive/state/vehicle_state/servicing_pooling_trip.py
+++ b/hive/state/vehicle_state/servicing_pooling_trip.py
@@ -2,6 +2,7 @@
 
 import logging
 from typing import NamedTuple, Tuple, TYPE_CHECKING, Optional
+from uuid import uuid4
 
 import immutables
 
@@ -14,7 +15,7 @@
 from hive.state.vehicle_state.idle import Idle
 from hive.state.vehicle_state.servicing_ops import get_active_pooling_trip, pick_up_trip, \
     update_active_pooling_trip, ActivePoolingTrip
-from hive.state.vehicle_state.vehicle_state import VehicleState
+from hive.state.vehicle_state.vehicle_state import VehicleState, VehicleStateInstanceId
 from hive.state.vehicle_state.vehicle_state_ops import move
 from hive.state.vehicle_state.vehicle_state_type import VehicleStateType
 from hive.util import SimulationStateError, TupleOps
@@ -37,6 +38,26 @@ class ServicingPoolingTrip(NamedTuple, VehicleState):
     routes: Tuple[Route, ...]
     num_passengers: int
 
+    instance_id: VehicleStateInstanceId
+
+    @classmethod
+    def build(
+        cls,
+        vehicle_id: VehicleId,
+        trip_plan: Tuple[Tuple[RequestId, TripPhase], ...],
+        boarded_requests: immutables.Map[RequestId, Request],
+        departure_times: immutables.Map[RequestId, SimTime],
+        routes: Tuple[Route, ...],
+        num_passengers: int,
+    ) -> ServicingPoolingTrip:
+        return ServicingPoolingTrip(vehicle_id=vehicle_id,
+                                    trip_plan=trip_plan,
+                                    boarded_requests=boarded_requests,
+                                    departure_times=departure_times,
+                                    routes=routes,
+                                    num_passengers=num_passengers,
+                                    instance_id=uuid4())
+
     @property
     def vehicle_state_type(cls) -> VehicleStateType:
         return VehicleStateType.SERVICING_POOLING_TRIP
@@ -49,10 +70,12 @@ def route(cls) -> Route:
         """
         return cls.routes[0] if len(cls.routes) > 0 else ()
 
-    def update(self, sim: SimulationState, env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def update(self, sim: SimulationState,
+               env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return VehicleState.default_update(sim, env, self)
 
-    def enter(self, sim: SimulationState, env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def enter(self, sim: SimulationState,
+              env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         transition from DispatchTrip into a pooling trip service leg. this first frame of servicing
         a pooling trip should be happening at the start location of the first request in the pool,
@@ -72,7 +95,8 @@ def enter(self, sim: SimulationState, env: Environment) -> Tuple[Optional[Except
         if vehicle is None:
             return SimulationStateError(f"vehicle note found; context: {context}"), None
         if first_req is None:
-            return SimulationStateError(f"request {first_req_id} not found; context: {context}"), None
+            return SimulationStateError(
+                f"request {first_req_id} not found; context: {context}"), None
         elif not vehicle.vehicle_state.vehicle_state_type == VehicleStateType.DISPATCH_POOLING_TRIP:
             # the only supported transition into ServicingPoolingTrip comes from DispatchTrip
             prev_state = vehicle.vehicle_state.__class__.__name__
@@ -86,9 +110,11 @@ def enter(self, sim: SimulationState, env: Environment) -> Tuple[Optional[Except
         else:
 
             # pick up first request
-            pickup_error, pickup_sim = servicing_ops.pick_up_trip(sim, env, self.vehicle_id, first_req_id)
+            pickup_error, pickup_sim = servicing_ops.pick_up_trip(sim, env, self.vehicle_id,
+                                                                  first_req_id)
             if pickup_error:
-                result = SimulationStateError(f"failed to pick up first trip in ServicingPoolingTrip {self}")
+                result = SimulationStateError(
+                    f"failed to pick up first trip in ServicingPoolingTrip {self}")
                 result.__cause__ = pickup_error
                 return result, None
             else:
@@ -97,18 +123,13 @@ def enter(self, sim: SimulationState, env: Environment) -> Tuple[Optional[Except
                     boarded_requests=immutables.Map({first_req_id: first_req}),
                     departure_times=immutables.Map({first_req_id: sim.sim_time}),
                     num_passengers=len(first_req.passengers),
-                    trip_plan=remaining_trip_plan
-                )
-                result = VehicleState.apply_new_vehicle_state(pickup_sim,
-                                                              self.vehicle_id,
+                    trip_plan=remaining_trip_plan)
+                result = VehicleState.apply_new_vehicle_state(pickup_sim, self.vehicle_id,
                                                               vehicle_state_with_first_trip)
                 return result
 
-    def exit(self,
-             next_state: VehicleState,
-             sim: SimulationState,
-             env: Environment
-             ) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def exit(self, next_state: VehicleState, sim: SimulationState,
+             env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         exit when there is no remaining trip_phase to complete
 
@@ -135,8 +156,8 @@ def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Envir
         return len(self.trip_plan) == 0
 
     def _default_terminal_state(
-            self, sim: SimulationState, env: Environment
-    ) -> Tuple[Optional[Exception], Optional[VehicleState]]:
+            self, sim: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
         """
         give the default state to transition to after having met a terminal condition
 
@@ -144,11 +165,11 @@ def _default_terminal_state(
         :param env: the simulation environment
         :return: an exception due to failure or the next_state after finishing a task
         """
-        next_state = Idle(self.vehicle_id)
+        next_state = Idle.build(self.vehicle_id)
         return None, next_state
 
-    def _perform_update(self, sim: SimulationState, env: Environment) -> Tuple[
-        Optional[Exception], Optional[SimulationState]]:
+    def _perform_update(self, sim: SimulationState,
+                        env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         move forward on our trip, making pickups and dropoffs as needed
 
@@ -161,7 +182,8 @@ def _perform_update(self, sim: SimulationState, env: Environment) -> Tuple[
         err1, active_trip = get_active_pooling_trip(self)
         if err1 is not None:
             response = SimulationStateError(
-                f"failure during ServicingPoolingTrip._perform_update for vehicle {self.vehicle_id}")
+                f"failure during ServicingPoolingTrip._perform_update for vehicle {self.vehicle_id}"
+            )
             response.__cause__ = err1
             return response, None
         else:
@@ -171,7 +193,8 @@ def _perform_update(self, sim: SimulationState, env: Environment) -> Tuple[
 
             if move_error:
                 response = SimulationStateError(
-                    f"failure during ServicingPoolingTrip._perform_update for vehicle {self.vehicle_id}")
+                    f"failure during ServicingPoolingTrip._perform_update for vehicle {self.vehicle_id}"
+                )
                 response.__cause__ = move_error
                 return response, None
             elif not moved_vehicle:
diff --git a/hive/state/vehicle_state/servicing_trip.py b/hive/state/vehicle_state/servicing_trip.py
index 68108f6c..138484f5 100644
--- a/hive/state/vehicle_state/servicing_trip.py
+++ b/hive/state/vehicle_state/servicing_trip.py
@@ -2,6 +2,7 @@
 
 import logging
 from typing import NamedTuple, Tuple, Optional, TYPE_CHECKING
+from uuid import uuid4
 
 from hive.model.request import Request
 from hive.model.roadnetwork.route import Route, route_cooresponds_with_entities
@@ -10,7 +11,7 @@
 from hive.state.simulation_state import simulation_state_ops
 from hive.state.vehicle_state.idle import Idle
 from hive.state.vehicle_state.servicing_ops import drop_off_trip, pick_up_trip
-from hive.state.vehicle_state.vehicle_state import VehicleState
+from hive.state.vehicle_state.vehicle_state import VehicleState, VehicleStateInstanceId
 from hive.state.vehicle_state.vehicle_state_ops import move
 from hive.state.vehicle_state.vehicle_state_type import VehicleStateType
 from hive.util.exception import SimulationStateError
@@ -28,17 +29,35 @@ class ServicingTrip(NamedTuple, VehicleState):
     departure_time: SimTime
     route: Route
 
+    instance_id: VehicleStateInstanceId
+
+    @classmethod
+    def build(cls, vehicle_id: VehicleId, request: Request, departure_time: SimTime,
+              route: Route) -> ServicingTrip:
+        """
+        build a servicing trip state
+
+        :param vehicle_id: the vehicle id
+        :param request: the request
+        :param departure_time: the departure time
+        :param route: the route
+        :return: the servicing trip state
+        """
+        return cls(vehicle_id=vehicle_id,
+                   request=request,
+                   departure_time=departure_time,
+                   route=route,
+                   instance_id=uuid4())
+
     @property
     def vehicle_state_type(cls) -> VehicleStateType:
         return VehicleStateType.SERVICING_TRIP
 
-    def update(self,
-               sim: SimulationState,
+    def update(self, sim: SimulationState,
                env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         return VehicleState.default_update(sim, env, self)
 
-    def enter(self,
-              sim: SimulationState,
+    def enter(self, sim: SimulationState,
               env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         transition from DispatchTrip into a non-pooling trip service leg
@@ -47,9 +66,12 @@ def enter(self,
         :param env: the simulation environment
         :return: an error, or, the sim with state entered
         """
+
         vehicle = sim.vehicles.get(self.vehicle_id)
         request = sim.requests.get(self.request.id)
-        is_valid = route_cooresponds_with_entities(self.route, request.origin_position, request.destination_position) if vehicle and request else False
+        is_valid = route_cooresponds_with_entities(
+            self.route, request.origin_position,
+            request.destination_position) if vehicle and request else False
         context = f"vehicle {self.vehicle_id} entering servicing trip state for request {self.request.id}"
         if vehicle is None:
             return SimulationStateError(f"vehicle not found; context: {context}"), None
@@ -72,7 +94,8 @@ def enter(self,
             msg = f"vehicle {vehicle.id} attempting to service request {self.request.id} invalid route (doesn't match location of vehicle)"
             log.warning(msg)
             return None, None
-        elif not route_cooresponds_with_entities(self.route, request.origin_position, request.destination_position):
+        elif not route_cooresponds_with_entities(self.route, request.origin_position,
+                                                 request.destination_position):
             msg = f"vehicle {vehicle.id} attempting to service request {self.request.id} invalid route (doesn't match o/d of request)"
             log.warning(msg)
             return None, None
@@ -84,14 +107,12 @@ def enter(self,
                 response.__cause__ = pickup_error
                 return response, None
             else:
-                enter_result = VehicleState.apply_new_vehicle_state(pickup_sim, self.vehicle_id, self)
+                enter_result = VehicleState.apply_new_vehicle_state(pickup_sim, self.vehicle_id,
+                                                                    self)
                 return enter_result
 
-    def exit(self,
-             next_state: VehicleState,
-             sim: SimulationState,
-             env: Environment
-             ) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+    def exit(self, next_state: VehicleState, sim: SimulationState,
+             env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         leave this state when the route is completed
 
@@ -114,9 +135,9 @@ def _has_reached_terminal_state_condition(self, sim: SimulationState, env: Envir
         """
         return len(self.route) == 0
 
-    def _default_terminal_state(self,
-                                sim: SimulationState,
-                                env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
+    def _default_terminal_state(
+            self, sim: SimulationState,
+            env: Environment) -> Tuple[Optional[Exception], Optional[VehicleState]]:
         """
         gets the default terminal state for this state which should be transitioned to
         once it reaches the end of the current task.
@@ -124,11 +145,10 @@ def _default_terminal_state(self,
         :param env: the sim environment
         :return: an exception or the default VehicleState
         """
-        next_state = Idle(self.vehicle_id)
+        next_state = Idle.build(self.vehicle_id)
         return None, next_state
 
-    def _perform_update(self,
-                        sim: SimulationState,
+    def _perform_update(self, sim: SimulationState,
                         env: Environment) -> Tuple[Optional[Exception], Optional[SimulationState]]:
         """
         take a step along the route to the base
diff --git a/hive/state/vehicle_state/vehicle_state.py b/hive/state/vehicle_state/vehicle_state.py
index f3b69cb3..866f1e9c 100644
--- a/hive/state/vehicle_state/vehicle_state.py
+++ b/hive/state/vehicle_state/vehicle_state.py
@@ -1,7 +1,10 @@
 from __future__ import annotations
 
+from uuid import UUID, uuid4
+
 from abc import abstractmethod, ABCMeta, abstractproperty
 from typing import Tuple, Optional, NamedTupleMeta, TYPE_CHECKING
+import uuid
 
 from hive.state.entity_state import entity_state_ops
 from hive.state.entity_state.entity_state import EntityState
@@ -14,6 +17,7 @@
     from hive.runner.environment import Environment
     from hive.state.simulation_state.simulation_state import SimulationState
 
+VehicleStateInstanceId = UUID
 
 class VehicleState(ABCMeta, NamedTupleMeta, EntityState):
     """
@@ -25,7 +29,6 @@ class VehicleState(ABCMeta, NamedTupleMeta, EntityState):
     an enter or exit can return an exception, a SimulationState, or (None, None) signifying that the
     state cannot be entered/exited under this circumstance.
     """
-
     @abstractproperty
     def vehicle_id(self) -> VehicleId:
         """
@@ -33,8 +36,7 @@ def vehicle_id(self) -> VehicleId:
         """
         pass
 
-    @property
-    @abstractmethod
+    @abstractproperty
     def vehicle_state_type(cls) -> VehicleStateType:
         """
         unique state type, used for comparison, replaces need to call isinstance on the concrete
@@ -43,6 +45,13 @@ def vehicle_state_type(cls) -> VehicleStateType:
         """
         pass
 
+    @abstractproperty
+    def instance_id(self) -> VehicleStateInstanceId:
+        """
+        a unique id for this state instance
+        """
+        pass
+    
     def __repr__(self) -> str:
         return super().__repr__()
 
@@ -154,3 +163,4 @@ def _perform_update(
         :return: an exception due to failure or an optional updated simulation
         """
         pass
+
diff --git a/hive/state/vehicle_state/vehicle_state_ops.py b/hive/state/vehicle_state/vehicle_state_ops.py
index f83e0018..d988778f 100644
--- a/hive/state/vehicle_state/vehicle_state_ops.py
+++ b/hive/state/vehicle_state/vehicle_state_ops.py
@@ -18,10 +18,7 @@
     from hive.runner.environment import Environment
 
 
-def charge(sim: SimulationState,
-           env: Environment,
-           vehicle_id: VehicleId,
-           station_id: StationId,
+def charge(sim: SimulationState, env: Environment, vehicle_id: VehicleId, station_id: StationId,
            charger_id: ChargerId) -> Tuple[Optional[Exception], Optional[SimulationState]]:
     """
     apply any effects due to a vehicle being advanced one discrete time unit in this VehicleState
@@ -44,18 +41,22 @@ def charge(sim: SimulationState,
     if not vehicle:
         return SimulationStateError(f"vehicle not found; context: {context}"), None
     elif not mechatronics:
-        return SimulationStateError(f"invalid mechatronics_id {vehicle.mechatronics_id}; context: {context}"), None
+        return SimulationStateError(
+            f"invalid mechatronics_id {vehicle.mechatronics_id}; context: {context}"), None
     elif not charger:
         return SimulationStateError(f"invalid charger_id; context: {context}"), None
     elif not station:
         return SimulationStateError(f"station not found; context {context}"), None
     elif mechatronics.is_full(vehicle):
-        return SimulationStateError(f"vehicle is full but still attempting to charge; context {context}"), None
+        return SimulationStateError(
+            f"vehicle is full but still attempting to charge; context {context}"), None
     else:
-        charged_vehicle, _ = mechatronics.add_energy(vehicle, charger, sim.sim_timestep_duration_seconds)
+        charged_vehicle, _ = mechatronics.add_energy(vehicle, charger,
+                                                     sim.sim_timestep_duration_seconds)
 
         # determine price of charge event
-        kwh_transacted = charged_vehicle.energy[charger.energy_type] - vehicle.energy[charger.energy_type]  # kwh
+        kwh_transacted = charged_vehicle.energy[charger.energy_type] - vehicle.energy[
+            charger.energy_type]  # kwh
         charger_price = station.charger_prices_per_kwh.get(charger_id)  # Currency
         charging_price = kwh_transacted * charger_price if charger_price else 0.0
 
@@ -69,7 +70,8 @@ def charge(sim: SimulationState,
             response.__cause__ = veh_error
             return response, None
         else:
-            report = vehicle_charge_event(vehicle, updated_vehicle, sim_with_vehicle, updated_station, charger)
+            report = vehicle_charge_event(vehicle, updated_vehicle, sim_with_vehicle,
+                                          updated_station, charger)
             env.reporter.file_report(report)
 
             return simulation_state_ops.modify_station(sim_with_vehicle, updated_station)
@@ -82,9 +84,7 @@ class MoveResult(NamedTuple):
     route_traversal: RouteTraversal = RouteTraversal()
 
 
-def _apply_route_traversal(sim: SimulationState,
-                           env: Environment,
-                           vehicle_id: VehicleId,
+def _apply_route_traversal(sim: SimulationState, env: Environment, vehicle_id: VehicleId,
                            route: Route) -> Tuple[Optional[Exception], Optional[MoveResult]]:
     """
     Moves the vehicle and consumes energy.
@@ -125,13 +125,16 @@ def _apply_route_traversal(sim: SimulationState,
         step_distance_km = traverse_result.traversal_distance_km
 
         if not experienced_route:
-            return SimulationStateError(f"after traversal, no route was experienced for vehicle {vehicle_id} at time {sim.sim_time}"), None
+            return SimulationStateError(
+                f"after traversal, no route was experienced for vehicle {vehicle_id} at time {sim.sim_time}"
+            ), None
         else:
             last_link_traversed = experienced_route[-1]
             # quick trick here to turn the final traversed link into a Positional Link (where link.start == link.end)
             # used to represent the Vehicle's new position
             vehicle_position = EntityPosition(last_link_traversed.link_id, last_link_traversed.end)
-            updated_vehicle = less_energy_vehicle.modify_position(position=vehicle_position).tick_distance_traveled_km(step_distance_km)
+            updated_vehicle = less_energy_vehicle.modify_position(
+                position=vehicle_position).tick_distance_traveled_km(step_distance_km)
             error, updated_sim = simulation_state_ops.modify_vehicle(sim, updated_vehicle)
             if error:
                 response = SimulationStateError(
@@ -142,9 +145,9 @@ def _apply_route_traversal(sim: SimulationState,
                 return None, MoveResult(updated_sim, vehicle, updated_vehicle, traverse_result)
 
 
-def _go_out_of_service_on_empty(sim: SimulationState,
-                                env: Environment,
-                                vehicle_id: VehicleId) -> Tuple[Optional[Exception], Optional[SimulationState]]:
+def _go_out_of_service_on_empty(
+        sim: SimulationState, env: Environment,
+        vehicle_id: VehicleId) -> Tuple[Optional[Exception], Optional[SimulationState]]:
     """
     sets a vehicle to OutOfService if it is out of energy after a move event
 
@@ -156,9 +159,11 @@ def _go_out_of_service_on_empty(sim: SimulationState,
     moved_vehicle = sim.vehicles.get(vehicle_id)
     mechatronics = env.mechatronics.get(moved_vehicle.mechatronics_id)
     if not moved_vehicle:
-        return SimulationStateError(f"vehicle not found; context: vehicle {vehicle_id} going out of service"), None
+        return SimulationStateError(
+            f"vehicle not found; context: vehicle {vehicle_id} going out of service"), None
     elif not mechatronics:
-        return SimulationStateError(f"cannot find {moved_vehicle.mechatronics_id} in environment"), None
+        return SimulationStateError(
+            f"cannot find {moved_vehicle.mechatronics_id} in environment"), None
     elif mechatronics.is_empty(moved_vehicle):
         # todo: are we in ServicingTrip or ServicingPoolingTrip? report stranded passengers!!!
         # error, exit_sim = moved_vehicle.vehicle_state.exit(sim, env)
@@ -169,17 +174,15 @@ def _go_out_of_service_on_empty(sim: SimulationState,
         #     return SimulationStateError(
         #         f"vehicle {moved_vehicle.id} cannot exit state {moved_vehicle.vehicle_state.__class__.__name__}"), None
         # else:
-        #     next_state = OutOfService(vehicle_id)
+        #     next_state = OutOfService.build(vehicle_id)
         #     return next_state.enter(exit_sim, env)
-        next_state = OutOfService(vehicle_id)
+        next_state = OutOfService.build(vehicle_id)
         return next_state.enter(sim, env)
     else:
         return None, None
 
 
-def move(sim: SimulationState,
-         env: Environment,
-         vehicle_id: VehicleId,
+def move(sim: SimulationState, env: Environment, vehicle_id: VehicleId,
          route: Route) -> Tuple[Optional[Exception], Optional[MoveResult]]:
     """
     moves the vehicle, and moves to OutOfService if resulting vehicle energy is empty
@@ -197,10 +200,10 @@ def move(sim: SimulationState,
     elif not move_result:
         return None, MoveResult(sim)
     else:
-        empty_check_error, empty_vehicle_sim = _go_out_of_service_on_empty(move_result.sim, env, vehicle_id)
+        empty_check_error, empty_vehicle_sim = _go_out_of_service_on_empty(
+            move_result.sim, env, vehicle_id)
         if empty_check_error:
-            response = SimulationStateError(
-                f"failure during move for vehicle {vehicle_id}")
+            response = SimulationStateError(f"failure during move for vehicle {vehicle_id}")
             response.__cause__ = empty_check_error
             return response, None
         elif empty_vehicle_sim:
@@ -209,5 +212,3 @@ def move(sim: SimulationState,
             report = vehicle_move_event(move_result, env)
             env.reporter.file_report(report)
             return None, move_result
-
-


Test patch:
diff --git a/tests/test_human_driver_state.py b/tests/test_human_driver_state.py
index 801d41d7..0deba822 100644
--- a/tests/test_human_driver_state.py
+++ b/tests/test_human_driver_state.py
@@ -11,18 +11,14 @@ def off_schedule(a, b):
     return False
 
 
-test_schedules = {
-    "on": on_schedule,
-    "off": off_schedule
-}
+test_schedules = {"on": on_schedule, "off": off_schedule}
 
 
 class TestHumanDriverState(TestCase):
-
     def test_stays_available(self):
         state = mock_human_driver(available=True, schedule_id="on")
         veh = mock_vehicle(driver_state=state)
-        sim = mock_sim(vehicles=(veh,))
+        sim = mock_sim(vehicles=(veh, ))
 
         env = mock_env(schedules=test_schedules)
         error, updated_sim = state.update(sim, env)
@@ -35,7 +31,7 @@ def test_stays_available(self):
     def test_becomes_unavailable(self):
         state = mock_human_driver(available=True, schedule_id="off")
         veh = mock_vehicle(driver_state=state)
-        sim = mock_sim(vehicles=(veh,))
+        sim = mock_sim(vehicles=(veh, ))
 
         env = mock_env(schedules=test_schedules)
         error, updated_sim = state.update(sim, env)
@@ -48,15 +44,22 @@ def test_becomes_unavailable(self):
     def test_becomes_unavailable_while_station_charging(self):
         state = mock_human_driver(available=True, schedule_id="off", home_base_id="home_base")
         veh_id = DefaultIds.mock_vehicle_id()
-        veh_state = ChargingStation(veh_id, "away_station", "DCFC")
+        veh_state = ChargingStation.build(veh_id, "away_station", "DCFC")
 
         # vehicle is at away_station, but has a home_base with a home_station
-        veh = mock_vehicle(lat=39.7664622, lon=-105.0390823, vehicle_id=veh_id, driver_state=state, vehicle_state=veh_state)
+        veh = mock_vehicle(lat=39.7664622,
+                           lon=-105.0390823,
+                           vehicle_id=veh_id,
+                           driver_state=state,
+                           vehicle_state=veh_state)
         away_station = mock_station(lat=39.7664622, lon=-105.0390823, station_id="away_station")
-        home_base = mock_base(lat=39.7544977, lon=-104.9809168, base_id="home_base", station_id="home_station")
+        home_base = mock_base(lat=39.7544977,
+                              lon=-104.9809168,
+                              base_id="home_base",
+                              station_id="home_station")
         home_station = mock_station(lat=39.7544977, lon=-104.9809168, station_id="home_station")
 
-        sim = mock_sim(vehicles=(veh,), bases=(home_base,), stations=(home_station, away_station))
+        sim = mock_sim(vehicles=(veh, ), bases=(home_base, ), stations=(home_station, away_station))
         env = mock_env(schedules=test_schedules)
         error, updated_sim = state.update(sim, env)
         if error:
@@ -64,19 +67,27 @@ def test_becomes_unavailable_while_station_charging(self):
         else:
             updated_veh = updated_sim.vehicles.get(veh_id)
             instruction = updated_veh.driver_state.generate_instruction(updated_sim, env, ())
-            self.assertEqual(instruction, DispatchBaseInstruction("v0", "home_base"), "should have been sent home where charging is cheaper")
+            self.assertEqual(instruction, DispatchBaseInstruction("v0", "home_base"),
+                             "should have been sent home where charging is cheaper")
 
     def test_becomes_unavailable_while_station_charging_no_home_charger(self):
         state = mock_human_driver(available=True, schedule_id="off", home_base_id="home_base")
         veh_id = DefaultIds.mock_vehicle_id()
-        veh_state = ChargingStation(veh_id, "away_station", "DCFC")
+        veh_state = ChargingStation.build(veh_id, "away_station", "DCFC")
 
         # vehicle is at away_station, but has a home_base with a home_station
-        veh = mock_vehicle(lat=39.7664622, lon=-105.0390823, vehicle_id=veh_id, driver_state=state, vehicle_state=veh_state)
+        veh = mock_vehicle(lat=39.7664622,
+                           lon=-105.0390823,
+                           vehicle_id=veh_id,
+                           driver_state=state,
+                           vehicle_state=veh_state)
         away_station = mock_station(lat=39.7664622, lon=-105.0390823, station_id="away_station")
-        home_base = mock_base(lat=39.7544977, lon=-104.9809168, base_id="home_base", station_id=None)
+        home_base = mock_base(lat=39.7544977,
+                              lon=-104.9809168,
+                              base_id="home_base",
+                              station_id=None)
 
-        sim = mock_sim(vehicles=(veh,), bases=(home_base,), stations=(away_station,))
+        sim = mock_sim(vehicles=(veh, ), bases=(home_base, ), stations=(away_station, ))
         env = mock_env(schedules=test_schedules)
         error, updated_sim = state.update(sim, env)
         if error:
@@ -84,14 +95,16 @@ def test_becomes_unavailable_while_station_charging_no_home_charger(self):
         else:
             updated_veh = updated_sim.vehicles.get(veh_id)
             instruction = updated_veh.driver_state.generate_instruction(updated_sim, env, ())
-            self.assertEqual(instruction, DispatchBaseInstruction("v0", "home_base"), "should have been sent home, "
-                                                                                      "enough charge to get to "
-                                                                                      "station in the morning")
+            self.assertEqual(
+                instruction, DispatchBaseInstruction("v0", "home_base"),
+                "should have been sent home, "
+                "enough charge to get to "
+                "station in the morning")
 
     def test_stays_unavailable(self):
         state = mock_human_driver(available=False, schedule_id="off")
         veh = mock_vehicle(driver_state=state)
-        sim = mock_sim(vehicles=(veh,))
+        sim = mock_sim(vehicles=(veh, ))
 
         env = mock_env(schedules=test_schedules)
         error, updated_sim = state.update(sim, env)
@@ -104,7 +117,7 @@ def test_stays_unavailable(self):
     def test_becomes_available(self):
         state = mock_human_driver(available=False, schedule_id="on")
         veh = mock_vehicle(driver_state=state)
-        sim = mock_sim(vehicles=(veh,))
+        sim = mock_sim(vehicles=(veh, ))
 
         env = mock_env(schedules=test_schedules)
         error, updated_sim = state.update(sim, env)
@@ -120,7 +133,7 @@ def test_go_home_instruction(self):
         veh = mock_vehicle_from_geoid(driver_state=state, geoid=somewhere())
         station = mock_station_from_geoid(geoid=somewhere_else())
         base = mock_base_from_geoid(station_id=station.id, geoid=somewhere_else())
-        sim = mock_sim(vehicles=(veh,), bases=(base,), stations=(station,))
+        sim = mock_sim(vehicles=(veh, ), bases=(base, ), stations=(station, ))
         env = mock_env()
 
         i = veh.driver_state.generate_instruction(sim, env)
@@ -133,7 +146,7 @@ def test_home_charge_instruction(self):
         veh = mock_vehicle_from_geoid(driver_state=state, geoid=somewhere(), soc=0.5)
         station = mock_station_from_geoid(geoid=somewhere())
         base = mock_base_from_geoid(station_id=station.id, geoid=somewhere())
-        sim = mock_sim(vehicles=(veh,), bases=(base,), stations=(station,))
+        sim = mock_sim(vehicles=(veh, ), bases=(base, ), stations=(station, ))
         env = mock_env()
 
         i = veh.driver_state.generate_instruction(sim, env)
@@ -143,12 +156,13 @@ def test_home_charge_instruction(self):
     def test_reposition_instruction(self):
         # vehicle is at home but is available, should try to reposition
         state = mock_human_driver(available=True, schedule_id="on")
-        veh = mock_vehicle(driver_state=state, vehicle_state=ReserveBase(
-            DefaultIds.mock_vehicle_id(),
-            DefaultIds.mock_base_id(),
-        ))
+        veh = mock_vehicle(driver_state=state,
+                           vehicle_state=ReserveBase.build(
+                               DefaultIds.mock_vehicle_id(),
+                               DefaultIds.mock_base_id(),
+                           ))
         req = mock_request_from_geoids(origin=somewhere_else(), destination=somewhere())
-        sim = mock_sim(vehicles=(veh,))
+        sim = mock_sim(vehicles=(veh, ))
         _, sim_w_req = simulation_state_ops.add_request(sim, req)
         env = mock_env()
 
@@ -158,16 +172,14 @@ def test_reposition_instruction(self):
 
     def test_stop_fast_charging_instruction(self):
         state = mock_human_driver(available=True, schedule_id="on")
-        veh = mock_vehicle(
-            driver_state=state,
-            vehicle_state=ChargingStation(
-                vehicle_id=DefaultIds.mock_vehicle_id(),
-                station_id=DefaultIds.mock_station_id(),
-                charger_id=mock_dcfc_charger_id(),
-            ),
-            soc=0.9
-        )
-        sim = mock_sim(vehicles=(veh,))
+        veh = mock_vehicle(driver_state=state,
+                           vehicle_state=ChargingStation.build(
+                               vehicle_id=DefaultIds.mock_vehicle_id(),
+                               station_id=DefaultIds.mock_station_id(),
+                               charger_id=mock_dcfc_charger_id(),
+                           ),
+                           soc=0.9)
+        sim = mock_sim(vehicles=(veh, ))
         env = mock_env()
 
         # the default soc limit for charging at a station is 0.8 so we should generate an idle instruction.
@@ -179,18 +191,14 @@ def test_turn_off_at_home(self):
         state = mock_human_driver(available=False, schedule_id="off")
         veh = mock_vehicle_from_geoid(
             driver_state=state,
-            vehicle_state=Idle(vehicle_id=DefaultIds.mock_vehicle_id()),
+            vehicle_state=Idle.build(vehicle_id=DefaultIds.mock_vehicle_id()),
             geoid=somewhere(),
         )
         base = mock_base_from_geoid(geoid=somewhere())
-        sim = mock_sim(vehicles=(veh,), bases=(base,))
+        sim = mock_sim(vehicles=(veh, ), bases=(base, ))
         env = mock_env()
 
         # the driver is at home and idle so it should try to turn off (i.e. transition to ReserveBase).
         i = veh.driver_state.generate_instruction(sim, env)
 
         self.assertIsInstance(i, ReserveBaseInstruction)
-
-
-
-
diff --git a/tests/test_station_load_handler.py b/tests/test_station_load_handler.py
index ec154ab3..f8f6fbce 100644
--- a/tests/test_station_load_handler.py
+++ b/tests/test_station_load_handler.py
@@ -5,7 +5,6 @@
 
 
 class TestStationLoadHandler(TestCase):
-
     def test_correctly_reads_one_vehicle_charge_event(self):
         """
         this test is mostly here just to make sure we don't mess with
@@ -13,14 +12,12 @@ def test_correctly_reads_one_vehicle_charge_event(self):
         break station load reporting
         :return:
         """
-        state = ChargingStation(
-            vehicle_id=DefaultIds.mock_vehicle_id(),
-            station_id=DefaultIds.mock_station_id(),
-            charger_id=mock_dcfc_charger_id()
-        )
+        state = ChargingStation.build(vehicle_id=DefaultIds.mock_vehicle_id(),
+                                      station_id=DefaultIds.mock_station_id(),
+                                      charger_id=mock_dcfc_charger_id())
         veh = mock_vehicle(soc=0.1)
         sta = mock_station()
-        sim = mock_sim(vehicles=(veh,), stations=(sta,))
+        sim = mock_sim(vehicles=(veh, ), stations=(sta, ))
         env = mock_env()
 
         err, sim_charging = state.enter(sim, env)
diff --git a/tests/test_step_simulation_ops.py b/tests/test_step_simulation_ops.py
index 8a989b76..3aedf829 100644
--- a/tests/test_step_simulation_ops.py
+++ b/tests/test_step_simulation_ops.py
@@ -5,7 +5,6 @@
 
 
 class TestStepSimulationOps(TestCase):
-
     def test_step_vehicle(self):
         """
         build a sim with two idle vehicles and only step one of them for 10 time steps (600 seconds)
@@ -27,7 +26,8 @@ def test_step_vehicle(self):
         veh2_idle_time = vehicle2.vehicle_state.idle_duration
 
         self.assertEqual(veh1_idle_time, 0, "vehicle 1 should not have idled")
-        self.assertEqual(veh2_idle_time, 600, "vehicle 2 should have idled for 10 time steps (600 s)")
+        self.assertEqual(veh2_idle_time, 600,
+                         "vehicle 2 should have idled for 10 time steps (600 s)")
 
     def test_step_vehicle_state_change(self):
         """
@@ -38,13 +38,13 @@ def test_step_vehicle_state_change(self):
         station = mock_station("s1")
         vehicle1 = mock_vehicle(
             vehicle_id="1",
-            vehicle_state=ChargingStation("1", "s1", mock_dcfc_charger_id()),
+            vehicle_state=ChargingStation.build("1", "s1", mock_dcfc_charger_id()),
         )
         vehicle2 = mock_vehicle(
             vehicle_id="2",
-            vehicle_state=ChargingStation("2", "s1", mock_dcfc_charger_id()),
+            vehicle_state=ChargingStation.build("2", "s1", mock_dcfc_charger_id()),
         )
-        sim = mock_sim(vehicles=(vehicle1, vehicle2), stations=(station,))
+        sim = mock_sim(vehicles=(vehicle1, vehicle2), stations=(station, ))
         env = mock_env()
 
         for _ in range(10):
@@ -56,5 +56,6 @@ def test_step_vehicle_state_change(self):
         veh1_state = vehicle1.vehicle_state
         veh2_state = vehicle2.vehicle_state
 
-        self.assertIsInstance(veh1_state, ChargingStation, "vehicle 1 should still be in charging state")
+        self.assertIsInstance(veh1_state, ChargingStation,
+                              "vehicle 1 should still be in charging state")
         self.assertIsInstance(veh2_state, Idle, "vehicle 2 should have transitioned to idle")
diff --git a/tests/test_vehicle_state.py b/tests/test_vehicle_state.py
index f71a2282..25e24c51 100644
--- a/tests/test_vehicle_state.py
+++ b/tests/test_vehicle_state.py
@@ -18,13 +18,10 @@ def test_charging_station_enter(self):
         vehicle = mock_vehicle()
         station = mock_station()
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ))
         env = mock_env()
 
-        state = ChargingStation(vehicle.id, station.id, charger)
+        state = ChargingStation.build(vehicle.id, station.id, charger)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(error, "should have no errors")
@@ -32,20 +29,18 @@ def test_charging_station_enter(self):
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
         updated_station = updated_sim.stations.get(station.id)
         available_chargers = updated_station.available_chargers.get(charger)
-        self.assertIsInstance(updated_vehicle.vehicle_state, ChargingStation, "should be in a charging state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, ChargingStation,
+                              "should be in a charging state")
         self.assertEquals(available_chargers, 0, "should have claimed the only DCFC charger_id")
 
     def test_charging_station_bad_membership(self):
         vehicle = mock_vehicle(membership=Membership.single_membership("uber"))
         station = mock_station(membership=Membership.single_membership("lyft"))
 
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ))
         env = mock_env()
 
-        state = ChargingStation(vehicle.id, station.id, mock_dcfc_charger_id())
+        state = ChargingStation.build(vehicle.id, station.id, mock_dcfc_charger_id())
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(updated_sim, "should return none for updated sim")
@@ -54,62 +49,57 @@ def test_charging_station_exit(self):
         vehicle = mock_vehicle()
         station = mock_station()
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ))
         env = mock_env()
 
-        state = ChargingStation(vehicle.id, station.id, charger)
+        state = ChargingStation.build(vehicle.id, station.id, charger)
         enter_error, updated_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
         # begin test
-        error, updated_sim = state.exit(Idle(vehicle.id), updated_sim, env)
+        error, updated_sim = state.exit(Idle.build(vehicle.id), updated_sim, env)
 
         self.assertIsNone(error, "should have no errors")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
         updated_station = updated_sim.stations.get(station.id)
         available_chargers = updated_station.available_chargers.get(charger)
-        self.assertIsInstance(updated_vehicle.vehicle_state, ChargingStation, "should still be in a charging state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, ChargingStation,
+                              "should still be in a charging state")
         self.assertEquals(available_chargers, 1, "should have returned the only DCFC charger_id")
 
     def test_charging_station_update(self):
         vehicle = mock_vehicle(soc=0.5)
         station = mock_station()
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ))
         env = mock_env()
 
-        state = ChargingStation(vehicle.id, station.id, charger)
+        state = ChargingStation.build(vehicle.id, station.id, charger)
         enter_error, sim_with_charging_vehicle = state.enter(sim, env)
+        entered_state = sim_with_charging_vehicle.vehicles.get(vehicle.id).vehicle_state
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
         update_error, sim_updated = state.update(sim_with_charging_vehicle, env)
         self.assertIsNone(update_error, "should have no error from update call")
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
-        self.assertAlmostEqual(
-            first=updated_vehicle.energy[EnergyType.ELECTRIC],
-            second=vehicle.energy[EnergyType.ELECTRIC] + 0.76,
-            places=2,
-            msg="should have charged for 60 seconds")
+        self.assertAlmostEqual(first=updated_vehicle.energy[EnergyType.ELECTRIC],
+                               second=vehicle.energy[EnergyType.ELECTRIC] + 0.76,
+                               places=2,
+                               msg="should have charged for 60 seconds")
+
+        self.assertEqual(entered_state.instance_id, updated_vehicle.vehicle_state.instance_id,
+                         "should be the same instance")
 
     def test_charging_station_update_terminal(self):
         vehicle = mock_vehicle(soc=1)
         station = mock_station()
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ))
         env = mock_env()
 
-        state = ChargingStation(vehicle.id, station.id, charger)
+        state = ChargingStation.build(vehicle.id, station.id, charger)
         enter_error, sim_with_charging_vehicle = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
@@ -118,18 +108,18 @@ def test_charging_station_update_terminal(self):
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
         updated_station = sim_updated.stations.get(station.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, Idle, "vehicle should be in idle state")
-        self.assertEquals(updated_station.available_chargers.get(charger), 1, "should have returned the charger_id")
+        self.assertIsInstance(updated_vehicle.vehicle_state, Idle,
+                              "vehicle should be in idle state")
+        self.assertEquals(updated_station.available_chargers.get(charger), 1,
+                          "should have returned the charger_id")
 
     def test_charging_station_enter_with_no_station(self):
         vehicle = mock_vehicle(soc=0.99)
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-        )
+        sim = mock_sim(vehicles=(vehicle, ), )
         env = mock_env()
 
-        state = ChargingStation(vehicle.id, DefaultIds.mock_station_id(), charger)
+        state = ChargingStation.build(vehicle.id, DefaultIds.mock_station_id(), charger)
         enter_error, sim_with_charging_vehicle = state.enter(sim, env)
 
         self.assertIsInstance(enter_error, Exception, "should have exception")
@@ -137,12 +127,11 @@ def test_charging_station_enter_with_no_station(self):
     def test_charging_station_enter_with_no_vehicle(self):
         station = mock_station()
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            stations=(station,),
-        )
+        sim = mock_sim(stations=(station, ), )
         env = mock_env()
 
-        state = ChargingStation(DefaultIds.mock_vehicle_id(), DefaultIds.mock_station_id(), charger)
+        state = ChargingStation.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_station_id(),
+                                      charger)
         enter_error, sim_with_charging_vehicle = state.enter(sim, env)
 
         self.assertIsInstance(enter_error, Exception, "should have exception")
@@ -152,41 +141,47 @@ def test_charging_station_invalid_charger(self):
         charger = mock_dcfc_charger_id()
         vehicle = mock_vehicle(mechatronics=mock_ice())
         sim = mock_sim(
-            stations=(station,),
-            vehicles=(vehicle,),
+            stations=(station, ),
+            vehicles=(vehicle, ),
         )
         env = mock_env(mechatronics={DefaultIds.mock_mechatronics_ice_id(): mock_ice()})
 
-        state = ChargingStation(DefaultIds.mock_vehicle_id(), DefaultIds.mock_station_id(), charger)
+        state = ChargingStation.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_station_id(),
+                                      charger)
         enter_error, sim_with_charging_vehicle = state.enter(sim, env)
 
         self.assertIsInstance(enter_error, Exception, "should have exception")
 
     def test_charging_station_subsequent_charge_instructions(self):
         vehicle = mock_vehicle()
-        station = mock_station(chargers=immutables.Map({mock_l2_charger_id(): 1, mock_dcfc_charger_id(): 1}))
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,)
-        )
+        station = mock_station(chargers=immutables.Map({
+            mock_l2_charger_id(): 1,
+            mock_dcfc_charger_id(): 1
+        }))
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ))
         env = mock_env()
 
-        state = ChargingStation(vehicle.id, station.id, mock_dcfc_charger_id())
+        state = ChargingStation.build(vehicle.id, station.id, mock_dcfc_charger_id())
         err1, sim1 = state.enter(sim, env)
+        entered_state = sim1.vehicles.get(vehicle.id).vehicle_state
         self.assertIsNone(err1, "test precondition (enter works correctly) not met")
 
-        next_state = ChargingStation(vehicle.id, station.id, mock_l2_charger_id())
+        next_state = ChargingStation.build(vehicle.id, station.id, mock_l2_charger_id())
         err2, sim2 = entity_state_ops.transition_previous_to_next(sim1, env, state, next_state)
+        next_entered_state = sim2.vehicles.get(vehicle.id).vehicle_state
 
         self.assertIsNone(err2, "two subsequent charge instructions should not produce an error")
         self.assertIsNotNone(sim2, "two subsequent charge instructions should update the sim state")
+        self.assertNotEqual(entered_state.instance_id, next_entered_state.instance_id,
+                            "should have different instance ids")
 
         updated_station = sim2.stations.get(station.id)
         self.assertIsNotNone(updated_station, "station not found in sim")
         dc_available = updated_station.available_chargers.get(mock_dcfc_charger_id())
         l2_available = updated_station.available_chargers.get(mock_l2_charger_id())
         self.assertEqual(dc_available, 1, "should have released DC charger_id on second transition")
-        self.assertEqual(l2_available, 0, "second instruction should have claimed the only L2 charger_id")
+        self.assertEqual(l2_available, 0,
+                         "second instruction should have claimed the only L2 charger_id")
 
     ####################################################################################################################
     # ChargingBase #####################################################################################################
@@ -197,14 +192,10 @@ def test_charging_base_enter(self):
         station = mock_station()
         base = mock_base(station_id=DefaultIds.mock_station_id())
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,),
-            bases=(base,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ), bases=(base, ))
         env = mock_env()
 
-        state = ChargingBase(vehicle.id, base.id, charger)
+        state = ChargingBase.build(vehicle.id, base.id, charger)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(error, "should have no errors")
@@ -212,7 +203,8 @@ def test_charging_base_enter(self):
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
         updated_station = updated_sim.stations.get(station.id)
         available_chargers = updated_station.available_chargers.get(charger)
-        self.assertIsInstance(updated_vehicle.vehicle_state, ChargingBase, "should be in a charging state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, ChargingBase,
+                              "should be in a charging state")
         self.assertEquals(available_chargers, 0, "should have claimed the only DCFC charger_id")
 
     def test_charging_base_bad_membership(self):
@@ -224,14 +216,10 @@ def test_charging_base_bad_membership(self):
         station = mock_station(membership=base.membership)
 
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,),
-            bases=(base,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ), bases=(base, ))
         env = mock_env()
 
-        state = ChargingBase(vehicle.id, base.id, charger)
+        state = ChargingBase.build(vehicle.id, base.id, charger)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(updated_sim, "should have returned None for updated_sim")
@@ -242,13 +230,13 @@ def test_charging_base_invalid_charger(self):
         charger = mock_l2_charger_id()
         vehicle = mock_vehicle(mechatronics=mock_ice())
         sim = mock_sim(
-            stations=(station,),
-            vehicles=(vehicle,),
-            bases=(base,),
+            stations=(station, ),
+            vehicles=(vehicle, ),
+            bases=(base, ),
         )
         env = mock_env(mechatronics={DefaultIds.mock_mechatronics_ice_id(): mock_ice()})
 
-        state = ChargingBase(vehicle.id, base.id, charger)
+        state = ChargingBase.build(vehicle.id, base.id, charger)
         enter_error, sim_with_charging_vehicle = state.enter(sim, env)
 
         self.assertIsInstance(enter_error, Exception, "should have exception")
@@ -258,26 +246,23 @@ def test_charging_base_exit(self):
         station = mock_station()
         base = mock_base(station_id=DefaultIds.mock_station_id())
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,),
-            bases=(base,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ), bases=(base, ))
         env = mock_env()
 
-        state = ChargingBase(vehicle.id, base.id, charger)
+        state = ChargingBase.build(vehicle.id, base.id, charger)
         enter_error, updated_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
         # begin test
-        error, updated_sim = state.exit(Idle(vehicle.id), updated_sim, env)
+        error, updated_sim = state.exit(Idle.build(vehicle.id), updated_sim, env)
 
         self.assertIsNone(error, "should have no errors")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
         updated_station = updated_sim.stations.get(station.id)
         available_chargers = updated_station.available_chargers.get(charger)
-        self.assertIsInstance(updated_vehicle.vehicle_state, ChargingBase, "should still be in a charging state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, ChargingBase,
+                              "should still be in a charging state")
         self.assertEquals(available_chargers, 1, "should have returned the only DCFC charger_id")
 
     def test_charging_base_update(self):
@@ -285,40 +270,35 @@ def test_charging_base_update(self):
         station = mock_station()
         base = mock_base(station_id=DefaultIds.mock_station_id())
         charger = mock_l2_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,),
-            bases=(base,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ), bases=(base, ))
         env = mock_env()
 
-        state = ChargingBase(vehicle.id, base.id, charger)
+        state = ChargingBase.build(vehicle.id, base.id, charger)
         enter_error, sim_with_charging_vehicle = state.enter(sim, env)
+        entered_state = sim_with_charging_vehicle.vehicles.get(vehicle.id).vehicle_state
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
         update_error, sim_updated = state.update(sim_with_charging_vehicle, env)
         self.assertIsNone(update_error, "should have no error from update call")
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
-        self.assertAlmostEqual(
-            first=updated_vehicle.energy[EnergyType.ELECTRIC],
-            second=vehicle.energy[EnergyType.ELECTRIC] + 0.12,
-            places=2,
-            msg="should have charged for 60 seconds")
+        self.assertAlmostEqual(first=updated_vehicle.energy[EnergyType.ELECTRIC],
+                               second=vehicle.energy[EnergyType.ELECTRIC] + 0.12,
+                               places=2,
+                               msg="should have charged for 60 seconds")
+
+        self.assertEqual(entered_state.instance_id, updated_vehicle.vehicle_state.instance_id,
+                         "should be the same instance")
 
     def test_charging_base_update_terminal(self):
         vehicle = mock_vehicle(soc=1.0)
         station = mock_station()
         base = mock_base(station_id=DefaultIds.mock_station_id())
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,),
-            bases=(base,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ), bases=(base, ))
         env = mock_env()
 
-        state = ChargingBase(vehicle.id, base.id, charger)
+        state = ChargingBase.build(vehicle.id, base.id, charger)
         enter_error, sim_with_charging_vehicle = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
@@ -327,20 +307,22 @@ def test_charging_base_update_terminal(self):
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
         updated_base = sim_updated.bases.get(base.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, ReserveBase, "vehicle should be in ReserveBase state")
-        self.assertEquals(updated_base.available_stalls, 0, "should have taken the only available stall")
+        self.assertIsInstance(updated_vehicle.vehicle_state, ReserveBase,
+                              "vehicle should be in ReserveBase state")
+        self.assertEquals(updated_base.available_stalls, 0,
+                          "should have taken the only available stall")
 
     def test_charging_base_enter_with_no_base(self):
         vehicle = mock_vehicle(soc=1.0)
         station = mock_station()
         charger = mock_dcfc_charger_id()
         sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,),
+            vehicles=(vehicle, ),
+            stations=(station, ),
         )
         env = mock_env()
 
-        state = ChargingBase(vehicle.id, DefaultIds.mock_base_id(), charger)
+        state = ChargingBase.build(vehicle.id, DefaultIds.mock_base_id(), charger)
         enter_error, _ = state.enter(sim, env)
 
         self.assertIsInstance(enter_error, Exception, "should have exception")
@@ -349,13 +331,10 @@ def test_charging_base_enter_with_no_station(self):
         vehicle = mock_vehicle(soc=1.0)
         base = mock_base(station_id=DefaultIds.mock_station_id())
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            bases=(base,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), bases=(base, ))
         env = mock_env()
 
-        state = ChargingBase(vehicle.id, base.id, charger)
+        state = ChargingBase.build(vehicle.id, base.id, charger)
         enter_error, _ = state.enter(sim, env)
 
         self.assertIsInstance(enter_error, Exception, "should have exception")
@@ -364,13 +343,10 @@ def test_charging_base_enter_with_missing_station_id(self):
         vehicle = mock_vehicle(soc=1.0)
         base = mock_base()
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            bases=(base,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), bases=(base, ))
         env = mock_env()
 
-        state = ChargingBase(vehicle.id, base.id, charger)
+        state = ChargingBase.build(vehicle.id, base.id, charger)
         enter_error, _ = state.enter(sim, env)
 
         self.assertIsInstance(enter_error, Exception, "should have exception")
@@ -379,33 +355,29 @@ def test_charging_base_enter_with_no_vehicle(self):
         station = mock_station()
         base = mock_base(station_id=DefaultIds.mock_station_id())
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            stations=(station,),
-            bases=(base,)
-        )
+        sim = mock_sim(stations=(station, ), bases=(base, ))
         env = mock_env()
 
-        state = ChargingBase(DefaultIds.mock_vehicle_id(), base.id, charger)
+        state = ChargingBase.build(DefaultIds.mock_vehicle_id(), base.id, charger)
         enter_error, sim_with_charging_vehicle = state.enter(sim, env)
 
         self.assertIsInstance(enter_error, Exception, "should have exception")
 
     def test_charging_base_subsequent_charge_instructions(self):
         vehicle = mock_vehicle()
-        station = mock_station(chargers=immutables.Map({mock_l2_charger_id(): 1, mock_dcfc_charger_id(): 1}))
+        station = mock_station(chargers=immutables.Map({
+            mock_l2_charger_id(): 1,
+            mock_dcfc_charger_id(): 1
+        }))
         base = mock_base(station_id=DefaultIds.mock_station_id())
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,),
-            bases=(base,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ), bases=(base, ))
         env = mock_env()
 
-        state = ChargingBase(vehicle.id, base.id, mock_dcfc_charger_id())
+        state = ChargingBase.build(vehicle.id, base.id, mock_dcfc_charger_id())
         err1, sim1 = state.enter(sim, env)
         self.assertIsNone(err1, "test precondition (enter works correctly) not met")
 
-        next_state = ChargingBase(vehicle.id, base.id, mock_l2_charger_id())
+        next_state = ChargingBase.build(vehicle.id, base.id, mock_l2_charger_id())
         err2, sim2 = entity_state_ops.transition_previous_to_next(sim1, env, state, next_state)
 
         self.assertIsNone(err2, "two subsequent charge instructions should not produce an error")
@@ -416,7 +388,8 @@ def test_charging_base_subsequent_charge_instructions(self):
         dc_available = updated_station.available_chargers.get(mock_dcfc_charger_id())
         l2_available = updated_station.available_chargers.get(mock_l2_charger_id())
         self.assertEqual(dc_available, 1, "should have released DC charger_id on second transition")
-        self.assertEqual(l2_available, 0, "second instruction should have claimed the only L2 charger_id")
+        self.assertEqual(l2_available, 0,
+                         "second instruction should have claimed the only L2 charger_id")
 
     ####################################################################################################################
     # DispatchBase #####################################################################################################
@@ -425,34 +398,29 @@ def test_charging_base_subsequent_charge_instructions(self):
     def test_dispatch_base_enter(self):
         vehicle = mock_vehicle()
         base = mock_base()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            bases=(base,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), bases=(base, ))
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, base.geoid)
 
-        state = DispatchBase(vehicle.id, base.id, route)
+        state = DispatchBase.build(vehicle.id, base.id, route)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(error, "should have no errors")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, DispatchBase, "should be in a dispatch to base state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, DispatchBase,
+                              "should be in a dispatch to base state")
         self.assertEquals(len(updated_vehicle.vehicle_state.route), 1, "should have a route")
 
     def test_dispatch_base_bad_membership(self):
         vehicle = mock_vehicle(membership=Membership.single_membership("uber"))
         base = mock_base(membership=Membership.single_membership("lyft"))
 
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            bases=(base,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), bases=(base, ))
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, base.geoid)
 
-        state = DispatchBase(vehicle.id, base.id, route)
+        state = DispatchBase.build(vehicle.id, base.id, route)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(updated_sim, "should have returned None for updated_sim")
@@ -460,19 +428,16 @@ def test_dispatch_base_bad_membership(self):
     def test_dispatch_base_exit(self):
         vehicle = mock_vehicle()
         base = mock_base()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            bases=(base,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), bases=(base, ))
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, base.geoid)
 
-        state = DispatchBase(vehicle.id, base.id, route)
+        state = DispatchBase.build(vehicle.id, base.id, route)
         enter_error, entered_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
         # begin test
-        error, exited_sim = state.exit(Idle(vehicle.id), entered_sim, env)
+        error, exited_sim = state.exit(Idle.build(vehicle.id), entered_sim, env)
 
         self.assertIsNone(error, "should have no errors")
         self.assertEquals(entered_sim, exited_sim, "should see no change due to exit")
@@ -482,39 +447,38 @@ def test_dispatch_base_update(self):
         omf_brewing = h3.geo_to_h3(39.7608873, -104.9845391, 15)
         vehicle = mock_vehicle_from_geoid(geoid=near)
         base = mock_base_from_geoid(geoid=omf_brewing)
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            bases=(base,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), bases=(base, ))
         env = mock_env()
         route = mock_route_from_geoids(near, omf_brewing)
 
-        state = DispatchBase(vehicle.id, base.id, route)
+        state = DispatchBase.build(vehicle.id, base.id, route)
         enter_error, sim_with_dispatched_vehicle = state.enter(sim, env)
+        entered_state = sim_with_dispatched_vehicle.vehicles.get(vehicle.id).vehicle_state
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
-        update_error, sim_updated = state.update(sim_with_dispatched_vehicle, env)
+        update_error, sim_updated = entered_state.update(sim_with_dispatched_vehicle, env)
         self.assertIsNone(update_error, "should have no error from update call")
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
         old_soc = env.mechatronics.get(vehicle.mechatronics_id).fuel_source_soc(vehicle)
-        new_soc = env.mechatronics.get(updated_vehicle.mechatronics_id).fuel_source_soc(updated_vehicle)
+        new_soc = env.mechatronics.get(
+            updated_vehicle.mechatronics_id).fuel_source_soc(updated_vehicle)
         self.assertNotEqual(vehicle.geoid, updated_vehicle.geoid, "should have moved")
         self.assertIsInstance(updated_vehicle.vehicle_state, DispatchBase,
                               "should still be in a dispatch to base state")
         self.assertLess(new_soc, old_soc, "should have less energy")
 
+        self.assertEqual(entered_state.instance_id, updated_vehicle.vehicle_state.instance_id,
+                         "should be the same instance")
+
     def test_dispatch_base_update_terminal(self):
         vehicle = mock_vehicle()
         base = mock_base()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            bases=(base,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), bases=(base, ))
         env = mock_env()
         route = ()  # empty route should trigger a default transition
 
-        state = DispatchBase(vehicle.id, base.id, route)
+        state = DispatchBase.build(vehicle.id, base.id, route)
         enter_error, sim_with_dispatch_vehicle = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
@@ -523,30 +487,28 @@ def test_dispatch_base_update_terminal(self):
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
         updated_base = sim_updated.bases.get(base.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, ReserveBase, "vehicle should be in ReserveBase state")
-        self.assertEquals(updated_base.available_stalls, 0, "should have taken the only available stall")
+        self.assertIsInstance(updated_vehicle.vehicle_state, ReserveBase,
+                              "vehicle should be in ReserveBase state")
+        self.assertEquals(updated_base.available_stalls, 0,
+                          "should have taken the only available stall")
 
     def test_dispatch_base_enter_no_base(self):
         vehicle = mock_vehicle()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-        )
+        sim = mock_sim(vehicles=(vehicle, ), )
         env = mock_env()
         route = ()
 
-        state = DispatchBase(vehicle.id, DefaultIds.mock_base_id(), route)
+        state = DispatchBase.build(vehicle.id, DefaultIds.mock_base_id(), route)
         enter_error, _ = state.enter(sim, env)
         self.assertIsInstance(enter_error, Exception, "should have exception")
 
     def test_dispatch_base_enter_no_vehicle(self):
         base = mock_base()
-        sim = mock_sim(
-            bases=(base,),
-        )
+        sim = mock_sim(bases=(base, ), )
         env = mock_env()
         route = ()
 
-        state = DispatchBase(DefaultIds.mock_vehicle_id(), base.id, route)
+        state = DispatchBase.build(DefaultIds.mock_vehicle_id(), base.id, route)
         enter_error, _ = state.enter(sim, env)
         self.assertIsInstance(enter_error, Exception, "should have exception")
 
@@ -556,13 +518,13 @@ def test_dispatch_base_enter_route_with_bad_source(self):
         vehicle = mock_vehicle()
         base = mock_base()
         sim = mock_sim(
-            vehicles=(vehicle,),
-            bases=(base,),
+            vehicles=(vehicle, ),
+            bases=(base, ),
         )
         env = mock_env()
         route = mock_route_from_geoids(omf_brewing, base.geoid)
 
-        state = DispatchBase(vehicle.id, base.id, route)
+        state = DispatchBase.build(vehicle.id, base.id, route)
         enter_error, enter_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "should be no error")
         self.assertIsNone(enter_sim, "invalid route should have not changed sim state")
@@ -573,13 +535,13 @@ def test_dispatch_base_enter_route_with_bad_destination(self):
         vehicle = mock_vehicle()
         base = mock_base()
         sim = mock_sim(
-            vehicles=(vehicle,),
-            bases=(base,),
+            vehicles=(vehicle, ),
+            bases=(base, ),
         )
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, omf_brewing)
 
-        state = DispatchBase(vehicle.id, base.id, route)
+        state = DispatchBase.build(vehicle.id, base.id, route)
         enter_error, enter_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "should be no error")
         self.assertIsNone(enter_sim, "invalid route should have not changed sim state")
@@ -593,14 +555,11 @@ def test_dispatch_station_enter(self):
         vehicle = mock_vehicle()
         station = mock_station_from_geoid(geoid=outer_range_brewing)
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ))
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, station.geoid)
 
-        state = DispatchStation(vehicle.id, station.id, route, charger)
+        state = DispatchStation.build(vehicle.id, station.id, route, charger)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(error, "should have no errors")
@@ -616,13 +575,13 @@ def test_dispatch_station_bad_membership(self):
         charger = mock_dcfc_charger_id()
 
         sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,),
+            vehicles=(vehicle, ),
+            stations=(station, ),
         )
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, station.geoid)
 
-        state = DispatchStation(vehicle.id, station.id, route, charger)
+        state = DispatchStation.build(vehicle.id, station.id, route, charger)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(updated_sim, "should have returned None for updated_sim")
@@ -632,39 +591,34 @@ def test_dispatch_station_enter_but_already_at_destination(self):
         vehicle = mock_vehicle()
         station = mock_station()
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ))
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, station.geoid)
 
-        state = DispatchStation(vehicle.id, station.id, route, charger)
+        state = DispatchStation.build(vehicle.id, station.id, route, charger)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(error, "should have no errors")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, ChargingStation,
-                              "should actually end up charging with no delay since we are already at the destination")
+        self.assertIsInstance(
+            updated_vehicle.vehicle_state, ChargingStation,
+            "should actually end up charging with no delay since we are already at the destination")
 
     def test_dispatch_station_exit(self):
         vehicle = mock_vehicle()
         station = mock_station()
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ))
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, station.geoid)
 
-        state = DispatchStation(vehicle.id, station.id, route, charger)
+        state = DispatchStation.build(vehicle.id, station.id, route, charger)
         enter_error, entered_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
         # begin test
-        error, exited_sim = state.exit(Idle(vehicle.id), entered_sim, env)
+        error, exited_sim = state.exit(Idle.build(vehicle.id), entered_sim, env)
 
         self.assertIsNone(error, "should have no errors")
         self.assertEquals(entered_sim, exited_sim, "should see no change due to exit")
@@ -675,44 +629,39 @@ def test_dispatch_station_update(self):
         vehicle = mock_vehicle_from_geoid(geoid=near)
         station = mock_station_from_geoid(geoid=omf_brewing)
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ))
         env = mock_env()
         route = mock_route_from_geoids(near, omf_brewing)
 
-        state = DispatchStation(vehicle.id, station.id, route, charger)
+        state = DispatchStation.build(vehicle.id, station.id, route, charger)
         enter_error, sim_with_dispatched_vehicle = state.enter(sim, env)
+        entered_state = sim_with_dispatched_vehicle.vehicles.get(vehicle.id).vehicle_state
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
-        update_error, sim_updated = state.update(sim_with_dispatched_vehicle, env)
+        update_error, sim_updated = entered_state.update(sim_with_dispatched_vehicle, env)
         self.assertIsNone(update_error, "should have no error from update call")
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
         old_soc = env.mechatronics.get(vehicle.mechatronics_id).fuel_source_soc(vehicle)
-        new_soc = env.mechatronics.get(updated_vehicle.mechatronics_id).fuel_source_soc(updated_vehicle)
+        new_soc = env.mechatronics.get(
+            updated_vehicle.mechatronics_id).fuel_source_soc(updated_vehicle)
         self.assertNotEqual(vehicle.geoid, updated_vehicle.geoid, "should have moved")
         self.assertIsInstance(updated_vehicle.vehicle_state, DispatchStation,
                               "should still be in a dispatch to station state")
         self.assertLess(new_soc, old_soc, "should have less energy")
 
+        self.assertEqual(entered_state.instance_id, updated_vehicle.vehicle_state.instance_id,
+                         "should be the same instance")
+
     def test_dispatch_station_update_terminal(self):
         initial_soc = 0.1
         charger = mock_dcfc_charger_id()
         route = ()  # empty route should trigger a default transition
-        state = DispatchStation(
-            DefaultIds.mock_vehicle_id(),
-            DefaultIds.mock_station_id(),
-            route,
-            charger
-        )
+        state = DispatchStation.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_station_id(),
+                                      route, charger)
         vehicle = mock_vehicle(soc=initial_soc, vehicle_state=state)
         station = mock_station()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,)
-        )
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ))
         env = mock_env()
 
         # enter_error, sim_with_dispatch_vehicle = state.enter(sim, env)
@@ -723,7 +672,8 @@ def test_dispatch_station_update_terminal(self):
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
         updated_station = sim_updated.stations.get(station.id)
-        new_soc = env.mechatronics.get(updated_vehicle.mechatronics_id).fuel_source_soc(updated_vehicle)
+        new_soc = env.mechatronics.get(
+            updated_vehicle.mechatronics_id).fuel_source_soc(updated_vehicle)
         self.assertIsInstance(updated_vehicle.vehicle_state, ChargingStation,
                               "vehicle should be in ChargingStation state")
         self.assertEquals(updated_station.available_chargers.get(charger), 0,
@@ -733,26 +683,22 @@ def test_dispatch_station_update_terminal(self):
     def test_dispatch_station_enter_no_station(self):
         vehicle = mock_vehicle()
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-        )
+        sim = mock_sim(vehicles=(vehicle, ), )
         env = mock_env()
         route = ()
 
-        state = DispatchStation(vehicle.id, DefaultIds.mock_station_id(), route, charger)
+        state = DispatchStation.build(vehicle.id, DefaultIds.mock_station_id(), route, charger)
         enter_error, _ = state.enter(sim, env)
         self.assertIsInstance(enter_error, Exception, "should have exception")
 
     def test_dispatch_station_enter_no_vehicle(self):
         station = mock_station()
         charger = mock_dcfc_charger_id()
-        sim = mock_sim(
-            stations=(station,),
-        )
+        sim = mock_sim(stations=(station, ), )
         env = mock_env()
         route = ()
 
-        state = DispatchStation(DefaultIds.mock_vehicle_id(), station.id, route, charger)
+        state = DispatchStation.build(DefaultIds.mock_vehicle_id(), station.id, route, charger)
         enter_error, _ = state.enter(sim, env)
         self.assertIsInstance(enter_error, Exception, "should have exception")
 
@@ -762,13 +708,13 @@ def test_dispatch_station_enter_route_with_bad_source(self):
         station = mock_station_from_geoid(geoid=omf_brewing)
         charger = mock_dcfc_charger_id()
         sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,),
+            vehicles=(vehicle, ),
+            stations=(station, ),
         )
         env = mock_env()
         route = mock_route_from_geoids(omf_brewing, station.geoid)
 
-        state = DispatchStation(vehicle.id, station.id, route, charger)
+        state = DispatchStation.build(vehicle.id, station.id, route, charger)
         enter_error, enter_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "should be no error")
         self.assertIsNone(enter_sim, "invalid route should have not changed sim state")
@@ -780,17 +726,19 @@ def test_dispatch_station_enter_route_with_bad_destination(self):
         station = mock_station_from_geoid(geoid=omf_brewing)
         charger = mock_dcfc_charger_id()
         sim = mock_sim(
-            vehicles=(vehicle,),
-            stations=(station,),
+            vehicles=(vehicle, ),
+            stations=(station, ),
         )
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, outer_range_brewing)
 
-        state = DispatchStation(vehicle.id, station.id, route, charger)
+        state = DispatchStation.build(vehicle.id, station.id, route, charger)
         enter_error, enter_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "should be no error")
-        self.assertIsNone(enter_sim,
-                          "invalid route should have not changed sim state - station and route destination do not match")
+        self.assertIsNone(
+            enter_sim,
+            "invalid route should have not changed sim state - station and route destination do not match"
+        )
 
     ####################################################################################################################
     # DispatchTrip #####################################################################################################
@@ -799,32 +747,34 @@ def test_dispatch_station_enter_route_with_bad_destination(self):
     def test_dispatch_trip_enter(self):
         vehicle = mock_vehicle()
         request = mock_request()
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.geoid)
 
-        state = DispatchTrip(vehicle.id, request.id, route)
+        state = DispatchTrip.build(vehicle.id, request.id, route)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(error, "should have no errors")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
         updated_request = updated_sim.requests.get(request.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, DispatchTrip, "should be in a dispatch to request state")
-        self.assertEquals(updated_request.dispatched_vehicle, vehicle.id, "request should be assigned this vehicle")
+        self.assertIsInstance(updated_vehicle.vehicle_state, DispatchTrip,
+                              "should be in a dispatch to request state")
+        self.assertEquals(updated_request.dispatched_vehicle, vehicle.id,
+                          "request should be assigned this vehicle")
         self.assertEquals(len(updated_vehicle.vehicle_state.route), 1, "should have a route")
 
     def test_dispatch_trip_bad_membership(self):
         vehicle = mock_vehicle(membership=Membership.single_membership("uber"))
         request = mock_request(fleet_id="lyft")
 
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.geoid)
 
-        state = DispatchTrip(vehicle.id, request.id, route)
+        state = DispatchTrip.build(vehicle.id, request.id, route)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(updated_sim, "should have returned None for updated_sim")
@@ -832,37 +782,42 @@ def test_dispatch_trip_bad_membership(self):
     def test_dispatch_trip_exit(self):
         vehicle = mock_vehicle()
         request = mock_request()
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.geoid)
 
-        state = DispatchTrip(vehicle.id, request.id, route)
+        state = DispatchTrip.build(vehicle.id, request.id, route)
         enter_error, entered_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
-        self.assertTrue(entered_sim.requests.get(request.id).dispatched_vehicle == vehicle.id, "test precondition not met")
+        self.assertTrue(
+            entered_sim.requests.get(request.id).dispatched_vehicle == vehicle.id,
+            "test precondition not met")
 
         # begin test
-        error, exited_sim = state.exit(Idle(vehicle.id), entered_sim, env)
+        error, exited_sim = state.exit(Idle.build(vehicle.id), entered_sim, env)
 
         self.assertIsNone(error, "should have no errors")
-        self.assertIsNone(exited_sim.requests.get(request.id).dispatched_vehicle, "should have unset the dispatched vehicle")
+        self.assertIsNone(
+            exited_sim.requests.get(request.id).dispatched_vehicle,
+            "should have unset the dispatched vehicle")
 
     def test_dispatch_trip_update(self):
         near = h3.geo_to_h3(39.7539, -104.974, 15)
         omf_brewing = h3.geo_to_h3(39.7608873, -104.9845391, 15)
         vehicle = mock_vehicle()
         request = mock_request_from_geoids(origin=omf_brewing)
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(near, omf_brewing)
 
-        state = DispatchTrip(vehicle.id, request.id, route)
+        state = DispatchTrip.build(vehicle.id, request.id, route)
         enter_error, sim_with_dispatched_vehicle = state.enter(sim, env)
+        entered_state = sim_with_dispatched_vehicle.vehicles.get(vehicle.id).vehicle_state
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
-        update_error, sim_updated = state.update(sim_with_dispatched_vehicle, env)
+        update_error, sim_updated = entered_state.update(sim_with_dispatched_vehicle, env)
         self.assertIsNone(update_error, "should have no error from update call")
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
@@ -874,6 +829,8 @@ def test_dispatch_trip_update(self):
             vehicle.energy[EnergyType.ELECTRIC],
             "should have less energy",
         )
+        self.assertEqual(entered_state.instance_id, updated_vehicle.vehicle_state.instance_id,
+                         "should be the same instance")
 
     def test_dispatch_trip_update_terminal(self):
         vehicle = mock_vehicle_from_geoid(geoid="8f268cdac30e2d3")
@@ -882,14 +839,13 @@ def test_dispatch_trip_update_terminal(self):
             destination="8f268cdac70e2d3",
         )._replace(  # a hack so we can test equality on this request later
             dispatched_vehicle=vehicle.id,
-            dispatched_vehicle_time=mock_sim().sim_time
-        )
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+            dispatched_vehicle_time=mock_sim().sim_time)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = ()  # vehicle is at the request
 
-        state = DispatchTrip(vehicle.id, request.id, route)
+        state = DispatchTrip.build(vehicle.id, request.id, route)
         enter_error, sim_with_dispatch_vehicle = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
@@ -898,18 +854,21 @@ def test_dispatch_trip_update_terminal(self):
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
         updated_request = sim_updated.requests.get(request.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, ServicingTrip, "vehicle should be in ServicingTrip state")
-        self.assertEquals(request, updated_vehicle.vehicle_state.request, "passengers not picked up")
-        self.assertIsNone(updated_request, "request should no longer exist as it has been picked up")
+        self.assertIsInstance(updated_vehicle.vehicle_state, ServicingTrip,
+                              "vehicle should be in ServicingTrip state")
+        self.assertEquals(request, updated_vehicle.vehicle_state.request,
+                          "passengers not picked up")
+        self.assertIsNone(updated_request,
+                          "request should no longer exist as it has been picked up")
 
     def test_dispatch_trip_enter_no_request(self):
         vehicle = mock_vehicle()
         request = mock_request()
-        sim = mock_sim(vehicles=(vehicle,))  # request not added to sim
+        sim = mock_sim(vehicles=(vehicle, ))  # request not added to sim
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.geoid)
 
-        state = DispatchTrip(vehicle.id, request.id, route)
+        state = DispatchTrip.build(vehicle.id, request.id, route)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(error, "should have no errors")
@@ -922,7 +881,7 @@ def test_dispatch_trip_enter_no_vehicle(self):
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.geoid)
 
-        state = DispatchTrip(vehicle.id, request.id, route)
+        state = DispatchTrip.build(vehicle.id, request.id, route)
         enter_error, updated_sim = state.enter(sim, env)
 
         self.assertIsInstance(enter_error, Exception, "should have exception")
@@ -931,12 +890,12 @@ def test_dispatch_trip_enter_route_with_bad_source(self):
         omf_brewing = h3.geo_to_h3(39.7608873, -104.9845391, 15)
         vehicle = mock_vehicle()
         request = mock_request()
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(omf_brewing, request.geoid)
 
-        state = DispatchTrip(vehicle.id, request.id, route)
+        state = DispatchTrip.build(vehicle.id, request.id, route)
         enter_error, enter_sim = state.enter(sim, env)
 
         self.assertIsNone(enter_error, "should be no error")
@@ -946,12 +905,12 @@ def test_dispatch_trip_enter_route_with_bad_destination(self):
         omf_brewing = h3.geo_to_h3(39.7608873, -104.9845391, 15)
         vehicle = mock_vehicle()
         request = mock_request()
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, omf_brewing)
 
-        state = DispatchTrip(vehicle.id, request.id, route)
+        state = DispatchTrip.build(vehicle.id, request.id, route)
         enter_error, enter_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "should be no error")
         self.assertIsNone(enter_sim, "invalid route should have not changed sim state")
@@ -963,31 +922,32 @@ def test_dispatch_trip_enter_route_with_bad_destination(self):
     def test_idle_enter(self):
         # should intially not be in an Idle state
         vehicle = mock_vehicle().modify_vehicle_state(
-            DispatchBase(DefaultIds.mock_vehicle_id(), DefaultIds.mock_base_id(), ()))
-        sim = mock_sim(vehicles=(vehicle,))
+            DispatchBase.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_base_id(), ()))
+        sim = mock_sim(vehicles=(vehicle, ))
         env = mock_env()
 
-        state = Idle(vehicle.id)
+        state = Idle.build(vehicle.id)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(error, "should have no errors")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, Idle, "should be in an idle to request state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, Idle,
+                              "should be in an idle to request state")
 
     def test_idle_exit(self):
         # should intially not be in an Idle state
         vehicle = mock_vehicle().modify_vehicle_state(
-            DispatchBase(DefaultIds.mock_vehicle_id(), DefaultIds.mock_base_id(), ()))
-        sim = mock_sim(vehicles=(vehicle,))
+            DispatchBase.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_base_id(), ()))
+        sim = mock_sim(vehicles=(vehicle, ))
         env = mock_env()
 
-        state = Idle(vehicle.id)
+        state = Idle.build(vehicle.id)
         enter_error, entered_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
         # begin test
-        error, exited_sim = state.exit(Idle(vehicle.id), entered_sim, env)
+        error, exited_sim = state.exit(Idle.build(vehicle.id), entered_sim, env)
 
         self.assertIsNone(error, "should have no errors")
         self.assertEquals(entered_sim, exited_sim, "should see no change due to exit")
@@ -995,37 +955,41 @@ def test_idle_exit(self):
     def test_idle_update(self):
         # should intially not be in an Idle state
         vehicle = mock_vehicle().modify_vehicle_state(
-            DispatchBase(DefaultIds.mock_vehicle_id(), DefaultIds.mock_base_id(), ()))
-        sim = mock_sim(vehicles=(vehicle,))
+            DispatchBase.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_base_id(), ()))
+        sim = mock_sim(vehicles=(vehicle, ))
         env = mock_env()
 
-        state = Idle(vehicle.id)
+        state = Idle.build(vehicle.id)
         enter_error, updated_sim = state.enter(sim, env)
+        entered_state = updated_sim.vehicles.get(vehicle.id).vehicle_state
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
-        update_error, sim_updated = state.update(updated_sim, env)
+        update_error, sim_updated = entered_state.update(updated_sim, env)
         self.assertIsNone(update_error, "should have no error from update call")
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
         self.assertEqual(vehicle.geoid, updated_vehicle.geoid, "should not have moved")
-        self.assertIsInstance(updated_vehicle.vehicle_state, Idle, "should still be in an Idle state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, Idle,
+                              "should still be in an Idle state")
         self.assertLess(
             updated_vehicle.energy[EnergyType.ELECTRIC],
             vehicle.energy[EnergyType.ELECTRIC],
             "should have less energy",
         )
         self.assertEqual(updated_vehicle.vehicle_state.idle_duration,
-                         sim.sim_timestep_duration_seconds,
-                         "should have recorded the idle time")
+                         sim.sim_timestep_duration_seconds, "should have recorded the idle time")
+        self.assertEqual(entered_state.instance_id, updated_vehicle.vehicle_state.instance_id,
+                         "should be the same instance")
 
     def test_idle_update_terminal(self):
         initial_soc = 0.0
-        ititial_state = DispatchBase(DefaultIds.mock_vehicle_id(), DefaultIds.mock_base_id(), ())
+        ititial_state = DispatchBase.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_base_id(),
+                                           ())
         vehicle = mock_vehicle(soc=initial_soc).modify_vehicle_state(ititial_state)
-        sim = mock_sim(vehicles=(vehicle,))
+        sim = mock_sim(vehicles=(vehicle, ))
         env = mock_env()
 
-        state = Idle(vehicle.id)
+        state = Idle.build(vehicle.id)
         enter_error, updated_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
@@ -1034,7 +998,8 @@ def test_idle_update_terminal(self):
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
         is_empty = env.mechatronics.get(updated_vehicle.mechatronics_id).is_empty(updated_vehicle)
-        self.assertIsInstance(updated_vehicle.vehicle_state, OutOfService, "vehicle should be OutOfService")
+        self.assertIsInstance(updated_vehicle.vehicle_state, OutOfService,
+                              "vehicle should be OutOfService")
         self.assertTrue(is_empty, "vehicle should have no energy")
 
     ####################################################################################################################
@@ -1044,29 +1009,30 @@ def test_idle_update_terminal(self):
     def test_out_of_service_enter(self):
         # should intially not be in an Idle state
         vehicle = mock_vehicle(soc=0.0)
-        sim = mock_sim(vehicles=(vehicle,))
+        sim = mock_sim(vehicles=(vehicle, ))
         env = mock_env()
 
-        state = OutOfService(vehicle.id)
+        state = OutOfService.build(vehicle.id)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(error, "should have no errors")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, OutOfService, "should be in an OutOfService state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, OutOfService,
+                              "should be in an OutOfService state")
 
     def test_out_of_service_exit(self):
         # should intially not be in an Idle state
         vehicle = mock_vehicle(soc=0.0)
-        sim = mock_sim(vehicles=(vehicle,))
+        sim = mock_sim(vehicles=(vehicle, ))
         env = mock_env()
 
-        state = OutOfService(vehicle.id)
+        state = OutOfService.build(vehicle.id)
         enter_error, entered_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
         # begin test
-        error, exited_sim = state.exit(Idle(vehicle.id), entered_sim, env)
+        error, exited_sim = state.exit(Idle.build(vehicle.id), entered_sim, env)
 
         self.assertIsNone(error, "should have no errors")
         self.assertEquals(entered_sim, exited_sim, "should see no change due to exit")
@@ -1074,24 +1040,28 @@ def test_out_of_service_exit(self):
     def test_out_of_service_update(self):
         # should intially not be in an Idle state
         vehicle = mock_vehicle(soc=0.0)
-        sim = mock_sim(vehicles=(vehicle,))
+        sim = mock_sim(vehicles=(vehicle, ))
         env = mock_env()
 
-        state = OutOfService(vehicle.id)
+        state = OutOfService.build(vehicle.id)
         enter_error, entered_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
         update_error, updated_sim = state.update(entered_sim, env)
+        entered_state = updated_sim.vehicles.get(vehicle.id).vehicle_state
         self.assertIsNone(update_error, "should have no error from update call")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
         self.assertEqual(vehicle.geoid, updated_vehicle.geoid, "should not have moved")
-        self.assertIsInstance(updated_vehicle.vehicle_state, OutOfService, "should still be in an OutOfService state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, OutOfService,
+                              "should still be in an OutOfService state")
         self.assertEqual(
             updated_vehicle.energy[EnergyType.ELECTRIC],
             vehicle.energy[EnergyType.ELECTRIC],
             "should have the same energy",
         )
+        self.assertEqual(entered_state.instance_id, updated_vehicle.vehicle_state.instance_id,
+                         "should be the same instance")
 
     # def test_out_of_service_update_terminal(self):  # there is no terminal state for OutOfService
 
@@ -1101,35 +1071,32 @@ def test_out_of_service_update(self):
 
     def test_repositioning_enter(self):
         vehicle = mock_vehicle()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-        )
+        sim = mock_sim(vehicles=(vehicle, ), )
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, vehicle.geoid)
 
-        state = Repositioning(vehicle.id, route)
+        state = Repositioning.build(vehicle.id, route)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(error, "should have no errors")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, Repositioning, "should be in a repositioning state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, Repositioning,
+                              "should be in a repositioning state")
         self.assertEquals(len(updated_vehicle.vehicle_state.route), 1, "should have a route")
 
     def test_repositioning_exit(self):
         vehicle = mock_vehicle()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-        )
+        sim = mock_sim(vehicles=(vehicle, ), )
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, vehicle.geoid)
 
-        state = Repositioning(vehicle.id, route)
+        state = Repositioning.build(vehicle.id, route)
         enter_error, entered_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
         # begin test
-        error, exited_sim = state.exit(Idle(vehicle.id), entered_sim, env)
+        error, exited_sim = state.exit(Idle.build(vehicle.id), entered_sim, env)
 
         self.assertIsNone(error, "should have no errors")
         self.assertEquals(entered_sim, exited_sim, "should see no change due to exit")
@@ -1138,37 +1105,37 @@ def test_repositioning_update(self):
         near = h3.geo_to_h3(39.7539, -104.974, 15)
         omf_brewing = h3.geo_to_h3(39.7608873, -104.9845391, 15)
         vehicle = mock_vehicle()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-        )
+        sim = mock_sim(vehicles=(vehicle, ), )
         env = mock_env()
         route = mock_route_from_geoids(near, omf_brewing)
 
-        state = Repositioning(vehicle.id, route)
+        state = Repositioning.build(vehicle.id, route)
         enter_error, entered_sim = state.enter(sim, env)
+        entered_state = entered_sim.vehicles.get(vehicle.id).vehicle_state
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
-        update_error, sim_updated = state.update(entered_sim, env)
+        update_error, sim_updated = entered_state.update(entered_sim, env)
         self.assertIsNone(update_error, "should have no error from update call")
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
         self.assertNotEqual(vehicle.geoid, updated_vehicle.geoid, "should have moved")
-        self.assertIsInstance(updated_vehicle.vehicle_state, Repositioning, "should still be in a Repositioning state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, Repositioning,
+                              "should still be in a Repositioning state")
         self.assertLess(
             updated_vehicle.energy[EnergyType.ELECTRIC],
             vehicle.energy[EnergyType.ELECTRIC],
             "should have less energy",
         )
+        self.assertEqual(entered_state.instance_id, updated_vehicle.vehicle_state.instance_id,
+                         "should be the same instance")
 
     def test_repositioning_update_terminal(self):
         vehicle = mock_vehicle()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-        )
+        sim = mock_sim(vehicles=(vehicle, ), )
         env = mock_env()
         route = ()
 
-        state = Repositioning(vehicle.id, route)
+        state = Repositioning.build(vehicle.id, route)
         enter_error, entered_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
         self.assertIsNotNone(entered_sim, "test precondition (enter works correctly) not met")
@@ -1177,7 +1144,8 @@ def test_repositioning_update_terminal(self):
         self.assertIsNone(update_error, "should have no error from update call")
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, Idle, "vehicle should be in Idle state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, Idle,
+                              "vehicle should be in Idle state")
 
     def test_repositioning_enter_no_vehicle(self):
         vehicle = mock_vehicle()
@@ -1185,7 +1153,7 @@ def test_repositioning_enter_no_vehicle(self):
         env = mock_env()
         route = ()
 
-        state = Repositioning(vehicle.id, route)
+        state = Repositioning.build(vehicle.id, route)
         enter_error, updated_sim = state.enter(sim, env)
 
         self.assertIsInstance(enter_error, Exception, "should have exception")
@@ -1193,11 +1161,11 @@ def test_repositioning_enter_no_vehicle(self):
     def test_repositioning_enter_route_with_bad_source(self):
         omf_brewing = h3.geo_to_h3(39.7608873, -104.9845391, 15)
         vehicle = mock_vehicle()
-        sim = mock_sim(vehicles=(vehicle,))
+        sim = mock_sim(vehicles=(vehicle, ))
         env = mock_env()
         route = mock_route_from_geoids(omf_brewing, vehicle.geoid)
 
-        state = Repositioning(vehicle.id, route)
+        state = Repositioning.build(vehicle.id, route)
         enter_error, enter_sim = state.enter(sim, env)
 
         self.assertIsNone(enter_error, "should be no error")
@@ -1211,27 +1179,28 @@ def test_repositioning_enter_route_with_bad_source(self):
     def test_reserve_base_enter(self):
         vehicle = mock_vehicle()
         base = mock_base()
-        sim = mock_sim(vehicles=(vehicle,), bases=(base,))
+        sim = mock_sim(vehicles=(vehicle, ), bases=(base, ))
         env = mock_env()
 
-        state = ReserveBase(vehicle.id, base.id)
+        state = ReserveBase.build(vehicle.id, base.id)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(error, "should have no errors")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
         updated_base = updated_sim.bases.get(base.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, ReserveBase, "should be in an ReserveBase state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, ReserveBase,
+                              "should be in an ReserveBase state")
         self.assertEqual(updated_base.available_stalls, 0, "only stall should now be occupied")
 
     def test_reserve_base_bad_membership(self):
         vehicle = mock_vehicle(membership=Membership.single_membership("uber"))
         base = mock_base(membership=Membership.single_membership("lyft"))
 
-        sim = mock_sim(vehicles=(vehicle,), bases=(base,))
+        sim = mock_sim(vehicles=(vehicle, ), bases=(base, ))
         env = mock_env()
 
-        state = ReserveBase(vehicle.id, base.id)
+        state = ReserveBase.build(vehicle.id, base.id)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(updated_sim, "should have returned None for updated_sim")
@@ -1239,17 +1208,18 @@ def test_reserve_base_bad_membership(self):
     def test_reserve_base_exit(self):
         vehicle = mock_vehicle()
         base = mock_base()
-        sim = mock_sim(vehicles=(vehicle,), bases=(base,))
+        sim = mock_sim(vehicles=(vehicle, ), bases=(base, ))
         env = mock_env()
 
-        state = ReserveBase(vehicle.id, base.id)
+        state = ReserveBase.build(vehicle.id, base.id)
         enter_error, entered_sim = state.enter(sim, env)
         entered_base = entered_sim.bases.get(base.id)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
-        self.assertEqual(entered_base.available_stalls, 0, "test precondition (stall in use) not met")
+        self.assertEqual(entered_base.available_stalls, 0,
+                         "test precondition (stall in use) not met")
 
         # begin test
-        error, exited_sim = state.exit(Idle(vehicle.id), entered_sim, env)
+        error, exited_sim = state.exit(Idle.build(vehicle.id), entered_sim, env)
 
         exited_base = exited_sim.bases.get(base.id)
         self.assertIsNone(error, "should have no errors")
@@ -1258,59 +1228,61 @@ def test_reserve_base_exit(self):
     def test_reserve_base_update(self):
         vehicle = mock_vehicle()
         base = mock_base()
-        sim = mock_sim(vehicles=(vehicle,), bases=(base,))
+        sim = mock_sim(vehicles=(vehicle, ), bases=(base, ))
         env = mock_env()
 
-        state = ReserveBase(vehicle.id, base.id)
+        state = ReserveBase.build(vehicle.id, base.id)
         enter_error, entered_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
         update_error, updated_sim = state.update(entered_sim, env)
+        entered_state = updated_sim.vehicles.get(vehicle.id).vehicle_state
         self.assertIsNone(update_error, "should have no error from update call")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
         self.assertEqual(vehicle.geoid, updated_vehicle.geoid, "should not have moved")
-        self.assertIsInstance(updated_vehicle.vehicle_state, ReserveBase, "should still be in a ReserveBase state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, ReserveBase,
+                              "should still be in a ReserveBase state")
         self.assertEqual(
             updated_vehicle.energy[EnergyType.ELECTRIC],
             vehicle.energy[EnergyType.ELECTRIC],
             "should have the same energy",
         )
+        self.assertEqual(entered_state.instance_id, updated_vehicle.vehicle_state.instance_id,
+                         "should be the same instance")
 
     def test_reserve_base_enter_no_base(self):
         vehicle = mock_vehicle()
-        sim = mock_sim(
-            vehicles=(vehicle,),
-        )
+        sim = mock_sim(vehicles=(vehicle, ), )
         env = mock_env()
         route = ()
 
-        state = ReserveBase(vehicle.id, DefaultIds.mock_base_id())
+        state = ReserveBase.build(vehicle.id, DefaultIds.mock_base_id())
         enter_error, _ = state.enter(sim, env)
         self.assertIsInstance(enter_error, Exception, "should have exception")
 
     def test_reserve_base_enter_no_vehicle(self):
         base = mock_base()
-        sim = mock_sim(
-            bases=(base,),
-        )
+        sim = mock_sim(bases=(base, ), )
         env = mock_env()
         route = ()
 
-        state = ReserveBase(DefaultIds.mock_vehicle_id(), base.id)
+        state = ReserveBase.build(DefaultIds.mock_vehicle_id(), base.id)
         enter_error, _ = state.enter(sim, env)
         self.assertIsInstance(enter_error, Exception, "should have exception")
 
     def test_reserve_base_no_stalls_available(self):
         vehicle = mock_vehicle()
         base = mock_base(stall_count=0)
-        sim = mock_sim(vehicles=(vehicle,), bases=(base,))
+        sim = mock_sim(vehicles=(vehicle, ), bases=(base, ))
         env = mock_env()
 
-        state = ReserveBase(vehicle.id, base.id)
+        state = ReserveBase.build(vehicle.id, base.id)
         enter_error, entered_sim = state.enter(sim, env)
-        self.assertIsNone(enter_error, "no stall failure should not result in an exception (fail silently)")
-        self.assertIsNone(entered_sim, "no stall failure should result in no SimulationState result")
+        self.assertIsNone(enter_error,
+                          "no stall failure should not result in an exception (fail silently)")
+        self.assertIsNone(entered_sim,
+                          "no stall failure should result in no SimulationState result")
 
     # def test_reserve_base_update_terminal(self):  # there is no terminal state for OutOfService
 
@@ -1319,88 +1291,95 @@ def test_reserve_base_no_stalls_available(self):
     ####################################################################################################################
 
     def test_servicing_trip_enter(self):
-        prev_state = DispatchTrip(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(), ())
+        prev_state = DispatchTrip.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(),
+                                        ())
         vehicle = mock_vehicle(vehicle_state=prev_state)
         request = mock_request_from_geoids(origin=vehicle.geoid)
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.destination)
 
-        state = ServicingTrip(vehicle.id, request, sim.sim_time, route)
+        state = ServicingTrip.build(vehicle.id, request, sim.sim_time, route)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(error, "should have no errors")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, ServicingTrip, "should be in a ServicingTrip state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, ServicingTrip,
+                              "should be in a ServicingTrip state")
         self.assertEquals(len(updated_vehicle.vehicle_state.route), 1, "should have a route")
 
     def test_servicing_trip_bad_membership(self):
         vehicle = mock_vehicle(membership=Membership.single_membership("uber"))
         request = mock_request(fleet_id="lyft")
 
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.destination)
 
-        state = ServicingTrip(vehicle.id, request, sim.sim_time, route)
+        state = ServicingTrip.build(vehicle.id, request, sim.sim_time, route)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(updated_sim, "should have returned None for updated_sim")
 
     def test_servicing_trip_exit(self):
-        prev_state = DispatchTrip(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(), ())
+        prev_state = DispatchTrip.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(),
+                                        ())
         vehicle = mock_vehicle(vehicle_state=prev_state)
         request = mock_request_from_geoids(destination=vehicle.geoid)
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(request.origin, request.destination)
 
-        state = ServicingTrip(vehicle.id, request, sim.sim_time, route)
+        state = ServicingTrip.build(vehicle.id, request, sim.sim_time, route)
         enter_error, entered_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
         # begin test
-        error, exited_sim = state.exit(Idle(vehicle.id), entered_sim, env)
+        error, exited_sim = state.exit(Idle.build(vehicle.id), entered_sim, env)
 
-        self.assertIsNone(error, "should have no errors")  # errors due to passengers not being at destination
+        self.assertIsNone(
+            error, "should have no errors")  # errors due to passengers not being at destination
 
     def test_servicing_trip_exit_when_still_has_passengers(self):
-        prev_state = DispatchTrip(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(), ())
+        prev_state = DispatchTrip.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(),
+                                        ())
         vehicle = mock_vehicle(vehicle_state=prev_state)
         request = mock_request_from_geoids()
         self.assertNotEqual(request.origin, request.destination, "test invariant failed")
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
 
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(request.origin, request.destination)
 
-        state = ServicingTrip(vehicle.id, request, sim.sim_time, route)
+        state = ServicingTrip.build(vehicle.id, request, sim.sim_time, route)
         enter_error, entered_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
         # begin test
-        error, exited_sim = state.exit(Idle(vehicle.id), entered_sim, env)
+        error, exited_sim = state.exit(Idle.build(vehicle.id), entered_sim, env)
 
-        self.assertIsNone(error, "should have no errors")  # errors due to passengers not being at destination
+        self.assertIsNone(
+            error, "should have no errors")  # errors due to passengers not being at destination
         self.assertIsNone(exited_sim, "should not have allowed exit of ServicingTrip")
 
     def test_servicing_trip_exit_when_still_has_passengers_but_out_of_fuel(self):
-        prev_state = DispatchTrip(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(), ())
+        prev_state = DispatchTrip.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(),
+                                        ())
         vehicle = mock_vehicle(soc=0, vehicle_state=prev_state)
         request = mock_request_from_geoids()
         self.assertNotEqual(request.origin, request.destination, "test invariant failed")
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
 
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(request.origin, request.destination)
 
-        state = ServicingTrip(vehicle.id, request, sim.sim_time, route)
+        state = ServicingTrip.build(vehicle.id, request, sim.sim_time, route)
         enter_error, entered_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
@@ -1408,49 +1387,57 @@ def test_servicing_trip_exit_when_still_has_passengers_but_out_of_fuel(self):
         error, updated_sim = state.update(entered_sim, env)
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
-        self.assertIsNone(error, "should have no errors")  # errors due to passengers not being at destination
-        self.assertIsInstance(updated_vehicle.vehicle_state, OutOfService, "vehicle should be out of service")
+        self.assertIsNone(
+            error, "should have no errors")  # errors due to passengers not being at destination
+        self.assertIsInstance(updated_vehicle.vehicle_state, OutOfService,
+                              "vehicle should be out of service")
         # self.assertIsNotNone(updated_sim,
         #                      "should have allowed exit of ServicingTrip because out of fuel allows transition to OutOfService")
 
     def test_servicing_trip_update(self):
         near = h3.geo_to_h3(39.7539, -104.974, 15)
         omf_brewing = h3.geo_to_h3(39.7608873, -104.9845391, 15)
-        prev_state = DispatchTrip(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(), ())
+        prev_state = DispatchTrip.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(),
+                                        ())
         vehicle = mock_vehicle_from_geoid(geoid=near, vehicle_state=prev_state)
         request = mock_request_from_geoids(origin=near, destination=omf_brewing)
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(near, omf_brewing)
 
-        state = ServicingTrip(vehicle.id, request, sim.sim_time, route)
+        state = ServicingTrip.build(vehicle.id, request, sim.sim_time, route)
         enter_error, sim_servicing = state.enter(sim, env)
+        entered_state = sim_servicing.vehicles.get(vehicle.id).vehicle_state
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
-        update_error, sim_updated = state.update(sim_servicing, env)
+        update_error, sim_updated = entered_state.update(sim_servicing, env)
         self.assertIsNone(update_error, "should have no error from update call")
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
         self.assertNotEqual(vehicle.geoid, updated_vehicle.geoid, "should have moved")
-        self.assertIsInstance(updated_vehicle.vehicle_state, ServicingTrip, "should still be in a servicing state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, ServicingTrip,
+                              "should still be in a servicing state")
         self.assertLess(
             updated_vehicle.energy[EnergyType.ELECTRIC],
             vehicle.energy[EnergyType.ELECTRIC],
             "should have less energy",
         )
         self.assertEqual(updated_vehicle.vehicle_state.request, request, "should have passengers")
+        self.assertEqual(entered_state.instance_id, updated_vehicle.vehicle_state.instance_id,
+                         "should be the same instance")
 
     def test_servicing_trip_update_terminal(self):
-        prev_state = DispatchTrip(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(), ())
+        prev_state = DispatchTrip.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(),
+                                        ())
         vehicle = mock_vehicle(vehicle_state=prev_state)
         request = mock_request_from_geoids(origin=vehicle.geoid, destination=vehicle.geoid)
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = ()  # end of route
 
-        state = ServicingTrip(vehicle.id, request, sim.sim_time, route)
+        state = ServicingTrip.build(vehicle.id, request, sim.sim_time, route)
         enter_error, sim_servicing = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
@@ -1458,25 +1445,26 @@ def test_servicing_trip_update_terminal(self):
         self.assertIsNone(update_error, "should have no error from update call")
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, Idle, "vehicle should be in Idle state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, Idle,
+                              "vehicle should be in Idle state")
 
     def test_servicing_trip_enter_no_request(self):
         vehicle = mock_vehicle()
         request = mock_request()
-        e0, sim_with_req = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e0, sim_with_req = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e0, "test invariant failed")
 
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.geoid)
         rev_route = mock_route_from_geoids(request.geoid, vehicle.geoid)
 
-        vs0 = DispatchTrip(vehicle.id, request.id, route)
+        vs0 = DispatchTrip.build(vehicle.id, request.id, route)
         e2, s0 = vs0.enter(sim_with_req, env)
         self.assertIsNone(e2, "test invariant failed")
 
         # begin test
         e1, sim_no_req = simulation_state_ops.remove_request(s0, request.id)
-        vs1 = ServicingTrip(vehicle.id, request, sim_no_req.sim_time, rev_route)
+        vs1 = ServicingTrip.build(vehicle.id, request, sim_no_req.sim_time, rev_route)
         e3, s1 = vs1.enter(sim_no_req, env)
 
         self.assertIsNone(e3, "should have no errors")
@@ -1489,37 +1477,40 @@ def test_servicing_trip_enter_no_vehicle(self):
         env = mock_env()
         route = mock_route_from_geoids(request.geoid, request.destination)
 
-        state = ServicingTrip(vehicle.id, request, sim.sim_time, route)
+        state = ServicingTrip.build(vehicle.id, request, sim.sim_time, route)
         enter_error, updated_sim = state.enter(sim, env)
 
         self.assertIsInstance(enter_error, Exception, "should have exception")
 
     def test_servicing_trip_enter_route_with_bad_source(self):
         omf_brewing = h3.geo_to_h3(39.7608873, -104.9845391, 15)
-        prev_state = DispatchTrip(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(), ())
+        prev_state = DispatchTrip.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(),
+                                        ())
         vehicle = mock_vehicle(vehicle_state=prev_state)
         request = mock_request()
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(omf_brewing, request.destination)
 
-        state = ServicingTrip(vehicle.id, request, sim.sim_time, route)
+        state = ServicingTrip.build(vehicle.id, request, sim.sim_time, route)
         enter_error, enter_sim = state.enter(sim, env)
 
         self.assertIsNotNone(enter_error, "an invalid route should return an error")
 
     def test_servicing_trip_enter_route_with_bad_destination(self):
         omf_brewing = h3.geo_to_h3(39.7608873, -104.9845391, 15)
-        prev_state = DispatchTrip(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(), ())
+        prev_state = DispatchTrip.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_request_id(),
+                                        ())
         vehicle = mock_vehicle_from_geoid(vehicle_state=prev_state)
         request = mock_request_from_geoids(origin=vehicle.geoid)
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
-        route = mock_route_from_geoids(vehicle.geoid, omf_brewing)  # request.destination should not be omf brewing co
+        route = mock_route_from_geoids(
+            vehicle.geoid, omf_brewing)  # request.destination should not be omf brewing co
 
-        state = ServicingTrip(vehicle.id, request, sim.sim_time, route)
+        state = ServicingTrip.build(vehicle.id, request, sim.sim_time, route)
         enter_error, enter_sim = state.enter(sim, env)
         self.assertIsNotNone(enter_error, "bad destination is a good reason to bork this sim")
 
@@ -1530,10 +1521,10 @@ def test_servicing_trip_enter_route_with_bad_destination(self):
     def test_charge_queueing_enter(self):
         vehicle = mock_vehicle_from_geoid()
         station = mock_station_from_geoid(chargers={})
-        sim = mock_sim(vehicles=(vehicle,), stations=(station,))
+        sim = mock_sim(vehicles=(vehicle, ), stations=(station, ))
         env = mock_env()
 
-        state = ChargeQueueing(vehicle.id, station.id, mock_dcfc_charger_id(), 0)
+        state = ChargeQueueing.build(vehicle.id, station.id, mock_dcfc_charger_id(), 0)
         error, updated_sim = state.enter(sim, env)
 
         updated_station = updated_sim.stations.get(station.id)
@@ -1541,21 +1532,22 @@ def test_charge_queueing_enter(self):
 
         self.assertIsNone(error, "should have no errors")
         self.assertIsNotNone(updated_sim, "the sim should have been updated")
-        self.assertEqual(enqueued_count, 1, "the station should also know 1 new vehicle is enqueued")
+        self.assertEqual(enqueued_count, 1,
+                         "the station should also know 1 new vehicle is enqueued")
 
     def test_charge_queueing_exit(self):
         vehicle = mock_vehicle_from_geoid()
         station = mock_station_from_geoid(chargers={})
-        sim0 = mock_sim(vehicles=(vehicle,), stations=(station,))
+        sim0 = mock_sim(vehicles=(vehicle, ), stations=(station, ))
         env = mock_env()
 
-        state = ChargeQueueing(vehicle.id, station.id, mock_dcfc_charger_id(), 0)
+        state = ChargeQueueing.build(vehicle.id, station.id, mock_dcfc_charger_id(), 0)
         err1, sim1 = state.enter(sim0, env)
 
         self.assertIsNone(err1, "test invariant failed")
         self.assertIsNotNone(sim1, "test invariant failed")
 
-        err2, sim2 = state.exit(Idle(vehicle.id), sim1, env)
+        err2, sim2 = state.exit(Idle.build(vehicle.id), sim1, env)
 
         updated_station = sim2.stations.get(station.id)
         enqueued_count = updated_station.enqueued_vehicle_count_for_charger(mock_dcfc_charger_id())
@@ -1568,20 +1560,25 @@ def test_charge_queueing_update(self):
         vehicle_charging = mock_vehicle_from_geoid(vehicle_id="charging")
         vehicle_queueing = mock_vehicle_from_geoid(vehicle_id="queueing")
         station = mock_station_from_geoid()
-        sim0 = mock_sim(vehicles=(vehicle_charging, vehicle_queueing,), stations=(station,))
+        sim0 = mock_sim(vehicles=(
+            vehicle_charging,
+            vehicle_queueing,
+        ), stations=(station, ))
         env = mock_env()
 
-        charging_state = ChargingStation(vehicle_charging.id, station.id, mock_dcfc_charger_id())
+        charging_state = ChargingStation.build(vehicle_charging.id, station.id,
+                                               mock_dcfc_charger_id())
         e1, sim1 = charging_state.enter(sim0, env)
         self.assertIsNone(e1, "test invariant failed")
 
-        state = ChargeQueueing(vehicle_queueing.id, station.id, mock_dcfc_charger_id(), 0)
+        state = ChargeQueueing.build(vehicle_queueing.id, station.id, mock_dcfc_charger_id(), 0)
         e2, sim2 = state.enter(sim1, env)
+        entered_state = sim2.vehicles.get(vehicle_queueing.id).vehicle_state
 
         self.assertIsNone(e2, "test invariant failed")
         self.assertIsNotNone(sim2, "test invariant failed")
 
-        e3, sim3 = state.update(sim2, env)
+        e3, sim3 = entered_state.update(sim2, env)
 
         self.assertIsNone(e3, "should have no errors")
         self.assertIsNotNone(sim3, "should have updated the sim")
@@ -1592,6 +1589,8 @@ def test_charge_queueing_update(self):
             vehicle_queueing.energy[EnergyType.ELECTRIC],
             "should have less energy",
         )
+        self.assertEqual(entered_state.instance_id, updated_vehicle.vehicle_state.instance_id,
+                         "should be the same instance")
 
         # update should apply idle cost if stall is still not available
 
@@ -1599,14 +1598,18 @@ def test_charge_queueing_update_terminal(self):
         vehicle_charging = mock_vehicle_from_geoid()
         vehicle_queueing = mock_vehicle_from_geoid()
         station = mock_station_from_geoid()
-        sim1 = mock_sim(vehicles=(vehicle_charging, vehicle_queueing,), stations=(station,))
+        sim1 = mock_sim(vehicles=(
+            vehicle_charging,
+            vehicle_queueing,
+        ), stations=(station, ))
         env = mock_env()
 
-        charging_state = ChargingStation(vehicle_charging.id, station.id, mock_dcfc_charger_id())
+        charging_state = ChargingStation.build(vehicle_charging.id, station.id,
+                                               mock_dcfc_charger_id())
         e1, sim2 = charging_state.update(sim1, env)
         self.assertIsNone(e1, "test invariant failed")
 
-        state = ChargeQueueing(vehicle_queueing.id, station.id, mock_dcfc_charger_id(), 0)
+        state = ChargeQueueing.build(vehicle_queueing.id, station.id, mock_dcfc_charger_id(), 0)
         error, updated_sim = state.enter(sim1, env)
 
         self.assertIsNone(error, "should have no errors")
@@ -1616,10 +1619,11 @@ def test_charge_queueing_update_terminal(self):
 
     def test_charge_queueing_enter_no_vehicle(self):
         station = mock_station_from_geoid()
-        sim = mock_sim(stations=(station,))
+        sim = mock_sim(stations=(station, ))
         env = mock_env()
 
-        state = ChargeQueueing(DefaultIds.mock_vehicle_id(), station.id, mock_dcfc_charger_id(), 0)
+        state = ChargeQueueing.build(DefaultIds.mock_vehicle_id(), station.id,
+                                     mock_dcfc_charger_id(), 0)
         err, _ = state.enter(sim, env)
 
         self.assertIsInstance(err, Exception, "should have exception")
@@ -1627,10 +1631,10 @@ def test_charge_queueing_enter_no_vehicle(self):
     def test_charge_queueing_enter_when_station_has_charger_available(self):
         vehicle_queueing = mock_vehicle_from_geoid()
         station = mock_station_from_geoid()
-        sim1 = mock_sim(vehicles=(vehicle_queueing,), stations=(station,))
+        sim1 = mock_sim(vehicles=(vehicle_queueing, ), stations=(station, ))
         env = mock_env()
 
-        state = ChargeQueueing(vehicle_queueing.id, station.id, mock_dcfc_charger_id(), 0)
+        state = ChargeQueueing.build(vehicle_queueing.id, station.id, mock_dcfc_charger_id(), 0)
         error, updated_sim = state.enter(sim1, env)
 
         self.assertIsNone(error, "should have no errors")
@@ -1643,28 +1647,30 @@ def test_charge_queueing_enter_when_station_has_charger_available(self):
     def test_dispatch_pooling_trip_enter(self):
         vehicle = mock_vehicle()
         request = mock_request()
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.geoid)
         trip_plan = ((request.id, TripPhase.PICKUP), (request.id, TripPhase.DROPOFF))
 
-        state = DispatchPoolingTrip(vehicle.id, trip_plan, route)
+        state = DispatchPoolingTrip.build(vehicle.id, trip_plan, route)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(error, "should have no errors")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
         updated_request = updated_sim.requests.get(request.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, DispatchPoolingTrip, "should be in a dispatch to request state")
-        self.assertEquals(updated_request.dispatched_vehicle, vehicle.id, "request should be assigned this vehicle")
+        self.assertIsInstance(updated_vehicle.vehicle_state, DispatchPoolingTrip,
+                              "should be in a dispatch to request state")
+        self.assertEquals(updated_request.dispatched_vehicle, vehicle.id,
+                          "request should be assigned this vehicle")
         self.assertEquals(len(updated_vehicle.vehicle_state.route), 1, "should have a route")
 
     def test_dispatch_pooling_trip_enter_two_requests(self):
         vehicle = mock_vehicle()
         r1 = mock_request('r1', 39.70, -104.97, 39.85, -104.97)  # pickup 1, dropoff 2
         r2 = mock_request('r2', 39.75, -104.97, 39.80, -104.97)  # pickup 2, dropoff 1
-        s0 = mock_sim(vehicles=(vehicle,))
+        s0 = mock_sim(vehicles=(vehicle, ))
         env = mock_env()
         e1, s1 = simulation_state_ops.add_request(s0, r1)
         e2, s2 = simulation_state_ops.add_request(s1, r2)
@@ -1673,9 +1679,10 @@ def test_dispatch_pooling_trip_enter_two_requests(self):
         self.assertIsNone(e2, "test invariant failed")
 
         route = mock_route_from_geoids(vehicle.geoid, r1.geoid)
-        trip_plan = ((r1.id, TripPhase.PICKUP), (r2.id, TripPhase.PICKUP), (r2.id, TripPhase.DROPOFF), (r1.id, TripPhase.DROPOFF))
+        trip_plan = ((r1.id, TripPhase.PICKUP), (r2.id, TripPhase.PICKUP),
+                     (r2.id, TripPhase.DROPOFF), (r1.id, TripPhase.DROPOFF))
 
-        state = DispatchPoolingTrip(vehicle.id, trip_plan, route)
+        state = DispatchPoolingTrip.build(vehicle.id, trip_plan, route)
         error, updated_sim = state.enter(s2, env)
 
         self.assertIsNone(error, "should have no errors")
@@ -1683,21 +1690,24 @@ def test_dispatch_pooling_trip_enter_two_requests(self):
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
         updated_r1 = updated_sim.requests.get(r1.id)
         updated_r2 = updated_sim.requests.get(r2.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, DispatchPoolingTrip, "should be in a dispatch to request state")
-        self.assertEquals(updated_r1.dispatched_vehicle, vehicle.id, "r1 should be assigned this vehicle")
-        self.assertEquals(updated_r2.dispatched_vehicle, vehicle.id, "r2 should be assigned this vehicle")
+        self.assertIsInstance(updated_vehicle.vehicle_state, DispatchPoolingTrip,
+                              "should be in a dispatch to request state")
+        self.assertEquals(updated_r1.dispatched_vehicle, vehicle.id,
+                          "r1 should be assigned this vehicle")
+        self.assertEquals(updated_r2.dispatched_vehicle, vehicle.id,
+                          "r2 should be assigned this vehicle")
         self.assertEquals(len(updated_vehicle.vehicle_state.route), 1, "should have a route")
 
     def test_dispatch_pooling_trip_bad_membership(self):
         vehicle = mock_vehicle(membership=Membership.single_membership("uber"))
         request = mock_request(fleet_id="lyft")
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.geoid)
         trip_plan = ((request.id, TripPhase.PICKUP), (request.id, TripPhase.DROPOFF))
 
-        state = DispatchPoolingTrip(vehicle.id, trip_plan, route)
+        state = DispatchPoolingTrip.build(vehicle.id, trip_plan, route)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(updated_sim, "should have returned None for updated_sim")
@@ -1705,39 +1715,44 @@ def test_dispatch_pooling_trip_bad_membership(self):
     def test_dispatch_pooling_trip_exit(self):
         vehicle = mock_vehicle()
         request = mock_request()
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.geoid)
         trip_plan = ((request.id, TripPhase.PICKUP), (request.id, TripPhase.DROPOFF))
 
-        state = DispatchPoolingTrip(vehicle.id, trip_plan, route)
+        state = DispatchPoolingTrip.build(vehicle.id, trip_plan, route)
         enter_error, entered_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
-        self.assertTrue(entered_sim.requests.get(request.id).dispatched_vehicle == vehicle.id, "test precondition not met")
+        self.assertTrue(
+            entered_sim.requests.get(request.id).dispatched_vehicle == vehicle.id,
+            "test precondition not met")
 
         # begin test
-        error, exited_sim = state.exit(Idle(vehicle.id), entered_sim, env)
+        error, exited_sim = state.exit(Idle.build(vehicle.id), entered_sim, env)
 
         self.assertIsNone(error, "should have no errors")
-        self.assertIsNone(exited_sim.requests.get(request.id).dispatched_vehicle, "should have unset the dispatched vehicle")
+        self.assertIsNone(
+            exited_sim.requests.get(request.id).dispatched_vehicle,
+            "should have unset the dispatched vehicle")
 
     def test_dispatch_pooling_trip_update(self):
         near = h3.geo_to_h3(39.7539, -104.974, 15)
         far = h3.geo_to_h3(39.7608873, -104.9845391, 15)
         vehicle = mock_vehicle_from_geoid(geoid=near)
         request = mock_request_from_geoids(origin=far)
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(near, far)
         trip_plan = ((request.id, TripPhase.PICKUP), (request.id, TripPhase.DROPOFF))
 
-        state = DispatchPoolingTrip(vehicle.id, trip_plan, route)
+        state = DispatchPoolingTrip.build(vehicle.id, trip_plan, route)
         enter_error, sim_with_dispatched_vehicle = state.enter(sim, env)
+        entered_state = sim_with_dispatched_vehicle.vehicles.get(vehicle.id).vehicle_state
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
-        update_error, sim_updated = state.update(sim_with_dispatched_vehicle, env)
+        update_error, sim_updated = entered_state.update(sim_with_dispatched_vehicle, env)
         self.assertIsNone(update_error, "should have no error from update call")
 
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
@@ -1749,17 +1764,19 @@ def test_dispatch_pooling_trip_update(self):
             vehicle.energy[EnergyType.ELECTRIC],
             "should have less energy",
         )
+        self.assertEqual(entered_state.instance_id, updated_vehicle.vehicle_state.instance_id,
+                         "should be the same instance")
 
     def test_dispatch_pooling_trip_update_terminal(self):
         vehicle = mock_vehicle_from_geoid(geoid="8f268cdac30e2d3")
         request = mock_request_from_geoids(origin="8f268cdac30e2d3", destination="8f268cdac70e2d3")
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = ()  # vehicle is at the request
         trip_plan = ((request.id, TripPhase.PICKUP), (request.id, TripPhase.DROPOFF))
 
-        state = DispatchPoolingTrip(vehicle.id, trip_plan, route)
+        state = DispatchPoolingTrip.build(vehicle.id, trip_plan, route)
         enter_error, sim_with_dispatch_vehicle = state.enter(sim, env)
         self.assertIsNone(enter_error, "test precondition (enter works correctly) not met")
 
@@ -1769,19 +1786,25 @@ def test_dispatch_pooling_trip_update_terminal(self):
         updated_vehicle = sim_updated.vehicles.get(vehicle.id)
         updated_request = sim_updated.requests.get(request.id)
         boarded_request = updated_vehicle.vehicle_state.boarded_requests.get(request.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, ServicingPoolingTrip, "vehicle should be in ServicingPoolingTrip state")
-        self.assertIsNone(updated_request, "request should no longer exist as it has been picked up")
-        self.assertIsNotNone(boarded_request, f"request {request.id} should have boarded the vehicle")
-        self.assertEquals(boarded_request.id, request.id, f"request {request.id} should have boarded the vehicle, found {boarded_request.id} instead")
+        self.assertIsInstance(updated_vehicle.vehicle_state, ServicingPoolingTrip,
+                              "vehicle should be in ServicingPoolingTrip state")
+        self.assertIsNone(updated_request,
+                          "request should no longer exist as it has been picked up")
+        self.assertIsNotNone(boarded_request,
+                             f"request {request.id} should have boarded the vehicle")
+        self.assertEquals(
+            boarded_request.id, request.id,
+            f"request {request.id} should have boarded the vehicle, found {boarded_request.id} instead"
+        )
 
     def test_dispatch_pooling_trip_enter_no_request(self):
         vehicle = mock_vehicle()
         request = mock_request()
-        sim = mock_sim(vehicles=(vehicle,))  # request not added to sim
+        sim = mock_sim(vehicles=(vehicle, ))  # request not added to sim
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.geoid)
 
-        state = DispatchTrip(vehicle.id, request.id, route)
+        state = DispatchTrip.build(vehicle.id, request.id, route)
         error, updated_sim = state.enter(sim, env)
 
         self.assertIsNone(error, "should have no errors")
@@ -1794,7 +1817,7 @@ def test_dispatch_pooling_trip_enter_no_vehicle(self):
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.geoid)
 
-        state = DispatchTrip(vehicle.id, request.id, route)
+        state = DispatchTrip.build(vehicle.id, request.id, route)
         enter_error, updated_sim = state.enter(sim, env)
 
         self.assertIsInstance(enter_error, Exception, "should have exception")
@@ -1803,12 +1826,12 @@ def test_dispatch_pooling_trip_enter_route_with_bad_source(self):
         omf_brewing = h3.geo_to_h3(39.7608873, -104.9845391, 15)
         vehicle = mock_vehicle()
         request = mock_request()
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(omf_brewing, request.geoid)
 
-        state = DispatchTrip(vehicle.id, request.id, route)
+        state = DispatchTrip.build(vehicle.id, request.id, route)
         enter_error, enter_sim = state.enter(sim, env)
 
         self.assertIsNone(enter_error, "should be no error")
@@ -1818,12 +1841,12 @@ def test_dispatch_pooling_trip_enter_route_with_bad_destination(self):
         omf_brewing = h3.geo_to_h3(39.7608873, -104.9845391, 15)
         vehicle = mock_vehicle()
         request = mock_request()
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, omf_brewing)
 
-        state = DispatchTrip(vehicle.id, request.id, route)
+        state = DispatchTrip.build(vehicle.id, request.id, route)
         enter_error, enter_sim = state.enter(sim, env)
         self.assertIsNone(enter_error, "should be no error")
         self.assertIsNone(enter_sim, "invalid route should have not changed sim state")
@@ -1835,37 +1858,39 @@ def test_dispatch_pooling_trip_enter_route_with_bad_destination(self):
     def test_servicing_pooling_trip_enter(self):
         vehicle = mock_vehicle()
         request = mock_request()
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.geoid)
         trip_plan = ((request.id, TripPhase.PICKUP), (request.id, TripPhase.DROPOFF))
 
-        prev_state = DispatchPoolingTrip(vehicle.id, trip_plan, route)
+        prev_state = DispatchPoolingTrip.build(vehicle.id, trip_plan, route)
         error, disp_sim = prev_state.enter(sim, env)
         self.assertIsNone(error, "test invariant failed")
 
         # things constructed when transitioning from DispatchPoolingTrip to ServicingPoolingTrip
-        boarded_trip_plan = ((request.id, TripPhase.DROPOFF),)
+        boarded_trip_plan = ((request.id, TripPhase.DROPOFF), )
         boarded_reqs = immutables.Map({request.id: request})
         departure_times = immutables.Map({request.id: SimTime(0)})
         route = mock_route_from_geoids(request.origin, request.destination)
-        routes = (route,)
+        routes = (route, )
 
-        next_state = ServicingPoolingTrip(vehicle.id, boarded_trip_plan, boarded_reqs, departure_times, routes, 1)
+        next_state = ServicingPoolingTrip.build(vehicle.id, boarded_trip_plan, boarded_reqs,
+                                                departure_times, routes, 1)
         error, updated_sim = next_state.enter(disp_sim, env)
 
         self.assertIsNone(error, "should have no errors")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, ServicingPoolingTrip, "should be in a ServicingPoolingTrip state")
+        self.assertIsInstance(updated_vehicle.vehicle_state, ServicingPoolingTrip,
+                              "should be in a ServicingPoolingTrip state")
         self.assertEquals(len(updated_vehicle.vehicle_state.routes), 1, "should have a route")
 
     def test_servicing_pooling_trip_update_terminal(self):
         # 3 adjacent h3 cells, total trip distance is ~ 2 meters
         vehicle = mock_vehicle_from_geoid(geoid='8f268cd9601daa1')
         request = mock_request_from_geoids(origin='8f268cd9601daac', destination='8f268cd9601da10')
-        sim0 = mock_sim(vehicles=(vehicle,), sim_timestep_duration_seconds=60)
+        sim0 = mock_sim(vehicles=(vehicle, ), sim_timestep_duration_seconds=60)
 
         # place request and vehicle in a mock simulation
         err1, sim1 = simulation_state_ops.add_request(sim0, request)
@@ -1875,7 +1900,7 @@ def test_servicing_pooling_trip_update_terminal(self):
         trip_plan = ((request.id, TripPhase.PICKUP), (request.id, TripPhase.DROPOFF))
 
         # set vehicle to dispatch state
-        prev_state = DispatchPoolingTrip(vehicle.id, trip_plan, route)
+        prev_state = DispatchPoolingTrip.build(vehicle.id, trip_plan, route)
         err2, sim2 = prev_state.enter(sim1, env)
         self.assertIsNone(err2, "test invariant failed")
 
@@ -1896,7 +1921,8 @@ def test_servicing_pooling_trip_update_terminal(self):
         # test final state
         updated_vehicle = sim5.vehicles.get(vehicle.id)
         self.assertIsInstance(updated_vehicle.vehicle_state, Idle, "should be in an Idle state")
-        self.assertEqual(len(sim5.requests), 0, "request should have been picked up and dropped off fully")
+        self.assertEqual(len(sim5.requests), 0,
+                         "request should have been picked up and dropped off fully")
 
     def test_servicing_pooling_trip_update_picks_up_second_request(self):
         v0_src, r0_src, r1_src, r0_dst, r1_dst = '8f268cd9601daa1', '8f268cd9601daac', '8f268cd9601da10', '8f268cd9601da1a', '8f268cd9601da88'
@@ -1905,7 +1931,7 @@ def test_servicing_pooling_trip_update_picks_up_second_request(self):
         v0 = mock_vehicle_from_geoid(geoid=v0_src)
         r0 = mock_request_from_geoids(request_id='r0', origin=r0_src, destination=r0_dst)
         r1 = mock_request_from_geoids(request_id='r1', origin=r1_src, destination=r1_dst)
-        sim0 = mock_sim(vehicles=(v0,), sim_timestep_duration_seconds=60)
+        sim0 = mock_sim(vehicles=(v0, ), sim_timestep_duration_seconds=60)
 
         # place request and vehicle in a mock simulation
         err1, sim1 = simulation_state_ops.add_request(sim0, r0)
@@ -1915,10 +1941,11 @@ def test_servicing_pooling_trip_update_picks_up_second_request(self):
 
         env = mock_env()
         route = mock_route_from_geoids(v0.geoid, r0.geoid)
-        trip_plan = ((r0.id, TripPhase.PICKUP), (r1.id, TripPhase.PICKUP), (r0.id, TripPhase.DROPOFF), (r1.id, TripPhase.DROPOFF))
+        trip_plan = ((r0.id, TripPhase.PICKUP), (r1.id, TripPhase.PICKUP),
+                     (r0.id, TripPhase.DROPOFF), (r1.id, TripPhase.DROPOFF))
 
         # set vehicle to dispatch state
-        prev_state = DispatchPoolingTrip(v0.id, trip_plan, route)
+        prev_state = DispatchPoolingTrip.build(v0.id, trip_plan, route)
         err3, sim3 = prev_state.enter(sim2, env)
         self.assertIsNone(err3, "test invariant failed")
 
@@ -1935,7 +1962,8 @@ def test_servicing_pooling_trip_update_picks_up_second_request(self):
         state_pooling = veh_pooling.vehicle_state
         self.assertEqual(veh_pooling.geoid, r1_src, "should be at the pickup location for r1")
         self.assertEqual(len(state_pooling.boarded_requests), 2, "should have 2 requests boarded")
-        self.assertEqual(state_pooling.trip_plan, trip_plan[2:4], "should have the correct remaining plan")
+        self.assertEqual(state_pooling.trip_plan, trip_plan[2:4],
+                         "should have the correct remaining plan")
         # todo: check state of remaining route, should be r1_src -> r0_dst, r0_dst -> r1_dst
 
     def test_servicing_pooling_trip_update_drops_off_first_request(self):
@@ -1945,7 +1973,7 @@ def test_servicing_pooling_trip_update_drops_off_first_request(self):
         v0 = mock_vehicle_from_geoid(geoid=v0_src)
         r0 = mock_request_from_geoids(request_id='r0', origin=r0_src, destination=r0_dst)
         r1 = mock_request_from_geoids(request_id='r1', origin=r1_src, destination=r1_dst)
-        sim0 = mock_sim(vehicles=(v0,), sim_timestep_duration_seconds=60)
+        sim0 = mock_sim(vehicles=(v0, ), sim_timestep_duration_seconds=60)
 
         # place request and vehicle in a mock simulation
         err1, sim1 = simulation_state_ops.add_request(sim0, r0)
@@ -1955,10 +1983,11 @@ def test_servicing_pooling_trip_update_drops_off_first_request(self):
 
         env = mock_env()
         route = mock_route_from_geoids(v0.geoid, r0.geoid)
-        trip_plan = ((r0.id, TripPhase.PICKUP), (r1.id, TripPhase.PICKUP), (r0.id, TripPhase.DROPOFF), (r1.id, TripPhase.DROPOFF))
+        trip_plan = ((r0.id, TripPhase.PICKUP), (r1.id, TripPhase.PICKUP),
+                     (r0.id, TripPhase.DROPOFF), (r1.id, TripPhase.DROPOFF))
 
         # set vehicle to dispatch state
-        prev_state = DispatchPoolingTrip(v0.id, trip_plan, route)
+        prev_state = DispatchPoolingTrip.build(v0.id, trip_plan, route)
         err3, sim3 = prev_state.enter(sim2, env)
         self.assertIsNone(err3, "test invariant failed")
 
@@ -1979,30 +2008,39 @@ def test_servicing_pooling_trip_update_drops_off_first_request(self):
         veh_pooling = sim6.vehicles.get(v0.id)
         state_pooling = veh_pooling.vehicle_state
         self.assertEqual(veh_pooling.geoid, r0_dst, "should be at the dropoff location for r0")
-        self.assertEqual(len(state_pooling.boarded_requests), 1, "should have 1 requests still boarded")
-        self.assertIsNone(sim6.requests.get('r0'), "request 0 should have been dropped off and no longer be in the simulation")
-        self.assertEqual(state_pooling.trip_plan, trip_plan[3:4], "should have the correct remaining plan")
+        self.assertEqual(len(state_pooling.boarded_requests), 1,
+                         "should have 1 requests still boarded")
+        self.assertIsNone(
+            sim6.requests.get('r0'),
+            "request 0 should have been dropped off and no longer be in the simulation")
+        self.assertEqual(state_pooling.trip_plan, trip_plan[3:4],
+                         "should have the correct remaining plan")
         # todo: check state of remaining route, should be r0_dst -> r1_dst
 
     def test_servicing_single_pooling_trip_reaches_destination(self):
         vehicle = mock_vehicle()
         request = mock_request()
-        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle,)), request)
+        e1, sim = simulation_state_ops.add_request(mock_sim(vehicles=(vehicle, )), request)
         self.assertIsNone(e1, "test invariant failed")
         env = mock_env()
         route = mock_route_from_geoids(vehicle.geoid, request.geoid)
         trip_plan = ((request.id, TripPhase.PICKUP), (request.id, TripPhase.DROPOFF))
 
-        state = DispatchPoolingTrip(vehicle.id, trip_plan, route)
+        state = DispatchPoolingTrip.build(vehicle.id, trip_plan, route)
         error, updated_sim = state.enter(sim, env)
+        entered_state = updated_sim.vehicles.get(vehicle.id).vehicle_state
 
         self.assertIsNone(error, "should have no errors")
 
         updated_vehicle = updated_sim.vehicles.get(vehicle.id)
         updated_request = updated_sim.requests.get(request.id)
-        self.assertIsInstance(updated_vehicle.vehicle_state, DispatchPoolingTrip, "should be in a dispatch to request state")
-        self.assertEquals(updated_request.dispatched_vehicle, vehicle.id, "request should be assigned this vehicle")
+        self.assertIsInstance(updated_vehicle.vehicle_state, DispatchPoolingTrip,
+                              "should be in a dispatch to request state")
+        self.assertEquals(updated_request.dispatched_vehicle, vehicle.id,
+                          "request should be assigned this vehicle")
         self.assertEquals(len(updated_vehicle.vehicle_state.route), 1, "should have a route")
+        self.assertEqual(entered_state.instance_id, updated_vehicle.vehicle_state.instance_id,
+                         "should be the same instance")
 
     def test_servicing_pooling_trip_exit(self):
         """
diff --git a/tests/test_vehicle_state_ops.py b/tests/test_vehicle_state_ops.py
index 01ddee20..dd274d6d 100644
--- a/tests/test_vehicle_state_ops.py
+++ b/tests/test_vehicle_state_ops.py
@@ -5,7 +5,6 @@
 
 
 class TestVehicleStateOps(TestCase):
-
     def test_move(self):
         somewhere = h3.geo_to_h3(39.7539, -104.974, 15)
         somewhere_else = h3.geo_to_h3(39.7579, -104.978, 15)
@@ -13,7 +12,7 @@ def test_move(self):
         somewhere_link = sim.road_network.position_from_geoid(somewhere)
         somewhere_else_link = sim.road_network.position_from_geoid(somewhere_else)
         route = sim.road_network.route(somewhere_link, somewhere_else_link)
-        state = Repositioning(DefaultIds.mock_vehicle_id(), route)
+        state = Repositioning.build(DefaultIds.mock_vehicle_id(), route)
         vehicle = mock_vehicle_from_geoid(geoid=somewhere, vehicle_state=state)
         e1, sim_with_veh = simulation_state_ops.add_vehicle(sim, vehicle)
         self.assertIsNone(e1, "test invariant failed")
@@ -32,20 +31,20 @@ def test_move(self):
 
         self.assertLess(soc, 1, "should have used 1 unit of mock energy")
         self.assertNotEqual(somewhere, moved_vehicle.geoid, "should not be at the same location")
-        self.assertNotEqual(somewhere, moved_vehicle.position.geoid, "link start location should not be the same")
+        self.assertNotEqual(somewhere, moved_vehicle.position.geoid,
+                            "link start location should not be the same")
 
     def test_charge(self):
 
-        state = ChargingBase(DefaultIds.mock_vehicle_id(), DefaultIds.mock_base_id(), mock_dcfc_charger_id())
+        state = ChargingBase.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_base_id(),
+                                   mock_dcfc_charger_id())
         veh = mock_vehicle_from_geoid(vehicle_state=state, soc=0.5)
         sta = mock_station_from_geoid()
         bas = mock_base_from_geoid(station_id=sta.id)
-        sim = mock_sim(
-            vehicles=(veh,),
-            stations=(sta,),
-            bases=(bas,),
-            sim_timestep_duration_seconds=1
-        )
+        sim = mock_sim(vehicles=(veh, ),
+                       stations=(sta, ),
+                       bases=(bas, ),
+                       sim_timestep_duration_seconds=1)
         env = mock_env()
 
         error, result = vehicle_state_ops.charge(sim, env, veh.id, sta.id, mock_dcfc_charger_id())
@@ -54,23 +53,21 @@ def test_charge(self):
 
         updated_veh = result.vehicles.get(veh.id)
 
-        self.assertGreater(
-            updated_veh.energy[EnergyType.ELECTRIC],
-            veh.energy[EnergyType.ELECTRIC],
-            msg="should have charged")
+        self.assertGreater(updated_veh.energy[EnergyType.ELECTRIC],
+                           veh.energy[EnergyType.ELECTRIC],
+                           msg="should have charged")
 
     def test_charge_when_full(self):
 
-        state = ChargingBase(DefaultIds.mock_vehicle_id(), DefaultIds.mock_base_id(), mock_dcfc_charger_id())
+        state = ChargingBase.build(DefaultIds.mock_vehicle_id(), DefaultIds.mock_base_id(),
+                                   mock_dcfc_charger_id())
         veh = mock_vehicle_from_geoid(vehicle_state=state, soc=1.0)
         sta = mock_station_from_geoid()
         bas = mock_base_from_geoid(station_id=sta.id)
-        sim = mock_sim(
-            vehicles=(veh,),
-            stations=(sta,),
-            bases=(bas,),
-            sim_timestep_duration_seconds=1
-        )
+        sim = mock_sim(vehicles=(veh, ),
+                       stations=(sta, ),
+                       bases=(bas, ),
+                       sim_timestep_duration_seconds=1)
         env = mock_env()
 
         error, result = vehicle_state_ops.charge(sim, env, veh.id, sta.id, mock_dcfc_charger_id())