fix(v0.3): E2E power-safe-блок по реальным ошибкам Lima

- machineid bind (/data/state/machine-id→/etc/machine-id) держит /data busy →
  снимаем bind перед umount; remount через systemctl start (не mount /data — нет fstab).
- restart power/settings после install (повторный just run: иначе крутится старый
  бинарь, start=no-op) + restart power в начале power-safe-блока (чистый FSM Running).
- §3 oneshot-чек: firstboot/machineid валидны в active|inactive (отработал ИЛИ корректно
  пропущен Condition'ом на повторном boot); реальный сбой = failed.
- stage2.target тянет savetime.timer (WantedBy без enable не срабатывал).
Блок power-safe зелёный: N=3 цикла /data цел, abort→ShutdownAborted, power-cut fsck-clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Alexander <akotenev2003@gmail.com>
This commit is contained in:
2026-06-24 23:33:36 +03:00
parent 92a11c3c72
commit 93382d2de6
2 changed files with 28 additions and 15 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
[Unit]
Description=Штурман Stage 2 — фон (после интерактива)
After=shturman-stage1.target
Wants=shturman-stage2-warmup.service
Wants=shturman-stage2-warmup.service shturman-savetime.timer
+27 -14
View File
@@ -108,8 +108,10 @@ pass "юниты/политики разложены"
info "старт shturman.target (зонтик → Stage 0/1/2)"
sudo systemctl start shturman.target || fail "shturman.target не стартовал"
# Фазовый старт рендерит splash → frame → warmup по порядку (Before/After). Restart shell НЕ делаем:
# иначе frame.png стал бы новее stage2.ready и сломал ассерт «warmup после кадра» (порядок фаз).
# Перезапустить демоны, чтобы подхватить свежесобранные бинари (на повторном just run — иначе крутится
# старый бинарь, start=no-op; на чистом vm-reset сервисы и так стартуют с новым). shell НЕ трогаем:
# иначе frame.png стал бы новее stage2.ready и сломал бы ассерт «warmup после кадра» (порядок фаз).
sudo systemctl restart shturman-power.service shturman-settings.service
for _ in $(seq 1 15); do systemctl is-active --quiet shturman-shell && break; sleep 1; done
# ---- 1. /data до сервисов + реальные power-safe опции (§9.3.1) ----
@@ -138,8 +140,12 @@ for u in shturman-power shturman-settings shturman-shell; do
done
for u in shturman-firstboot shturman-machineid; do
state=$(systemctl is-active "$u" 2>&1)
[ "$state" = active ] || fail "$u не active(exited): $state"
pass "$u: $state (oneshot)"
# oneshot валиден в active (отработал, RemainAfterExit) ИЛИ inactive (корректно пропущен Condition'ом
# на повторном boot: firstboot — marker есть; reflects clean+re-run). Реальный сбой = failed/activating.
case "$state" in
active | inactive) pass "$u: $state (oneshot)" ;;
*) fail "$u не active/inactive (сбой): $state" ;;
esac
done
# ---- 4. имена на системной шине (own) + сервис отвечает (§9.3.3) ----
@@ -192,6 +198,10 @@ echo " $(systemd-analyze time 2>/dev/null | head -1 || echo 'systemd-analyze н
# ---- power-safe (v0.3): FSM ShutdownImminent + N циклов зажигания + abort + power-cut ----
info "power-safe: ShutdownImminent + N=3 цикла зажигания + abort + power-cut"
# Чистый FSM Running для циклов (свежий бинарь + сброс любого «залипшего» состояния от §5 fake-ACC).
sudo systemctl restart shturman-power.service
for _ in $(seq 1 10); do systemctl is-active --quiet shturman-power && break; sleep 1; done
sleep 1 # дать power re-acquire ru.shturman.Power на шине
P_CALL() { busctl --system call "$P_NAME" "$P_PATH" "$P_MOCK" "$@"; }
busctl --system call "$S_NAME" "$S_PATH" "$S_IFACE" Set sv ui.theme s night >/dev/null
echo 0 | sudo tee /data/state/power-cycles >/dev/null
@@ -209,13 +219,15 @@ observe_imminent() { # SetAcc(false) → ждём ShutdownImminent на шин
for i in 1 2 3; do
observe_imminent || fail "цикл $i: ShutdownImminent не наблюдаем"
n=$(($(sudo cat /data/state/power-cycles) + 1))
sudo systemctl stop shturman-stage1.target # освобождает /data
sync; sudo umount /data || fail "цикл $i: umount /data"
sudo mount /data || fail "цикл $i: mount /data"
echo "$n" | sudo tee /data/state/power-cycles >/dev/null
sudo systemctl start shturman.target
sudo systemctl stop shturman-stage1.target # стоп сервисов (освобождает /data)
sudo umount /etc/machine-id 2>/dev/null || true # снять machineid-bind, иначе /data busy
sync; sudo umount /data || fail "цикл $i: umount /data (PONR)"
findmnt /data >/dev/null && fail "цикл $i: /data не размонтирован (PONR не достигнут)"
sudo systemctl start shturman.target # re-mount data.mount + сервисы (machineid re-bind)
for _ in $(seq 1 15); do systemctl is-active --quiet shturman-settings && break; sleep 1; done
pass "цикл зажигания $i: stop→umount→remount→restart"
findmnt /data >/dev/null || fail "цикл $i: /data не вернулся после remount"
echo "$n" | sudo tee /data/state/power-cycles >/dev/null
pass "цикл зажигания $i: stop→umount(PONR)→remount→restart, /data вернулся"
done
got=$(busctl --system call "$S_NAME" "$S_PATH" "$S_IFACE" Get s ui.theme 2>/dev/null)
echo "$got" | grep -q '"night"' || fail "ui.theme потерян после циклов"
@@ -238,13 +250,14 @@ pass "abort до PONR: ShutdownAborted + /data RW + running"
P_CALL SetAcc b false >/dev/null; sleep 0.3
sudo systemctl kill -s KILL shturman-power.service shturman-settings.service 2>/dev/null || true
sudo systemctl stop shturman-stage1.target 2>/dev/null || true
sudo umount /data 2>/dev/null || true
sudo umount /etc/machine-id 2>/dev/null || true
sudo umount /data 2>/dev/null || sudo umount -l /data 2>/dev/null || true
findmnt /data >/dev/null && fail "power-cut: /data не размонтирован (fsck был бы на смонтированном)"
sudo fsck.ext4 -n /var/lib/shturman/data.img >/dev/null 2>&1 || fail "fsck /data не clean после power-cut"
sudo mount /data
sudo systemctl start shturman.target # re-mount + restart
for _ in $(seq 1 15); do systemctl is-active --quiet shturman-settings && break; sleep 1; done
sudo grep -q night /data/settings/settings.json || fail "last durable value потерян после power-cut"
pass "power-cut-сим: /data консистентен (fsck clean, night present)"
sudo systemctl start shturman.target
for _ in $(seq 1 15); do systemctl is-active --quiet shturman-settings && break; sleep 1; done
# watchdog/save-time конфиг
test -f /etc/systemd/system.conf.d/shturman-watchdog.conf || fail "нет watchdog-конфига"