Files
houseplan-card/docs/specs/146-four-phase-sun-background.md
2026-08-14 16:34:45 +03:00

44 KiB
Raw Permalink Blame History

Issue #146 — четырёхфазный фон «Следует за Солнцем»

Статус: ТЗ на ревью
Дата: 2026-08-14
Тип: feature · приоритет: P1 · пользовательская ценность: 7/10 · сложность/риск: 8/10

Issue: #146
Ветка: issue/146-four-phase-sun-background
Канонические документы: SCOPE, SUN, UX-MODES, TOUCH-SUPPORT, ARCHITECTURE, CONFIG-COMPATIBILITY.

Текст issue имеет приоритет над приложенными к нему SPECIFICATION.md, README.md и интерактивным прототипом. Решения владельца Q1–Q3 и все defaults приняты 2026-08-14 в issue #146.

1. Сценарий и продуктовый контекст

Основная персона — человек, который ежедневно смотрит на дом в View либо на настенной kiosk-панели и должен одним взглядом узнавать сам план и текущее состояние дома. Фон вокруг плана даёт спокойный временной контекст, но не имеет права искажать цвета пола, стен, Glow, устройств или других данных.

Это часть J1 из docs/SCOPE.md: House Plan существует прежде всего как правдивый пространственный обзор дома. Новая функция декоративна, поэтому она не становится единственным носителем состояния и не меняет доступность управления. View и kiosk остаются полностью поддержанными touch-поверхностями.

2. Что человек увидит до и после

До: режим daynight непрерывно интерполирует один сплошной цвет по высоте Солнца, требует настроенный север, а ночью уменьшает яркость всего плана до 10 %. У новых установок и пространств режим не включён автоматически.

После: вокруг неизменного плана отображается одно из четырёх узнаваемых окружений — рассвет, день, сумерки или ночь. Между фазами за 1100 ms плавно меняются только градиент окружения, декоративный солнечный свет, внешняя виньетка и alpha-aware контур плана. При доступном sun.sun состояние и свет следуют реальному Солнцу; иначе работают по локальным часам браузера. Новые установки и новые пространства начинают с режима «Следует за Солнцем», старые неявные настройки сохраняют прежний статический вид.

3. Проблема и подтверждённая техническая база

  1. src/sun.ts::dayPhase() возвращает непрерывный solid background, planDim и warmth. Это не четырёхфазная модель из issue.
  2. Full card применяет ночной brightness() к .zoomwrap, поэтому вместе с окружением меняются сам план и все его live-слои.
  3. bg_mode: daynight сейчас зависит от north_deg и валидного sun.sun; без компаса либо сущности молча возвращается статический фон.
  4. sunStateOf() читает только azimuth/elevation. Направление движения (attributes.rising) ещё не участвует в выборе dawn/dusk.
  5. Full card и houseplan-space-card рассчитывают фон разными render-путями, а editor canvases намеренно остаются статическими.
  6. Текущий fallback отсутствующего bg_mode — static. Простая смена fallback на daynight изменила бы уже существующие планы без согласия пользователя.
  7. Уже существуют независимые солнечные лучи из окон, Glow/spill, room fills, hover, decor/backdrop, vacuum и mode-transition #101. Новый фон должен интегрироваться с ними, а не заменить или перекрасить их.

4. Решения владельца

  1. В диапазоне высоты между −6° и +6° признак sun.sun.attributes.rising различает рассвет и сумерки: rising — dawn, falling — dusk. Выше +6° — day, ниже −6° — night.
  2. Если elevation или rising отсутствуют/некорректны, используется полный fallback по локальному времени браузера: dawn 05:00–07:59, day 08:00–17:59, dusk 18:00–20:59, night 21:00–04:59.
  3. При валидном sun.sun и фаза, и положение декоративного фонового света следуют реальным elevation/azimuth. Часовая дуга прототипа используется только при полном fallback. Ночью отдельный световой круг скрыт.
  4. Существующие пространства без явного выбора сохраняют прежний статический вид через совместимую миграцию. Явный daynight получает новую четырёхфазную семантику. Новые установки и каждое новое пространство начинают с daynight; явный static автоматически не меняется.
  5. Сам план не затемняется и не тонируется. Существующие Glow/spill, солнечные лучи из окон, room fills/hover, decor/backdrop, vacuum и остальные live-слои сохраняют действующее поведение.

5. Scope

