textmachine/frontend/docs/S4_SESSION_PROMPT.md

254 lines
29 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.

# Промт: фронт-сессия S4 — библиотека, загрузка, разбор, прогресс (+ спек-правка 0.2.3)
> Выдан оркестратором №16 14.08.2026 по D39.130 («гейт S4 пал»). Запуск — по слову владельца.
## 0. Какую проблему решаем и что даст твой результат
Продукт — веб-сервис художественного перевода книг: пользователь загружает книгу, запускает
перевод, подписывает словарь терминов, читает результат. Фронт строится этапами S0S7; пройдено
всё до S3.7 включительно: оболочка трёх панелей по референсу Fleet, слой данных на контракте
OpenAPI (MSW как сетевой мок), i18n-каталог, гейты. Чего до сих пор НЕ существует —
**пути пользователя от «у меня есть файл» до «книга переводится»**: форма добавления книги есть
как вид (модал S3.5 по `antigravity_add_folder.png`), но никуда не отправляет; статусы
`uploading`/`parsing`/`rejected` в контракте есть, а экраном не прожиты; запустить прогон с
экрана нельзя. До сих пор это было честно: серверной ручки загрузки не существовало. 14.08
платформа P5 принята (D39.130) — `POST /books` построен и жив, и вместе с его приёмкой владелец
контракта ратифицировал пакет правок спеки (**0.2.3**), которые вносишь ты. Твой результат =
пользовательский путь загрузки и запуска работает на форме контракта 0.2.3, и следующая сессия
(S5, подпись банка) строится на книге, которую в интерфейсе можно завести, а не только замокать.
Ты — девятая фронт-сессия. Промт — твой ЕДИНСТВЕННЫЙ контекст: ты не видишь чужих переписок;
всё несущее — здесь или в названных доках. Дом фронта остаётся миром моков (MSW по контракту);
живой стенд платформы — только опциональная проба, §10.
## 1. Онбординг (порядок чтения, до кода)
1. `CLAUDE.md` (корень) — гардрейлы проекта; из них жёсткие для тебя: зона записи ТОЛЬКО
`frontend/`, ты НЕ коммитишь, `.env` не читать, чужие незакоммиченные файлы не трогать.
2. `frontend/docs/FRONTEND_SESSION_PROMPT.md` — живой носитель норм зоны: §«Зона и git», §3
сценарий (тебе — §3.13.4), §4 жёсткие ограничения, §5.1 поддерживаемость, §7 «как работать».
Статусные абзацы там частично исторические — статус этапов только в `frontend-PROGRESS.md`.
3. `frontend/docs/frontend-PROGRESS.md` — шапка «Текущее состояние» + раздел «Решения владельца
по продукту» (перевешивает всё в старых промтах) + записи S3.5S3.7 и приёмок.
4. `frontend/docs/BACKLOG.md` (Ф-строки) и `frontend/docs/STACK_DECISIONS.md` (пины — не двигать).
5. Контракт: зонная копия `frontend/docs/api-contract/openapi.yaml` (**0.2.2**, байт-зеркало
канона `docs/architecture/14-api-contract/openapi.yaml`) + компаньон-README рядом с каноном
(провенанс, К-вопросы, §2.15 — потолок прогона).
6. Референсы `frontend/references/*.png` — открой глазами, если трогаешь вид.
**Эхо-протокол:** первый деливерабл — ≤10 строк «что я понял: скоуп / инварианты / не-делать»
ДО работы. Расхождение чинится на минуте пять, не на приёмке.
## 2. Задача 1 — спек-правка 0.2.3 (делай РОВНО так; ратифицировано D39.130 п.2в + ратификация ниже)
Правишь ЗОННУЮ копию `frontend/docs/api-contract/openapi.yaml`, бампаешь `info.version` до
**0.2.3**, перегенерируешь типы (`npm run contract`), spectral зелёный. Канон правит оркестратор
диффом при лендинге — байт-сверку не выравнивай сам, расхождение с каноном на время сессии
легально (прецедент Ф-47/0.2.2). Пять правок:
- **PD-172:** у `createBook` записать проводное правило — часть `file` идёт **последней** в
multipart-форме; поля после файла = 400. Причина (одной строкой в описание): платформа читает
форму потоково и обязана увидеть языки до тела файла.
- **PD-173:** у отклонённой книги появляется **машинная причина** — поле с закрытым enum по
словарю платформы (`source_unreadable` · `not_configured` · `parser_unavailable`; её колонка
`books.reject_reason`, миграция 00013 — сверь имена значений по `platform/` read-only, код
первичен). **Продуктовые фразы различения пишешь ТЫ** (слово владельца 14.08: «пусть сам
придумает, только над хорошими вариантами подумает»). Рамки: фразы должны честно ложиться на
РЕАЛЬНЫЙ флоу отказов (движок отдаёт платформе классы отказа, платформа сводит их в этот enum —
прочитай маппинг read-only, чтобы фраза не врала о причине), при этом НЕ раскрывать внутреннее
устройство (ПТ-33: ни стадий, ни моделей, ни слов вроде «манифест»/«парсер движка»; та же
дисциплина, что у `Problem.title`) и не звучать тревожно (§3.8). Разное следующее действие —
разные фразы: «файл не тот» → проверить файл; «сервис недоступен» → повторить позже. В UI —
через словарь с веткой неизвестного, как все enum; свои фразы вынеси в отчёте отдельным
списком на утверждение владельцем.
- **PD-174:** назвать классы ответов, которые провод уже даёт, а спека молчит: 404 на
`POST /books` у инстанса без интейка; 503 у `resume` на деплое без маркер-команды/шаблона
потолка (0.2.1 ратифицировала 503 только для СТАРТА).
- **PD-180:** описание `createBook` честное: **201 несёт `parsing`**, не «responds immediately …
enters uploading» (`uploading` наблюдаем только параллельным чтением библиотеки); плюс перечень
отказов интейка: 400 (>16 частей формы; текстовое поле >1 КиБ), 413 (потолок тела — порог
принадлежит деплою, числа в спеку не зашивать), **408** (тело не успело за дедлайн маршрута —
медленный клиент, лечение повтором).
- **`title` в `BookIntake`** — необязательное поле: пустое/отсутствующее = «название даст разбор»
(сегодня платформа берёт имя загруженного файла), заполненное = задано вручную и разбор его не
перепишет. **Ратифицировано оркестратором 14.08** как исполнение замечания 12 владельца
(S3.5: «автор вводит название руками ЛИБО скипает — источники различать»; форма это уже
разводит, а отправить рукописное название до сих пор некуда — черновик S3.5 висел без
ратификации). Владелец промт ревьюит — отмена этого пункта одной репликой; дошла отмена
аддендумом → эхо-подтверди и отрази в отчёте.
## 3. Задача 2 — загрузка книги (форма → `createBook`)
Половина Ф-26. Модал «Добавить книгу» отправляет форму через слой `src/api/` (типы `createBook`
уже сгенерены), MSW-хендлер — по форме 0.2.3, включая порядок частей (file последним) и ветки
отказов. Экранный путь по §3.23.3 сценария:
- **Прогресс отправки файла** видимый (книги бывают десятки МиБ). `fetch` прогресса отправки не
даёт — механизм выбери сам (XHR либо иное) и аргументируй в отчёте; слой данных остаётся одним
входом, гейт транспорта (`src/api/**` only) в силе.
- После 201 книга в библиотеке в `parsing`**разбор это отдельный видимый шаг**, не часть
перевода. ⚠ Числового прогресса разбора у контракта НЕТ — процент не выдумывать, честное
состояние «разбирается»; по завершении главы появляются в дереве, статус `not_started`.
- **Отказ (`rejected`) — с причиной** по PD-173 и советом, различимым по классу (файл не тот ≠
сервис недоступен), фразы — как в задаче 1. Отказные ветки (400/408/413/404) — честные
состояния формы, не алерты; тон — §3.8 («если экран выглядит тревожным — он неправильный»).
- Подтверждение успешной загрузки видимое: размер/объём/язык (§3.2) — из данных контракта
(`character_count`, `chapter_count`, языки), не выдумывая полей.
## 4. Задача 3 — запуск прогона с ручкой потолка (остаток строки 126 единого бэклога)
Форма запуска перевода. Ратифицировано (D39.110/115, компаньон §2.15 — не переоткрывать):
- Перед показом формы читается `GET /books/{id}/run-options`; шкала потолка строится из
`CeilingBounds` **в ГЛАВАХ** — никаких долларов, сумм и оценок стоимости на экране (§4.8 в
силе; пересчёт «главы→деньги» — целиком платформа).
- `ceiling_chapters` обязателен, **ноль выбрать нельзя**; предустановка шкалы =
`default_chapters` от платформы (клиент своё значение не выбирает — это продуктовая политика);
`max_chapters: 0` = старт недоступен, честное исчерпанное состояние вместо шкалы.
- Bounds приходят готовыми и уже подрезанными — клиент **не клампит второй раз** и не выводит
свои; между чтением и стартом они могут уехать — **409 на старте** обрабатывается
пере-чтением run-options и честной фразой, не ретраем втихую.
- Галочка «остановиться на подписи банка» (`verify_bank`) — параметр ЭТОГО запуска, не глобальная
настройка (решение владельца 02.08, §4 п.11 промта зоны).
- **`paused` — прожитое состояние прогресса** (ПТ-35: стоп по потолку = статус `paused` +
оповещение). Фраза «остановлена: лимиты» зарезервирована за владельцем (В-3/В-6) — берёшь
существующую из каталога, не переписываешь; механизм оповещения (статус-полоса/бейдж уже несут
состояние — нужен ли отдельный тост) реши сам и аргументируй. После потолочного стопа `resume`
сам по себе прогон не двигает — resume как «продолжить после потолка» не предлагать (описание
`resume` в спеке); различение «свой потолок vs нет кредита» — К-13, на владельце, не решай.
Страница лимитов `GET /usage` остаётся в настройках = S7 (Ф-27) — эту диспозицию не двигай.
-**На ЖИВОЙ платформе потолочный стоп сегодня приезжает `failed`** — потребительская половина
шва (П-15 зоны платформы) ещё не построена, PD-113 открыт. В мире моков строй ПО КОНТРАКТУ
(`paused`, `EventCeiling`); от живого стенда правильного поведения не жди и не подгоняй под
него ни код, ни тесты.
Прогресс во время прогона — по построенному `useRunStream`: пер-главные индикаторы в дереве и
общий прогресс в статус-полосе уже живут на фикстурах (одна доля без названий фаз, ETA из
`eta_seconds` — ратифицировано); доведи их до пути «книга, заведённая формой, переводится» в
мире моков. Внутренности конвейера не раскрывать (§4.1).
## 5. Задача 4 — библиотека на настоящем масштабе
- Хвост Ф-12: дерево глав ПЛОСКОЕ — на 2284 разделах нужна группировка (по сотням или иная —
реши сам по референсу и замеру на `/scale`, аргументируй).
- Все статусы `BookStatus` прожиты в дереве и карточке книги — включая `uploading` (виден
параллельным чтением), `parsing`, `rejected` с причиной. Метки состояния — из словаря с веткой
неизвестного; слова, зарезервированные за владельцем (В-3/В-8), НЕ трогать и не укорачивать.
- Ф-42 (потолок keep-alive вкладок) — по замеру: реши сам, чинить или отложить с записью.
## 6. Задача 5 — находки приёмки S3.7 (все получают диспозицию)
Ревью приёмки: 12 подтверждённых (1 high) + 16 минорных. Из подтверждённых **9 несущих — список
ниже**; остальные 3 — поправки к записи S3.7 (цена бандла, дрейф ширин `hidden`, счёт «106 из
119»), их носители — запись оркестратора 14.08 в журнале и задача 7. Каждой — диспозиция в
записке-плане (починено с тестом / не воспроизводится с замером / отложено со строкой):
1. **(high) Дубль подстановки строк:** запись S3.7 ложно клеймила «заполнение вынесено в
`src/i18n/fill.ts`, его импортируют и приложение, и сцены» — `scenes.mjs` держит собственный
инлайн-филлер `say()` с ДРУГОЙ семантикой пропуска, добавленный самой S3.7. Сведи к одному
носителю.
2. Отметка «перечитывание в пути» ставится без старта чтения и глушит следующие кадры ключа
(`useRunStream.ts:146` на момент приёмки).
3. `onResync` не чистит `asked` — отметки прежней эпохи съедают кадр в окне ресинка (`:42`).
4. Клейм «resync не рвёт живой поток» — принять только по живому инструментированному прогону с
задержкой ответа (статика `resetQueries` говорит обратное).
5. Сцена `hidden`: прекондиция «ушли до прихода банка» не ассертится (гонка с синхронным моком),
сторож дифференциальный — симметричная регрессия замера пройдёт; и «таблица вообще нарисована»
не проверяется (два пустых замера равны).
6. Дырка ESLint-гейта строк: вычисленный литерал (`?? 'Word'`, тернарник) в читаемом атрибуте и
литерал в expression-child проходят — живой экземпляр `Showcase.tsx:134`.
7. Perf-оседание может проходить вакуумно: заходы 25 ждут 300 мс против конвенции 700.
8. Zoom-ассерт «в ряд» не запрещает стопку (сравнивает left с left, не с right).
9. Кадр `status` не перечитывает список глав — по собственной посылке Ф-49 его счётчики на
границе стадии тоже протухают.
Минорные (актуальный остаток свода; часть уже закрыта попутно — сверь и диспозиционируй
коротко): свип языка `catalogue.test.ts` не метёт `package.json` и безрасширенный
`scripts/githooks/pre-commit` · гейт вызовов каталога держится на имени локальной переменной
`text` · шапка `ru.ts` ссылается на несуществующий `src/i18n.test.ts` · perf: удлинение запроса
с 3 до 5 символов само занижает мс/клавишу · защёлка флага съёмки — эвристика окна 150 мс
(знать, не чинить без замера).
`file:line` — ориентиры на момент приёмки, код первичен. Несогласие с находкой = диспозиция с
замером, не молчание (образец: арбитраж 8.1, где права оказалась сессия).
## 7. Задача 6 — гигиена зонных доков и арбитраж двух чисел
Стейлы (аудит-свип 14.08), чинить по факту кода:
- `FRONTEND_PLAN.md:42-43` — «платформа = ноль строк кода» устарело (P5 принята, D39.130);
`:26-30` — таблица §0.1 подаёт строки 95/99103 как «чего ещё нет» (95 ратифицирована D39.99,
99/100/103 построены D39.122/131) и шлёт читать `API_CONTRACT_INPUT.md` как живой вход (он под
баннером «исполнено»); `:207-208` — ссылка на удалённый `src/api/types.ts`.
- `STACK_DECISIONS.md:179` — тулчейн платформы «≥1.26.5» → ратифицирован **1.26.6** (D39.130);
`:144` — состав `npm run check` отстал (4 шага против нынешних 7).
- `FRONTEND_SESSION_PROMPT.md:62` — «скелет P0 ждёт приёмки» → P5 принята и залендена.
- `frontend/README.md:63-66` — зонная копия спеки названа «черновик на ратификацию» — она давно
байт-зеркало ратифицированного канона (D39.99/115).
Арбитраж чисел ЗАМЕРОМ (два носителя врозь, оба претендуют на факт): цена бандла i18n —
запись S3.7 говорит «724130 (+7999)», Ф-35 говорит «723102 (+6971)»; ключей каталога — запись
«147», Ф-35 и живой `ru.ts` — «157» (верно 157). Прогони сборку и счёт ключей на текущем дереве,
живые носители (BACKLOG/план) приведи к замеру; записи хроники НЕ правятся — расхождение назови
попуткой в своей записи.
## 8. Задача 7 — завести Ф-строки (долги без носителя, найдены свипом 14.08)
- **RTL-проверка:** триггер «появление I18nProvider» НАСТУПИЛ в S3.7 — строка с честной ценой.
- **Спойлер достаётся в обход:** размытие глифов не прячет текст от поиска по странице (Ctrl+F)
и печати в PDF — продуктовое слово нужно, строка + В-вопрос владельцу.
- **Канвас-замер ширин не знает `lang`:** при двух CJK-лицах один код-пойнт рендерится
по-разному — строка с условием срабатывания.
Плюс одно закрытие по слову владельца 14.08 — **Ф-52:** поднять `--color-danger` на ступень до
≥3:1 на полотне панели (тот же приём, что у `--color-note` 2.98→3.07 в S3.7 — минимальный сдвиг
тона, не редизайн), пере-прогнать axe, закрыть строку замером.
## 9. На владельце — НЕ решать и не трогать
В-7 (плотность интерфейса; держит Ф-54 — обрезку центра на зуме: ЧИСЛО печатай, не чини) ·
В-8 (слово состояния в дереве) · В-9 (языки интерфейса) · В-4/Ф-30 (глава без заголовка — «на
экране ничего не менять», ждёт и строку 160 движка) · Ф-38 (вкладка «Замечания») · К-13/Ф-31
(различение причин паузы) · В-3/В-6 (фразы ступени и паузы) · граница ПТ-33 (добавочные поля
`BankTerm` — форм не выдумывать). Фразы PD-173 — исключение: их пишешь ты (§2), владелец
утверждает по отчёту. Пины `STACK_DECISIONS` не двигать (Ф-28 — измерено и передано, не патчить).
## 10. Границы скоупа
Подпись банка и его редактирование — S5. Читалка — S6. Экспорт, настройки (вкл. страницу
лимитов), светлая тема — S7. Живого канала банка нет (строка 169 единого — гейт S5).
**Добавление ГЛАВЫ (§3.10 промта зоны) не строить:** ручки в контракте не существует — кнопка
без действия запрещена нормой зоны; счёл нужным — предложи черновиком правки спеки с пингом, не
кодом. Мок-слой остаётся домом фронта (Ф-29 — триггер сноса не наступил); **опционально**, если
локальный стенд платформы поднимется без боя — smoke живого `POST /books` через dev-прокси
(same-origin, В-кватер; переключатель `TM_PLATFORM` дев-сервера уже существует — S3 чинила его
достижимость) с честным отчётом; это НЕ гейт сдачи, со стендом не воюй, и помни каветы §4 про
П-15: живой стенд местами отстаёт от контракта — эталон формы всегда спека, не стенд.
## 11. Самопроверка и сдача (мандат, не пожелание)
- **Ревью исполнением:** каждый экранный путь прогнан живым браузером (скриншот-цикл/сценарии),
каждый новый гейт и тест проверен ЖИВЫМ нарушением, тесты фиксов падают на до-фиксном коде.
Вердикт о поведении браузера без живого прогона = PLAUSIBLE, не факт (D39.129).
- **Адверсариальное ревью диффа — механизмом, author≠reviewer:** независимые ревью-агенты по
диффу с установкой опровергать; субагенты/параллельные агенты для этого **разрешены явно**
дефолт-запреты харнесса на этот пункт не распространяются. Артефакт — таблица
«находка → диспозиция» в записи журнала; «перечитал внимательно сам» исполнением НЕ считается.
- **Предметные оси ревью этого пака** (вправе добавить свою или аргументированно снять):
(1) контракт — каждая форма данных из спеки, новая потребность = черновик правки спеки, не
фикстура (класс «фикстура выдумала форму» ловился четырежды); (2) доступность — axe на новых
экранах/сценах, полный путь загрузки и шкала потолка проходимы клавиатурой; (3) статусные
ветки — каждая (uploading/parsing/rejected по причинам/404/408/413/409/paused) показана сценой
или кадром, не только тестом.
- **Заявление = команда:** каждое число отчёта — с командой, которой получено; «сделано» без
прогона не писать; чего НЕ сделал/не удалось — отдельным явным списком.
- Гейты не подгонять: порог/проверку не двигать ради зелени; несогласие с гейтом — пинг.
- Сдача: `npm run check` и `npm run check:full` зелёные; новые сцены в батарее; запись в
`frontend/docs/frontend-PROGRESS.md` (механика, замеры, диспозиции всех находок §6, В-вопросы);
BACKLOG обновлён; дерево НЕ коммитить — лендит оркестратор.
## 12. Канал вопросов
Непонятно / промт конфликтует с кодом или доками / нужна правка вне `frontend/` → пинг
оркестратору через владельца и запись в зонный журнал; НЕ интерпретация и НЕ правка чужой зоны.
Настоящие развилки эскалируй быстро — это норма, а не слабость.