textmachine/frontend/docs/S3_SESSION_PROMPT.md

203 lines
22 KiB
Markdown
Raw 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.

# Промт: фронт-сессия S3 — слой данных (вход под замком)
Ты — фронтенд-сессия TextMachine, четвёртая по счёту. Зона записи — **только `frontend/`**;
сессия не коммитит — лендит оркестратор (протокол — стоящий промт `FRONTEND_SESSION_PROMPT.md`
§«Зона и git», он в силе целиком). Пре-коммит хук стоит и самоустанавливается при `npm install`.
> Промт написан сессией S2 по просьбе владельца — как передача с той точки, где S2 встала.
> Ратифицирует и правит его оркестратор; при расхождении с его хендофф-промтом побеждает тот.
## Входное состояние (04.08, проверено исполнением, а не заявлено)
- **Пройдено S0S2:** план · инструменты и скриншот-цикл · `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<string, …>`).
Значит состояние прогона из будущего контракта даёт `undefined.tone` — падение дерева
библиотеки, а не пустую метку; `noteTone` тем же классом тихо уводит незнакомую ступень
замечания в СПОКОЙНУЮ ветку. **Место ветки — шов `src/api/`, где значение входит**, а не
каждый компонент: перевод «вердикт → вид» уже расползался по экранам, и контрольный вопрос
владельца требует, чтобы новое значение правило один файл.
6. **Поля не выдумывать.** Нет в контракте — это вопрос владельцу/оркестратору в
«Открытые вопросы» журнала зоны, а не догадка в типе. Дефект «фикстура выдумала поле»
в этой зоне случался дважды и оба раза стоил экрана.
**Не в скоупе:** экраны S4S7 · редактор текста · светлая тема · мобильная раскладка ·
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`; раскрытие — `<Button slot="chevron">` · `renderEmptyState` обёрнут
в `role="option"` (зонд S2 принял это за дефект кода).
- **`react-resizable-panels` v4:** `className`/`style` панели уезжают во ВЛОЖЕННЫЙ div, у него
всегда inline `overflow:auto` · `Separator` обязан быть прямым ребёнком `Group` · ключ
хранилища раскладки содержит состав видимых панелей, поэтому сворачивание удалением панели
меняет ключ (сделано осознанно: так поле оболочки остаётся ровно замеренным).
- **Скриншот-цикл:** `vite build` и `preview` в один момент дают пустой экран — это гонка,
а не регрессия; перезапусти прогон, прежде чем «чинить» рабочий код.
- **Зонды и живые нарушения:** файлы зондов удалять за собой (в S2 агенты ревью оставили свои
в чужом дереве), правленые файлы восстанавливать из памяти процесса, **а не `git checkout`**
в дереве могут лежать незакоммиченные правки параллельных сессий.
- **Первому красному результату зонда не верить:** три «находки» первого прогона S2 оказались
дефектами зонда (селектор бил по трём панелям сразу), и соблазн «починить» рабочий код был
вполне реальный.
- **esquery:** селектор вида `Property[key.value=/^--/] > Literal[…]` ловит собственный ключ
свойства — в TSX-гейте это записано комментарием, не убирай его.
## Технические рамки
- Новый маршрут → сразу в `KNOWN_ROUTES` (`scripts/shot.mjs`); тест сверяет списки и упадёт.
- Гейты не ослаблять. Точечное подавление — только именованное правило с причиной.
- Файл = компонент + модуль стилей рядом; >150 строк — делить. Комментарии — одна-две строки
«почему», не «что».
- Тексты фикстур — настоящие пропорции кириллицы и иероглифов, объём минимальный: вопрос
авторского права (В-2) у владельца, цитаты не расширять.
## Как работать
1. **Ревью исполнением — мандат проекта, не пожелание:** приложение поднимается, скриншоты
сняты и ПРОСМОТРЕНЫ, каждый гейт проверен живым нарушением с перечнем форм, `npm run check:full`
зелёный. «Должно работать» ревью не является.
2. **Скриншот-цикл — с первого изменения**, критерий сведения — не растровое совпадение,
а плотность и впечатление (стоящий промт §8).
3. **Адверсариальная самопроверка перед финишем** (author≠reviewer): пройди по своим правкам
с установкой опровергать. S1 так поймала пять дефектов в собственных гейтах, S2 — восемь.
4. **Спорное с каноном или новое продуктовое решение не принимай сам:** вопрос в «Открытые
вопросы к владельцу» журнала зоны, и продолжай то, что вопроса не требует.
## Готово — это когда
- Замок проверен и результат записан: либо S3 сделана, либо в журнале лежит, чем сессия
занималась вместо неё и почему.
- Если S3 сделана: `src/api/` асинхронен, MSW отдаёт все состояния, ветки ожидания/ошибки/пустоты
живые, Ф-15 закрыта, типы сведены с контрактом, а расхождения — не «поправим потом»,
а строки бэклога с триггером.
- `npm run check:full` зелёный; новые маршруты в `shot.mjs`; скриншоты просмотрены.
- `frontend/docs/frontend-PROGRESS.md`: обновлено «Текущее состояние», добавлена хроника сессии
(что построено · что видно на снимках · где отошёл от канона и почему · что осталось);
строки бэклога получили диспозиции.
- Ничего не закоммичено: дерево подготовлено и передано оркестратору.
## Что ждёт не тебя (не решай сам)
**В-1** данные привязаны к одной машине (вопрос на движок/платформу) · **В-2** копирайт фикстур ·
**В-3/Ф-21** каким словом называть ступень замечания (словарь продуктовый, ПТ-33) ·
**Ф-11** контраст палитры ниже WCAG на четырёх узлах — принято как есть до слова владельца.