В задачу входят:

  1. дискретная модель dawn | day | dusk | night для bg_mode: daynight;
  2. реальный sun-source с полным локальным clock-fallback;
  3. четырёхфазный градиент окружения, декоративный солнечный свет, виньетка и alpha-aware контур по утверждённым visual tokens;
  4. переход фаз за 1100 ms и prefers-reduced-motion;
  5. одинаковая фаза и визуальные tokens в full View, kiosk и статической houseplan-space-card;
  6. совместимость с mode-transition View ↔ editor из #101;
  7. новый default для новой установки, ручного создания пространства и Floors/Areas onboarding;
  8. однократная совместимая миграция старого неявного global default;
  9. совместимость полного и per-space export/import;
  10. обновление существующих RU/EN строк режима и подсказок;
  11. unit, backend, production-bundle smoke, golden и performance coverage;
  12. пользовательская/архитектурная документация, оба changelog и три поставляемые bundle-копии.

6. Non-scope

В задачу не входят:

  • новый публичный mode token вместо static | daynight;
  • ручной phase selector, видимый QA-control или пользовательское расписание;
  • геолокация, внешний weather API, сетевой астрономический расчёт либо новые HA service calls/entities;
  • изменение существующей геометрии/порогов/цвета солнечных лучей из окон;
  • настройка visual palette, длительности перехода или порогов −6/+6 в UI;
  • окрашивание, затемнение, насыщенность, opacity либо blend самого плана;
  • day/night окружение внутри Plan/Devices/Decor editors;
  • пересмотр бумажного слоя, room fill, Glow, hover, decor, vacuum, isometric/Labs или View ↔ editor UX;
  • сохранение текущей вычисленной фазы в config/storage;
  • canvas/WebGL только ради фонового градиента;
  • production-обработка URL-параметра ?time=HH:MM;
  • изменение touch-контракта desktop-first редакторов.

7. Контракт источника и выбора фазы

7.1. Валидный real-sun sample

Для окружения real-sun sample валиден, только если одновременно:

  • существует live-state sun.sun;
  • attributes.elevation — конечное число;
  • attributes.azimuth — конечное число, нормализуемое в [0, 360);
  • attributes.rising — настоящий boolean.

Если любое условие нарушено, весь day-cycle — и фаза, и положение фонового света — атомарно переходит на clock-fallback. Нельзя смешивать фазу от часов с позицией из частично валидного sun.sun.

Это отдельная проверка только для окружения. Действующий sunStateOf() и существующие оконные лучи продолжают использовать azimuth/elevation по docs/SUN.md: отсутствие rising не должно выключить уже работающие лучи.

7.2. Фаза по реальному Солнцу

Границы детерминированы так:

Условие Фаза
elevation <= -6 night
-6 < elevation < 6 и rising === true dawn
elevation >= 6 day
-6 < elevation < 6 и rising === false dusk

На точной верхней границе состояние уже day, на точной нижней — уже night. Azimuth wrap 359°→0° не меняет фазу.

7.3. Полный clock-fallback

Используются часы и часовой пояс браузера через локальный Date, без серверного времени:

Локальные минуты Фаза
300 <= m < 480 (05:00–07:59) dawn
480 <= m < 1080 (08:00–17:59) day
1080 <= m < 1260 (18:00–20:59) dusk
иначе (21:00–04:59) night

DST и смену часового пояса даёт платформа Date. Интервал проверяет часы каждые 30 s только пока используется fallback и вкладка видима. Дополнительно состояние пересчитывается до первого видимого кадра, при pageshow и при visibilitychange обратно в visible. Если фаза и позиционные tokens не изменились, Lit/DOM update не запрашивается.

7.4. Переключение источника

Появление, исчезновение либо восстановление валидности sun.sun пересчитывает весь day-cycle из одного snapshot. Текущая фаза не хранится как истина и не может остаться от прежнего источника. Обычная смена источника получает тот же 1100 ms environment transition; fresh mount сразу начинает с правильного состояния без вспышки default-day.

8. Контракт декоративного фонового света

Декоративный свет — pointer-inert слой окружения под планом. Он не является оконным солнечным лучом и не использует north_deg: фон существует независимо от ориентации дома.

8.1. Real-sun position

При валидном real-sun sample позиция вычисляется из реальных атрибутов:

