# M10 shutdown validation Development reference and historical context. For current operating instructions, use the [user manual](../README.md). Acceptance applies only to the source and artifacts identified in each record. [Shutdown usage](power.md) · [Implementation ledger](implementation-status.md) M10 software acceptance passed on 2026-09-21 in isolated ARM virtual machines. The complete shutdown suite, CLI packaging and earlier-milestone regressions passed. Physical Pi timing, flash-controller durability, power rails and battery behavior remain deferred. ## Verified shutdown behavior The ordinary FDS user can request `fds poweroff` or `fds reboot`. New cartridge operations freeze before the daemon stops the desktop, managed programs and networking. The original DATA filesystem descriptor checks writeback, the filesystem becomes read-only, a second check catches remount writeback, and normal unmount completes before native s6 stops the remaining services and halts or reboots. Dasung remains supervised until that final service stop. `make power-test` passed all six cases in `out/m10-vm.tituqzh7/`: | Case | Observed result | VM preparation-to-kernel-action time | | --- | --- | --- | | Idle | Ordinary-user poweroff; unprivileged internal hook refused | 540.733 ms | | Active workload | GUI, DHCP and TERM-ignoring descendants stopped; DATA verified; actual reboot | 1835.329 ms | | Direct native poweroff | Busy DATA blocks the native hook until resolved; cancellation refused | 414.091 ms | | Active media preview | Shutdown and new confirmation refused; cancellation leaves disk unchanged | 289.099 ms after retry | | Crashed DATA service | Same insertion stays quarantined across further restarts; no false SAFE | 281.307 ms after removing fixture | | Attached-device writeback error | Real EIO retained across restarts; SAFE and shutdown refused | 344.777 ms after removing fixture | These are measurements inside generic QEMU with the Pi kernel, not Pi or physical power-off benchmarks. They do not establish the sub-second idle or 1.5-second normal-DATA hardware targets. The workload deliberately ignores TERM, so the manager must wait its bounded termination deadline and confirm actual exit before proceeding. Each case records ordered `CLOCK_BOOTTIME` events and captures the final native shutdown record. The test waits for the kernel's halt/reboot message and QEMU's successful exit; closing the emulator does not count as a completed shutdown. The full command log is `out/logs/m10-final-power.log`. ## DATA integrity and failure handling The workload writes at least 128 MiB through a rotating 64 MiB file. After reboot, independent host `e2fsck` finds a clean filesystem, SHA-256 matches all 64 MiB, and an ordinary-user marker retains its exact contents. Open writable descriptors and a shell working directory both block shutdown. Explicit resume enables new operations but does not recreate stopped jobs. Before writable activation, a root-private insertion record is created under `/run/fds/data-sessions`. A service crash cannot discard the original descriptor's writeback history and then issue SAFE based on a fresh descriptor. The same insertion becomes read-only and quarantined; a known error retains its diagnostic. Records survive service restarts during this boot, not power loss or a different insertion. The separate DATA regression passed in `out/m7-vm.vmhkmyqk/`. The writeback test uses QEMU's `blkdebug` to return persistent EIO for writes to a known allocated file block. GPT, manifest and journal metadata remain outside that injected sector. The device stays attached; Linux reports a real writeback error and `syncfs` fails. The daemon refuses eject, keeps the fault after restart, and blocks shutdown through another restart. No filesystem-cleanliness claim is made for this deliberately damaged image. Removing the test fixture merely lets the remaining system shut down; it never turns the failed insertion SAFE. See [QEMU's fault-injection documentation](https://www.qemu.org/docs/master/devel/testing/blkdebug.html). A one-shot fault is insufficient evidence by itself because the guest may retry it successfully; the acceptance case verifies the actual guest-visible EIO. ## Native guard and dependencies The native shutdown hook remains alive while preparation is blocked, including when an administrator calls the upstream `poweroff` directly. A nonzero hook exit would let native shutdown continue, so watcher failure uses a blocked signal wait. Normal waiting uses inotify and process/cgroup exit events, with no sleep-based assumptions. Once native shutdown starts it cannot be cancelled. `fds-power` is a static alias of `fds`. The CLI now directly declares the already locked `libc` crate for inotify, identity and signal operations. No new external crate or target package is introduced. `fds-init` explicitly depends on `fds-cartridged`, since the native hook requires its DATA checks. DHCP and its privilege-separated children now share a managed cgroup. The final native grace period is zero after managed exits and DATA verification have completed. ## Regression evidence The development archive passed rootfs rejection checks, native PID 1/service control and actual poweroff, desktop/network transitions, tooling/static-ELF checks and `make check`. Logs are `out/logs/m10-final-*.log`; desktop evidence is `out/m8-vm.1c20rcpb/`, native init is `out/m2-vm.5ARJNL/`. The final CLI image also passed native init (`out/m2-vm.ysuhZ5/`), all nine stage0 cases (`out/m4-vm.e3SBsX/`), and the ordinary-user console checks (`out/m5-vm.4l796uhp/`). Cartridge regression passed in `out/m6-vm.p7gfwnj2/`, DATA regression in `out/m7-vm.82fqc9wa/`, and confirmed media writes in `out/m9-vm.5a58kou6/`. The development runtime tested here is `out/rootfs-build.WEtGnr/`, with 191 packages and 1406 AArch64 ELF files. Its rootfs SHA-256 is `9de03100b998cac2a754fc8e05acbad0bd3f45846dd637ba4c597ea3ba61f5bb`. Its SYSTEM is `out/system-build.dd3e74_2/system.img`, SHA-256 `bef3b01bc8bc68ca673aa6a0ec73892ddaa2901864eed204745749915ee34b18`. The subsequent explicit init-to-cartridge package dependency was verified in the final CLI build; the development archive already includes that daemon through its base package. Runtime binaries and service behavior are identical. The CLI archive has 180 packages and 1394 AArch64 ELF files, and is 499527680 bytes. Kernel-to-console was 1821.039788 ms versus M9's 1854.858231 ms: **−33.818443 ms**. The actual ARM comparison passed without a regression exception; see `out/logs/m10-boot-comparison.log`. This is a VM observation, not a Pi target result. | Artifact | Location | SHA-256 | | --- | --- | --- | | CLI rootfs | `out/rootfs-build.0AzReg/rootfs-aarch64.tar` | `ebfc58171aeee6f737c2a93a0f3a318d4f02eb915edb3b5a3cf0f51848863930` | | CLI SYSTEM | `out/system-build.c7q19iil/system.img` | `81dc8083777b497f02a290a15eeb246465689d0deaff97d761aeb882d140b44b` | | FAT32 boot partition | `out/boot-build.42abbf_h/boot.fat` | `0972b5973d1fefc377ec4c800043fb8e4aa3013140f95218cdebcf38dcd22a49` |