# SIM S0 AI worker runbook

This runbook is for MISSIONCOR-40. It does not authorize system installation,
real vehicle control or a `GO` verdict by itself.

## Safety and placement

- Target: reviewed Windows 11 worker with the dedicated `MissionCore-Sim` WSL2
  Ubuntu 24.04 distribution physically installed on D.
- All mutable bytes must be under `D:\NDC_MISSIONCORE`.
- Do not place a WSL distribution, source, build, cache, log, bag, ULog,
  Gazebo artifact or dataset on C.
- Do not use or reconfigure the existing Docker Desktop runtime in S0. Its
  backend is C-backed and serves unrelated co-tenants.
- Do not expose S0 services beyond loopback.
- Run providers inside the ephemeral loopback-only network namespace created
  by `simulation/s0/worker-smoke.sh`. Micro XRCE-DDS Agent binds UDP wildcard
  inside that namespace because v2.4.3 has no address-selection option.
- Do not download full external datasets.
- Keep `actuator_authority=false`.

## Repository preflight

From the repository-local Python 3.12 environment:

```bash
uv sync --frozen --group dev
uv run missioncore-sim s0 doctor --json
```

Before target evidence exists, the expected verdict is `INCOMPLETE`.

## Stock-rover smoke

Copy the reviewed script to the D-only worker source root, then invoke it as
root inside `MissionCore-Sim` with a unique immutable run ID:

```bash
simulation/s0/worker-smoke.sh <UTC-run-id>-stock-rover <1|2>
```

The script:

- creates a new network namespace containing only loopback;
- starts Micro XRCE-DDS Agent, PX4 SITL and the stock Gazebo Ackermann world;
- verifies the DDS session, `/fmu/out/vehicle_status_v1` and Gazebo `/clock`;
- proves pause freeze and exactly one 2 ms physics step;
- measures the declared 1× or 2× real-time factor over four seconds;
- samples owned CPU/RSS, available RAM, swap, D free space and global co-tenant
  GPU load at 500 ms intervals;
- records Gazebo service/topic inventory and runtime listeners;
- sends the PX4 `shutdown` command and rejects owned-process residue.

Run both factors as separate immutable cases. Do not reuse a run ID. A failed
run remains diagnostic evidence; rerun with a new ID after correction.

## Required target inventory

Record a redacted artifact for each of:

1. Windows release, WSL version, Ubuntu version and architecture.
2. GPU/driver, CPU, RAM and VRAM.
3. Physical location of the dedicated WSL distribution and confirmation that
   the Docker-free S0 profile is active.
4. D total/free bytes before installation.
5. Existing Docker images, WSL distributions and simulation-related processes.
6. Existing listeners and the candidate port registry.
7. Existing Frigate/Ollama/Triton resource use and whether S0 is coexistence or
   exclusive-profile testing.

Do not record credentials, tokens or private network secrets.

## Candidate stack

Use `simulation/s0/qualification-profile.yaml` as the input. Resolve and record:

- exact ROS 2 Jazzy package versions;
- exact Gazebo Harmonic package versions;
- PX4 v1.17.0 commit;
- matching `px4_msgs` commit;
- Micro XRCE-DDS Agent commit/version;
- Nav2 Jazzy commit/package versions;
- PX4 Gazebo model commit;
- licenses and image digests where containers are used.

Change every component to `accepted` only after target build/runtime evidence
exists. Updating a pin changes the profile SHA-256 and invalidates evidence for
the prior generation.

## Runtime cases

The S0 factual cases are:

1. Launch one stock rover headless.
2. Observe PX4 health and ROS 2 telemetry.
3. Prove Gazebo `/clock` is authoritative.
4. Prove pause freezes simulation time.
5. Prove one single-step advances the declared amount.
6. Prove 1× and 2× speed behavior and measure real-time factor.
7. Stop through the intended lifecycle.
8. Prove no owned process remains.
9. Repeat from a clean start.

QGroundControl is diagnostic only and cannot become the Mission Core command
owner.

## Evidence manifest

Create a private/redacted evidence directory outside normal Git. Its manifest:

```yaml
schema_version: missioncore.simulation-s0-evidence/v1
profile_sha256: <sha256 of qualification-profile.yaml>
checks:
  - id: worker-inventory
    status: pass
    artifact: worker-inventory.redacted.json
    sha256: <artifact sha256>
    note: Redacted host and resource inventory.
```

The manifest must contain exactly all IDs from `required_evidence`. Artifact
paths are relative, confined regular files. The doctor rejects missing,
additional, changed or cross-generation evidence.

Build the redacted manifest only from two passing runs made with the exact
accepted profile generation:

```bash
python3 simulation/s0/build_evidence_manifest.py \
  --profile /mnt/d/NDC_MISSIONCORE/simulation/source/qualification-profile.yaml \
  --run-1x /mnt/d/NDC_MISSIONCORE/simulation/artifacts/s0/<accepted-1x-run> \
  --run-2x /mnt/d/NDC_MISSIONCORE/simulation/artifacts/s0/<accepted-2x-run> \
  --output-dir /mnt/d/NDC_MISSIONCORE/simulation/artifacts/s0/evidence/<generation> \
  --d-root /mnt/d/NDC_MISSIONCORE \
  --source-root /mnt/d/NDC_MISSIONCORE/simulation/source \
  --repository-commit <full-commit-sha> \
  --wsl-windows-path 'D:\NDC_MISSIONCORE\simulation\wsl\MissionCore-Sim'
```

The generator refuses an existing output directory, non-passing runs,
candidate pins, commit drift, incomplete Nav2/ROS discovery or any input/output
outside the reviewed D root.

Validate without writing:

```bash
uv run missioncore-sim s0 doctor \
  --profile simulation/s0/qualification-profile.yaml \
  --evidence /path/to/redacted/evidence.yaml \
  --json
```

## Verdict rules

- `GO`: target host matches, every pin is accepted, D-only and disk thresholds
  pass, required tools exist and all digest-bound evidence validates.
- `INCOMPLETE`: work remains or the doctor is running on a development host.
- `BLOCKED`: contract, storage, tools or evidence fail validation.

Publish the factual result to MISSIONCOR-40 with exact repository commit,
profile SHA-256, files touched and validation performed.