x = 50 - sin(azimuth * pi / 180) * 42       // percent
y = 78 - clamp(elevation, 0, 90) / 90 * 64 // percent
opacityFactor = clamp((elevation + 6) / 12, 0, 1)

Так восток (около 90°) оказывается у левого горизонта, запад (около 270°) — у правого, а северный и южный полдень проходят через центр без зависимости от полушария. Отрицательная высота держит свет у горизонта; в night opacity принудительно равна нулю.

8.2. Clock-fallback arc

Fallback повторяет дугу прототипа:

progress = (minutes - 300) / (1260 - 300)
x = 8 + progress * 84
y = 78 - sin(progress * pi) * 64
opacityFactor = max(.18, min((minutes - 300) / 120,
                             (1260 - minutes) / 120, 1))

Формулы применяются только в диапазоне 05:00 ≤ time < 21:00; вне его opacity равна нулю. Позиция обновляется той же 30-секундной fallback-проверкой, не пересчитывая план, стены, room geometry или устройства.

8.3. Вид и слой

Свет — мягкий radial gradient без жёсткой окружности, размером 250 CSS px в эталонном desktop layout; на меньшей поверхности он ограничивается размером окружения и не создаёт overflow/scroll. Цвет задаётся фазой, позиция и opacity — источником выше. В night элемент может оставаться в DOM, но его вычисленная opacity обязана быть 0 и ни один видимый световой круг не допускается.

9. Визуальный контракт четырёх фаз

Окружение строится из вертикального linear gradient и мягкого radial horizon у нижней части сцены. Значения ниже — канонические tokens прототипа; отклонение требует нового решения владельца, а не локального «подбора на глаз».

Фаза top bottom horizon sun vignette
dawn #aabdd1 #e8c8b7 rgba(255,201,156,.56) rgba(255,188,125,.78) rgba(65,72,99,.21)
day #dce9ef #cbdce3 rgba(255,245,220,.45) rgba(255,239,190,.72) rgba(65,91,105,.16)
dusk #48536c #9a7380 rgba(242,156,114,.34) rgba(255,167,113,.55) rgba(20,26,44,.39)
night #111a27 #1f2f3e rgba(79,120,151,.16) transparent rgba(3,8,14,.58)

Alpha-aware контур применяет три zero-offset drop-shadow либо визуально эквивалентный эффект к одному каноническому plan-paper composite, не к каждой комнате/стене отдельно:

Фаза near 0 0 1px mid 0 0 5px far 0 0 10px
dawn rgba(74,57,61,.25) rgba(255,238,224,.40) rgba(255,224,202,.18)
day rgba(45,62,71,.28) rgba(255,255,255,.42) rgba(255,255,255,.20)
dusk rgba(238,219,225,.40) rgba(229,207,218,.26) rgba(215,190,205,.12)
night rgba(218,238,249,.56) rgba(174,215,238,.30) rgba(136,194,226,.14)

Контур повторяет внешний alpha-контур общего бумажного footprint, не имеет смещения, не создаёт внутренних швов между комнатами и не обводит устройства, labels, hover или live-эффекты. Для image-plan канонический footprint — его действующий opaque paper rect; для drawn-plan — объединённая по одному group совокупность room paper polygons. Пустой drawn space без комнат не получает ложный прямоугольный контур.

10. Инвариант неизменного плана

Во всех четырёх фазах к plan/content tree запрещено добавлять:

  • brightness, contrast, saturate, sepia, hue-rotate или иной color filter;
  • opacity родителя, mix-blend-mode или цветной overlay поверх плана;
  • смену paper/floor/room fill цвета из-за фазы;
  • изменение яркости/цвета devices, labels, decor/backdrop, vacuum, Glow/spill, hover или оконных солнечных лучей.

Действующий dayPhase().planDim удаляется из day-cycle render и из target brightness mode-transition #101. Plan pixels внутри plan-paper alpha footprint до и после смены фазы должны совпадать; разрешено меняться только внешнему zero-offset outline за границей footprint.

11. Переходы и surfaces

11.1. Phase transition

При смене фазы ровно 1100 ms с easing cubic-bezier(.22, .61, .36, 1) анимируются только:

  • environment top/bottom/horizon;
  • цвет/позиция/opacity декоративного света;
  • внешняя vignette;
  • три цвета alpha-aware outline.

Нельзя fade всего экрана или самого плана. Реализация обязана использовать действительно интерполируемые CSS primitives либо environment-layer crossfade; простая декларация transition на неанимируемом gradient не считается выполнением. При prefers-reduced-motion: reduce длительность практически нулевая (<= 0.01 ms либо transition: none).

