A Simulation ID Is Not a Running Simulation
createSimulation() returned an ID. startSimulation() still failed. That was the sixth version of the gateway's live MiroFish path. Five earlier runs had already found a wrong Redis instance, a duplicate simulation record, a synchronous ontology response mistaken for an asynchronous task, a project ID nested under data , and graph status nested under the same wrapper. The new ID looked like…
A simulation identifier does not indicate an active simulation. Despite creating a sixth live MiroFish path version, five previous attempts had identified wrong Redis instances, duplicate simulation records, and other issues. The new ID appeared to signify progress, yet MiroFish confirmed that the simulation had not been prepared.
The seventh run attempted preparation, revealing that report generation needed to be triggered explicitly after the swarm stopped. On the eighth attempt, the entire process completed successfully.
The client's failures were not due to complex algorithms but the client's misunderstanding that the upstream API was a state machine. The client had assumed an ordinary sequence of HTTP calls, unaware of the underlying state transitions. The first hint of the issue was the synchronous generation of ontology data, which was mistakenly treated as an asynchronous task. The graph status response used an additional nested payload, causing the poller to wait indefinitely after completing its job.
The simulation startup process required additional steps. The project ID from graph construction was not the same as the simulation ID. The gateway had to create the simulation, retain the returned simulation ID, prepare profiles and configurations, wait for preparation, and then start the run. A report was an asynchronous job with its own task ID and poll cycle.
TypeScript helped clarify these issues, but it could not infer them from an API that returned HTTP success for an operation whose downstream state was not ready. The response normalizers were adjusted to better reflect the actual operation state. Each normalizer accepted the specific wrapper shapes for that operation and rejected data that could not produce its domain type. This approach made the code more explicit and required more small functions than a universal unwrapData() helper.
The failure analysis also affected the test boundaries. Unit tests covered client methods and polling behavior, but the recorded response shapes were only hypotheses until a real MiroFish response was obtained. The launch run became a contract test for the entire lifecycle. A small set of verified responses was kept for future upstream updates, allowing failures to be detected at the decoder stage rather than during simulation execution.
The simulation process was divided into distinct phases: graph_building, simulating, reporting, completed, and failed. These phases were recorded in PostgreSQL as ownership transitions between the gateway and MiroFish. The gateway generated a seed document, asked for an ontology, started graph construction, and polled for the graph task's completion.
During simulation, it created and prepared the upstream run, waited for preparation, and started the simulation. During reporting, it triggered report generation, waited for the task, fetched the report, parsed predictions, and committed the terminal result.
The orchestration code was aware of the MiroFish lifecycle beyond mere UI labels. It needed to know which remote ID existed, which phase owned it, and whether repeating an operation was safe. The HTTP layer handled connection failures but did not retry state-changing requests, ensuring safe and predictable behavior.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.