skills/numericalos-verify-boot/SKILL.md
back to source
---
name: numericalos-verify-boot
description: Boot a NumericalOS artifact under QEMU and report what actually happened. Use after any NumericalOS build skill, or when the user asks to verify, test, smoke-test, or prove a NumericalOS image boots. This is the ONLY skill in the family permitted to make a boot claim, and only about the exact artifact and architecture it observed. Closes the verification gate the NumericalOS repository itself cannot close.
---
# Verify a NumericalOS boot
The NumericalOS repository ships with `Status: bootstrap logic tested; boot
unverified`, because its authors had no Linux host and no emulator. This skill is
how that gate gets closed - on the user's machine, for one artifact, on one
architecture, at a time.
**You are the only skill in this family allowed to say something booted.** Every
other skill produces artifacts and says so. You observe a boot and report it.
That asymmetry is the point; do not dilute it.
## Step 0 - what you are verifying
Get this straight before running anything, because the claim you may make differs:
| Artifact | What a successful run proves |
|---|---|
| ISO | the full chain: firmware, bootloader, kernel, initramfs, `/init`, phase walk |
| kernel + initramfs (direct) | everything except bootloader and media layout |
| OCI image | phase walk and DAG only - **no boot**; do not use this skill, use the run step in `numericalos-build-oci` |
If handed a container image, stop and say this skill does not apply.
## Step 1 - confirm the emulator matches the artifact's architecture
```sh
command -v qemu-system-x86_64 qemu-system-aarch64 qemu-system-riscv64
qemu-system-$QEMU_ARCH --version
```
The emulator arch must match the **artifact** arch, not the host arch. Verifying
an aarch64 image on an x86_64 host is the entire reason this skill is valuable -
but it needs `qemu-system-aarch64` installed, and `qemu-system-x86_64` will not
substitute.
If the needed emulator is absent, say which one and stop. Do not verify a
different arch and imply it covers the requested one.
## Step 2 - boot it, headless, capturing serial
Serial capture is mandatory: `numinit`'s entire output goes to the console, and a
graphical window you cannot read proves nothing.
**ISO:**
```sh
timeout 120 qemu-system-x86_64 \
-M q35 -m 512 \
-cdrom numericalos-x86_64.iso \
-nographic -serial mon:stdio \
-no-reboot \
2>&1 | tee boot.log
```
**Kernel + initramfs directly** (faster, skips the bootloader):
```sh
timeout 120 qemu-system-x86_64 \
-M q35 -m 512 \
-kernel /boot/vmlinuz-$(uname -r) \
-initrd numericalos-initramfs-x86_64.cpio.gz \
-append "console=ttyS0 NUMOS_STATE=/etc/numos.state" \
-nographic -serial mon:stdio \
-no-reboot \
2>&1 | tee boot.log
```
**aarch64** needs an explicit machine and CPU - there is no default:
```sh
timeout 120 qemu-system-aarch64 \
-M virt -cpu cortex-a57 -m 512 \
-kernel Image -initrd numericalos-initramfs-aarch64.cpio.gz \
-append "console=ttyAMA0 NUMOS_STATE=/etc/numos.state" \
-nographic -no-reboot \
2>&1 | tee boot.log
```
Three flags that are load-bearing:
- **`timeout 120`** - a hung `longrun` unit or a missing init will otherwise block
forever. A timeout is a *result*, not an error to retry away.
- **`-no-reboot`** - a kernel panic reboots by default and loops; this makes the
panic terminal and readable.
- **`-nographic` with `-serial mon:stdio`** - puts the guest console on stdout.
To exit QEMU from stdio, `Ctrl-a x`.
## Step 3 - read the log against the real contract
Grep for the two things that matter:
```sh
grep -E '^numos: ' boot.log
grep -E 'Kernel panic|not syncing|No working init' boot.log
```
| Observed | Verdict | What you may say |
|---|---|---|
| `numos: boot complete degraded=0` | **PASS** | it booted, all phases succeeded, on this arch |
| `numos: boot complete degraded=1` | **PASS, degraded** | it booted; a `degrade`-policy phase failed - name which |
| `numos: HALT: <reason>` | **fail-closed, working correctly** | it did not complete; the halt is the system behaving as designed - quote the reason |
| `Kernel panic ... Attempted to kill init` | **ambiguous** | PID 1 exited after the phase walk; if `boot complete` appeared first, the boot logic succeeded and the panic is expected for an all-`oneshot` state |
| `No working init found` | **FAIL** | the initramfs layout is wrong - `/init` missing or not executable |
| timeout with no `numos:` lines | **FAIL** | never reached userspace; kernel/arch mismatch or bad initramfs |
| timeout after `numos:` lines | **FAIL, hang** | a `longrun` unit blocked the phase walk - name the last phase reached |
**The panic case deserves care.** A kernel panics when PID 1 exits, even
successfully. If `numos: boot complete degraded=0` appears and *then* the kernel
panics, NumericalOS did its job and the panic is a property of the state file
having only `oneshot` units. Report both facts in that order; reporting only the
panic misrepresents a successful run, and reporting only the success hides that
the machine did not stay up.
## Step 4 - report precisely
Use this shape. Scope every claim to what you observed:
```
Artifact: <path> sha256 <hash>
Arch: <arch> Emulator: qemu-system-<arch> <version>
Boot: PASS / PASS-degraded / FAIL / HALT
numos output:
<every numos: line, verbatim>
Panic: <yes + text / no>
Verdict: <one sentence, scoped to this artifact on this arch>
Not proven: <other arches, real hardware, persistence, networking>
```
**Never generalize.** "It boots on x86_64 under QEMU 8.2" is a claim you earned.
"NumericalOS boots" is one you did not - other architectures are untested by this
run, and emulation is not hardware. Firmware, real device enumeration, and
timing all differ on metal.
## Step 5 - if it failed, hand back something actionable
Do not retry blindly. Map the failure to its owner:
| Failure | Fix belongs to |
|---|---|
| `No working init found` | `numericalos-build-initramfs` Step 3 - `/init` path/mode |
| `arch_table.sh: not found` | `numericalos-build-initramfs` - the `NUMOS_LIB` path issue |
| `HALT: unsupported architecture` | busybox arch does not match kernel arch |
| `HALT: state hash mismatch` | state was edited after export - re-export, never hand-edit |
| `HALT: NUMOS_STATE is unset` | cmdline or initramfs did not set it |
| `HALT: phase <n> ... on_failure=halt` | **not a build bug** - the state's units do not suit this environment; change the state |
| hang after a phase | a `longrun` unit; note that `numinit` backgrounds these, so a hang here is worth reporting upstream |
That last row matters: a `halt` on a seeded state under QEMU usually means the
seeded units (`mount -t proc`, `ip link`, `ntpd`, `numctl`) do not match a bare
emulated machine. That is the system correctly refusing to proceed, not a broken
image. Say so, and offer to export a state whose units suit the environment.
## Reporting upstream
If you close this gate, the result is genuinely new information about the
project. Encourage the user to report it - artifact, arch, emulator version, full
`numos:` output - since the repository's own status line can only change on the
strength of runs like this one.
## Related skills
- `numericalos-target-classifier` - run first
- `numericalos-build-initramfs`, `numericalos-build-iso` - produce what you verify
- `numericalos-build-oci` - containers do not boot; that skill's run step is their equivalent