11.2. Full View, kiosk и static card

Full View и kiosk используют один environment model. Статическая houseplan-space-card получает ту же effective phase, palette, свет, outline и fallback lifecycle; оконные wedges в static card не добавляются — действующее ограничение docs/SUN.md сохраняется.

11.3. Editors и #101

Plan/Devices/Decor editors сохраняют действующий editor background и не получают четырёхфазное окружение. При View ↔ editor переходе #101:

  1. исходный и целевой кадр используют один атомарный phase/source snapshot;
  2. сам plan composite не меняет brightness/color;
  3. environment transition не перехватывает pointer/focus и не оставляет скрытый слой после commit/cancel;
  4. при возвращении в View актуальная фаза пересчитывается до целевого кадра, включая возврат после долгой паузы.

11.4. Существующие live-эффекты

Glow/spill, room fills/hover, decor/backdrop, vacuum, labels/devices и оконные лучи сохраняют порядок слоёв, opacity, blend и lifecycle. north_deg продолжает управлять только ориентационно зависимыми оконными лучами; daynight background больше не выключается из-за отсутствия компаса.

12. Модель данных, defaults, migration и import/export

12.1. Persisted schema

Публичная schema не расширяется: global и per-space settings.bg_mode: 'static' | 'daynight', per-space null/absence = inherit. Вычисленная фаза, источник и позиция света не сохраняются. Backend продолжает принимать отсутствие bg_mode ради legacy compatibility.

Runtime fallback отсутствующего effective token остаётся static; новая семантика достигается материализованными defaults, а не переопределением отсутствующего поля.

12.2. Новая установка и новые пространства

  • Новый DEFAULT_CONFIG материализует global settings.bg_mode: 'daynight'.
  • Ручное Create space материализует per-space bg_mode: 'daynight'.
  • Каждое пространство, созданное Floors/Areas onboarding, материализует per-space bg_mode: 'daynight'.
  • Открытие и сохранение существующего space dialog не меняет его mode без действия пользователя.
  • Явный static на любом уровне никогда не переписывается автоматически.

Per-space materialization нужна даже при новом global default: добавленное позднее пространство начинает с daynight, в том числе внутри старой установки, чей global default мигрирован в static.

12.3. Однократная миграция существующего store

Backward-compatible storage migration выполняется один раз и идемпотентно:

  1. если в существующем config global settings.bg_mode отсутствует либо невалиден, он материализуется как static;
  2. валидный global static или daynight сохраняется;
  3. все валидные per-space overrides сохраняются без переписывания;
  4. spaces без override продолжают наследовать уже материализованный global;
  5. migration сохраняет неизвестные поля, не меняет layout и не создаёт config-update storm;
  6. повторный старт не создаёт новую запись/revision.

12.4. Export/import

  • Full export после миграции содержит явный global mode; legacy full import без него материализуется как static до preview/apply.
  • Per-space export материализует в экспортируемом space его effective mode, чтобы перенос не зависел от отсутствующего global settings блока.
  • Legacy per-space import без mode материализуется как static до merge.
  • Явные static/daynight сохраняются при same-instance и foreign import.
  • Import preview показывает тот же итоговый mode, который будет применён; preview не пишет storage.
  • Никакой импорт не превращается в «новое пространство по default»: импортированная семантика имеет приоритет над default ручного Create.

13. UX, i18n, accessibility и touch

Видимых новых controls нет. Действующий selector остаётся двухпозиционным: static и daynight; color picker виден только для static.

Обновляются существующие RU/EN keys без добавления третьего режима:

  • gs.bg_daynight: «Следует за Солнцем» / “Follows the Sun”;
  • gs.bg_daynight_hint: четыре фазы, real sun и локальный fallback, без требования компаса;
  • gs.sun_missing и gs.north_hint: отсутствие sun/севера отключает оконные лучи, но не clock-driven background.

Фаза декоративна: она не добавляет announcement, focus target, toast или semantic state; не меняет размеры сцены и не служит единственным носителем информации. forced-colors сохраняет функциональную читаемость plan/devices; декоративные environment layers могут быть упрощены либо отключены.

View, kiosk и static card полностью поддерживаются мышью, touch и без pointer: новые слои имеют pointer-events: none, не перехватывают tap/swipe/pinch и не меняют hit targets. Editor остаётся desktop-first и не получает новых действий.

