Двунаправленные швы (спека 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>
66 KiB
Спека реализации: 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+ orderingRequires(§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-unitis-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.
- ⚠️ Slint =
- Приватность/безопасность (указатель-шов): по умолчанию нулевой egress/телеметрия (security-privacy §7); at-rest-шифрование и нормативный audit-log — отложены туда (v2–v4); 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-inru.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 до PONRSleep(),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-barriersyncна 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) + эмититAccChangedSetIgnition(s state)TriggerShutdown(u seconds, s reason)→ эмититShutdownImminentAbortShutdown()→ эмититShutdownAborted- Гейтинг прод: прод-сборка идёт
--no-default-features(фичаdev-mocksвыкл.) →PowerMock1не регистрируется; CI имеет job, проверяющий это (§8.3); policyru.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 value—valueкак D-Bus variant; неизвестный ключ →ru.shturman.Error.InvalidArgument.Set(s key, v value)— валидировать ключ, записать (атомарно в/data), эмититьChanged.List(s prefix) → as keysReset(s key)— вернуть дефолт (или удалить), эмититьChanged.
- Сигналы:
Changed(s key, v value). - Стор:
/data/settings/settings.json; запись — durable-write (a-base §3):settings.json.tmp→fsync(file)→rename→fsync(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, не напрямую):- старт →
sdk::connect()→SettingsClient::get("ui.theme")→ применить тему; - подписка
Settings.Changed→ живое обновление темы/единиц; 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; барьеры — дефолт ядра (legacybarrier=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), идемпотентно:
- создать структуру
/data/{apps,settings,state,log}; - сгенерировать 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»); - посеять дефолт-настройки (
ui.theme=auto,ui.units=metric) — формат совместим сSettings-стором; - (плейсхолдер) активировать BSP/vehicle-профиль (в VM — no-op);
- последним шагом — 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-oomd—systemd/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/RateLimitBurst—systemd/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,Settingson-Set); всё прочее → fail. Калибровка (Lima, 2026-06-24): ~80–104 сектора/20 c простоя; порог T = 4096 секторов (~2 МБ/окно), envE2E_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.mount→shturman-firstboot.service→shturman-machineid.service→dbus→shturman-power+shturman-settings→shturman-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.conf—ownна имена сервисов;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 buildshell падает на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; overridefake-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-mockru.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-up→limactl start --name=shturman lima/shturman.yaml(создание+provision);just vm-shell→limactl 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обязателен; манифестныйidru.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/:
- boot/init + оркестрация:
findmnt /dataсмонтирован до сервисов и показывает реально отображаемые non-default опции (напр.errors=remount-ro); overlay-tmpfs присутствует; per-unitsystemctl is-active= active дляshturman-power/-settings/-shellи inactive(dead, Result=success) для firstboot/machineid — не довольствуемсяis-system-running ∈ {running,degraded}(degraded маскирует упавший юнит). - first-boot (A06): маркер есть; повторный запуск
shturman-firstboot— no-op. - шина:
busctl --system listсодержитru.shturman.Powerиru.shturman.Settings(own). - персист через перезапуск (функц., НЕ power-safe):
Settings.Set→ reboot VM → значение сохранилось;machine-idстабилен после reboot (every-boot bind работает). (Настоящий #5 — unit-тест атомарности §9.1; реальный power-cut — v0.3.) - fake-ACC:
PowerMock1.SetAcc(...)→AccChangedнаблюдаем. - первый кадр: Shell → software-renderer → PNG не пустой + (
slint::testing) root+тайлы+часы; тема соответствуетui.theme. - base-бюджеты (функц.): journald
Storage=volatileактивен;zramctlпоказывает одно zram-устройство;fake-hwclock-данные лежат в/data(не в/etc); eMMC-прокси (§7.5) — дельта секторов loop-/dataниже порога за окно простоя, вне allow-list писателей записей нет. - 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-пробел. Структура:
- Добро пожаловать + философия: open-source companion-ОС для авто (RK3588), MIT; язык общения — русский, код-идентификаторы — как есть.
- Красные линии (нерушимы, 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. - Источник правды —
docs/: не противоречить дизайн-докам; при расхождении реальности и дока — синхронизировать док (двунаправленный шов), не «молча обойти». - Лицензионная гигиена (#12): зависимости — MIT/Apache-2.0/BSD/ISC-совместимые; GPL/AGPL —
избегать или изолировать отдельным процессом; LGPL — гранулярно (динамическая/системная линковка
допустима, не blanket-запрет);
cargo deny check— обязателен (CI-гейт). Исключения (напр.slintGPL-3.0, §3) — явные и задокументированные. - Рабочий цикл (фаза реализации): roadmap ведёт; цикл на веху — спека → TDD → реализация → verify в Lima-VM → коммит; код не пишем до утверждённой спеки. Скиллы: TDD, writing/executing-plans, verification-before-completion, systematic-debugging, requesting-code-review.
- Стиль кода:
rustfmt+clippy -D warnings(черезjust lint); Rust везде в проде (Python — только dev/симуляторы, tech-stack). - Тесты: пирамида unit/integration/E2E; «тестируемо без машины» (#13) — каждая фича со своим
симулятором/моком;
just testзелёный до PR. - Коммиты/ветки:
feat/fix/chore(<area>): …; ветка отmain(вmainбез явного «ок» не коммитим); в конце коммита —Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>. - PR-чеклист: прошёл
just ci; не нарушает красные линии; доки синхронизированы; добавлены тесты. - DCO sign-off (обязателен):
git commit -s(Developer Certificate of Origin) — без CLA. ФайлDCO(текст DCO 1.1) — в корне. - Поведение/споры: Code of Conduct (указатель; добавить
CODE_OF_CONDUCT.md— рек. Contributor Covenant). - Где обсуждать: GitHub Issues/Discussions; 🟡-решения — в roadmap «Риск-реестр».
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):
- Контрибьюция — DCO (
git commit -s, Developer Certificate of Origin); CLA не вводим. ФайлDCO(1.1) в корень. - Правообладатель в LICENSE —
K9 Shturman(§10). - Топология шины в dev — системная шина устройства + policy (
systemd/dbus/ru.shturman.conf), зеркало прода (совпадает с рек. ipc §1); decision-журнал ipc.md синхронизируем при реализации (§13). - ФС
/dataв dev-VM — ext4 (барьеры — дефолт ядра,barrier=1не задаём); f2fs (fsync_mode=strict) — на HW (a-base §3; прод-ФС финализируется на прод-образе, 🟡 A02). - Ресурсы Lima-VM — 4 CPU / 6 GiB / 20 GiB (под 16 ГБ-хост; правится локально).
- Место спек —
docs/specs/. - Глубина «первого кадра» — минимум: статус-бар (часы + сеть «unknown») + грид-плейсхолдеры + тема из Settings; полный home/тема/анимации — v0.5.
- UI-тулкит / лицензия Slint — вариант A: отложено к v4. На v0 берём Slint; dev в VM не триггерит
копилефт-обязательств распространения. Royalty-free неприменим (embedded исключён) → бесплатный путь =
GPL-3.0;
deny.toml— exceptionslint = GPL-3.0, pending v4. Makepad (primary) / Iced (fallback) — пермиссивные запасные на случай HW-спайка. Финал (GPL-карв-аут #12 vs миграция vs commercial) — до прод-образа v4. Подробно: §3, §10-примечание.
Отложенное (CLAUDE.md «всплывут по ходу — НЕ блокеры старта»):
A01Armbian/Debian vs Yocto — прод-образ v4 (dev = Lima Ubuntu).A02f2fs vs ext4 — финал на прод-образе (dev = ext4, п.4).B08/B09MCU vs supercap-only — v0.4, вероятно нужна аппаратная проверка.- UI-тулкит/лицензия (Slint GPL vs пермиссивный Makepad/Iced vs commercial) — финал к v4; HW-спайк запасных при случае.
13. Двунаправленные швы (синхронизировать при реализации)
По правилу проекта (реальность ↔ док). Сейчас не правим (ещё спека) — фиксируем точечные правки на момент кода:
ipc.mddecision-журнал: топология шины 🟡 → ✅ «системная шина устройства» (§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=yes→is-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(тело юнитов без изменений, у shellRuntimeDirectory→ tmpfiles). Splash (Stage 0) + warmup (Stage 2) — новые; headless-render плумбинг вынесен вshturman-render. Детали —docs/specs/v0.2-boot-pipeline.md.
14. Дальше по ритму
- ✅ Спека → правки → adversarial-ревью пройдено (17 находок применены в этой v1) → ждём твоё финальное «ок».
- По «ок» — TDD-план (бите-сайз по writing-plans): порядок крейтов
(
common→ipc→sdk→firstboot/settings/power→shell→tools) + dev-харнесс + governance-файлы. - Реализация (TDD) → verify в Lima-VM (§9) → синхронизация швов §13 → коммит
(ветка от
main, после твоего «ок»).