328 lines
36 KiB
Markdown
328 lines
36 KiB
Markdown
# Промт: фронт-сессия 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-модулей.
|
||
- Правки 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. **Максимум шкалы = ДОСТУПНЫЙ остаток, то есть баланс МИНУС открытые холды** — не весь баланс.
|
||
Платформа берёт холд на сумму потолка ДО спавна прогона, поэтому шкала, упёртая в полный
|
||
баланс, запирает всё на одну книгу и вторую начать нельзя. Два числа платформа уже различает.
|
||
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. **Поля не выдумывать.** Нет в контракте — это вопрос владельцу/оркестратору в
|
||
«Открытые вопросы» журнала зоны, а не догадка в типе. Дефект «фикстура выдумала поле»
|
||
в этой зоне случался дважды и оба раза стоил экрана.
|
||
|
||
**Не в скоупе:** экраны 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`; раскрытие — `<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 на четырёх узлах — принято как есть до слова владельца.
|