Files
shturman/docs/specs/v0.1-v0.6-foundation.md
kk0t9 b9500356b0 docs(v0.3): синхронизация швов power-safe + статус
Двунаправленные швы (спека v0.3 §10) после реализации Плана 7:
- domain B: banner «Реализация (v0.3)» (B01–B07, VM-модель abort/PONR=stop+umount+remount);
  §12 — MCU/supercap (B08/B09) → v0.4.
- ipc.md §3: Power оживлён из FSM (не mock); Sleep/Wake/RequestSleep зарезервированы.
- foundation §5.2: «Power-стаб» → реальный PowerFsm (проекции state/ignition/source из FSM);
  dev-mock кормит входы FSM.
- CLAUDE.md: статус v0.3 ГОТОВО; «Следующее» → v0.5 shell / v0.4 MCU-thermal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Alexander <akotenev2003@gmail.com>
2026-06-25 00:21:10 +03:00

66 KiB
Raw Permalink Blame History

Спека реализации: v0.1 (Образ-болванка) + v0.6 (dev-харнесс) + шагающий скелет

Тип документа: имплементационная спека вехи (вход в TDD-цикл, не код). Фаза реализации (CLAUDE.md): спека → правки → adversarial-ревью → TDD → verify в VM → коммит. Код, LICENSE, CONTRIBUTING.md, Cargo.toml, крейты — создаются после утверждения спеки. Содержимое LICENSE/CONTRIBUTING.md приведено внутри спеки (§10–§11) для ревью.

Статус: v1 — применены 17 правок adversarial-ревью + позиция Slint (A); ожидает финального «ок» на TDD-план (2026-06-23). Источник правды по дизайну — docs/. Эта спека секвенирует доки в код, не противоречит им; при расхождении — синхронизируем док (двунаправленный шов, см. §13). Связано: roadmap §v0 · architecture · tech-stack · dev-environment · ipc · data-model · performance · plugin-sdk · security-privacy · домены A/B/C/F · principles

Changelog v0→v1 (по adversarial-ревью, 17 находок): every-boot bind machine-id (§7.2/§7.6); автомонтирование /data + ordering Requires (§7.1/§7.6); deny.toml — Slint=GPL-3.0 exception, позиция A (§3/§12); #5 доказывается unit-тестом атомарности, не reboot (§9.1/§9.3); измеримый eMMC-прокси (§7.5/§9.3); grim→software-renderer/ weston-screenshooter (§6); срезы C05/C07/C02 в трассировке (§2.4/§6); шов §6↔C§2 разделён (§2.2); barrier=1 убран (§7.1); zram-generator (§7.4); путь fake-hwclock/data (§7.3); гейт dev-mocks (§5.2/§8.3); factory-reset тест (§9.1); per-unit is-active вместо degraded (§9.3); README мини-спека (§11); LGPL гранулярно (§3/§11); указатель security-privacy (§1/§3); новый §13 «двунаправленные швы».


1. Цель и первый артефакт

Цель спеки: заложить фундамент репозитория и dev-цикла так, чтобы получить первый запускаемый артефакт (CLAUDE.md):

boot в Lima-VM → стаб-сервисы (ru.shturman.Power / ru.shturman.Settings) на D-Bus → первый Slint-кадр, читающий состояние с шины.

Это «шагающий скелет» (tracer bullet): тонкий сквозной поток через все слои архитектуры (Linux base → trusted core → SDK → first-party app), на стабах. Он доказывает, что плоскости (D-Bus control-plane, Slint UI), границы крейтов, dev-харнесс и тест-пирамида работают до того, как мы вложимся в реальную логику power-safe (v0.3), Vehicle-Data (v2) и полный shell (v0.5).

