diff --git a/README.md b/README.md index 5a0984d..f41d938 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,120 @@ Not yet implemented (planned): - Factory reset (backup, wipe, restore) - Flash modes (disk clone, network, HTTP/HTTPS) +## Startup Flow + +The diagram traces every phase from PID 1 start to `switch_root`. The `release-image` +feature flag determines the error-handling branch at each fatal failure point. + +```mermaid +flowchart TD + START([PID 1 starts]) --> MOUNT_ESS["mount_essential_filesystems\n/dev ยท /proc ยท /sys ยท /run"] + + MOUNT_ESS -->|OK| LOGGER["KmsgLogger::init()"] + MOUNT_ESS -->|Fail| EARLY_ERR{Image type?} + EARLY_ERR -->|release| HALT1(["๐Ÿ”ด eprintln loop โ€” halt"]) + EARLY_ERR -->|debug| ESHELL(["๐Ÿš emergency sh โ€” respawn"]) + + LOGGER -->|OK| CONFIG["Config::load()\n/proc/cmdline ยท os-release"] + LOGGER -->|Fail| FEB + + CONFIG -->|OK| RDEV["detect_root_device()"] + CONFIG -->|Fail| FEB + + RDEV -->|OK| LAYOUT["PartitionLayout::new()\ncreate_omnect_symlinks()"] + RDEV -->|Fail| FEB + + LAYOUT -->|OK| CORE["mount_core_partitions()\nrootfs + boot + fsck"] + LAYOUT -->|Fail| FEB + + CORE --> BENV["open_boot_env()"] + + BENV --> CLASSIFY{"classify_boot_env"} + CLASSIFY -->|"OK โ†’ Available"| APPLY["apply_boot_env_decision()\ncore_result ร— env decision\npersist_fsck_results โ€” always"] + CLASSIFY -->|"Fail + release โ†’ Degraded"| APPLY + CLASSIFY -->|"Fail + debug โ†’ Abort"| FEB + + APPLY -->|FsckRequiresReboot| FEB + APPLY -->|Fatal| FEB + APPLY -->|"OK\nDegraded: ods.degraded_boot=true"| FBDETECT["compute_first_boot()\nset_update_pending()"] + + FBDETECT --> ISETUP["init_setup::run()\nresize-data preflight\nif feature = resize-data"] + ISETUP -->|FsckRequiresReboot| FEB + ISETUP -->|"ResizeData error\nContinueDegraded โ€” warn"| BMODE["BootMode::detect() โ†’ Normal"] + ISETUP -->|"Fatal (non-resize)"| FEB + ISETUP -->|OK| BMODE + + BMODE -->|Fatal| FEB + + BMODE --> MREM["mount_remaining_partitions()\ndata ยท factory ยท cert + fsck\npersist_fsck_results โ€” always"] + MREM -->|FsckRequiresReboot| FEB + MREM -->|Fatal| FEB + MREM -->|OK| OVL["setup_raw_rootfs_mount()\nsetup_etc_overlay()\nsetup_data_overlay()"] + + OVL -->|OK| LINKS["create_fs_links()\ncreate_ods_runtime_files()"] + OVL -->|Fail| FEB + + LINKS -->|OK| FBM["write_first_boot_marker()\nif first_boot โˆง resize_ok โˆง env_available\nbest-effort โ€” warn on fail"] + LINKS -->|Fail| FEB + + FBM --> SR["switch_root โ†’ systemd"] + SR -->|OK| SUCCESS(["โœ… systemd running"]) + SR -->|Fail| FEB + + FEB{"Error handler\nRecoveryClass?"} + FEB -->|"RebootToApply (e.g. FsckRequiresReboot)"| REBOOT(["๐Ÿ” Reboot"]) + FEB -->|"Fatal + update_pending"| REBOOT + FEB -->|"Fatal + no update + release"| HALT2(["๐Ÿ”ด kmsg loop โ€” halt forever"]) + FEB -->|"Fatal + no update + debug"| DSHELL(["๐Ÿš debug bash/sh โ€” respawn"]) + + classDef success fill:#2d6a2d,color:#fff,stroke:#1a3d1a + classDef reboot fill:#1a4d7a,color:#fff,stroke:#0d2d4d + classDef halt fill:#7a1a1a,color:#fff,stroke:#4d0d0d + classDef shell fill:#7a4a1a,color:#fff,stroke:#4d2d0d + + class SUCCESS success + class REBOOT reboot + class HALT1,HALT2 halt + class ESHELL,DSHELL shell +``` + +**Terminal states** + +| Symbol | Outcome | Trigger | +|--------|---------|---------| +| โœ… | `switch_root` โ€” systemd takes over | Normal completion | +| ๐Ÿ” | Reboot | `FsckRequiresReboot` (unconditional); or any fatal error while `omnect_validate_update` is set โ€” triggers bootloader OTA rollback | +| ๐Ÿ”ด | Halt (kmsg loop, infinite) | Fatal error ยท release image ยท no OTA in flight | +| ๐Ÿš | Debug shell (bash โ†’ sh fallback, respawning) | Fatal error ยท debug image ยท no OTA in flight | + +**Notes on error handling** + +All errors from `run_init()` reach `handle_fatal_error` in `main.rs`, which dispatches on +`RecoveryClass`: +- `RebootToApply` (e.g. `FsckRequiresReboot`) โ†’ always Reboot, regardless of image type +- `Fatal` + `omnect_validate_update` set โ†’ Reboot (bootloader OTA rollback) +- `Fatal` + no OTA in flight + release โ†’ Halt (kmsg loop) +- `Fatal` + no OTA in flight + debug โ†’ debug shell + +`FsckRequiresReboot` edges in the diagram flow through this handler. + +**Notes on overlay, fs-link, and ODS setup (`OVL` / `LINKS` blocks)** + +These steps (`setup_raw_rootfs_mount`, `setup_etc_overlay`, `setup_data_overlay`, +`create_fs_links`, `create_ods_runtime_files`) abort the boot on any failure: the error +reaches `handle_fatal_error`, which halts the device on a release image, drops to a debug +shell on a debug image, or reboots when an OTA update is in flight (`update_pending`). +No dedicated design spec covers this region. + +**Notes on `apply_boot_env_decision`** + +`mount_core_partitions` result is captured rather than propagated immediately so that +fsck diagnostics can be persisted to the bootloader environment before any reboot. +`apply_boot_env_decision` enforces the invariant that `FsckRequiresReboot` always wins +over a concurrent `DegradedBoot` โ€” the two failure modes can co-occur when GRUB's +boot partition is unmountable. `persist_fsck_results` runs on every mount path, +including degraded boot. + ## Building ```bash diff --git a/docs/superpowers/specs/2026-05-04-resize-data-design.md b/docs/superpowers/specs/2026-05-04-resize-data-design.md index a06378c..32fd83c 100644 --- a/docs/superpowers/specs/2026-05-04-resize-data-design.md +++ b/docs/superpowers/specs/2026-05-04-resize-data-design.md @@ -1,5 +1,10 @@ # resize-data: Data Partition Auto-Resize +**Status:** Partially superseded โ€” the "resize failure is fatal" claim (ยง Error Handling, +line ~130) was reclassified to `ContinueDegraded` (non-fatal) in +`2026-05-27-fsck-and-resize-design.md` and `2026-06-22-first-boot-retry-on-resize-failure.md`. +The resize mechanism itself remains valid. Retained for design history. + ## Problem When an omnect OS image is flashed to a disk larger than the image itself, the diff --git a/docs/superpowers/specs/2026-05-12-first-boot-mode-design.md b/docs/superpowers/specs/2026-05-12-first-boot-mode-design.md index e5d5006..87e937b 100644 --- a/docs/superpowers/specs/2026-05-12-first-boot-mode-design.md +++ b/docs/superpowers/specs/2026-05-12-first-boot-mode-design.md @@ -1,9 +1,13 @@ # Design: BootMode::FirstBoot and resize-data relocation **Date:** 2026-05-12 -**Status:** Draft +**Status:** Superseded by `2026-05-13-preflight-design.md` **Branch:** TBD (follows feat/resize-data) +> **Note:** This design was superseded before implementation. `BootMode::FirstBoot` and +> `mode::first_boot::run` were never shipped. Resize-data was implemented as a preflight +> step instead โ€” see `2026-05-13-preflight-design.md`. Retained for design history. + --- ## 1. Motivation diff --git a/docs/superpowers/specs/2026-05-13-preflight-design.md b/docs/superpowers/specs/2026-05-13-preflight-design.md index 79b9148..e62443a 100644 --- a/docs/superpowers/specs/2026-05-13-preflight-design.md +++ b/docs/superpowers/specs/2026-05-13-preflight-design.md @@ -1,7 +1,10 @@ # Preflight: Separate One-Time Prep from Mode Dispatch **Date:** 2026-05-13 -**Status:** Draft +**Status:** Partially superseded โ€” the "resize failure is fatal" claim (verification table, +line ~191) was reclassified to `ContinueDegraded` (non-fatal) in +`2026-05-27-fsck-and-resize-design.md` and `2026-06-22-first-boot-retry-on-resize-failure.md`. +The preflight separation design itself remains valid. **Supersedes:** `2026-05-12-first-boot-mode-design.md` --- @@ -188,7 +191,7 @@ branch in `BootMode::detect()`. | Resize skipped when data partition missing | โœ“ (filesystem layer) | โœ“ (filesystem layer unchanged) | | Resize sets guard after success | โœ“ | โœ“ | | Resize runs after core mount, before data mount | โœ“ | โœ“ | -| Resize failure is fatal | โœ“ | โœ“ | +| Resize failure is fatal | โœ“ | โœ“ | โ† **superseded**: reclassified to `ContinueDegraded` in `2026-05-27-fsck-and-resize-design.md` | | `normal::run` mounts remaining + overlays + ODS + switch_root | โœ“ | โœ“ | | Degraded boot when bootloader unavailable | โœ“ | โœ“ |