textmachine/frontend/docs/BACKLOG.md

20 KiB
Raw Blame History

Бэклог фронта (зонный)

Ведёт зона frontend/ (решение владельца 02.08, D39.84: фронт и платформа держат СВОИ бэклоги; единый бэклог docs/PROGRESS.md — трекер движка/полигона/доков, фронт-строк не принимает). Нормы те же: ID стабилен навсегда, каждая петля получает диспозицию. Запросы к ДВИЖКУ сюда не пишутся — они заходят строками единого бэклога через оркестратора (пример: строки 99102 — пофазный прогресс · манифест глав · машиночитаемая таблица подписи · трасса). Засеян оркестратором при лендинге D39.84 — дальше правят фронт-сессии.

ID Хвост Вес Источник
Ф-1 Этапы S2S7 (FRONTEND_SESSION_PROMPT.md): S2 оболочка + слой src/ui/ · S3 слой данных/MSW/фикстуры всех состояний · S4 библиотека/загрузка/разбор/прогресс · S5 банк памяти и подпись (самый тяжёлый) · S6 читалка двух колонок и замечания · S7 метаданные/экспорт/настройки/сквозной прогон по очереди сессий (S0/S1 — текущая) FRONTEND_SESSION_PROMPT
Ф-2 React Compiler — включить ОТДЕЛЬНЫМ шагом после заморозки интерфейса, с CI-проверкой, что вставки реально попали в бандл (в MVP выключен; совместимость держит линт eslint-plugin-react-hooks) после заморозки UI STACK_DECISIONS §1
Ф-3 Пересмотр TS 6 → TS 7 после выхода TS 7.1 с программным API (~октябрь 2026); решение обратимо — тайпчек не участвует в сборке триггер: релиз TS 7.1 STACK_DECISIONS §1
Ф-4 Токен-гейт на отступывключён 04.08 в S2. Значение теперь обязано быть последовательностью var(--…); 0, auto и inherit легальны (это не размеры), calc() намеренно запрещён: составное выражение из литералов проверить нечем, а если размер понадобился новый — его место в tokens.css. Свойства взяты регулярками, поэтому длинные формы (border-top-left-radius, padding-inline) закрыты тоже. Обкатка потребовала трёх токенов, которых не хватало: --radius-round, --radius-bar, --row-inset. Проверено шестью живыми нарушениями и тремя легальными формами (перечень — FRONTEND_PLAN.md §5.4) закрыт STACK_DECISIONS §3
Ф-8 Вопрос владельцу: закрывать ли /* eslint-disable */ в TSXзакрыт 02.08 без решения владельца. Вопрос был поставлен как выбор из двух плохих вариантов, а у него есть отраслевой третий: @eslint-community/eslint-plugin-eslint-comments 4.7.2 (no-unlimited-disable + require-description + disable-enable-pair). Голое отключение падает тремя ошибками, точечное подавление с причиной проходит. Урок: не выносить владельцу технический вопрос, не поискав отраслевую практику закрыт адверсариальное ревью S1
Ф-9 Хвосты гейтов после адверсариального ревью S1все четыре закрыты 04.08 в S2, каждая живым нарушением: именованные и системные цвета в TSX ловятся разрешающим списком на цветных атрибутах (fill/stroke/color/stopColor/floodColor/lightingColor — только none, currentColor, inherit, transparent, var(--…)) · значение CSS-переменной в style={{…}} обязано быть числом или токеном · носитель style внутри объектного литерала запрещён, и тем самым закрыт JSX-спред · системные цветовые ключевые слова закрыты declaration-property-value-disallowed-list по ВСЕМ свойствам, то есть и в сокращённых записях outline/box-shadow/border. Перечень проверенных форм (15 TSX + 14 CSS) — FRONTEND_PLAN.md §5.4.1. Граница названа честно и там же: гейт цветных атрибутов ловит литерал и шаблон, но не вычисленное значение (const c = 'red'; <svg fill={c}/>) — синтаксически это не берётся, и объявлять строку «закрытой без остатка» было бы ровно тем переобещанием, из-за которого она и заведена закрыт для литеральных форм, граница названа адверсариальное ревью S1
Ф-10 Хвосты структурных тестовзакрыты 04.08 в S2. cssModules.test.ts строит пары по ИМПОРТАМ, а не по совпадению имён файлов, видит деструктуризацию const { name } = styles и выкидывает url(...) и строковые литералы до поиска классов; проверка сиротства стала «модуль, который никто не импортирует». tokens.test.ts получил замок полноты: каждый токен обязан попасть либо в замеры, либо в явный список подобранных, и обратно — список не может ссылаться на исчезнувший токен. Плюс новое: числа для виртуализатора живут в src/tokens/measures.ts и сверяются с одноимёнными токенами. Семь живых нарушений, перечень — FRONTEND_PLAN.md §5.4 закрыт адверсариальное ревью S1
Ф-11 Контраст ниже порога WCAG на 5 узлах (приглушённый текст #707479 и #8a8e91 на #17191a). Это прямое следствие копирования палитры Fleet, которую владелец принял; axe отчитывается о них отдельно и команду не валит. Решение владельца: оставить как есть ради верности референсу или поднять приглушённые тона решение владельца ревью S1 №2
Ф-12 Масштаб списковглавное закрыто 04.08 в S2 замером, а не обещанием. Заведён маршрут /scale: та же оболочка на настоящем масштабе — 2284 раздела и банк на 1200 терминов (src/mock/scale.ts через getScaleFixture()). Дерево и банк виртуализованы по умолчанию (RAC Virtualizer + ListLayout), и это измерено в живом браузере: на 2293 узлах дерева в DOM держится 31 строка, scrollHeight 59 618px, кадр при прокрутке — медиана 22 мс, p95 31 мс; банк на 1200 терминах — 30 строк в DOM, медиана 16,7 мс. Прокрутка до конца и обратно счётчик строк не растит. Замечания намеренно НЕ виртуализованы: их список пер-главный, а юнитов на главу ≈1,9 (ПТ-21) и флаг у юнита один — 10³ там не бывает по построению; сводный вид по книге, если он появится, придёт с S6 и получит тот же примитив ui/List. Два хвоста строки НЕ сделаны и получают диспозицию, а не зачёркивание (нашло адверсариальное ревью S2): дерево осталось плоским без группировки по сотням — это работа экрана глав (S4), у оболочки для неё нет ни данных, ни требования; банк без разделения «подписано / ждёт решения» — это работа подписного экрана (S5), справочному виду в 320px разделение только мешает масштаб закрыт; группировка — S4, разделение банка — S5 ревью S1 №2
Ф-14 Вход фронта в контракт API (единый бэклог, строка 95) — собрать одним списком то, что фронт уже знает и обязан подать автору контракта: язык кодами (не именами) · состояние прогона отдельно от выполнения главы · прогресс пофазно по ЮНИТАМ · ревизия/Last-Event-ID для докачки SSE · трёхзначный статус подписи термина (`auto draft approved), а не булев signed · поля банка, без которых подпись не работает (type/sense/since_ch) · **ключ строки банка** (ревью оркестратора 04.08): у движка термин уникален по (src, sense, since_ch, until_ch), то есть один и тот же src законно приходит несколькими строками, а список на экране требует различимого ключа — сейчас он собран из (src, dst, type) и это заплата, а не решение. **Своим решением фронт это не заводит** (BACKLOG.md` шапка: запросы к движку идут строками единого бэклога через оркестратора) — строка держит список готовым к дизайн-промту 95
Ф-15 src/api/ синхронен, состояний загрузки и ошибки в типах нет — диспозиция: НЕ чинить сейчас. Два независимых скептика показали, что S2 данных не читает вовсе, а асинхронность приезжает на S3 вместе с MSW и TanStack Query, до первого продуктового экрана; самодельный хук сегодня = работа S3, которую S3 выбросит. Строка держит обязательство S3: ветки ожидания/ошибки/пустоты появляются вместе с MSW, а не после первого экрана S3 ревью S1 №3
Ф-13 eslint-plugin-jsx-a11y не встаёт на ESLint 10 (peer ^3..^9, проверено --dry-run → ERESOLVE). Статическую половину доступности держит axe в скриншот-цикле; плагин подключить, когда выйдет совместимый мажор триггер: мажор плагина ревью S1 №2
Ф-7 Хвосты сведения витрины с референсом (FRONTEND_PLAN.md §5.2). Часть S2 закрыта 04.08: + стоит в ряду вкладок ЛЕВОЙ панели и запускает «Добавить книгу» (решение владельца 04.08); дублирующая строка «+ Добавить книгу» из дерева убрана — одна вещь, одно место. В центре и справа + нет и не будет: наборы вкладок там фиксированные, пользователь их не расширяет, и кнопка вышла бы декоративной — ровно то, что эта строка запрещает. Состояния наведения перестали быть «непроверенными»: они сняты не статичным кадром, а прогоном браузера (data-hovered/data-selected/data-focus-visible у кнопки, вкладки, строки дерева и списка). Осталось на S6: приглушённая колонка оригинала — решение, а не замер, пересмотреть на настоящей читалке S2 · S6 S1, сверка с fleet.png
Ф-16 Выгрузка книги (§3.11) ушла из верхней полосы. В витрине S1 там висела иконка загрузки без действия; по правилу Ф-7 («кнопка только вместе с действием») она снята до S7, который строит экспорт. Вернуть иконку вместе с самим действием S7 S2
Ф-17 Закрытие вкладки с клавиатуры не объявлено скринридеру. Крестик у вкладки-документа сознательно НЕ кнопка: у роли tab дети презентационные (ARIA), вложенный фокусируемый элемент выпал бы из дерева доступности, оставшись в порядке табуляции (это же ловит axe правилом nested-interactive). Мышь закрывает крестиком, клавиатура — Delete на выбранной вкладке. Объявить сочетание нечем: aria-keyshortcuts не входит в типизированный API Tab/TabList в react-aria-components 1.20.0. Пересмотреть при обновлении библиотеки либо при появлении палитры команд триггер: мажор RAC или экран горячих клавиш S2
Ф-19 Переключение вкладки размонтирует её содержимое, и прокрутка длинного списка теряется. Нашло адверсариальное ревью S2 и подтвердило независимым прогоном: react-aria-components не рендерит невыбранную TabPanel (dist/private/Tabs.mjs:276if (!isSelected && !shouldForceMount) return null), поэтому на /scale прокрутка дерева scrollTop=9000 → вкладка «Поиск» → обратно даёт scrollTop=0, а пересборка коллекции из 2284 узлов стоит 70140 мс (два независимых замера). Ф-12 это не переоткрывает: там речь про поведение списка при 10³, и оно держится. Чинить сейчас не стали: shouldForceMount не лечит (панель остаётся видимым div и легла бы поверх соседней в той же ячейке грида), а настоящее лекарство — keep-alive либо запоминание офсета на вкладку — это решение об устройстве оболочки, и принимать его вслепую, до настоящих экранов, значит строить механизм не под задачу S4 (экран глав) либо раньше, если владелец увидит потерю места адверсариальное ревью S2
Ф-20 Мёртвый шрифт и мёртвые токены в поставке. @fontsource-variable/jetbrains-mono грузится в бандл, но --font-mono не выбран ни одним правилом: художественную прозу моноширинным не набирают, а во Fleet на этом месте был код. Туда же несколько токенов, которые ни разу не используются (--color-current-line, --color-hint). Замок полноты токенов (S2) требует классифицировать токен, но не требует его использовать. Решать на S6, когда читалка станет настоящей: либо моноширинный находит работу, либо импорт и токены уходят S6 адверсариальное ревью S2
Ф-21 Ступень замечания различается только цветом полоски (attention синяя, glance серая). Зрячему разница читается ещё и светлотой, но скринридеру приоритет не достаётся вовсе. Лечение — слово, как это уже сделано для состояния прогона книги; какое именно слово, решает владелец: словарь замечаний продуктовый (ПТ-33), а §3.8 требует, чтобы экран не выглядел тревожным, так что «важно/неважно» не годится решение владельца адверсариальное ревью S2
Ф-18 Бандл 545 КБ (168 КБ gzip) одним куском — замер 04.08 после установки react-aria-components; Rolldown предупреждает про порог 500 КБ. Для десктопного SPA за логином это не авария (индексации нет, сеть локальная), но одним куском едут и экраны, которых пользователь ещё не открыл. Лечение — разбиение по маршрутам, и делать его осмысленно после S4S7, когда экраны появятся: сейчас резать нечего после S7 либо при жалобе на первый экран S2, замер
Ф-5 Tauri 2.x вторым шагом — по явным триггерам (трей · глобальные горячие клавиши · распространяемый .exe · офлайн · хранилище учёток ОС); до того — установимое PWA, только браузерные API триггеры названы STACK_DECISIONS §6, D39.84
Ф-6 @tanstack/react-virtual — в резерве, подключать только по замеру (дефолт виртуализации — RAC Virtualizer). Замер сделан 04.08 (S2) и триггер НЕ наступил: RAC Virtualizer держит 2284 раздела на 31 строке в DOM при медиане кадра 22 мс и 1200 терминов на 30 строках при 16,7 мс. Вторая библиотека виртуализации сейчас была бы платой без выигрыша; пересматривать — если появится список с переменной высотой строки (читалка S6) и estimatedRowSize не справится по замеру; замер 04.08 отрицательный STACK_DECISIONS §2