14. Архитектурный и performance-контракт

  1. Один pure resolver возвращает phase, source (sun | clock) и декоративные position/opacity tokens. Full/static surfaces не копируют пороги и формулы.
  2. Palette хранится одной typed таблицей. CSS и тесты не получают расходящиеся hard-coded варианты.
  3. Real-sun resolver запускается только на релевантном изменении sun snapshot, config/space или lifecycle catch-up; прочие hass ticks не меняют DOM.
  4. Clock timer существует только в fallback, не чаще 30 s, не выполняет необязательную работу в hidden document и очищается в disconnectedCallback.
  5. Не создаётся requestAnimationFrame loop, canvas/WebGL, geometry/layout polling, новая сеть, service call или storage write.
  6. Phase/source/position update меняет только bounded environment state/style. Rooms, walls, openings, Glow barriers, devices и backdrop geometry не пересчитываются.
  7. Alpha-aware outline применяется к одному plan-paper composite; per-room filter multiplication запрещён.
  8. Environment DOM count постоянен относительно rooms/devices и не растёт при каждой фазе или visibility cycle.
  9. #101 и cold-start #131 сохраняют первый полный кадр: не допускается второй пустой render ради вычисления локального времени.
  10. Таргетированный performance smoke доказывает отсутствие geometry/device rebuild на clock tick и обычном нерелевантном hass tick. Перед бетой на exact SHA обязательны существующие performance smoke и Full Performance без ослабления budgets.

15. Acceptance criteria

  • AC1 (unit; разработчик): real-sun resolver детерминированно выдаёт night/dawn/day/dusk на обеих сторонах −6/+6, точных границах, при rising и falling; azimuth wrap не меняет фазу.
  • AC2 (unit + production-bundle smoke; разработчик): отсутствие/garbage любого из elevation/azimuth/rising атомарно включает clock-fallback с точными границами 05:00/08:00/18:00/21:00; валидность real sample восстанавливает весь sun-source без смешивания данных.
  • AC3 (unit + targeted smoke; разработчик): real-sun позиция следует elevation/azimuth, fallback — утверждённой дуге 05:00→13:00→21:00, а night имеет opacity 0. Ни один update не пересчитывает plan geometry.
  • AC4 (golden + computed-style smoke; владелец/ревьюер): dawn/day/dusk/ night используют точные palette и outline tokens раздела 9; ночью нет видимого светового круга, а светлый дневной и тёмный ночной план отделены от окружения.
  • AC5 (pixel regression + code review; разработчик/ревьюер): внутри plan-paper footprint пиксели plan, floors, room fills, Glow/spill, devices/labels, decor/backdrop, vacuum, hover и оконных лучей совпадают между четырьмя фазами; никакой phase-dependent plan filter/opacity/blend не остаётся.
  • AC6 (targeted smoke; разработчик): смена фазы анимирует только environment и outer outline за 1100 ms с заданным easing; plan не мигает. Reduced motion завершает переход практически мгновенно.
  • AC7 (production-bundle smoke + golden; разработчик/владелец): full View, kiosk и static card получают одну effective phase/palette; editors остаются прежними, а #101 transition не оставляет stale/interactive environment layer.
  • AC8 (unit + smoke; разработчик): daynight работает без north_deg и без sun.sun через clock fallback; оконные лучи по-прежнему требуют свои действующие sun/north gates и не меняют геометрию/пороги.
  • AC9 (backend migration tests; разработчик): существующий отсутствующий global mode мигрирует в static ровно один раз; валидные global/per-space modes и неизвестные поля сохраняются; повторный старт не пишет store/rev.
  • AC10 (frontend unit + backend/import tests + smoke; разработчик): новая установка, manual Create и Floors/Areas create материализуют daynight; существующий Edit не меняет mode сам; явный static остаётся static.
  • AC11 (backend/import-export tests; разработчик): full/space export и legacy/current import выполняют контракт §12.4, preview совпадает с apply, а preview не пишет storage.
  • AC12 (i18n test + accessibility/touch smoke; разработчик): RU/EN тексты описывают четыре фазы и fallback; environment pointer-inert, не двигает focus, не меняет размеры/hit targets и не ломает tap/swipe/pinch в View/kiosk.
  • AC13 (lifecycle smoke; разработчик): fallback пересчитывается сразу, каждые 30 s только в visible state, на pageshow и visible-return; timer и listeners очищаются при disconnect, повторное подключение не дублирует их.
  • AC14 (performance smoke + code review; разработчик/ревьюер): sun/clock update меняет bounded environment state без room/device/geometry rebuild, RAF-loop, canvas, сети, HA calls или storage writes; DOM/timer count bounded.
  • AC15 (typecheck + unit + build + docs review; разработчик): локальные implementation gates зелёные, обе пользовательские документации/changelog и три bundle-копии обновлены в одном User-Visible: yes коммите.
  • AC16 (exact-SHA Linux gates; Claude/релиз-инженер): перед бетой зелёны Validate, полный smoke, reviewed golden и Full Performance на одном SHA без ослабления budgets; security/network/schema verdict явно записан в code review.

