Remove worked-off S3, S3.5 and S3.7 prompt copies from the frontend zone, archived in docs/archive/prompts with outcome banners

This commit is contained in:
heaven 2026-08-14 03:45:36 +03:00
parent 105dd6da08
commit 04396b90f1
3 changed files with 0 additions and 812 deletions

View file

@ -1,241 +0,0 @@
# Промт: фронт-сессия S3.5 — фикс-пак оболочки (гейт S4)
Ты — фронтенд-сессия TextMachine, шестая по счёту. Зона записи — **только `frontend/`**; сессия
НЕ коммитит — дерево готовит и передаёт на лендинг оркестратору. Протокол зоны — стоящий промт
`FRONTEND_SESSION_PROMPT.md` §«Зона и git», он в силе целиком; итоги, пинги и вопросы владельцу —
ТОЛЬКО зонный журнал `frontend-PROGRESS.md``docs/PROGRESS.md` фронт не пишет). В дереве живут
незакоммиченные файлы ДРУГИХ зон (полигон: `eval/*`, `docs/experiments/22*|23*`) — не трогать
и не «прибирать».
> Промт написан оркестратором №16 (09.08.2026) из **24 замечаний владельца**, снятых с живого
> прогона витрины. Замечания пронумерованы номерами владельца — **(1)…(24)**, номера локальны
> этому промту (как Q-номера паков). В отчёте пройтись по ВСЕМ 24: сделано (кадр ДО/ПОСЛЕ) /
> диспозиция с обоснованием / вопрос В-строкой. Молча пропустить номер нельзя.
## Зачем эта сессия (рамка)
Владельцу НЕ нравится текущее качество оболочки, и **S4 гейчен до её починки** (D39.119 п.4).
Скоуп S3.5 зафиксирован там же: **вкладки по VS Code-модели + Ф-19 + полный список владельца**
список ниже и есть. Это фикс-пак ОБОЛОЧКИ И ВИТРИНЫ: настоящие экраны S4S7 (загрузка книги,
механизм подписи банка, содержимое настроек, экспорт) НЕ строить. Общее замечание владельца,
рамка всего пака: модель до сих пор «не понимает связанность между элементами, их визуальную
ценность и не видит картину целиком» — каждый пункт чинить не изолированной заплаткой,
а глядя на весь экран.
## Обязательное чтение до кода (порядок)
1. `FRONTEND_SESSION_PROMPT.md` — стоящий промт: сценарий, жёсткие ограничения, зона и git.
2. Шапка `frontend-PROGRESS.md` + `BACKLOG.md` — пак прямо касается Ф-7, Ф-11, Ф-19, Ф-20, Ф-21,
Ф-30, Ф-32; их диспозиции не переизобретать.
3. `FRONTEND_PLAN.md` §5 — гейты, замеры, скриншот-цикл (что уже стоит и как расширять).
4. Референсы `frontend/references/`: **fleet.png — главный** (пропорции, зазоры, верхняя полоса,
статус-полоса) · **antigravity_add_folder.png** — модал добавления (свежий, докинут владельцем
09.08) · antigravity_chat.png · antigravity_main.png.
5. `docs/product-requirements.md` — продуктовые требования (из реестра, не из памяти).
6. Контракт: зонная копия `frontend/docs/api-contract/openapi.yaml` — формы данных берутся оттуда.
## Инструментальная рамка (обязательна)
- **Скриншот-цикл стоит и остаётся законом:** `npm run shot`, «смотреть на снимок — обязательная
часть цикла» (`scripts/shot.mjs` шапка). Для этого пака статики МАЛО: половина замечаний — про
ИНТЕРАКЦИЮ (клики по вкладкам, драг ручки, модалы, ховеры). Playwright уже в зависимостях
(`playwright` 1.62.1) — писать сценарии интеракций (клик → кадр → проверка состояния) и гонять
их частью самопроверки; долгоживущие — в `scripts/`, replayable, не в /tmp. Вердикт «сделано»
без кадра ДО/ПОСЛЕ не принимается.
- **Сверка с референсом:** `npm run shot --size 1280x764` — вьюпорт fleet.png для прямого
наложения. Замечание (1) дополнительно требует кадра **2560×1440** — владелец смотрит
на 27" 2K, и дефолтные масштабы не понравились именно там.
- Если в окружении установлен плагин **frontend-design** (официальный Anthropic) — он активируется
на фронт-задачах сам; его гайды по типографике/палитре применять, но пины `STACK_DECISIONS.md`
и токен-гейты (одно место для цвета и размера) первичны.
- **Мандат самопроверки:** ревью ИСПОЛНЕНИЕМ своего кода и своих кадров + адверсариальное ревью
диффа (author≠reviewer) в конце — как в S2/S3. `npm run check` и `npm run check:full` зелёные
на сдаче; гейт доступности axe зелёный (⚠ при смене фонов пересчитывать контраст — Ф-11 уже
на пороге WCAG).
## Замечания владельца
### Вкладки — модель VS Code (2, 3; сюда же Ф-19)
**(2) Спам вкладками.** Любой клик по главе плодит вкладку по центру. Сделать ровно модель
VS Code для текстовых файлов:
- одиночный клик по главе → **preview-вкладка** (одна на панель, заголовок курсивом);
- одиночный клик по другой главе **замещает содержимое** preview-вкладки — новая не создаётся;
- **пин** (превращение в постоянную): двойной клик по главе, двойной клик по самой вкладке,
либо содержательное взаимодействие с открытым содержимым (в VS Code — правка; что считать
содержательным у нас — реши и запиши в отчёт; простая прокрутка пинить не должна);
- закрытие — как сейчас (крестик/Delete, Ф-17 в силе).
**Ф-19 чинится здесь же:** переключение вкладки размонтирует панель и теряет прокрутку
(`react-aria-components` не рендерит невыбранную TabPanel), пересборка 2284 узлов стоит
70140 мс. Строка откладывала лекарство «до решения об устройстве оболочки» — это решение
принимается СЕЙЧАС, под задачу: keep-alive панелей или запоминание офсета на вкладку — выбрать
по замеру и записать почему.
Приёмка playwright-сценарием: пять одиночных кликов по разным главам → в ряду ровно ОДНА
preview-вкладка; двойной клик → пин, следующий одиночный клик открывает НОВУЮ preview рядом;
на `/scale` прокрутка дерева до 9000 → уход на другую вкладку → возврат → офсет жив.
**(3) Вид вкладок.** Слишком мелкие, «не отделимы для глаза», слиты с панелью. Вписать вкладки
в панель аккуратнее: активная читается мгновенно (фон/подчёрк/контраст), высота и отступы —
по референсу, а не меньше. Это не «скопировать Fleet ещё точнее», а передать аккуратнее — см. (14).
### Геометрия, масштабы, зазоры (1, 5)
**(1) Дефолтные масштабы великоваты**, особенно на 27" 2K (на 24" терпимее). Ориентир — пропорции
fleet.png ОТНОСИТЕЛЬНО кадра: доли панелей, высоты строк дерева/списков, плотность элементов
внутри панелей («элементы как будто не тот масштаб имеют»). Работать ЗАМЕРОМ — наложение кадра
1280×764 на референс, измеренные доли/пиксели в `FRONTEND_PLAN.md` §5, — не на глаз. Все размеры
через `tokens.css` (токен-гейт Ф-4 стоит и обязателен).
**(5) Выравнивание панелей, зазоров и статус-полосы.** «Особенно заметно по тексту снизу» —
нижняя статус-полоса: базовые линии, вертикальные центры, отступы от краёв — сверить с Fleet
попиксельно; у них аккуратнее.
### Кнопки, иконки, настройки (4, 17, 19, 11)
**(4) Две кнопки поиска, делают одно и то же, лупа в непонятном месте.** У Fleet роли РАЗНЫЕ:
вкладка «Search» в левой панели — полнотекстовый поиск по проекту; лупа в правом верхнем углу —
глобальная палитра «Goto / Search everywhere». Развести роли осмысленно для читалки (поиск по
книге ≠ быстрый переход к главе/термину) либо оставить одну кнопку; кнопка без действия
запрещена (норма Ф-7/Ф-16).
**(17) Иконки «шакальные»** — особенно тогглы левой/правой панели. Разобраться с причиной,
а не перерисовать вслепую: размер сетки иконки vs размер отрисовки, выравнивание на пиксельную
сетку, толщина штриха на тёмном фоне, `shape-rendering`. Туда же — «текст рендерится странновато,
особенно внизу»: проверить сглаживание, вес и размер шрифта статус-полосы (дробные px после
масштабирования дают мыло). Показать кадрами ДО/ПОСЛЕ с зумом.
**(19)+(11) Настройки.** Шестерёнку — в правый верхний угол (как у Fleet), из нижней части
убрать. Открываются НЕ вкладкой — **модальным окном поверх оболочки** (по примеру Claude Code /
ChatGPT). Внутри — честный каркас разделов без содержимого (содержимое — S7), включая два
будущих пункта из (15): светлая тема, язык интерфейса.
### Драг панелей (18; сюда же половина 14)
**(18а) Баг:** фиолетовая полоска ресайза ЗАЛИПАЕТ после отпускания драга. **(18б) Форма:**
полоску сделать тонкой и вписанной ВНУТРИ гепа между окнами — она не должна перекрывать чёрный
геп (владелец: «чёрный фон между окнами не трогать, он идеален» — (14)).
Референс-реализация по слову владельца — проект vojo, скопировать ПОДХОД оттуда:
- `/home/ubuntu/projects/vojo/src/app/components/page/style.css.ts:11-70` — ручка: невидимая
зона захвата 7px поверх зазора; индикатор — пилюля 2×36px по центру, `opacity: 0` в покое,
ховер 0.25, драг — подсветка акцентом и рост до 48px; на упоре min сплющивается, на упоре
max вытягивается («лимит чувствуется тактильно»); переходы 140ms.
- `/home/ubuntu/projects/vojo/src/app/components/page/Page.tsx:191-300` — механика: pointer
capture на ручке; живая ширина в состоянии компонента, персист одним махом на pointerup
(не на каждый move); cleanup-эффект снимает `cursor`/`user-select` с body при unmount
посреди драга — ЭТО и есть лекарство от залипания (Alt-Tab, смена маршрута, pointerup мимо).
- Правый край — та же схема в `ThreadDrawer.tsx:209-241`.
Копируется подход, не файл: у vojo vanilla-extract и folds, у нас токены и CSS-модули.
### Читалка (9, 10, 24)
**(9) Оригинал/перевод разделить мягко визуально.** Сейчас колонки различимы слабо. Очень
деликатно: фон на полтона, тонкий разделитель, отступ — один приём, не три сразу. (Приглушение
колонки оригинала — отдельный хвост Ф-7 на S6, здесь не решать, но не противоречить.)
**(10) Серо-синяя линия вдоль текста** — владелец не смог её прочитать. Это полоска ЗАМЕЧАНИЯ
(`src/ui/Callout.tsx:13`, `Callout.module.css`: синяя `attention` / серая `glance` — выноска
по образцу antigravity_chat). Непрочитываемость подтверждает Ф-21 живым владельцем: смысл
различается только цветом. Переделать так, чтобы значение читалось без объяснения — связью
с замечанием (иконка/счётчик у блока, ховер подсвечивает пару «полоска ↔ текст замечания»),
НО продуктовое слово ступени не выдумывать — словарь на владельце (В-3/Ф-21).
**(24) Пометка черновика.** Пока глава переводится/черновая — мягкая пометка (иконка/ворнинг
с тултипом): «черновой вариант, может быть перегенерирован» — чтобы человек не пугался смены
названий и числа глав (research/27: пере-нарезка легальна). Форму брать из данных контракта
(статусы главы/прогона по спеке); поля не выдумывать — класс ошибки «фикстура выдумала форму»
уже ловился четырежды (Ф-14).
### Вкладка «Замечания» (8)
Владелец: непонятно зачем — всё рисуется в тексте, статусы есть в дереве; «лишнее. Если хочешь
защитить этот дизайн — обоснуй». Два легальных исхода: (а) снять вкладку; (б) защитить —
обоснованием И кадром сценария, который без неё не живёт (кандидат: сводный список по книге,
Ф-32 — пер-книжное чтение замечаний уже есть в памяти клиента). Решение — диспозицией в отчёте,
финальное слово — владельцу.
### Банк памяти (7, 13, 20)
**(7)+(13) Представление банка перепридумать.** Текущее «прям совсем не годится: не
репрезентативно, непонятно, нечитаемо». Больше информации на термин и читаемо для верификации:
оригинал · перевод · тип · род/пол · описание/обоснование · варианты · статус решения · откуда
термин (глава появления); сведения, раскрывающие БУДУЩИЕ события книги, — под спойлер. Поля
брать из контракта (`BankEntry`: `type`/`sense`/`since_ch`/`source`, трёхзначный статус); чего
в контракте нет (пол/род? варианты? спойлер-граница?) — не выдумывать в фикстуре молча,
а ПРЕДЛОЖИТЬ черновиком правки спеки с пометкой «предложено» (право и обязанность зоны —
D39.99 п.4; ратифицирует оркестратор).
**(20) «Подписать банк» и «подписано 10 из 60» непонятны:** что подписываем — банк или термины?
зачем счётчик? кнопка открывает вкладку-заглушку. Механизм подписи = S5, здесь НЕ строить.
В S3.5 — честная витрина: непонятные кнопки-заглушки снять (кнопка без действия запрещена)
либо заменить читаемым состоянием («ждут решения: 50»), формулировки — по словарю подписи
контракта (auto/draft/approved), без выдуманных глаголов.
### Библиотека и метаданные (6, 12, 21, 22, 23)
**(6) «О книге» — метаданных больше, но подходы охранять.** Вытащить больше информации о книге,
при этом НИЧЕГО, что раскрывает принципы/логику работы бэкенда: деньги на экран не выносим
(§4.8 стоящего промта), модели/routing/внутренности пайплайна закрыты разрешающим списком
контракта — это осознанная защита подходов, не пробел. Легально из контракта: язык пары, объём,
главы/юниты, прогресс пофазно, статус, даты, замечания счётчиком.
**(12) Добавление книги — модальным окном**, по antigravity_add_folder.png (сейчас зачем-то
открывается новая вкладка): модал «добавить книгу» → выбор файла/папки → **поле названия:
автор вводит руками ЛИБО скипает — тогда название даёт авто-парсер** (различать эти два
источника) → слоты будущих пер-книжных настроек (каркасом). Сам аплоад (`POST /books`,
multipart, прогресс) строит S4 — здесь модал, его форма и мок-поведение.
**(21) Цветной статус книги — на ИКОНКЕ книги**, не точкой рядом с надписью.
**(23) Наложение системных надписей на название книги/главы** («готово», «нужна подпись»
перекрывают текст) — «исполнение кривое и некрасивое», сама идея бейджа не отвергнута.
Переделать: бейдж/иконка НЕ поверх названия (справа в ряд, отдельным слоем с гарантией
непересечения — эллипсис названию, фикс-место бейджу).
**(22) — вопрос владельца, ответ вписан оркестратором, сессии тут делать нечего:** «где
генерится оригинал названия главы?» Сейчас — фикстуры мока (`src/mock/book.ts:87-131`,
`src/mock/scale.ts:49-51`). В реальном пайплайне заголовок ПЕРЕВОДА движок рендерит
детерминистически из шаблона пары (`configs/langpacks/zh-ru/heading.txt`: «Глава {n}») и вклеивает
в финальный текст первого юнита, а исходный маркер остаётся телом оригинала; метка в дереве
по контракту — из ДАННЫХ (`heading`/`number`, К-3); чем подписывать главу без заголовка —
открытый В-4 на владельце (Ф-30).
### Цвет и тема (14, 15, 16)
**(14) «Слишком точная копия Fleet».** Подразбить цветовую схему чуть оригинальнее — акценты,
выделения, второстепенные поверхности; **чёрный фон между окнами НЕ трогать** («он идеален»).
Это доводка индивидуальности, не редизайн.
**(16) Фон главных окон «серо-неприятный».** Изучить (websearch) рекомендации по тёмным фонам,
приятным глазу: не чистый нейтральный серый — слегка тонированные тёмные (тёплый или холодный
подтон), не абсолютно чёрное полотно под текстом, контраст текста не на максимум. Предложить
23 варианта КАДРАМИ, выбрать лучший, показать владельцу оставшиеся как альтернативы. Токены —
в `tokens.css`; контраст пересчитать (Ф-11 у порога; axe-гейт остаётся зелёным).
**(15) Светлой темы нет, выбора языка интерфейса нет.** Оба живут в настройках (каркас — п. (11)).
Строить в S3.5 только если дёшево поверх токенов (палитра уже одно место — оценить честно);
иначе — ЧЕСТНАЯ диспозиция: цена, план, строки Ф-N в `BACKLOG.md`. Молчание нелегально.
## Что НЕ делать
- Не строить экраны S4S7: загрузка книги, механизм подписи банка, содержимое настроек, экспорт.
- Не править канон контракта; правки спеки — черновиком «предложено», ратифицирует оркестратор.
- Не трогать чужие зоны и чужие незакоммиченные файлы (полигон жив).
- Продуктовые слова не выдумывать: ступень замечания (В-3), фраза паузы (В-6), подпись главы
без заголовка (В-4) — на владельце.
- Тесты и гейты не подгонять под зелень (D39.121): красный гейт — вопрос оркестратору
через журнал, не правка гейта.
- Никакого `git add -A`/`git commit` — дерево передаётся на лендинг как есть.
## Отчёт и приёмка
- Таблица по всем 24 номерам: номер → что сделано → кадр/замер (ДО/ПОСЛЕ) → либо диспозиция /
В-строка. Прогон по номерам — в зонный журнал `frontend-PROGRESS.md`, хвосты — `BACKLOG.md`
строками Ф-N, вопросы владельцу — В-строками.
- `npm run check` и `npm run check:full` зелёные; кадры в `.shots/`; наложение 1280×764
на fleet.png и кадр 2560×1440 — приложены.
- Адверсариальное ревью диффа (author≠reviewer) исполнено, находки с диспозициями — в отчёт.
- Промт S4 не писать — его выдаёт оркестратор после приёмки этого пака.