Архитектурный тезис, который скелет валидирует рано: «тонкое ядро + всё на SDK» — first-party Shell общается с ядром (Power/Settings) через тот же shturman-sdk, что и будущие сторонние плагины (architecture §1, principles #9).


2. Скоуп и границы

2.1 В скоупе (делаем сейчас)

Трек Что ID каталога
v0.1 Образ-болванка (в VM) layout RO-rootfs + overlay(tmpfs) + журналируемый /data; first-boot provisioning; локаль A02 A06 A17 (+ A01 как dev-Ubuntu)
v0.6 base day-1 zram/OOM/cgroup-конфиг; journald volatile + критичное в /data; eMMC write-min; время (timesyncd + fake-hwclock); reference-«BSP» = Lima-профиль A09 A10 A11 A07 A16
v0.6 dev-харнесс Lima-VM + provisioning; justfile; CI-гейт; vcan/моки-каркас; shturman-sdk; валидатор манифеста + scaffolding F01 F02 (F03/F04/J06 — частично, см. §2.3)
Шагающий скелет стаб ru.shturman.Power + ru.shturman.Settings на шине; минимальный Slint-кадр на SDK; systemd-таргет shturman.target срезы B04 / Settings-core / C03 C04 C05 C07 C02 / A15
Governance LICENSE (MIT) + CONTRIBUTING.md + DCO + README.md; deny.toml (cargo-deny, #12)

2.2 Явно НЕ в скоупе (отложено, с указателем «куда»)

Скелет намеренно стабит, а не реализует, чтобы не тащить v0.3/v0.5/v2/v4 в первый артефакт:

Отложено Куда Почему не сейчас
Полная машина состояний Power + graceful shutdown sequencing + abort/PONR v0.3 (домен B §2/§4) стаб лишь публикует интерфейс + эмитит сигналы по dev-триггеру
MCU-копилот / supercap, hold-up, thermal-trip, watchdog-арбитраж v0.3/v0.4 (B §5/§6, hardware) требует железа (🟡 B08/B09)
smithay-композитор (мульти-клиент) + слот-поверхности (ru.shturman.shell_slot) v0.5 / с первым surface-апом (C §2, C06) v0-shell = одиночное Slint-приложение (де-рисковка C §2)
App-Host + Perm-Broker (запуск/песочница/прокси) v3 / с первым плагином в v0 нет сторонних плагинов и surface-апов; Shell — обычный systemd-сервис на шине
Vehicle-Data (ru.shturman.VehicleData), CAN/OBD, DTC v2 (домен E) vcan поднимаем в VM, но Vehicle Simulator и сервис — позже
Реальный A/B boot-select (U-Boot env), secure boot, at-rest (fscrypt), OTA (RAUC) v4 (A §4/§5, hardware) в VM нет U-Boot; моделируем layout, не boot-select
Полный home/тайлы/тема день-ночь по design-system, анимации v0.5 (C §3, design-system) сейчас — каркас кадра + нейтральные плейсхолдеры под токены
Connectivity, аудио (PipeWire-роли), Location/GPS v1 пакеты ставим для воспроизводимости, но не задействуем

Два независимых шва v0 (проговариваю явно — это РАЗНЫЕ решения): (1) Композитор — по C §2 v0-shell стартует как одиночное Slint-приложение; полноценный smithay-композитор + слот-поверхности включаем с первым surface-апом (v0.5). Это прямая де-рисковка C §2. (2) App-Host / Perm-Broker — architecture §6 ставит их в Stage 1 до Shell (Shell цепляется через них). C §2 это не покрывает (там только про композитор). Обоснование их отсутствия в v0 — другое: в скелете нет песочных апов/плагинов → нечего брокерить и хостить, поэтому Shell поднимается обычным systemd-сервисом и цепляется к шине напрямую; Broker/App-Host включаются с первым песочным клиентом (плагин — v3). Это осознанное секвенирование. По двунаправленному шву — добавить ремарку в architecture §6 (см. §13).

2.3 Частично в скоупе (каркас сейчас, тело — позже в v0)

Честно фиксируем (principle: no silent caps):

  • F03 Dev-run плагина в VM — зависит от App-Host (нет в v0-скелете). Делаем: justfile-таргет plugin-dev-run + каркас, который корректно сообщает «App-Host появится в v3». Тело — когда App-Host.
  • F04 Тест-харнесс plugin-host — делаем сейчас: библиотека «плохих манифестов» (fixtures/manifests/) как фикстуры для валидатора (F02). Рантайм fault-injection (crash-loop, mem-hog) — позже (нужен App-Host).
  • J06 Dev-симулятор камер — каркас в sim/ + just-плейсхолдер; тело — в домене J (v2). На критпути первого артефакта не нужен (камер нет до v2).
  • vcan + Vehicle Simulator — vcan-модуль и vcan0 поднимаем в provisioning (воспроизводимость базы); сам симулятор (Python, ELM327-emu) — домен E (v2). just sim — плейсхолдер.

2.4 Трассируемость ID → статус в этой спеке

ID Функция Статус сейчас Раздел
A01 base-образ dev = Lima Ubuntu ARM64; прод-образ (Armbian/Debian vs Yocto 🟡) — v4 §7.1, §8
A02 RO-rootfs A/B + overlay(tmpfs) + /data layout в VM (boot-select — v4/HW); ФС dev=ext4 (f2fs 🟡 на HW) §7.1
A06 First-boot provisioning полностью (shturman-firstboot, идемпотентно) §7.2
A07 Время (timesyncd + fake-hwclock) OS-уровень в provisioning; B-owned save-on-shutdown — v0.3 §7.3
A09 Память (zram/OOM/cgroup) конфиг день-1; числа-бюджеты — на железе §7.4
A10 Логи (journald volatile + критичное /data + pstore) полностью (конфиг) §7.5
A11 eMMC write-min дисциплина + измеримая проверка §7.5
A15 systemd-таргеты/оркестрация shturman.target + юниты + ordering §7.6
A16 reference-BSP dev-«BSP» = Lima-профиль; реальный DT/HAL/DBC — HW §8.1
A17 Локаль (ru_RU.UTF-8, tzdata, кириллица, keymap) полностью (provisioning) §7.1
B04 ru.shturman.Power сервис стаб-интерфейс + dev-mock (срез v0.3) §5.2
C03/C04 Shell первый кадр / Slint-shell срез (минимальный кадр) §6
C05 Декларативный рендер тайлов срез (плейсхолдер-тайлы); полный — v0.5 §6
C07 Статус-бар время+сеть-unknown срез (минимум); полный — v0.5 §6
C02 Тема день/ночь срез (по локальному времени); GPS-восход/датчик — v1/later §6
F01 shturman-sdk MVP-крейт (proxy-обёртки + схема манифеста) §4, §8.4
F02 scaffolding + валидатор манифеста полностью §8.4
F03 Dev-run плагина каркас, тело — с App-Host (v3) §2.3, §8.3
F04 Тест-харнесс plugin-host bad-manifest фикстуры; рантайм — позже §2.3, §8.4
J06 Dev-симулятор камер каркас; тело — v2 §2.3
ru.shturman.Settings (core-инфра) стаб + атомарная запись в /data §5.3

3. Красные линии, безопасность, лицензии в v0

  • CAN только на чтение / никогда не safety-critical держатся в v0 тривиально и архитектурно: Vehicle-Data не существует, путей к CAN/actuator нет вообще. В shturman-ipc/shturman-sdk не определяется ни одного write/actuator-метода или capability — «их не существует» (principles #2).
  • Power-safe (#5) с дня 1: даже стаб Settings пишет в /data только durable-write-контрактом (write-temp → fsync → rename → fsync(dir), a-base §3). Доказывается unit-тестом атомарности (§9.1, симуляция сбоя между rename и fsync(dir)), не graceful-reboot (reboot флашит кэш и проходит даже при неатомарной записи — §9.3 шаг 4 переименован в функциональный). Реальный power-cut-тест — v0.3.
  • Лицензионная гигиена (#12): deny.toml + CI-гейт cargo-deny с дня 1 — allow MIT/Apache-2.0/BSD/ ISC/Unicode/Zlib; deny GPL/AGPL (заразный копилефт). LGPL — гранулярно (не blanket-deny: динамическая/ системная линковка допустима), согласовано с principles #12.
    • ⚠️ Slint = GPL-3.0 для embedded. Royalty-free лицензия Slint исключает embedded, а авто-приборка = embedded (Slint сам называет «car dashboard» примером в FAQ). Бесплатный путь = только GPL-3.0 (шипимый UI-бинарь — копилефт). Решение по UI-тулкиту/лицензии — вариант A: отложено к v4 (§12 п.8). На v0 dev в Lima-VM не триггерит копилефт-обязательств распространения (они срабатывают при поставке прод-образа, v4) → в deny.toml явный exception: slint = GPL-3.0, pending v4. Makepad/Iced (MIT) — известные пермиссивные запасные. Двунаправленный шов в tech-stack — §13.
    • GPL-демоны (NM/MM/BlueZ) — отдельные процессы через D-Bus, не линкуются (вне графа cargo-deny), появляются в v1.
  • Приватность/безопасность (указатель-шов): по умолчанию нулевой egress/телеметрия (security-privacy §7); at-rest-шифрование и нормативный audit-log — отложены туда (v2v4); capability-таксономия валидатора (§8.4) — из security-privacy §3. В v0 нет сети/plugin-host/Vehicle-Data → активной работы здесь нет, только шов.
  • Отзывчивость (#11): Slint UI-поток не блокируем; D-Bus-вызовы из Shell — async (zbus+tokio). Перф-вердикт — на RK3588 (performance §2); в VM/CI — функциональные проверки + seed perf-gate.

4. Раскладка Rust-воркспейса

4.1 Дерево

shturman/
├── Cargo.toml                         # [workspace] + [workspace.dependencies] (единые пины)
├── rust-toolchain.toml                # пин тулчейна (stable, профиль)
├── deny.toml                          # cargo-deny: лицензии (#12) + advisories (+ slint GPL-3.0 exception)
├── justfile                           # единые dev-команды (§8.2)
├── LICENSE                            # MIT (§10)
├── CONTRIBUTING.md                    # governance (§11)
├── DCO                                # Developer Certificate of Origin 1.1 (§11 п.10)
├── README.md                          # точка входа репозитория (мини-спека §11 п.13)
├── lima/
│   └── shturman.yaml                  # Lima-шаблон VM (§8.1)
├── systemd/                           # юниты + политика шины + drop-ins (§7)
│   ├── shturman.target
│   ├── data.mount                     # постоянный mount loop-/data (power-safe опции, §7.1/§7.6)
│   ├── shturman-firstboot.service     # A06 (After/Requires data.mount)
│   ├── shturman-machineid.service     # every-boot bind /data/state/machine-id → /etc/machine-id (§7.2/§7.6)
│   ├── shturman-power.service
│   ├── shturman-settings.service
│   ├── shturman-shell.service
│   ├── dbus/ru.shturman.conf          # D-Bus policy (own-имена сервисов)
│   ├── dbus/ru.shturman.dev.conf      # dev-only drop-in: policy для ru.shturman.dev.* (НЕ в прод-образе)
│   ├── journald-shturman.conf         # Storage=volatile + RateLimit (A10)
│   ├── zram-generator.conf            # zram через zram-generator, секция [zram0] (A09)
│   └── oomd-shturman.conf             # systemd-oomd политика (A09)
├── crates/
│   ├── shturman-common/               # lib: tracing/journald init, layout `/data`, монотон. часы, durable-write
│   ├── shturman-ipc/                  # lib: Error-тип, имена/пути/версии, zbus #[proxy] Power1/Settings1
│   ├── shturman-sdk/                  # lib (F01): client-обёртки, схема манифеста (serde), helpers
│   ├── core/
│   │   ├── shturman-firstboot/        # bin (A06): идемпотентный init `/data` + генерация machine-id
│   │   ├── shturman-power/            # bin (B04 стаб): сервер Power1 + dev-mock (feature)
│   │   └── shturman-settings/         # bin (стаб): сервер Settings1 + атомарный стор в `/data`
│   ├── apps/
│   │   └── shturman-shell/            # bin (C03/C04 минимум): Slint-кадр на SDK
│   └── tools/
│       └── shturman-manifest-validator/  # bin (F02): валидатор manifest.yaml против схемы SDK
├── templates/
│   └── plugin/                        # scaffolding для `just new-plugin` (F02)
├── fixtures/
│   └── manifests/                     # F04: библиотека плохих манифестов + один валидный
├── sim/                               # dev-симуляторы (Python): Vehicle Sim (v2), camera mock (v2) — каркас
└── tests/
    └── e2e/                           # E2E-харнесс в VM (boot→шина→кадр) — раннер + ассерты

[workspace] members = ["crates/*", "crates/core/*", "crates/apps/*", "crates/tools/*"].

4.2 Ответственность крейтов и границы

Крейт Тип Ответственность Зависит от
shturman-common lib tracing→journald init; layout-константы /data (/data/{apps,settings,state,log}); монотонные часы (CLOCK_MONOTONIC, B §8); helper атомарной записи (durable-write, a-base §3)
shturman-ipc lib Контракт шины: ru.shturman.Error.* (zbus DBusError); константы well-known имён/путей/версий интерфейсов; #[proxy]-трейты Power1, Settings1; типы-энумы (PowerState, IgnitionState, PowerSource, ShutdownReason) с сериализацией в строки на проводе common
shturman-sdk lib (F01) Публичный API платформы: connect() (бутстрап соединения), ergon-обёртки SettingsClient/PowerClient над proxy из ipc; схема манифеста (manifest::Manifest, serde, plugin-sdk §2); helpers ipc, common
shturman-firstboot bin (A06) Идемпотентный first-boot: структура /data, генерация persistent machine-id в /data/state/, посев дефолт-настроек, durable-write маркера (привязку machine-id делает отдельный every-boot юнит — §7.2) common
shturman-power bin (B04 стаб) Сервер ru.shturman.Power1 (стаб-поведение §5.2) + dev-mock-интерфейс (feature dev-mocks) ipc, common
shturman-settings bin (стаб) Сервер ru.shturman.Settings1 (§5.3) + атомарный стор в /data/settings/ ipc, common
shturman-shell bin (C03/C04) Slint-приложение: первый кадр; читает Settings/Power через shturman-sdk sdk, common, slint
shturman-manifest-validator bin (F02) Валидирует manifest.yaml против shturman_sdk::manifest (схема + правила plugin-sdk §2/§3) sdk

Границы (ключевое):

  • Сервисы ядра (power/settings) зависят от ipc (контракт), НЕ от sdk (клиент). Они реализуют server-side (#[interface]). SDK — клиентская сторона. Это держит границу «ядро публикует ↔ апы потребляют» (architecture §3).
  • Апы (shell) зависят от sdk — реализуют тезис «first-party на том же SDK» (#1, #9).
  • ipc — единственный источник имён/версий/ошибок; и сервер, и клиент берут их отсюда (нет рассинхрона строк имён). Полные XML-сигнатуры фиксируются при реализации (ipc §2).

4.3 Общие зависимости (workspace.dependencies)

tokio (async-рантайм), zbus (D-Bus), serde/serde_yaml/serde_json (манифест/стор), tracing + tracing-subscriber + tracing-journald (логи A10), slint (UI; GPL-3.0, exception в deny.toml — §3/§12), anyhow/thiserror (ошибки), clap (CLI у bin-ов). Точные версии пинятся в Cargo.lock при реализации; лицензии — через cargo-deny.


5. Контракты D-Bus-стабов

Соглашения (ipc §2): имя ru.shturman.<Service>, путь /ru/shturman/<Service>, интерфейс ru.shturman.<Service>1 (N = мажор). Всё async. Ошибки — ru.shturman.Error.<Name>.

5.1 Топология шины (dev)

  • Прод/VM-E2E: одна системная шина устройства (ipc §1, зафиксировано 2026-06-23) + policy-файл systemd/dbus/ru.shturman.conf (право own на ru.shturman.Power/.Settings). Policy для ru.shturman.dev.* (dev-mock) — отдельный dev-only drop-in ru.shturman.dev.conf, не входящий в прод-образ. Зеркалит прод. Decision-журнал ipc.md синхронизируем при реализации (§13).
  • Unit/Integration-тесты: приватная сессионная шина на тест (dbus-run-session / встроенный dbus-daemon) — герметично, параллелизуемо, без root (§9).
  • Изоляция песочных апов — через прокси (появится с App-Host, v3).

5.2 ru.shturman.Power — стаб v0.1 → реальный FSM v0.3 (B03/B04, домен B §9)

  • Имя/путь/интерфейс: ru.shturman.Power · /ru/shturman/Power · ru.shturman.Power1.
  • Методы:
    • GetPowerState() → s — текущее состояние, enum-строка ∈ {off, accessory, running, shutting_down, sleep, battery_cutoff}.
    • RequestSleep() — внутренний; в стабе no-op (полная sleep/wake — v1/v2, B §7).
  • Сигналы:
    • AccChanged(b on)
    • ShutdownImminent(u seconds, s reason)reason ∈ {acc_off, under_voltage, thermal, battery_cutoff}
    • ShutdownAborted() — re-power до PONR
    • Sleep(), Wake()объявлены в интерфейсе (канон ipc §3), но в v0-стабе НЕ эмитятся — зарезервированы до v1/v2 (B §7), как RequestSleep и неиспользуемые Error-варианты (§5.4).
  • Properties (+ PropertiesChanged):
    • IgnitionState: s{off, accessory, running}канон (B §1; E зеркалит, не дублирует)
    • Uptime: t — секунды монотонных часов (CLOCK_MONOTONIC, B §8)
    • PowerSource: s{vehicle_12v, holdup_cap, sleep_rail, low_battery}
  • Поведение: старт в running/IgnitionState=running/PowerSource=vehicle_12v; Uptime растёт монотонно. Никаких методов записи/actuator (#2). v0.1 — плоский стаб; v0.3 — реальный PowerFsm (состояния/переходы B03, graceful shutdown подход A, grace-таймер + durable-barrier sync на commit/PONR); power_state/ignition/source — проекции FSM. Реальный источник событий (ACC/voltage/thermal через MCU) → v0.4; в v0.3 события кормит dev-mock.
  • Dev-mock (feature dev-mocks): доп.интерфейс ru.shturman.dev.PowerMock1 на том же объекте — «fake-ACC/voltage/thermal», кормит входы FSM (v0.3) для тестов/E2E:
    • SetAcc(b on)Event::AccOn/AccOff (FSM) + эмитит AccChanged
    • SetIgnition(s state)
    • TriggerShutdown(u seconds, s reason) → эмитит ShutdownImminent
    • AbortShutdown() → эмитит ShutdownAborted
    • Гейтинг прод: прод-сборка идёт --no-default-features (фича dev-mocks выкл.) → PowerMock1 не регистрируется; CI имеет job, проверяющий это (§8.3); policy ru.shturman.dev.* — dev-only drop-in (§5.1), не в прод-образе. (Прод-сборки в v0 нет — A01 v4; методы — мок-мутации, не actuator, #2 не нарушают; гейт заведён заранее, чтобы фича не протекла.)
  • Идентичность/жизненный цикл (ipc §2): идентичность вызывающего — по соединению (sender), не из аргумента; в v0 (нет песочниц) — без enforcement, зарезервировано. Серверное клиент-состояние (подписки) снимается по NameOwnerChanged — даёт zbus (тест — §9.2).

5.3 ru.shturman.Settings — стаб (core-инфра)

  • Имя/путь/интерфейс: ru.shturman.Settings · /ru/shturman/Settings · ru.shturman.Settings1.
  • Методы:
    • Get(s key) → v valuevalue как D-Bus variant; неизвестный ключ → ru.shturman.Error.InvalidArgument.
    • Set(s key, v value) — валидировать ключ, записать (атомарно в /data), эмитить Changed.
    • List(s prefix) → as keys
    • Reset(s key) — вернуть дефолт (или удалить), эмитить Changed.
  • Сигналы: Changed(s key, v value).
  • Стор: /data/settings/settings.json; запись — durable-write (a-base §3): settings.json.tmpfsync(file)renamefsync(dir). Загрузка на старте; дефолты сеются first-boot (§7.2). Settings — единственный писатель этого поддерева (architecture §3).
  • Дефолты v0 (seed): ui.theme = "auto" (∈ {auto, day, night}), ui.units = "metric" (канон единиц — data-model §2; конвертация на презентации).
  • Namespace-изоляция по sender→app-id (ipc §3) — отложена (нет песочных апов в v0); стаб = плоские ключи. Указатель: enforce появится с App-Host/прокси (v3).

5.4 ru.shturman.Error (контракт ошибок, ipc §2)

Enum в shturman-ipc (zbus DBusError): PermissionDenied, NotAvailable, Stale, Timeout, ReadOnly, InvalidArgument, Unsupported. В v0 реально используются InvalidArgument (Settings) и зарезервированы остальные (полная семантика — по мере сервисов).


6. Shell — первый Slint-кадр (срезы C03/C04/C05/C07/C02)

Скоуп: минимальный первый кадр, доказывающий сквозной поток, не полный v0.5-shell. Каталожные срезы (минимум каждого; полные версии — v0.5): C03 (первый кадр), C04 (Slint-shell), C05 (декларативный рендер тайлов — плейсхолдеры), C07 (статус-бар время + сеть-«unknown»), C02 (тема день/ночь по локальному времени).

  • UI (Slint): окно со статус-баром (часы из системного времени; индикатор сети — «unknown», C §3 деградированный контракт) + home-грид из нескольких крупных тайлов-плейсхолдеров (декларативно) + (dev) индикатор IgnitionState.
  • Поток данных (через shturman-sdk, не напрямую):
    1. старт → sdk::connect()SettingsClient::get("ui.theme") → применить тему;
    2. подписка Settings.Changed → живое обновление темы/единиц;
    3. PowerClient::power_state() + подписка AccChanged/PropertiesChanged(IgnitionState) → отразить.
  • Тема день/ночь: v0 — по локальному времени (RTC/NTP/fake-hwclock, C §6); GPS-восход — v1; до синка времени — пометка неопределённости (C §3). Визуальные токены design-system — каркасом (нейтральные плейсхолдеры); полный визуальный язык — гейт v0.5.
  • Рендер-бэкенды:
    • dev интерактивно: Slint под weston (вложенный Wayland) в VM, либо нативно на macOS-хосте.
    • CI/E2E без дисплея (автоматический ассерт кадра): (a) Slint software-renderer → RGBA/PNG-буфер (без дисплей-сервера и композитора) → ассерт «кадр не пустой» + (b) slint::testing — дерево элементов (root + тайлы + часы) и дата-байндинг темы из Settings.
    • ⚠️ grim НЕ годится (требует wlr-screencopy, которого у weston нет). Если нужен скриншот именно под weston — weston-screenshooter; но основной автотест кадра — software-renderer, композитор не нужен.
  • Границы: Shell не реализует логику апов, не трогает CAN/safety (C §1). Композитор (smithay), слот-поверхности, App-Host-хостинг — не здесь (v0.5/v3).

7. База v0.1 + v0.6 (day-1)

7.1 Образ/layout в VM (A01/A02/A17)

  • dev-база = Lima Ubuntu ARM64 (нативно к RK3588; A01-прод 🟡 Armbian/Debian vs Yocto — v4).
  • Layout /data + overlay (моделируем, не boot-select):
    • /dataотдельный журналируемый носитель: loopback-образ + постоянный mount (data.mount/ fstab, см. §7.6), монтируется на каждом boot до сервисов с power-safe-опциями. ФС dev = ext4; барьеры — дефолт ядра (legacy barrier=1 НЕ задаём — он не отображается в findmnt), ассертим реально отображаемые non-default опции (errors=remount-ro, явный commit=N). Главная power-safe-гарантия — durable-write (§5.3/§9.1), не mount-опция. f2fs (fsync_mode=strict) — на HW (a-base §3, 🟡 A02; формулировку a-base §3 про barrier=1 синхронизируем — §13).
    • overlay upper/work — на tmpfs (volatile) (a-base §3); персист — только в /data.
    • RO-rootfs — дисциплина (rootfs трактуем read-only; запись только в /data/tmpfs).
    • A/B boot-select (U-Boot env) — НЕ в VM (нет U-Boot): layout документируем, переключение слотов — на HW/v4. Честная граница VM↔HW (a-base §2 — два разных «образа»).
  • Локаль (A17): ru_RU.UTF-8 (LANG/LC_*), tzdata, кириллические шрифты до Shell (консоль/splash), console keymap — в provisioning.

7.2 First-boot provisioning (A06) — shturman-firstboot

Oneshot-бинарь под shturman-firstboot.service (ConditionPathExists=!/data/.shturman-provisioned, After=/Requires=data.mount), идемпотентно:

  1. создать структуру /data/{apps,settings,state,log};
  2. сгенерировать persistent machine-id в /data/state/machine-id (one-shot; гейт маркером ок). Привязку к /etc/machine-id делает отдельный every-boot юнит shturman-machineid.service (§7.6) — bind волатилен (overlay tmpfs / RO-rootfs) и не переживает ребут, а firstboot после маркера скипается (a-base §11: «генерим один раз, биндим каждый boot»);
  3. посеять дефолт-настройки (ui.theme=auto, ui.units=metric) — формат совместим с Settings-стором;
  4. (плейсхолдер) активировать BSP/vehicle-профиль (в VM — no-op);
  5. последним шагом — durable-write маркера /data/.shturman-provisioned (частичный сбой mid-run → повтор на next boot довосстанавливает, т.к. маркер пишется атомарно в самом конце). Идемпотентность, factory-reset (маркер удалён + /data очищен → пересоздание), прерывание mid-run — тесты §9.1.

7.3 Время (A07)

  • systemd-timesyncd (NTP) + fake-hwclock с override пути на /data (FILE=/data/state/fake-hwclock.data через /etc/default/fake-hwclock или drop-in) — стоковый путь /etc/fake-hwclock.data бьёт по RO-rootfs и нарушает «персист только в /data» (A11). Персист last-known-time (период + on-shutdown) — OS-уровень в provisioning. GPS-источник UTC — v1 (домен K).
  • Дисциплина монотонности (B §8): все lifecycle-таймеры/Uptime — на CLOCK_MONOTONIC (helper в shturman-common), не wall-clock.
  • B-owned save-on-shutdown (периодика + on-sync + on-shutdown, B §8) — интегрируется в v0.3 (graceful shutdown). В v0.6 — базовый OS-fake-hwclock.

7.4 Память (A09)

  • zram (lz4/zstd) через zram-generator/etc/systemd/zram-generator.conf (секция [zram0]); только один механизм (НЕ zram-tools — конфликт за /dev/zram0). Swap-на-flash запрещён (a-base §8).
  • systemd-oomdsystemd/oomd-shturman.conf: защищаем Stage-1 critical set (в v0: power/settings/ shell); первые жертвы — фон (в v0 их нет — задел иерархии, performance §5/§3).
  • cgroup-лимиты (MemoryMax/MemoryHigh) — drop-ins юнитов (числа 🟡, профиль на железе).

7.5 Логи (A10) и eMMC write-min (A11)

  • journald Storage=volatile (журнал в RAM/tmpfs, не на flash) + RuntimeMaxUse + RateLimitIntervalSec/ RateLimitBurstsystemd/journald-shturman.conf. Rust-tracing → journald (tracing-journald), та же политика.
  • Критичные события переживают power-cut: size-capped append с fsync в /data/log/ (kernel panic, итог ACC-off, причина recovery) + pstore/ramoops — каркас в shturman-common (тело крит-логов — по мере событий).
  • eMMC write-min (A11): дисциплина (volatile-логи, tmpfs-транзиент, zram, без спама в /data). Измеримая проверка (детерминированный VM-прокси): дельта записанных секторов на loop-устройстве /data (/proc/diskstats, поле 10) за фиксированное окно простоя (E2E: E2E_IDLE_SECS=20 c после boot-settle) ниже порога T + нет периодических флашей вне allow-list писателей (fake-hwclock, Settings on-Set); всё прочее → fail. Калибровка (Lima, 2026-06-24): ~80104 сектора/20 c простоя; порог T = 4096 секторов (~2 МБ/окно), env E2E_EMMC_MAX_SECTORS. Абсолютный байт-бюджет — вердикт на RK3588 (performance §2).

7.6 systemd-оркестрация (A15)

  • data.mount — постоянный mount loop-/data с power-safe-опциями; зависимые юниты несут RequiresMountsFor=/data.
  • shturman-machineid.service — every-boot oneshot: bind /data/state/machine-id/etc/machine-id (After=data.mount, Before=dbus.service/systemd-machine-id-commit) — стабильный machine-id до старта шины (нужен системной шине, §5.1).
  • shturman.target — v0 critical set (Stage-1 срез), порядок: data.mountshturman-firstboot.serviceshturman-machineid.servicedbusshturman-power + shturman-settingsshturman-shell.
  • Requires= + After=shturman-firstboot.service у power/settings/shell — при провале firstboot сервисы не стартуют (не работают против полу-провиженного /data; Wants= лишь упорядочивает — недостаточно).
  • Юниты: Restart=on-failure (изоляция #4); RuntimeWatchdogSec — задел (полный watchdog — v0.3, B §6).
  • D-Bus policy ru.shturman.confown на имена сервисов; ru.shturman.dev.*dev-only drop-in (§5.1).

8. Dev-харнесс (v0.6)

8.1 Lima-VM (lima/shturman.yaml) и как поднимаем

  • Параметры: vmType: vz (Apple Virtualization, нативно ARM64); images: Ubuntu ARM64 LTS; ресурсы (зафиксировано): cpus: 4, memory: 6GiB, disk: 20GiB (под 16 ГБ хоста, dev-environment «VM лёгкая»; правится локально); mounts: репозиторий writable (правим на хосте — собираем в VM).
  • provision (system): установить пакеты (systemd, dbus, pipewire + WirePlumber [задел v1], weston + weston-screenshooter, can-utils, rustup/toolchain, python3+venv, systemd-zram-generator, fake-hwclock, кириллические шрифты + build-deps Slint/winit на Linux: libfontconfig1-dev/libxkbcommon-dev/ libwayland-dev — иначе cargo build shell падает на yeslogic-fontconfig-sys); linux-modules-extra-$(uname -r) (модули zram/vcan НЕ входят в базовый vz-образ Lima); modprobe vcan + ip link add vcan0 (vcan может отсутствовать в vz-ядре — VM↔HW-граница, как раньше); создать loopback-/data (ext4 + power-safe-опции) и завести постоянный data.mount/fstab + tmpfs-overlay; override fake-hwclock пути на /data + удалить стоковый /etc/fake-hwclock.data (A11; сервис в Lima masked — Lima сам синхронит время, на HW юнит размаскирован и читает FILE через EnvironmentFile); разложить systemd/-юниты + journald/zram-generator/oomd drop-ins + dbus policy (прод ru.shturman.conf; dev-mock ru.shturman.dev.PowerMock1 — интерфейс на объекте ru.shturman.Power, отдельное dev-имя/policy НЕ нужны, покрыт send_destination); включить shturman.target. (screenshot кадра в E2E — через Slint software-renderer → PNG, без weston/grim; shell.service в v0.6 = oneshot-screenshot, живой weston-shell — v0.5; см. §6.)
  • reference-«BSP» (A16): в dev это Lima-профиль (дев-таргет). Реальный reference-BSP (DT overlay + HAL + DBC) — на HW (a-base §13), вне VM.
  • Подъём: just vm-uplimactl start --name=shturman lima/shturman.yaml (создание+provision); just vm-shelllimactl shell shturman; just vm-reset (сброс «ничего не сломать», dev-environment).

8.2 justfile — единые команды

Цель Делает
vm-up / vm-down / vm-reset / vm-shell жизненный цикл Lima-VM
build cargo build --workspace (в VM/нативно на Linux/CI)
test cargo test --workspace (unit + integration, §9)
lint cargo fmt --check + cargo clippy --workspace -- -D warnings
deny cargo deny check (лицензии #12 + advisories)
up / down старт/стоп сервисов Штурмана на шине (dev)
run поднять shturman.target локально (Power+Settings+Shell) для ручной проверки
shell-frame запустить Shell один раз, отрендерить кадр (software-renderer) → PNG для инспекции
e2e сквозной VM-тест приёмки (§9.3): boot → имена на шине → fake-ACC → кадр
new-plugin <name> scaffolding плагина из templates/plugin/ (F02)
validate-manifest <path> запустить shturman-manifest-validator (F02)
plugin-dev-run <path> каркас (F03): сообщает «нужен App-Host (v3)»
sim плейсхолдер Vehicle Simulator (v2, домен E)
ci локальный прогон гейта: lint + test + deny

8.3 CI (локальный гейт; авто-CI — позже)

⚠️ .github/workflows/ci.yml удалён (2026-06-24): self-hosted Gitea триггерится на GitHub-Actions-формат, что не нужно. Активный гейт — локальный just ci. Авто-CI на Gitea (Gitea Actions/runner) — решение позже. Описание ниже — задел на тот момент.

  • (задел) jobs lint (fmt+clippy), build, test (unit+integration — раннер уже Linux, шину/сервисы/headless-рендер гоним напрямую, без Lima), license (cargo-deny), prod-build-gate (cargo build --workspace --no-default-features + ассерт, что PowerMock1 не экспортируется без фичи dev-mocks — §5.2). Позже — integration (vcan+симулятор, v2) и build образа (v4).
  • Фолбэк (dev-environment): если ARM64-раннеры вне бесплатного тира — x86 + кросс / self-hosted.
  • seed perf-gate (performance §9 ◻️): замер time-to-first-frame в VM/раннере — функционально (smoke), с явной пометкой «не перф-вердикт» (вердикт — RK3588, performance §2). Состав авто-гейта (boot/кадр/ввод/голос) расширяем по фазам.

8.4 SDK (F01) + dev-tools (F02) + харнесс (F04)

  • shturman-sdk (F01): connect(); SettingsClient/PowerClient (обёртки proxy из ipc); manifest::Manifest (serde-схема YAML, plugin-sdk §2) — single source схемы для валидатора и будущего рантайма. semver.
  • shturman-manifest-validator (F02): schema-валидация + правила (capabilities из таксономии; len(tiles) ≤ ui_tiles; vehicle_read только из каталога data-model; ru обязателен; манифестный id ru.shturman.* закрыт для сторонних — F §3). Коды ошибок — человекочитаемые.
  • scaffolding (F02): templates/plugin/ (валидный manifest.yaml + минимальный бинарь-каркас на shturman-sdk + locales/ru.yaml); just new-plugin foo генерит плагин, проходящий валидатор.
  • F04 (частично): fixtures/manifests/ — библиотека плохих манифестов (коллизия id, неподдерж. shturman_api, tiles>ui_tiles, capability вне таксономии, vehicle_read вне каталога, попытка ru.shturman.*) + один валидный → фикстуры тестов валидатора. Рантайм-fault-injection (crash-loop/mem-hog) — позже (нужен App-Host).

9. План тестирования и приёмка

Пирамида (dev-environment): unit → integration → E2E → HW-in-the-loop. HW (RK3588) — отложено (нет железа); перф-вердикт там (performance §2).

9.1 Unit (хост/VM/CI, по крейтам)

  • shturman-ipc: сериализация enum↔строка (PowerState/IgnitionState/PowerSource/ShutdownReason); маппинг Error ↔ D-Bus-имя.
  • shturman-common: тест power-safe #5 — атомарная запись (temp→fsync→rename→fsync(dir)) + симуляция сбоя между rename и fsync(dir) / прерывания: читается прошлая ИЛИ новая версия, нет частично записанного/битого файла (это и есть доказательство #5, не reboot); монотонные часы.
  • shturman-settings: load/seed/round-trip Set→Get; Reset к дефолту; неизвестный ключ → InvalidArgument; персист переживает перезапуск процесса (тест на tmp-/data).
  • shturman-firstboot: идемпотентность — (а) маркер на месте → no-op; (б) factory-reset: маркер удалён + /data очищен → пересоздаёт /data/{apps,settings,state,log}, повторно сеет дефолты, ставит маркер, machine-id перегенерён; (в) прерывание mid-run (kill до маркера) → повторный прогон довосстанавливает.
  • shturman-sdk: парс манифеста (валидный/битый).
  • shturman-manifest-validator: каждый bad-фикстур → ожидаемый код; good → pass.
  • shturman-shell: slint::testing — дерево элементов строится, тема байндится из мок-Settings.
  • Команда: just test (или cargo test -p <crate>).

9.2 Integration (сервисы на приватной шине)

  • Харнесс поднимает dbus-run-session → стартует shturman-settings + shturman-power → тест-клиент через shturman-sdk:
    • Settings.Set("ui.theme","night")Get = night; приходит Changed;
    • Power.GetPowerState() = running; IgnitionState читается;
    • dev-mocks: PowerMock1.SetAcc(false) → подписчик получил AccChanged(false);
    • снятие клиент-состояния: подписчик исчезает → серверная подписка снимается по NameOwnerChanged (ipc §2).
  • Команда: just test (integration-тесты в crates/*/tests/, фича dev-mocks).

9.3 E2E (полный поток в Lima-VM) — just e2e

В VM: shturman.target поднят → раннер tests/e2e/:

  1. boot/init + оркестрация: findmnt /data смонтирован до сервисов и показывает реально отображаемые non-default опции (напр. errors=remount-ro); overlay-tmpfs присутствует; per-unit systemctl is-active = active для shturman-power/-settings/-shell и inactive(dead, Result=success) для firstboot/machineid — не довольствуемся is-system-running ∈ {running,degraded} (degraded маскирует упавший юнит).
  2. first-boot (A06): маркер есть; повторный запуск shturman-firstboot — no-op.
  3. шина: busctl --system list содержит ru.shturman.Power и ru.shturman.Settings (own).
  4. персист через перезапуск (функц., НЕ power-safe): Settings.Setreboot VM → значение сохранилось; machine-id стабилен после reboot (every-boot bind работает). (Настоящий #5 — unit-тест атомарности §9.1; реальный power-cut — v0.3.)
  5. fake-ACC: PowerMock1.SetAcc(...)AccChanged наблюдаем.
  6. первый кадр: Shell → software-renderer → PNG не пустой + (slint::testing) root+тайлы+часы; тема соответствует ui.theme.
  7. base-бюджеты (функц.): journald Storage=volatile активен; zramctl показывает одно zram-устройство; fake-hwclock-данные лежат в /data (не в /etc); eMMC-прокси (§7.5) — дельта секторов loop-/data ниже порога за окно простоя, вне allow-list писателей записей нет.
  8. seed perf: замер time-to-first-frame (логируется, не блокирует — не вердикт).

9.4 Критерии приёмки (acceptance)

v0.1 (roadmap: «init поднялся; RO-rootfs A/B + overlay + /data ок»):

  • VM грузится; per-unit critical set active; /data смонтирован до сервисов с реальными power-safe-опциями; overlay(tmpfs) есть.
  • First-boot создал структуру /data идемпотентно; machine-id стабилен после reboot (every-boot bind).
  • RO-rootfs-дисциплина соблюдена (запись только в /data/tmpfs). (A/B boot-select — HW/v4.)

v0.6 (roadmap: «dev-итерация без железа работает; память/лог/eMMC в бюджете»):

  • just vm-up && just build && just test && just lint && just deny — зелено без железа.
  • journald Storage=volatile; одно zram-устройство; oomd-политика загружена; eMMC-прокси в пороге (§7.5).
  • shturman-sdk собирается; just validate-manifest корректно принимает good / отвергает bad; just new-plugin foo генерит плагин, проходящий валидатор.
  • Governance: LICENSE, CONTRIBUTING.md, DCO, README.md (мини-спека §11) присутствуют; prod-build-gate (§8.3) зелёный.

Шагающий скелет (CLAUDE.md):

  • ru.shturman.Power + ru.shturman.Settings владеют именами на шине.
  • Settings round-trip + персист через reboot; fake-ACC AccChanged наблюдаем.
  • Первый Slint-кадр рендерится (PNG не пустой), отражает ui.theme из Settings и состояние Power.

10. LICENSE (MIT) — содержимое файла LICENSE

MIT License

Copyright (c) 2026 K9 Shturman

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Примечание (лицензионный слой UI): наш код — MIT. Но slint для embedded доступен бесплатно только под GPL-3.0, поэтому шипимый UI-бинарь (Shell и будущие first-party-апы, линкующие Slint) при распространении прод-образа (v4) будет под GPL-3.0. Это отложенное решение (вариант A, §12 п.8); до v4 dev-использование обязательств не создаёт. MIT GPL-совместима (односторонне). Если копилеф­т неудобен — миграция на Makepad/Iced (MIT) или commercial-лицензия Slint.


11. CONTRIBUTING.md — предлагаемое содержимое

Закрывает governance-пробел. Структура:

  1. Добро пожаловать + философия: open-source companion-ОС для авто (RK3588), MIT; язык общения — русский, код-идентификаторы — как есть.
  2. Красные линии (нерушимы, principles #1#2): PR, добавляющий CAN-write/actuator/Mode-04/UDS-write или safety-critical-интеграцию (двигатель/тормоза/ABS/ESP/руль/подушки), отклоняется — допустимы только OBD-read (Mode 01/03/07/09/0A). Граница — docs/contracts/safety.md.
  3. Источник правды — docs/: не противоречить дизайн-докам; при расхождении реальности и дока — синхронизировать док (двунаправленный шов), не «молча обойти».
  4. Лицензионная гигиена (#12): зависимости — MIT/Apache-2.0/BSD/ISC-совместимые; GPL/AGPL — избегать или изолировать отдельным процессом; LGPL — гранулярно (динамическая/системная линковка допустима, не blanket-запрет); cargo deny check — обязателен (CI-гейт). Исключения (напр. slint GPL-3.0, §3) — явные и задокументированные.
  5. Рабочий цикл (фаза реализации): roadmap ведёт; цикл на веху — спека → TDD → реализация → verify в Lima-VM → коммит; код не пишем до утверждённой спеки. Скиллы: TDD, writing/executing-plans, verification-before-completion, systematic-debugging, requesting-code-review.
  6. Стиль кода: rustfmt + clippy -D warnings (через just lint); Rust везде в проде (Python — только dev/симуляторы, tech-stack).
  7. Тесты: пирамида unit/integration/E2E; «тестируемо без машины» (#13) — каждая фича со своим симулятором/моком; just test зелёный до PR.
  8. Коммиты/ветки: feat/fix/chore(<area>): …; ветка от mainmain без явного «ок» не коммитим); в конце коммита — Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>.
  9. PR-чеклист: прошёл just ci; не нарушает красные линии; доки синхронизированы; добавлены тесты.
  10. DCO sign-off (обязателен): git commit -s (Developer Certificate of Origin) — без CLA. Файл DCO (текст DCO 1.1) — в корне.
  11. Поведение/споры: Code of Conduct (указатель; добавить CODE_OF_CONDUCT.md — рек. Contributor Covenant).
  12. Где обсуждать: GitHub Issues/Discussions; 🟡-решения — в roadmap «Риск-реестр».
  13. README.md (мини-спека, корень репо): название + абзац-описание; красные линии (CAN read-only / не safety-critical); ссылки на CLAUDE.md и docs/; быстрый старт (just vm-up / just run); лицензия MIT + DCO + примечание о GPL-слое UI (§10). Галочка приёмки — §9.4.

12. Решения (зафиксированы) и отложенное (не блокеры)

Зафиксировано (ревью 2026-06-23):

  1. Контрибьюция — DCO (git commit -s, Developer Certificate of Origin); CLA не вводим. Файл DCO (1.1) в корень.
  2. Правообладатель в LICENSE — K9 Shturman (§10).
  3. Топология шины в dev — системная шина устройства + policy (systemd/dbus/ru.shturman.conf), зеркало прода (совпадает с рек. ipc §1); decision-журнал ipc.md синхронизируем при реализации (§13).
  4. ФС /data в dev-VM — ext4 (барьеры — дефолт ядра, barrier=1 не задаём); f2fs (fsync_mode=strict) — на HW (a-base §3; прод-ФС финализируется на прод-образе, 🟡 A02).
  5. Ресурсы Lima-VM — 4 CPU / 6 GiB / 20 GiB (под 16 ГБ-хост; правится локально).
  6. Место спек — docs/specs/.
  7. Глубина «первого кадра» — минимум: статус-бар (часы + сеть «unknown») + грид-плейсхолдеры + тема из Settings; полный home/тема/анимации — v0.5.
  8. UI-тулкит / лицензия Slint — вариант A: отложено к v4. На v0 берём Slint; dev в VM не триггерит копилеф­т-обязательств распространения. Royalty-free неприменим (embedded исключён) → бесплатный путь = GPL-3.0; deny.toml — exception slint = GPL-3.0, pending v4. Makepad (primary) / Iced (fallback) — пермиссивные запасные на случай HW-спайка. Финал (GPL-карв-аут #12 vs миграция vs commercial) — до прод-образа v4. Подробно: §3, §10-примечание.

Отложенное (CLAUDE.md «всплывут по ходу — НЕ блокеры старта»):

  • A01 Armbian/Debian vs Yocto — прод-образ v4 (dev = Lima Ubuntu).
  • A02 f2fs vs ext4 — финал на прод-образе (dev = ext4, п.4).
  • B08/B09 MCU vs supercap-only — v0.4, вероятно нужна аппаратная проверка.
  • UI-тулкит/лицензия (Slint GPL vs пермиссивный Makepad/Iced vs commercial) — финал к v4; HW-спайк запасных при случае.

13. Двунаправленные швы (синхронизировать при реализации)

По правилу проекта (реальность ↔ док). Сейчас не правим (ещё спека) — фиксируем точечные правки на момент кода:

  • ipc.md decision-журнал: топология шины 🟡 «системная шина устройства» (§5.1).
  • a-base §3: barrier=1 — дефолт ядра (legacy-алиас, не отображается); привести формулировку к «барьеры по умолчанию; durable-write — главная гарантия power-safe» (§7.1).
  • architecture §6: ремарка «Stage-1-минимум для v0-скелета: Perm-Broker/App-Host включаются с первым песочным клиентом (плагин/surface-ап), Shell в v0 — обычный сервис на шине» (§2.2).
  • tech-stack.md: лицензия Slint — GPL-3.0 для embedded (royalty-free неприменим); решение по тулкиту отложено к v4 (§12 п.8); Makepad/Iced — пермиссивные запасные.
  • principles #12: уточнить LGPL — гранулярно (динамическая/системная линковка допустима), а не blanket; согласовать с deny.toml/§3.

Реализация (План 5 ч.2 — v0.6 Lima E2E, 2026-06-24, проверено в Lima):

  • shell.service (§6/§7.6): v0.6 = oneshot software-render → PNG (shturman-shell --screenshot /run/shturman/frame.png, tmpfs/volatile; RemainAfterExit=yesis-active=active детерминированно, без хрупкого weston). Живой weston-shell (ui.run()) — v0.5. shturman-shell стал lib+bin (рендер тестируем headless и на dev-Mac). Доки §6/§7.6 уже называют software-renderer основным — синхронизировано.
  • Провижининг Lima (§8.1): добавлены build-deps libfontconfig1-dev/libxkbcommon-dev/libwayland-dev (Slint/winit на Linux тянут fontconfig — на macOS CoreText, поэтому всплыло только в VM) + linux-modules-extra-$(uname -r) (модули zram/vcan не в базовом vz-ядре). vcan-модуль всё равно может отсутствовать — zram ставится, vcan — best-effort (Vehicle Sim v2).
  • fake-hwclock (§7.3): сервис в Lima masked (Lima синхронит время с хоста) → override через EnvironmentFile не срабатывает в VM; скрипт читает FILE из env, E2E демонстрирует запись в /data напрямую. Стоковый /etc/fake-hwclock.data удаляется в провижининге (A11). На HW юнит размаскирован — механизм тот же.
  • eMMC-порог T (§7.5): калибровка ~80–104 сект/20 c → T=4096 сект; окно простоя 20 c (не 60).
  • CARGO_TARGET_DIR (§8.2): E2E/сборка в VM пишут в VM-локальный target (~/.cache/shturman/target), не в смонтированный target/ — иначе конфликт Darwin↔Linux-артефактов + медленный virtiofs.
  • dev-mock policy (§5.1): отдельный ru.shturman.dev.conf для PowerMock1 не нужен — это интерфейс на объекте ru.shturman.Power (имя то же), покрыт send_destination=ru.shturman.Power. Файл зарезервирован на случай отдельного dev-имени на шине.
  • E2E reboot (§9.3.4): двухфазно just e2e (pre → guest-reboot Lima → post); персист Settings + machine-id every-boot bind проверяются после реального ребута. just run = только pre (без ребута).
  • v0.2 (boot-конвейер): shturman.target стал зонтиком фаз; критический набор v0.1 переехал в shturman-stage1.target (тело юнитов без изменений, у shell RuntimeDirectory → tmpfiles). Splash (Stage 0) + warmup (Stage 2) — новые; headless-render плумбинг вынесен в shturman-render. Детали — docs/specs/v0.2-boot-pipeline.md.

14. Дальше по ритму

  1. Спека → правки → adversarial-ревью пройдено (17 находок применены в этой v1) → ждём твоё финальное «ок».
  2. По «ок» — TDD-план (бите-сайз по writing-plans): порядок крейтов (commonipcsdkfirstboot/settings/powershelltools) + dev-харнесс + governance-файлы.
  3. Реализация (TDD)verify в Lima-VM (§9) → синхронизация швов §13 → коммит (ветка от main, после твоего «ок»).