Cut documentation sediment in the frontend zone: agents read each file end to end and removed layered history whose content lives in the decision journal
This commit is contained in:
parent
d40e9bf88e
commit
e60533bc36
8 changed files with 355 additions and 1226 deletions
|
|
@ -1,11 +1,8 @@
|
|||
# frontend — веб-интерфейс
|
||||
|
||||
Зона записи сессий «Фронт». Пройдены S0 (план), S1 (инструменты, скриншот-цикл, токены, витрина),
|
||||
S2 (оболочка трёх панелей и слой `src/ui/`), S3 (слой данных на контракте API), S3.5 (фикс-пак
|
||||
оболочки по 24 замечаниям владельца), S3.6 (второй круг: палитра по замеру референсов, модель
|
||||
вкладок, банк таблицей) и S3.7 (долги слоя данных Ф-49…Ф-51, `sense` обязателен в контракте 0.2.2,
|
||||
строки интерфейса в каталог и гейт против хардкода, английский язык кода). Продуктовых экранов
|
||||
ещё нет — они на S4–S7 (`docs/BACKLOG.md`, Ф-1).
|
||||
Зона записи сессий «Фронт». **Статус этапов, что построено и решения владельца по продукту —
|
||||
`docs/frontend-PROGRESS.md`** (единственный носитель; здесь его копии намеренно нет, она отставала).
|
||||
Что впереди — `docs/BACKLOG.md`, Ф-1.
|
||||
|
||||
## Как запустить
|
||||
|
||||
|
|
@ -18,9 +15,10 @@ npm run shot # снимки в .shots/ — открыть и посмо
|
|||
npm run scenes # сценарии интеракций: клик → кадр → проверка состояния
|
||||
```
|
||||
|
||||
Маршрутов семь, и все снимаются (`/showcase` `/scale` `/empty` `/loading` `/error` `/offline`
|
||||
`/partial`) — маршрут выбирает мир фикстур. Новый экран заводится вместе с маршрутом и строкой
|
||||
в `scripts/shot.mjs` — расхождение двух списков падает тестом.
|
||||
Маршрут выбирает мир фикстур, и снимаются все. Список маршрутов выводится из
|
||||
`src/api/scenarios.ts` (`src/routes.tsx`), второй его экземпляр держит `scripts/shot.mjs`,
|
||||
и расхождение двух списков падает тестом `src/routes.test.ts`. Прозаической копии списка здесь
|
||||
нет намеренно: она отставала на этап, а два списка кода замкнуты друг на друга.
|
||||
|
||||
Два ключа снимка стоит знать: `--size 1280x764` даёт вьюпорт референса для прямого наложения,
|
||||
`--dpr 1` снимает то, что видит владелец на своём мониторе (при дефолтных 2x полупиксель CSS
|
||||
|
|
@ -45,13 +43,14 @@ npm run scenes # сценарии интеракций: клик → ка
|
|||
|
||||
## Документы
|
||||
|
||||
- [`docs/FRONTEND_SESSION_PROMPT.md`](docs/FRONTEND_SESSION_PROMPT.md) — промт фронт-сессий:
|
||||
область этапов, референсы с измеренными значениями, экраны, ограничения, порядок работы.
|
||||
- [`docs/FRONTEND_SESSION_PROMPT.md`](docs/FRONTEND_SESSION_PROMPT.md) — нормы зоны (не задание):
|
||||
референсы с измеренными значениями, продуктовый сценарий экранов, жёсткие ограничения
|
||||
владельца, порядок работы и критерий сравнения с референсом.
|
||||
- [`docs/STACK_DECISIONS.md`](docs/STACK_DECISIONS.md) — пины версий, ловушки и список того,
|
||||
что достраивается в движке. Источник: многоагентное исследование 02.08.2026.
|
||||
- [`docs/FRONTEND_PLAN.md`](docs/FRONTEND_PLAN.md) — план S0: сверенные пины, карта `src/`,
|
||||
слои каскада, правила поддерживаемости в проверяемой форме, протокол скриншот-цикла,
|
||||
перепроверенные замеры референса и список расхождений витрины.
|
||||
- [`docs/FRONTEND_PLAN.md`](docs/FRONTEND_PLAN.md) — как пишется фронт: сверенные пины, карта
|
||||
`src/`, слои каскада, правила поддерживаемости в проверяемой форме, протокол скриншот-цикла,
|
||||
перепроверенные замеры референса и границы гейтов.
|
||||
- [`docs/frontend-PROGRESS.md`](docs/frontend-PROGRESS.md) — журнал зоны: текущее состояние,
|
||||
решения владельца по продукту, открытые вопросы, хроника сессий. Имя с префиксом `frontend-`
|
||||
намеренно: журнал оркестратора называется `docs/PROGRESS.md`, и одноимённые файлы путали
|
||||
|
|
@ -65,10 +64,9 @@ npm run scenes # сценарии интеракций: клик → ка
|
|||
генерится `src/api/schema.ts`. ⚠ **Не черновик** (исправлено S4 14.08): контракт ратифицирован
|
||||
D39.99, эта копия — байт-зеркало канона `docs/architecture/14-api-contract/` ⚠ **С ФРИЗА ЗОНЫ ОТСТАЁТ РАТИФИЦИРОВАННО (D39.142 п.5): нормативен ТОЛЬКО канон (0.9.0), зеркало стоит на 0.2.3 — байт-равенства сегодня НЕТ. Синк `cmp` + перегенерация типов — первое касание зоны при разморозке.**; правит её
|
||||
фронт-сессия, переносит в канон диффом оркестратор при лендинге (прецедент Ф-47/0.2.2).
|
||||
- [`docs/API_CONTRACT_DRAFT.md`](docs/API_CONTRACT_DRAFT.md) — спутник спеки: откуда взято
|
||||
каждое решение (выведено из кода движка · предложено фронтом · открыто), обоснования,
|
||||
зависимости и двенадцать открытых вопросов. При расхождении по форме побеждает YAML,
|
||||
при вопросе «почему так» — этот файл.
|
||||
- [`docs/API_CONTRACT_DRAFT.md`](docs/API_CONTRACT_DRAFT.md) — ⚠ **исполнено:** зонный указатель
|
||||
на канон спутника `docs/architecture/14-api-contract/README.md`. Провенанс каждого решения,
|
||||
обоснования, зависимости и К-вопросы живут ТАМ; при расхождении по форме побеждает спека.
|
||||
- `references/` — скриншоты-референсы (Fleet, Antigravity), в git не едут: кладёт владелец.
|
||||
|
||||
## Язык кода и язык интерфейса — разные вещи
|
||||
|
|
@ -76,7 +74,7 @@ npm run scenes # сценарии интеракций: клик → ка
|
|||
Исходники зоны **английские**: комментарии, имена тестов, сообщения ошибок и логи скриптов.
|
||||
Русский в `src/` живёт ровно в двух местах, и оба — данные: каталог сообщений
|
||||
`src/i18n/ru.ts` (слова интерфейса, переводится файлом) и фикстуры `src/mock/` (проза книги,
|
||||
которая стоит вместо настоящей). Держат это два гейта, и правила у них РАЗНЫЕ: ESLint запрещает текст в разметке МЕСТОМ, а не алфавитом (буква в `JSXText` или в читаемом атрибуте — ловится и русский литерал, и английский), а `src/i18n/catalogue.test.ts` подметает то, чего AST не видит, — комментарии, CSS, python-скрипты и корневые конфиги зоны.
|
||||
которая стоит вместо настоящей). Держат это два гейта с РАЗНЫМИ правилами: ESLint ловит текст в разметке по МЕСТУ (а не по алфавиту), `src/i18n/catalogue.test.ts` — то, чего AST не видит: комментарии, CSS, python-скрипты и корневые конфиги зоны. Почему именно так — `docs/STACK_DECISIONS.md` §1.
|
||||
|
||||
## Источник внешнего вида — один
|
||||
|
||||
|
|
|
|||
|
|
@ -1,336 +1,29 @@
|
|||
# ⚠ ИСПОЛНЕНО. Канон компаньона живёт не здесь
|
||||
|
||||
> **Не читать как действующий документ и не править.** Этот файл — черновик спутника, написанный
|
||||
> фронт-сессией S3 04.08 и с тех пор **ратифицированный и перенесённый**. Канон компаньона —
|
||||
> **Не читать как действующий документ и не править.** Этот файл был черновиком спутника спеки
|
||||
> (фронт-сессия S3, 04.08); с тех пор он **ратифицирован и перенесён**. Канон компаньона —
|
||||
> [`../../docs/architecture/14-api-contract/README.md`](../../docs/architecture/14-api-contract/README.md),
|
||||
> рядом с канонической спекой; там же живёт ревью-шапка ратификации D39.99 и К-таблица.
|
||||
> рядом с канонической спекой; там же ревью-шапка ратификации D39.99 и К-таблица.
|
||||
>
|
||||
> Баннер поставлен фронт-сессией S3 08.08 по указанию оркестратора №15: два экземпляра одного
|
||||
> состояния расходятся при первой же правке, и расходится всегда тот, который забыли. **Один факт —
|
||||
> один носитель.** Ниже — историческое тело; ссылаться на него можно, поддерживать — нет.
|
||||
>
|
||||
> Куда смотреть вместо него:
|
||||
> Баннер поставлен по указанию оркестратора №15 (08.08): два экземпляра одного состояния
|
||||
> расходятся при первой же правке, и расходится всегда тот, который забыли. **Один факт —
|
||||
> один носитель.**
|
||||
>
|
||||
> | Что нужно | Где это теперь |
|
||||
> |---|---|
|
||||
> | норматив контракта (ФОРМА) | `docs/architecture/14-api-contract/openapi.yaml`; зонное байт-зеркало — `api-contract/openapi.yaml` |
|
||||
> | норматив контракта (ФОРМА) | канон `docs/architecture/14-api-contract/openapi.yaml`. ⚠ Зонное зеркало `api-contract/openapi.yaml` с фриза зоны отстаёт РАТИФИЦИРОВАННО (D39.142 п.5): нормативен только канон |
|
||||
> | провенанс, обоснования, К-вопросы | `docs/architecture/14-api-contract/README.md` |
|
||||
> | правки, которые ждут переноса в канон | канон `docs/architecture/14-api-contract/` (патч ПРИМЕНЁН и удалён, D39.115) — живёт до лендинга S3 и удаляется |
|
||||
|
||||
## Историческое тело (04.08, не поддерживается)
|
||||
**Тело черновика снято 02.09** и здесь не воспроизводится. Причина не в объёме: канон несёт те же
|
||||
разделы под ТЕМИ ЖЕ номерами (§0 пометки провенанса · §1 почему YAML · §2.1–2.13 решения и их
|
||||
происхождение · §3 зависимости · §4 К-вопросы · §5 ревью-вопрос · приложение А), и с тех пор он
|
||||
ушёл вперёд на редакции 0.3.0…0.9.0 — то есть ссылка вида «§2.7 спутника» резолвится в каноне
|
||||
и отдаёт ДЕЙСТВУЮЩУЮ редакцию, а не редакцию 0.2.x.
|
||||
|
||||
> **Нормативная поверхность контракта — [`api-contract/openapi.yaml`](api-contract/openapi.yaml)**
|
||||
> (OpenAPI 3.1). Этот файл её НЕ дублирует: он несёт то, чего YAML не выражает — откуда взято
|
||||
> каждое решение, чем оно обосновано, что осталось открытым. При расхождении по ФОРМЕ
|
||||
> побеждает YAML; при вопросе «почему так» — этот файл.
|
||||
>
|
||||
> **Статус: черновик фронт-сессии S3 на ратификацию.** Дом ратифицированной копии —
|
||||
> `docs/architecture/14-api-contract/`, зона оркестратора; перенос делает он, генерация типов
|
||||
> после ратификации идёт из перенесённой копии — контракт первичен, код вторичен.
|
||||
>
|
||||
> **Язык.** Спека английская: из неё генерятся типы, а исходники фронта по конвенции
|
||||
> английские (слово владельца 04.08). Ссылки на К-вопросы внутри YAML набраны латинской
|
||||
> `K-N` — это те же вопросы §4. Спутник и остальные доки зоны — русские.
|
||||
>
|
||||
> **Зона строки 95 — «оркестратор/бэкенд/фронт».** Фронт авторитетен в одной трети: форма
|
||||
> read-модели и продуктовые словари. Транспорт платформы (пути, аутентификация, коды) и
|
||||
> работы движка (99–103) здесь ПРЕДЛОЖЕНЫ и без подтверждения своих зон не действуют.
|
||||
⚠ Держать тело рядом было не просто дублем, а активной ловушкой: канон прямо объявляет прежний
|
||||
ответ §5 **опровергнутым исполнением** (research/28 Б-0, разбор — D39.138) и не сохраняет его
|
||||
даже как историю, «потому что он учил зону неправде»; §2.5 (прогресс), §2.6 (`finalizing`),
|
||||
§2.8 (слова банка) и §2.11 (канал потока) с тех пор пере-решены. Черновик 0.2.x отвечал бы на эти
|
||||
вопросы устаревшим способом молча.
|
||||
|
||||
## 0. Пометки провенанса
|
||||
|
||||
| Пометка | Что значит |
|
||||
|---|---|
|
||||
| **✓ выведено** | следует из кода движка или ратифицированного решения; грунт `file:line` рядом |
|
||||
| **◆ предложено** | решение фронта, разумное по его сведениям; подтверждает названная зона |
|
||||
| **○ открыто** | развилка, на которую у фронта ответа нет; перечень — §4 |
|
||||
|
||||
⚠ Пометка ставится **на утверждение, а не на раздел**: у одного пункта половина бывает
|
||||
выведенной, а половина предложенной. Первая редакция черновика этим и грешила — восемь мест
|
||||
несли ✓ там, где верно было ◆; ниже разведено.
|
||||
|
||||
---
|
||||
|
||||
## 1. Почему YAML, а не проза
|
||||
|
||||
Контракт — машинный артефакт: из него генерируются типы, по нему линтуется форма, им
|
||||
типизируются моки. Прозаический контракт расходится с кодом ровно тем способом, ради
|
||||
предотвращения которого заведена строка 95.
|
||||
|
||||
Инструменты, пины и отклонение по пиру TS не дублирую: они в `STACK_DECISIONS.md` §3 и в
|
||||
бэклоге зоны — Ф-23 (`overrides` вместо `--legacy-peer-deps`), Ф-24 (AsyncAPI отложен с
|
||||
причиной). Четыре формы нарушения гейта, каждая проверена живьём, — `FRONTEND_PLAN.md` §5.4.2.
|
||||
|
||||
---
|
||||
|
||||
## 2. Решения и их происхождение
|
||||
|
||||
### 2.1. Язык — код, никогда не имя — ✓ выведено
|
||||
|
||||
Движок держит коды (`backend/internal/config/book.go:26-27`), ключ пары — `zh-ru`
|
||||
(`configs/langpacks/zh-ru/`). Имя языка в данных — пар-специфика в общем слое, запрещённая
|
||||
§2 канона. Вторая цена, дороже: `lang` элемента берётся из данных книги, и пара ja→ru с именем
|
||||
вместо кода отрисует кандзи китайскими начертаниями молча.
|
||||
|
||||
### 2.2. Идентификаторы непрозрачны — ✓ выведено
|
||||
|
||||
`glossary.id` — свежий автоинкремент на каждой пересборке банка и намеренно не хешируется
|
||||
(`store/migrate.go:173-174`); номер главы плотный, «Chapters that yield no text … do NOT
|
||||
consume a chapter number» (`chunk/chunker.go:99-105`), поэтому правка исходника сдвигает
|
||||
номера последующих глав. Стабильность обеспечивает платформа при персисте манифеста (100).
|
||||
|
||||
### 2.3. Заголовок главы отдельным полем — ◆ предложено
|
||||
|
||||
**Движок сегодня делает ОБРАТНОЕ**, и это надо назвать прямо: титул рендерится
|
||||
детерминистически из шаблона пары (`configs/langpacks/zh-ru/heading.txt`: `template Глава {n}`),
|
||||
исходный маркер вырезается из текста для модели (`chunk/chunker.go:110-114`), а на экспорте
|
||||
титул **вклеивается внутрь текста первого юнита** (`pipeline/export.go:215`), причём колонка
|
||||
исходника остаётся без него.
|
||||
|
||||
Выведена здесь только МЕХАНИКА. Само поле `heading` — предложение фронта, и у него есть цена
|
||||
на другой стороне: движку придётся отдавать титул отдельно. Альтернатива (оставить вклейку,
|
||||
фронт отрезает строку) хуже: отрезание титула из текста — это парсинг прозы, и он сломается
|
||||
на первой главе без заголовка. Развилка — К-2.
|
||||
|
||||
**Расхождение фикстуры, найденное разбором:** дерево витрины показывает «Раздел 2. …», колонка
|
||||
оригинала — неснятый «第二节:». Для zh→ru движок не порождает ни одной из форм. Не чинится
|
||||
до ответа на К-2.
|
||||
|
||||
### 2.4. Состояние — у прогона; у главы выполнение — ✓ выведено
|
||||
|
||||
Подпись банка это один стоп на всю книгу (`pipeline/mining.go:201`), поэтому «глава ждёт
|
||||
подписи, пока соседняя финализируется» — невозможная картина. У главы движок держит
|
||||
`ChapterPassport` (`pipeline/status.go:37-55`).
|
||||
|
||||
### 2.5. Прогресс пофазно и в юнитах — ✓ выведено
|
||||
|
||||
«A unit is DONE when every member draft AND the unit's edit resolved ok»
|
||||
(`pipeline/status.go:328-331`), редактура не стартует до стопа банка ⇒ сквозной счётчик стоит
|
||||
на нуле всю черновую волну. Зависимость — строка 99.
|
||||
|
||||
### 2.6. Словарь статусов — ✓ лестница, ◆ ненормальные исходы
|
||||
|
||||
Лестница дословно из строки 95: «загрузка → разбор → перевод → подпись банка → финал →
|
||||
готово». **`not_started` — дыра, найденная самопроверкой черновика:** лестница описывает
|
||||
идущий прогон, а библиотека обязана показывать разобранную книгу, которую не запускали.
|
||||
`stopped`, `rejected`, `not_started` контракт ВЫВОДИТ из поведения процесса, а не получает
|
||||
полем: механика стопа у движка есть (`cmd/tmctl/main.go:63`), но «кто нажал» знает платформа.
|
||||
|
||||
**Стоп по потолку — не `failed`** ✓ выведено: «Ceiling is a hard, book-wide stop (not a
|
||||
per-chunk flag): the job stays 'pending' and resume continues once the ceiling is raised»
|
||||
(`pipeline/stagerun.go:488-489`). Мапить его в `failed` запрещено — это соврало бы про
|
||||
резюмируемость. Каким статусом и словом он показывается — К-8, вопрос владельцу.
|
||||
|
||||
### 2.7. Состояние пары выводится из ПАРЫ — ✓ выведено (исправление первой редакции)
|
||||
|
||||
Первая редакция утверждала «`withheld` = текст не выдан» как факт о движке. **Это было
|
||||
ложно:** флагнутый юнит легально приходит С ТЕКСТОМ в двух случаях —
|
||||
|
||||
- косметическая зачистка санитайзера: «the chunk is NOT lost — the cleaned text is committed
|
||||
as the export» (`pipeline/disposition.go:96-104`);
|
||||
- c-lite member-drop: редактор отгружает отредактированный чистый остаток, а юнит флагнут
|
||||
из-за выпавшего члена (`pipeline/export.go:203-207`).
|
||||
|
||||
Поэтому состояние выводится из ПАРЫ (вердикт, наличие финального текста): флаг+текст →
|
||||
`translated` с замечанием; флаг+пусто → `withheld`.
|
||||
|
||||
**Причина флага при этом ПРОИЗВОЛЬНА, и «единственный легальный случай» — снято** (ревью
|
||||
оркестратора, round-2, пункт 4; утверждение противоречило выводу строкой выше). При c-lite drop
|
||||
юнит несёт `FlagReason` ПЕРВОГО выпавшего члена, каким бы он ни был (`export.go:203-207`:
|
||||
`ce.FlagReason = drops[0].Reason`, и тут же `ce.FinalText … still ships`). Значит «текст +
|
||||
замечание» — это класс, а не один случай, и карта вердиктов обязана иметь фразу для каждой
|
||||
причины, а не для двух.
|
||||
|
||||
**Но `glossary_miss` в этот класс НЕ входит, и это проверено отдельно** (иначе правка выше
|
||||
воскресила бы невоспроизводимый пример). `memberDrops` берёт причину из ЧЕРНОВОЙ строки члена
|
||||
(`status.go:242-257`: `draftStages[cs.Stage] && flagged`), а `glossary_miss` ставится
|
||||
пост-чеком только там, где отгружается финал: в черновой волне — лишь когда она сама финальная
|
||||
(`waverun.go:373-381`, draft-only), в c-lite — на строке РЕДАКТУРЫ (`waverun.go:494`). В
|
||||
пайплайне, где текст отгружает редактура (то есть где c-lite drop вообще возможен), черновая
|
||||
строка `glossary_miss` нести не может. При включённом гейте текст удерживается целиком
|
||||
(`export.go:295-300`), при выключенном мисса нет вовсе. **Итог: пара «текст + промах словаря»
|
||||
невозможна ни одним каналом — фикстура витрины, показывавшая её, переведена на c-lite drop.**
|
||||
|
||||
**Свежесть** ✓ выведено: `target` обновляется на границах стадий и на стопах, а не
|
||||
непрерывно — посреди прогона канала чтения не существует (эксклюзивный лок движка;
|
||||
санкционированное чтение — завершённый либо остановленный прогон, `research/23` §0, §4).
|
||||
|
||||
### 2.8. Банк: словари ✓, имена ◆, `kind` ◆ с дырой
|
||||
|
||||
Значения выведены из схемы и гейтов Go; **имена полей контракта — предложение фронта.**
|
||||
|
||||
| Поле | Словарь | Грунт |
|
||||
|---|---|---|
|
||||
| `status` | `auto · draft · approved` | `store/migrate.go:191`; только `approved` — канон |
|
||||
| `kind` ◆ | `name · place · title · term · nickname` **плюс отсутствие значения** | `terminology/classify.go:15` + `pipeline/banknote.go:74`; пустое — `membank/memseed.go:323-326` |
|
||||
| `origin` | `seed · ruby · mined` | пути записи, см. ниже |
|
||||
| `sense` | свободный текст, ОБЯЗАТЕЛЕН с 0.2.2; пустая строка = «различителя нет» (Ф-47, ратифицировано оркестратором, внесено S3.7) | `store/migrate.go:182` |
|
||||
| `since_chapter`/`until_chapter` | целые, `0` = без границы | `store/migrate.go:189-190` |
|
||||
|
||||
**Фантом `auto` в провенансе убран.** Первая редакция взяла словарь из комментария схемы
|
||||
(`migrate.go:193`: `seed|ruby|auto`) — комментарий устарел. По путям записи `"auto"` пишет
|
||||
**статус**, не провенанс (`membank/memseed.go:328`: `Status:"auto", Source:"ruby"`), а майнинг
|
||||
ставит `mined` (`pipeline/mining.go:424`). Правка комментария в движке — за оркестратором.
|
||||
|
||||
**`kind` пере-размечен ✓→◆, и вот почему это не косметика** (ревью round-2, пункт 3). Словарь
|
||||
из пяти значений выведен верно, но ЗАКРЫТЫМ и обязательным он делает нелегальной легальную
|
||||
строку: ruby-кандидат получает `Type: ""`, если его класс не `name` — то есть gloss и
|
||||
ambiguous живут без типа по построению (`membank/memseed.go:323-326`: `typ := ""`, и только
|
||||
`class == rubyClassName` даёт `"name"`). Материализатору read-модели такую строку было
|
||||
физически нечем заполнить. **Правило пустого:** `kind` присутствует всегда и допускает `null`;
|
||||
`null` значит «движок не решил», строка при этом остаётся подписываемой, и клиенту запрещено
|
||||
и выбрасывать её, и додумывать тип за движок. Проекция `""` → `null` — работа платформы.
|
||||
|
||||
**Ложный друг устранён.** У движка колонка `source` — это ПРОВЕНАНС. Первая редакция назвала
|
||||
провенанс `origin`, а имя `source` отдала ДРУГОЙ колонке (тексту термина) — то есть завела
|
||||
между схемами ложного друга. Теперь: провенанс `origin`, формы термина `src`/`dst`, как их
|
||||
зовёт сам движок; имя `source` в схеме банка не используется вовсе.
|
||||
|
||||
### 2.9. Подпись — набор решений — ✓ выведено
|
||||
|
||||
Конвейер заменяет банк целиком (`store/migrate.go:169-170`), поэтому `PATCH /term/{id}` молча
|
||||
не работает. Механика дословно: «for EACH term either promote it into the mined-delta file OR
|
||||
decline it in the mined-rejects file, then resume — the stop clears once every proposed term
|
||||
is promoted or rejected» (`pipeline/mining.go:201`). Отсюда: решение `promote|decline` ·
|
||||
счётчик «решено N из M» · запрет «продолжить» при неполном наборе · частичное сохранение ◆.
|
||||
|
||||
`POST /runs/{id}/resume` — **нормативная операция, а не резерв** (первая редакция помечала её
|
||||
«○ резерв строки 94», хотя тут же делала её носителем снятия стопа банка). Резерв строки 94 —
|
||||
это `stop`, продуктовая кнопка.
|
||||
|
||||
### 2.10. Ревизия — ✓ у ре-синка, ◆ у чтений
|
||||
|
||||
**✓ выведено:** правило ре-синка — идемпотентный апсерт по `(run_id, seq)`, канал согласования
|
||||
— `status --json` (`research/23` §2, §8; D39.85).
|
||||
|
||||
**◆ предложено фронтом:** что ревизию несут и ЧТЕНИЯ, и что счётчик у потока и у чтений ОДИН.
|
||||
Обоснование — гонка, которую иначе нечем разрешить: фронт живёт на снимке и потоке разом,
|
||||
а рефетч по возврату фокуса окна у ратифицированного `@tanstack/react-query` включён по
|
||||
умолчанию, то есть гонка на каждое переключение вкладки. Но это просьба, не вывод; выбор —
|
||||
К-4.
|
||||
|
||||
**Скоуп ревизии** (дыра первой редакции: она отдавала `revision` на межкнижной библиотеке при
|
||||
пер-прогонном определении): в спеке ревизия объявлена НА РЕСУРС — у библиотеки своя, у
|
||||
прогона своя. Единая сквозная или пер-ресурсная — часть К-4.
|
||||
|
||||
### 2.11. Разрыв потока — ◆ предложено
|
||||
|
||||
При переподключении клиент шлёт `Last-Event-ID`. Если сервер докачать не может, он обязан
|
||||
ответить событием `resync_required`, а не молча начать с текущего момента: **реплей истории
|
||||
запрещён**, иначе разовое событие вроде `note` теряется молча и замечание не появится
|
||||
до перезагрузки. Клиент по этому событию перечитывает снимки.
|
||||
|
||||
### 2.12. Разрешающий список — ✓ инвариант, ◆ форма
|
||||
|
||||
Проекция «read-модель → фронт» строится как allowlist. Что лежит в операторских структурах
|
||||
(`pipeline/status.go:58-130`, `:37-55`) и не может доехать: пять денежных полей плюс
|
||||
`cost_usd` главы (§4.8 — денег в MVP-интерфейсе нет вовсе) · `routing` вида «stage=model»,
|
||||
`content_labels`, `content_routing_problems` (ПТ-33) · снапшот, дрифт, ре-билл ·
|
||||
`escalations`, `postcheck_misses`, `style_flags`, `glossary_miss_flagged`, `stages_skipped`,
|
||||
`repair_applied`, `worst_flag_reason` · `flag_reason` и `detail` — последний несёт сырой текст
|
||||
движка вида «CJK leak in the ru output: 第一节»; строку собирает `checks/sanitizer.go:658`,
|
||||
а `pipeline/export.go:34` — лишь объявление поля, куда она доезжает.
|
||||
|
||||
### 2.13. ПТ-34 — перевод не индексируется — ✓ инвариант
|
||||
|
||||
Реестр требований назначает носителем ПТ-34 в том числе контракт, а первая редакция пункта
|
||||
не имела вовсе. В спеке: приложение живёт под `X-Robots-Tag: noindex`, ответы с текстом
|
||||
перевода несут `Cache-Control: no-store`, ссылка на выгрузку выдаётся только владельцу.
|
||||
|
||||
---
|
||||
|
||||
## 3. Зависимости: без чего контракт не заработает
|
||||
|
||||
| Что | Строка | Без чего именно |
|
||||
|---|---|---|
|
||||
| Пофазный прогресс `draft ∥ edit` | 99 | прогресс (§2.5) |
|
||||
| Персист манифеста + `chunker_version` | 100 | стабильный `id` главы (§2.2) |
|
||||
| Машиночитаемая таблица ПОДПИСИ | 101 | экран подписи (§2.9) |
|
||||
| Событийный эмиттер + событие потолка | 103 | весь поток (§2.11), событие `note`, событие `ceiling` |
|
||||
| **Артефакт экспорта БАНКА** | **строки нет — заводит оркестратор** | чтение `GET /books/{id}/bank` |
|
||||
| **Механизм поднятия потолка** | **строки нет — заводит оркестратор** | `POST /runs/{id}/resume` после стопа по потолку (см. ниже) |
|
||||
| HTTP/SSE, аутентификация, воркер | П-1 | всё; в `platform/` ноль строк кода |
|
||||
|
||||
⚠ **Отдельно про банк — дыра, найденная ревью оркестратора.** Строка 101 даёт таблицу
|
||||
ПОДПИСИ (стоп-таблица, кап 20 на stdout — `cmd/tmctl/render.go:98`), а не экспорт всего банка;
|
||||
сам банк живёт в приватном SQLite движка, читать который платформе запрещено (D39.85). То есть
|
||||
у чтения `/bank` сегодня **нет канала вообще**. Фронт этого не решает — нужна строка единого
|
||||
бэклога, и заводит её оркестратор.
|
||||
|
||||
Та же природа у события `note`: пер-юнитных замечаний посреди прогона движок не эмитит —
|
||||
зависимость на словарь строки 103.
|
||||
|
||||
⚠ **Потолок: `resume` сам по себе не сдвинет прогон** (ревью round-2, пункт 5). Движок
|
||||
продолжает «once the ceiling is raised» (`pipeline/stagerun.go:488-489`), а канала поднятия
|
||||
в контракте нет — и в MVP-интерфейсе быть не может: денег на экране нет вовсе (D39.84). Значит
|
||||
между стопом по потолку и продолжением обязан стоять механизм ПЛАТФОРМЫ (поднятие по политике,
|
||||
или явное действие вне интерфейса книги), и до него `resume` после потолка возвращает прогон
|
||||
в то же состояние. Что при этом видит пользователь — К-8, вопрос владельцу; чем поднимают —
|
||||
строка единого бэклога, которой нет.
|
||||
|
||||
---
|
||||
|
||||
## 4. Открытые вопросы
|
||||
|
||||
| # | Вопрос | Кому |
|
||||
|---|---|---|
|
||||
| К-1 | Десять статусов (§2.6) — принять или поправить? Три контракт выводит, а не получает | владелец / автор контракта |
|
||||
| К-2 | Титул главы: отдать полем `heading` (предложено) — или оставить вклейку в текст, и фронт отрезает строкой? | автор контракта + бэкенд |
|
||||
| К-3 | Метка главы в дереве: титул это ровно «Глава N», узлов 2284 — дерево одинаковых по форме строк | владелец (продуктовое) |
|
||||
| К-4 | Ревизия: одна сквозная на прогон или своя на ресурс? И несут ли её чтения вообще (§2.10) | платформа |
|
||||
| К-5 | Показывать ли оценку времени. **ПТ-19 существует** (`docs/product-requirements.md:49`: «видимый прогресс/ETA — из Ф3-видения ридер-IDE»), то есть посылка «не запрошено» неверна; вопрос в том, показываем ли в MVP | владелец (продуктовое) |
|
||||
| К-6 | Ступени замечания: сколько их и где граница. Сегодняшние две — проекция ОПЕРАТОРСКОЙ лестницы рангов, а она не обязана совпадать с продуктовой осью | владелец (продуктовое) |
|
||||
| К-7 | Пагинация: 2284 главы и 1200 терминов одним ответом или курсором? Фронт виртуализует, ему годится любой | платформа |
|
||||
| К-8 | Стоп по потолку: каким статусом и каким словом? В `failed` мапить нельзя — стоп резюмируемый | владелец (продуктовое) |
|
||||
| К-9 | **Отказ прескрина не выразим ни одним из десяти статусов.** Абьюз/misuse-прескрин до трат токенов и UI-контракт отказа — строка 94 (ПТ-16); книга, отклонённая прескрином, это не `rejected` (тот про неразобранный файл) и не `failed` | владелец + автор контракта |
|
||||
| К-10 | **Выполнение главы — тот самый несплитованный счётчик, который §2.5 объявляет негодным.** `Chapter.units_done` не разведён по фазам, значит дерево глав показывает ноль всю черновую волну — ровно то, из-за чего прогресс книги сделан пофазным. Развести и тут (цена — пофазные счётчики НА ГЛАВУ в строке 99) или показывать в дереве другое | автор контракта + бэкенд |
|
||||
| К-11 | **Условная обязательность полей — выражена у банка, не выражена у чтений.** У `BankDecision` констрейнт поставлен (`if action=promote → dst` непустой), и вот что это стоило, измерено: spectral его валидирует, а **openapi-typescript его игнорирует** — в генерённых типах `dst?: string` как был. То есть 3.1-условие защищает сервер, но не экран; клиентское сужение (юнион `promote`-с-`dst` ↔ `decline`) — работа подписного экрана S5, писать его до экрана не на чем проверить. Остаётся решить то же для чтений: `Note` не требует ни `chapter_id`, ни `unit_id`, `Unit.target` не обязателен при `translated`; обе схемы служат и вложенно, и отдельно, поэтому простое `required` соврало бы | автор контракта |
|
||||
| К-12 | **Завершение выгрузки: опрос или событие?** Чтение `GET /books/{id}/exports/{id}` заведено — без него создающий вызов был тупиком (`ready:false` и ни слова дальше). Но пушить ли завершение ещё и кадром потока, чтобы не опрашивать, решает платформа: у неё воркер и её цена | платформа |
|
||||
|
||||
---
|
||||
|
||||
## 5. Проверка ревью-вопросом строки 95
|
||||
|
||||
**«Сменится стадия конвейера — придётся ли править фронт?»**
|
||||
|
||||
| Изменение в движке | Правит ли фронт |
|
||||
|---|---|
|
||||
| переименована стадия / добавлена волна | **нет** — имена стадий не пересекают шов, прогресс пофазный, а не постадийный |
|
||||
| сменилась модель или маршрутизация | **нет** — `routing`/`content_labels` в allowlist не входят |
|
||||
| добавлена новая причина флага | **нет** — на провод идёт продуктовая фраза, карта живёт в контракте |
|
||||
| добавлен новый тип термина | **нет** — словарь расширяется минором, ветка неизвестного стоит на шве |
|
||||
| добавлено новое продуктовое состояние | **да, один файл** — карта «статус → вид» на шве `src/api/`; это и есть контрольный вопрос владельца |
|
||||
| сменился чанкер, главы пере-разобраны | **частично** — код фронта не правится (ключ непрозрачный, номер отображаемый), но **сохранность соответствия старых `id` новым главам контрактом не гарантируется**: это работа персиста манифеста (строка 100). Если соответствие потеряно, у пользователя разъезжаются открытые вкладки и закладки — не правка кода, но видимый ущерб, и решать его строке 100 |
|
||||
|
||||
Единственное безусловное «да» — то, которое и должно быть «да».
|
||||
|
||||
---
|
||||
|
||||
## Приложение А. Карта «вердикт → продуктовая фраза» — ЗАГОТОВКА
|
||||
|
||||
Заполняет автор контракта вместе с бэкендом и владельцем. **Правило: фраза пишется по
|
||||
доккомменту `disposition.go`, а не по имени константы, и рядом кладётся цитата** — иначе
|
||||
повторяется инверсия, стоившая двух фраз (`glossary_miss` подан как «термин не подписан»,
|
||||
хотя термин ПОДПИСАН и его проигнорировали, `disposition.go:78-79`; `sanitizer_stripped` подан
|
||||
как потеря текста, хотя «the chunk is NOT lost», `disposition.go:99`).
|
||||
|
||||
⚠ **Русские фразы ниже — плейсхолдеры, а не предложение фронта.** Словарь продуктовый, его
|
||||
слова выбирает владелец (ПТ-33, В-3). Ступень — тоже: сегодняшние `attention`/`glance`
|
||||
унаследовали ОПЕРАТОРСКУЮ ось рангов, а она не обязана совпадать с продуктовой (К-6).
|
||||
|
||||
| Причина движка | Ранг | Продуктовая фраза | Ступень |
|
||||
|---|---|---|---|
|
||||
| `hard_refusal` · `soft_refusal` · `content_filter` · `hard_block` | 0 | ⬜ | ⬜ |
|
||||
| `cjk_artifact` · `excision_suspect` · `coverage_fail` | 1 | ⬜ | ⬜ |
|
||||
| `sanitizer_defect` | 2 | ⬜ | ⬜ |
|
||||
| `loop_degenerate` | 3 | ⬜ | ⬜ |
|
||||
| `decode_error` | 4 | ⬜ | ⬜ |
|
||||
| `glossary_miss` | 5 | плейсхолдер: «Подписанный термин не применён в переводе» | ⬜ |
|
||||
| `length` · `empty` | 6 | ⬜ | ⬜ |
|
||||
| `sanitizer_stripped` | 7 | плейсхолдер: «Служебная разметка вычищена автоматически» | ⬜ |
|
||||
| `upstream_not_ok` | 8 (по умолчанию) | ⬜ | ⬜ |
|
||||
| незнакомая причина | 8 (по умолчанию) | ⬜ нейтральная, НЕ «ошибка» | ⬜ |
|
||||
|
||||
Причин пятнадцать; `upstream_not_ok` в первой редакции отсутствовал — у него нет своей ветки
|
||||
в `flagReasonSeverity`, поэтому он падает в ранг по умолчанию (`pipeline/status.go:174`),
|
||||
как и любая будущая причина. Последняя строка — не формальность: контракт обязан иметь фразу
|
||||
для причины, которой ещё не существует.
|
||||
Исходное тело — в истории git этого файла.
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
# Вход фронта в контракт API v0 — материал для строки 95
|
||||
|
||||
> ⚠ **ИСПОЛНЕНО И БОЛЬШЕ НЕ ПОДДЕРЖИВАЕТСЯ (04.08).** Этот файл — ВХОД, написанный до
|
||||
> контракта: просьба к его автору. Контракт с тех пор написан, и все 18 пунктов ниже стали
|
||||
> либо полем спеки [`api-contract/openapi.yaml`](api-contract/openapi.yaml), либо разделом §2
|
||||
> спутника [`API_CONTRACT_DRAFT.md`](API_CONTRACT_DRAFT.md) с тем же грунтом `file:line`, либо
|
||||
> К-вопросом §4. **Читать для решения — те два файла; этот не сверяется с ними и будет
|
||||
> расходиться.** Ценность, ради которой он не удалён: аудиторский след — как каждая просьба
|
||||
> выводилась из кода движка, и что ломалось без неё. Место такому — `docs/archive/`, перенос
|
||||
> при ратификации делает оркестратор (зона).
|
||||
> контракта: просьба к его автору. Контракт с тех пор написан и ратифицирован, и все 18 пунктов
|
||||
> ниже стали либо полем канонической спеки `docs/architecture/14-api-contract/openapi.yaml`, либо
|
||||
> разделом §2 канонического спутника `docs/architecture/14-api-contract/README.md`, либо его
|
||||
> К-вопросом §4. **Читать для решения — канон; этот файл с ним не сверяется и уже расходится:**
|
||||
> его `file:line` в чужую зону протухли (проверено 02.09 — цитаты грепаются, номера строк нет),
|
||||
> а часть просьб канон пере-решил. Ценность, ради которой он не удалён и не сокращён:
|
||||
> аудиторский след — как каждая просьба выводилась из кода движка, и что ломалось без неё;
|
||||
> цитаты из движка в нём дословные и грепаются. Место такому — `docs/archive/`, перенос делает
|
||||
> оркестратор (зона).
|
||||
>
|
||||
> **Что это было.** Ф-14 бэклога зоны, доведённый до артефакта: перечень того, что интерфейсу
|
||||
> нужно от read-модели платформы, с обоснованием на КАЖДЫЙ пункт. Адресат — автор контракта
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,23 +1,20 @@
|
|||
# План фронта — как пишется интерфейс TextMachine
|
||||
|
||||
> Этап **S0** сессии фронта: план до первой строки кода (`FRONTEND_SESSION_PROMPT.md` §7.2).
|
||||
> Ландится отдельным коммитом. Правят следующие фронт-сессии; при конфликте с
|
||||
> `STACK_DECISIONS.md` побеждает STACK_DECISIONS (там ратифицированы пины), при конфликте
|
||||
> с промтом сессии — промт (там ратифицирован продукт).
|
||||
> Как пишется фронт: пины, карта `src/`, правила в проверяемой форме, протокол скриншот-цикла,
|
||||
> замеры референса. Правят фронт-сессии; при конфликте с `STACK_DECISIONS.md` побеждает
|
||||
> STACK_DECISIONS (там ратифицированы пины), при конфликте с промтом сессии — промт (там
|
||||
> ратифицирован продукт). Статус этапов — `frontend-PROGRESS.md`, очередь этапов — `BACKLOG.md` Ф-1.
|
||||
|
||||
## 0. Границы
|
||||
|
||||
Эта сессия делает **S0 (план) и S1 (инструменты, скриншот-цикл, `tokens.css`, витрина)** и
|
||||
останавливается на витрине. Экраны — S2–S7, по одной сессии на этап (`BACKLOG.md` Ф-1).
|
||||
|
||||
Зона записи — только `frontend/`. `backend/`, `platform/`, `docs/`, `eval/` — read-only.
|
||||
|
||||
### 0.1. Канон: что читать перед чем
|
||||
|
||||
Фронт живёт в проекте с ратифицированным контрактом, и половина ответов на вопросы «как правильно»
|
||||
уже написана — не здесь. Опыт S0/S1: два дефекта пришли ровно из непрочитанного канона (мокался
|
||||
не тот уровень контракта; смысл двух вердиктов взят по имени причины вместо доккоммента), оба
|
||||
нашлись бы за десять минут чтения. Ссылки, а не пересказ — пересказ протухает:
|
||||
уже написана — не здесь. Таблица заведена после того, как два дефекта зоны пришли ровно
|
||||
из непрочитанного канона (разбор — `frontend-PROGRESS.md`, запись про карту канона).
|
||||
Ссылки, а не пересказ — пересказ протухает:
|
||||
|
||||
| Читать | Зачем фронту | Когда |
|
||||
|---|---|---|
|
||||
|
|
@ -25,7 +22,7 @@
|
|||
| [../../docs/README.md](../../docs/README.md) | карта: где что лежит и что чем перекрыто | первым делом |
|
||||
| [../../docs/product-requirements.md](../../docs/product-requirements.md) | реестр ПТ-1..ПТ-34; **ПТ-33 и ПТ-34 — жёсткие инварианты интерфейса**, ПТ-21 задаёт якорь чтения | до первого экрана |
|
||||
| [../../docs/research/23-engine-platform-seam.md](../../docs/research/23-engine-platform-seam.md) | форма шва движок↔платформа; `docs/README.md` требует читать его **перед любым кодом стыка** | до любого кода данных |
|
||||
| CURRENT-STATE + единый бэклог в [../../docs/PROGRESS.md](../../docs/PROGRESS.md) | что движок обязан отдать фронту и чего ещё нет. ⚠ **Актуализировано S4 14.08:** строка 95 (контракт API) ЗАКРЫТА ратификацией D39.99, строки 99/100/103 построены (D39.122/131) — таблица ниже больше не подаёт их как «чего нет»; живые для фронта — **101** (машиночитаемая таблица подписи, вход S5) и **169** (экспорт банка) | перед планированием этапа |
|
||||
| CURRENT-STATE + единый бэклог в [../../docs/PROGRESS.md](../../docs/PROGRESS.md) | что движок обязан отдать фронту и чего ещё нет. ⚠ Номера строк из прежних редакций этого плана (95, 99–103) закрыты и в бэклоге больше не стоят — искать грепом по слову, а не по номеру | перед планированием этапа |
|
||||
| [STACK_DECISIONS.md](STACK_DECISIONS.md) §5 | транспорт до фронта и правила стрима | до слоя данных |
|
||||
| [api-contract/openapi.yaml](api-contract/openapi.yaml) + компаньон рядом с каноном (`../../docs/architecture/14-api-contract/README.md`) | **ратифицированный контракт** — форма данных, коды ответов, словари. ⚠ Заменил `API_CONTRACT_INPUT.md`: тот под баннером «исполнено» и живым входом больше не является | до слоя данных и при каждой правке формы |
|
||||
| [../../docs/glossary.md](../../docs/glossary.md) | жаргон проекта (D-номер, банк, голден, юнит) | при первом непонятном слове |
|
||||
|
|
@ -39,10 +36,9 @@
|
|||
|
||||
**С движком — никогда.** Это не стилистика, а ратифицированный анти-паттерн: движок — CLI-процесс
|
||||
на прогон под эксклюзивным локом, его SQLite платформой не читается, HTTP внутрь него не тащится
|
||||
(`research/23` §4, §0). Между фронтом и движком стоит платформа. ⚠ **Актуализировано S4 14.08:**
|
||||
«у платформы пока ноль строк кода» устарело — P5 принята D39.130, живы вход OIDC, библиотека,
|
||||
кредитный леджер, раннер и `POST /books`; чего у неё нет, смотреть в `../../platform/BACKLOG.md`,
|
||||
а не здесь.
|
||||
(`research/23` §4, §0). Между фронтом и движком стоит платформа. Что в ней построено и чего ещё
|
||||
нет — `../../platform/BACKLOG.md` и `../../platform/docs/platform-PROGRESS.md`; копии её состояния
|
||||
здесь нет намеренно, она отставала на паки.
|
||||
|
||||
Проводов два, и их легко перепутать:
|
||||
|
||||
|
|
@ -66,12 +62,9 @@
|
|||
Строка «= latest» означает: на 02.08.2026 эта версия и есть последняя опубликованная.
|
||||
Единственное намеренное отставание от latest — TypeScript (см. сноску).
|
||||
|
||||
**S2 (04.08) поставила три ратифицированных пина, новых не заводила:** `react-aria-components@1.20.0`,
|
||||
`react-resizable-panels@4.12.2`, `zustand@5.0.14` — версии перепроверены `npm view` в день установки,
|
||||
все три по-прежнему latest, `npm audit` — 0 уязвимостей. API `react-resizable-panels` сверен
|
||||
не по статьям, а по `.d.ts` в поставке: `Group`/`Panel`/`Separator` и `useDefaultLayout` на месте,
|
||||
`onLayoutChange` помечен deprecated в пользу `onLayoutChanged` — берём второй, как и записано
|
||||
в `STACK_DECISIONS.md` §2.
|
||||
⚠ API `react-resizable-panels` сверен по `.d.ts` в поставке, а не по статьям: `Group`/`Panel`/`Separator`
|
||||
и `useDefaultLayout` на месте, `onLayoutChange` помечен deprecated в пользу `onLayoutChanged` —
|
||||
берём второй (`STACK_DECISIONS.md` §2).
|
||||
|
||||
### Рантайм
|
||||
|
||||
|
|
@ -80,7 +73,7 @@
|
|||
| `react` | `19.2.8` | 2026-07-21 | ядро; = latest, React 20 не существует |
|
||||
| `react-dom` | `19.2.8` | 2026-07-21 | обязан совпадать с `react` версия-в-версию |
|
||||
| `react-router` | `8.3.0` | 2026-07-22 | библиотечный (data) режим: маршрут на экран — несущая правила «каждый экран открывается в изоляции» (§3.10). Требует Node ≥ 22.22 |
|
||||
| `@tanstack/react-query` | `5.101.4` | 2026-07-21 | всё серверное состояние: прогон, книги, банк. Мажор 5 актуален. **Поставлен S3 08.08**, версия ратифицирована ещё в `STACK_DECISIONS` §1 и перепроверена live в день установки — по-прежнему latest, «v6» на npm это svelte-адаптер. Зачем нам: серверное состояние живёт ровно в одном месте, и поток пишет в тот же кэш, что читает экран — иначе прогресс существовал бы в двух копиях. Рефетч по возврату фокуса оставлен ВКЛЮЧЁННЫМ намеренно: это ровно та гонка, ради которой контракт завёл ревизию, и выключить его значило бы спрятать гонку вместо того, чтобы её обработать |
|
||||
| `@tanstack/react-query` | `5.101.4` | 2026-07-21 | всё серверное состояние: прогон, книги, банк (мажор и ловушка «v6» — `STACK_DECISIONS.md` §1). Зачем нам: серверное состояние живёт ровно в одном месте, и поток пишет в тот же кэш, что читает экран — иначе прогресс существовал бы в двух копиях. Рефетч по возврату фокуса оставлен ВКЛЮЧЁННЫМ намеренно: это ровно та гонка, ради которой контракт завёл ревизию, и выключить его значило бы спрятать гонку вместо того, чтобы её обработать |
|
||||
| `zustand` | `5.0.14` | 2026-05-28 | состояние интерфейса (свёрнутость панелей, активные вкладки). Только с селектором или `useShallow` |
|
||||
| `react-aria-components` | `1.20.0` | 2026-07-31 | ЕДИНСТВЕННАЯ библиотека примитивов: `Table`+`Virtualizer` (банк), `Tree` (главы), `Tabs/Menu/Dialog/Popover/Tooltip` (оболочка), `I18nProvider` (русские служебные строки). Стилей не навязывает. Импорт разрешён только внутри `src/ui/` |
|
||||
| `react-resizable-panels` | `4.12.2` | 2026-07-12 | три панели Fleet. API v4: `Group`/`Panel`/`Separator` — старые имена `PanelGroup`/`PanelResizeHandle` мертвы |
|
||||
|
|
@ -120,8 +113,8 @@ CJK-шрифт не ставим: иероглифы отдаём системн
|
|||
| `happy-dom` | `20.11.1` | 2026-07-22 | DOM для контракт-теста токенов: в отличие от jsdom считает каскад CSS-переменных |
|
||||
| `@testing-library/react` | `16.3.2` | 2026-01-19 | рендер компонентов в тестах |
|
||||
| `@playwright/test` | `1.62.1` | 2026-07-30 | скриншот-цикл и сверка вычисленных токенов в настоящем Chromium |
|
||||
| `msw` | `2.15.0` | 2026-07-08 | моки сети. Пин ратифицирован при D39.84, **установлен 04.08** ради proof-of-form контракта; **в S3 (08.08) развёрнут в полный набор хендлеров и в браузерный воркер.** Замер, сделанный ДО того, как на нём что-то построили: сервис-воркер MSW перехватывает `EventSource` в живом Chromium и отдаёт потоковое тело кадр за кадром, `lastEventId` заполняется. В тестовом DOM `EventSource` нет вовсе — отсюда развод «разбор кадра отдельно от транспорта» (`src/api/stream.ts`). `public/mockServiceWorker.js` — вывод `msw init`, вендорный файл: исключён из prettier и eslint |
|
||||
| `@stoplight/spectral-cli` | `6.16.3` | 2026-08-03 | линт контракта шестым шагом `npm run check`: битые `$ref`, дубли `operationId`, ответы без описания. Порог строгий (`--fail-severity=warn`) — предупреждение валит команду так же, как ошибка |
|
||||
| `msw` | `2.15.0` | 2026-07-08 | моки сети, пин ратифицирован при D39.84. Замер, сделанный ДО того, как на нём что-то построили: сервис-воркер MSW перехватывает `EventSource` в живом Chromium и отдаёт потоковое тело кадр за кадром, `lastEventId` заполняется. В тестовом DOM `EventSource` нет вовсе — отсюда развод «разбор кадра отдельно от транспорта» (`src/api/stream.ts`). `public/mockServiceWorker.js` — вывод `msw init`, вендорный файл: исключён из prettier и eslint |
|
||||
| `@stoplight/spectral-cli` | `6.16.3` | 2026-08-03 | линт контракта шагом `npm run check` (внутри `npm run contract`): битые `$ref`, дубли `operationId`, ответы без описания. Порог строгий (`--fail-severity=warn`) — предупреждение валит команду так же, как ошибка |
|
||||
| `openapi-typescript` | `7.13.0` | 2026-02-11 | генерит `src/api/schema.ts` из `docs/api-contract/openapi.yaml`. Типы перестают быть авторским пересказом контракта: расходиться им физически нечем. ⚠ Пир `typescript@^5.x` против нашего TS 6 — снят точечным `overrides`, обоснование и проба — `BACKLOG.md` Ф-23 |
|
||||
| `globals` | `17.8.0` | 2026-07-26 | наборы глобалов для flat-config |
|
||||
|
||||
|
|
@ -197,7 +190,7 @@ CJK-шрифт не ставим: иероглифы отдаём системн
|
|||
| 15 | Доступность проверяется машиной, а не глазами владельца | **машинно**: axe-core прогоняется в `npm run shot` по каждому маршруту и валит команду; список маршрутов скрипта сверяется с `routes.tsx` тестом (`src/routes.test.ts`) — иначе новый экран просто не попадал в прогон, а команда возвращала успех. Контраст вынесен в отчёт: палитра снята с Fleet замером и принята владельцем, менять её под порог WCAG — отдельное решение (`BACKLOG.md` Ф-11) |
|
||||
| 17 | Пара языков живёт в данных, а не в разметке | **машинно**: `src/generality.test.ts` запрещает литерал `lang="…"` в TSX. Это ревью-вопрос канона в исполняемой форме («заработает ли пара, которой в репо ещё нет, без правки кода?» — `CLAUDE.md` §2). Найдено ревью S1 в живом коде: `lang="zh"` пережил бы приезд ja→ru молча, и кандзи отрисовались бы китайскими начертаниями |
|
||||
| 16 | Плавающий промис не проходит | **машинно**: `tseslint.configs.recommendedTypeChecked` с `projectService`. Проверено: `load()` без `await` роняет линт |
|
||||
| 13 | Инлайновый стиль — только литерал объекта прямо в атрибуте, и только с ключами-CSS-переменными | **машинно**: `no-restricted-syntax` запрещает сам атрибут `style` и разрешает единственную форму. Перечислять формы записи оказалось бесполезно: из одиннадцати способов записать то же самое ловилось три. ~~Остаточная дыра — JSX-спред~~ — закрыта в S2 с другой стороны: запрещено само свойство `style` в объектных литералах, то есть носитель ловится там, где он собран. Плюс значение CSS-переменной обязано быть числом или `var(--…)` |
|
||||
| 13 | Инлайновый стиль — только литерал объекта прямо в атрибуте, и только с ключами-CSS-переменными | **машинно**: `no-restricted-syntax` запрещает сам атрибут `style` и разрешает единственную форму. Перечислять формы записи оказалось бесполезно: из одиннадцати способов записать то же самое ловилось три. JSX-спред закрыт с другой стороны — запрещено само свойство `style` в объектных литералах, то есть носитель ловится там, где он собран. Плюс значение CSS-переменной обязано быть числом или `var(--…)` |
|
||||
| 18 | Библиотека примитивов видна ровно из одного места | **машинно**: `no-restricted-imports` на `react-aria-components` с исключением для `src/ui/**`. Две библиотеки примитивов означали бы два focus-scope и два портальных менеджера (`STACK_DECISIONS.md` §2), а один вход делает замену библиотеки правкой одной папки |
|
||||
| 19 | Отступы, промежутки и радиусы — только из токенов | **машинно** (Ф-4, включён в S2): stylelint `declaration-property-value-allowed-list` на `/^(padding\|margin)(-\|$)/`, `/^(gap\|row-gap\|column-gap)$/`, `/^border-.*radius$/`. Значение — последовательность `var(--…)`; `0`, `auto`, `inherit` легальны, `calc()` намеренно нет |
|
||||
| 20 | Числа, которые нужны коду, а не стилям, не заводят вторую истину | **машинно**: `src/tokens/measures.ts` держит их одним объектом (высота строки для виртуализатора, ширины панелей для `react-resizable-panels`), а `tokens.test.ts` сверяет каждое значение с одноимённым токеном. Без этого высота строки существовала бы в двух местах и разъезжалась молча |
|
||||
|
|
@ -206,8 +199,8 @@ CJK-шрифт не ставим: иероглифы отдаём системн
|
|||
добавление нового состояния главы или нового вида замечания правит **один** файл.
|
||||
Правит три — структура неверна, переделать до движения дальше.
|
||||
|
||||
Ответ этой структуры (⚠ актуализировано S4 14.08 — `src/api/types.ts` удалён ещё в S3, типы
|
||||
приходят из спеки): состояние книги живёт **одной записью в `src/api/vocabulary.ts`** — там же его
|
||||
Ответ этой структуры (типы приходят из спеки, авторского файла типов у зоны нет):
|
||||
состояние книги живёт **одной записью в `src/api/vocabulary.ts`** — там же его
|
||||
слово, тон и то, что с ним можно делать (`startable`, `intake`); экраны эту запись читают и ничего
|
||||
о списке значений не знают. Цвет — токен. Новый вид замечания — запись в карте видов, компонент
|
||||
выноски не трогается. Проверено S4: добавление свойства «книгу в этом состоянии можно запустить»
|
||||
|
|
@ -240,11 +233,11 @@ SSE-соединение открытым ПО ПОСТРОЕНИЮ — сеть
|
|||
на давно сошедшемся экране. У маршрута `/loading` финальная картинка — само ожидание, поэтому он
|
||||
перечислен в `PENDING_ROUTES` скрипта и снимается на `loading`.
|
||||
|
||||
**Маршрутов стало семь, и каждый — свой мир фикстур** (S3): `/showcase` трудный случай ·
|
||||
`/scale` длинный хвост · `/empty` пусто · `/loading` ожидание · `/error` отказ платформы ·
|
||||
`/offline` живой поток потерян при рабочих чтениях · `/partial` отказал ОДИН рид при рабочих
|
||||
остальных. Каждый получает свой кадр и свой прогон axe:
|
||||
ветка, которую не открыть по прямой ссылке, не проверяется ничем.
|
||||
**У каждого маршрута свой мир фикстур.** Список маршрутов выводится из `src/api/scenarios.ts`
|
||||
и замкнут со вторым списком `scripts/shot.mjs` тестом `src/routes.test.ts`; что означает каждый
|
||||
мир — в шапке `src/mock/worlds.ts`. Прозаической копии списка здесь нет намеренно: она отставала
|
||||
на этап. Каждый маршрут получает свой кадр и свой прогон axe: ветка, которую не открыть
|
||||
по прямой ссылке, не проверяется ничем.
|
||||
|
||||
**Что делаю я после команды — обязательная часть цикла, а не опция:** открываю PNG инструментом
|
||||
чтения файлов, смотрю на него, кладу рядом `references/fleet.png`, называю расхождения словами,
|
||||
|
|
@ -378,20 +371,18 @@ rm -f *.deb && cd ..
|
|||
не стали: снимок должен показывать то, что рисует настоящий браузер.
|
||||
2. **Колонка оригинала приглушена** (`--color-text-secondary`) — это решение, а не замер:
|
||||
во Fleet на этом месте подсвеченный код. Пересмотреть на S6, когда читалка будет настоящей.
|
||||
3. ~~**`+` в конце рядов вкладок** у Fleet есть, у нас нет~~ — **закрыто в S2 (04.08).** `+` стоит
|
||||
в ряду вкладок левой панели и запускает «Добавить книгу» (решение владельца 04.08); в центре
|
||||
и справа его нет, потому что наборы вкладок там фиксированные и кнопка вышла бы декоративной.
|
||||
4. ~~**Наведение и прочие интерактивные состояния** витриной не проверены~~ — **закрыто в S2:**
|
||||
состояния сняты прогоном браузера, а не статичным кадром. `--color-raised` подтверждён
|
||||
на наведении кнопки, вкладки и строки списка.
|
||||
5. **Боковые панели — пиксели, центр — доля.** Решено в S2 замером и продуктом: боковые получают
|
||||
3. **`+` стоит только в ряду вкладок левой панели** и запускает «Добавить книгу» (решение
|
||||
владельца 04.08); в центре и справа его нет — наборы вкладок там фиксированные, и кнопка
|
||||
вышла бы декоративной. Интерактивные состояния снимаются прогоном браузера, а не статичным
|
||||
кадром: `--color-raised` подтверждён на наведении кнопки, вкладки и строки списка.
|
||||
4. **Боковые панели — пиксели, центр — доля.** Решено в S2 замером и продуктом: боковые получают
|
||||
`groupResizeBehavior: "preserve-pixel-size"` и стартуют с замеренных 320px, центр —
|
||||
`preserve-relative-size`. На 1920 всю лишнюю ширину забирает читалка, а справочные колонки
|
||||
остаются той ширины, под которую нарисованы. Библиотека требует хотя бы одну панель с долевым
|
||||
поведением — ею и оказывается центр. Минимум и максимум боковой — новые токены
|
||||
`--panel-side-min`/`--panel-side-max`; минимум центра — та же ширина, что у боковой панели
|
||||
(читалка уже боковой панели бессмысленна).
|
||||
6. **Свёрнутая панель уходит из раскладки вместе со своим разделителем**, а не сжимается
|
||||
5. **Свёрнутая панель уходит из раскладки вместе со своим разделителем**, а не сжимается
|
||||
`collapsible`-ом до нуля: при сжатии на её месте оставался бы промежуток разделителя, и поле
|
||||
оболочки переставало быть замеренными 8px. Размеры при этом не теряются — `useDefaultLayout`
|
||||
хранит раскладку по СОСТАВУ видимых панелей (ключ storage = id группы + id панелей), и это
|
||||
|
|
@ -426,163 +417,94 @@ rm -f *.deb && cd ..
|
|||
- **`import.meta.glob` по `*.module.css` брать с `?raw`, а не `?inline`:** `?inline` отдаёт уже
|
||||
скомпилированный CSS с хешированными именами (`._shell_1abc_1`), и сверять с ним имена
|
||||
из TSX бессмысленно.
|
||||
### 5.4. Что именно проверено живым нарушением
|
||||
### 5.4. Гейты: что проверено живым нарушением и чего они не видят
|
||||
|
||||
> Прежние **§5.4.1–5.4.3** (перечни форм по сессиям) слиты СЮДА: ссылки на них из кода
|
||||
> (`src/generality.test.ts`) и из зонного журнала резолвятся в этот раздел.
|
||||
|
||||
Формулировка «гейты проверены живым нарушением» без перечня форм — ловушка: она звучит как
|
||||
машинная гарантия, а покрывает ровно те входы, которые придумал автор. Ниже полный список
|
||||
проверенных форм; каждая роняла проверку, после отката дерево зелёное.
|
||||
машинная гарантия, а покрывает ровно те входы, которые придумал автор. Поэтому граница названа
|
||||
в обе стороны. **Метод, обязательный к повторению:** формы прогоняются разом одним временным
|
||||
файлом, ожидание записано против каждой строки, файл уносится после прогона, откат — копией
|
||||
файла, а НЕ `git checkout` (в дереве лежат незакоммиченные правки чужих зон); после отката
|
||||
дерево зелёное.
|
||||
|
||||
**CSS:** hex-литерал в модуле · hex в сокращённой записи `border` · `rgb()` · `color()`
|
||||
в сокращённой записи · `font-size` числом · именованное отключение правила
|
||||
(`/* stylelint-disable color-no-hex */`) · **голое** `/* stylelint-disable */` в шапке файла.
|
||||
**Что ловится — по швам** (полные перечни форм каждого захода — в истории git этого файла;
|
||||
здесь классы, потому что список форм растёт, а классы держат):
|
||||
|
||||
**TSX:** hex всех четырёх длин (`#RGB`, `#RGBA`, `#RRGGBB`, `#RRGGBBAA`) · цветовые функции
|
||||
включая `color()` и `light-dark()` · одиннадцать форм инлайнового стиля (литерал, спред,
|
||||
вынесенная переменная, приведение `as`, свойство объекта, вызов фабрики, тернарник,
|
||||
вычисляемый ключ, JSX-спред, смешанный литерал) · импорт глобального CSS мимо `main.tsx` ·
|
||||
предупреждение линта при `--max-warnings 0`.
|
||||
- **цвет и размер в CSS:** hex во всех записях, включая сокращённую `border`; `rgb()`/`color()`;
|
||||
системный и именованный цвет в `outline`/`caret-color`/`box-shadow`; `font-size` числом;
|
||||
`padding`/`margin`/`gap`/`border-radius` литералом; `calc()` — намеренно тоже; отключение
|
||||
правила комментарием, включая **голое** `/* stylelint-disable */` в шапке файла;
|
||||
- **цвет и размер в TSX:** hex всех четырёх длин, цветовые функции включая `light-dark()`,
|
||||
именованные и системные цвета в `fill`/`stroke`/`color`, одиннадцать форм инлайнового стиля
|
||||
(литерал, спред, вынесенная переменная, приведение `as`, свойство объекта, вызов фабрики,
|
||||
тернарник, вычисляемый ключ, JSX-спред, смешанный литерал), тернарник и шаблонная строка
|
||||
в значении CSS-переменной, импорт глобального CSS мимо `main.tsx`;
|
||||
- **сеть:** все восемь транспортов (`fetch` · `EventSource` · `WebSocket` · `XMLHttpRequest` ·
|
||||
те же через `window`/`globalThis`/`self` · `navigator.sendBeacon`), в `src/api/**` все восемь
|
||||
проходят;
|
||||
- **швы импорта:** `react-aria-components` вне `src/ui/` и моки вне `src/api/` — статическим
|
||||
И динамическим импортом. Причина дубля селектором: `no-restricted-imports` разбирает только
|
||||
ОБЪЯВЛЕНИЕ, а `await import(…)` — выражение;
|
||||
- **структурные тесты:** опечатка в имени класса CSS Module (в том числе через соседний модуль
|
||||
и через деструктуризацию), опечатка в имени токена в CSS и в данных TS, число в `measures.ts`,
|
||||
разошедшееся с одноимённым токеном, модуль стилей, который никто не импортирует, маршрут,
|
||||
забытый в `scripts/shot.mjs`, литерал `lang="zh"` в разметке и `:lang(zh)` в стилях;
|
||||
- **контракт API:** правка спеки без пере-генерации типов · правка генерённого файла руками ·
|
||||
битый `$ref` · значение словаря, выпавшее из перечня ветки неизвестного (`tsc` роняет TS2344
|
||||
«does not satisfy the constraint 'never'») · мажор спеки, разошедшийся с `supportedMajor`
|
||||
клиента — без него спека, бампнутая до 1.x, отвергала бы каждый живой поток в рантайме
|
||||
вместо падения на сборке.
|
||||
|
||||
**Сеть (все восемь форм ловятся, в `src/api/**` все восемь проходят):** `fetch` · `EventSource` ·
|
||||
`WebSocket` · `XMLHttpRequest` · `window.fetch` · `globalThis.fetch` · `self.fetch` ·
|
||||
`navigator.sendBeacon`. Проверялось файлом на диске, а не через `--stdin`: тип-осведомлённый
|
||||
линт отказывается разбирать stdin («was not found by the project service»).
|
||||
**Ловушки, найденные исполнением, — не переоткрывать:**
|
||||
|
||||
**Структурные тесты:** опечатка в имени класса CSS Module · то же при переименованном импорте
|
||||
модуля · опечатка в имени токена в CSS · опечатка в имени токена в данных TS · маршрут добавлен
|
||||
в `routes.tsx` и забыт в `scripts/shot.mjs` · литерал `lang="zh"` возвращён в разметку.
|
||||
|
||||
**Что НЕ ловилось и осталось дырой после S1:** `/* eslint-disable */` в TSX (см. правило 2) ·
|
||||
JSX-спред `<div {...props} />` со `style` внутри объекта · именованные цвета CSS в TSX
|
||||
(`fill="red"`). Две последние закрыты в S2 (Ф-9), первая закрыта правилами
|
||||
`@eslint-community/eslint-comments` ещё в S1 — см. правило 2.
|
||||
|
||||
### 5.4.1. Что добавила S2 (04.08) — перечень форм
|
||||
|
||||
Гейты прогонялись разом: один временный файл со всеми формами, один прогон линтера, ожидание
|
||||
записано против каждой строки. Файлы уносились после прогона, дерево после отката зелёное.
|
||||
|
||||
**Цвет в TSX (Ф-9), 15 форм.** Падают: именованный цвет в `fill` · именованный цвет в `color`
|
||||
иконки · системный цвет в `stroke` через выражение · цвет в CSS-переменной `style={{'--x':'red'}}` ·
|
||||
системный цвет там же · размер литералом там же (`'10px'`) · hex-литерал · цветовая функция ·
|
||||
обычное свойство в инлайновом стиле · `{ style: {...} }` в объектном литерале · `{ 'style': {...} }`
|
||||
строковым ключом · импорт `react-aria-components` вне `src/ui/`.
|
||||
Проходят (и обязаны): `fill="none"` · `fill="currentColor"` · число в CSS-переменной
|
||||
(`'--progress': 0.5`) · токен в CSS-переменной (`var(--space-4)`).
|
||||
|
||||
⚠ Ловушка, найденная исполнением: селектор `Property[key.value=/^--/] > Literal[…]` ловил
|
||||
СОБСТВЕННЫЙ ключ (имя переменной — тоже прямой потомок Property) и падал на легальном
|
||||
`style={{ '--progress': x }}`. Поэтому в списке разрешённых значений стоит и префикс `--`.
|
||||
|
||||
**Отступы и системные цвета в CSS (Ф-4, Ф-9), 14 форм.** Падают: `padding: 8px` ·
|
||||
`margin: 0 4px` · `gap: 4px` · `border-radius: 6px` · `border-top-left-radius: 4px` ·
|
||||
`calc(var(--space-4) + 2px)` · системный цвет в `outline` · в `caret-color` · в `box-shadow` ·
|
||||
именованный цвет · hex в сокращённой записи `border`.
|
||||
Проходят: `padding: var(--space-4)` · `padding-top: 0` · `margin-left: auto`.
|
||||
|
||||
⚠ **Обход, найденный адверсариальным ревью и НЕ прошедший:** объявить в модуле свою переменную
|
||||
(`--local-pad: 13px`) и подставить её (`padding: var(--local-pad)`). Форму обёртки stylelint
|
||||
принимает — но падает контракт-тест токенов, который требует, чтобы каждое имя `var(--…)` было
|
||||
объявлено в `tokens.css`. То есть гейт держит вторым слоем, а не первым; проверено исполнением.
|
||||
|
||||
**Структурные тесты (Ф-10), 7 форм — каждая роняет `vitest`.** Опечатка в классе у компонента,
|
||||
импортирующего СОСЕДНИЙ модуль (прежде выпадала из проверки) · опечатка в классе через
|
||||
деструктуризацию `const { x } = styles` · расширение из `url(./x.woff2)`, притворяющееся
|
||||
объявленным классом · новый токен, не попавший ни в замеры, ни в подобранные · токен, оставшийся
|
||||
в списке подобранных после удаления из `tokens.css` · число в `measures.ts`, разошедшееся
|
||||
с одноимённым токеном · модуль стилей, который никто не импортирует.
|
||||
|
||||
**Хвосты ревью оркестратора (04.08), 11 форм — один прогон линтера, 7 падений и 4 прохода.**
|
||||
Падают: динамический `import('../mock/book')` · динамический `import('../tokens/tokens.css')` ·
|
||||
динамический `import('react-aria-components')` вне `src/ui/` · то же из `src/api/` (фикстуры ему
|
||||
можно, примитивы — нет) · динамический импорт моков из `src/ui/` · тернарник в значении
|
||||
CSS-переменной (`{'--x': flag ? 'red' : 'blue'}`) · шаблонная строка там же.
|
||||
Проходят (и обязаны): динамический импорт `*.module.css` · идентификатор в значении переменной ·
|
||||
`import('react-aria-components')` внутри `src/ui/` · `import('../mock/book')` внутри `src/api/`.
|
||||
Причина всей пачки одна: `no-restricted-imports` разбирает только ОБЪЯВЛЕНИЕ импорта, а
|
||||
`await import(…)` — выражение, поэтому каждый шов продублирован селектором и точечные послабления
|
||||
у обеих половин совпадают по имени шва.
|
||||
|
||||
**Языковая ветка в CSS (`generality.test.ts`, вторая половина).** Падает `:lang(zh) .source { … }`
|
||||
в любом файле стилей — проверено живым нарушением с восстановлением файла из памяти, не из git.
|
||||
Комментарии из CSS снимаются перед проверкой: правило объясняется в том же файле, где действует,
|
||||
и объяснение содержит запрещённую форму дословно.
|
||||
|
||||
### 5.4.2. Замки контракта API (04.08) — четыре формы, каждая проверена нарушением
|
||||
|
||||
Гейт контракта — шестой шаг `npm run check` плюс тесты `src/api/contract.test.ts`.
|
||||
Каждая форма роняла проверку, после отката дерево зелёное; откат — копией файла, не `git`.
|
||||
|
||||
Падают: **правка спеки без пере-генерации типов** (в `openapi.yaml` добавлено поле, тест дрифта
|
||||
сравнил свежую генерацию с закоммиченной и упал) · **правка генерённого файла руками**
|
||||
(в `schema.ts` вписан лишний тип — тот же тест) · **битый `$ref` в спеке**
|
||||
(`invalid-ref` от spectral, `npm run contract` = 1) · **значение словаря, выпавшее из перечня
|
||||
ветки неизвестного** (`awaiting_bank` убран из `bookStatuses` → `tsc` роняет TS2344
|
||||
«Type '"awaiting_bank"' does not satisfy the constraint 'never'»).
|
||||
|
||||
⚠ Ложное срабатывание, найденное этим же заходом и починенное по классу: контракт-тест токенов
|
||||
собирает имена по кавычкам вокруг `--…`, и флаг движка в доккомментарии генерённого файла
|
||||
(`` `--verify-bank` ``) для него неотличим от имени токена. Генерённый файл из скана исключён
|
||||
с названной причиной; остаточная граница честная — обратные кавычки вокруг `--флаг`
|
||||
в АВТОРСКОМ комментарии всё ещё дадут ложное падение, но падение громкое, а не тихое.
|
||||
|
||||
### 5.4.3. Замки слоя данных (S3, 08.08) — семь форм, каждая проверена нарушением
|
||||
|
||||
Прогон одним заходом, откат — копией файла, не `git checkout` (в дереве лежат незакоммиченные
|
||||
правки чужих зон). После отката `npm run check` зелёный.
|
||||
|
||||
**Падают:**
|
||||
|
||||
1. **Значение словаря выброшено из `src/api/vocabulary.ts`** — `tsc` роняет `TS2345`, называя
|
||||
пропавшее значение по имени (`Property 'paused' is missing`). Это и есть механизм «новое
|
||||
состояние правит ОДИН файл»: словарь объявлен как `Record<Значение, Смысл>`, поэтому список
|
||||
значений выводится из записей и разойтись с типом физически не может.
|
||||
2. **Спека правлена без пере-генерации типов** — тест дрифта (`contract.test.ts`).
|
||||
3. **Мажор спеки разошёлся с мажором, который говорит клиент** — новый тест: `info.version`
|
||||
спеки читается из файла и сверяется с `supportedMajor` в `stream.ts`. Без него спека, бампнутая
|
||||
до 1.x, отвергала бы каждый живой поток в рантайме вместо падения на сборке.
|
||||
4. **Новый маршрут в `routes.tsx`, забытый в `scripts/shot.mjs`** — `routes.test.ts`.
|
||||
5. **`EventSource` прямо в экране** — `no-restricted-globals` с проектным сообщением.
|
||||
6. **`window.fetch` прямо в экране** — `no-restricted-properties`.
|
||||
7. **Импорт `../mock/*` в экране, статический И динамический** — `no-restricted-imports` плюс
|
||||
селектор на `ImportExpression`.
|
||||
|
||||
**Проходят (и обязаны):** `new EventSource(...)` внутри `src/api/**`.
|
||||
|
||||
**Что этот слой гейтом НЕ закрыт, названо честно:** соответствие MSW-хендлеров контракту держится
|
||||
типами тел (`json<Schemas['X']>`), но ПУТЬ хендлера — обычная строка, и опечатка в нём даёт
|
||||
`onUnhandledRequest`, а не падение сборки; в тестах это ловится (`'error'`), в браузере хендлер
|
||||
просто не сработает. Второе: пагинация проверена на мире `scale`, где страницы настоящие, но
|
||||
гейта, требующего страничности от каждого нового мира фикстур, нет.
|
||||
- селектор `Property[key.value=/^--/] > Literal[…]` ловит СОБСТВЕННЫЙ ключ (имя переменной — тоже
|
||||
прямой потомок `Property`) и падал на легальном `style={{ '--progress': x }}`; поэтому в списке
|
||||
разрешённых значений стоит и префикс `--`;
|
||||
- обход «объявить свою переменную в модуле (`--local-pad: 13px`) и подставить её» форму обёртки
|
||||
stylelint принимает — его ловит контракт-тест токенов, требующий, чтобы каждое имя `var(--…)`
|
||||
было объявлено в `tokens.css`. Гейт держит ВТОРЫМ слоем, а не первым;
|
||||
- контракт-тест токенов собирает имена по кавычкам вокруг `--…`, и флаг движка в доккомментарии
|
||||
генерённого файла (`` `--verify-bank` ``) для него неотличим от имени токена. Генерённый файл
|
||||
из скана исключён; остаточная граница честная — обратные кавычки вокруг `--флаг` в АВТОРСКОМ
|
||||
комментарии всё ещё дадут ложное падение, но падение громкое, а не тихое;
|
||||
- тип-осведомлённый линт отказывается разбирать `--stdin` («was not found by the project
|
||||
service») — проверять нарушение только файлом на диске;
|
||||
- комментарии из CSS снимаются перед проверкой языковой ветки: правило объясняется в том же
|
||||
файле, где действует, и объяснение содержит запрещённую форму дословно.
|
||||
|
||||
**Чего гейты по-прежнему не видят — перечислено, потому что «закрыто» без границ и есть та самая
|
||||
ловушка, о которой этот раздел** (найдено адверсариальным ревью S2, каждая граница воспроизведена):
|
||||
ловушка, о которой этот раздел** (найдено адверсариальным ревью, каждая граница воспроизведена):
|
||||
|
||||
- **соответствие мок-хендлера контракту по ПУТИ.** Тело типизировано (`json<Schemas['X']>`), путь —
|
||||
обычная строка: опечатка даёт `onUnhandledRequest`, а не падение сборки. В тестах она ловится
|
||||
(`'error'`), в браузере хендлер просто молчит;
|
||||
- **вычисленное значение цвета.** Гейт на цветных атрибутах ловит литерал и шаблонную строку;
|
||||
- **страничность нового мира фикстур.** Пагинация проверена на мире `scale`, где страницы
|
||||
настоящие, но гейта, требующего страничности от каждого нового мира, нет;
|
||||
- **вычисленное значение цвета.** Гейт ловит литерал и шаблонную строку;
|
||||
`const c = 'red'; <svg fill={c}/>`, тернарник, вызов функции и JSX-спред проходят. Синтаксически
|
||||
это не ловится, а типовое правило под такую проверку в `typescript-eslint` отсутствует;
|
||||
это не ловится, а типового правила под такую проверку в `typescript-eslint` нет;
|
||||
- **значение CSS-переменной, собранное вызовом или вынесенное в идентификатор**
|
||||
(`style={{'--x': compute()}}`, `style={{'--x': value}}`) — проверять статически нечего, а запрет
|
||||
идентификатора убил бы само исключение. Тернарник и шаблонная строка с 04.08 ловятся: это
|
||||
носители литерала, и обход через них был буквальным;
|
||||
идентификатора убил бы само исключение;
|
||||
- **значения стилей в тесте общности.** `generality.test.ts` видит разметку и селекторы, но не
|
||||
значения: именованная гарнитура письменности в токене (`--font-cjk: 'Noto Sans CJK SC', …`)
|
||||
действует как `:lang(zh)`, только молча. Ровно такой токен и приехал в S2 — снял его человек
|
||||
действует как `:lang(zh)`, только молча. Ровно такой токен однажды и приехал — снял его человек
|
||||
на ревью, не гейт; список именованных фейсов в правиле был бы хрупким по построению;
|
||||
- **императивный стиль** (`node.style.padding = '13px'`, `setAttribute('style', …)`) — в коде
|
||||
такого нет ни одного случая, но правилом это не запрещено;
|
||||
- **отключение TSX-гейта комментарием.** Оно обязано называть правило и нести причину, но проходит;
|
||||
в CSS-половине `reportDisables` роняет даже именованное. Асимметрия честно записана в комментарии
|
||||
конфига, а не выдана за симметрию, как было раньше;
|
||||
- **`width`/`height`/`top`/`left` литералами в CSS:** Ф-4 по STACK_DECISIONS §3 закрывает
|
||||
в CSS-половине `reportDisables` роняет даже именованное. Асимметрия записана в комментарии
|
||||
конфига, а не выдана за симметрию;
|
||||
- **`width`/`height`/`top`/`left` литералами в CSS:** гейт отступов закрывает
|
||||
`padding`/`margin`/`gap`/`border-radius`, а собственные размеры компонента (точка 6px,
|
||||
полоска 24×3) остаются на ревью глазами;
|
||||
- **ложные срабатывания, которые мы принимаем:** строка вида `#1234` объявляется цветом (это
|
||||
четырёхзначный hex), а поле объекта с именем `style` — инлайновым стилем. Оба случая лечатся
|
||||
переименованием и стоят дешевле, чем пропущенный литерал.
|
||||
|
||||
|
||||
### 5.5. Пере-замер S3.5 (09.08) — что нашёл замер, а не глаз
|
||||
|
||||
Замечания владельца 1 и 5 требовали работать замером. Инструмент замера теперь живёт в репозитории
|
||||
|
|
@ -607,8 +529,7 @@ CSS-переменной (`{'--x': flag ? 'red' : 'blue'}`) · шаблонна
|
|||
на 0 · 328 · 944 · 1272. Это и есть ответ на «особенно заметно по тексту снизу»: полосы у Fleet
|
||||
прижаты к краям окна, а не отступают от них на поле оболочки, и текст в них центрируется по всей
|
||||
полосе. Поле оболочки поэтому принадлежит области ПАНЕЛЕЙ, а не окну целиком — иначе поле полосы
|
||||
складывается с полем оболочки, и текст уезжает вдвое дальше от края, чем в референсе (эту ошибку
|
||||
первый заход S3.5 и совершил: было 14, стало бы 20 при цели 12.5; поймало адверсариальное ревью).
|
||||
складывается с полем оболочки, и текст уезжает вдвое дальше от края, чем в референсе.
|
||||
После правки замер сходится с референсом по обеим осям: текст статус-полосы `x 12.5…1267.0`,
|
||||
`y 744.5…756.0` против `12.5…1266.5`, `744.5…756.0` у Fleet.
|
||||
|
||||
|
|
@ -620,12 +541,11 @@ CSS-переменной (`{'--x': flag ? 'red' : 'blue'}`) · шаблонна
|
|||
1x: «штрих 2 при 16» и «размер 18, штрих 2» дают жирнее, но по-прежнему мыльно; полупиксельный
|
||||
сдвиг SVG даёт целые линии. Взят он (`reset.css`).
|
||||
|
||||
**Мера набора читалки.** На кадре 2560×1440 строка перевода вырастала до ~150 знаков: боковые панели
|
||||
держат функциональную ширину в пикселях, и вся лишняя ширина доставалась центру. S3.5 ввела
|
||||
`--reader-width: 1040px` с центрированием блоков — ~75 знаков в колонке. ⚠ **Отменено 10.08:**
|
||||
владелец назвал это «жёстко приклеен к центру», токен удалён, набор снова во всю ширину панели;
|
||||
на 2560×1440 это ~120 знаков в строке. Ограничивать ширину повторно нельзя — решение владельца. Функциональную ширину
|
||||
боковых панелей намеренно НЕ переводили в долю кадра: 25% у Fleet это следствие окна 1280, а
|
||||
**Мера набора читалки.** ⚠ **Ограничивать ширину набора нельзя — решение владельца 10.08:**
|
||||
попытку центрировать колонку фиксированным `--reader-width` он назвал «жёстко приклеен к центру»,
|
||||
токен удалён. Набор идёт во всю ширину панели; на 2560×1440 это ~120 знаков в строке (боковые
|
||||
панели держат функциональную ширину в пикселях, и вся лишняя ширина достаётся центру).
|
||||
Функциональную ширину боковых панелей намеренно НЕ переводили в долю кадра: 25% у Fleet это следствие окна 1280, а
|
||||
не инвариант — 640px под дерево разделов были бы пустой ширины.
|
||||
|
||||
---
|
||||
|
|
@ -644,17 +564,16 @@ CSS-переменной (`{'--x': flag ? 'red' : 'blue'}`) · шаблонна
|
|||
| выбранная строка | `#184176` (срез строки CardPicker.tsx) | — | `--color-selected: #1c4478` |
|
||||
| синий/зелёный акценты | — | `#2964ad` / `#3c7d4f` (NOTE/TIP) | `--color-note` / `--color-ok` |
|
||||
|
||||
Главное, что дал замер: **оба референса строго нейтральны, R=G=B**. То есть тёплый подтон,
|
||||
внесённый S3.5 «по рекомендациям про тёмные фоны», противоречил тем самым кадрам, на которые
|
||||
сессия ссылалась. Второй вывод — про вкладки: срез по вертикали через активную вкладку fleet_2
|
||||
(x=790, y=36‥68) даёт `#181818` без единой разделительной линии до содержимого, тогда как
|
||||
соседняя неактивная лежит на `#292929`.
|
||||
Главное, что дал замер: **оба референса строго нейтральны, R=G=B** — тёплый подтон «по
|
||||
рекомендациям про тёмные фоны» противоречил тем самым кадрам, на которые ссылались. Второй
|
||||
вывод — про вкладки: срез по вертикали через активную вкладку fleet_2 (x=790, y=36‥68) даёт
|
||||
`#181818` без единой разделительной линии до содержимого, тогда как соседняя неактивная лежит
|
||||
на `#292929`.
|
||||
|
||||
⚠ **Поправка S3.7 (10.08): вывод про вкладки в коде НЕ стоит.** Модель «вырез в хроме» была
|
||||
построена по этому замеру и тем же паком ОТКАЧЕНА по слову владельца — в `Tabs.module.css`
|
||||
сегодня пилюля по `fleet.png`: полоса вкладок это полотно панели, активная вкладка лежит НА нём
|
||||
скруглённой подложкой (`--color-raised`, у документа `--color-tab-active`). Замер остаётся верным
|
||||
про сам референс; описание кода в этом абзаце было неверным.
|
||||
⚠ **Этот вывод про вкладки в коде НЕ воплощён, и воплощать его не надо:** модель «вырез в хроме»
|
||||
была построена по нему и ОТКАЧЕНА по слову владельца. В `Tabs.module.css` пилюля по `fleet.png` —
|
||||
полоса вкладок это полотно панели, активная вкладка лежит НА нём скруглённой подложкой
|
||||
(`--color-raised`, у документа `--color-tab-active`). Замер верен про сам референс, не про наш код.
|
||||
|
||||
Способ повторить (проверено исполнением 10.08 в S3.7, числа сошлись): `scripts/measure.py` для
|
||||
поверхностей и промежутков, `PIL.Image` + `collections.Counter` по `im.getdata()` для общей
|
||||
|
|
@ -681,66 +600,45 @@ CSS-переменной (`{'--x': flag ? 'red' : 'blue'}`) · шаблонна
|
|||
| линия в 1px на x=515 | — | есть | не дефект: жёлоб читалки покрашен в цвет оболочки (замечание второго круга 5) |
|
||||
| полосы по вертикали | верхняя 0‥36, статус 735.5‥764 | 0‥37 и 735‥764 | сошлось (полпикселя — округление среза) |
|
||||
|
||||
То есть за два пака поехали ровно те числа, которые двигали намеренно, и ни одного сверх того;
|
||||
разошлось бы что-то ещё — эта таблица и была бы местом, где это видно.
|
||||
## 6. Форма данных — из контракта, не из пересказа
|
||||
|
||||
## 6. Что уже известно про движок — учтено в форме данных
|
||||
⚠ **Здесь стоял пересказ того, что движок отдаёт фронту, и он отстал от канона.** Пересказ снят:
|
||||
единственный носитель формы — ратифицированный контракт `docs/architecture/14-api-contract/`
|
||||
(спека + спутник с провенансом, обоснованиями и К-вопросами). Разделы спутника по номерам:
|
||||
язык кодом — §2.1, непрозрачные идентификаторы — §2.2, заголовок главы — §2.3, состояние прогона
|
||||
против выполнения главы — §2.4, прогресс — §2.5, состояние пары — §2.7, подпись банка как набор
|
||||
решений — §2.9, разрешающий список полей — §2.12, карта «причина → продуктовая фраза» —
|
||||
приложение А. Цена расхождения замерена на этой же зоне: редакция 0.3.0 сняла пофазный
|
||||
сплит с провода (К-10 закрыт вердиктом «НЕ строить», D39.138), а здешний абзац ещё учил строить
|
||||
две пары счётчиков.
|
||||
|
||||
Из `FRONTEND_SESSION_PROMPT.md` §9 и `STACK_DECISIONS.md` §8. Влияет на типы в `src/api/`
|
||||
уже сейчас, поэтому записано в плане, а не откладывается на S3.
|
||||
Что остаётся правилом ФРОНТА, а не контракта:
|
||||
|
||||
1. **Прогресс — пофазный, не «готово N из M».** Юнит становится `done` только когда есть и
|
||||
черновые строки всех членов, и строка редактуры: во время черновой волны сквозной счётчик
|
||||
стоит на нуле почти всё время. Тип прогресса — две пары `{ done, total }` (черновик ∥ редактура),
|
||||
а не одно число. Пользователю всё равно показывается одна полоса без названий фаз (§4.1).
|
||||
⚠ Это правило было записано здесь ещё в S0 — и витрина S1 всё равно вышла с наивным
|
||||
счётчиком глав, потому что фикстура выдумала поле `translatedChapters`, которого read-модель
|
||||
не отдаёт. Лечение сильнее правила: фикстура поставлена в состояние «идёт черновая волна»,
|
||||
где наивный счётчик дал бы ноль, и скриншот-цикл видит трудный случай, а не удобный.
|
||||
1а. **Состояние — у ПРОГОНА над книгой, у главы его нет.** Подпись банка — один стоп между
|
||||
черновой и редакторской волнами на всю книгу (`mining.go:199`), поэтому «глава ждёт подписи,
|
||||
пока соседняя финализируется» — картина, которую движок породить не может. У главы есть
|
||||
выполнение: `ChapterPassport` (`status.go:37-55`) несёт `units_total/done/in_progress/pending`
|
||||
и вердикт. В интерфейсе: лестница из девяти состояний — на строке книги, полоса выполнения —
|
||||
на строке главы. Девять состояний точкой не различаются (проверено снимком: три пары
|
||||
совпадали) — метка словом, цвет только дублирует и только для внимания и отказа.
|
||||
1б. **Язык — код, а не слово.** Движок держит `source_lang`/`target_lang` кодами (`book.go:26-27`),
|
||||
ключ пары — `zh-ru`; контракт отдаст то же самое. Человеческое имя считается на экране
|
||||
через `Intl.DisplayNames`, `lang` на элементе берётся из данных книги (правило 17).
|
||||
2. **Подпись термина — не UPDATE строки.** Банк пересобирается из файлов на каждом прогоне,
|
||||
прямая запись стирается следующим прогоном. Контракт подписи — отправка набора решений, а не
|
||||
правка записи; фикстуры S3 моделируют именно это.
|
||||
3. **Байтовых смещений у замечаний нет.** Замечание адресует блок или главу — и только.
|
||||
Тип замечания не имеет полей `offset`/`length`; подчёркиваний внутри текста не будет.
|
||||
4. **Единица пары — edit-unit, ~1.9 на главу.** Местами вся глава окажется одним блоком.
|
||||
Читалка обязана нормально выглядеть в этом случае; выравнивание грубое, по единицам экспорта.
|
||||
5. **Одна вертикальная прокрутка на читалку.** Список строк-пар, внутри строки
|
||||
1. **Единица пары — edit-unit, ~1.9 на главу** (замер движка, ПТ-21): местами вся глава окажется
|
||||
одним блоком, и читалка обязана нормально выглядеть в этом случае.
|
||||
2. **Одна вертикальная прокрутка на читалку.** Список строк-пар, внутри строки
|
||||
`grid-template-columns: 1fr 1fr`. Синхронизации двух прокруток нет и не будет — колонки
|
||||
физически не могут разъехаться.
|
||||
|
||||
Денежных полей в интерфейсе нет нигде (§4.8): ни сумм, ни потолков, ни оценки, ни остатка.
|
||||
физически не могут разъехаться (`STACK_DECISIONS.md` §2).
|
||||
3. **Денежных полей в интерфейсе нет нигде** (§4.8 промта): ни сумм, ни потолков, ни оценки,
|
||||
ни остатка.
|
||||
4. **Фикстура ставится в ТРУДНЫЙ случай, а не в удобный.** Правило «прогресс не строить на
|
||||
„готово N из M“» было записано здесь ещё на S0 — и витрина всё равно вышла с наивным
|
||||
счётчиком глав, потому что фикстура выдумала поле, которого read-модель не отдаёт. Лечение
|
||||
сильнее правила: мир фикстур поставлен в состояние, где наивный счётчик дал бы ноль,
|
||||
и скриншот-цикл смотрит на трудный случай.
|
||||
|
||||
---
|
||||
|
||||
## 7. Порядок S1 и что в него НЕ входит
|
||||
## 7. Pre-commit хук зоны
|
||||
|
||||
Порядок работ S1: каркас → `npm run check` → скриншот-цикл и проверка, что он живой →
|
||||
`tokens.css` → витрина → контракт-тест токенов → сверка витрины с референсом.
|
||||
Хук один — pre-commit (запрос владельца 02.08; CI в репозитории отсутствует, до его появления
|
||||
хук — единственный машинный рубеж). Зонный фрагмент `scripts/githooks/pre-commit` зовёт тот же
|
||||
`npm run check` (один список инструментов, не дубль) только когда в коммите есть frontend-пути;
|
||||
плюс блок файлов «никогда не коммитить» и блок смеси `frontend/` с чужой зоной (картина
|
||||
инцидентов D39.88). Ставится сам: `npm install` через `prepare` кладёт диспетчер
|
||||
в `.git/hooks/pre-commit` (идемпотентно, чужой хук не перетирает). Осознанный обход —
|
||||
`git commit --no-verify`.
|
||||
|
||||
`npm run check` = `prettier --check` → `eslint` → `stylelint` → `tsc --noEmit` → `vitest run`.
|
||||
Шагов пять, а не четыре: `STACK_DECISIONS.md` §3 перечисляет четыре, но там же требует, чтобы
|
||||
обе половины гейта цвета жили в одной команде, а CSS-половину гоняет именно stylelint.
|
||||
`npm run check:full` = `check` → `vite build` → `shot`. Отдельного e2e-набора в S1 нет:
|
||||
единственная браузерная проверка — скриншот-цикл, он и стоит в `check:full`.
|
||||
Git-хук один — pre-commit (запрос владельца 02.08, вторая фронт-сессия; отменяет прежнее
|
||||
«хуков нет»: CI в репозитории отсутствует, до его появления хук — единственный машинный рубеж).
|
||||
Зонный фрагмент `scripts/githooks/pre-commit` зовёт тот же `npm run check` (один список
|
||||
инструментов, не дубль) только когда в коммите есть frontend-пути; плюс блок файлов
|
||||
«никогда не коммитить» и блок смеси frontend/ с чужой зоной (картина инцидентов D39.88).
|
||||
Ставится сам: `npm install` через prepare кладёт диспетчер в `.git/hooks/pre-commit`
|
||||
(идемпотентно, чужой хук не перетирает). Осознанный обход — `git commit --no-verify`.
|
||||
Состав `npm run check` и `check:full` — `STACK_DECISIONS.md` §3 (там он сверен по `package.json`);
|
||||
копии состава здесь нет намеренно, она отставала на два пака.
|
||||
|
||||
**Не входит в S1** (и не должно появиться раньше срока): экраны, оболочка трёх панелей,
|
||||
слой данных и MSW, React Compiler (Ф-2), токен-гейт на отступы (Ф-4 — включается, когда
|
||||
шкала отступов устоится, иначе мешает подбору), эталонные скриншоты в тестах (отложены
|
||||
решением `STACK_DECISIONS.md` §3), светлая тема, мобильная раскладка.
|
||||
|
|
|
|||
|
|
@ -1,32 +1,12 @@
|
|||
> ⚠ **S0–S3 ОТРАБОТАНЫ. Файл остаётся ЖИВЫМ, но не как задание, а как носитель норм зоны** — на него
|
||||
> так ссылаются `S3_SESSION_PROMPT.md` и `frontend/README.md`. **Живое здесь:** §«Зона и git», §3
|
||||
> сценарий, §4 жёсткие ограничения, §7 «как работать». **Историческое:** §«Область ЭТОЙ сессии»
|
||||
> (там сказано «этапы S0 и S1, дальше не идти» — это уже неверно) и таблица этапов, где S3 стоит
|
||||
> как «хвост S2 или отдельная». **Статус этапов — ТОЛЬКО `frontend-PROGRESS.md`**, здесь его нет.
|
||||
> ⚠ **ЭТО НЕ ЗАДАНИЕ, а носитель норм зоны** — референсы с измеренными значениями, продуктовый
|
||||
> сценарий экранов, жёсткие ограничения владельца и порядок работы. На него так ссылается
|
||||
> `frontend/README.md`. **Статус этапов и что уже построено — ТОЛЬКО `frontend-PROGRESS.md`**,
|
||||
> здесь этого нет намеренно (копия отставала); очередь этапов S2–S7 — `BACKLOG.md` Ф-1.
|
||||
> Поставлено оркестратором №15 08.08 по слепому онбординг-замеру: холодная сессия открывала файл
|
||||
> первым по указателям и читала запрет «дальше не идти».
|
||||
> первым по указателям и читала запрет «дальше не идти», адресованный давно отработавшей сессии.
|
||||
|
||||
# Промт: сессия ФРОНТ TextMachine — MVP веб-интерфейса
|
||||
|
||||
## Область ЭТОЙ сессии — этапы S0 и S1, дальше не идти
|
||||
|
||||
Фронт режется на шесть сессий. Ты — первая. **Твоя работа кончается на витрине токенов, до единого
|
||||
экрана приложения.** Это не осторожность: без проверяемой петли обратной связи и сошедшихся токенов
|
||||
все следующие этапы слепы, а смешивание инструментальной работы с интерфейсной сжигает контекст.
|
||||
|
||||
| Этап | Что | Сессия |
|
||||
|---|---|---|
|
||||
| **S0** | Планирование: как писать фронт. Пины версий, карта файлов, правила, протокол проверки | **твоя** |
|
||||
| **S1** | Инструменты, скриншот-цикл, `tokens.css`, витрина | **твоя** |
|
||||
| S2 | Оболочка (три панели, вкладки, статус-полоса) + слой `src/ui/` | отдельная |
|
||||
| S3 | Слой данных, MSW, фикстуры всех состояний | хвост S2 или отдельная |
|
||||
| S4 | Библиотека, загрузка, разбор, прогресс перевода | отдельная |
|
||||
| S5 | Банк памяти и подпись (самый тяжёлый) | отдельная |
|
||||
| S6 | Читалка двух колонок и замечания | отдельная |
|
||||
| S7 | Метаданные, экспорт, настройки, сквозной прогон | отдельная |
|
||||
|
||||
**S0 лендится отдельным коммитом до первой строки кода** — иначе план останется словами.
|
||||
|
||||
## Обязательное чтение перед работой
|
||||
|
||||
1. `frontend/docs/STACK_DECISIONS.md` — **пины версий и ловушки**. Все версии сверены 02.08.2026
|
||||
|
|
@ -57,17 +37,20 @@
|
|||
|
||||
Веб-сервис перевода книг (веб-новеллы, ранобэ) с одного языка на другой через ИИ. Пользователь загружает книгу, запускает перевод, подписывает словарь терминов, читает результат, выгружает готовое.
|
||||
|
||||
**Бэкенда пока нет** — точнее, движок перевода есть, но HTTP-API к нему ещё не построено. Ты работаешь на моках (см. §6). Это не помеха: контракт данных зафиксирован ниже, замена моков на реальные запросы будет точечной.
|
||||
⚠ **API у ПЛАТФОРМЫ, а не у движка; движок фронту не виден вообще** (D39.85, `research/23`).
|
||||
Ошибка дорогая: моки, снятые не с того уровня контракта, разойдутся с API. Прежде чем писать слой
|
||||
данных, прочитай `FRONTEND_PLAN.md` §0.1–0.2 (карта канона и два провода) и первоисточник
|
||||
`docs/research/23-engine-platform-seam.md`.
|
||||
|
||||
> ⚠ **Поправка канона №2 (оркестратор, 04.08, D39.99/100/101):** контракт API v0 РАТИФИЦИРОВАН (`docs/architecture/14-api-contract/` — OpenAPI 3.1, типы генерятся из спеки), строка 95 закрыта; слой данных S3 строится ПО КОНТРАКТУ, не по гипотезам.
|
||||
> ⚠ **Поправка S4 (14.08):** «скелет платформы P0 существует, ждёт приёмки» устарело — **P5 принята (D39.130)**, живой `POST /books` построен, и путь загрузки книги S4 стоит на нём.
|
||||
> ⚠ **Поправка канона (фронт-сессия S1, 02.08).** Формулировка выше читается как «однажды у движка появится HTTP-API». Это не так, и ошибка дорогая: **API будет у платформы, не у движка**, а движок фронту не виден вообще. Прежде чем писать слой данных, прочитай `FRONTEND_PLAN.md` §0.1–0.2 (карта канона и два провода) и первоисточник `docs/research/23-engine-platform-seam.md`. Там же цена ошибки: моки, снятые не с того уровня контракта, разойдутся с API — ровно то, ради предотвращения чего заведена строка 95 единого бэклога.
|
||||
Контракт API v0 РАТИФИЦИРОВАН (D39.99/100/101; канон `docs/architecture/14-api-contract/` —
|
||||
OpenAPI 3.1, типы генерятся из спеки), и слой данных строится ПО КОНТРАКТУ, а не по гипотезам.
|
||||
Что у платформы уже живо и чего ещё нет — `platform/BACKLOG.md` и её зонный журнал.
|
||||
|
||||
---
|
||||
|
||||
## 1. Референсы — копировать, а не вдохновляться
|
||||
|
||||
В `frontend/references/` лежат три скриншота. **Открой их своим инструментом чтения файлов — ты умеешь смотреть изображения.** Это обязательный первый шаг, до единой строки кода.
|
||||
В `frontend/references/` лежат скриншоты-референсы (кладёт владелец, в git они не едут). **Открой их своим инструментом чтения файлов — ты умеешь смотреть изображения.** Это обязательный первый шаг, до единой строки кода.
|
||||
|
||||
- `fleet.png` — **главная основа макета**. Копируем оболочку целиком: как устроены панели, отступы, скругления, плотность, палитра.
|
||||
- `antigravity_main.png` — пустое состояние (ничего не выбрано).
|
||||
|
|
@ -86,7 +69,7 @@
|
|||
| Фон оболочки: промежутки, верхняя полоса, статус-полоса | `#090909` | 10.5% площади |
|
||||
| Заливка панели (все три панели одинаковы) | `#17191a` | 80.5% площади |
|
||||
| Приподнятая поверхность / наведение | `#27292b` | |
|
||||
| Выбранная строка, активная вкладка | `#353739` | |
|
||||
| Выбранная строка дерева, клавиша-чип | `#353739` | ⚠ **не активная вкладка:** пере-замер развёл роли, вкладка документа — `#142f4c` (`FRONTEND_PLAN.md` §5.1) |
|
||||
| Подсветка текущей строки (синеватая) | `#152945` | |
|
||||
| Выделение | `#164e8d` | |
|
||||
| Основной текст | `#dfe1e3` | |
|
||||
|
|
@ -148,37 +131,15 @@
|
|||
|
||||
## 2. Раскладка нашего приложения
|
||||
|
||||
> ⚠ Раскладка частично устарела — перевешивают «Решения владельца по продукту» в `frontend-PROGRESS.md`; метки глав — из ДАННЫХ книги, зашитого «Глава N» не существует (D39.100 К-3).
|
||||
Оболочка Fleet, наполнение наше: три сворачиваемые панели, вкладки в шапке каждой, верхняя полоса
|
||||
и статус-полоса.
|
||||
|
||||
Оболочка Fleet, наполнение наше.
|
||||
⚠ **Схемы раскладки здесь больше нет: оболочка ПОСТРОЕНА, и её описывал устаревающий рисунок.**
|
||||
Действующая раскладка — код `src/shell/` (`Shell.tsx`, `Panel.tsx`, `layout.ts`) плюс наполнение
|
||||
панелей; продуктовые решения по составу вкладок и панелей перевешивают из «Решений владельца
|
||||
по продукту» в `frontend-PROGRESS.md`.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ верхняя полоса: сворачивание панелей · название книги · действия │
|
||||
├──────────────┬────────────────────────────────┬─────────────────┤
|
||||
│ ЛЕВАЯ │ ЦЕНТР │ ПРАВАЯ │
|
||||
│ │ │ │
|
||||
│ вкладки: │ вкладки открытых глав │ вкладки: │
|
||||
│ Книги · │ │ О книге · │
|
||||
│ Банк · │ содержимое: │ Замечания │
|
||||
│ Поиск │ — две колонки оригинал/перевод │ │
|
||||
│ │ — или таблица банка памяти │ метаданные, │
|
||||
│ дерево: │ — или пустое состояние │ выноски с │
|
||||
│ книга │ │ неточностями │
|
||||
│ ├ глава 1 │ │ │
|
||||
│ ├ глава 2 │ │ │
|
||||
│ └ + глава │ │ │
|
||||
│ │ │ │
|
||||
│ ⚙ Настройки │ │ │
|
||||
├──────────────┴────────────────────────────────┴─────────────────┤
|
||||
│ статус-полоса: путь · состояние · прогресс │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- **Левая панель** — вкладки в шапке (`Книги` / `Банк памяти` / `Поиск`), дерево книг и глав, `Настройки` внизу. Клик по книге открывает её состояние, клик по главе открывает главу в центре.
|
||||
- **Центр** — вкладки открытых глав, как файлы в Fleet.
|
||||
- **Правая панель** — вкладки `О книге` (метаданные) и `Замечания` (места, требующие внимания).
|
||||
- Все три панели сворачиваются кнопками в верхней полосе.
|
||||
⚠ Метки глав — из ДАННЫХ книги; зашитого «Глава N» не существует (D39.100 К-3).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -201,7 +162,7 @@
|
|||
Кнопка запуска перевода. Пока идёт:
|
||||
|
||||
- **у каждой главы в дереве — свой индикатор выполнения** (крутящийся/прогресс), главы обрабатываются не по порядку и это нормально;
|
||||
- общий прогресс по книге в статус-полосе (+ ETA: решение владельца 04.08, D39.100 К-5 — поле `eta_seconds` добавит в спеку правка S3, опциональное);
|
||||
- общий прогресс по книге в статус-полосе (+ ETA — решение владельца, D39.100 К-5; поле в спеке есть);
|
||||
- ⚠ **не рассказывать, что происходит внутри.** Никаких «сейчас работает редактор», «идёт добыча глоссария», никаких названий моделей. Пользователь видит «идёт перевод» и прогресс. Всё.
|
||||
|
||||
### 3.5. Банк памяти — подпись
|
||||
|
|
@ -276,60 +237,32 @@
|
|||
## 5. Технический стек
|
||||
|
||||
**Стек зафиксирован в `frontend/docs/STACK_DECISIONS.md` — бери пины оттуда, не выбирай сам.**
|
||||
Коротко: React 19.2.8 · TypeScript 6.0.3 (не 7 — там нет программного API) · Vite 8.2.0 (внутри
|
||||
Rolldown, не Rollup) · react-router 8.3.0 в библиотечном режиме · TanStack Query 5 + Zustand 5 ·
|
||||
CSS Modules + `tokens.css` · **react-aria-components 1.20.0 как ЕДИНСТВЕННАЯ библиотека
|
||||
примитивов** · react-resizable-panels 4.12.2 (API v4: `Group`/`Panel`/`Separator`).
|
||||
Перечня пинов здесь нет намеренно: третий экземпляр списка версий отставал бы первым.
|
||||
|
||||
Три вещи, которые чаще всего делают неправильно, — прочитай про них в STACK_DECISIONS:
|
||||
раздел про гейт «одно место для цвета», раздел про читалку без синхронизации прокруток,
|
||||
и §7 «Ловушки».
|
||||
и §7 «Ловушки». Карта `src/` с назначением каждой папки и правилом зависимостей —
|
||||
`FRONTEND_PLAN.md` §2.
|
||||
|
||||
**Никакого редактора текста** в этом MVP: тексты только читают и сравнивают.
|
||||
|
||||
Структура:
|
||||
|
||||
```
|
||||
frontend/
|
||||
references/ скриншоты (уже лежат)
|
||||
docs/ этот промт и последующие заметки
|
||||
src/
|
||||
tokens/ tokens.css — ЕДИНСТВЕННЫЙ источник цвета и размера
|
||||
api/ слой данных: сейчас моки, потом HTTP. Один вход.
|
||||
mock/ фикстуры
|
||||
shell/ оболочка: панели, вкладки, статус-полоса
|
||||
features/ books, chapters, bank, reader, settings
|
||||
ui/ кнопки, поля, таблица, дерево, выноска
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5.1. Поддерживаемость — требование владельца, не пожелание
|
||||
|
||||
Владелец сформулировал прямо: **«главное, чтоб это было очень легко поддерживаемо»**. Этот код будут править другие сессии, не ты. Правила ниже — проверяемые, не лозунги.
|
||||
Владелец сформулировал прямо: **«главное, чтоб это было очень легко поддерживаемо»**. Этот код будут править другие сессии, не ты.
|
||||
|
||||
1. **У каждой вещи одно место.** Цвет и размер — только `tokens.css`. Данные — только `src/api/`. **Слова интерфейса — только каталог `src/i18n/ru.ts`** (с S3.7; в разметке их ловит гейт ESLint, в комментариях, CSS и скриптах — `src/i18n/catalogue.test.ts`). Если значение появилось во втором месте, это ошибка, а не удобство.
|
||||
2. **Плоско и скучно.** Никаких абстракций «на будущее», фабрик, слоёв ради слоёв. Проектный норматив: механизм строится там, где несёт ценность, а не ради архитектурной красоты. Дублирование двух строк лучше преждевременного обобщения.
|
||||
3. **Файл = один компонент** плюс его `.module.css` рядом. Больше ~150 строк — делить.
|
||||
4. **Компоненты глупые.** Данные приходят пропсами. Запросы живут на уровне экрана, не в листьях дерева.
|
||||
5. **Состояние ровно в двух местах:** TanStack Query — всё серверное, Zustand — состояние интерфейса. Копий серверных данных в локальном состоянии нет. Контекстов «на всякий случай» нет.
|
||||
6. **Глобальных стилей два файла:** сброс и `tokens.css`. Всё остальное — CSS Modules, они скоупятся сами.
|
||||
7. **Никаких обёрток над обёртками** и никаких UI-китов.
|
||||
8. **Имена человеческие, без аббревиатур** — проектная норма.
|
||||
9. **Комментарии короткие:** одна-две строки «почему», не «что». Простыни не нужны.
|
||||
10. **Каждый экран открывается в изоляции** — свой маршрут и своя фикстура, чтобы его можно было снять скриншотом и проверить отдельно от остальных.
|
||||
**Сами правила — в `FRONTEND_PLAN.md` §3, и там у каждого стоит его проверка** (машинный гейт либо ревью-вопрос с однозначным ответом). Здесь их перечня нет намеренно: правило без проверки — лозунг, а два экземпляра списка расходятся.
|
||||
|
||||
**Проверка на поддерживаемость, которую применяй к своему коду:** добавление нового состояния главы или нового вида замечания должно требовать правки **одного** файла. Если требует трёх — структура неверная, переделай до того, как двинешься дальше.
|
||||
|
||||
## 6. Данные: моков придерживаться, API не выдумывать
|
||||
|
||||
HTTP-слоя ещё нет. **Весь доступ к данным — через `src/api/`, внутри которого сейчас моки.** Компоненты не знают, что данные поддельные. Когда появится сервер, меняется один модуль.
|
||||
|
||||
Заведи фикстуры на одну книгу целиком: метаданные, 5–8 глав, у каждой главы оригинал и перевод по несколько абзацев, несколько помеченных мест, таблица банка на 20–30 терминов в разных состояниях, состояние прогона.
|
||||
**Весь доступ к данным — через `src/api/`, внутри которого пока моки.** Компоненты не знают, что данные поддельные; когда появится сервер, меняется один модуль. Мир фикстур на маршрут — карта в `FRONTEND_PLAN.md` §2 и шапка `src/mock/worlds.ts`.
|
||||
|
||||
**Материал для фикстур бери реальный** — китайский текст с русским переводом, имена и термины. Выдуманный «Lorem ipsum» даст неверную плотность: кириллица длиннее латиницы, иероглифы короче всего, и колонки поедут. Пример реального фрагмента лежит в `backend/example/chapter1-zh.txt` (читать можно, править нельзя).
|
||||
|
||||
Состояния фикстур — по enum `BookStatus` контракта (`docs/architecture/14-api-contract/openapi.yaml`: десять значений; `paused` добавит правка S3 — D39.99/100 К-8); исторический список: книга не начата · разбирается · переводится (первый проход) · ждёт подписи банка · переводится (финал) · готова · остановлена пользователем · отклонена (не удалось разобрать файл) · прервана ошибкой.
|
||||
Состояния фикстур — по словарю КОНТРАКТА, а не по списку в прозе: перечень значений живёт в канонической спеке `docs/architecture/14-api-contract/openapi.yaml` и приезжает в код генерацией. Прозаический список снят намеренно — он отставал от спеки на редакции.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -341,59 +274,11 @@ HTTP-слоя ещё нет. **Весь доступ к данным — чер
|
|||
|
||||
Без этого цикла ты пишешь вслепую: код валиден, вид случаен. Это единственный способ выполнить требование «скопировать Fleet».
|
||||
|
||||
**Установка проверена на этой машине — делай ровно так, sudo не нужен ни на одном шаге.** Среда: WSL2, Ubuntu 24.04, Node 22, пароль root недоступен.
|
||||
**Стенд ставится без sudo** (WSL2/Ubuntu, пароль root недоступен): Chromium Playwright плюс четыре системные библиотеки локально в `.tooling/`. Процедура — `FRONTEND_PLAN.md` §4, второго её экземпляра здесь нет намеренно: команды уже разошлись бы (там же CJK-шрифт стенда, без которого весь китайский в кадре — квадраты).
|
||||
|
||||
```bash
|
||||
npm i -D playwright
|
||||
npx playwright install chromium # БЕЗ --with-deps: он требует sudo и ставит лишнее
|
||||
```
|
||||
Проверка, что цикл живой, если ставишь его заново: отрисуй страницу с панелью `#17191a` на фоне `#090909`, радиус 6, отступ 8 — сними, открой, убедись, что видишь именно это. Браузер headless, окна нет, графический сервер не нужен.
|
||||
|
||||
Chromium запустится не сразу: не хватает четырёх системных библиотек (`libnss3.so`, `libnssutil3.so`, `libnspr4.so`, `libasound.so.2`). Ставятся локально, без root:
|
||||
|
||||
```bash
|
||||
mkdir -p .tooling && cd .tooling
|
||||
apt-get download libnss3 libnspr4 libasound2t64
|
||||
for d in *.deb; do dpkg -x "$d" root; done
|
||||
rm -f *.deb && cd ..
|
||||
```
|
||||
|
||||
Путь к ним — `.tooling/root/usr/lib/x86_64-linux-gnu`. Пропиши его прямо в команду, чтобы никто о нём больше не думал:
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"shot": "LD_LIBRARY_PATH=$PWD/.tooling/root/usr/lib/x86_64-linux-gnu node scripts/shot.mjs"
|
||||
}
|
||||
```
|
||||
|
||||
В `.gitignore`: `.tooling/` и `.shots/` — бинарники и снимки в репозиторий не едут.
|
||||
|
||||
Проверка, что цикл живой (сделай её до всякого интерфейса): отрисуй страницу с панелью `#17191a` на фоне `#090909`, радиус 6, отступ 8 — сними, открой, убедись, что видишь именно это. Работает браузер headless, окна нет, графический сервер не нужен.
|
||||
|
||||
### 7.2. Порядок твоей сессии
|
||||
|
||||
**S0 — план (кода нет).** Прочитай всё из блока обязательного чтения. Напиши
|
||||
`frontend/docs/FRONTEND_PLAN.md`: таблица пинов (пакет · версия · дата релиза · зачем именно нам,
|
||||
сверенная по npm), карта `src/` с назначением каждой папки, правила поддерживаемости в проверяемой
|
||||
форме, протокол скриншот-цикла и критерий сравнения с референсом. **Заленди отдельным коммитом.**
|
||||
|
||||
**S1 — инструменты и токены.**
|
||||
|
||||
1. Проверь `node -v` — нужно **≥ 22.22**. Установи Playwright по процедуре из §7.1 (без sudo).
|
||||
2. Каркас: Vite + React + TS, ESLint + stylelint + prettier, единая команда `npm run check`.
|
||||
3. Скриншот-цикл и проверка, что он живой (страница с панелью на фоне, снять, посмотреть).
|
||||
4. `tokens.css` — значения из `STACK_DECISIONS`/§1.1 этого промта, с комментарием об источнике.
|
||||
Задай `@layer` явно: порядок каскада между стилями React Aria, токенами и модулями — источник
|
||||
тихих расхождений. Сразу стилизуй скроллбары: дефолтные на Windows толстые и светлые, они
|
||||
ломают вид на трёх панелях.
|
||||
5. **Витрина**: страница из панелей, вкладок, строки дерева и строки таблицы. Сними, положи рядом
|
||||
с `fleet.png`, **посмотри обе**, сведи.
|
||||
6. Контракт-тест токенов: рендерим корень, сверяем вычисленные значения CSS-переменных с
|
||||
замеренными числами. Это заменяет визуальный гейт с эталонами.
|
||||
|
||||
**На витрине остановись.** Если токены не сошлись — это и есть результат сессии, честно описанный;
|
||||
дальше не иди. Экраны пишут следующие сессии.
|
||||
|
||||
### 7.3. Ревью исполнением — обязательно
|
||||
### 7.2. Ревью исполнением — обязательно
|
||||
|
||||
Проектная норма: любая сессия, пишущая код, обязана **проверять себя исполнением**, а не заявлением. Для тебя это значит:
|
||||
|
||||
|
|
@ -404,7 +289,7 @@ rm -f *.deb && cd ..
|
|||
|
||||
Отчёт в конце: что построено, что видно на скриншотах, где отошёл от референса и почему, что осталось.
|
||||
|
||||
### 7.4. Чего не делать
|
||||
### 7.3. Чего не делать
|
||||
|
||||
- Не изобретать API и не ходить в `backend/` за данными — только моки.
|
||||
- Не тащить UI-киты и не «улучшать» референс своим вкусом.
|
||||
|
|
@ -414,36 +299,24 @@ rm -f *.deb && cd ..
|
|||
|
||||
---
|
||||
|
||||
## 8. Готово — это когда (для ТВОЕЙ сессии, S0+S1)
|
||||
## 8. Критерий сравнения с референсом
|
||||
|
||||
- `FRONTEND_PLAN.md` заленден отдельным коммитом до кода.
|
||||
- `npm run check` зелёный и делает все четыре шага.
|
||||
- `npm run shot` снимает PNG, ты его открывал и смотрел.
|
||||
- `tokens.css` — единственный источник цвета и размера; гейт против хардкода включён и **проверен
|
||||
на живом нарушении** (вставь `#fff` в компонент, убедись, что проверка падает, убери).
|
||||
- Витрина снята и сведена с `fleet.png`; расхождения названы вслух.
|
||||
- Контракт-тест токенов проходит.
|
||||
⚠ **Не растровое совпадение.** JetBrains Fleet закрыт (скачивание прекращено 22.12.2025), референс
|
||||
снят на macOS при 2x, наша цель — Windows-Chromium с другим хинтингом шрифтов. Пиксель-в-пиксель
|
||||
недостижим и целью не является. Сходиться обязаны: **замеренные цвета, промежутки 8px, радиусы
|
||||
6px, плотность строк и общее впечатление**. Кегли и интерлиньяж — подбором на глаз до похожести,
|
||||
потому что померить их больше негде.
|
||||
|
||||
⚠ **Критерий сравнения — не растровое совпадение.** JetBrains Fleet закрыт (скачивание прекращено
|
||||
22.12.2025), референс снят на macOS при 2x, наша цель — Windows-Chromium с другим хинтингом
|
||||
шрифтов. Пиксель-в-пиксель недостижим и не является целью. Сходиться обязаны: **замеренные цвета,
|
||||
промежутки 8px, радиусы 6px, плотность строк и общее впечатление**. Кегли и интерлиньяж — подбором
|
||||
на глаз до похожести, потому что померить их больше негде.
|
||||
Гейт против хардкода цвета и размера обязан быть **проверен живым нарушением** (вставить `#fff`
|
||||
в компонент, убедиться, что проверка падает, убрать) — а не объявлен зелёным. Что именно уже
|
||||
проверено этим способом и чего гейты НЕ видят — `FRONTEND_PLAN.md` §5.4.
|
||||
|
||||
## 9. Что уже известно про движок — учесть в плане, не открывать заново
|
||||
## 9. Форма данных — из контракта
|
||||
|
||||
Разобрано по коду 02.08. Влияет на форму данных, поэтому знать надо уже на S0.
|
||||
⚠ **Пересказ того, что движок отдаёт фронту, снят: он отставал от канона.** Форму задаёт
|
||||
ратифицированный контракт `docs/architecture/14-api-contract/` (спека + спутник с провенансом
|
||||
и К-вопросами), а что достраивается в движке — `STACK_DECISIONS.md` §8. Цена расхождения
|
||||
замерена на этой же зоне: редакция 0.3.0 сняла с провода пофазный прогресс, который здесь ещё
|
||||
предписывалось строить (К-10, вердикт «НЕ строить», D39.138).
|
||||
|
||||
- **Индикатор прогресса нельзя строить на «готово N из M»**: unit становится `done` только когда
|
||||
есть и черновые строки всех его членов, и строка редактуры. Во время черновой волны это ноль
|
||||
почти всё время. Правильно — **пофазно**: `черновик N/M` ∥ `редактура N/M`.
|
||||
- **Подпись термина — не UPDATE строки.** Банк пересобирается из файлов на каждом прогоне, прямая
|
||||
запись в таблицу молча стирается следующим прогоном. Форма контракта подписи это учитывает.
|
||||
- **Байтовых смещений у замечаний нет** — только счётчики. Поэтому подсветка сомнительных мест
|
||||
идёт **на уровне блока и главы**, а не внутри текста. Владелец это принял 02.08 (§4.9),
|
||||
вопрос закрыт.
|
||||
- **Гранулярность пар грубее ожидаемой**: финальный текст существует на уровне edit-unit, замерено
|
||||
~1.9 блока на главу — местами вся глава окажется одним блоком. Читалка обязана нормально
|
||||
выглядеть и в этом случае.
|
||||
|
||||
Полный список того, что достраивается в движке — `STACK_DECISIONS.md` §8.
|
||||
Что остаётся правилом ФРОНТА, а не контракта, — `FRONTEND_PLAN.md` §6.
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@
|
|||
«текста нет в разметке» держит ESLint по МЕСТУ (буква в `JSXText` или в читаемом атрибуте), поэтому
|
||||
оно ловит и русский литерал, и английский — алфавит тут ни при чём; правило «код зоны английский»
|
||||
держит `src/i18n/catalogue.test.ts`, потому что комментарии, CSS и python в AST линтера не входят.
|
||||
Первая редакция держала оба правила алфавитом и в двух носителях сразу — переделано по ревью.
|
||||
Легальных квартир русского ровно две, и обе — данные: сам каталог и фикстуры `src/mock/`.
|
||||
|
||||
**Почему React Router, а не TanStack Router.** Оба выбора обоснованы: TanStack даёт типобезопасность
|
||||
|
|
@ -111,11 +110,13 @@
|
|||
| Браузер | `@playwright/test 1.62.1` (точный пин) |
|
||||
| Контракт API | `@stoplight/spectral-cli 6.16.3` + `openapi-typescript 7.13.0` |
|
||||
|
||||
**Контракт API — машинный артефакт, а не проза (04.08, дофикс ревью оркестратора).**
|
||||
Нормативная поверхность — OpenAPI 3.1 в `docs/api-contract/openapi.yaml`: spectral линтует её
|
||||
шестым шагом `npm run check`, openapi-typescript генерит из неё `src/api/schema.ts`, тест
|
||||
дрифта не даёт спеке и типам разъехаться. Прозаический контракт расходится с кодом ровно тем
|
||||
способом, ради предотвращения которого заведена строка 95 единого бэклога.
|
||||
**Контракт API — машинный артефакт, а не проза.** Спека OpenAPI 3.1 в
|
||||
`docs/api-contract/openapi.yaml`: spectral линтует её шагом `npm run check` (внутри
|
||||
`npm run contract`), openapi-typescript генерит из неё `src/api/schema.ts`, тест дрифта не даёт
|
||||
спеке и типам разъехаться. ⚠ **На время фриза зоны нормативен ТОЛЬКО канон
|
||||
`docs/architecture/14-api-contract/` (D39.142 п.5): зонная копия — отстающее зеркало,
|
||||
байт-равенства сегодня НЕТ.** Прозаический контракт расходится с кодом ровно тем способом,
|
||||
ради предотвращения которого контракт и заводили.
|
||||
⚠ `openapi-typescript` объявляет пир `typescript@^5.x` при нашем намеренном TS 6 —
|
||||
`--legacy-peer-deps` ОТВЕРГНУТ проверкой (после него ломается обычный `npm install`), взят
|
||||
точечный `overrides` на один пакет; `npm ci` с нуля проходит. Хвост — `BACKLOG.md` Ф-23.
|
||||
|
|
@ -145,16 +146,15 @@ npm run check # prettier → eslint → stylelint → spectral (контр
|
|||
npm run check:full # + vite build + npm run shot (маршруты и axe) + npm run scenes (сценарии)
|
||||
```
|
||||
|
||||
⚠ Состав сверен S4 14.08 по `package.json` (`node -e` по `scripts.check`): **шесть** шагов у
|
||||
`check` и три добавочных у `check:full`. Раньше здесь стояли четыре — список отстал на два пака.
|
||||
⚠ Промт S4 §7 назвал семь; это ошибка промта, и первая редакция этой правки её повторила.
|
||||
⚠ Состав сверен по `package.json` (`node -e` по `scripts.check`): **шесть** шагов у `check`
|
||||
и три добавочных у `check:full`. Число шагов проверять этой командой, а не по прозе доков:
|
||||
здесь и в промтах оно уже отставало.
|
||||
|
||||
CI вызывает **именно их**, а не дублирует список инструментов. Path-фильтры на уровне job'ов
|
||||
(правка CSS не должна гонять тесты Go) плюс агрегирующий job с явной проверкой
|
||||
`contains(needs.*.result,'failure')||contains(needs.*.result,'cancelled')`. ~~Git-хуков в MVP нет~~ —
|
||||
пересмотрено 02.08 запросом владельца: CI ещё не поднят, и до него pre-commit — единственный
|
||||
машинный рубеж. Хук зовёт те же `npm run check`-команды, не дубль списка (`scripts/githooks/`,
|
||||
детали — `FRONTEND_PLAN.md` §7).
|
||||
`contains(needs.*.result,'failure')||contains(needs.*.result,'cancelled')`. **Pre-commit хук есть**
|
||||
(запрос владельца 02.08): CI ещё не поднят, и до него хук — единственный машинный рубеж. Он зовёт
|
||||
те же `npm run check`-команды, не дубль списка (`scripts/githooks/`, детали — `FRONTEND_PLAN.md` §7).
|
||||
|
||||
**Визуальный гейт с эталонными скриншотами — ОТЛОЖЕН.** Он флейкует между платформами, а наш
|
||||
референс снят на macOS при 2x, целевая платформа — Windows. Вместо него **контракт-тест токенов**:
|
||||
|
|
@ -176,23 +176,13 @@ CI вызывает **именно их**, а не дублирует списо
|
|||
ссылок на прочтение. Причины две: авторские права на исходники и политика поисковиков в отношении
|
||||
машинно-сгенерированного контента.
|
||||
|
||||
## 5. Платформа
|
||||
## 5. Платформа — только то, что видно фронту
|
||||
|
||||
| Что | Пин | Заметка |
|
||||
|---|---|---|
|
||||
| Go | `1.26.4` в `go.mod`, ⚠ тулчейн сборки платформы **≥1.26.6** (`platform/Makefile` `GO_MIN_VERSION`, ратифицирован D39.130; сверено S4 14.08 по самому Makefile); пин живёт в Makefile, здесь только указатель | |
|
||||
| HTTP | стандартный `net/http` + `ServeMux` | роутер-библиотеку не тянуть |
|
||||
| Postgres | `pgx v5.10.0` | нижняя допустимая граница 5.9.2, **не** 5.9.0 |
|
||||
| Миграции | `goose v3.27.3` | как библиотека, `embed.FS`, `WithLocker` |
|
||||
| Очередь | `river v0.42.0` | на том же Postgres |
|
||||
| Безопасность | `govulncheck` | обязательный гейт CI |
|
||||
|
||||
**Redis не заводим нигде** — зафиксировано как архитектурное «нет», иначе он приползёт по частям.
|
||||
|
||||
**Конкурентность по книге.** Одна книга = один процесс-воркер; сериализация в очереди по `book_id`
|
||||
плюс пиннинг книги к одному хосту на MVP. Лизы `book_leases` с heartbeat — поверх, ради вежливого
|
||||
ожидания вместо аварии. Глобальный брокер исходящих LLM-вызовов — отдельный компонент, включаемый
|
||||
по гейту «до второго параллельного пользователя».
|
||||
⚠ **Пины и внутреннее устройство платформы здесь БОЛЬШЕ НЕ ДУБЛИРУЮТСЯ** — их дом
|
||||
`platform/docs/STACK_DECISIONS.md` (Go/тулчейн, pgx, goose, river, govulncheck, «Redis нет»,
|
||||
конкурентность по книге и лизы), и зонная копия отставала: она называла тулчейн старой версии
|
||||
против ратифицированной D39.130. Ниже остаются два шва, на которых стоит КОД ФРОНТА, — стрим
|
||||
и предъявление сессии.
|
||||
|
||||
**Прогресс — SSE, и события ПУШИТ воркер**, а не фронт опрашивает read-model (см. §7, риск про
|
||||
ре-ингест). Браузер — нативный `EventSource` на cookie; десктоп и CLI — обычный GET с
|
||||
|
|
@ -244,17 +234,26 @@ CSRF только на cookie-пути: `Sec-Fetch-Site` → фолбэк на `
|
|||
|
||||
## 8. Что придётся достроить в движке
|
||||
|
||||
Через оркестратора, в зону `backend/`, в порядке критичности. Обосновано разбором кода.
|
||||
Через оркестратора, в зону `backend/`. Обосновано разбором кода.
|
||||
|
||||
1. **Пофазный прогресс** `draft N/M` ∥ `edit N/M` поверх `chunk_status`. Сейчас unit объявляется
|
||||
`done` только когда есть и все draft-строки его членов, и edit-строка — то есть во время черновой
|
||||
волны индикатор показывал бы 0% почти всё время.
|
||||
2. **Персист манифеста глав и чанков** (+ `chunker_version` и хеш источника). Нужен под экран
|
||||
разбора и дерево глав, и снимает ре-ингест: сейчас каждый read-вызов заново читает и режет
|
||||
исходник — замерено 1.42–1.51 с процессорного времени на книге 23 МБ, и это умножается на число
|
||||
книг в библиотеке.
|
||||
3. **JSON-выход таблицы подписи банка** — сейчас только человеческий текст с ограничением 20 строк.
|
||||
4. **Приём внешнего `TRACEPARENT`** в `cmd/tmctl`, чтобы прогон был одной трассой.
|
||||
**Живое:**
|
||||
|
||||
1. **Приём внешнего `TRACEPARENT`** в `cmd/tmctl`, чтобы прогон был одной трассой. Не построено:
|
||||
грепа `traceparent` по `*.go` — ноль хитов (положительный контроль: греп по `trace` в тех же
|
||||
каталогах хиты даёт).
|
||||
2. **Машиночитаемая таблица ПОДПИСИ.** Весь банк JSON-сайдкаром уже есть
|
||||
(`internal/pipeline/bankexport.go`, D39.122; единый бэклог, строка 169), а стоп-таблица подписи
|
||||
по-прежнему человеческий текст с капом (`cmd/tmctl/render.go`, константа `bankStopStdoutCap`).
|
||||
⚠ Дверь правок банка при этом смонтирована — `tmctl bank-apply` (D39.162), см. спутник контракта
|
||||
§2.19.
|
||||
|
||||
**Закрыто — не переоткрывать:**
|
||||
|
||||
- **Пофазный прогресс** `draft N/M` ∥ `edit N/M` — вердикт **«НЕ строить»** (D39.138; спутник
|
||||
контракта, К-10). Фаз на проводе нет: одна полоса до ближайшей остановки.
|
||||
- **Персист манифеста глав и чанков** (+ `chunker_version`, хеш источника) — **построено**
|
||||
(`backend/internal/pipeline/manifest.go`). Он же снял ре-ингест: прежде каждый read-вызов
|
||||
заново читал и резал исходник, замерено 1.42–1.51 с процессорного времени на книге 23 МБ.
|
||||
|
||||
Спаны для подсветки **внутри** текста — **не делаем** (владелец решил 02.08): у дешёвых гейтов
|
||||
нет ни одного байтового смещения, только счётчики, а подсветка на уровне блока признана достаточной.
|
||||
|
|
|
|||
|
|
@ -5,22 +5,18 @@
|
|||
Зона заморожена, работы не заказываю: перечисленное ждёт разморозки и правится ТВОЕЙ сессией (её журнал и бэклог — твоя зона).
|
||||
1. **В-3 (каким словом называть ступень замечания) — с листа владельца СНЯТ.** D39.176 п.4 (30.08): фразы В-3 · К-6 · Приложение А ДЕЛЕГИРОВАНЫ проекту, подписи владельца не ждём. Требование ИНЖЕНЕРНОЕ: фраза не литерал ни в Go платформы, ни в TSX фронта, а ДАННЫЕ по машинному коду причины и локали; словарь — один на продукт. То же снимает развилку **Ф-61** («платформа отвечает по-английски») — направление задано, выбор «кто и чем» больше не открыт.
|
||||
2. **Ф-43 протухла обеими половинами** (сверено деревом 30.08): движковый сайдкар банка построен (`backend/internal/pipeline/bankexport.go`), платформа его декодирует и отдаёт проекцией. Основание «S5 непостроим» и зависимости Ф-44/Ф-46 «после Ф-43» больше не держатся. Первый пинг об этом был 16.08 и не исполнен — потому повторяю.
|
||||
3. **В-1** утверждает «платформы ещё нет ни строки» — платформа живёт с 04.08 и прошла паки P0–P11 плюс sqlc; половина вопроса снята фактом.
|
||||
3. **В-1** утверждал «платформы ещё нет ни строки» — платформа живёт с 04.08 и прошла паки P0–P11 плюс sqlc; половина вопроса снята фактом (текст строки В-1 поправлен 02.09 при чистке журнала).
|
||||
4. **Состояние зеркала контракта — 0.2.3 против канона 0.9.0** (0.9.0 ЗАЛЕНДЖЕН 31.08 с паком P12, D39.180): СЕМЬ миноров, первый из них ломающий, 16 операций против 20. Синк зеркала + перегенерация типов — первое касание при разморозке, ОДНОЙ перегенерацией.
|
||||
|
||||
|
||||
> **Файл переименован 04.08 по слову владельца: был `PROGRESS.md`, стал `frontend-PROGRESS.md`.**
|
||||
> Причина — путаница с `docs/PROGRESS.md` оркестратора: два файла с одним именем в одном
|
||||
> дереве, и ссылка «PROGRESS.md» без пути читалась по-разному в разных доках.
|
||||
>
|
||||
> **Оркестратору:** прогресс фронта пишется СЮДА же, в этот файл, а не в `docs/PROGRESS.md` —
|
||||
> так у зоны один журнал и конфликтов правок в общем файле не возникает (слово владельца 04.08).
|
||||
>
|
||||
> Зонный журнал: сессии фронта пишут сюда сами и работают независимо (решение владельца 02.08).
|
||||
> Хвосты — `BACKLOG.md`, план и замеры — `FRONTEND_PLAN.md`, ратифицированные пины —
|
||||
> `STACK_DECISIONS.md`, контракт API — спека `api-contract/openapi.yaml` и спутник
|
||||
> `API_CONTRACT_DRAFT.md` (`API_CONTRACT_INPUT.md` — исполненный вход, под ⚠-баннером).
|
||||
> Сверху — текущее состояние, ниже — хроника по сессиям.
|
||||
> **Зонный журнал** (решение владельца 02.08): сюда пишут и фронт-сессии, и оркестратор; в
|
||||
> `docs/PROGRESS.md` фронт не пишет (роль-таблица `CLAUDE.md`). Хвосты — `BACKLOG.md`, план и
|
||||
> замеры — `FRONTEND_PLAN.md`, ратифицированные пины — `STACK_DECISIONS.md`. Контракт: канон —
|
||||
> `docs/architecture/14-api-contract/` (спека + компаньон-README), зонное зеркало —
|
||||
> `api-contract/openapi.yaml` (отстаёт РАТИФИЦИРОВАННО, см. пинг №20 п.4); `API_CONTRACT_DRAFT.md`
|
||||
> и `API_CONTRACT_INPUT.md` — исполненные входы под ⚠-баннерами, не поддерживаются.
|
||||
> Сверху — состояние и пинги, ниже — хроника. Записи сессий S0–S4 и их приёмок (02–14.08) вынесены
|
||||
> в [archive/frontend-PROGRESS-S0-S4.md](archive/frontend-PROGRESS-S0-S4.md) (слово владельца 16.08).
|
||||
|
||||
## 📌 ПИНГ ОРКЕСТРАТОРА (29.08, D39.169) — новый терминальный кадр SSE `session_ended`, зоне при разморозке
|
||||
|
||||
|
|
@ -42,175 +38,6 @@
|
|||
|
||||
## Текущее состояние
|
||||
|
||||
> **Запись оркестратора №15, 07.08.** Абзацы ниже — состояние на 04.08, и первый из них с тех пор
|
||||
> УСТАРЕЛ: «замок на входе закрыт, контракта API v0 нет» больше не так. Контракт ратифицирован
|
||||
> **D39.99**, канон — `docs/architecture/14-api-contract/` (спека + компаньон-README), зонная копия
|
||||
> `api-contract/openapi.yaml` — байт-зеркало канона. Вторая сторона контракта, платформа, **написана,
|
||||
> принята и залендена** (D39.109): вход через OIDC, сессии, кредитный леджер, админ-CLI. Строка 95
|
||||
> единого бэклога закрыта.
|
||||
>
|
||||
> **S3 ИСПОЛНЕН и ПРИНЯТ D39.115** (`60595f6`); промт отработан и заархивирован. Было в этой строке до 08.08: «запускается по слову владельца, промт готов» (`S3_SESSION_PROMPT.md`) и с 07.08 несёт три
|
||||
> новых пункта списка правок спеки: **А-нуль** — шапка `info` спеки исправлена мной (три дня она
|
||||
> называла себя нератифицированным черновиком S3 и ссылалась на несуществующий рядом компаньон;
|
||||
> S3 остаётся бампнуть `version`) · **В-трис** — управляемый потолок прогона, решение владельца
|
||||
> 07.08 (шкала в ИНТЕРФЕЙСЕ от минимума до доступного остатка, единица — ГЛАВЫ, не доллары, иначе
|
||||
> это амендмент D39.84; максимум = баланс минус открытые холды ⚠ ФОРМУЛА ОШИБОЧНА, superseded D39.115 п.2(а): максимум = Balance КАК ЕСТЬ, холд уже дебет (вычитание дважды резало шкалу; испр. D39.125); носитель пересчёта «главы → деньги»
|
||||
> — платформа, ручка объявляется правкой спеки) · **В-кватер** — фронт живёт на ТОМ ЖЕ origin, что
|
||||
> платформа: CORS-слоя в ней нет вовсе, preflight `OPTIONS` с чужим `Origin` получает 401 от гарда
|
||||
> сессии (замер на живом бинаре, PD-96 регистра платформы) ⇒ в деве ходить через прокси
|
||||
> dev-сервера, кросс-origin не проектировать.
|
||||
>
|
||||
> **Мелочь зоны, которую стоит закрыть при следующем касании:** `API_CONTRACT_DRAFT.md` — второй
|
||||
> экземпляр компаньона; канон компаньона теперь `docs/architecture/14-api-contract/README.md`,
|
||||
> поэтому зонному файлу нужен баннер-указатель, иначе это второй носитель одного состояния.
|
||||
|
||||
|
||||
> **Запись оркестратора №15, 08.08 (после приёмки S3) — СЛОВО ВЛАДЕЛЬЦА О КАЧЕСТВЕ, гейт на S4.**
|
||||
> Владельцу НЕ нравится текущее качество фронта; названная претензия — «логика спама вкладками»:
|
||||
> вкладки обязаны работать **ровно как в VS Code** — одиночный клик открывает PREVIEW-вкладку,
|
||||
> следующий одиночный клик ЗАМЕЩАЕТ её содержимое (новая вкладка не плодится), пин вкладки — по
|
||||
> двойному клику/взаимодействию с содержимым. Полный список претензий владелец сформулирует
|
||||
> отдельным входом — этот абзац лишь первый пункт, НЕ весь список. **Диспозиция оркестратора:**
|
||||
> S4 НЕ стартует до списка; когда список придёт — сначала «S3.5 фикс-пак оболочки» (вкладки
|
||||
> по VS Code-модели + Ф-19 прокрутка + список владельца), затем S4 на починенной оболочке.
|
||||
> Промт S3.5 выдаёт оркестратор по слову владельца.
|
||||
|
||||
> **Запись оркестратора №16, 09.08 — список владельца ПРИШЁЛ, промт S3.5 ВЫДАН.** Абзац выше
|
||||
> исполнен: полный список претензий (24 замечания, снятые владельцем с живого прогона витрины)
|
||||
> получен 09.08 и завёрнут в промт **`S35_SESSION_PROMPT.md`** (эта папка): VS Code-модель
|
||||
> вкладок + Ф-19 + все 24 номера блоками, приёмка кадрами ДО/ПОСЛЕ и playwright-сценариями,
|
||||
> референс драг-ручки — vojo (`src/app/components/page/Page.tsx:191-300`, `style.css.ts:11-70`).
|
||||
> Запуск сессии — по слову владельца; S4 остаётся гейчен до приёмки S3.5. Попутное: замечание
|
||||
> (10) владельца независимо подтвердило Ф-21 — полоска замечания нечитаема без слова; само
|
||||
> слово ступени — по-прежнему В-3 на владельце. Референс `antigravity_add_folder.png` докинут
|
||||
> владельцем 09.08 (модал добавления книги, замечание (12)).
|
||||
|
||||
> **Запись фронт-сессии S3, 08.08.** Абзац «S3 НЕ сделана: замок закрыт» ниже — состояние на
|
||||
> 04.08, и он УСТАРЕЛ целиком: замок открыт, **S3 сделана**. Хроника — первым разделом ниже.
|
||||
> Коротко: `src/api/` асинхронен и разговаривает HTTP; MSW стоит СЕТЕВЫМ моком, поэтому приложение
|
||||
> делает настоящий `fetch` и настоящий `EventSource`; фикстур шесть миров, по маршруту на каждый;
|
||||
> живой прогресс идёт по SSE и двигает счётчик в браузере (замерено, не предположено); `types.ts`
|
||||
> удалён, экраны живут на типах контракта; ветка неизвестного значения стоит на шве. Спека
|
||||
> бампнута до **0.2.0** со всем списком правок промта — канон правит оркестратор по
|
||||
> канон `docs/architecture/14-api-contract/` (патч ПРИМЕНЁН и удалён, D39.115). Ф-15 и Ф-22 закрыты, заведены Ф-26..Ф-31.
|
||||
> `npm run check` — 7 шагов, **108 тестов** на 08.08 (число живёт в батарее, а не здесь: сверяется прогоном), зелёный; `npm run shot` — шесть маршрутов, гейт
|
||||
> доступности зелёный на всех.
|
||||
|
||||
> **Запись фронт-сессии S3.5, 09.08.** Фикс-пак оболочки по 24 замечаниям владельца ИСПОЛНЕН —
|
||||
> прогон по всем номерам первым разделом хроники ниже. Коротко: вкладки живут по модели VS Code
|
||||
> (предпросмотр/закрепление), Ф-19 закрыта keep-alive панелей, полоска ресайза больше не залипает
|
||||
> (причина найдена в коде библиотеки), настройки и добавление книги открываются модальными окнами,
|
||||
> лупа стала палитрой быстрого перехода, банк перепридуман со спойлерами по окну применимости,
|
||||
> кнопка-заглушка «Подписать банк» снята в пользу честного состояния, палитра ушла в тёплый подтон
|
||||
> (три варианта кадрами), причина «шакальных» иконок найдена в сетке набора и вылечена. Гейты
|
||||
> зелёные, **контраст axe — ноль узлов на всех семи маршрутах** (Ф-11 закрыта замером).
|
||||
> Адверсариальное ревью тремя линзами исполнено, находки и диспозиции — там же; две из них были
|
||||
> моими дефектами по существу (правка полос увела ОТ референса; тронуто слово паузы, закреплённое
|
||||
> за владельцем) и откачены.
|
||||
> Ждут владельца: В-7 (масштаб интерфейса), В-8 (слово состояния в дереве), Ф-38 (защита вкладки
|
||||
> «Замечания»), выбор палитры. Ждёт оркестратора: два черновика правки спеки — поля банка и
|
||||
> `BookIntake.title` (в конце записи S3.5).
|
||||
|
||||
> **Запись фронт-сессии S3.6, 10.08.** Второй круг замечаний владельца (10 пунктов, снятых
|
||||
> с прогона после S3.5) отработан. Коротко: палитра переведена на НЕЙТРАЛЬНЫЙ ряд, снятый
|
||||
> замером с `fleet_2.png` и `antigravity_*.png` (тёплый подтон S3.5 был моей отсебятиной
|
||||
> ПРОТИВ этих же референсов); вкладки перестроены по замеру fleet_2 — полоса вкладок это ХРОМ,
|
||||
> активная вкладка вырезана из него цветом полотна; найдена и починена причина обрезки коротких
|
||||
> названий; ручка ресайза перенесена из vojo дословно, включая числа упоров; линия жёлоба
|
||||
> читалки — в цвет оболочки, ширина набора отпущена по окну; **банк памяти переделан в таблицу**
|
||||
> и открывается вкладкой в центре, спойлер снят с ПЕРЕВОДА и поставлен на описание; иконки книги
|
||||
> и главы разведены и несут состояние цветом. Первым разделом записи — АУДИТ всех 24 буллетов
|
||||
> S3.5 по коду (просьба владельца), с честными вердиктами второго круга.
|
||||
> Ждут не меня: две дыры платформы/контракта под банк (ниже, «Жалобы») и подписной механизм S5.
|
||||
|
||||
> **Сессия S3.6 ЗАКРЫТА 10.08 (разделы 12–15 хроники).** Дерево готово к лендингу, фронт не
|
||||
> коммитит. Гейты: `npm run check:full` зелёный — 160 тестов, 7 маршрутов, 7 сценариев, ноль
|
||||
> блокирующих нарушений axe, драг панели p90 17 мс. Новые строки бэклога Ф-49…Ф-51 (свежесть
|
||||
> текста главы · потерянные кадры потока · resync сбрасывает один гард из двух) заведены по
|
||||
> ресёрчу отдельным агентом и проверены по коду; два моих собственных предложения тот же ресёрч
|
||||
> опроверг, и это записано в разделе 15 вместе с долгами сессии.
|
||||
|
||||
> **Запись оркестратора, 14.08 — ГЕЙТ S4 ПАЛ (платформа P5 принята, D39.130): живой `POST /books`
|
||||
> существует; S4-промт выдаёт оркестратор по слову владельца** (вход: экраны библиотеки/загрузки +
|
||||
> находки приёмки S3.7 + спек-правка 0.2.3). Попутно аудит-свип корпуса 14.08 — стейлы зонных доков,
|
||||
> закрыть S4-сессией (или раньше): `FRONTEND_PLAN.md:42-43` «платформа = ноль строк кода» и `: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 принята. Два числа живут в двух
|
||||
> версиях без арбитража: ключей i18n — 147 (запись S3.7 ниже) против 157 (Ф-35 и живой `ru.ts`;
|
||||
> верно **157**) · цена бандла +7999 (запись) против +6971 (Ф-35). Долги без Ф-строки: RTL-проверка
|
||||
> (триггер «появление I18nProvider» НАСТУПИЛ в S3.7) · спойлер достаётся поиском по странице/печатью
|
||||
> в PDF (продуктовое слово) · канвас-замер не знает `lang` при двух CJK-лицах — завести Ф-строки
|
||||
> S4-сессией. Промты S3/S3.5/S3.7 архивированы с баннерами-исходами (`docs/archive/prompts/FRONTEND_S3*`),
|
||||
> зонные копии сняты.
|
||||
>
|
||||
> **Дописка 14.08 (вечер): S4-промт ВЫДАН** — `S4_SESSION_PROMPT.md` (эта папка), запуск одобрен
|
||||
> владельцем. Его решения того же дня, внесённые в промт: (1) `BookIntake.title` — в спек-правку
|
||||
> 0.2.3 (ратификация оркестратора как исполнение замечания 12 S3.5, владелец подтвердил);
|
||||
> (2) продуктовые фразы причин отказа PD-173 пишет S4-сессия сама — по реальному флоу отказов
|
||||
> платформы, в границах ПТ-33, на утверждение владельцем по отчёту; (3) Ф-52 — `--color-danger`
|
||||
> осветлить на ступень до ≥3:1 силами S4 (закрыть замером).
|
||||
|
||||
> **Запись фронт-сессии S4, 14.08.** Путь «у меня есть файл → книга переводится» построен и прожит
|
||||
> в мире моков: форма загрузки отправляет книгу (XHR — `fetch` прогресса ТЕЛА запроса не даёт вовсе),
|
||||
> 201 приносит `parsing`, разбор — отдельный видимый шаг БЕЗ выдуманного процента, отказы интейка
|
||||
> (400/404/408/413) и отказ книги (три причины PD-173 с разными советами) — состояния формы и
|
||||
> карточки, а не алерты. Форма запуска даёт потолок в ГЛАВАХ из `CeilingBounds`; потолочный стоп
|
||||
> прожит по контракту (`EventCeiling` + `paused`), и оповещение — не тост, а несъезжающая фраза в
|
||||
> статус-полосе и на карточке (аргумент в записи). Библиотека: дерево свыше 200 разделов
|
||||
> складывается по сотням, книга в дереве стала ВЫБИРАЕМОЙ (без этого половина статусов была
|
||||
> недостижима на карточке), все одиннадцать состояний прожиты. Контракт зонной копии бампнут до
|
||||
> **0.2.3** (PD-172/173/174/180 + `BookIntake.title`); ⚠ по коду платформы `not_configured` на
|
||||
> отклонённую книгу сегодня не попадает вовсе — названо в записи. Девять несущих находок приёмки
|
||||
> S3.7 разобраны, и одна из них ОПРОВЕРГЛА клейм S3.7 замером (resync рвал живой поток, когда
|
||||
> чтение медленное).
|
||||
>
|
||||
> ⚠ **Три ошибки в самом промте S4, найденные вторым проходом ревью и исправленные** (промт писала
|
||||
> нейросеть, и это ровно тот случай): (1) «поле после файла = 400» — платформа так НЕ делает, она
|
||||
> перестаёт читать на файле, и необязательное поле за ним просто теряется (спека и фикстура
|
||||
> исправлены, два теста); (2) §7 велит записать «7 шагов у `npm run check`» — их шесть, и первая
|
||||
> редакция моей правки повторила ошибку промта; (3) §4 сводит 409 на старте к «уехал потолок», а
|
||||
> платформа отвечает им на пять разных состояний — совет на экране сделан причинно-нейтральным.
|
||||
> Ждут владельца: **В-10** (спойлер — защита или вежливость) и **В-11** (фразы отказа PD-173 на
|
||||
> утверждение).
|
||||
|
||||
> **Запись фронт-сессии S3.7, 10.08.** Долги слоя данных закрыты тестами, падающими на до-фиксном
|
||||
> коде (Ф-49…Ф-51), контракт зонной копии бампнут до **0.2.2** (`BankTerm.sense` обязателен),
|
||||
> строки интерфейса уехали в КАТАЛОГ (`src/i18n/`, 147 ключей, механизм — `@internationalized/string`,
|
||||
> уже лежавший в дереве под react-aria-components), и весь код зоны переведён на английский:
|
||||
> комментарии, имена тестов, сообщения ошибок, логи скриптов. Держат это два гейта, и каждый
|
||||
> проверен ЖИВЫМ нарушением — ESLint ловит текст в разметке МЕСТОМ, а не алфавитом (ловит и русский,
|
||||
> и английский литерал), `src/i18n/catalogue.test.ts` подметает комментарии, CSS и python.
|
||||
> `npm run check` зелёный: **266 тестов** (было 160), `npm run check:full` — 7 маршрутов и
|
||||
> **9 сценариев** (добавлены `hidden` и `zoom`: раскладка при 125/150/200% с прогоном axe).
|
||||
> ⚠ Находка приёмки 8.1 (HIGH) **не воспроизвелась** — замер в браузере в записи ниже, механизм,
|
||||
> написанный было под неё, снят; сценарий на тот же путь оставлен сторожем (Ф-53).
|
||||
|
||||
> **Запись оркестратора, 10.08 — ПРИЁМКА S3.7: принято и залендено (D39.129).** Гейты
|
||||
> пере-прогнаны исполнением (279 тестов, 9 сценариев, EXIT=0); **арбитраж по 8.1 — права
|
||||
> СЕССИЯ, HIGH приёмки S3.6 снята как моя ложноположительная** (разбор — приёмочной записью
|
||||
> хроники). 12 находок ревью (1 high: ложный клейм про импорт `fill.ts` сценами) — носитель:
|
||||
> блок находок следующего фронт-промта (S4). Канон контракта догнал 0.2.2 диффом при лендинге,
|
||||
> зеркало байт-равно. **S4 — следующий этап; промт выдаёт оркестратор** (после приёмки
|
||||
> платформы P5 — экран загрузки стоит на её `POST /books`).
|
||||
|
||||
> **Четвёртый круг замечаний, 10.08 (раздел 12 хроники).** Четыре бага владельца по таблице банка
|
||||
> закрыты, и один из них был МОЙ механизм: заморозка ширин (`useSettledWidth`) оставляла таблицу
|
||||
> сжатой после драга, а перезамер показал, что она ничего не покупала — драг держит p90 17 мс и
|
||||
> без неё. Ширины отданы алгоритму библиотеки, содержимое меряется канвасом по всему банку и идёт
|
||||
> ПОТОЛКОМ столбца. Спойлер описания переделан с фильтра на боксе на размытие глифов. Сверх списка
|
||||
> владельца найдено шесть дефектов того же класса — в том числе замер по запасному шрифту (−4.7%
|
||||
> ширины) и живой `getComputedStyle`, обнулявшийся за время загрузки шрифтов. Приёмка: 158 тестов,
|
||||
> 7 сценариев, 4 новые проверки, каждая падает на откате своего фикса.
|
||||
|
||||
> **Запись оркестратора, 10.08 — ПРИЁМКА S3.6: принято и залендено (D39.127).** Гейты
|
||||
> пере-прогнаны исполнением, 12 находок ревью (1 high) и поправки к записи S3.6 — первой записью
|
||||
> хроники ниже. Промт **S3.7** выдан (`S37_SESSION_PROMPT.md`: Ф-49…Ф-51, Ф-47 ратифицирован,
|
||||
> Ф-40, Ф-25, Ф-35 — словарь строк + гейт против хардкода русского; ⚠ Ф-35 в первой редакции
|
||||
> промта отсутствовала — пропуск поймал владелец при ревью промта, D39.127 п.2г), запуск
|
||||
> по слову владельца; **S4 — после приёмки S3.7.** Экспорт банка заведён строкой 169
|
||||
> единого бэклога (Ф-43).
|
||||
|
||||
- **Пройдено:** S0 (план), S1 (инструменты, скриншот-цикл, `tokens.css`, витрина),
|
||||
S2 (оболочка трёх панелей, слой `src/ui/`, масштаб списков, хвосты гейтов),
|
||||
S3 (слой данных: контракт 0.2.0, MSW, фикстуры всех состояний, живой поток, типы контракта),
|
||||
|
|
@ -218,44 +45,10 @@
|
|||
**S3.6 (второй круг: палитра по замеру референсов, модель вкладок fleet_2, банк таблицей,
|
||||
ручка из vojo дословно)**, **S3.7 (долги слоя данных: контракт 0.2.2, каталог i18n 157 ключей,
|
||||
код зоны на английском; D39.129)**, **S4 (загрузка книги и запуск прогона на контракте 0.2.3,
|
||||
библиотека на масштабе, находки приёмки S3.7 — ждёт приёмки)**.
|
||||
- ⚠ **АБЗАЦ НИЖЕ ИСТОРИЧЕСКИЙ (04.08), читать как хронику:** замок с тех пор открыт (контракт ратифицирован D39.99), S3 исполнен и принят D39.115. Оставлен, потому что объясняет, ПОЧЕМУ S3 однажды остановилась, а не как диспозиция.
|
||||
- **S3 НЕ сделана: замок на входе закрыт, проверено исполнением 04.08.** Контракта API v0
|
||||
по-прежнему нет (`docs/architecture/14-api-contract.md` отсутствует; строка 95 единого
|
||||
бэклога — статус «дизайн-промт по слову владельца», то есть не исполнена). Вместо S3 сессия
|
||||
сделала то, что предписывает её же промт на этот случай: **довела Ф-14 до артефакта
|
||||
`API_CONTRACT_INPUT.md`** и остановилась. Кода не написано ни строки — это диспозиция,
|
||||
а не недоделка: моки, снятые не с того контракта, разойдутся с API, ровно ради чего
|
||||
строка 95 и заведена.
|
||||
- **Написан черновик контракта, принят в работу ревью оркестратора и ДОФИКШЕН (04.08).**
|
||||
Форма сменена решением владельца: нормативная поверхность — **OpenAPI 3.1
|
||||
`docs/api-contract/openapi.yaml`**, `API_CONTRACT_DRAFT.md` стал обязательным спутником
|
||||
(провенанс ✓/◆/○, обоснования, К-вопросы — то, чего YAML не выражает). Дом ратифицированной
|
||||
копии — `docs/architecture/14-api-contract/`, зона оркестратора; после ратификации типы
|
||||
генерятся из перенесённой копии — контракт первичен.
|
||||
- **Гейт контракта живой:** `npm run check` стал шестишаговым (spectral на строгом пороге),
|
||||
`openapi-typescript` генерит `src/api/schema.ts`, тест дрифта не даёт спеке и типам
|
||||
разъехаться, ветка неизвестного значения стоит на шве (`src/api/contract.ts`), MSW отдаёт
|
||||
тело в форме контракта. Четыре формы нарушения проверены живьём (`FRONTEND_PLAN.md` §5.4.2).
|
||||
- **Ждёт оркестратора, четыре вещи:** (1) ратифицировать спеку и перенести в
|
||||
`docs/architecture/14-api-contract/`, либо вернуть с правками; (2) три зависимости, которых
|
||||
фронт заводить не может — **артефакт экспорта БАНКА** (строки в едином бэклоге нет вовсе:
|
||||
101 даёт таблицу подписи, не банк), **механизм поднятия потолка** (без него `resume` после
|
||||
стопа по потолку прогон не двигает) и правка устаревшего комментария провенанса
|
||||
`store/migrate.go:193`; (3) залендить дерево сессии; (4) вопросы владельцу — В-4, В-5 ниже
|
||||
и К-1..К-12 спутника, из них продуктовых пять, остальные — автору контракта и платформе.
|
||||
- **Ждёт владельца:** контраст палитры (`BACKLOG.md` Ф-11) и два вопроса ниже — В-1 диск,
|
||||
В-2 копирайт.
|
||||
- **Ждёт движка:** манифест глав и пофазный прогресс (единый бэклог, строки 99–100),
|
||||
к S5 — машиночитаемая таблица подписи банка (строка 101).
|
||||
- `npm run check` зелёный (6 шагов, 80 тестов), `npm run check:full` зелёный, гейт доступности
|
||||
зелёный на обоих маршрутах. Гейтов стало больше: включён Ф-4 (отступы), закрыты Ф-9 и Ф-10,
|
||||
по ревью оркестратора 04.08 добавлены динамические импорты швов и языковая ветка в CSS.
|
||||
библиотека на масштабе, находки приёмки S3.7) — ПРИНЯТ 15.08, D39.135; фикс-лист ФС-1…ФС-12
|
||||
остаётся входом первого касания**.
|
||||
- **Карта канона для фронт-сессии — `FRONTEND_PLAN.md` §0.1, транспорт — §0.2.** Заведены
|
||||
после того, как две ошибки S1 пришли из непрочитанного канона; читать до кода, не после.
|
||||
- **Передача следующей сессии написана — `S3_SESSION_PROMPT.md`** (сессией S2 по просьбе
|
||||
владельца; ратифицирует оркестратор): вход в S3 под замком контракта API, который сессия
|
||||
проверяет сама, плюс фолбэк на Ф-14, если замок закрыт, и перечень ловушек, купленных S2.
|
||||
- **Гейты стоят и на коммите:** pre-commit хук (02.08, вторая сессия) гоняет `npm run check`
|
||||
для коммитов с frontend-путями и блокирует смесь зон и файлы «никогда не коммитить»;
|
||||
ставится сам при `npm install`. Обход — только осознанный `git commit --no-verify`.
|
||||
|
|
@ -270,12 +63,10 @@
|
|||
вкладок у нас фиксированные (слева `Книги · Поиск`, справа `О книге · Замечания · Банк`) —
|
||||
пользователь их не расширяет, и fleet-овский смысл «+» («добавить инструмент») к ним не применим.
|
||||
|
||||
Владелец: **«+» слева = «Добавить книгу»**. Отсюда:
|
||||
|
||||
| Было | Стало | Почему |
|
||||
|---|---|---|
|
||||
| «+ Добавить книгу» строкой в дереве, «+» в рядах вкладок нет | «+» в ряду вкладок ЛЕВОЙ панели, строка из дерева убрана | одна вещь — одно место; в референсе «+» стоит ровно там |
|
||||
| — | в центре и справа «+» нет | добавлять в фиксированный набор нечего, кнопка вышла бы декоративной |
|
||||
Владелец: **«+» слева = «Добавить книгу»**. Отсюда: «+» стоит в ряду вкладок ЛЕВОЙ панели и
|
||||
запускает добавление, дублирующая строка «+ Добавить книгу» из дерева убрана (одна вещь — одно
|
||||
место, и в референсе «+» стоит ровно там); в центре и справа «+» нет — наборы вкладок там
|
||||
фиксированные, пользователь их не расширяет, и кнопка вышла бы декоративной. Остаток строки — Ф-7.
|
||||
|
||||
В S2 действие открывает вкладку-документ «Добавить книгу»; содержимое экрана (§3.1) делает S4.
|
||||
|
||||
|
|
@ -292,14 +83,10 @@
|
|||
Плюс три дефекта структуры в той же реплике: две кнопки настроек, банк продублирован
|
||||
(вкладка слева + отдельная карточка внизу), окно главы слишком мало.
|
||||
|
||||
Что из этого следует и сделано:
|
||||
|
||||
| Было | Стало | Почему |
|
||||
|---|---|---|
|
||||
| Настройки в верхней полосе И внизу левой панели | только внизу левой панели | промт §3.12: настройки — отдельный раздел из низа левой панели, второй вход лишний |
|
||||
| Банк во вкладке слева И карточкой в центре снизу | одна вкладка в правой панели | одна вещь — одно место; слева осталась только навигация |
|
||||
| Центр разрезан на две карточки | одна панель во всю высоту | нижняя карточка была скопирована с терминала Fleet и не несла задачи |
|
||||
| Кнопки сворачивания трёх панелей | двух | кнопка есть у той панели, которая существует |
|
||||
Сделано: настройки — только внизу левой панели (второй вход из верхней полосы снят, промт §3.12),
|
||||
банк — одной вкладкой в правой панели вместо «вкладка слева + карточка в центре», центр — одна
|
||||
панель во всю высоту (нижняя карточка была скопирована с терминала Fleet и не несла задачи),
|
||||
кнопки сворачивания — у двух панелей, а не трёх.
|
||||
|
||||
**Вертикальный параллелизм — суть решения:** рабочий цикл читателя это «читаю перевод → спотыкаюсь
|
||||
о термин → проверяю его в банке». Все три вещи обязаны быть видны одновременно, поэтому колонки:
|
||||
|
|
@ -312,32 +99,6 @@
|
|||
экран открывается **вкладкой в центре**, как документ, а правая панель ведёт в него действием
|
||||
«Подписать банк». В витрине эта связка показана.
|
||||
|
||||
### 02.08 — данные: моки берут форму от движка
|
||||
|
||||
Владелец: **«всё на диске лежит, и главы, и банки памяти»**. Разбор бэкенда подтвердил: моя
|
||||
первая фикстура выдумывала поля (`kind: 'имя'`, `state: 'подписан'`), а промт прямо запрещает
|
||||
изобретать API. Формы переписаны по настоящим:
|
||||
|
||||
- банк — таблица `glossary` (`backend/internal/store/migrate.go:176`): `src · dst · type · sense ·
|
||||
status · source · since_ch/until_ch`. Статусы подписи ровно три: `auto|draft|approved`,
|
||||
провенанс — `seed|ruby|mined` (⚠ **испр. 04.08 по ревью оркестратора:** здесь стояло
|
||||
`seed|ruby|auto` плюс `mined` по комментарию схемы; комментарий устарел, а литералы `"auto"`
|
||||
в коде пишут СТАТУС, не провенанс — `membank/memseed.go:328`).
|
||||
**Типы — исправлено 04.08 по ревью оркестратора:** сама колонка `type` в схеме свободный текст,
|
||||
и её комментарий перечень не закрывает («name|term|title|…»), поэтому истина — гейты в Go:
|
||||
классификатор знает четыре (`terminology/classify.go:15`), банкнота принимает их же плюс
|
||||
`nickname` (`pipeline/banknote.go:74`). Итого пять: `name|place|title|term|nickname`.
|
||||
`org` и `fullname` движок не знает вовсе — они приехали в тип фронта из этой самой строки;
|
||||
- пары — `ChunkExport` из `tmctl export --pairs` (`backend/internal/pipeline/export.go:25`):
|
||||
`chapter · chunk_idx · disposition · flag_reason · detail · final_text · source`;
|
||||
- причины замечаний — enum из `backend/internal/pipeline/disposition.go:56-104`.
|
||||
|
||||
**Следствие для интерфейса, которое из этого выросло:** движок говорит своим языком —
|
||||
`sanitizer_stripped`, «CJK leak in the ru output: 第一节». Промт §4.1 запрещает показывать это
|
||||
пользователю. Значит между движком и экраном обязан стоять **слой перевода вердиктов
|
||||
в продуктовые понятия**, и он заведён одной картой в данных: причина → человеческая фраза;
|
||||
поле `detail` не показывается никогда. Это же ПТ-33 реестра требований.
|
||||
|
||||
### 02.08 — шов данных заведён: моки выкидываются одной папкой
|
||||
|
||||
Владелец: **«все моки рано или поздно придётся выкинуть; нужна минимальность моков,
|
||||
|
|
@ -350,92 +111,30 @@
|
|||
видит только `api/`. Правило теперь машинное, а не на совести ревьюера: `no-restricted-imports`
|
||||
на `**/mock/**` и запрет сетевых вызовов, с исключением для `src/api/**`.
|
||||
|
||||
**Две поправки к этой записи, обе от ревью 02.08 (№3) — исходные формулировки были сильнее
|
||||
сделанного:**
|
||||
|
||||
- «Доменных значений в разметке не осталось» — **было неверно**: в JSX оставались четыре
|
||||
литерала языка (`lang="zh"`, `lang="ru"`, подпись пары `zh → ru`), и тот же проход,
|
||||
который вынес числа в `book.*`, прошёл мимо них в той же строке. Починено: язык — код
|
||||
в данных, имя считается `Intl.DisplayNames`, `lang` берётся из книги; правило закрыто
|
||||
тестом (`src/generality.test.ts`), а не обещанием.
|
||||
- «В день HTTP правится тело функций, экраны не трогаются» — **переобещание**. Верно только
|
||||
про день замены MSW настоящим сервером. Асинхронность приезжает раньше, на S3, вместе
|
||||
с MSW и TanStack Query — то есть ДО первого продуктового экрана (S4–S7), поэтому
|
||||
переписывать экраны и правда не придётся. Но четыре вызова витрины на S3 изменятся.
|
||||
|
||||
### 02.08 — что дал разбор канона: фронт мокает не тот контракт
|
||||
|
||||
`docs/README.md` прямо требует читать `research/23` перед любым кодом стыка. Не читал,
|
||||
и это стоило двух вещей.
|
||||
`docs/README.md` прямо требует читать `research/23` перед любым кодом стыка. Не читал, и это стоило
|
||||
двух ошибок сразу — формы моков и способа связи.
|
||||
|
||||
1. **D39.85 (`research/23` §0): фронт читает ТОЛЬКО read-модель платформы в Postgres,
|
||||
материализованную из NDJSON-потока событий; движок не опрашивается никогда, чтение живого
|
||||
SQLite движка — запрещённый анти-паттерн.** Значит формы `glossary`/`ChunkExport` — словарь
|
||||
предметной области, но НЕ форма API. Типы фронта переписаны так, чтобы это было видно:
|
||||
`src/api/types.ts` объявлен рабочей гипотезой до появления контракта.
|
||||
2. **Единый бэклог, строка 95:** «Контракт API v0 + продуктовый словарь статусов, ранняя
|
||||
вставка, зафиксировать артефактом ДО большого кода обеих сторон — фронт пишется на моках,
|
||||
без пришпиленного контракта моки и API разойдутся». Дом — `docs/architecture/14-api-contract.md`,
|
||||
**файла нет**. То есть фронт делает ровно то, ради предотвращения чего строка заведена.
|
||||
Для S2 (оболочка, `src/ui/`) это не помеха. **Для S3 (слой данных) — вход, которого нет.**
|
||||
**D39.85 (`research/23` §0): фронт читает ТОЛЬКО read-модель платформы в Postgres, материализованную
|
||||
из NDJSON-потока событий; движок не опрашивается никогда, чтение живого SQLite движка — запрещённый
|
||||
анти-паттерн.** Значит движковые формы (таблица `glossary`, `ChunkExport`) — словарь предметной
|
||||
области, но НЕ форма API: форма API живёт только в каноне `docs/architecture/14-api-contract/`.
|
||||
Оттуда же берётся и запрет показывать пользователю язык конвейера (`sanitizer_stripped` и подобное) —
|
||||
это ПТ-33 реестра требований: `detail` не показывается никогда, а вердикт превращается в фразу по
|
||||
словарю машинных кодов (карта — приложение А компаньона контракта).
|
||||
|
||||
### 02.08 — инверсия смысла двух вердиктов (дефект, внесённый фронтом)
|
||||
|
||||
Продуктовые фразы к замечаниям были написаны по ИМЕНИ причины, а не по её доккомменту,
|
||||
и две из пяти получились наоборот:
|
||||
|
||||
- `glossary_miss` — «Термин не подписан в банке» ⟶ **неверно**. `disposition.go:78-85`:
|
||||
«an **approved** term's src fired in the chunk but no accepted dst form appears in the
|
||||
output — the model ignored the glossary». Термин подписан, его проигнорировал перевод.
|
||||
Фраза посылала человека подписывать уже подписанное — по банку, который в каноне
|
||||
центральная ценность.
|
||||
- `sanitizer_stripped` — «Часть блока не переведена» ⟶ **неверно**. `disposition.go:96-104`:
|
||||
«the chunk is NOT lost — the cleaned text is committed as the export»; `status.go:165-170`:
|
||||
«the **least alarming** flag». Самый безобидный флаг из восьми подан как потеря текста.
|
||||
|
||||
Заодно вскрылось, что у движка есть **ратифицированная лестница важности вердиктов**
|
||||
(`status.go:150-175`, ранги 1..8), а интерфейс красил все замечания одинаково — то есть
|
||||
терял единственный сигнал приоритета. Введены две ступени: `attention` и `glance`.
|
||||
|
||||
**Правило на будущее:** фраза к вердикту пишется по доккомменту `disposition.go`, а не по имени
|
||||
причины, и рядом кладётся цитата. Соответствие целиком — в шапке `src/mock/book.ts`; его
|
||||
настоящий дом — контракт API (строка 95), не фронт.
|
||||
|
||||
### 02.08 — ревью №3 на полном каноне: что оно поменяло
|
||||
|
||||
Владелец разрешил читать весь корпус доков и потребовал прогнать ревью после чтения.
|
||||
Прочитано: `CLAUDE.md` · `docs/README.md` · CURRENT-STATE и единый бэклог · `research/23` ·
|
||||
`product-requirements.md` · карта D-лога. **Ответ на вопрос владельца о способе связи:
|
||||
NDJSON — это шов ДВИЖОК↔ПЛАТФОРМА (D39.85), а фронту принадлежит JSON поверх HTTP плюс SSE
|
||||
от платформы (D39.84). Разведено в `FRONTEND_PLAN.md` §0.2, потому что путал их я сам.**
|
||||
|
||||
Ревью: 5 линз × находки, на каждую независимый скептик с установкой опровергать.
|
||||
15 находок → 12 проверено → **5 выжило, 7 опровергнуто**. Хардблокеров нет; S2 не блокирован ничем.
|
||||
|
||||
| Что | Диспозиция |
|
||||
|---|---|
|
||||
| Язык зашит в разметку (`lang="zh"`), а в типе — русское слово | починено: коды в данных + `Intl.DisplayNames` + тест-замок |
|
||||
| Девять состояний нарисованы на ГЛАВАХ, хотя это состояния ПРОГОНА над книгой | починено: состояние → книга, у главы выполнение по юнитам; библиотека из нескольких книг |
|
||||
| Прогресс — наивное «N из M глав», прямо запрещённое собственным планом | починено: две пары по юнитам, фикстура поставлена в середину черновой волны (наивный счётчик дал бы 0) |
|
||||
| Сетевой гейт ловил только `fetch` — `EventSource` (ратифицированный транспорт) проходил молча | починено: восемь форм, все проверены живым нарушением |
|
||||
| `npm run shot` зелен, когда экран не попал в список маршрутов скрипта | починено: тест сверяет `routes.tsx` и `shot.mjs` |
|
||||
|
||||
**Опровергнуто (и это ценнее половины находок):** гипотеза «синхронный `src/api/` — хардблокер,
|
||||
нужен свой хук уже сегодня». Два независимых скептика показали, что S2 данных не читает вовсе,
|
||||
а асинхронность приезжает на S3 вместе с MSW и TanStack Query — то есть самодельный хук был бы
|
||||
работой S3, которую S3 же и выбросит. Правку я не делал; переобещание в журнале поправил.
|
||||
|
||||
**Что нашла не ревьюшница, а снимок:** после переноса состояний на книгу три пары из девяти
|
||||
рисовались одинаково (готова ≡ не начата, перевод ≡ финал, ошибка ≡ не разобрана) — точка
|
||||
не различает девять значений. Метка стала словом; цвет остался вторым каналом и живёт
|
||||
на точке, а не на тексте (цвета замечания и отказа не проходят порог контраста для текста —
|
||||
проверено axe: было 5 узлов, стало 14, вернулось к 5).
|
||||
**Ответ на вопрос владельца о способе связи:** NDJSON — это шов ДВИЖОК↔ПЛАТФОРМА (D39.85), а фронту
|
||||
принадлежит JSON поверх HTTP плюс SSE от платформы (D39.84). Разведено в `FRONTEND_PLAN.md` §0.2,
|
||||
потому что путал их я сам.
|
||||
|
||||
**⛔ ПИНГ ОРКЕСТРАТОРА №19 — 23.08, при разморозке зоны (сейчас действий не требует).**
|
||||
Аудит доков нашёл два расхождения внутри зоны; рукой не лезу, зона заморожена.
|
||||
1. **Три из одиннадцати вопросов ниже уже ОТВЕЧЕНЫ каноном** и просто не погашены — при разморозке
|
||||
сверить каждый против D-лога прежде, чем нести владельцу повторно.
|
||||
2а. **Ключ `prepare` вашего `package.json` — единственный бутстрап РЕПО-ШИРОКОГО гейта доков (дописано 24.08, строка 220).**
|
||||
1. **Три вопроса списка были ОТВЕЧЕНЫ каноном и не погашены** — В-3 (D39.176 п.4: фразы делегированы
|
||||
проекту), В-6 (D39.132 п.2а, а кадр `EventCeiling` снят каноном 0.3.0) и В-10 (D39.138 п.2к —
|
||||
«вежливость»); строки сняты 02.09 при чистке журнала. Остальные восемь — сверить каждый против
|
||||
D-лога прежде, чем нести владельцу повторно.
|
||||
2а. **Ключ `prepare` вашего `package.json` — единственный бутстрап РЕПО-ШИРОКОГО гейта доков (дописано 24.08; см. буллет «Гейты стоят и на коммите» в «Текущем состоянии»).**
|
||||
`frontend/scripts/githooks/install.mjs` — единственный в дереве установщик диспетчера `.git/hooks/pre-commit`, а тот
|
||||
судит `CLAUDE.md` и доки трёх зон, не только ваши. Следствие проверено исполнением: на свежем клоне без `npm install`
|
||||
гейт молча выключен. Просьба при разморозке: перевести `prepare` на зоно-нейтральный установщик, который заведут в
|
||||
|
|
@ -448,13 +147,10 @@ NDJSON — это шов ДВИЖОК↔ПЛАТФОРМА (D39.85), а фрон
|
|||
|
||||
| # | Вопрос | Почему это его вопрос |
|
||||
|---|---|---|
|
||||
| В-1 | **Данные привязаны к одной машине.** Вся фактура — прогоны, экспорты, банки — лежит в `/home/ubuntu/books/gu-zhenren/` вне git; оба `backend/example/sample-*.db` пустые (только схема); пути стенда зашиты в тесты (`miner_parity_test.go:33-35`, `labelharness_test.go:43`) и в `book.yaml` абсолютными путями. Владелец сказал: «мне не нравится, что это живёт конкретно на моём диске». Лечение — платформа (`platform/README.md:20-29`: библиотека книг, хранение исходников и экспортов), но платформы ещё нет ни строки. Фронт от этого не зависит (работает на моках), но **вопрос на движок/платформу, а не на фронт** | скоуп-решение и порядок работ |
|
||||
| В-3 | **Каким словом называть ступень замечания.** Сейчас `attention` и `glance` различаются только цветом полоски: синяя и серая. Зрячему разница читается ещё и светлотой, скринридеру — никак. Лечение то же, что уже применено к состоянию прогона книги: слово рядом с цветом. Но словарь замечаний продуктовый (ПТ-33), а §3.8 требует, чтобы экран не выглядел тревожным, — «важно / неважно» и «ошибка / предупреждение» не годятся. Строка Ф-21 | продуктовая формулировка, которую видит пользователь |
|
||||
| В-6 | **Упёрся в свой потолок ≠ кончились деньги, а интерфейс их не различает.** Владелец 07.08 дал человеку поставить перед стартом колпак в ГЛАВАХ; платформа берёт холд на его сумму. Но машинная причина паузы в контракте ровно одна — `credit_exhausted`, и она про кредит. Значит прогон, честно доехавший до выбранной человеком границы, и прогон, у которого кончился баланс, приходят на экран одной фразой «перевод остановлен: лимиты исчерпаны». Первому она врёт: лимит не исчерпан, человек сам так решил, и следующее действие у него другое — поднять колпак, а не пополнить счёт. Нужно ли второе значение и какая у него фраза — продуктовое. Заведено К-13, строка Ф-31 | продуктовая формулировка, которую видит пользователь, и разное следующее действие |
|
||||
| В-1 | **Данные привязаны к одной машине.** Вся фактура — прогоны, экспорты, банки — лежит в `/home/ubuntu/books/gu-zhenren/` вне git; оба `backend/example/sample-*.db` пустые (только схема); пути стенда зашиты в тесты (`miner_parity_test.go:33-35`, `labelharness_test.go:43`) и в `book.yaml` абсолютными путями. Владелец сказал: «мне не нравится, что это живёт конкретно на моём диске». Лечение — платформа (`platform/README.md`, раздел библиотеки книг: хранение исходников и экспортов); она с тех пор построена и принята (D39.109), поэтому половина вопроса снята фактом — пинг №20 п.3. Фронт от этого не зависит (работает на моках), но **вопрос на движок/платформу, а не на фронт** | скоуп-решение и порядок работ |
|
||||
| В-8 | **Какое слово о состоянии книги стоит в дереве.** Замечание 23 потребовало бейджу ФИКС-место, и оно сделано; но две метки словаря в него не помещаются и усекаются с тултипом — «остановлена: лимиты» (19 знаков) и ветка неизвестного «неизвестное состояние» (21). Укоротить их сессия НЕ вправе: фраза паузы зарезервирована за владельцем (В-6), и первый заход S3.5 её укоротил и был откачен собственным ревью. Варианты: (а) оставить усечение с тултипом, (б) владелец даёт короткое слово для дерева, а полная фраза остаётся в статус-полосе, (в) расширить колонку бейджа за счёт названия книги | продуктовая формулировка, которую видит пользователь |
|
||||
| В-7 | **Физический масштаб интерфейса на мониторе.** Замечание 1 говорит «масштабы великоваты, особенно на 27″ 2K, на 24″ терпимее». Замер при вьюпорте референса расхождения с Fleet не нашёл (§5.5 плана): промежутки, ширины панелей, шаг строки и высота вкладки совпадают. Значит речь про физический размер на конкретном экране, а он зависит от масштабирования ОС, и «просто уменьшить токены» означало бы уехать от референса, который владелец принял. Правильное лекарство — ручка «плотность интерфейса» в настройках (3 ступени), но она требует перевода ВСЕХ размерных токенов в rem и пере-замера, то есть отдельного прохода (Ф-36). Вопрос: нужна ли ручка, и какой масштаб ОС стоит на 27″ 2K — по нему видно, во сколько раз расходится физика | продуктовое решение и цена отдельного пакета |
|
||||
| В-9 | **Какие языки интерфейса продукт обещает.** Механизм переводимости построен (S3.7, Ф-35): слова интерфейса живут в каталоге `src/i18n/ru.ts`, второй язык — второй файл того же вида плюс строка в `src/i18n/language.ts`, ни один компонент при этом не трогается. Чего фронт сделать не может и не должен: решить, какие языки продукт обещает, и выдумать сами переводы. Пока список из одного значения (`ru`), и каркасный пункт настроек честно говорит «сейчас интерфейс только русский» | продуктовое решение и обещание пользователю |
|
||||
| В-10 | **Спойлер банка — это защита или вежливость?** Замер S4: закрытое описание лежит в тексте страницы, `window.find()` его находит, то есть Ctrl+F подсвечивает закрытую ячейку; под `@media print` размытие сохраняется, но текстовый слой PDF строится из DOM. Если спойлер — ЗАЩИТА от спойлера сюжета, описание не должно приезжать в браузер до раскрытия, а это правка контракта (признак «показывать смыслы» на чтении банка). Если ВЕЖЛИВОСТЬ («не мозолит глаза, но достать можно») — сделанного достаточно, и цена названа. Строка Ф-59 | продуктовое решение и правка контракта |
|
||||
| В-11 | **Фразы отказа книги (PD-173) — на утверждение.** Контракт 0.2.3 несёт машинную причину (`source_unreadable` · `not_configured` · `parser_unavailable`), а слова пишет клиент — их сочинила эта сессия по слову владельца 14.08 и они ждут его «да»: «Файл не удалось разобрать на разделы» / «Проверьте файл — возможно, это не текст книги — и добавьте её заново»; «Сервис пока не готов принять эту книгу» / «Файл здесь ни при чём: повторная загрузка поможет не раньше, чем мы это наладим»; «Не удалось обработать файл на нашей стороне» / «Файл здесь ни при чём: попробуйте добавить книгу позже»; неизвестная причина — «Книгу не удалось принять» / «Причина не названа. Попробуйте добавить книгу заново или позже» | продуктовая формулировка, которую видит пользователь |
|
||||
| В-2 | **Авторское право на текст фикстур.** Витрина использует короткую выдержку из настоящего прогона (蛊真人). Правило проекта уже есть: `eval/.gitignore:4` держит копирайтные тексты вне git. Сейчас взят объём под проверку плотности и не больше; нужно ли убрать совсем и заменить синтетикой — решать владельцу | юридика |
|
||||
| В-4 | **Чем подписывать главу в дереве, если заголовок — ровно «Глава N».** Титул движок рендерит детерминистически из шаблона пары (`configs/langpacks/zh-ru/heading.txt`: `template Глава {n}`), а подзаголовок остаётся телом текста и названием НЕ является. На 2284 разделах это дерево из 2284 одинаковых по форме строк. Оставить так, показывать номер иначе или просить у контракта что-то ещё — продуктовое решение. Артефакт §1.4 и К-3 | продуктовая формулировка, которую видит пользователь |
|
||||
|
|
@ -468,46 +164,24 @@ NDJSON — это шов ДВИЖОК↔ПЛАТФОРМА (D39.85), а фрон
|
|||
|
||||
**Фикс-лист ФС (носитель состава — эта запись; вход промта S5):** ФС-1 (major, мир intake) прогон над загруженной книгой стартует с `begin(0)` — общий прогресс 0/0 весь прогон, ETA = NaN (лечение: begin суммой units_total глав); путь заказа §4 «книга из формы переводится» в мок-мире добить · ФС-2 showcase: принятый старт прогона не меняет мир — следующее чтение отдаёт прежнее состояние, «Перевести» выглядит как ничего · ФС-3 мир intake: повторный старт переиспользует id прогона — поток не переподписывается, translating вечно (verify_bank тем же кластером: наблюдаемого эффекта в моках нет, awaiting_bank недостижим) · ФС-4 `BookIntake.title` maxLength 200 не держит никто на клиенте (TextField без лимита — собственная спека нарушаема своим клиентом) · ФС-5 докблок `upload.ts` несёт СНЯТОЕ правило «field after file → 400» (неверно для необязательного) · ФС-6 два дока «spectral шестым шагом» (`STACK_DECISIONS.md:116`, `FRONTEND_PLAN.md:124`) — spectral четвёртый из шести · ФС-7 мок считает части формы по ВСЕЙ форме (платформа — до файла) и меряет 413 по file.size (платформа — по всему телу) — мок строже провода · ФС-8 флейк сцены refusals: closeModal не дождался закрытия за 5000 мс (1 из 3 на тихой машине) · ФС-9 `role="status"` оборачивает байтовый прогресс отправки — на живом XHR скринридер зачитывает каждое обновление (в регионе — только смены состояний) · ФС-10 группа из одного раздела подписывается «разделы 201–201» · ФС-11 интейк-мир: ПЕРВОЕ нажатие «Перевести» всегда 409 (счётчик; кандидат — маркер) · ФС-12 после-пакные числа Ф-35 слегка отстают от дерева (пересборка: 800293 / 246670 gzip против записанных 800219 / 243446).
|
||||
|
||||
**Поправки к записи S4 (чужой текст не редактирую):** «словарь платформы — четыре значения» → в коде ПЯТЬ (пропущен `schema_mismatch`) · «одна из трёх фраз сегодня недостижима» → недостижимы ВСЕ ТРИ: платформа `reject_reason` вообще не проецирует (`v0.go:497` «never projected») — доезжает только ветка неизвестного · §1/§2 записи несут снятое правило PD-172 «поле после файла = 400», которое сама же §9а опровергла (код и спека исправлены, текст записи — нет) · «136 проверок» = 134 (наивный греп ловил 2 строки vite) · «31 строка DOM / 59 618px» — замер S2 04.08, не этого пака · «46 находок → 6 выжило» = 5 различных (одна — дубль) · три поправки к записи S3.7 из заказа §6: разобрана только цена бандла; «дрейф ширин hidden» и «счёт 106 из 119» остались на записи оркестратора 14.08 (носитель там, дефекта сессии нет — фиксирую для полноты).
|
||||
**Поправки к записи S4** (сама запись — в [archive/frontend-PROGRESS-S0-S4.md](archive/frontend-PROGRESS-S0-S4.md); чужой текст не редактирую): «словарь платформы — четыре значения» → в коде ПЯТЬ (пропущен `schema_mismatch`) · «одна из трёх фраз сегодня недостижима» → недостижимы ВСЕ ТРИ: платформа `reject_reason` вообще не проецирует (`v0.go:497` «never projected») — доезжает только ветка неизвестного · §1/§2 записи несут снятое правило PD-172 «поле после файла = 400», которое сама же §9а опровергла (код и спека исправлены, текст записи — нет) · «136 проверок» = 134 (наивный греп ловил 2 строки vite) · «31 строка DOM / 59 618px» — замер S2 04.08, не этого пака · «46 находок → 6 выжило» = 5 различных (одна — дубль) · три поправки к записи S3.7 из заказа §6: разобрана только цена бандла; «дрейф ширин hidden» и «счёт 106 из 119» остались на записи оркестратора 14.08 (носитель там, дефекта сессии нет — фиксирую для полноты).
|
||||
|
||||
*(Записи сессий S0–S4 и их приёмок (02–14.08) — в [archive/frontend-PROGRESS-S0-S4.md](archive/frontend-PROGRESS-S0-S4.md); вынесено 16.08 по слову владельца. Запись приёмки S4 с фикс-листом ФС — ВЫШЕ, она живая.)*
|
||||
|
||||
## Пинг оркестратора №15 (09.08): контракт 0.2.1 — `503` на старте прогона; платформа-раннер заленден
|
||||
|
||||
- **Контракт бампнут 0.2.0 → 0.2.1** (D39.123): `POST /books/{bookId}/runs` получил ответ **`503`**
|
||||
(`ServiceUnavailable`, problem+json) — старт прогона на деплое, где шов движка не сконфигурирован;
|
||||
временное состояние, ретрай позже, `Retry-After` не обещан. Зеркало `frontend/docs/api-contract/`
|
||||
синхронизировано байт-в-байт этим же лендингом. При следующей сессии — перегенерировать типы из
|
||||
спеки; других изменений форм нет, правка аддитивная.
|
||||
- **Платформа-раннер заленден** (`d29e30c`): пять ручек `/v0` контракта живые — библиотека ·
|
||||
карточка · run-options · старт прогона · usage; прогресс прогона пофазный (draft/edit) в карточке.
|
||||
Фронт может говорить с живой платформой вместо моков — стык проверен 47 wire-проверками приёмки.
|
||||
- Известное ограничение до эмиттера (строка 103): стоп по потолку и штатный останов приезжают как
|
||||
`failed` (PD-113/двойник) — на экране не строить различение этих состояний до строки 165 движка.
|
||||
|
||||
## Пинг оркестратора №16 — 09.08.2026 (кросс-ссылка зонных половинок, строка 167)
|
||||
|
||||
Движковая половина темы Ф-30/В-4 («глава без заголовка») — **строка 160 единого бэклога** (`titleRaw` в манифест · provenance у heading · тип chapter/fragment; research/27 §6 Этап 0). До её лендинга `heading` манифеста = ВРЕМЕННЫЙ рендер движка (D39.122 п.2д) — честный заголовок из данных приедет строкой 160, продуктовое слово — В-4 на владельце. На экране ничего не менять до обоих.
|
||||
|
||||
## Пинг оркестратора №17 — 16.08.2026 (зона во фризе; контракт-ревью принято D39.138 — четыре ваших вопроса ОТВЕЧЕНЫ, входы разморозки)
|
||||
|
||||
Контракт-ревью API v0 отработало отдельной сессией и ПРИНЯТО (отчёт — `docs/research/28-contract-review.md`; решения владельца — его §8, ратификация D39.138). Заказан ЛОМАЮЩИЙ батч 0.3.0 (строка 183 единого бэклога) — типы перегенерируются после его лендинга; до него нормативной остаётся 0.2.3. ⚠ Читать ОРИГИНАЛ отчёта, не пересказ. По вашим строкам:
|
||||
1. **Ф-56 (конец интейка):** Б-6(а) — поток перевешивается на КНИГУ (`GET /books/{id}/events`) с кадрами конца разбора и конца прогона и `204` на завершённом; ваш опрос — легитимное лечение до батча.
|
||||
2. **Ф-57 (пары):** Б-2 — `GET /capabilities` (пары со статусом · порог интейка · форматы · размеры страниц · версия контракта); `languages.ts` станет чтением.
|
||||
3. **Ф-61 (английские фразы):** В-1 решён владельцем вариантом B (§8 п.4): машинный `code` + `request_id`; `title`/`detail` — developer-facing, **фразу рисует клиент по словарю кодов** (ваша таблица `AddBook.tsx:282-288` становится нормой); после батча строка `AddBook.tsx:289` (`problem?.detail ?? problem?.title`) и её сёстры `Loaded.tsx:66`, `RunStart.tsx:188` СНИМАЮТСЯ.
|
||||
4. **Ф-62 (переименование):** Б-5 — `PATCH /books/{id}` ТОЛЬКО с `title` (409 при живом прогоне) + `DELETE`; правка — отображение, до движкового брифа не доезжает. Жанр из продукта УХОДИТ совсем (Б-23, слово владельца 16.08) — поле формы снимется при разморозке.
|
||||
5. **Ф-43 протух текстом:** движковая половина банка ПОСТРОЕНА ещё D39.122 (сайдкар `<db>.bank.json` + `bank-stop.json`), не хватает платформенной проекции (строка 169 единого бэклога, вход P7) — обновите текст строки.
|
||||
6. **Решение владельца К-6 (§8 п.11):** замечания показывать ВСЕ; в тексте — сворачивать и раскрывать кнопкой; словарь ступеней заранее НЕ проектировать; зависимость `Note.id` закрывает батч. **Зарегистрируйте зонной строкой при разморозке.**
|
||||
1–6. **Ф-56 · Ф-57 · Ф-61 · Ф-62 · Ф-43 · К-6 — ответы ревью, и все они с тех пор ВОШЛИ в канон** (батч 0.3.0 и далее до 0.9.0; 20 операций), поэтому при разморозке строки читаются по канону, а не по этому пересказу. **Ф-56** (п.1): поток перевешен на КНИГУ — `GET /books/{bookId}/events` с кадрами конца разбора и конца прогона и `204` на завершённом; ваш опрос интейка снимается. **Ф-57** (п.2): `GET /capabilities` (пары со статусом · порог интейка · форматы · размеры страниц · версия контракта) — `languages.ts` становится чтением. **Ф-61** (п.3): владелец выбрал вариант B (§8 п.4) — машинный `code` + `request_id`, `title`/`detail` developer-facing, фразу рисует КЛИЕНТ по словарю кодов (ваша таблица `AddBook.tsx:282-288` становится нормой; строка `AddBook.tsx:289` — `problem?.detail ?? problem?.title` — и её сёстры `Loaded.tsx:66`, `RunStart.tsx:188` СНИМАЮТСЯ; карта «код → фраза» — приложение А компаньона, слова делегированы проекту D39.176 п.4). **Ф-62** (п.4): `PATCH /books/{bookId}` ТОЛЬКО с `title` (409 при живом прогоне) плюс `DELETE`; жанр из продукта УХОДИТ совсем (Б-23, слово владельца 16.08) — поле формы снять. **Ф-43** (п.5): движковая половина банка построена, но названный тут сайдкар `bank-stop.json` СНЕСЁН (D39.158) — строка протухла ОБЕИМИ половинами, читать пинг №20 п.2 и пинг №22. **К-6** (п.6, §8 п.11): замечания показывать ВСЕ, в тексте сворачивать и раскрывать кнопкой, словарь ступеней заранее НЕ проектировать — зарегистрировать зонной строкой при разморозке. ⚠ Читать ОРИГИНАЛ отчёта, не пересказ.
|
||||
7. **Мок-находки §9 (чинить при разморозке):** отказ интейка триггерится маркером в ИМЕНИ файла (`mock/intake.ts:238`) — переключатель без аналога на сервере; стоп игнорирует `runId` (`mock/handlers.ts:190`); мир `scale`: чтения на ревизии 4102, кадры с 1841 — расходятся по построению (`mock/worlds.ts:136`); в `loading` любой POST уходит МИМО мока в реальную сеть (`handlers.ts:59`); переполнение обхода страниц = статус 0 = неотличимо от «сервер недоступен» (`client.ts:114` → `Loaded.tsx:65`); мок интейка считает ВСЕ части формы, платформа — только до файла (`intake.ts:212` против `v0.go:337-360`).
|
||||
8. **Перф-входы S5 (§5б/Б-11а):** снять `refetchOnWindowFocus` для тяжёлых коллекций (замер: самый частый трафик порождает переключение вкладок, не пайплайн — 12 вкладок = 562 КБ на фокус) и поднять `staleTime` после появления ETag/304 у платформы; ErrorBoundary в зоне нет ни одного (Б-0: TypeError рендера при смене словаря волн) — завести при разморозке; 401 сегодня тупик без ссылки на вход (§4 №27) — зонная строка. Вопросы — через владельца.
|
||||
|
||||
**Аддендум к пингу №17 (16.08, свип планировочных доков D39.139):** (1) Три носителя зоны учат ПОФАЗНОМУ прогрессу «draft N/M ∥ edit N/M» — `FRONTEND_SESSION_PROMPT.md` §9 п.1, `FRONTEND_PLAN.md` §6 п.1, `STACK_DECISIONS.md` §8 п.1; после батча 0.3.0 это устареет (Б-0/D39.138: на проводе ОДИН счётчик до ближайшей остановки, знаменатель — купленный объём) — при разморозке обновить все три, не только код. (2) `STACK_DECISIONS.md` §8 протух целиком: все четыре пункта «достроить в движке» ПОСТРОЕНЫ (пофазные счётчики и манифест — D39.122; JSON таблицы подписи — `bank-stop.json` D39.122; TM_TRACE_ID — P6/D39.132). (3) Диспозиции строк: **Ф-59** — В-10 закрыт владельцем 16.08 «вежливость» (D39.138 п.2к: всё правильно, цена названа — строку закрыть); **Ф-37** — К-10 отвечен «НЕ строить» (поправка приёмки ревью: фазы уходят с провода и у главы — пометка черновика остаётся книжной, хвост пере-диспозиционировать); **Ф-39** — закрыта ещё словом владельца 15.08 (D39.136 п.4б: полей BankTerm не заводить, sense хватает; его калибровка — строка 178 единого). Вопросы — через владельца.
|
||||
**Аддендум к пингу №17 (16.08, свип планировочных доков D39.139):** (1) Три носителя зоны учат ПОФАЗНОМУ прогрессу «draft N/M ∥ edit N/M» — `FRONTEND_SESSION_PROMPT.md` §9 п.1, `FRONTEND_PLAN.md` §6 п.1, `STACK_DECISIONS.md` §8 п.1; батч 0.3.0 ЗАЛЕНДЕН, и это устарело (Б-0/D39.138: на проводе ОДИН счётчик до ближайшей остановки, знаменатель — купленный объём) — при разморозке обновить все три, не только код. (2) `STACK_DECISIONS.md` §8 протух целиком: все четыре пункта «достроить в движке» ПОСТРОЕНЫ (пофазные счётчики и манифест — D39.122; JSON таблицы подписи — был `bank-stop.json` (D39.122), СНЕСЁН вместе с писателем D39.158, см. пинг №22; TM_TRACE_ID — P6/D39.132). (3) Диспозиции строк: **Ф-59** — В-10 закрыт владельцем 16.08 «вежливость» (D39.138 п.2к: всё правильно, цена названа — строку закрыть); **Ф-37** — К-10 отвечен «НЕ строить» (поправка приёмки ревью: фазы уходят с провода и у главы — пометка черновика остаётся книжной, хвост пере-диспозиционировать); **Ф-39** — закрыта ещё словом владельца 15.08 (D39.136 п.4б: полей BankTerm не заводить, sense хватает; его калибровка — строка 178 единого). Вопросы — через владельца.
|
||||
|
||||
**Дописка к аддендуму (16.08):** гейт на утечку конвейера в провод — «на проводе нет имён стадий/волн и движковых словарей», машинный тест по образцу `generality.test.ts` — назначен вашей зоне (D39.138 п.2а; спек-сессия батча запишет правило в компаньон контракта). Строить при первом касании зоны вместе с синком зеркала и перегенерацией типов.
|
||||
|
||||
**Пинг оркестратора №17 — 16.08 (модель подписи банка, слово владельца; читать ПЕРЕД S5):** подписывается ВЕСЬ банк ОДНИМ действием юзера — черновой перевод + черновой банк появляются вместе после черновой волны, юзер смотрит банк, правит ТОЛЬКО то, что захотел, и жмёт один ОК (= resume) — перевод уходит в доработку; нерешённые термины едут как предложил сервис. Пер-термная масс-подпись сотен строк НЕ строится — это была инерция (владелец уже говорил зоне 10.08, снимая счётчики «подписано N»; теперь снята и из контракта: 409-гейт полноты убран, `pending_decisions`/`complete` информационные — D39.144). Строки Ф-44/Ф-45/Ф-46 при разморозке читать через эту модель: «действие подписи» = один ОК + опциональные точечные правки, НЕ проход по строкам. Пост-ридинговые правки банка («поправить/добавить термин → пере-генерация по вхождениям») — направление, механика не спроектирована нигде (полигон работает над точечным редактором) — экранов под неё не строить до отдельного слова. Вопросы — через владельца.
|
||||
|
||||
**Дописка к пингам №17 (16.08, по аудиту готовности зоны — ЧЕТЫРЕ нормы зоны сменили знак, читать ДО всего остального):** (1) **Канон контракта = 0.3.0, ЗАЛЕНДЕН (D39.142/143); ваше зеркало `docs/api-contract/` осталось на 0.2.3 РАТИФИЦИРОВАННО** (D39.142 п.5, на время фриза) — «зеркало = байт-копия» временно НЕ действует, нормативен только канон в `docs/architecture/14-api-contract/`; синк + перегенерация типов = первое касание зоны. (2) **Запрет клиентской метки «Глава {n}» СНЯТ каноном 0.3.0** — клиент теперь ОБЯЗАН сам рисовать порядковый в локали интерфейса, когда `heading`/`number` = null (Ф-30 и пинг №16 «на экране ничего не менять» — устарели; движковая строка 160 остаётся про НАСТОЯЩИЕ названия). (3) **Ф-31/В-6 закрыты ещё D39.132 п.2а** (`null` на проводе ратифицирован), а кадр `EventCeiling`, на который они опирались, снят 0.3.0 целиком — строки закрыть при разморозке. (4) **В-11 (фразы) пере-собрать на словарь 0.3.0**: карта «код → фраза» — приложение А компаньона; `parser_unavailable` переименован в `processing_failed`, добавлен `content_refused`. Плюс: **дев-стенд платформы СУЩЕСТВУЕТ с P6** (`tmplatformctl seed` + дев-вход, D39.132) — Ф-29 (снос моков) и Ф-18 (вес бандла) читать через него; статус зоны: ФРИЗ (D39.136 п.2), снятие = лендинг P7. Вопросы — через владельца.
|
||||
**Дописка к пингам №17 (16.08, по аудиту готовности зоны — ЧЕТЫРЕ нормы зоны сменили знак, читать ДО всего остального):** (1) **Ваше зеркало `docs/api-contract/` остаётся на 0.2.3 РАТИФИЦИРОВАННО** (D39.142 п.5, на время фриза) — «зеркало = байт-копия» временно НЕ действует, нормативен только канон `docs/architecture/14-api-contract/` (сегодня 0.9.0 — пинг №20 п.4); синк + перегенерация типов = первое касание зоны. (2) **Запрет клиентской метки «Глава {n}» СНЯТ каноном 0.3.0** — клиент теперь ОБЯЗАН сам рисовать порядковый в локали интерфейса, когда `heading`/`number` = null (строка Ф-30 и прежний пинг №16 «на экране ничего не менять» устарели; сам пинг №16 снят при чистке журнала 02.09, движковая строка 160 остаётся про НАСТОЯЩИЕ названия). (3) **Ф-31/В-6 закрыты ещё D39.132 п.2а** (`null` на проводе ратифицирован), а кадр `EventCeiling`, на который они опирались, снят 0.3.0 целиком — ИСПОЛНЕНО 02.09: строка Ф-31 зачёркнута, вопрос В-6 снят с листа. (4) **В-11 (фразы) пере-собрать на словарь 0.3.0**: карта «код → фраза» — приложение А компаньона; `parser_unavailable` переименован в `processing_failed`, добавлен `content_refused`. Плюс: **дев-стенд платформы СУЩЕСТВУЕТ с P6** (`tmplatformctl seed` + дев-вход, D39.132) — Ф-29 (снос моков) и Ф-18 (вес бандла) читать через него; статус зоны: ФРИЗ (D39.136 п.2; ⚠ условие «снятие = лендинг P7» отменено пингом №18 от 17.08 — разморозка отдельным словом владельца). Вопросы — через владельца.
|
||||
|
||||
**Пинг оркестратора №18 — 17.08.2026: ФРИЗ ПРОДЛЁН, разморозка отвязана от P7 (слово владельца, D39.147).** Прежнее условие «разморозка = лендинг P7 + гейт S5» БОЛЬШЕ НЕ ДЕЙСТВУЕТ: владелец 17.08 сказал морозить зону дальше — сначала движок и платформа доводятся до состояния «работают и отдают результат», и только потом фронт накручивается на готовую функциональность. Практически: **лендинг P7 зону не размораживает, промт S5 не выдаётся**, разморозка — отдельным словом владельца. Всё, что было записано как первое касание зоны (синк зеркала контракта 0.3.0 · перегенерация типов · моки · гейт утечки конвейера · фикс-лист ФС-1..12 · Ф-63/Ф-28 · пере-сборка фраз В-11 по словарю кодов), НЕ отменено — оно ждёт разморозки, а не P7. Направление, которое стоит знать заранее: моки при разморозке уступают реальному источнику — дев-стенд платформы существует с P6 (`tmplatformctl seed` + дев-вход), а форму «держать моки на уровне бэкендов или отдельно» владелец назвал направлением, но механику проектировать пока не просил. Живой smoke против дев-стенда остаётся легитимным и во фризе. Вопросы — через владельца.
|
||||
|
||||
|
|
@ -522,33 +196,25 @@ NDJSON — это шов ДВИЖОК↔ПЛАТФОРМА (D39.85), а фрон
|
|||
|
||||
### Правка оркестратора №22 — 01.09.2026 (ревизия доков, батч Б0 «канон 0.9.0»)
|
||||
|
||||
⚠ **Я правил ДВА файла вашей зоны** — по прямой санкции владельца 01.09 на документацию всех зон.
|
||||
Кода зоны не трогал; зеркало `frontend/docs/api-contract/openapi.yaml` НЕ трогал (его отставание
|
||||
ратифицировано D39.142 п.5 на время фриза), `src/api/schema.ts` и `src/mock/handlers.ts` НЕ трогал.
|
||||
|
||||
- `frontend/README.md` — буллет о байт-зеркале учил неверному: канон ушёл на **0.9.0**, зеркало стоит
|
||||
на 0.2.3, байт-равенства сегодня НЕТ, и правило «зеркало = байт-копия» на время фриза
|
||||
приостановлено ратифицированно. Дописана ⚠-оговорка; сам буллет не снесён.
|
||||
- `frontend/docs/frontend-PROGRESS.md` (эта строка 9) — «канон 0.8.0 … шесть миноров» → **0.9.0, СЕМЬ**.
|
||||
Цена ошибки была не косметическая: перечень первого касания зоны планировался на неверное число
|
||||
перегенераций.
|
||||
|
||||
**Проверено моей командой:** канон `docs/architecture/14-api-contract/openapi.yaml` = `0.9.0`,
|
||||
зеркало = `0.2.3`, операций 20 против 16, миноров между ними семь (0.3.0…0.9.0).
|
||||
Находки ревизии: `N013`, `N022`, `N026`, `N034`, `N053`, `N056`, `N108` — реестр
|
||||
`docs/DOC_REVISION_FINDINGS.md`.
|
||||
⚠ **Оркестратор правил ДВА файла вашей зоны** — по прямой санкции владельца 01.09 на документацию
|
||||
всех зон: `frontend/README.md` (дописана ⚠-оговорка к буллету о байт-зеркале) и шапку этого журнала
|
||||
(«канон 0.8.0 … шесть миноров» → **0.9.0, СЕМЬ**; перечень первого касания планировался на неверное
|
||||
число перегенераций). Кода зоны, зеркала `api-contract/openapi.yaml`, `src/api/schema.ts` и
|
||||
`src/mock/handlers.ts` не трогал. Числа проверены его исполнением: канон
|
||||
`docs/architecture/14-api-contract/openapi.yaml` = `0.9.0`, зеркало = `0.2.3`, операций 20 против 16,
|
||||
миноров между ними семь (0.3.0…0.9.0). Находки ревизии — реестр `docs/DOC_REVISION_FINDINGS.md`.
|
||||
|
||||
### Пинг оркестратора №22 — 02.09.2026 (ревизия доков, находка N018)
|
||||
|
||||
**`.bank-stop.json` БОЛЬШЕ НЕ СУЩЕСТВУЕТ**, а ваш живой док его описывает как существующий.
|
||||
**`.bank-stop.json` БОЛЬШЕ НЕ СУЩЕСТВУЕТ**, а ваш живой док описывал его как существующий.
|
||||
|
||||
Файл снесён вместе с писателем и четырьмя тестами при постройке входной двери шва (**D39.158**).
|
||||
Проверено моей командой: `grep -rc 'bank-stop.json' backend/ --include='*.go'` → **ни одного вхождения**;
|
||||
холодный прогон 31.08 подтвердил это на диске. Свой носитель (`docs/PROGRESS.md`, строка 157) я поправил.
|
||||
|
||||
**Ваш носитель:** `frontend/docs/frontend-PROGRESS.md` около строки 499 (Ф-43 и аддендум к пингу №17)
|
||||
называет `.bank-stop.json` как живой сайдкар. Зона заморожена — правьте при первом касании,
|
||||
но в перечень касания это внести стоит: планировать чтение несуществующего файла дорого.
|
||||
**Ваш носитель ПОЧИЩЕН 02.09** чисткой этого журнала: ни пинг №17 п.5, ни аддендум к нему больше не
|
||||
называют `.bank-stop.json` живым сайдкаром. Остаток первого касания — строка Ф-43 бэклога: она
|
||||
протухла обеими половинами (пинг №20 п.2).
|
||||
|
||||
⚠ **Что править НЕ надо** (проверено, это законная история в прошедшем времени, а не дрейф):
|
||||
`docs/architecture/18-bank-ontology.md`, `backend/docs/SEAM_FIX3_PLAN.md`,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue