# Промт: фронт-сессия S3 — слой данных (вход под замком) Ты — фронтенд-сессия TextMachine, четвёртая по счёту. Зона записи — **только `frontend/`**; сессия не коммитит — лендит оркестратор (протокол — стоящий промт `FRONTEND_SESSION_PROMPT.md` §«Зона и git», он в силе целиком). Пре-коммит хук стоит и самоустанавливается при `npm install`. > Промт написан сессией S2 по просьбе владельца — как передача с той точки, где S2 встала. > Ратифицирует и правит его оркестратор; при расхождении с его хендофф-промтом побеждает тот. ## Входное состояние (04.08, проверено исполнением, а не заявлено) - **Пройдено S0–S2:** план · инструменты и скриншот-цикл · `tokens.css` по замерам референса · оболочка трёх панелей (`src/shell/`) · примитивы `src/ui/` на `react-aria-components` · витрина `/showcase` и нагрузочный `/scale` (2284 раздела, 1200 терминов). - `npm run check` — 5 гейтов (prettier · eslint тип-осведомлённый · stylelint · tsc · vitest, **77 тестов**), `npm run check:full` — + сборка + три скриншота с гейтом доступности axe. Всё зелёное на 04.08. - **Данные всё ещё синхронная фикстура:** `src/api/` отдаёт объекты из `src/mock/` напрямую. Это диспозиция Ф-15, а не недосмотр: асинхронность приезжает вместе с MSW, то есть в S3. - **Гейты, которые ты застаёшь:** одно место для цвета и размера (TSX-половина в `eslint.config.js`, CSS-половина в `stylelint.config.js`) · три шва — фикстуры видит только `src/api/`, глобальный CSS только `src/main.tsx`, `react-aria-components` только `src/ui/` — закрытые и для статического, и для динамического импорта · сеть запрещена вне `src/api/` во всех транспортах (`fetch`/`EventSource`/`WebSocket`/`XMLHttpRequest`, включая `window.*`) · тест общности: код языка не зашивается в разметку, стили не ветвятся по `:lang(…)` · контракт-тест токенов и структурные тесты CSS-модулей. - ⚠ **На момент написания промта в дереве лежали незакоммиченные правки** по ревью оркестратора (`frontend/src/{api/types.ts,generality.test.ts,mock/book.ts,mock/scale.ts,showcase/Context.tsx, showcase/Reader.module.css,tokens/tokens.css,tokens/tokens.test.ts}`, `eslint.config.js`, `frontend/docs/*`). Их лендит оркестратор. **Первым делом сверься с `git log` и `git status`:** если правок в дереве нет — они уже уехали, ничего восстанавливать не нужно. ## Замок на входе — первое действие сессии **Проверь сам, промту не верь** (он написан 04.08 и мог устареть): 1. Существует ли `docs/architecture/14-api-contract/` (спека + компаньон)? — **на 04.08 вечера: ДА, ратифицирован (D39.99), замок ОТКРЫТ**; 2. Строки 95 в таблице единого бэклога больше НЕТ — она ЗАКРЫТА (D39.99, закрытые строки уходят в D-лог); если видишь её живой — дерево старее канона, стоп и пинг. **Открыт** (контракт есть и ратифицирован) → скоуп S3 ниже. **Закрыт** → раздел «Если замок закрыт» (контингенция; на вечер 04.08 замок ОТКРЫТ — D39.99). Не начинай S3 «пока на своих типах, потом поправим»: моки, снятые не с того контракта, разойдутся с API — ровно то, ради чего строка 95 заведена. ## Обязательное чтение до кода (порядок) 1. `frontend/docs/FRONTEND_SESSION_PROMPT.md` — стоящий промт: сценарий §3, жёсткие ограничения §4, «как работать» §7. **Его не редактировать.** ⚠ Раскладка §2 частично устарела — её перевешивают решения владельца из журнала зоны. 2. `frontend/docs/frontend-PROGRESS.md` целиком: «Текущее состояние» · «Решения владельца по продукту» · «Открытые вопросы» · хронику 04.08 (в ней — что S2 построила, где отошла от референса и что поправило ревью оркестратора). 3. `frontend/docs/FRONTEND_PLAN.md` — §0.1 (карта канона: что читать перед чем), **§0.2 (транспорт: с движком фронт не разговаривает никогда — это ратифицированный анти-паттерн, D39.81/D39.85)**, §5.4 и §5.4.1 (формы обхода гейтов и названные границы), **§6 (что уже известно про движок и как это меняет форму данных — читать до первой строки типов)**. 4. `frontend/docs/STACK_DECISIONS.md` — §2 пины (там уже названы `@tanstack/react-query` и `msw`), §5 транспорт и правила стрима, §7 риск ре-ингеста. Библиотеки сам не выбирай и версии по памяти не «обновляй»: твои знания об экосистеме устарели, сверка — live по npm. 5. `docs/research/23-engine-platform-seam.md` — `docs/README.md` требует читать его **перед любым кодом стыка**; форма событий и правило ре-синка оттуда. 6. `frontend/docs/BACKLOG.md` — строки **Ф-14** (вход фронта в контракт), **Ф-15** (закрывается этой сессией), Ф-19, Ф-21. 7. `references/*.png` и `.shots/*.png` — открой и посмотри до первой правки. ## Решения владельца 04.08 вечер (D39.100) — правки спеки ПЕРВЫМ шагом S3 Контракт ратифицирован, четыре К-вопроса закрыты владельцем; спеку правишь ты (минорный бамп версии, регенерация типов, батарея), компаньон-К-таблицу — статусами «решено»: 1. **К-1 закрыт: десять статусов приняты.** 2. **К-8 закрыт механизмом «как Claude Code»** (ПТ-35, П-5): `BookStatus` получает 11-е значение **`paused`** (стоп по потолку; резюмируемый; в `failed` не мапить — уже запрещено спекой); пользователь видит оповещение «перевод остановлен: лимиты исчерпаны», сами лимиты — страница в настройках (СТАТУС использования, не суммы: запрет денег на проводе в силе). API лимитов проектирует платформа (П-5) — поле/эндпоинт в спеку НЕ выдумывать, только статус и семантику оповещения. 3. **К-5 закрыт: ETA показывать** — `eta_seconds` в прогресс-модель (движок отдаёт, `status.go:112`; поле опциональное — оценка бывает недоступна). 4. **К-3 закрыт с канон-уточнением: метка главы — ИЗ ДАННЫХ книги, зашитых форм нет.** «Глава N» — это шаблон langpack-ДАННЫХ, не закон: легальна книга вовсе без номеров и глав. Дерево рендерит label, который дал манифест (строка 100)/heading (К-2), внутренний порядок — непрозрачный индекс. Проверь спеку и фикстуры на зашитую форму «Глава N». 5. После правок: байт-сверка зонной спеки с ратифицированной копией (`docs/architecture/14-api-contract/openapi.yaml`) — расхождение отдай оркестратору на пере-ратификацию диффом, сам канон не правь. ## Скоуп S3 — слой данных, и ни шагом дальше 1. **`src/api/` становится асинхронным**: MSW как сетевой мок + серверное состояние на пинах из STACK_DECISIONS §2 (версии сверить live на дату сессии; `.npmrc` держит `save-exact`). Каждый новый пин — строкой в таблицу `FRONTEND_PLAN.md` с датой релиза и «зачем нам». 2. **Фикстуры всех состояний, а не одного счастливого:** ожидание · ошибка · пусто · частично · длинный хвост · потеря соединения на стриме. Правило S1/S2, которое здесь действует сильнее всего: **фикстура по умолчанию — трудный случай.** Наивный счётчик глав должен на ней давать ноль (идёт черновая волна), иначе скриншот-цикл смотрит на удобное. 3. **Живой прогресс — `EventSource` за швом `src/api/`** (STACK_DECISIONS §5; гейт сети уже запрещает его где-либо ещё). Докачка по `Last-Event-ID`/ревизии — часть контракта, не выдумка. 4. **Ф-15 закрывается здесь:** ветки ожидания/ошибки/пустоты появляются вместе с MSW, а не после первого продуктового экрана. 5. **Типы `src/api/types.ts` приводятся к контракту.** Что в них сегодня заведомо временное: булев `signed` вместо трёхзначного `auto|draft|approved` · ключ строки банка, собранный из (`src`, `dst`, `type`), — у движка термин уникален по (`src`, `sense`, `since_ch`, `until_ch`) · отсутствие ревизии для докачки. Всё это перечислено строкой Ф-14. **Закрытые юнионы получают явную ветку неизвестного значения** (строка Ф-22) — сегодня её нет ни у одного, и строгость тайпчека здесь НЕ страхует: `runStates[book.state]` (`showcase/Library.tsx:103`) — обращение к `Record` по КОНЕЧНОМУ юниону, а `noUncheckedIndexedAccess` добавляет `undefined` только индексным сигнатурам (проверено `tsc` на пробе: обращение по юниону компилируется молча, ругается только `Record`). Значит состояние прогона из будущего контракта даёт `undefined.tone` — падение дерева библиотеки, а не пустую метку; `noteTone` тем же классом тихо уводит незнакомую ступень замечания в СПОКОЙНУЮ ветку. **Место ветки — шов `src/api/`, где значение входит**, а не каждый компонент: перевод «вердикт → вид» уже расползался по экранам, и контрольный вопрос владельца требует, чтобы новое значение правило один файл. 6. **Поля не выдумывать.** Нет в контракте — это вопрос владельцу/оркестратору в «Открытые вопросы» журнала зоны, а не догадка в типе. Дефект «фикстура выдумала поле» в этой зоне случался дважды и оба раза стоил экрана. **Не в скоупе:** экраны S4–S7 · редактор текста · светлая тема · мобильная раскладка · React Compiler (Ф-2) · Tauri (Ф-5). ## Если замок закрыт Контингенция на случай, если проверка замка даст «закрыт» (на вечер 04.08 замок ОТКРЫТ); тогда правильный ход — не ждать молча и не строить экраны на синхронных моках (порядок этапов Ф-1 стоит именно на слое данных). - **Доведи Ф-14 до артефакта, который можно отдать автору контракта:** не список намерений, а перечень с обоснованием на каждый пункт — язык кодами · состояние прогона отдельно от выполнения главы · прогресс пофазно по ЮНИТАМ · ревизия/`Last-Event-ID` · трёхзначный статус подписи · поля банка (`type`/`sense`/`since_ch`) и ключ строки · продуктовый словарь вердиктов (ПТ-33; §4.1 запрещает протечку внутренностей конвейера на экран). Половина уже написана в строке Ф-14 и в §6 плана — собери, а не сочиняй заново. - Отдай его оркестратору пингом в `frontend/docs/frontend-PROGRESS.md` и **остановись**: заводить контракт своим решением фронт не может (шапка `BACKLOG.md`). ⚠ **Испр. сессией S3 04.08 по слову владельца:** здесь стояло «пингом в `docs/PROGRESS.md`» — это чужая зона, и шапка журнала фронта фиксирует решение владельца 02.08 «в `docs/PROGRESS.md` оркестратора не пишут». Канал передачи — журнал своей зоны; оркестратор пишет туда же. - Ф-16, Ф-17, Ф-18, Ф-20 — **не трогать**: у каждой в бэклоге стоит свой триггер, и ни один из них не наступил. ## Ловушки, купленные S2 — не переоткрывать - **`react-aria-components`:** `Tab` не принимает клавиатурных обработчиков (обработчик живёт на ряду вкладок) · у роли `tab` дети презентационные, поэтому крестик закрытия — не кнопка (axe роняет `nested-interactive`), закрытие с клавиатуры — `Delete`, объявить его нечем (Ф-17) · **невыбранная `TabPanel` не рендерится вовсе** (`dist/private/Tabs.mjs:276`), отсюда потеря прокрутки при переключении вкладки (Ф-19, замерено дважды) · `Tree` — это `treegrid`/`row`, а не `treeitem`; раскрытие — `