# ADR 0015: Simulation Polygon as a qualification boundary ## Status Accepted for architecture; SIM S0 qualification returned `GO` on 2026-07-24. Navigation behavior, safety behavior and real actuator authority are not accepted. ## Context Mission Core has proven a K1 observation/archive/perception vertical, but the perception outputs do not yet have a closed-loop consumer. Adding Gazebo, PX4, ROS 2 and Nav2 directly to the web process or modeling every experiment as an observation session would blur product ownership, lifecycle, evidence and authority. Replay, physics simulation, a digital twin, HIL and real shadow also have different causal meaning. A recorded stream cannot react to a hypothetical command, while a closed-loop world can. Calling both `SimulationRun` would make reports easy to misuse. ## Decision 1. Polygon is a first-class Mission Core bounded context for autonomy qualification and assurance. 2. Mission Core owns scenarios, qualification-run lifecycle, authority policy, canonical contracts, provenance, evidence admission, evaluation and reports. 3. Gazebo, PX4 SITL, ROS 2, Nav2, Rerun and datasets are replaceable providers. 4. `QualificationRun` is the umbrella aggregate. Its kinds explicitly separate `simulation_closed_loop`, `replay_shadow`, `digital_twin_closed_loop`, `controller_in_loop`, `hil` and `physical_shadow`. 5. Simulation Orchestrator is the sole process-lifecycle owner. Gazebo and PX4 do not run inside the Mission Core web process, and the browser has no direct PX4 channel. 6. Gazebo `/clock` is authoritative in closed-loop simulation. ROS 2 uses simulated time and PX4 uXRCE-DDS time synchronization is disabled for this profile. 7. Mission Core uses ENU/FLU. PX4 uses NED/FRD. Exactly one adapter boundary converts frames and requires golden tests. 8. The first command boundary permits only rover speed+steering and speed+yaw-rate profiles with sequence, TTL, heartbeat, watchdog and stop-on-loss behavior. Direct actuator setpoints are rejected. 9. Source-of-record, derived artifacts and presentation state remain separate. Reset creates a new immutable run/episode. 10. Ground truth is evaluator-only and is not visible to perception or planning. 11. SIM S0 targets the reviewed Windows 11 worker through a dedicated `MissionCore-Sim` WSL2 Ubuntu 24.04 distribution physically under `D:\NDC_MISSIONCORE`. 12. SIM S0 does not depend on the existing Docker Desktop runtime. Its WSL backend is C-backed and currently serves unrelated co-tenants. 13. S1 uses exact `px4_msgs` directly. The experimental `px4-ros2-interface-lib` is not a mandatory S1 dependency. 14. A top-level Polygon UI is gated on accepted contracts and a proven S1 backend lifecycle with persisted run history. 15. Real actuator authority requires a new decision and physical safety gate. 16. S0 processes run inside an ephemeral loopback-only Linux network namespace. Micro XRCE-DDS Agent's UDP wildcard bind is acceptable only inside that boundary; no S0 listener may be reachable from the worker LAN or Windows host network. ## Consequences - Perception can be measured by downstream decisions without granting it hidden behavior authority. - Standard planners and future Mission Core planners can be compared on the same scenario and metric profiles. - Replay cannot be presented as closed-loop safety evidence. - Provider upgrades require explicit compatibility and evidence refresh. - More up-front domain and artifact work is required before a visual demo. - The AI worker must satisfy D-only, resource and coexistence constraints before installation is accepted. - Simulation and HIL reduce risk but do not certify the real trike. ## First implementation The qualification doctor is deliberately read-only: - `simulation/s0/qualification-profile.yaml` is the accepted S0 stack, storage, clock, authority, port and process contract. - `k1link.simulation.s0` strictly validates that contract and optional digest-bound worker evidence. - `missioncore-sim s0 doctor` inspects the current host without installing, starting or modifying anything. - MISSIONCOR-40 tracks factual worker qualification. The first target bootstrap also includes `simulation/s0/worker-smoke.sh`. It creates the reviewed loopback-only namespace, starts only the pinned stock-rover providers, observes ROS 2 vehicle status and Gazebo clock, requests PX4 shutdown, checks for owned-process residue and writes D-only factual artifacts. It never arms the vehicle or sends an actuator command. `simulation/s0/gazebo_time_control.py` owns the S0 pause/single-step/1×/2× RTF and resource acceptance probe. `simulation/s0/build_evidence_manifest.py` admits only exact-profile, D-confined, passing 1×/2× runs into the digest-bound evidence generation. These scripts are qualification tooling; the S1 Simulation Orchestrator remains the future product lifecycle owner. The doctor reports `INCOMPLETE` until it is running on the reviewed target with resolved/accepted pins and all required evidence. On the accepted profile generation and nine-claim confined evidence set it returned `GO`; it cannot infer that result from repository state alone. ## S1A implementation `k1link.simulation.contracts` now defines the first product-owned `QualificationRun`, provider/authority/artifact provenance, canonical Ackermann and Differential rover setpoints, and qualification events. `k1link.simulation.run_store` persists them as an immutable manifest plus one-file-per-sequence append-only journals. Exclusive creation and file/directory fsync prevent partially acknowledged records; loading rejects gaps, identity drift, non-regular records and illegal lifecycle history. The aggregate derives its current state by replay. Terminal runs cannot accept new events, commands or artifacts. A restart never silently resumes an active run: reconciliation terminates it as `failed` with `orchestrator-recovery-interrupted`. Reset requires a terminal parent and creates a new linked run/episode. Commands are admitted only for a running closed-loop virtual run with the current authority generation and bounded simulation-time TTL. Replay/shadow commands, direct actuator authority and ungated HIL/controller-in-loop admission fail closed. This increment proves the local lifecycle/repository contract. It does not yet own provider processes, transmit a PX4 setpoint, execute heartbeat/watchdog failsafe behavior, or qualify navigation/safety behavior. The target orchestrator must place the caller-supplied repository root under the accepted D-only worker boundary.