# Журнал зоны «Фронт» > Зонный журнал: сессии фронта пишут сюда сами, работают независимо, в `docs/PROGRESS.md` > оркестратора не пишут (решение владельца 02.08). Хвосты — `BACKLOG.md`, план и замеры — > `FRONTEND_PLAN.md`, ратифицированные пины — `STACK_DECISIONS.md`. > Сверху — текущее состояние, ниже — хроника по сессиям. ## Текущее состояние - **Пройдено:** S0 (план) и S1 (инструменты, скриншот-цикл, `tokens.css`, витрина). - **Дальше:** S2 — оболочка трёх панелей и слой `src/ui/`. - **Ждёт владельца:** контраст палитры (`BACKLOG.md` Ф-11) и два вопроса ниже — В-1 диск, В-2 копирайт. - **Ждёт движка:** до S2 — ничего. **Вход в S3 — контракт API v0 (единый бэклог, строка 95, `docs/architecture/14-api-contract.md`): файла нет, а без него моки и API разойдутся — ровно то, ради чего строка заведена как ранняя вставка.** Дальше — манифест глав и пофазный прогресс (строки 99–100), к S5 — машиночитаемая таблица подписи банка (строка 101). - `npm run check` зелёный (5 шагов, 27 тестов, тип-осведомлённый линт), `npm run check:full` зелёный, гейт доступности в скриншот-цикле зелёный. ## Решения владельца по продукту Записываю здесь, потому что они меняют раскладку из §2 промта, а промт правит не фронт-сессия. ### 02.08 — раскладка: банк памяти переезжает в правую панель Владелец, глядя на витрину: **«банк памяти перенёс бы в правую вкладку, чтоб можно было параллельно вертикально смотреть и оригинал, и главу, и банк памяти, а не горизонтально»**. Плюс три дефекта структуры в той же реплике: две кнопки настроек, банк продублирован (вкладка слева + отдельная карточка внизу), окно главы слишком мало. Что из этого следует и сделано: | Было | Стало | Почему | |---|---|---| | Настройки в верхней полосе И внизу левой панели | только внизу левой панели | промт §3.12: настройки — отдельный раздел из низа левой панели, второй вход лишний | | Банк во вкладке слева И карточкой в центре снизу | одна вкладка в правой панели | одна вещь — одно место; слева осталась только навигация | | Центр разрезан на две карточки | одна панель во всю высоту | нижняя карточка была скопирована с терминала Fleet и не несла задачи | | Кнопки сворачивания трёх панелей | двух | кнопка есть у той панели, которая существует | **Вертикальный параллелизм — суть решения:** рабочий цикл читателя это «читаю перевод → спотыкаюсь о термин → проверяю его в банке». Все три вещи обязаны быть видны одновременно, поэтому колонки: навигация · оригинал+перевод · справочное о читаемой главе. Правая панель становится панелью контекста, и её вкладки — `О книге · Замечания · Банк`. **Что при этом надо развести (иначе §3.5 ломается):** у банка две разные работы. Справочная — «что это за термин», ей хватает 320px правой панели. Подписная — сотни терминов подряд, клавиатура важнее мыши, массовые действия — в 320px не помещается. Поэтому подписной экран открывается **вкладкой в центре**, как документ, а правая панель ведёт в него действием «Подписать банк». В витрине эта связка показана. ### 02.08 — данные: моки берут форму от движка Владелец: **«всё на диске лежит, и главы, и банки памяти»**. Разбор бэкенда подтвердил: моя первая фикстура выдумывала поля (`kind: 'имя'`, `state: 'подписан'`), а промт прямо запрещает изобретать API. Формы переписаны по настоящим: - банк — таблица `glossary` (`backend/internal/store/migrate.go:176`): `src · dst · type · sense · status · source · since_ch/until_ch`. Типы: `name|term|title|place|org|nickname|fullname`, статусы подписи ровно три: `auto|draft|approved`, провенанс `seed|ruby|auto|mined`; - пары — `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 — шов данных заведён: моки выкидываются одной папкой Владелец: **«все моки рано или поздно придётся выкинуть; нужна минимальность моков, минимальная инвазивность в код и изолированность»**. На вопрос «соблюдаешь ли» честный ответ был «нет»: правило «данные только через `src/api/`» стояло в плане, а в коде компонент импортировал фикстуру напрямую — шва не было вовсе. Радиус поражения был мал (один экран), но образец для S2–S7 задавался неправильный. Заведено: `src/api/` — типы и функции, единственный вход; `src/mock/` — фикстура, которую видит только `api/`. Правило теперь машинное, а не на совести ревьюера: `no-restricted-imports` на `**/mock/**` и `no-restricted-globals` на `fetch`, с исключением для `src/api/**`. Доменных значений в разметке не осталось — их было восемь, и grep их не находил. **В день появления HTTP правится тело функций в `api/` и удаляется одна папка; экраны не трогаются.** ### 02.08 — что дал разбор канона: фронт мокает не тот контракт `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 (слой данных) — вход, которого нет.** ### 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), не фронт. ## Открытые вопросы к владельцу | # | Вопрос | Почему это его вопрос | |---|---|---| | В-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`: библиотека книг, хранение исходников и экспортов), но платформы ещё нет ни строки. Фронт от этого не зависит (работает на моках), но **вопрос на движок/платформу, а не на фронт** | скоуп-решение и порядок работ | | В-2 | **Авторское право на текст фикстур.** Витрина использует короткую выдержку из настоящего прогона (蛊真人). Правило проекта уже есть: `eval/.gitignore:4` держит копирайтные тексты вне git. Сейчас взят объём под проверку плотности и не больше; нужно ли убрать совсем и заменить синтетикой — решать владельцу | юридика | ## Хроника ### 02.08 — сессия S0+S1 (первая фронт-сессия) **Заленжено:** `e9a6bb2` план S0 · `b98afb5` весь код S1 · `d8437d6` закрытие дыр после ревью. **Построено:** каркас Vite 8.2.0 + React 19.2.8 + TS 6.0.3 точными пинами (22 из 23 совпали с latest на 02.08; TS 6 при latest 7.0.2 — намеренно, у TS 7 нет программного API); `npm run check` из пяти шагов; скриншот-цикл на Playwright без sudo, с локальными системными библиотеками и шрифтом CJK в `.tooling/`; `tokens.css` по замерам референса; витрина. **Поправка к замерам промта §1.1:** роль `#353739` там смешана. В кадре это разные вещи — вкладка открытого документа залита `#142f4c`, вкладка панели `#27292b`, а `#353739` лежит только под строкой дерева и клавишами-чипами. В токенах роли разведены. **Сведение с референсом:** промежутки, верхний край панелей, радиус, шаг строки 26px, подложка выделения 24px и нижняя полоса сошлись с `fleet.png` до пикселя (перемерено тем же кодом). Расхождения названы в `FRONTEND_PLAN.md` §5.2, вид принят владельцем. **Адверсариальное агентское ревью диффа** (6 ревьюеров по дименсиям + по 2 опровергателя на находку; 30 находок, 18 выживших). Пять дефектов были в самих гейтах — то есть в главной поставке S1 — и соло-самопроверка их не видела: голое `/* stylelint-disable */` снимало гейт цвета молча; опечатка в имени токена не ловилась ничем; `shot.mjs` проглатывал первый маршрут; запрет инлайнового стиля ловил 3 формы из 11; `check` был зелёным при warning. Всё починено и перепроверено живыми нарушениями, перечень форм — `FRONTEND_PLAN.md` §5.4. **Урок, вписанный в §5.4:** формулировка «проверено живым нарушением» без перечня форм — ловушка. Она звучит как машинная гарантия, а покрывает ровно те входы, которые придумал автор. **Ф-8 закрыт технически, решение владельца не потребовалось.** Вопрос был «запрещать ли `/* eslint-disable */` в TSX». Отраслевая практика — не бинарный `noInlineConfig`, а `@eslint-community/eslint-plugin-eslint-comments` 4.7.2: `no-unlimited-disable` требует называть правило, `require-description` — писать причину, `disable-enable-pair` — закрывать область. Легитимное точечное подавление `react-hooks/exhaustive-deps` с причиной проходит, голое отключение падает тремя ошибками. Симметрично `reportUnscopedDisables` на стороне CSS. **Git-инцидент:** два первых коммита (`e9a6bb2`, `b98afb5`) сделаны голым `git commit` и унесли застейдженные файлы оркестратора. Содержимое цело, потеряна атрибуция; историю не переписывали. Норма ратифицирована владельцем как D39.88 — коммит только pathspec-формой с явным списком путей.