docs(v0.2): спека boot-конвейера (Stage 0/1/2 + splash)

Веха v0.2: рефактор плоского shturman.target в фазовый конвейер.
- Stage 0: shturman-splash (Slint software-render → splash.png, до первого кадра);
- Stage 1: критический набор v0.1 → shturman-stage1.target (без изменений тела);
- Stage 2: shturman-stage2-warmup (oneshot-плейсхолдер, после кадра);
- shturman.target → зонтик; общий headless-render хелпер из shell переиспользует splash.
D-Bus-поверхности нет (фазы — systemd). Splash в VM = software-render PNG
(U-Boot framebuffer — HW, шов §10). Тайминг <10c — функц. в VM, вердикт на RK3588.
Приёмка: фазы разделены + splash до кадра + warmup после + регресс v0.1/v0.6 зелёный.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Alexander <akotenev2003@gmail.com>
This commit is contained in:
2026-06-24 19:51:49 +03:00
parent 9b87751ab8
commit 9a3b6a8753
+197
View File
@@ -0,0 +1,197 @@
# Спека реализации: v0.2 — Boot-конвейер (Stage 0/1/2 + splash)
> Веха `v0.2` роадмапа: «splash → таргет фазами»; capabilities **A04** (быстрый boot Stage 0/1/2, <10 c),
> **A05** (splash, Stage 0), **A15** (systemd-таргеты/оркестрация). Поверх **v0.1** (образ + `/data` + `shturman.target`).
> Источники: `docs/architecture.md` §6 (boot), `docs/domains/a-base-system.md` §4, `docs/roadmap.md` §v0.
> Приёмка роадмапа: **«Stage 0/1/2 разделены; splash мгновенно»**.
---
## 1. Цель и первый артефакт
Превратить плоский `shturman.target` (v0.1: один critical set) в **фазовый boot-конвейер** из трёх явных
стадий, с **мгновенным splash** до первого кадра Shell и **деферредом фоновой нагрузки** после интерактива.
**Первый артефакт:** на boot VM рендерится `/run/shturman/splash.png` (Stage 0) **раньше**, чем
`/run/shturman/frame.png` (Stage 1, Shell), а Stage 2 (warmup) стартует **после** кадра. Все три стадии —
отдельные systemd-таргеты, достижимые и упорядоченные; boot-тайминг логируется.
**Не цель v0.2:** перфоманс-вердикт (<10 c — на RK3588, performance §2; в VM — функционально); красивый
визуальный язык splash/Shell (язык — гейт v0.5); реальные Stage-2-сервисы (Vehicle-Data/Assistant/… — v1+).
---
## 2. Скоуп и границы
### 2.1 В скоупе (делаем сейчас)
- **Splash (A05):** новый `shturman-splash` — Slint software-render брендового splash-кадра → PNG
(headless, без дисплея/композитора; зеркалит механику shell-кадра v0.1). Стартует максимально рано
(Stage 0, минимум зависимостей), **до** первого кадра Shell.
- **Фазовые таргеты (A15):** `shturman-stage0/1/2.target` + рефактор `shturman.target` в **зонтик**.
Члены нынешнего critical set переезжают в `shturman-stage1.target`.
- **Деферред Stage 2:** `shturman-stage2-warmup.service` — oneshot-плейсхолдер (лог+маркер), `After` первого
кадра. Каркас для реальных фоновых сервисов v1+.
- **Boot-тайминг (A04):** E2E логирует `systemd-analyze` + Δ(splash→frame); **функционально, не гейт**.
Жёстко ассертим **порядок фаз** (splash → frame → warmup) и **достижимость** трёх таргетов.
- **Общий рендер-хелпер:** headless Slint-software-render → PNG выделяется из `shturman-shell` в
переиспользуемый модуль (используют shell + splash). Точное место — план реализации.
### 2.2 Явно НЕ в скоупе (отложено, с указателем «куда»)
- **U-Boot splash (Stage 0 на железе):** framebuffer-картинка загрузчика до ядра — **HW** (a-base §4, §1).
В VM U-Boot'а нет → splash моделируем systemd-сервисом (шов §10).
- **Splash→Shell handoff на реальном дисплее** (без чёрного мелькания, передача поверхности композитору) —
**v0.5** (Shell/композитор smithay). В VM обе стадии — отдельные PNG.
- **Ранний низколатентный путь задней камеры/парктроника в Stage 0** (a-base §1 шов с J/B) — **домен J / v1+**.
- **A/B boot-select, bootlimit, mark-good, secure/verified boot, security-version rollback** (a-base §2, §4) —
**HW/v4** (нет U-Boot/eFuse в VM).
- **Перф-вердикт <10 c** — **RK3588** (performance §2). В VM — функциональный замер с пометкой «не вердикт».
- **Реальные Stage-2-сервисы** (Vehicle-Data, Assistant, Media, Nav, Connectivity) — **v1+** (домены E/D/H/I/G).
### 2.3 Частично в скоупе (каркас сейчас, тело — позже)
- **Stage 2** — только структура (таргет + один warmup-плейсхолдер); приоритеты/oomd-жертвы фона
проверяются на реальной нагрузке позже (performance §5).
- **Параллельный быстрый boot** (минимальный initramfs, ленивые сервисы, a-base §4) — в VM моделируем
systemd-фазами; тюнинг initramfs/ядра — HW.
### 2.4 Трассируемость ID → статус
| ID | Веха | Статус в v0.2 |
|----|------|----------------|
| A04 | Быстрый boot Stage 0/1/2 <10 c | фазы разделены + тайминг логируется (вердикт — HW) |
| A05 | Splash (Stage 0) | splash-сервис рендерит PNG до первого кадра (U-Boot framebuffer — HW) |
| A15 | systemd-таргеты/оркестрация | зонтик + 3 фазовых таргета + splash/warmup юниты |
---
## 3. Красные линии, безопасность, лицензии
- **#1/#2 (нерушимы):** boot-оркестрация + read-only splash — **нет** CAN/actuator/safety-путей. Splash не
читает шину (статичный бренд-кадр), Shell-кадр — read-only OBD/состояние (как в v0.1).
- **Лицензии:** новых тяжёлых зависимостей нет; splash переиспользует Slint (GPL-3.0 exception в `deny.toml`,
уже заведено) + `png` (уже в lock). `just deny` — зелёный.
- **eMMC write-min (A11):** splash.png/frame.png/маркеры — в **tmpfs `/run`** (volatile), не на flash.
---
## 4. Раскладка (новые/изменённые артефакты)
### 4.1 Бинари/крейты
- **`shturman-splash`** (новый bin, `crates/apps/`): Slint-компонент splash + `--screenshot <path>` режим
(как shell). Дефолт — интерактив (HW/dev-Mac); `--screenshot` — headless PNG (VM/E2E). Splash **не** читает
шину (нет зависимости от Power/Settings — стартует до них).
- **Общий рендер-хелпер** (выделить из `shturman-shell`): `render_component_to_png(ui, size, path)` поверх
Slint software-renderer (thread_local `MinimalSoftwareWindow` + `set_platform` once + `draw_if_needed` + `png`).
Используют `shturman-shell` и `shturman-splash`. Место (отдельный lib-крейт vs модуль) — план.
### 4.2 systemd-юниты
| Юнит | Роль | Ключевое |
|------|------|----------|
| `shturman.target` | **зонтик** v0 | `Wants=`stage0+stage1+stage2; `After=data.mount`; `WantedBy=multi-user.target` |
| `shturman-stage0.target` | Stage 0 (splash) | `Wants=shturman-splash.service` |
| `shturman-stage1.target` | Stage 1 (ядро+кадр) | `Wants=`firstboot+machineid+power+settings+shell; `Requires/After=data.mount` |
| `shturman-stage2.target` | Stage 2 (фон) | `Wants=shturman-stage2-warmup.service`; `After=shturman-stage1.target` |
| `shturman-splash.service` | рендер splash | минимум зависимостей; `Before=shturman-shell.service`; oneshot+RemainAfterExit |
| `shturman-stage2-warmup.service` | плейсхолдер фона | oneshot; `After=shturman-shell.service`; лог+маркер `/run/shturman/stage2.ready` |
**Рефактор:** нынешние `WantedBy=shturman.target` у firstboot/machineid/power/settings/shell → **`WantedBy=shturman-stage1.target`**.
`shturman.target` перестаёт прямо тянуть сервисы — тянет три под-таргета.
---
## 5. Контракты D-Bus
**Новой поверхности нет.** Фазы boot — это systemd-оркестрация, не шина. (`BootStage`-property на `Power`
рассматривалась — **YAGNI**, отвергнута: фазы наблюдаемы через `systemctl`/journald; реальный lifecycle-FSM —
v0.3, домен B.) Power/Settings-контракты v0.1 — без изменений.
---
## 6. Splash — Stage-0 кадр (срез A05)
- **UI (Slint):** минимальный брендовый кадр — wordmark «Штурман» по центру на тёмном фоне (нейтральный
плейсхолдер; **визуальные токены design-system — каркас**, полный язык — гейт v0.5). Без статус-бара/тайлов
(это Shell, Stage 1). Без чтения шины — **статичный** (поэтому стартует до Power/Settings → «мгновенно»).
- **Рендер-бэкенды** (как Shell §6 v0.1):
- *dev интерактивно:* Slint под weston/нативно.
- *VM/E2E:* **software-renderer → `/run/shturman/splash.png`** (без дисплея); ассерт «splash не пустой».
- **«Мгновенность»:** splash-сервис без `Requires=data.mount`/dbus — стартует на `basic.target`/`/run` готов,
параллельно критическому набору; `Before=shturman-shell.service` гарантирует splash.png **раньше** frame.png.
- **Граница:** на железе Stage-0-splash — U-Boot framebuffer **до** systemd (a-base §4); systemd-splash здесь —
dev-модель + ранний пост-ядерный splash. Handoff на дисплее (splash→Shell без мелькания) — v0.5.
---
## 7. Boot-конвейер фазами (A04/A15)
Модель (architecture §6, a-base §4): **Stage 0 мгновенно · Stage 1 ~35 c · Stage 2 фоном**.
- **Stage 0:** `shturman-splash.service` → splash.png. Минимум зависимостей, до первого кадра.
- **Stage 1 (нынешний critical set v0.1):** `data.mount` → firstboot → machineid → dbus → power+settings →
**shell (первый кадр frame.png)**. Ordering — в самих юнитах (как в v0.1), членство — `shturman-stage1.target`.
- **Stage 2:** `shturman-stage2-warmup.service` `After=shturman-shell.service` — стартует **после** кадра
(деферред); пишет маркер `/run/shturman/stage2.ready` + лог. Каркас для фоновых сервисов v1+.
- **Наблюдаемый порядок фаз:** `splash.png` (mtime) < `frame.png` (mtime) < `stage2.ready` (mtime); три
таргета достижимы (`is-active`); warmup `After` кадра (journald-время старта > времени рендера кадра).
- **Тайминг (A04, функц.):** E2E логирует `systemd-analyze time` + Δ(boot→splash) + Δ(boot→frame); порог <10 c
**не гейтит** (вердикт — RK3588). Дисциплина: монотонные часы для замеров (a-base §4 / common-helper).
---
## 8. Dev-харнесс (расширение v0.6)
- **`justfile`:** `splash-frame [path]` — инспекция splash-кадра (как `shell-frame`). `run`/`e2e` — без новых
целей (фазы поднимаются тем же `shturman.target`).
- **`tests/e2e/run.sh`:** добавить блок **«Stage 0/1/2»**:
- три таргета `is-active`/reached;
- `splash.png` существует, валидный PNG, mtime **< `frame.png`**;
- `stage2.ready` существует, mtime **> `frame.png`** (warmup после кадра);
- лог `systemd-analyze` + Δ-тайминги (не гейт).
- **`lima/shturman.yaml`:** разложить новые юниты (stage0/1/2.target, splash, warmup) + бинарь splash в `run.sh`.
Splash-зависимостей (пакетов) нет — Slint build-deps уже есть (v0.6).
---
## 9. План тестирования и приёмка
### 9.1 Unit
- `shturman-splash`: `render → PNG` непустой, верный размер, бренд-фон тёмный (зеркало shell `screenshot.rs`).
- Общий рендер-хелпер: один тест на оба компонента (или по тесту на крейт).
### 9.2 E2E (Lima, расширение `run.sh`)
- **Фазы разделены:** `shturman-stage0/1/2.target` достижимы; splash.png **до** frame.png; stage2.ready **после**.
- **Splash:** PNG валиден/непустой.
- **Регресс v0.1/v0.6 не сломан:** все прежние проверки (mount/firstboot/per-unit/шина/fake-ACC/кадр/бюджеты/
персист+reboot) — зелёные на рефакторенных таргетах.
- **Тайминг:** залогирован (функц.).
### 9.3 Критерии приёмки (acceptance)
- [ ] `shturman.target` = зонтик; `shturman-stage0/1/2.target` достижимы и **разделены** (per-target active).
- [ ] Splash-кадр рендерится (`splash.png` непустой) **раньше** первого кадра Shell (`frame.png`).
- [ ] Stage 2 (warmup) стартует **после** первого кадра (деферред наблюдаем).
- [ ] Boot-тайминг логируется (Δ splash/frame, `systemd-analyze`); <10 c — пометка «вердикт на RK3588».
- [ ] Вся приёмка v0.1/v0.6 (§9.4 foundation) — **зелёная** на новой фазовой раскладке (нет регресса).
- [ ] `just ci` зелёный; красные линии целы (нет CAN/actuator).
---
## 10. Двунаправленные швы (синхронизировать при реализации)
- **a-base §4 / architecture §6:** уточнить, что в **dev-VM** Stage-0-splash — systemd-сервис (software-render
PNG), а U-Boot framebuffer-splash — HW; пометить как VM↔HW-границу (как уже сделано для overlay/A-B в v0.6).
- **roadmap §v0.2:** по прохождении — отметить веху ✅; «splash мгновенно» в VM = splash.png до frame.png.
- **CLAUDE.md:** обновить статус (v0.2 готово → следующее v0.3/v0.5).
- **v0.1-v0.6 spec §13:** добавить шов «shturman.target → зонтик; критический набор → stage1.target».
- Если всплывёт: handoff splash→Shell и ранний путь камеры — указатели на v0.5 / домен J.
---
## 11. Дальше по ритму
`v0.2` (эта спека) → **writing-plans** (план реализации: рендер-хелпер → splash → таргеты-рефактор →
warmup → E2E-блок) → **TDD** → реализация → **verify в Lima** → коммит. Не писать код до утверждённой спеки.
Далее по роадмапу: `v0.3` power-safe и `v0.5` shell — параллельно поверх v0.2.