# ЗАКАЛКА — против «чёрного экрана смерти» и «кирпича»

Такт **v1.42.0 «ЗАКАЛКА»** (2026-10-06). Приказ владельца:

> «ИСПРАВЛЯЙ КОСЯЧКИ БАГИ ПРИБИВАЙ, ЧТОБ У ЧЕЛОВЕКА НЕ ВЫЛЕЗ СИНИЙ (СЕЙЧАС ЧЁРНЫЙ =)) ЭКРАН СМЕРТИ И УСТРОЙСТВО НЕ СТАЛО КИРПИЧЁМ»

Открытый код: всё, что здесь описано, живёт в репозитории и читается любым.

---

## 1. Как болело (карта болезней до такта)

| № | Болезнь | Где жила | Чем грозила |
|---|---------|----------|-------------|
| 1 | Ни одного глобального ловца ошибок (`window.onerror` / `unhandledrejection`) во всём доме | все страницы | любая ошибка JS = чёрная тишина без единого слова — тот самый «чёрный экран смерти» |
| 2 | Безлимитный рост кэша сервис-воркера: каждый же GET навсегда ложился `c.put` без потолка и без catch | `sw15.js` | телефон забивался до квоты, воркер ронял обещания, дом превращался в кирпич |
| 3 | `localStorage.setItem` без страховки в части рум (·23, ·29 и др.) | румы | приватный режим Safari / забитая квота → бросок → смерть инициализации комнаты |
| 4 | `JSON.parse` в бою: битое хранилище = `SyntaxError` прямо в горячем коде | румы, ядро | падение на ровном месте, молча |
| 5 | `new BroadcastChannel` без try в отдельных классах (например, транспорт зала ·17) | ·17 и др. | старый браузер / частная вкладка → ReferenceError в конструкторе |
| 6 | `crypto.subtle` без проверки (личность ·19: ECDSA-ключи, подписи) | ·19 | не-secure контекст / старый браузер → TypeError вместо честного слова |
| 7 | Анимации без общего «выключателя» на слабых устройствах | ядро, движок | слабый телефон греется и жрёт батарею — тот же «кирпич», только медленно |

## 2. Что прибито

### 2.1 СИНГУЛЯР·КОРД — страховочный корд дома (`БИБЛИОТЕКИ/корд/корд.js`)

Один код, вшит **в каждую страницу дома** (26 файлов) первым скриптом — до всего кода дома:

- **Глобальные ловцы** `error` + `unhandledrejection`: любое падение больше не тишина.
  Появляется панель **«◇ НИТЬ ПОРВАЛАСЬ — ДОМ ДЕРЖИТ»** — стекло + void + золото,
  ноль эмодзи, DOM-сборка (ноль innerHTML). Честно называет: что сломалось, где (файл:строка),
  сколько раз за такт. Кнопки-решения человека: **ПЕРЕЗАПУСТИТЬ / ТИХИЙ РЕЖИМ / ПОД КАПОТ / СВЕРНУТЬ**.
  После «СВЕРНУТЬ» остаётся золотой глоток-ромб — дверь обратно к панели.
- **Журнал сбоев**: последние 6 падений хранятся в хранилище (guarded), видны под капотом,
  выгружаются `window.SNG_КОРД_ЖУРНАЛ()`.
- **Хранилище-гард**: `setItem`/`removeItem` больше физически не умеют бросать —
  при квоте сначала уходит самый старый ключ дома, затем честная запись в журнал.
  Если сам `localStorage` недоступен (старые приватные режимы) — прозрачная заглушка в памяти.
- **JSON-гард**: битый текст → `null` + запись в журнал, а не `SyntaxError` в бою.
- **BroadcastChannel-заглушка**: там, где канала нет, конструктор больше не убивает комнату.
- **ТИХИЙ РЕЖИМ** (ЧЕЛОВЕК РЕШАЕТ — включается только человеком из панели):
  `sessionStorage 'sng-тихий'` → `window.SNG_ТИХО` + CSS-убийца анимаций.
- Сам корд целиком в `try` — он не имеет права уронить страницу.

### 2.2 Тихий режим вживлён в движки

- `singulyar-modules.js` (3 места: пульс сцены, призма, осколки): `движение` и `сокращено`
  уважают `window.SNG_ТИХО` наравне с `prefers-reduced-motion`.
- `singulyar-ux-engine-v9.js` (reducedMotion) — то же самое.

### 2.3 Честная личность ·19

`создатьЛичность` и `подписьВызова` проверяют `crypto.subtle` и отвечают честным
обещанием-отказом («нужен современный браузер»), а не TypeError в бою.

### 2.4 Сервис-воркер v52 (кирпичу нет)

- **Потолок кэша `S15_MAX=140`**: старейшие записи вытесняются — квоте некуда расти.
- **Каждый `put` в catch**: квота/приватный режим не роняют воркер.
- **Навигация без сети и кэша** (первый заход офлайн): честная офлайн-оболочка
  в каноне дома вместо пустоты браузера.
- **Нехватка ресурса**: честный `504`, а не подвешенный запрос.

### 2.5 Ядро и версии

- Ядро модулей **SNG v1.6.0 → v1.7.0** (ТИХО-хуки — поведение ядра).
- Сборка **v1.42.0 · ЗАКАЛКА**, SW-кэш **v52**, ГОТОВНОСТЬ **1.42.0**.

## 3. Как это проверено машиной

- `ИНСТРУМЕНТЫ/test_закалка.py` (Playwright): корд на месте и первым скриптом;
  падение показывает панель (не тишину); журнал пишет; квота-setItem не роняет;
  битый JSON даёт null; BroadcastChannel-заглушка живёт; тихий режим ставит флаг.
- Регрессия: весь живой полигон дома (модули-логика, каталог, s01-дым, s29, s30 ядро+браузер и др.).
- `node --check` на каждом тронутом JS.

## 4. Честные границы

- `ИНСТРУМЕНТЫ/test_s18_logic.js` — красный **и на чистом v1.41.0** (проверено живьём:
  файл ·18 с прода падает тем же крашем в таймере мок-транспорта симуляции зала).
  Это наследственный провал прибора симуляции, не следствие ЗАКАЛКИ; стоит в очереди ревизии.

- Корд не лечит причину падения — он делает причину **видимой** и держит дом живым.
  Чинить конкретную болезнь по стеку — работа следующих тактов (журнал под капотом показывает где).
- `JSON.parse → null` меняет контракт для не-guarded вызовов: они получат TypeError на месте,
  но уже **видимый** (панель + журнал), вместо чёрной тишины.
- Потолок кэша 140 — дом решает вытеснять старейшее; если не хватает — скажет журнал воркера.
