# План: линтеры, тесты и качество кодовой базы > Статус: **draft — Q0–Q5 и Q6a–Q6f.5 готовы к ревью; Q7 — следующий этап**. > > Источник: `docs/backlog.md`, аудит конфигурации `web/`, e2e-тестов и > инфраструктурных скриптов. Q0–Q5 и Q6a–Q6f.5 выполнены; Q7–Q8 выполняются > отдельными атомарными шагами. > > Цель: сделать качество кодовой базы воспроизводимым, убрать ложную «зелёность» > проверок, стабилизировать тестовые контракты и только после этого расширять > автоматические правила. ## 1. Границы и принципы 1. Сначала stabilизировать существующие проверки и тестовые контракты, а не добавлять новые сложные lint-правила. 2. Старый UI удалён: production работает на корневых маршрутах, а `/v1/**` и v1-specific код больше не являются частью приложения. 3. Не добавлять новые v1-фичи, не чинить v1-баги и не расширять v1-тесты. 4. `core/` и `theme/` — общие модули. Их изменения проверяются unit-тестами и smoke-сценариями production-потребителей. 5. Изменения делаются небольшими атомарными частями, в соответствии с `AGENTS.md`. 6. Каждый новый lint-rule сначала появляется в report-only/warn-only режиме, получает fixtures и тесты, и только после очистки baseline становится blocking. 7. Детали реализации правил остаются в `web/eslint-plugins/README.md`; `AGENTS.md` содержит краткие обязательные политики, понятные агенту без дополнительных поисков, и ссылки на подробные документы. ### Политика удаления legacy v1 - Q3 — отдельная атомарная cleanup-задача; поддержка v1 не возвращается. - Старые маршруты и UI-файлы удаляются без redirects и без совместимого maintenance-режима. - В рабочей кодовой базе не оставлять UI-v1-пути, исключения или документацию, которые выглядят как действующий источник совместимости. - Исторические упоминания допустимы только в `docs/archive/`. - Будущий API-контракт `POST /v1/process` из `docs/plan-platform.md` не связан с удалённым UI-v1 и остаётся в планах. ## 2. Текущее состояние quality-контура - `pnpm verify` объединяет typecheck, unit, rule-тесты, lint, форматирование, docs и production build; deploy выполняет его перед сборкой. - `i18n/dict-consistency` даёт 0 warnings на текущем baseline; после Q7 вычищены 4 неиспользуемых design token, token audit тоже чист. - E2E-контракт использует semantic locators и общий source-aware schema resolver; активных known-issue `test.fixme` нет. - `ToolError` хранится как key/vars и переводится только при выводе; смена локали обновляет уже видимый alert. - E2E гоняется в четырёх Playwright-проектах: полный Chromium, Firefox и WebKit для browser-critical спецификаций и mobile Chromium 390×844. Job остаётся non-blocking, число воркеров ограничено из-за памяти. ## 3. Этапы выполнения ### Q0. Зафиксировать baseline и синхронизировать документацию **Статус:** готов к ревью. **Оценка:** S. **Зависимости:** нет. - Проверить фактические команды и зафиксировать их в quality-gate документации. - Вынести расширенный workflow агента в `docs/agent-workflow.md`, оставив в `AGENTS.md` только обязательные инварианты и ссылки. - Обновить `docs/backlog.md`: - отметить dictionary linter как реализованный, но не доведённый до zero-warning; - убрать противоречие по кнопке Generate; - синхронизировать числа инструментов и тестовых сценариев. - Восстановить или исправить ссылку на раздел G в `docs/checklist-manual-testing.md`. - Убрать устаревшее утверждение про hardcoded debt в `kit` из `AGENTS.md`, если оно больше не соответствует коду. - Перенести явно устаревшие `docs/analysis.md` и `docs/sorting-backend.md` в `docs/archive/`, добавить даты архивирования и удаления через 30 дней. - Создать `docs/README.md` как индекс активных документов и карту источников истины; подробные правила не дублировать в корневых файлах. **Готово, когда:** один документ описывает текущие команды, актуальный baseline и все известные `test.fixme` имеют ссылки на зарегистрированный баг. ### Q1. Единый quality gate и CI **Статус:** готов к ревью. **Проверено:** `pnpm verify` проходит; E2E оставлен отдельным non-blocking job. **Оценка:** S–M. **Зависимости:** Q0. Добавить read-only `verify`-команду, которая запускает: ```text pnpm --dir web check pnpm --dir web test pnpm --dir web test:rules pnpm --dir web lint:all pnpm --dir web exec prettier --check . pnpm check:docs pnpm --dir web build ``` E2E оставить отдельным тяжёлым гейтом: ```text pnpm --dir web test:e2e ``` - Не дублировать вручную аргументы ESLint/Stylelint в нескольких скриптах; оркестратор должен вызывать package scripts или единый общий модуль. - Описать фактические команды и semantics ошибок/warnings в `docs/quality-gates.md`; здесь нужна точная процедура, а не длинный дубль в `AGENTS.md`. - На первом этапе явно разделить `errors` и `warnings`. - После очистки i18n baseline добавить строгий режим `--max-warnings=0`. - Добавить отдельный workflow для pull request: typecheck, unit, rule-тесты, lint, format и build. - E2E сначала запускать отдельным job; сделать его blocking после закрытия известных ошибок и стабилизации baseline. Решение отложено: условия matrix пересматриваются после Q6f.3, см. раздел 7 «Отложенное решение». **Готово, когда:** одна команда воспроизводит все быстрые проверки, а deploy не может пройти при type/unit/lint/build-ошибке. ### Q2. Политика i18n и zero-warning baseline **Статус:** готов к ревью. **Проверено:** i18n lint даёт 0 warnings, rule-тесты и полный `pnpm verify` проходят. **Оценка:** M. **Зависимости:** Q0, Q1. - `BASE_LOCALE` — единственный источник registry-English fallback для `tools.*.title` и `tools.*.description`; остальные локали требуют физические ключи. - Временно неиспользуемые `tools.*.params` исключены pattern-based policy до отдельной очистки мёртвого словаря; пустые значения и placeholders не подавляются. - Исправлены три реальных RU option-label gaps и удалены неиспользуемые option-группы из `ru.ts`. - Исправлен tie-break placeholder parity: при равенстве голосов выбирается `BASE_LOCALE`. - `lint-all` запускает ESLint с `--max-warnings=0`; unused-token сообщение оставалось неблокирующим и закрыто в Q7. - Plural-формы остаются отдельной задачей после Q2 и не смешиваются с этим этапом. ### Q3. Удалить UI-v1 (cleanup завершён) **Статус:** выполнен. **Проверено:** `pnpm verify` и production E2E smoke проходят; после проверки обновлены конфиги и активные документы. **Оценка:** M. **Зависимости:** Q0, Q1, Q2. - Удалены `web/src/routes/v1/**`, `web/src/lib/v1/**`, `app_v1.css`, v1-specific i18n/tests и fixtures. - Удалены isolation plugin, его тест и все v1-исключения из lint-конфигураций. - Убраны v1-ссылки из production-кода и активных документов; исторические упоминания остаются в `docs/archive/`. - Проверены production build, verify и отдельный E2E smoke production-маршрутов. **Результат:** `/v1/**`, UI-v1-файлы и legacy-конфигурация отсутствуют; следующий этап — Q5. ### Q4. Тестовый контракт e2e **Статус:** выполнен. **Проверено:** `pnpm verify` и полный Playwright smoke проходят; 105 тестов passed, 4 known-issue теста skipped. **Оценка:** M. **Зависимости:** Q0–Q3. - Добавлен `docs/testing-strategy.md` с locator, fixture, i18n и `test.fixme` policy. - В production markup добавлены только разрешённые hooks для source/result/ verdict/empty-state; текстовые элементы получили accessible labels, фильтры — `aria-pressed`, а каталог — семантические heading/status landmarks. - E2E переведён на `role`, label и stable `href`; удалены `TOTAL`/`GROUPS`, exact fuzzy counts, internal CSS selectors, `alt`-locators и полные verdict strings. Точные verdict-переводы перенесены в unit-тесты. - Helpers централизованы в `web/e2e/helpers/page.ts`; исправлены stale theme/source/locale checks. Theme fixme снят. - Q5-фиксы resize/crop, generator, Reset и error-сценария не смешивались с этим этапом. **Результат:** изменение формулировки, класса или порядка элементов не ломает поведенческий тест без изменения пользовательского контракта; следующий этап — Q6. ### Q5. Закрыть известные preview-баги **Статус:** выполнен. **Проверено:** unit, Q5 E2E smoke и полный `pnpm verify` проходят; активных known-issue `test.fixme` нет. **Оценка:** M–L. **Зависимости:** Q4. - Добавлен общий opt-in `defaultFromSource` resolver в `registry-schema`: он принимает source dimensions, обновляет только source-aware поля при upload и используется в UI, executor и worker. `resize-png` и `crop-png` получают размеры текущего source; static/generator/text schemas не затронуты. - Reset сохраняет source/text, восстанавливает source-aware defaults и запускает deferred auto-run через debounce. - Генераторы проверены в auto-run без кнопки Generate; кнопка не добавляется. - `ToolError` хранится как key/vars, переводится derived-значением при выводе и обновляется при смене локали. E2E использует независимый invalid pixel-count сценарий. - Resize/crop, generator, Reset и localization fixme сняты; Q6a расширяет покрытие, не смешиваясь с исправлением этих контрактов. **Результат:** Q5 known issues закрыты и отражены в `docs/backlog.md` и `docs/checklist-manual-testing.md`; Q6a выполнен, следующий этап — Q6b. ### Q6. Расширение unit и browser-покрытия **Оценка:** M–L. **Зависимости:** Q4, Q5. #### Q6a. Executor protocol, fallback и debounce **Статус:** выполнен. Unit-тесты покрывают debounce, worker image/string/ verdict/files protocol, `ToolError` transport, constructor/postMessage/onerror/ malformed fallback, source-aware sanitization и DOM metadata. Browser smoke проверяет rapid auto-run и no-worker fallback; deferred transport fallback покрыт unit-тестами. - `execute(tool, ctx)` сохраняет публичный API; `createExecutor` и protocol handler являются внутренними test seams. - `ToolError` не запускает direct retry повторно; transport/protocol failures отключают worker и переходят в direct fallback. - DOM-зависимые registry entries помечены `domOnly`, поэтому зелёный smoke не скрывает нормальный worker fallback. - `debounce` покрыт fake timers; E2E подтверждает последние значения при быстрых последовательных изменениях генератора. #### Q6b. FileResult → ZIP → download **Статус:** выполнен. Q6a закрыл worker-транспорт; Q6b добавил проверяемый ZIP seam, unit round-trip для `FileResult`, защиту пустого результата и error/busy контракт download. Отдельный browser-сценарий скачивает архив и проверяет имена частей и PNG-сигнатуры. - `buildZipEntries` не зависит от DOM; `downloadZip` остаётся UI-обёрткой. - `SchemaToolView` переиспользует `downloadBlob`, ловит ошибки кодирования и блокирует повторный download на время сборки. - E2E использует `downloadResultFile` и `unzipSync`, не добавляя новые `data-testid`; files-инструменты не проходят через image-smoke. #### Q6c. Output MIME/quality **Статус:** выполнен. Q6c покрывает текущий фиксированный output-контракт JPG/WebP/BMP, не превращаясь в UX-райз кнопки Download. Registry проверяет согласованность MIME/extension/quality-поля, unit — границы quality, browser — реальные сигнатуры файлов и разницу размера при изменении quality. - `ToolEntry.output` остаётся декларативным описанием формата; новый селектор форматов не добавляется. - `validateOutputQuality` отделяет проверку 0..1 от DOM-рендеринга; JPEG/WebP используют quality `1..100` из schema, BMP не имеет quality-параметра. - E2E проверяет JPEG `FF D8 FF`, WebP `RIFF/WEBP`, BMP `BM` и PNG-фолбэк. - Ошибки кодирования и unsupported MIME используют существующие i18n-ключи. #### Q6d. Malformed и special PNG fixtures **Статус:** выполнен. Runtime-fixtures покрывают CRC, truncated, palette и 16-bit без бинарных файлов в git. E2E фиксирует браузерный контракт: critical CRC/truncated отклоняются без падения, ancillary CRC и хвост после `IEND` допускаются, palette/16-bit декодируются. Unit фиксирует контракт анализаторов для RGBA-данных, которые браузер отдаёт после нормализации. - `web/e2e/helpers/fixtures.ts` собирает indexed, 16-bit, CRC-mutation, truncation и tail-варианты из уже существующих `crc32`/`chunk`. - Отклонение битых файлов — свойство декодера движка, а не единый продуктовый контракт: Q6f.3 зафиксировал, что Firefox и WebKit декодируют `idat-cut` и `truncated-no-iend`, а WebKit ещё и `crc-bad-idat`. Это ожидаемое поведение, которое закреплено per-engine в `web/e2e/png-fixtures.spec.ts`. - Обязательное требование при любом исходе: файл не роняет страницу, ошибка показывается alert-ом, а не «пустым» результатом. - Поведение `createImageBitmap` является частью browser-контракта, а не самостоятельного PNG-парсера product-кода. - Сырое сообщение браузера при decode-ошибке не локализуется в Q6d; это отдельная задача в backlog. #### Q6e. Registry-wide smoke **Статус:** выполнен. Smoke расширен до всех 122 production tools из registry: image→image, image→text, text→image, generator input→image и verdict. FileResult и malformed/special PNG не дублируются — они уже покрыты Q6b/Q6d. - `tools-smoke.spec.ts` расширен image/text-output кейсами, включая все незакрытые color/geometry инструменты. - `generators.spec.ts` использует единый data-harness для всех генераторов; `single-color-png` сохраняет отдельную проверку auto-run/размеров. - Text-input сценарии используют общий `renderText` helper, включая `data-uri-to-png`. - Component-тесты не добавлены: Vitest работает в node без DOM/Testing Library, а текущий e2e-контракт уже покрывает поведение preview. Выбор test seam или DOM-окружения перенесён в Q6f. #### Q6f.1. Layout resolver seam **Статус:** выполнен. Чистая логика группировки полей вынесена из `SchemaFields.svelte` в `resolveLayoutGroups` и покрыта unit-тестами на дедупликацию, неизвестные поля, пустые группы, `cols` и default-хвост. - Компонент использует общий resolver, поэтому unit-тесты блокируют изменения layout-логики без DOM/Testing Library. - `SchemaPreview` view-model и DOM-компоненты не менялись; их seam остаётся отдельным решением Q6f.2. - Новые зависимости, vitest-конфиг и browser matrix не добавлялись. #### Q6f.2. SchemaPreview model seam **Статус:** выполнен. Вычисления `sourceValue`, `resultValue`, `formatValue` и `hasResult` вынесены в `buildSchemaPreviewModel`; Svelte-компонент использует модель, а unit-тесты проверяют все input/result-ветки без DOM. - Translator передаётся в модель явно, поэтому unit-тесты не зависят от locale state и проверяют только view-model. - image/text/verdict/files и пустые результаты покрыты в `pnpm verify`. - Новые зависимости, vitest-конфиг и browser matrix не добавлялись. #### Q6f.3. Browser matrix и mobile viewport **Статус:** выполнен. Playwright запускает четыре проекта: полный Chromium, Firefox и WebKit для browser-critical спецификаций и mobile Chromium 390×844 с `hasTouch` для навигации, каталога, генераторов и pipeline. - `web/playwright.config.ts`: `browserContractTests` — `pipeline.spec.ts`, `png-fixtures.spec.ts`, `text-and-verdicts.spec.ts`; `mobileTests` — `navigation.spec.ts`, `catalog.spec.ts`, `generators.spec.ts`, `pipeline.spec.ts`. - `workers` — 1 локально и 2 в CI. Browser matrix ограничена памятью: при 12 воркерах на машине с 16 GB RAM браузеры не успевают поднять worker, и тесты падают без ошибок (проверено и в Chromium). Локально matrix поэтому медленная, но почти не нагружает компьютер. Per-project лимита воркеров в Playwright нет. - Per-project `expect.timeout` не задаётся: Firefox и WebKit отдают результат так же быстро, а расхождения объясняются памятью, а не скоростью движка. - Декодеры движков расходятся, и это зафиксировано как ожидаемое поведение: `png-fixtures.spec.ts` хранит список движков, отклоняющих каждый битый файл, и не пропускает проверки. В любом браузере файл не должен ронять страницу, а отказ декодера показывается alert-ом. - CI устанавливает `chromium firefox webkit`; job остаётся non-blocking. - Локальный прогон: 276 passed, 0 skipped, 0 failed. Visual snapshots и coverage thresholds не добавлялись — это Q6f.4. #### Q6f.4. Coverage thresholds **Статус:** выполнен для чистой логики. Vitest считает покрытие только по `src/lib`, с явным списком исключений, и `pnpm verify` падает при падении ниже порога. - `web/vitest.config.ts`: provider `v8`, `reporter: text-summary`, пороги statements 90, branches 82, functions 72, lines 91. - Пороги поставлены на 1.5–2.5 п.п. ниже измеренного baseline (91.77 / 83.3 / 74.31 / 92.53), поэтому gate ловит регрессию, а не текущий долг. - Scope — логика, которую node-Vitest вообще может исполнить. Исключены браузерные модули `core/domText.ts` и `core/io.ts` (canvas, `createImageBitmap`), data-модули `i18n/en.ts`, `i18n/ru.ts`, `tool-icons.ts`, rune-файлы `*.svelte.ts`, entry worker-а и type-only `components/define.ts`. Причина каждого исключения зафиксирована в конфиге и в `docs/testing-strategy.md`. - `registry/*` (47–81% statements, 25–47% functions) в scope остаётся: это обычная непокрытая логика, а не ограничение окружения. Покрывается e2e-smoke Q6e, но unit-порог её не скрывает. - `verify` теперь выполняет `test:coverage` вместо `test`: набор тестов тот же, двойного прогона нет. - Порог functions самый слабый (74.31% против 91.77%): добавление инструмента с непокрытым `run` быстрее всего опускает именно его. Это ожидаемо, порог держит запас. - DOM-тесты `SchemaToolView`/controls не начаты: браузерные модули исключены из coverage сознательно, а их перенос в scope — отдельное решение вместе с visual snapshots, см. Q6f.5. Не применять один одинаковый smoke-сценарий к image-, text-, generator- и file-result-инструментам. #### Q6f.5. Visual snapshots — решение отложено **Статус:** сознательно не делаем. Это решение, а не долг. - Механика: `toHaveScreenshot` сравнивает отрендеренную страницу с PNG-baseline, закоммиченным в `web/e2e/-snapshots/`. Эталон — картинка из прошлого коммита, а не замысел дизайнера; обновление идёт через `--update-snapshots` и ревью картинок. - Почему отложено: - визуальный язык сейчас активно меняется, поэтому каждый дизайн-PR превращается в перегенерацию и ревью картинок, а соблазн обновить снапшот не глядя появляется постоянно; - baseline привязан к платформе (ОС, шрифты, особенности рендеринга): снимок с Windows не совпадёт на `ubuntu-latest`, значит эталоны должны рождаться на Linux, а локальные прогоны будут постоянно расходиться; - при матрице из четырёх проектов плюс тема и viewport число файлов Baseline растёт нелинейно. - Что закрывает потребность сейчас: e2e-поведение (276 тестов), `lint:tokens` и token audit, i18n-гейт, ручной чеклист `docs/checklist-manual-testing.md`. Не закрыто только «элемент визуально наехал» — это ловится чеклистом и ревью. - Условие возврата: визуальный язык стабилен, изменения редкие, ручной чеклист перестал справляться. Тогда первым идёт `/kit` в двух темах, а эталоны генерируются на Linux (docker-образ Playwright или отдельный CI-job). ### Q7. Укрепление lint-инфраструктуры **Оценка:** M. **Зависимости:** Q1, Q2, Q3. #### Q7a. Очистка неиспользуемых design tokens **Статус:** выполнен. Token audit чист: `All app.css tokens are used somewhere`. - Удалены `--color-text-accent-muted` (light и dark, иначе ломается parity), `--space-text-s: 0`, `--size-content-max: 880px` и `--duration-m: 0.3s`. - Проверено, что мёртвые токены — это пыль, а не признак хардкода: дублирующих значений (`max-width`, `0.3s`, muted-цвет) в `src` нет. Потребители `space-text-m/xl/2xl`, `size-content-max-wide` и `size-workspace-min` используются, поэтому шкалы не тронуты. - Шкалы остались неполными намеренно (`duration-s/l` без `m`, `space-text` без `s`): мёртвый элемент шкалы хуже, чем «дырявая» шкала, а вернуть его можно вместе с потребителем. - Второй пункт («после очистки решить, должен ли unused audit быть fail-режимом») остаётся открытым: сам audit пока только сообщает. #### Q7b. Тесты token audit и PostCSS HCT-плагина **Статус:** выполнен. У обоих не было ни одного теста, хотя HCT-плагин (`web/scripts/postcss-hct.mjs`, 430 строк) пересчитывает всю палитру. - `web/scripts/__tests__/postcss-hct.test.mjs` — 17 тестов: literal и производный `hct()`, seed из `:root` и из hex, цепочка hct-токенов, refs `h/c/t`, `calc/min/max/clamp`, обёртка hue, клампы tone/chroma, несколько вызовов в одном объявлении, а также warn-and-keep для неизвестного seed, циклической ссылки и битого тела `hct()`. - `web/scripts/__tests__/token-audit.test.mjs` — 15 тестов на parity (light-only, dark-only, derived, не-цвет), authorship (hct, color-mix, seed-токены, хардкод с локацией, игнор не-цветов и обычных свойств), unused и предикаты `isColorValue`/`isDerived`. - Ожидания не зашивают вывод плагина, а сравнивают его с эквивалентной литеральной записью: `hct(from var(--seed) h c calc(t + 10))` должен совпасть с `hct(120 40 60)`, если seed — `hct(120 40 50)`. Иначе тест просто зафиксировал бы текущее поведение. - Единственный seam: `checkUnused(root, used = collectUsedTokens())` — зависимость от чтения `src` стала инъецируемой, production-вызов не изменился. - `vitest.config.ts` подхватывает `scripts/**/*.test.mjs`, поэтому скрипты покрываются обычным прогоном и `verify`; `test:rules` не трогали. - Проверка мутацией выявила дыру: первая версия тестов не ловила кламп tone в ветке `from` (смена `clamp(t, 0, 100)` на `0, 90` проходила зелёной). Добавлен отдельный тест на производный кламп; после этого мутация падает. #### Q7c–Q7d. Оставшиеся направления - Покрыть базовым JS recommended сам код ESLint-плагинов и lint-скриптов. - Отдельно решить, должен ли обычный CSS проходить проверку undefined tokens и hardcoded colors наравне со Svelte styles. - Новые правила подключать только после fixtures, `test:rules` и baseline. - Решить, должен ли unused audit стать fail-режимом (см. Q7a). ### Q8. Новые автоматические правила **Оценка:** L. **Зависимости:** Q4, Q7. #### Дублирование HTML/Svelte-разметки Сначала провести report-only аудит новых `components/**` и `routes/**`. Определить: - минимальный размер клона; - нормализацию Svelte blocks и динамических классов; - список допустимых fixture/allowlist-случаев. Только после оценки false positives выбирать между готовым detector и собственным AST-правилом. Не включать detector в blocking gate сразу. #### Пользовательские тексты вне i18n Сделать warn-only пилот с исключениями для: - бренда и версии; - форматов и имён файлов; - `X`/`Y` и технических значений; - registry source strings; - `/kit`; - тестовых данных и внутренних сообщений. Blanket-запрет всех string literals не использовать. #### Согласованность комментариев Автоматический lint не делать. Это review/process policy: комментарий должен объяснять причину исключения, а `TODO`/`FIXME` — ссылаться на backlog или issue и иметь критерий закрытия. ## 4. Что описать в `AGENTS.md` В `AGENTS.md` добавить только следующие неочевидные правила: 1. Какой набор команд считается полным quality gate и что означает успешный результат при warnings. 2. Production-scope и политику удаления v1: новые изменения относятся только к production UI; исторические UI-v1-упоминания остаются в архиве. 3. Политику e2e: semantic locators, допустимый `data-testid`, запрет бессмысленных counts/classes/alt/exact prose. 4. Политику i18n: новые пользовательские тексты через i18n; registry source, brand, formats и `/kit` — явные исключения. 5. Жизненный цикл `test.fixme`: только для зарегистрированного бага, с ссылкой; исправление и удаление теста происходят вместе. 6. Комментарии и `TODO`/`FIXME` должны объяснять «почему» и иметь ссылку на работу. 7. Shared-модули `core`/`theme` требуют проверки production-потребителей. Синтаксические списки ESLint-правил и подробные токены остаются в `web/eslint-plugins/README.md`. ### Политика документации для агентов `AGENTS.md` не должен превращаться в полный справочник проекта. В нём оставляем только обязательный рабочий контракт: команды, production-scope, ключевые инварианты, исключения и ссылки на подробные документы. Если агенту нужно понять причину или workflow, в `AGENTS.md` должна быть короткая понятная формулировка; длинные примеры, точные процедуры, таблицы и критерии проверки переносятся в `docs/` или в README рядом с соответствующей подсистемой. Принцип выбора места документа: 1. **Поведение агента, которое нельзя пропустить** — кратко в `AGENTS.md`. 2. **Точная команда, скрипт, параметр или критерий качества** — в `docs/quality-gates.md` или в README соответствующей подсистемы. 3. **Архитектурное решение, поток данных и границы модулей** — в `docs/architecture-overview.md` или существующем плане. 4. **Реализация конкретного lint-правила и его fixtures** — только в `web/eslint-plugins/README.md` и исходниках правила. 5. **Продуктовая задача, backlog и ручная приёмка** — в `docs/backlog.md`, активном плане или `docs/checklist-manual-testing.md`. Подробные документы не дублируются в нескольких местах: у документа должен быть один источник истины, а остальные файлы ссылаются на него. Все подробные материалы хранятся в `docs/`, чтобы не засорять корень репозитория. Активный документ должен иметь статус (`draft`, `active`, `completed` или `archived`) и понятную область применения. Архивные планы не используются как текущие критерии приёмки без отдельной сверки с кодом. Новый документ создаётся только когда темы нет в существующем файле; иначе обновляется существующий. ### Архивирование и срок хранения Завершённые, отменённые или явно устаревшие документы переносятся в `docs/archive/`, а не остаются в корне `docs/` как рабочие планы. При переносе добавляются дата архивирования, дата удаления и причина. Архивный документ — временная справка для быстрого просмотра прошлых решений, но не источник текущих требований. Срок хранения после архивирования — 30 дней. После этой даты документ можно удалить после проверки ссылок; если он снова нужен, его восстанавливают из git history или пересоздают как новый активный документ. Подробная процедура и шаблон метаданных находятся в `docs/archive/README.md`. ### Какие документы подготовить | Документ | Назначение | Когда создавать/обновлять | | ------------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | | `docs/README.md` | Индекс активных документов, карта источников истины и краткий порядок чтения для агента | Q0; при добавлении нового активного документа | | `docs/agent-workflow.md` | Подробный workflow: исследование, изменение, проверка и обновление документации | Q0–Q1; только для процессов, не описанных кратко в `AGENTS.md` | | `docs/quality-gates.md` | Команды проверки, semantics errors/warnings, порядок запуска и требования CI | Q1; при изменении scripts/config/CI | | `docs/testing-strategy.md` | Слои тестов, locator policy, i18n assertions, fixtures, `test.fixme` и правила e2e | Q4; при изменении тестовой архитектуры | | `docs/architecture-overview.md` | Карта `core`, registry, executor, production UI и shared-модулей | Q5 или перед крупным рефакторингом; не дублировать существующие планы | | `web/eslint-plugins/README.md` | Детали custom lint rules, token policy и fixtures | Уже существует; менять только вместе с правилами | | `docs/archive/README.md` | Политика временного архива, даты и удаления через 30 дней | Q0; обновлять при изменении архивной политики | Не создавать отдельные `docs/commands.md`, `docs/known-issues.md` или копию backlog: команды принадлежат `quality-gates`, известные проблемы — backlog и checklist. `README.md` в корне остаётся кратким обзором продукта, а не вторым руководством агента. ## 5. Порядок PR 1. **Q0** — синхронизация backlog/checklist/AGENTS. 2. **Q1** — `verify` и PR quality gate. 3. **Q2** — i18n parity и strict warning gate. 4. **Q3** — удалить v1 отдельной атомарной задачей. 5. **Q4** — тестовый контракт и stale-тесты для production UI. 6. **Q5** — preview-баги и снятие `fixme`. 7. **Q6** — расширенное покрытие. 8. **Q7** — тесты lint-инфраструктуры и unused tokens. 9. **Q8** — report-only duplicate/text audits; blocking — только после baseline. Каждый PR должен быть атомарным и небольшим; не объединять `SchemaPreview` refactor, новые i18n-правила и исправление продуктовых багов в одну работу. ## 6. Отдельные направления и что не начинать автоматически - полноценный browser/visual/coverage matrix; - полноценная PWA/API/WASM-платформа; - `SchemaPreview` state/view-model refactor; - plural-формы до модели словаря; - универсальный линтер одинаковых комментариев; - batch/архивы, region-инструменты и остальные продуктовые волны из backlog. ## 7. Решения, которые нужно принять на ревью - Какие executor/worker/fallback сценарии войдут в первый Q6 coverage PR? - Нужны ли browser/mobile matrix и visual snapshots до coverage thresholds? Решено: matrix — Q6f.3, coverage — Q6f.4, visual snapshots отложены с обоснованием в Q6f.5. - Какие unused design tokens можно удалить, а какие должны остаться? Решено в Q7a: удалены все четыре, audit чист. - Какие browser/e2e проверки должны быть обязательными для PR, а какие — отдельным ночным прогоном? ### Отложенное решение: условия matrix и blocking e2e Окончательное решение про `e2e` в CI принимается **не сейчас**, а после пересмотра условий тестов. К этому моменту нужно вернуться и решить одним шагом: 1. Пересмотреть условия matrix: какие спецификации обязаны идти в `browserContractTests` и `mobileTests`, нужен ли `png-fixtures` вне Chromium и что делать с engine-специфичными расхождениями декодеров. 2. Накопить стабильные прогоны `e2e` на раннере (сейчас ориентир — 276 тестов, ~3 мин на job) без падений по таймингу. 3. Только после этого решать, делать ли `e2e` blocking, и если да — снимать `continue-on-error`, иначе общий conclusion прогона останется зелёным при красном `e2e` (проверено на run `36225579363`). До этого решения `e2e` остаётся non-blocking baseline, а его фактический статус проверяется по отдельному job, а не по conclusion прогона.