Files
bare/docs/decisions/070-update-compares-shell-version.md
mayatnikovandClaude Opus 5 383e374f0f iOS: запрет масштабирования, версия в подвале, самообновление, строка ввода
Отзыв с iPhone после первого живого прогона.

Масштабирование запрещено (ADR-066): viewport как в соседнем sixlines плюс
viewport-fit=cover, мета-теги установленного приложения, touch-action против
двойного тапа и страховка на JS — гашение жестов iOS, потому что мета-строку
Safari вправе проигнорировать. Размеры шрифтов не тронуты: 14 px из брифа
остаются, поднимать поля до 16 px владелец отклонил. Цена — доступность
для тех, кому зум нужен — записана в ADR и в ui.md честно.

Версия в подвале (ADR-065, 067): GET /api/config отдаёт короткую ревизию
и время коммита. Именно коммита, а не сборки: штамп компиляции в бинаре
means каждая пересборка даёт новый хеш, и обещание ADR-022 «бинарь собирается
из тега байт в байт» перестаёт проверяться.

Самообновление PWA (ADR-068, 070): в режиме приложения на iPhone перезагрузки
нет, и старая версия жила бы вечно. Проверка при возврате в приложение,
включение ждущей оболочки, перезагрузка — но не тогда, когда в строке набран
текст. Замок от петли сравнивает версию оболочки, а не считает перезагрузки:
счётчик отменял саму фичу, долгоживущая вкладка переставала обновляться.
Замерено: было 30, 60, 120, 240 секунд между релизами, стало ровно 30.

Строка ввода — редактируемый блок (ADR-069): iOS рисует над клавиатурой
полосу со стрелками и «готово» для полей формы, и убрать её со страницы
нельзя. Для contenteditable не рисует. Режим plaintext-only с проверкой,
что он применился, и запасным путём с разбором вставки: невалидное значение
атрибута в части браузеров делает блок нередактируемым вовсе. Чтение
и запись только через textContent, innerHTML в клиенте по-прежнему нет.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015DbCjVfTFq4ZFG8juD45YJ
2026-08-23 15:11:13 +03:00

30 lines
6.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-070: Обновление сверяется с версией оболочки, а не со счётчиком перезагрузок
Заменяет второй замок из [ADR-068](068-pwa-self-update.md): растущая пауза уходит, различение петли и релиза остаётся.
## Контекст
ADR-068 закрыл петлю перезагрузок двумя замками. Второй — пауза, которая удваивается с каждой перезагрузкой ради обновления: 30 секунд, минута, две, четыре. Считает её счётчик `bare-updates` в `sessionStorage`; он только растёт и обнуляется лишь смертью вкладки.
Счётчик не различает петлю и обычный релиз. Каждая законная перезагрузка удваивает паузу перед следующей, и вкладка, которая живёт долго, обновляться перестаёт: после десятого релиза ждать четыре часа, после двенадцатого — сутки. Долгоживущая вкладка — это ровно установленное на «Домой» приложение, которое не закрывают неделями, то есть тот самый случай, ради которого ADR-068 и написан. Защита от петли отменяла фичу, которую защищала.
Различие между петлёй и релизом есть, и оно не в числе перезагрузок: **при петле версия оболочки после перезагрузки не меняется, при релизе меняется**. Сервер, отдающий новый файл воркера на каждый запрос, отдаёт ту же оболочку — версия в `sw.js` пишется руками при релизе (`docs/deploy.md`), и сама собой она не меняется.
## Решение
- `sw.js` отвечает на сообщение `version` своей константой `VERSION`. Ответ уходит каналом вопроса (`MessageChannel`): вопросов бывает два подряд и к разным воркерам.
- Страница спрашивает версию у своего контроллёра при запуске. Это версия оболочки, код которой она исполняет: модули пришли из кэша именно этого воркера.
- Смена контроллёра ведёт к перезагрузке, только если версия включившейся оболочки другая. Та же версия означает, что сервер отдал другой файл воркера при неизменной оболочке: перезагрузка вернула бы ту же самую страницу, и следующий запрос за `sw.js` начал бы всё сначала. Петля обрывается здесь и целиком — перезагрузок в ней не случается вовсе.
- Версия, которой не назвали (воркер прежнего выпуска такого вопроса не знает, ответа нет за три секунды), считается другой: обновление до выпуска, который отвечает, важнее. Повторяться этому не с чего — после перезагрузки отвечают оба.
- Первая установка запоминает версию нового контроллёра: страница пришла из сети, и оболочка, которую она исполняет, — та, что только что встала.
- Пауза между перезагрузками остаётся, но постоянная — 30 секунд, и `bare-updates` уходит. Замком от петли она больше не служит; это предел частоты на случай сервера, который отдаёт разные версии на каждый запрос. Отметка времени `bare-updated-at` остаётся в `sessionStorage`.
- Отложенная паузой перезагрузка возвращается сама, по таймеру: другого повода может и не быть — поля пусты, вкладка открыта, а вкладка спрашивает сервер об обновлении только при запуске и при возвращении в приложение (ADR-068).
## Следствия
- Десять релизов подряд за жизнь одной вкладки доезжают все и через одну и ту же паузу: замер — 29–30 секунд на релиз. Прежний замок на тех же релизах давал 30, 60, 120, 240 секунд и дальше вдвое.
- Сервер, отдающий новый `sw.js` на каждый запрос при неизменной версии, перезагрузок не вызывает ни одной. Воркер он при этом меняет — это дело браузера, и стоит оно одной установки в минуту, не чаще: чаще страница сервер не спрашивает.
- Релиз без смены `VERSION` страницу не перезагружает. Это не новое ограничение: ADR-023 обещал такому релизу только обновление статики по ETag.
- В `sw.js` появился второй вопрос от страницы. Протокола (`docs/protocol.md`) это не касается: разговор идёт внутри браузера.
- `docs/storage.md`: у клиента остаётся один ключ в `sessionStorage` вместо двух.