16. План автотестов и визуального review

16.1. Unit/frontend

Добавить pure-helper coverage:

  1. elevation -6.1/-6/-5.9/5.9/6/6.1 при обоих rising values;
  2. NaN/infinity/string/missing для elevation, azimuth и rising;
  3. fallback минуты 299/300/479/480/1079/1080/1259/1260;
  4. real position east/south/west/north и elevation below horizon/0/45/90;
  5. fallback position/opacity в 05:00, 07:00, 13:00, 19:00, 21:00;
  6. palette completeness и night opacity 0;
  7. mode inheritance с compatibility fallback static;
  8. отсутствие влияния нового rising requirement на sun-ray helpers.

16.2. Backend и compatibility

Покрыть storage migration: missing, static, daynight, mixed per-space, malformed legacy, unknown fields, idempotent second load и rev/event count.

Расширить import/export matrix: legacy full/space без mode, current full/space с обоими modes, inherited export materialization, same/foreign source, preview versus apply и import into target с противоположным global mode.

16.3. Targeted production-bundle smoke

Обновить sun/background smoke либо добавить отдельный four-phase smoke на собранном bundle:

  1. full View, kiosk и houseplan-space-card проходят четыре deterministic real-sun phase;
  2. отдельная matrix проходит clock fallback и source recovery;
  3. computed styles проверяют palette, outline, light visibility и transition;
  4. pixel probes/masked screenshots доказывают неизменность plan interior;
  5. existing window rays работают с north, а background — без north;
  6. editor и View↔editor transition не получают stale layer;
  7. visibility/pageshow/timer cleanup и reduced motion проверяются под fake clock;
  8. manual/Floors create и existing Edit проверяют persisted modes;
  9. counters доказывают отсутствие geometry/device rebuild и storage/network activity на fallback tick.

Targeted smoke пишется вместе с кодом; полный smoke-suite запускается перед бетой, не в implementation loop.

16.4. Golden

Нужны новые deterministic Linux golden-сцены full View для dawn, day, dusk, night и static-card сцены для двух контрастных крайних состояний day/night. Одинаковые fixture plan/live state/camera исключают посторонний diff; reduced motion включён.

Golden artifact ревьюится владельцем/Claude как единый набор: соответствие прототипу, отсутствие plan tint/dim, световой круг не виден ночью, outline не создаёт per-room seams. Baseline принимается только штатной командой golden:accept -- --reviewed из полного Linux-артефакта и отдельным коммитом с обязательными release/baseline trailers по runbook; принять baseline ради зелёного CI нельзя.

17. План реализации

  1. Ввести typed day-cycle resolver/palette рядом с sun pure logic, не меняя оконную ray geometry.
  2. Подключить единый environment model и lifecycle к full/static surfaces; убрать phase-dependent plan dim и compass gate background.
  3. Реализовать environment layers, decorative light, vignette, composite outline, 1100 ms/reduced-motion и интеграцию #101.
  4. Материализовать новые defaults, storage migration и import/export compatibility.
  5. Обновить selectors/hints i18n, документацию и changelog.
  6. Добавить unit/backend/targeted production-bundle smoke и deterministic golden scenes.
  7. Выполнить implementation-loop typecheck, unit, build; остальные гейты — на пре-релизе по команде владельца.

Точные имена helper-функций, CSS classes и private fields не являются продуктовым контрактом.

18. Release-артефакты