View file

@ -1,235 +0,0 @@
# Промт: фронт-сессия S3.7 — долги слоя данных и мелкие хвосты (до S4)
Ты — фронтенд-сессия TextMachine, восьмая по счёту. Зона записи — **только `frontend/`**; сессия
НЕ коммитит — дерево готовит и передаёт на лендинг оркестратору. Протокол зоны — стоящий промт
`FRONTEND_SESSION_PROMPT.md` §«Зона и git», он в силе целиком; итоги, пинги и вопросы владельцу —
ТОЛЬКО зонный журнал `frontend-PROGRESS.md``docs/PROGRESS.md` фронт не пишет). В дереве живут
незакоммиченные файлы ДРУГИХ зон (полигон: `eval/*`, `docs/*`) — не трогать и не «прибирать»;
у полигона прямо сейчас идёт ЖИВОЙ платный прогон — ничего в `eval/` и `~/books/` не запускать,
не читать замки, не убивать процессы.
> Промт написан оркестратором (10.08.2026) по приёмке S3.6. Контекст холодному исполнителю:
> оболочка и витрина построены сессиями S0S3.6 и приняты владельцем (три круга замечаний);
> слой данных стоит с S3 — `src/api/` разговаривает настоящим HTTP через MSW-мок сети,
> живой поток идёт по SSE (`src/showcase/useRunStream.ts`), чтения — TanStack Query
> (`src/api/queries.ts`), контракт — зонная копия `frontend/docs/api-contract/openapi.yaml`
> (0.2.1), типы генерятся из неё. Эта сессия НЕ строит новых экранов: она закрывает названный
> техдолг слоя данных (Ф-49…Ф-51), одну ратифицированную правку контракта (Ф-47),
> интернационализацию строк интерфейса (Ф-35: словарь + гейт, без хардкода русского)
> и мелкие хвосты. S4 стартует после неё отдельным промтом.
> По всем задачам — прогон в отчёте;
> **молча пропустить задачу нельзя**: сделано / диспозиция с обоснованием / вопрос В-строкой.
## Обязательное чтение до кода (порядок)
1. `FRONTEND_SESSION_PROMPT.md` — стоящий промт: зона, git, жёсткие ограничения.
2. `frontend-PROGRESS.md`: шапка «Текущее состояние», затем **приёмочная запись оркестратора
10.08 (первая в хронике)** — в записи S3.6 есть число-дрейф против финального дерева,
и поправки перечислены именно там; затем сама запись S3.6 (⚠ её разделы 1215 стоят
в ОБРАТНОМ порядке после §11). `BACKLOG.md` строки Ф-40, Ф-47, Ф-49, Ф-50, Ф-51 —
формулировки не переизобретать, но и не принимать на веру: КАЖДУЮ проверить по коду
до правки.
3. Контракт: зонная копия `frontend/docs/api-contract/openapi.yaml` — §`Unit`,
`EventEnvelope`, `Revision`, `resync_required`, `BankTerm`; поведение ревизий и правило
`>= R` — норматив.
4. `FRONTEND_PLAN.md` §5 — гейты и замеры: что уже стоит и как расширять.
## Задачи
### 1. Ф-49 — текст открытой главы не подписан ни на один сигнал изменения
`keys.units` не инвалидируется нигде: `useRunStream.ts` по кадрам инвалидирует notes/bank/book,
кадр `chapter` патчит лишь СПИСОК глав, а открытые главы живут на keep-alive вкладках и не размонтируются —
читатель, не уводивший фокус из окна, нового текста не увидит вообще. Контракт прямо предписывает
обратное (`openapi.yaml`, `Unit`: «the live "something changed" signal arrives as an event; the
text arrives with a read after the boundary»).
Сделать: инвалидация чтений юнитов открытой главы на кадре `chapter` этой главы И на смене
`status` книги (границы стадий — за всю черновую волну кадр `chapter` может не прийти ни разу,
пока К-10 открыт). Безопасная сторона: лишняя инвалидация дешевле невидимого текста, но
НЕ инвалидировать все главы на каждый кадр — скоуп по ключу главы из кадра.
### 2. Ф-50 — кадр потока, пришедший до появления чтения в кэше, теряется молча
`useRunStream.ts`: обработчики делают `list === undefined ? list : {...}` — в пустой кэш кадр
не пишется, а `acceptFrame` уже сдвинул отметку ревизии; `freshest(undefined, next)` затем
принимает снимок, сделанный ДО кадра. Окно — старт экрана и resync. Рядом второе того же класса:
многостраничное чтение штампует «рваный» список ревизией ПОСЛЕДНЕЙ страницы (`queries.ts`),
хотя контракт предупреждает, что ревизия движется во время пагинации.
Сделать: кадр, не нашедший кэша, обязан оставить след — либо инвалидация ключа, либо отметка
ревизии не сдвигается до успешной записи; для пагинации — ревизия списка не выше МИНИМАЛЬНОЙ
из страниц либо перечитывание при сдвиге ревизии между страницами. Выбор решения обосновать
в журнале ЗАМЕРОМ/тестом, не рассуждением.
### 3. Ф-51 — `resync` сбрасывает один гард из двух
На `resync_required` зовётся `invalidateQueries()` без ключа: данные сохраняются, гард
`dropStaleReads` продолжает сравнивать новый снапшот со СТАРОЙ отметкой — а по контракту resync
выдаётся именно после полной замены, где монотонность ревизии не обещана. Вторым слоем: вызов
без ключа бьёт по чужим скоупам (библиотека, usage, run-options, другие книги).
Сделать: `resetQueries`/`removeQueries` по скоупу КНИГИ + сброс отметок ревизий этой книги
(стриминговая сторона уже сбрасывает — довести симметрию до кэша). Чужие скоупы не трогать.
### 4. Ф-47 — правка контракта: `sense` становится обязательным (РАТИФИЦИРОВАНО)
Решение оркестратора (автор контракта), исполняется этой сессией в зонной копии:
`BankTerm.sense` — в `required`; пустая строка = «различителя нет». Дифф в канон
`docs/architecture/14-api-contract/` переносит оркестратор при лендинге — канон сам не трогать.
Сделать: правка `frontend/docs/api-contract/openapi.yaml``required` + описание правила пустого),
бамп `info.version` 0.2.1 → 0.2.2, `npm run contract` зелёный, `npm run contract:types`
перегенерить `src/api/schema.ts`, убрать костыли `?? ''` по `sense` (проверить грепом ВСЕ места,
не по счёту из строки бэклога — он там завышен), фикстуры мока привести (каждый терм несёт
`sense`, хотя бы пустой строкой).
### 5. Ф-40 — общий модуль стилей поля ввода
`src/ui/TextField.module.css` и `src/ui/FilterField.module.css` держат байт-одинаковые `.input`,
`::placeholder`, `:focus`; отличие — цвет границы в покое. Роли примитивов разные, сливать
примитивы нельзя — общий CSS-модуль поля на оба, отличия остаются у ролей.
### 6. Ф-25 — свод языка комментариев (конвенция владельца 04.08)
Комментарии S1/S2 в ~40 файлах (`api/index.ts`, `showcase/*`, `ui/*`, `tokens/*`, `scripts/*`)
остались русскими; приёмка нашла русские комментарии и в **~14 НОВЫХ файлах S3.6** (например,
`src/showcase/About.tsx`) — вопреки конвенции и уточнению владельца по ходу самой S3.6.
Перевести на английский ОДНИМ механическим проходом, покрыв ОБА множества. Граница жёсткая:
переводятся комментарии, имена тестов и служебные строки; **UI-текст и фикстурная проза остаются
русскими — это продукт, а не код.** Смыслы не менять; комментарий, который при переводе оказался
враньём против кода, — чинить по коду и назвать в журнале.
### 7. Сторожа на то, что мерилось руками
Из таблицы долгов S3.6: зум 125/150/200% и узкая панель замерены руками, сторожа нет. Дёшево
и без новых механизмов: в существующие сценарии добавить прогон одной-двух ключевых проверок
на втором вьюпорте/зуме (выбор — по замеру, что реально ловит регресс раскладки). Если дёшево
не выходит — честная диспозиция в журнале, не молчание.
Отдельно: референс fleet_2.png, на котором стоит палитра S3.6 и категория `toned` в
`src/tokens/tokens.test.ts`, докинут владельцем в `frontend/references/` при приёмке 10.08
(быструю пиксельную пробу якорей оркестратор сделал — совпало; запись в журнале). Прогнать
полный `measure.py` по палитре и вкладкам, записать числа в `FRONTEND_PLAN.md` §5 — замер
становится воспроизводимым; ⚠ PIL для `measure.py` не запинен (см. 8.5) — решается тем же
пунктом.
### 8. Находки приёмки S3.6 (закрыть здесь же)
Приёмочное ревью оркестратора (12 агентов: шесть линз + скептики-верификаторы, author≠reviewer)
подтвердило 12 находок. Ниже те, что закрывает эта сессия; каждая грунтована `file:line`
проверяй по коду, не по списку (строки могли съехать).
**8.1 (HIGH) Замер ширин таблицы банка молча проваливается в скрытой keep-alive вкладке
и не повторяется при показе.** `src/ui/Table.tsx:232` — зависимости эффекта замера
`[frame, named, sample, marks, hasRows]` не знают о видимости вкладки; под
`content-visibility: hidden` (`src/ui/Tabs.module.css`) геометрия нулевая, `read()` не набирает столбцов,
10 кадров ретрая и молчаливый `return`. Сценарий: открыть книгу, уйти с вкладки «Банк» ДО прихода
`/bank`, вернуться — столбцы падают в `1fr` без потолков (ровно «баг 4» владельца, объявленный
починенным). Лечение выбрать по замеру (триггер на возврат видимости:
`contentvisibilityautostatechange` / ResizeObserver / зависимость от выбранной вкладки);
приёмка — playwright-сценарий, воспроизводящий уход-возврат, и падающий на до-фиксном коде тест.
**8.2 Вторичный текст и индикаторы теряют контраст на новом синем `--color-selected`.**
Выбранная строка дерева — ПОСТОЯННОЕ состояние (открытый раздел всегда выбран), а содержимое
не перекрашивается: на #1c4478 лежат текст 12px #9b9b9b (3.52:1 при пороге 4.5), иконка #707070
(1.98:1), дорожка прогресса #262626 (1.55:1) — `src/ui/Tree.module.css:46` красит только фон,
`src/showcase/Library.module.css` содержимое не трогает. Починить пары на selected-фоне, не уезжая от
референса; axe этого НЕ ловит (non-text не проверяет, выбранную строку со счётчиком не видел) —
добавить сторож в сцену.
**8.3 Путь из «Замечаний» не загарден: `open` по id, отсутствующему в `chapters`.**
`src/showcase/Showcase.tsx:186` + `src/showcase/Notes.tsx:53-54` — строка сводки с определённым, но отсутствующим id
рисуется кликабельной; `Tabs` контролируемый (авто-фолбэка react-stately на первую вкладку НЕТ),
итог — пустой центр без empty-состояния. Гардить как Library-путь (`positionOf`), сироту
помечать и в строке сводки (сейчас `data-orphan` только при `chapter_id === undefined`).
**8.4 Съёмка нестабильна — два источника гонки, лечить оба.**
(а) `src/showcase/useScreenshotFlag.ts:22` — на первом рендере `useIsFetching() === 0` и экран
объявляется `ready` ДО старта первого запроса (нотификация батчится в отдельную задачу); гейт
`scripts/shot.mjs` ждёт ровно `dataset.screen`. (б) Поймано исполнением на приёмке: axe стартует во время 220мс-анимации
карточки ожидания — один прогон `npm run shot` напечатал «контраст: 4 узла» на `/loading`,
два повторных — ноль. Стабилизировать цикл: убрать транзиент `ready`, дожидаться устаканивания
анимаций до прогона axe.
**8.5 Минорные — одним проходом** (правки на строки, не механизмы):
- `src/ui/Table.tsx:257` — кэш канвас-замера не инвалидируется, если шрифт так и не загрузился
(отравление ширинами fallback);
- `src/showcase/Document.tsx:33` — пин вкладки срабатывает на ЛЮБОЙ `mouseup` при непустом
глобальном выделении (правый клик, выделение в соседней панели);
- `src/showcase/Showcase.tsx:59``awaiting()` на tracked-результатах + `useIsFetching` даёт
полный ре-рендер экрана на каждом префетче; чинить ТОЛЬКО с замером до/после, иначе не трогать;
- `src/api/vocabulary.ts:112``termStatus.label` («предложен»/«черновик»/«подписан») не
рендерится ни одним экраном: снять либо применить; продуктовые слова не выдумывать;
- `src/tokens/tokens.css:27` — в комментарии `--color-text-muted` число 3.9:1, фактически 3.58:1;
`tokens.css:33``--color-note` 2.98:1 на панели при пороге 3:1 для non-text (добрать);
`tokens.css:68` — комментарий `--row-height-term` ссылается на удалённый двухстрочный список;
- `src/showcase/Status.module.css:8``.paused`/`.offline` стали no-op (цвет совпал с базой):
вернуть отличие либо снять классы;
- `src/ui/Button.module.css:36` — подпись disabled-кнопки ТЕКСТОМ `--color-text-muted` против
контракта токена «non-text only» (WCAG disabled освобождает — противоречие контракту токена,
привести одно к другому);
- `scripts/scenes.mjs:170` — сторож обрезки вкладок молча зеленеет, если заголовок вкладки
перестанет быть `span`;
- `scripts/measure.py:13` — PIL нигде не запинен: манифест окружения для `scripts/` либо честная
шапка «чем ставить».
**8.6 Доки зоны — привести к коду** (правится СВОЁ; записи журнала S3.6 не переписывать —
поправки к ним уже аннотированы приёмочной записью оркестратора в журнале):
- `FRONTEND_PLAN.md` §5.6 описывает СНЯТУЮ модель вкладок (вырез в хроме) как текущую — в коде
пилюля по fleet.png (возврат разделом 13 записи S3.6); §4 говорит «сценариев пять» — их семь;
- `frontend/README.md` — вписать S3.6 (и эту сессию) в карту сессий;
- `BACKLOG.md` Ф-47 — «`?? ''` в трёх местах» завышено: вхождение одно
(`src/showcase/Bank.tsx:166`); поправить при закрытии строки.
### 9. Ф-35 — строки интерфейса без хардкода: словарь + гейт
Сегодня ~90 русских литералов живут прямо в разметке (Ф-35); локаль примитивов уже сведена
в одно место (`src/ui/Locale.tsx`, `interfaceLocale`). Сделать интерфейс переводимым
МЕХАНИЗМОМ, не переводом:
- все строки интерфейса уезжают в КАТАЛОГ сообщений, механизм — ОТРАСЛЕВОЙ i18n-инструмент,
не самописная поделка: компонент берёт строку по ключу, локаль = данные, новый язык
добавляется файлом перевода без захода в компоненты. Кандидатов сравнить и выбор обосновать
в журнале (у react-aria уже есть свой слой `@internationalized/string`; react-intl / lingui —
альтернативы); новая зависимость — с точным пином и записью в `STACK_DECISIONS.md` §3,
как того требует политика пинов; цену бандла замерить;
- **гейт против нового хардкода:** кириллический литерал в JSX/разметке = ошибка линта
(исключения — модуль словаря и фикстуры); гейт проверить ЖИВЫМ нарушением, как Ф-4/Ф-9;
- хранилище выбора языка подключить к словарю; каркасный пункт настроек «язык интерфейса»
уже есть — честное состояние «сейчас интерфейс только русский» остаётся, пока локаль
содержимого одна (ru);
- границы прежние: фикстурная проза (тексты мок-книг, названия глав, термины банка) — ДАННЫЕ,
не интерфейс, в словарь не уезжает; продуктовый список языков — решение владельца (если
нужен ответ сейчас — В-строкой); переводы строк на другие языки НЕ выдумывать.
Приёмка: гейт ловит подсунутый литерал (показать нарушением); кадры семи маршрутов
не изменились — это переезд строк, не перевод.
## Что НЕ делать
- Не строить экраны S4S7 (загрузка книги, механизм подписи, содержимое настроек, экспорт).
- Не трогать канон контракта `docs/architecture/14-api-contract/` — только зонную копию (задача 4).
- Поля банка из черновика S3.5/S3.6 (`note`, `gender`, `contexts`…) НЕ заводить ни в спеку,
ни в фикстуры — черновик ждёт ратификации (граница ПТ-33 — на владельце).
- Продуктовые слова не выдумывать: В-3 (ступень замечания), В-4 (глава без заголовка),
В-6 (фраза паузы), В-8 (слово состояния в дереве) — на владельце.
- Тесты и гейты не подгонять под зелень (D39.121): красный гейт — вопрос оркестратору
через журнал, не правка гейта.
- Никакого `git add`/`git commit` — дерево передаётся на лендинг как есть.
- Ф-48: если по ходу покажется, что пора вернуть ресайз столбцов — НЕ возвращать; строка
требует свежего замера от новой базы, это не задача пака.
## Отчёт и приёмка
- Каждая задача 13: ТЕСТ, падающий на до-фиксном коде (vitest поверх MSW; поток мокается
событиями), и зелёный после; в журнал — что именно ловит тест и как воспроизводился дефект.
- `npm run check` и `npm run check:full` зелёные на сдаче; счётчик тестов вырос, ни одна
существующая проверка не ослаблена.
- **Мандат самопроверки:** ревью ИСПОЛНЕНИЕМ своего кода, своих кадров и своих сценариев +
адверсариальное ревью диффа (author≠reviewer) в конце; находки с диспозициями — в журнал.
- Прогон по всем задачам таблицей в `frontend-PROGRESS.md`: задача → что сделано → тест/замер →
либо диспозиция. Хвосты — строками Ф-N в `BACKLOG.md` (Ф-49/50/51/40/47 закрыть или
переформулировать честно; Ф-35 и Ф-25 закрываются в своей код-половине, продуктовая половина
Ф-35 «какие языки» остаётся на владельце), вопросы владельцу — В-строками.
- Промт S4 не писать — его выдаёт оркестратор после приёмки этого пака.