Изменение пользовательское: implementation-коммит имеет User-Visible: yes и в том же коммите обновляет:

  • docs/CHANGELOG.md и docs/CHANGELOG.ru.md;
  • docs/SUN.md — новый источник истины по фазам, fallback, визуалу и независимости от компаса;
  • docs/USER-GUIDE.ru.md — выбор режима, четыре фазы, defaults и fallback;
  • docs/ARCHITECTURE.md — resolver/lifecycle/layers и full/static parity;
  • docs/CONFIG-COMPATIBILITY.md — storage/default/import migration;
  • docs/TESTING.md — targeted smoke/golden matrix;
  • RU/EN i18n и три поставляемые bundle-копии.

Перед бетой на exact SHA обязательны:

  • Linux Validate;
  • полный smoke-suite;
  • reviewed/accepted golden artifact без необъяснённого diff;
  • performance smoke и Full Performance без ослабления budgets;
  • code review с явным negative security/network/HA-call verdict.

Новый отдельный security report не требуется: feature не принимает HTML/URL, не добавляет network/storage input или HA action. Публикация проходит через обычную бету до stable; issue закрывает владелец пачкой после опубликованной беты.

19. Риски и меры

Риск Вероятность / влияние Мера
План незаметно остаётся dim/tinted старым filter средняя / высокая AC5 pixel mask, удаление planDim consumers, four-phase golden
Старый план без mode внезапно включит daynight средняя / высокая materialized storage migration в static, idempotence/backend tests
Новое пространство в старой установке наследует static высокая / средняя explicit per-space daynight в manual и Floors create, AC10
Partial sun sample смешивается с clock position средняя / средняя атомарный resolver/source, garbage matrix AC2
CSS gradient прыгает вместо перехода средняя / средняя проверяемые intermediate styles/pixels, animatable primitive/crossfade
Outline создаёт швы по комнатам или обводит devices средняя / высокая один plan-paper group, full/static golden и DOM assertion
30 s timer течёт/дублируется средняя / средняя fallback-only lifecycle, disconnect/reconnect smoke
Background начинает зависеть от north и молчит средняя / высокая отдельные gates background/rays, no-north smoke
#101 оставляет stale слой либо pointer blocker средняя / высокая mode-transition targeted smoke, pointer-events none
Import наследует target mode вместо исходного средняя / высокая effective space export + legacy import materialization matrix
Visual layers ухудшают слабое устройство низкая / высокая bounded layers, один outline filter, performance exact-SHA gates

20. Откат

Пользовательский немедленный откат — выбрать static глобально либо для пространства; environment timer/layers становятся инертны без потери данных.

Технический откат — revert implementation-коммита вместе с тестами, документацией, changelog и bundle-копиями. Миграция только материализует ранее эффективный static, поэтому старая версия полностью читает результат; новые явные daynight также являются старым валидным token. Обратная миграция и удаление полей не нужны. Если default новой установки требуется временно отключить без полного revert, вернуть creation defaults в static; сохранённые пользовательские choices не переписывать.

21. Принятые технические предположения — можно менять без продуктового ревью

  1. Точная граница считается <= -6 => night, >= 6 => day; только открытый интервал различает rising/falling.
  2. Environment использует отдельный strict real-sun validator; ray API не расширяется обязательным rising.
  3. Real decorative position использует формулы §8.1: horizontal проекция через sin(azimuth), vertical — линейно по положительной elevation. Это декоративное отображение, не плановая координата и не зависит от north.
  4. Размер light можно выразить через bounded clamp() вместо ровно 250 px на маленьких карточках, сохранив desktop reference и visual intent.
  5. Конкретный способ плавной смены gradient — registered custom properties или два bounded environment layers; AC важнее механизма.
  6. QA не читает production ?time: fake clock/sun внедряется только в unit, demo и smoke harness. Пользовательского QA-control нет.
  7. Storage migration рекомендуется реализовать bump minor version с проверкой формы config-store document; точный helper свободен при соблюдении AC9.
  8. Per-space export может материализовать effective mode в копии export payload, не меняя live config.
  9. Один shared fallback ticker допустим вместо timer на карточку, если lifecycle, isolation нескольких карточек и bounded listener count доказаны тестами.
  10. DOM может хранить фазу как data-day-cycle либо typed class; публичной schema/DOM API это не становится.
  11. Контур строится вокруг действующего канонического opaque paper composite: image rect либо общего group room papers; новый анализ alpha пикселей загруженного raster-файла не требуется.
  12. Открытых продуктовых вопросов нет; все Q1–Q3 и defaults приняты владельцем 2026-08-14.