View file

@ -1,336 +0,0 @@
> ⚠⚠ **ПРОМТ ОТРАБОТАН — исторический, НЕ задание.** Исполнен двумя фронт-сессиями (04.08 и 08.08),
> **ПРИНЯТ и заленден D39.115** (`60595f6`): контракт **0.2.0** в каноне, канон и зонная копия
> байт-равны, блоки компаньона 2.142.16 внесены, слой данных построен. **Список «Правки спеки»
> ниже ИСПОЛНЕН целиком — не исполнять заново: спека уже 0.2.0, и повторная правка откатит
> ратифицированный контракт.** Живое состояние зоны — `frontend-PROGRESS.md`; хвосты — `BACKLOG.md`
> (Ф-29 · Ф-30 · Ф-31/К-13 · Ф-32); следующий промт зоны (S4) выдаёт оркестратор по слову владельца.
> Архивная копия — `docs/archive/prompts/FRONTEND_S3_SESSION_PROMPT_2026-08-04.md`.
# Промт: фронт-сессия 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-модулей.
- Правки S2 и дофикс по ревью ЗАЛЕНДЕНЫ (`02872b6`) — восстанавливать нечего, дерево фронта чистое.
В дереве могут лежать незакоммиченные файлы ДРУГИХ зон (параллельные сессии — норма): не трогать
и не «прибирать».
## Замок снят (оркестратор №14, 05.08)
Контракт существует и ратифицирован (`docs/architecture/14-api-contract/`, D39.99), строка 95
закрыта, вторая сторона контракта — платформа — уже написана и принята: её скелет живёт в
`platform/` и ратифицированные ответы платформы вошли в список правок спеки ниже. Раздел «Если
замок закрыт» оставлен как исторический — исполнять его НЕ надо. Один сохраняющийся запрет:
не начинать «пока на своих типах, потом поправим» — моки, снятые не с того контракта, разойдутся
с 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` — открой и посмотри до первой правки.
## Правки спеки — ПЕРВЫЙ шаг S3 (список актуализирован оркестратором №15, 07.08: добавлены В-трис и В-кватер)
Контракт ратифицирован. Спеку правишь ты: минорный бамп версии, регенерация типов, батарея,
компаньон-К-таблица получает статусы. **Ниже — ПОЛНЫЙ список; он вырос с 04.08**, потому что
платформа ответила на свои К-вопросы и владелец сменил модель лимитов. Порядок правок твой,
но ни одна не «на потом»: спека — нормативная поверхность, и на ней уже стоит код платформы.
**А-нуль. Шапка `info` спеки уже исправлена оркестратором №15 (07.08) — НЕ возвращай её обратно.**
Три дня после ратификации файл называл себя «DRAFT of frontend session S3 — not a ratified
contract» и ссылался на компаньон по пути, которого рядом с каноном нет. Сейчас там сказана
правда: ратифицирован D39.99, канон в `docs/architecture/14-api-contract/`, зонная копия —
байт-зеркало, компаньон = `README.md` рядом с каноном. **Твоё в этом пункте одно:** строка
`version` всё ещё `0.1.0-draft` — бампни её первым же действием (минорный бамп, как и сказано
ниже), и байт-сверку копий из пункта Д делай уже с этой шапкой.
**А. Решения владельца 04.08 (D39.100).**
1. **К-1:** десять статусов приняты — статусов больше не трогать.
2. **К-8:** `BookStatus` получает 11-е значение **`paused`** (резюмируемый стоп; в `failed` не
мапить — спека это уже запрещает). Оповещение «перевод остановлен: лимиты исчерпаны».
3. **К-5:** `eta_seconds` в прогресс-модель (движок отдаёт, `status.go:112`; поле опциональное).
4. **К-3:** метка главы — ИЗ ДАННЫХ книги; зашитых форм «Глава N» не существует, легальна книга
без номеров и глав вовсе. Проверь спеку И фикстуры на зашитую форму.
**Б. Ратифицированные ответы платформы. Это уже НЕ вопросы — это форма, которую спека обязана
выразить.**
> ⚠ Ссылки на `platform/docs/*` ниже и в разделе Г — **происхождение, а не задание.** Всё, что тебе
> нужно, приведено в этом промте целиком. В зону платформы ходить НЕ надо: там прямо сейчас живая
> сессия, её документы движутся, и промт, который зависит от чужого живого файла, ломается ровно в
> тот момент, когда сосед его правит. Если всё же полезешь — только читать, и знай, что читаешь
> черновик.
5. **К-4 (ревизия):** счётчик пер-КНИЖНЫЙ, одно и то же число несут все книго-скоупные чтения и
`id` кадра потока. Прозой в спеку: ревизия монотонна В ПРЕДЕЛАХ скоупа и между скоупами не
сравнивается · отбрасывание устаревшего чтения — обязанность клиента · докачка по
`Last-Event-ID` читает **`revision >= R`**, не `>` (одна транзакция = одна ревизия, но
НЕСКОЛЬКО кадров; строгое «больше» теряет кадры-братья) · после транзакции ПОЛНОЙ ЗАМЕНЫ
(банк пересобирается целиком, пере-чанковка заменяет главы) сервер обязан выдать
`resync_required`: дельта-чтение удаления выразить не может.
6. **К-7 (пагинация):** keyset-курсор на КАЖДОМ списочном ответе, `?limit=&cursor=`,
`next_cursor: string|null` присутствует ВСЕГДА (добавить позже — значит молча отрезать хвост
у клиента, который поля не читает). Дефолты: главы 5000 · банк 1000 · замечания 500.
Курсор привязан к СТРУКТУРНОЙ эпохе (поколение манифеста / `chunker_version`), **НЕ к
ревизии книги**: ревизия бампается на каждой материализации, и привязка к ней даёт вечный
рестарт пагинации во время прогона на книге в 5000 глав. Отклонение протухшего курсора —
обязанность СЕРВЕРА (MUST), клиент её исполнить не может: курсор непрозрачен.
7. **К-12 (экспорт):** опрос, а не пуш. `202` + `Location`; статус-чтение отдаёт `ready:false`
с заголовком **`Retry-After`**, объявленным ЯВНО как заголовок этого 200-ответа (RFC 9110
определяет его для 503 и 3xx — на 200 общая семантика не действует, поэтому объявляем сами).
8. **CSRF:** браузерный клиент обязан слать заголовок **`X-TM-Client`** на небезопасных запросах
cookie-пути. Место — описание `sessionCookie`. Несущим является ПРИСУТСТВИЕ заголовка,
значение любое: не выдумывай токенную семантику.
9. **`Run.paused_reason`** — машинная причина паузы либо `null`; фразу рисует клиент, API несёт
состояние. **Значение ровно одно и оно уже занято платформой: `credit_exhausted`.** (В редакции
этого промта от 05.08 стояло `limits_exhausted` — ошибка оркестратора, значение должно совпадать
с тем, что пишет платформа, иначе клиент не опознает причину.)
**В. Решение владельца 05.08 — модель лимитов СМЕНИЛАСЬ.** Не подписка с окнами, а **БАЛАНС
кредитов** («покупка токенов как у OpenRouter»). Следствия для спеки:
10. Окон и `resets_at` НЕТ. Экран показывает ОСТАТОК, а не «сбросится через». Денежные СУММЫ
по-прежнему на провод не идут (D39.84) — процент остатка, не доллары.
11. Прежняя инструкция этого промта «API лимитов проектирует платформа, в спеку не выдумывать»
ОТМЕНЕНА: форма ответила (`GET /v0/usage`), и её надо выразить — но БЕЗ полей окон. Форма
приведена ЗДЕСЬ целиком, ходить за ней в чужую зону не нужно и не надо (промт, зависящий от
живого документа соседней зоны, ломается ровно тогда, когда сосед его правит):
```yaml
Usage:
required: [state, remaining_percent]
properties:
state: { enum: [ok, low, exhausted] } # low — порог показа предупреждения
remaining_percent: { type: integer, minimum: 0, maximum: 100 }
paused_reason: { enum: [credit_exhausted], nullable: true }
```
Процент считается от суммы грантов аккаунта, не от «лимита периода»: периодов больше нет.
`Run.paused_reason` несёт то же значение на прогоне. Сумм нет ни в каком виде (D39.84).
(В редакции 05.08 этот пункт слал за схемой в `PLATFORM_DIRECTION.md` §2 — там её нет, там
денежная модель; ошибка оркестратора.)
**В-бис. Поверхность входа — появилась в P1, в спеке её нет** (добавлено оркестратором 05.08 после
приёмки платформы P1; в редакции этого промта от 05.08 утра этого пункта не было, потому что кода
входа ещё не существовало).
Платформа построила OIDC-вход. Четыре ручки живут ВНЕ версионного префикса, как `/healthz`, потому
что это механика сессии, а не контрактная поверхность:
| Ручка | Метод | Что делает |
|---|---|---|
| `/auth/login` | GET | начинает вход, редиректит к провайдеру; принимает `?return_to=<путь на этом сайте>` |
| `/auth/callback` | GET | завершает вход, ставит сессионную куку, редиректит на `return_to` либо на дефолт |
| `/auth/logout` | POST | завершает ЭТУ сессию |
| `/auth/logout-all` | POST | завершает ВСЕ сессии пользователя («выйти везде») |
15-бис. **Описать их в КОМПАНЬОНЕ, в `openapi.yaml` не тащить** — решение оркестратора как
владельца контракта. Клиенту нужно знать три вещи: куда вести на вход, что `return_to` принимает
только путь этого сайта (сервер молча заменит чужой на дефолт — открытый редирект закрыт), и что
обе `POST`-ручки лежат на cookie-пути, то есть требуют `X-TM-Client` из пункта 8.
Отказ входа — `problem+json`, как везде; различать причины отказа клиент не может и не должен.
**Г. Ограничения скорости, которые спека обязана удержать** (замер и разбор —
`platform/docs/PLATFORM_DIRECTION.md` §4; для тебя это правила, а не предложения):
12. **Юниты читаются только пер-главно.** Пер-книжной ручки юнитов не заводить никогда —
на этом стоит вся память клиента (рабочий набор 27 КБ против ~62 МБ).
13. **Экспорт — артефакт по ссылке;** сборка текста книги на клиенте запрещена явно.
14. **Сервер ВПРАВЕ склеивать события прогресса**, клиент обязан терпеть скачки счётчиков —
записать в описания событий (иначе прогон на 9500 юнитов = неограниченный источник рендера).
15. **Заголовок главы ограничен по длине на сервере** — единственная неограниченная строка в
списке из 5000 строк.
**В-трис. Управляемый потолок прогона — РЕШЕНИЕ ВЛАДЕЛЬЦА 07.08, поверхности в спеке нет**
(добавлено оркестратором №15 по приёмке платформы P2, D39.109/D39.110; в редакции 05.08 этого
пункта не было — решения не существовало).
Проблема, которую это решает: перевод книги стоит денег, и человек должен уметь сказать «не
больше вот столько» ДО старта, а не узнавать пост-фактум. Сегодня потолок живёт только в конфиге
книги на стороне движка, и управлять им нельзя вообще.
Форма, ратифицированная владельцем: **шкала в интерфейсе от минимума до максимума**, ноль выбрать
НЕЛЬЗЯ (подсвечивается серым — прогон с нулевым потолком не начнётся), виджет выбираешь сам
(ползунок — предложение владельца, не мандат). Три вещи в этой форме — «делай РОВНО так», потому
что каждая куплена проверкой:
16. **Единица шкалы — ГЛАВЫ, а не доллары.** Не «поставить $3», а «перевести до N глав». Причина
жёсткая: D39.84 запрещает денежные суммы на пользовательском проводе и экране, и П-5 показывает
процент ровно поэтому. Доллары на этой шкале = амендмент D39.84, а он не выдан. Побочная
выгода: минимум шкалы объясняет себя сам — одна глава.
17. **Максимум шкалы = ДОСТУПНЫЙ остаток.****Первая редакция этого пункта говорила «баланс МИНУС открытые холды» — ошибка оркестратора, вычитание дважды; исправлено 08.08 по находке самой S3 (D39.115).** Холд — дебет в момент взятия, поэтому баланс уже без него.
Платформа берёт холд на сумму потолка ДО спавна прогона, поэтому шкала, упёртая в полный
баланс, запирает всё на одну книгу и вторую начать нельзя. Два числа платформа уже различает.
18. **Это потолок ПРОГОНА, не книги.** Он едет со стартом прогона и не является свойством книги;
в спеке ему место рядом с созданием прогона, а не в модели книги.
⚠ Чего в спеке НЕ выражать: пересчёт «глав → деньги». Он живёт на платформе (по оценке движка) и
наружу не выходит ни в каком виде. Поверхность отдаёт границы шкалы и выбранное значение в главах.
**В-кватер. ФРОНТ ЖИВЁТ НА ТОМ ЖЕ ORIGIN, что платформа — сегодня это не выбор, а факт**
(замер приёмки №15 на живом бинаре, строка PD-96 регистра платформы).
CORS-слоя в платформе нет вовсе: preflight `OPTIONS` с чужим `Origin` получает 401 от гарда
сессии, заголовков `Access-Control-*` нет ни на одном ответе. Значит браузерный клиент с другого
origin неработоспособен как класс, а не «требует настройки». Для тебя два следствия: в деве ходи
через прокси dev-сервера на тот же origin (не напрямую на порт платформы), и **не проектируй ничего
вокруг кросс-origin запросов** — если продукт когда-нибудь потребует отдельный домен фронта, это
отдельная работа платформы, и она не сделана. Заголовок `X-TM-Client` (пункт 8) при этом обязателен
и на same-origin: он не про CORS.
**Д. После правок:** байт-сверка зонной спеки с ратифицированной копией
(`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** каким словом называть ступень замечания: владелец 05.08 передал разметку оркестратору
(«пусть работает как работает, потом вернёмся») ⇒ карту «причина → фраза» ты НЕ сочиняешь; до её
выдачи держи сегодняшние формулировки и не расширяй словарь ·
**Ф-11** контраст палитры ниже WCAG на четырёх узлах — принято как есть до слова владельца.