From a50cbd8f915604ff8a26e3964f2788d95f2e397f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 23 Sep 2026 11:50:58 +0300 Subject: [PATCH] =?UTF-8?q?docs(reviews):=20=D0=B8=D0=BD=D0=B4=D0=B5=D0=BA?= =?UTF-8?q?=D1=81=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E,=20=D1=83=D1=80=D0=BE?= =?UTF-8?q?=D0=BA=D0=B8,=20=D0=BF=D0=B5=D1=80=D0=B5=D1=81=D0=B1=D0=BE?= =?UTF-8?q?=D1=80=D0=BA=D0=B0=20=D0=B8=D0=BD=D0=B4=D0=B5=D0=BA=D1=81=D0=B0?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BD=D0=B2=D0=B5=D0=B9=D0=B5=D1=80=D0=BE=D0=BC?= =?UTF-8?q?=20(#635)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scripts/reviews-index.mjs собирает docs/reviews/INDEX.md: одна строка на документ — issue, этап, раунд, вердикт (явная строка, раздел «Вердикт», свободная форма хвоста; 936 из 986 распознаны), High/Medium по строке вердикта или заголовкам находок, до шести заголовков находок. Индекс детерминирован, не индексирует сам себя, перечисляет файлы вне схемы имён; `--check` — гейт свежести. process.yml публикует INDEX.md тем же коммитом, что документ ревью. docs/LESSONS.md — датированные уроки со ссылками на источники (12 записей из аудитов и разборов недели). PROCESS.md §2.10 — где искать решения. Тесты: разбор имён, вердиктов, счётчиков, находок; фикстурный каталог; живой каталог (100 % покрытие, >90 % вердиктов); контракт шага конвейера. Мутанты reviews-index-skips-self-check, reviews-index-verdict-substring. Issue: #635 User-Visible: no --- .github/workflows/process.yml | 6 + PROCESS.md | 16 + docs/LESSONS.md | 22 + docs/reviews/INDEX.md | 996 ++++++++++++++++++++++++++++++++++ scripts/mutation-registry.mjs | 23 + scripts/reviews-index.mjs | 159 ++++++ test/reviews-index.test.mjs | 99 ++++ 7 files changed, 1321 insertions(+) create mode 100644 docs/LESSONS.md create mode 100644 docs/reviews/INDEX.md create mode 100644 scripts/reviews-index.mjs create mode 100644 test/reviews-index.test.mjs diff --git a/.github/workflows/process.yml b/.github/workflows/process.yml index 3eb81d2a..9cce9bc5 100644 --- a/.github/workflows/process.yml +++ b/.github/workflows/process.yml @@ -1400,6 +1400,12 @@ jobs: # забрал бы всё, что там окажется, а после reset там не должно быть # ничего постороннего — но полагаться на «не должно» здесь нельзя. git add -- "$doc" 2>/dev/null || true + # #635: индекс ревью пересобирается тем же коммитом, что и документ — + # иначе он устаревает на первом же раунде. Генерируемый файл, класс C. + if [ -f "$doc" ]; then + node scripts/reviews-index.mjs --dir=docs/reviews + git add -- docs/reviews/INDEX.md + fi if git diff --cached --quiet; then # Пустая рабочая копия — ещё не провал: ревьюер иногда коммитит # документ сам, своим app-токеном мимо этого шага (CODE-REVIEW-150-r1, diff --git a/PROCESS.md b/PROCESS.md index 74e92623..634ad47d 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -329,6 +329,22 @@ S1-new → S2-analysis → S3-spec → S4-spec-review ⟲ → S5-ready → проверки, со ссылкой на документ того раунда и его материал. Без перечня сокращение превращается в молчаливое доверие. +**Индекс документов ревью** — `docs/reviews/INDEX.md` (#635): одна строка на +документ — issue, этап, раунд, вердикт, число High/Medium, заголовки находок. +Файл генерируется `node scripts/reviews-index.mjs` и пересобирается конвейером +тем же коммитом, что публикует документ; руками не правится. Прежде чем брать +задачу по подсистеме, стоит прочитать её строки в индексе: что находили и чем +закрывали — там, а не в тысяче файлов. Уроки, пережившие свою задачу, +собираются в `docs/LESSONS.md` с датой и ссылкой на источник. + +**Хранение документов** (решение владельца 23.09, #635): в `docs/reviews/` +лежат все раунды всех задач текущей линии — предыдущие раунды нужны ссылкам +«Унаследовано из r» и якорям материала. При стабильном релизе документы +задач, вошедших в него (`RELEASE-MEMBERSHIP.json` линии), переносятся в +`legacy/reviews//` одним коммитом класса C; индекс пересобирается и +перечисляет только живые. Перенос — часть чеклиста стабильного релиза, не +отдельная задача. + Дешёвые гейты (`typecheck`, `test`, `build` со сверкой копий бандла) гоняются в каждом раунде: код изменился, а стоят они минуты. Тяжёлые — по дельте (§10.2). diff --git a/docs/LESSONS.md b/docs/LESSONS.md new file mode 100644 index 00000000..0e7764bd --- /dev/null +++ b/docs/LESSONS.md @@ -0,0 +1,22 @@ +# Уроки + +Датированные выводы, пережившие свою задачу (#635). Каждый — одной мыслью, со +ссылкой на источник: документ ревью, issue, аудит. Полный след решений — в +`docs/reviews/INDEX.md` и комментариях issue; здесь только то, что меняет +поведение на следующей задаче. Конспекты ролей (#634) и промпт ревьюера +ссылаются сюда. + +| Дата | Урок | Источник | +|---|---|---| +| 2026-08-23 | Одно число, видимое пользователю дважды (превью/факт, подпись/запись), обязано иметь один источник. | аудит 23.08, #233/#234, тест `single-source-numbers` | +| 2026-09-06 | Число смоков/тестов не живёт в тексте: считается командой (`npm run inventory`). | #472, PROCESS.md | +| 2026-09-09 | Мутанты по диффу — на кандидате, не на каждом push: 48 из 56 часов job-минут за два дня ушли в отменённые прогоны. | #510 | +| 2026-09-13 | Ненулевой exit гарда ≠ «мутант пойман»: setup-failure, invalid-mutation и survived различаются; засчитывается только assertion-killed. | #558, #568 | +| 2026-09-18 | Ревью на не принятых эталонах — потерянный цикл: `golden:verify` ревьюер гоняет лично, эталоны принимаются до `S7`. | CODE-REVIEW-598-r1 H1 | +| 2026-09-20 | Стенд ≠ Home Assistant: нативный `` и `ha-dialog` — разные ветки; свидетель для реального HA — фикстура #505, а не golden стенда. | аудит 22.09 A/H1, #607, #609 | +| 2026-09-20 | Контролируемое поле без `live()` показывает не то число, что в состоянии; клампить на коммите (`change`), не на каждом символе. | аудит 22.09 A/H2, #608 | +| 2026-09-21 | Лог без итоговой строки — обрыв, а не «ok»: отчёт судит по маркеру завершения и исходу шага, не по отсутствию `FAIL`. | #604 | +| 2026-09-21 | На `main` после fast-forward `merge-base(dev, HEAD)` = HEAD: пустой дифф пропускает тяжёлые job, а гейт, судящий только самый свежий прогон, объявляет релиз красным. | аудит 22.09 D, #619 | +| 2026-09-22 | Обвязка процесса тоже рождает P1: дефекты #595/#596 нашлись в бою, а не тестами обвязки — тесты по тексту YAML и регэкспам исходников не заменяют фикстурного прогона. | аудит 22.09 | +| 2026-09-23 | Смена события коммита в общем контроле (`input` → `change`) краснит смоки, которые набор контролов не называют: связь символ → смок должна быть явной в `smoke-links`, иначе краснота доживает до ночи. | #639 | +| 2026-09-23 | Ожидание другого workflow внутри job — занятый раннер и потолок бюджета стадии; ждать должно событие (`workflow_run`) с маркером ожидания, а будить без маркера нельзя — это второй вызов модели. | #636 | diff --git a/docs/reviews/INDEX.md b/docs/reviews/INDEX.md new file mode 100644 index 00000000..e141cc87 --- /dev/null +++ b/docs/reviews/INDEX.md @@ -0,0 +1,996 @@ +# Индекс ревью + +Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 990, issue: 343. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. + +| Issue | Документ | Этап · раунд | Вердикт | H | M | Находки | +|---|---|---|---|---:|---:|---| +| #639 | [CODE-REVIEW-639-r1.md](CODE-REVIEW-639-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #639 | [CODE-REVIEW-639-r2.md](CODE-REVIEW-639-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #637 | [CODE-REVIEW-637-r1.md](CODE-REVIEW-637-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #637 | [CODE-REVIEW-637-r2.md](CODE-REVIEW-637-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #636 | [CODE-REVIEW-636-r1.md](CODE-REVIEW-636-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #619 | [CODE-REVIEW-619-r1.md](CODE-REVIEW-619-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #613 | [SPEC-REVIEW-613-r1.md](SPEC-REVIEW-613-r1.md) | spec · r1 | 🟡 жёлтый | 1 | 1 | High. Принятое предположение о scroll-инвалидации не работает в основном заявленном сце…; Medium (в скоупе), сопутствует H1. AC1/AC2 не требуют, чтобы smoke-контейнер пересекал … | +| #613 | [SPEC-REVIEW-613-r2.md](SPEC-REVIEW-613-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #613 | [CODE-REVIEW-613-r1.md](CODE-REVIEW-613-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #612 | [CODE-REVIEW-612-r1.md](CODE-REVIEW-612-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #611 | [SPEC-REVIEW-611-r1.md](SPEC-REVIEW-611-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #611 | [SPEC-REVIEW-611-r2.md](SPEC-REVIEW-611-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #611 | [CODE-REVIEW-611-r1.md](CODE-REVIEW-611-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #610 | [SPEC-REVIEW-610-r1.md](SPEC-REVIEW-610-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #610 | [CODE-REVIEW-610-r1.md](CODE-REVIEW-610-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #610 | [CODE-REVIEW-610-r2.md](CODE-REVIEW-610-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #609 | [SPEC-REVIEW-609-r1.md](SPEC-REVIEW-609-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #609 | [CODE-REVIEW-609-r1.md](CODE-REVIEW-609-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #609 | [CODE-REVIEW-609-r2.md](CODE-REVIEW-609-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #608 | [SPEC-REVIEW-608-r1.md](SPEC-REVIEW-608-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | «Шесть потребителей» на самом деле семь вызовов rangeLine; Ответ владельца на Q1–Q3 не оставлен отдельным комментарием | +| #608 | [CODE-REVIEW-608-r1.md](CODE-REVIEW-608-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | Русский changelog: сломанное предложение (Medium, в скоупе — правится в этой же задаче) | +| #608 | [CODE-REVIEW-608-r2.md](CODE-REVIEW-608-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #607 | [SPEC-REVIEW-607-r1.md](SPEC-REVIEW-607-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #607 | [CODE-REVIEW-607-r1.md](CODE-REVIEW-607-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #607 | [CODE-REVIEW-607-r2.md](CODE-REVIEW-607-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #606 | [SPEC-REVIEW-606-r1.md](SPEC-REVIEW-606-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #606 | [CODE-REVIEW-606-r1.md](CODE-REVIEW-606-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #605 | [SPEC-REVIEW-605-r1.md](SPEC-REVIEW-605-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #605 | [CODE-REVIEW-605-r1.md](CODE-REVIEW-605-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #604 | [CODE-REVIEW-604-r1.md](CODE-REVIEW-604-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #603 | [SPEC-REVIEW-603-r1.md](SPEC-REVIEW-603-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #603 | [CODE-REVIEW-603-r1.md](CODE-REVIEW-603-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #602 | [SPEC-REVIEW-602-r1.md](SPEC-REVIEW-602-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #602 | [SPEC-REVIEW-602-r2.md](SPEC-REVIEW-602-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #602 | [CODE-REVIEW-602-r1.md](CODE-REVIEW-602-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #602 | [CODE-REVIEW-602-r2.md](CODE-REVIEW-602-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #601 | [CODE-REVIEW-601-r1.md](CODE-REVIEW-601-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #600 | [SPEC-REVIEW-600-r1.md](SPEC-REVIEW-600-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | регресс-гарантия для существующих кнопок действий не названа отдельным AC | +| #600 | [CODE-REVIEW-600-r1.md](CODE-REVIEW-600-r1.md) | code · r1 | 🟡 жёлтый | 0 | 4 | Room settings: подпись «Temperature» в 5-сегменте переносится посреди слова; Device dialog: у Glow radius появилась не согласованная владельцем блокировка Save; Дефект №5 (поповер «?» обрезается границей диалога комнаты) не имеет свидетеля закрытия; ACCEPTANCE.md: заявлено 11 эталонных сцен, фактически принято 13; docs/USER-GUIDE.ru.md:529 — испорченная строка таблицы | +| #600 | [CODE-REVIEW-600-r2.md](CODE-REVIEW-600-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | .hpf-seg label: min-width: fit-content (колонка не сжимается ниже слова) + overflow-wra…; glowRadiusValid/проблема marker-glow-radius удалены целиком; marker-dialog.ts больше не…; Свидетель добавлен: новый смок доказывает исполнением, что подсказка живёт вне скроллер…; Абзац переписан: явно «13 сцен» = 11 из ТЗ + 2 room-temperature-dialog-, с объяснением … | +| #598 | [SPEC-REVIEW-598-r1.md](SPEC-REVIEW-598-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #598 | [SPEC-REVIEW-598-r2.md](SPEC-REVIEW-598-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #598 | [CODE-REVIEW-598-r1.md](CODE-REVIEW-598-r1.md) | code · r1 | 🔴 красный | 1 | 3 | AC8 (эталоны) не выполнен: golden:verify красный на этом SHA; AC2/AC3 нарушены: изменён один из «неприкосновенных» .srcrow-смоков; Сегментированный переключатель обещан в семи местах ТЗ, реализован в одном; AC5 доказан мутантом только наполовину; AC1 не имеет обещанного автотеста для диалога «Пространство» (закрыто чтением) | +| #598 | [CODE-REVIEW-598-r2.md](CODE-REVIEW-598-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #597 | [SPEC-REVIEW-597-r1.md](SPEC-REVIEW-597-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #597 | [SPEC-REVIEW-597-r2.md](SPEC-REVIEW-597-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #597 | [CODE-REVIEW-597-r1.md](CODE-REVIEW-597-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #597 | [CODE-REVIEW-597-r2.md](CODE-REVIEW-597-r2.md) | code · r2 | 🟢 зелёный | 0 | 1 | — | +| #596 | [CODE-REVIEW-596-r1.md](CODE-REVIEW-596-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #595 | [CODE-REVIEW-595-r1.md](CODE-REVIEW-595-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #594 | [SPEC-REVIEW-594-r2.md](SPEC-REVIEW-594-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | — | +| #594 | [SPEC-REVIEW-594-r3.md](SPEC-REVIEW-594-r3.md) | spec · r3 | 🟢 зелёный | 0 | 0 | — | +| #594 | [CODE-REVIEW-594-r1.md](CODE-REVIEW-594-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #594 | [CODE-REVIEW-594-r2.md](CODE-REVIEW-594-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #594 | [CODE-REVIEW-594-r3.md](CODE-REVIEW-594-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #593 | [SPEC-REVIEW-593-r1.md](SPEC-REVIEW-593-r1.md) | spec · r1 | 🔴 красный | 1 | 0 | — | +| #593 | [SPEC-REVIEW-593-r2.md](SPEC-REVIEW-593-r2.md) | spec · r2 | 🟢 зелёный | 1 | 0 | — | +| #593 | [SPEC-REVIEW-593-r3.md](SPEC-REVIEW-593-r3.md) | spec · r3 | 🔴 красный | 0 | 0 | — | +| #593 | [CODE-REVIEW-593-r1.md](CODE-REVIEW-593-r1.md) | code · r1 | 🟡 жёлтый | 0 | 2 | — | +| #593 | [CODE-REVIEW-593-r2.md](CODE-REVIEW-593-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #592 | [SPEC-REVIEW-592-r1.md](SPEC-REVIEW-592-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #592 | [SPEC-REVIEW-592-r2.md](SPEC-REVIEW-592-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #592 | [CODE-REVIEW-592-r1.md](CODE-REVIEW-592-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #592 | [CODE-REVIEW-592-r2.md](CODE-REVIEW-592-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #588 | [SPEC-REVIEW-588-r1.md](SPEC-REVIEW-588-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #588 | [SPEC-REVIEW-588-r2.md](SPEC-REVIEW-588-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #588 | [SPEC-REVIEW-588-r3.md](SPEC-REVIEW-588-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 0 | — | +| #588 | [CODE-REVIEW-588-r1.md](CODE-REVIEW-588-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #587 | [CODE-REVIEW-587-r1.md](CODE-REVIEW-587-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #586 | [CODE-REVIEW-586-r1.md](CODE-REVIEW-586-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #585 | [SPEC-REVIEW-585-r1.md](SPEC-REVIEW-585-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #585 | [CODE-REVIEW-585-r1.md](CODE-REVIEW-585-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #585 | [CODE-REVIEW-585-r2.md](CODE-REVIEW-585-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #584 | [SPEC-REVIEW-584-r1.md](SPEC-REVIEW-584-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #584 | [SPEC-REVIEW-584-r2.md](SPEC-REVIEW-584-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #584 | [CODE-REVIEW-584-r1.md](CODE-REVIEW-584-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | рост ленивого чанка furniture-plan-art.generated не назван числом, как того требует соб… | +| #584 | [CODE-REVIEW-584-r2.md](CODE-REVIEW-584-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #583 | [SPEC-REVIEW-583-r1.md](SPEC-REVIEW-583-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #583 | [CODE-REVIEW-583-r1.md](CODE-REVIEW-583-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #583 | [CODE-REVIEW-583-r2.md](CODE-REVIEW-583-r2.md) | code · r2 | 🟡 жёлтый | 0 | 1 | — | +| #582 | [SPEC-REVIEW-582-r1.md](SPEC-REVIEW-582-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #582 | [CODE-REVIEW-582-r1.md](CODE-REVIEW-582-r1.md) | code · r1 | 🔴 красный | 0 | 0 | — | +| #582 | [CODE-REVIEW-582-r2.md](CODE-REVIEW-582-r2.md) | code · r2 | 🔴 красный | 0 | 0 | — | +| #582 | [CODE-REVIEW-582-r3.md](CODE-REVIEW-582-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #581 | [SPEC-REVIEW-581-r1.md](SPEC-REVIEW-581-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #581 | [CODE-REVIEW-581-r1.md](CODE-REVIEW-581-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #580 | [SPEC-REVIEW-580-r1.md](SPEC-REVIEW-580-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #580 | [SPEC-REVIEW-580-r2.md](SPEC-REVIEW-580-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #580 | [CODE-REVIEW-580-r1.md](CODE-REVIEW-580-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #580 | [CODE-REVIEW-580-r2.md](CODE-REVIEW-580-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #579 | [SPEC-REVIEW-579-r1.md](SPEC-REVIEW-579-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #579 | [CODE-REVIEW-579-r1.md](CODE-REVIEW-579-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #579 | [CODE-REVIEW-579-r2.md](CODE-REVIEW-579-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #578 | [SPEC-REVIEW-578-r1.md](SPEC-REVIEW-578-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #578 | [CODE-REVIEW-578-r1.md](CODE-REVIEW-578-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #577 | [SPEC-REVIEW-577-r1.md](SPEC-REVIEW-577-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #577 | [SPEC-REVIEW-577-r2.md](SPEC-REVIEW-577-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #577 | [CODE-REVIEW-577-r1.md](CODE-REVIEW-577-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #577 | [CODE-REVIEW-577-r2.md](CODE-REVIEW-577-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #576 | [CODE-REVIEW-576-r1.md](CODE-REVIEW-576-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #573 | [CODE-REVIEW-573-r1.md](CODE-REVIEW-573-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #573 | [CODE-REVIEW-573-r2.md](CODE-REVIEW-573-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #571 | [CODE-REVIEW-571-r1.md](CODE-REVIEW-571-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #570 | [SPEC-REVIEW-570-r1.md](SPEC-REVIEW-570-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #570 | [CODE-REVIEW-570-r1.md](CODE-REVIEW-570-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #569 | [CODE-REVIEW-569-r1.md](CODE-REVIEW-569-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #568 | [CODE-REVIEW-568-r1.md](CODE-REVIEW-568-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #567 | [CODE-REVIEW-567-r1.md](CODE-REVIEW-567-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #566 | [CODE-REVIEW-566-r1.md](CODE-REVIEW-566-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #566 | [CODE-REVIEW-566-r2.md](CODE-REVIEW-566-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #565 | [SPEC-REVIEW-565-r1.md](SPEC-REVIEW-565-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #565 | [CODE-REVIEW-565-r1.md](CODE-REVIEW-565-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #565 | [CODE-REVIEW-565-r2.md](CODE-REVIEW-565-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #564 | [SPEC-REVIEW-564-r1.md](SPEC-REVIEW-564-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | DoR: «затронутые файлы и модули» не названы (Medium, в скоупе); «Сценарий» и «Что человек увидит» объединены без явной персоны на | +| #564 | [SPEC-REVIEW-564-r2.md](SPEC-REVIEW-564-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #564 | [CODE-REVIEW-564-r1.md](CODE-REVIEW-564-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #564 | [CODE-REVIEW-564-r2.md](CODE-REVIEW-564-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #563 | [SPEC-REVIEW-563-r1.md](SPEC-REVIEW-563-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #563 | [CODE-REVIEW-563-r1.md](CODE-REVIEW-563-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #562 | [CODE-REVIEW-562-r1.md](CODE-REVIEW-562-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #562 | [CODE-REVIEW-562-r2.md](CODE-REVIEW-562-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #562 | [CODE-REVIEW-562-r3.md](CODE-REVIEW-562-r3.md) | code · r3 | 🟡 жёлтый | 0 | 0 | — | +| #561 | [SPEC-REVIEW-561-r1.md](SPEC-REVIEW-561-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #561 | [SPEC-REVIEW-561-r2.md](SPEC-REVIEW-561-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #561 | [CODE-REVIEW-561-r1.md](CODE-REVIEW-561-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #560 | [CODE-REVIEW-560-r1.md](CODE-REVIEW-560-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #558 | [CODE-REVIEW-558-r1.md](CODE-REVIEW-558-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #557 | [CODE-REVIEW-557-r1.md](CODE-REVIEW-557-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #556 | [CODE-REVIEW-556-r1.md](CODE-REVIEW-556-r1.md) | code · r1 | 🔴 красный | 1 | 0 | — | +| #556 | [CODE-REVIEW-556-r2.md](CODE-REVIEW-556-r2.md) | code · r2 | 🔴 красный | 0 | 0 | — | +| #555 | [CODE-REVIEW-555-r1.md](CODE-REVIEW-555-r1.md) | code · r1 | 🔴 красный | 0 | 0 | — | +| #555 | [CODE-REVIEW-555-r2.md](CODE-REVIEW-555-r2.md) | code · r2 | 🔴 красный | 0 | 0 | — | +| #554 | [SPEC-REVIEW-554-r1.md](SPEC-REVIEW-554-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #554 | [CODE-REVIEW-554-r1.md](CODE-REVIEW-554-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #553 | [CODE-REVIEW-553-r2.md](CODE-REVIEW-553-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #553 | [CODE-REVIEW-553-r3.md](CODE-REVIEW-553-r3.md) | code · r3 | 🟡 жёлтый | 0 | 0 | — | +| #551 | [CODE-REVIEW-551-r1.md](CODE-REVIEW-551-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #551 | [CODE-REVIEW-551-r4.md](CODE-REVIEW-551-r4.md) | code · r4 | 🟢 зелёный | 0 | 0 | — | +| #550 | [CODE-REVIEW-550-r1.md](CODE-REVIEW-550-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #549 | [CODE-REVIEW-549-r1.md](CODE-REVIEW-549-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #549 | [CODE-REVIEW-549-r2.md](CODE-REVIEW-549-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #548 | [CODE-REVIEW-548-r1.md](CODE-REVIEW-548-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #547 | [CODE-REVIEW-547-r1.md](CODE-REVIEW-547-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #546 | [CODE-REVIEW-546-r1.md](CODE-REVIEW-546-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #545 | [SPEC-REVIEW-545-r1.md](SPEC-REVIEW-545-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #545 | [CODE-REVIEW-545-r1.md](CODE-REVIEW-545-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #544 | [SPEC-REVIEW-544-r1.md](SPEC-REVIEW-544-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #544 | [CODE-REVIEW-544-r1.md](CODE-REVIEW-544-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #543 | [SPEC-REVIEW-543-r1.md](SPEC-REVIEW-543-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #543 | [SPEC-REVIEW-543-r2.md](SPEC-REVIEW-543-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #543 | [CODE-REVIEW-543-r1.md](CODE-REVIEW-543-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #542 | [CODE-REVIEW-542-r1.md](CODE-REVIEW-542-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #541 | [CODE-REVIEW-541-r1.md](CODE-REVIEW-541-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #536 | [SPEC-REVIEW-536-r1.md](SPEC-REVIEW-536-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #536 | [CODE-REVIEW-536-r1.md](CODE-REVIEW-536-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #535 | [SPEC-REVIEW-535-r1.md](SPEC-REVIEW-535-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #535 | [CODE-REVIEW-535-r1.md](CODE-REVIEW-535-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #534 | [SPEC-REVIEW-534-r1.md](SPEC-REVIEW-534-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #534 | [SPEC-REVIEW-534-r2.md](SPEC-REVIEW-534-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #534 | [SPEC-REVIEW-534-r3.md](SPEC-REVIEW-534-r3.md) | spec · r3 | 🟢 зелёный | 0 | 0 | — | +| #534 | [CODE-REVIEW-534-r1.md](CODE-REVIEW-534-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #533 | [CODE-REVIEW-533-r1.md](CODE-REVIEW-533-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #533 | [CODE-REVIEW-533-r2.md](CODE-REVIEW-533-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #533 | [CODE-REVIEW-533-r3.md](CODE-REVIEW-533-r3.md) | code · r3 | 🟡 жёлтый | 0 | 0 | — | +| #532 | [SPEC-REVIEW-532-r1.md](SPEC-REVIEW-532-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #532 | [SPEC-REVIEW-532-r2.md](SPEC-REVIEW-532-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #532 | [CODE-REVIEW-532-r1.md](CODE-REVIEW-532-r1.md) | code · r1 | 🟡 жёлтый | 0 | 2 | — | +| #532 | [CODE-REVIEW-532-r2.md](CODE-REVIEW-532-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #531 | [SPEC-REVIEW-531-r1.md](SPEC-REVIEW-531-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #531 | [SPEC-REVIEW-531-r2.md](SPEC-REVIEW-531-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #531 | [CODE-REVIEW-531-r1.md](CODE-REVIEW-531-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #531 | [CODE-REVIEW-531-r2.md](CODE-REVIEW-531-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #530 | [SPEC-REVIEW-530-r1.md](SPEC-REVIEW-530-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #530 | [SPEC-REVIEW-530-r2.md](SPEC-REVIEW-530-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #530 | [CODE-REVIEW-530-r1.md](CODE-REVIEW-530-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #529 | [SPEC-REVIEW-529-r1.md](SPEC-REVIEW-529-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #529 | [SPEC-REVIEW-529-r2.md](SPEC-REVIEW-529-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #529 | [CODE-REVIEW-529-r1.md](CODE-REVIEW-529-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #528 | [CODE-REVIEW-528-r1.md](CODE-REVIEW-528-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #526 | [SPEC-REVIEW-526-r1.md](SPEC-REVIEW-526-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #526 | [SPEC-REVIEW-526-r2.md](SPEC-REVIEW-526-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | — | +| #526 | [CODE-REVIEW-526-r1.md](CODE-REVIEW-526-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #525 | [SPEC-REVIEW-525-r1.md](SPEC-REVIEW-525-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #525 | [CODE-REVIEW-525-r1.md](CODE-REVIEW-525-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #524 | [SPEC-REVIEW-524-r1.md](SPEC-REVIEW-524-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #524 | [CODE-REVIEW-524-r1.md](CODE-REVIEW-524-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #522 | [SPEC-REVIEW-522-r1.md](SPEC-REVIEW-522-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #521 | [SPEC-REVIEW-521-r1.md](SPEC-REVIEW-521-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #521 | [SPEC-REVIEW-521-r2.md](SPEC-REVIEW-521-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | — | +| #521 | [SPEC-REVIEW-521-r3.md](SPEC-REVIEW-521-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 0 | — | +| #521 | [CODE-REVIEW-521-r1.md](CODE-REVIEW-521-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #521 | [CODE-REVIEW-521-r2.md](CODE-REVIEW-521-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #520 | [SPEC-REVIEW-520-r1.md](SPEC-REVIEW-520-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 3 | — | +| #520 | [SPEC-REVIEW-520-r2.md](SPEC-REVIEW-520-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | — | +| #520 | [SPEC-REVIEW-520-r3.md](SPEC-REVIEW-520-r3.md) | spec · r3 | 🟢 зелёный | 0 | 0 | — | +| #520 | [CODE-REVIEW-520-r1.md](CODE-REVIEW-520-r1.md) | code · r1 | 🔴 красный | 1 | 0 | — | +| #520 | [CODE-REVIEW-520-r2.md](CODE-REVIEW-520-r2.md) | code · r2 | 🟡 жёлтый | 0 | 1 | — | +| #520 | [CODE-REVIEW-520-r3.md](CODE-REVIEW-520-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #517 | [SPEC-REVIEW-517-r1.md](SPEC-REVIEW-517-r1.md) | spec · r1 | 🟢 зелёный | 0 | 2 | — | +| #517 | [SPEC-REVIEW-517-r2.md](SPEC-REVIEW-517-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 2 | — | +| #517 | [CODE-REVIEW-517-r1.md](CODE-REVIEW-517-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #516 | [CODE-REVIEW-516-r1.md](CODE-REVIEW-516-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #516 | [CODE-REVIEW-516-r2.md](CODE-REVIEW-516-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #515 | [CODE-REVIEW-515-r1.md](CODE-REVIEW-515-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #514 | [SPEC-REVIEW-514-r1.md](SPEC-REVIEW-514-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #514 | [CODE-REVIEW-514-r1.md](CODE-REVIEW-514-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #514 | [CODE-REVIEW-514-r2.md](CODE-REVIEW-514-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #514 | [CODE-REVIEW-514-r3.md](CODE-REVIEW-514-r3.md) | code · r3 | 🟡 жёлтый | 0 | 1 | — | +| #514 | [CODE-REVIEW-514-r4.md](CODE-REVIEW-514-r4.md) | code · r4 | 🟢 зелёный | 0 | 0 | — | +| #513 | [CODE-REVIEW-513-r1.md](CODE-REVIEW-513-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #513 | [CODE-REVIEW-513-r2.md](CODE-REVIEW-513-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #512 | [SPEC-REVIEW-512-r1.md](SPEC-REVIEW-512-r1.md) | spec · r1 | 🔴 красный | 1 | 0 | ТЗ не учитывает, что правка demo/docs/capture.mjs ломает captureScriptSha256 и красит d… | +| #512 | [SPEC-REVIEW-512-r2.md](SPEC-REVIEW-512-r2.md) | spec · r2 | 🔴 красный | 0 | 0 | — | +| #512 | [CODE-REVIEW-512-r1.md](CODE-REVIEW-512-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #511 | [SPEC-REVIEW-511-r1.md](SPEC-REVIEW-511-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #511 | [SPEC-REVIEW-511-r2.md](SPEC-REVIEW-511-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #511 | [CODE-REVIEW-511-r1.md](CODE-REVIEW-511-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #510 | [SPEC-REVIEW-510-r1.md](SPEC-REVIEW-510-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #510 | [SPEC-REVIEW-510-r2.md](SPEC-REVIEW-510-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #510 | [SPEC-REVIEW-510-r3.md](SPEC-REVIEW-510-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 0 | — | +| #510 | [SPEC-REVIEW-510-r4.md](SPEC-REVIEW-510-r4.md) | spec · r4 | 🟡 жёлтый | 0 | 0 | — | +| #510 | [CODE-REVIEW-510-r1.md](CODE-REVIEW-510-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #510 | [CODE-REVIEW-510-r2.md](CODE-REVIEW-510-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #510 | [CODE-REVIEW-510-r3.md](CODE-REVIEW-510-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #509 | [SPEC-REVIEW-509-r1.md](SPEC-REVIEW-509-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #509 | [SPEC-REVIEW-509-r2.md](SPEC-REVIEW-509-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #509 | [CODE-REVIEW-509-r1.md](CODE-REVIEW-509-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #508 | [SPEC-REVIEW-508-r1.md](SPEC-REVIEW-508-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #508 | [SPEC-REVIEW-508-r2.md](SPEC-REVIEW-508-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #508 | [CODE-REVIEW-508-r1.md](CODE-REVIEW-508-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #508 | [CODE-REVIEW-508-r2.md](CODE-REVIEW-508-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #508 | [CODE-REVIEW-508-r3.md](CODE-REVIEW-508-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #506 | [SPEC-REVIEW-506-r1.md](SPEC-REVIEW-506-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #506 | [CODE-REVIEW-506-r1.md](CODE-REVIEW-506-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #505 | [SPEC-REVIEW-505-r1.md](SPEC-REVIEW-505-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #505 | [SPEC-REVIEW-505-r2.md](SPEC-REVIEW-505-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #505 | [CODE-REVIEW-505-r1.md](CODE-REVIEW-505-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #505 | [CODE-REVIEW-505-r2.md](CODE-REVIEW-505-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #502 | [SPEC-REVIEW-502-r1.md](SPEC-REVIEW-502-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #502 | [CODE-REVIEW-502-r1.md](CODE-REVIEW-502-r1.md) | code · r1 | 🔴 красный | 0 | 0 | — | +| #500 | [SPEC-REVIEW-500-r1.md](SPEC-REVIEW-500-r1.md) | spec · r1 | 🟡 жёлтый | 2 | 0 | — | +| #500 | [SPEC-REVIEW-500-r2.md](SPEC-REVIEW-500-r2.md) | spec · r2 | 🟡 жёлтый | 1 | 0 | — | +| #500 | [SPEC-REVIEW-500-r3.md](SPEC-REVIEW-500-r3.md) | spec · r3 | 🟢 зелёный | 0 | 0 | — | +| #500 | [CODE-REVIEW-500-r1.md](CODE-REVIEW-500-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #500 | [CODE-REVIEW-500-r2.md](CODE-REVIEW-500-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #500 | [CODE-REVIEW-500-r3.md](CODE-REVIEW-500-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #498 | [SPEC-REVIEW-498-r1.md](SPEC-REVIEW-498-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #498 | [SPEC-REVIEW-498-r2.md](SPEC-REVIEW-498-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 2 | — | +| #498 | [CODE-REVIEW-498-r1.md](CODE-REVIEW-498-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #495 | [SPEC-REVIEW-495-r1.md](SPEC-REVIEW-495-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #495 | [CODE-REVIEW-495-r1.md](CODE-REVIEW-495-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #493 | [SPEC-REVIEW-493-r1.md](SPEC-REVIEW-493-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #493 | [CODE-REVIEW-493-r1.md](CODE-REVIEW-493-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #492 | [SPEC-REVIEW-492-r1.md](SPEC-REVIEW-492-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #492 | [SPEC-REVIEW-492-r2.md](SPEC-REVIEW-492-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #492 | [CODE-REVIEW-492-r1.md](CODE-REVIEW-492-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #491 | [SPEC-REVIEW-491-r1.md](SPEC-REVIEW-491-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #491 | [CODE-REVIEW-491-r1.md](CODE-REVIEW-491-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #491 | [CODE-REVIEW-491-r2.md](CODE-REVIEW-491-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #490 | [SPEC-REVIEW-490-r1.md](SPEC-REVIEW-490-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #490 | [CODE-REVIEW-490-r1.md](CODE-REVIEW-490-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | Recovery-адаптация обходит asset-readiness gate и continuity-overlay (в скоупе, Medium) | +| #490 | [CODE-REVIEW-490-r2.md](CODE-REVIEW-490-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #489 | [SPEC-REVIEW-489-r1.md](SPEC-REVIEW-489-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 3 | Условие существования зум-кнопок противоречит текущему рендеру и не-скоупу (Medium, в с…; Гейт свежести документации (check-docs) не назван обязательным при правке src/ (Medium,…; data-hp-mode="device" расходится с уже опубликованным именем mode-devices (Medium, в ск… | +| #489 | [SPEC-REVIEW-489-r2.md](SPEC-REVIEW-489-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #489 | [CODE-REVIEW-489-r1.md](CODE-REVIEW-489-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #488 | [CODE-REVIEW-488-r1.md](CODE-REVIEW-488-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #487 | [SPEC-REVIEW-487-r1.md](SPEC-REVIEW-487-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #487 | [CODE-REVIEW-487-r1.md](CODE-REVIEW-487-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #486 | [SPEC-REVIEW-486-r1.md](SPEC-REVIEW-486-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | Сверка утверждений о текущем коде с фактическим origin/dev; Проверка полномочий продуктовых решений; Проверка обязательных разделов ТЗ (PROCESS.md §7.1); Проверка непротиворечивости с продуктовым скоупом | +| #486 | [CODE-REVIEW-486-r1.md](CODE-REVIEW-486-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | 1 Дешёвые гейты — приняты по ссылке, не перегонялись; 2 Тяжёлые гейты — не приняты вслепую, прослежены по истории CI ветки; 3 Что прогнал/перепроверил сам; 4 Вывод smoke-select.mjs (для протокола); 5 Таблица гейтов; 1 High — нет | +| #485 | [SPEC-REVIEW-485-r1.md](SPEC-REVIEW-485-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #485 | [SPEC-REVIEW-485-r2.md](SPEC-REVIEW-485-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #485 | [SPEC-REVIEW-485-r3.md](SPEC-REVIEW-485-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 0 | — | +| #485 | [SPEC-REVIEW-485-r4.md](SPEC-REVIEW-485-r4.md) | spec · r4 | 🟢 зелёный | 0 | 0 | — | +| #485 | [CODE-REVIEW-485-r1.md](CODE-REVIEW-485-r1.md) | code · r1 | 🔴 красный | 0 | 0 | — | +| #485 | [CODE-REVIEW-485-r2.md](CODE-REVIEW-485-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #485 | [CODE-REVIEW-485-r3.md](CODE-REVIEW-485-r3.md) | code · r3 | 🟡 жёлтый | 0 | 0 | — | +| #485 | [CODE-REVIEW-485-r4.md](CODE-REVIEW-485-r4.md) | code · r4 | 🟢 зелёный | 0 | 0 | — | +| #484 | [SPEC-REVIEW-484-r1.md](SPEC-REVIEW-484-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #484 | [CODE-REVIEW-484-r1.md](CODE-REVIEW-484-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #483 | [CODE-REVIEW-483-r1.md](CODE-REVIEW-483-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #482 | [SPEC-REVIEW-482-r1.md](SPEC-REVIEW-482-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #482 | [SPEC-REVIEW-482-r2.md](SPEC-REVIEW-482-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | — | +| #482 | [CODE-REVIEW-482-r1.md](CODE-REVIEW-482-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #481 | [SPEC-REVIEW-481-r1.md](SPEC-REVIEW-481-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #481 | [SPEC-REVIEW-481-r2.md](SPEC-REVIEW-481-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 2 | — | +| #481 | [CODE-REVIEW-481-r1.md](CODE-REVIEW-481-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #478 | [SPEC-REVIEW-478-r1.md](SPEC-REVIEW-478-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #478 | [CODE-REVIEW-478-r1.md](CODE-REVIEW-478-r1.md) | code · r1 | 🔴 красный | 2 | 4 | — | +| #478 | [CODE-REVIEW-478-r2.md](CODE-REVIEW-478-r2.md) | code · r2 | 🟡 жёлтый | 0 | 1 | — | +| #478 | [CODE-REVIEW-478-r3.md](CODE-REVIEW-478-r3.md) | code · r3 | 🔴 красный | 0 | 0 | — | +| #477 | [SPEC-REVIEW-477-r1.md](SPEC-REVIEW-477-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #477 | [CODE-REVIEW-477-r1.md](CODE-REVIEW-477-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #476 | [SPEC-REVIEW-476-r1.md](SPEC-REVIEW-476-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #476 | [SPEC-REVIEW-476-r2.md](SPEC-REVIEW-476-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #476 | [CODE-REVIEW-476-r1.md](CODE-REVIEW-476-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #476 | [CODE-REVIEW-476-r2.md](CODE-REVIEW-476-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #476 | [CODE-REVIEW-476-r3.md](CODE-REVIEW-476-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #475 | [SPEC-REVIEW-475-r1.md](SPEC-REVIEW-475-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #475 | [SPEC-REVIEW-475-r2.md](SPEC-REVIEW-475-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #475 | [CODE-REVIEW-475-r1.md](CODE-REVIEW-475-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #475 | [CODE-REVIEW-475-r2.md](CODE-REVIEW-475-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #474 | [SPEC-REVIEW-474-r1.md](SPEC-REVIEW-474-r1.md) | spec · r1 | 🔴 красный | 0 | 0 | — | +| #474 | [SPEC-REVIEW-474-r2.md](SPEC-REVIEW-474-r2.md) | spec · r2 | 🔴 красный | 0 | 0 | — | +| #474 | [CODE-REVIEW-474-r1.md](CODE-REVIEW-474-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #473 | [SPEC-REVIEW-473-r1.md](SPEC-REVIEW-473-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #473 | [SPEC-REVIEW-473-r2.md](SPEC-REVIEW-473-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #473 | [CODE-REVIEW-473-r1.md](CODE-REVIEW-473-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #472 | [SPEC-REVIEW-472-r1.md](SPEC-REVIEW-472-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #472 | [SPEC-REVIEW-472-r2.md](SPEC-REVIEW-472-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #472 | [CODE-REVIEW-472-r1.md](CODE-REVIEW-472-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | Medium, в скоупе. SHA в отчёте — не тот, что был протестирован; Low, не блокирует. Поиск открытого issue по заголовку не проверен вживую | +| #472 | [CODE-REVIEW-472-r2.md](CODE-REVIEW-472-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #471 | [SPEC-REVIEW-471-r1.md](SPEC-REVIEW-471-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #471 | [CODE-REVIEW-471-r1.md](CODE-REVIEW-471-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #464 | [SPEC-REVIEW-464-r1.md](SPEC-REVIEW-464-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #464 | [CODE-REVIEW-464-r1.md](CODE-REVIEW-464-r1.md) | code · r1 | 🟡 жёлтый | 1 | 0 | — | +| #464 | [CODE-REVIEW-464-r2.md](CODE-REVIEW-464-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #463 | [SPEC-REVIEW-463-r1.md](SPEC-REVIEW-463-r1.md) | spec · r1 | ⚪ — | 0 | 0 | — | +| #463 | [SPEC-REVIEW-463-r2.md](SPEC-REVIEW-463-r2.md) | spec · r2 | 🔴 красный | 0 | 0 | — | +| #463 | [CODE-REVIEW-463-r1.md](CODE-REVIEW-463-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #462 | [SPEC-REVIEW-462-r1.md](SPEC-REVIEW-462-r1.md) | spec · r1 | ⚪ — | 0 | 0 | — | +| #462 | [SPEC-REVIEW-462-r2.md](SPEC-REVIEW-462-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | — | +| #462 | [SPEC-REVIEW-462-r3.md](SPEC-REVIEW-462-r3.md) | spec · r3 | 🔴 красный | 1 | 0 | — | +| #462 | [CODE-REVIEW-462-r1.md](CODE-REVIEW-462-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | 1 Backend (AC2, AC3, AC4, AC5, AC11); 2 Frontend (AC6–AC10, AC12); 3 Документация (AC1) и артефакты сборки (AC13) | +| #461 | [SPEC-REVIEW-461-r1.md](SPEC-REVIEW-461-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #461 | [CODE-REVIEW-461-r1.md](CODE-REVIEW-461-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #461 | [CODE-REVIEW-461-r2.md](CODE-REVIEW-461-r2.md) | code · r2 | 🟢 зелёный | 0 | 1 | — | +| #460 | [SPEC-REVIEW-460-r1.md](SPEC-REVIEW-460-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #460 | [CODE-REVIEW-460-r1.md](CODE-REVIEW-460-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #459 | [SPEC-REVIEW-459-r1.md](SPEC-REVIEW-459-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #459 | [SPEC-REVIEW-459-r2.md](SPEC-REVIEW-459-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #459 | [CODE-REVIEW-459-r1.md](CODE-REVIEW-459-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #459 | [CODE-REVIEW-459-r2.md](CODE-REVIEW-459-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #457 | [SPEC-REVIEW-457-r1.md](SPEC-REVIEW-457-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #457 | [CODE-REVIEW-457-r1.md](CODE-REVIEW-457-r1.md) | code · r1 | 🟡 жёлтый | 0 | 2 | — | +| #457 | [CODE-REVIEW-457-r2.md](CODE-REVIEW-457-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #456 | [SPEC-REVIEW-456-r1.md](SPEC-REVIEW-456-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #456 | [CODE-REVIEW-456-r1.md](CODE-REVIEW-456-r1.md) | code · r1 | 🟡 жёлтый | 0 | 3 | 1 Дешёвые гейты (гоняю сам — зелёного Validate на этом SHA нет); 2 Живой браузерный прогон новой фичи (нет закоммиченного смока — см. M3); check-docs.mjs красный: отпечаток скриншотов устарел; golden:verify красный: footer диалога пространства изменился, эталон не обновлён; заявленный в ТЗ browser smoke для Copy отсутствует | +| #456 | [CODE-REVIEW-456-r2.md](CODE-REVIEW-456-r2.md) | code · r2 | 🟡 жёлтый | 0 | 3 | 1 Дешёвые гейты — прогнаны заново (код изменился, стоят минуты); 2 Гейты, закрывающие находки r1 — прогнаны целиком (не только для проверки на слово авт…; 3 Не прогонялось повторно — обосновано в §5 «Унаследовано» | +| #454 | [SPEC-REVIEW-454-r1.md](SPEC-REVIEW-454-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #454 | [SPEC-REVIEW-454-r2.md](SPEC-REVIEW-454-r2.md) | spec · r2 | 🔴 красный | 0 | 0 | — | +| #454 | [CODE-REVIEW-454-r3.md](CODE-REVIEW-454-r3.md) | code · r3 | 🟢 зелёный | 0 | 1 | — | +| #454 | [CODE-REVIEW-454-r4.md](CODE-REVIEW-454-r4.md) | code · r4 | 🟡 жёлтый | 0 | 0 | — | +| #451 | [SPEC-REVIEW-451-r1.md](SPEC-REVIEW-451-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 3 | docs/PERFORMANCE.md не существует, но назван обязательным артефактом (Medium, в скоупе); docs/SCREENSHOTS.md не существует (Medium, в скоупе); верхняя граница диапазона «наблюдавшихся» замеров в §11.3 не подтверждена ни одним исто… | +| #451 | [SPEC-REVIEW-451-r2.md](SPEC-REVIEW-451-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #451 | [CODE-REVIEW-451-r1.md](CODE-REVIEW-451-r1.md) | code · r1 | 🔴 красный | 1 | 2 | — | +| #451 | [CODE-REVIEW-451-r2.md](CODE-REVIEW-451-r2.md) | code · r2 | 🔴 красный | 3 | 0 | — | +| #451 | [CODE-REVIEW-451-r3.md](CODE-REVIEW-451-r3.md) | code · r3 | 🟡 жёлтый | 3 | 1 | не заявленный в ТЗ постоянный отказ от live-preflight на больших планах, противоречит с… | +| #451 | [CODE-REVIEW-451-r4.md](CODE-REVIEW-451-r4.md) | code · r4 | 🟡 жёлтый | 0 | 1 | — | +| #450 | [CODE-REVIEW-450-r1.md](CODE-REVIEW-450-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #449 | [SPEC-REVIEW-449-r1.md](SPEC-REVIEW-449-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #449 | [SPEC-REVIEW-449-r2.md](SPEC-REVIEW-449-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #449 | [CODE-REVIEW-449-r1.md](CODE-REVIEW-449-r1.md) | code · r1 | 🔴 красный | 0 | 0 | — | +| #449 | [CODE-REVIEW-449-r2.md](CODE-REVIEW-449-r2.md) | code · r2 | 🔴 красный | 3 | 2 | — | +| #448 | [SPEC-REVIEW-448-r1.md](SPEC-REVIEW-448-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #448 | [SPEC-REVIEW-448-r2.md](SPEC-REVIEW-448-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #448 | [CODE-REVIEW-448-r1.md](CODE-REVIEW-448-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #448 | [CODE-REVIEW-448-r2.md](CODE-REVIEW-448-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #447 | [SPEC-REVIEW-447-r1.md](SPEC-REVIEW-447-r1.md) | spec · r1 | 🔴 красный | 1 | 0 | AC4 и §3 путают единицы «шага сетки»: render-space gridPitch vs normalised GRIDSTEPN, ч… | +| #447 | [SPEC-REVIEW-447-r2.md](SPEC-REVIEW-447-r2.md) | spec · r2 | 🔴 красный | 0 | 0 | — | +| #447 | [CODE-REVIEW-447-r1.md](CODE-REVIEW-447-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #445 | [SPEC-REVIEW-445-r1.md](SPEC-REVIEW-445-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #445 | [SPEC-REVIEW-445-r2.md](SPEC-REVIEW-445-r2.md) | spec · r2 | 🟢 зелёный | 0 | 1 | — | +| #445 | [CODE-REVIEW-445-r1.md](CODE-REVIEW-445-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #443 | [SPEC-REVIEW-443-r1.md](SPEC-REVIEW-443-r1.md) | spec · r1 | ⚪ — | 0 | 0 | — | +| #443 | [SPEC-REVIEW-443-r2.md](SPEC-REVIEW-443-r2.md) | spec · r2 | ⚪ — | 0 | 0 | — | +| #443 | [CODE-REVIEW-443-r1.md](CODE-REVIEW-443-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #442 | [SPEC-REVIEW-442-r1.md](SPEC-REVIEW-442-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #442 | [CODE-REVIEW-442-r1.md](CODE-REVIEW-442-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #441 | [CODE-REVIEW-441-r1.md](CODE-REVIEW-441-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #440 | [SPEC-REVIEW-440-r1.md](SPEC-REVIEW-440-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #440 | [CODE-REVIEW-440-r1.md](CODE-REVIEW-440-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #439 | [CODE-REVIEW-439-r1.md](CODE-REVIEW-439-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #437 | [SPEC-REVIEW-437-r1.md](SPEC-REVIEW-437-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #437 | [SPEC-REVIEW-437-r2.md](SPEC-REVIEW-437-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #437 | [CODE-REVIEW-437-r1.md](CODE-REVIEW-437-r1.md) | code · r1 | 🟡 жёлтый | 0 | 4 | существующий смок demo/smokekiosk.mjs падает необработанным исключением на этом SHA; исключение «удалённого устройства» в подсчёте Q2 не имеет свидетеля; tap-target реордер/удаления в форме настроек меньше заявленных 44×44 CSS px; идентичность карточки (AC21) не имеет ни выделенного резолвера, ни свидетеля теста | +| #437 | [CODE-REVIEW-437-r2.md](CODE-REVIEW-437-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #437 | [CODE-REVIEW-437-r3.md](CODE-REVIEW-437-r3.md) | code · r3 | 🟡 жёлтый | 0 | 0 | — | +| #437 | [CODE-REVIEW-437-r4.md](CODE-REVIEW-437-r4.md) | code · r4 | 🟢 зелёный | 0 | 0 | — | +| #434 | [SPEC-REVIEW-434-r1.md](SPEC-REVIEW-434-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #434 | [SPEC-REVIEW-434-r2.md](SPEC-REVIEW-434-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #434 | [CODE-REVIEW-434-r1.md](CODE-REVIEW-434-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #434 | [CODE-REVIEW-434-r2.md](CODE-REVIEW-434-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #432 | [SPEC-REVIEW-432-r1.md](SPEC-REVIEW-432-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #432 | [CODE-REVIEW-432-r1.md](CODE-REVIEW-432-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #431 | [SPEC-REVIEW-431-r1.md](SPEC-REVIEW-431-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #431 | [CODE-REVIEW-431-r1.md](CODE-REVIEW-431-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #428 | [SPEC-REVIEW-428-r1.md](SPEC-REVIEW-428-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #428 | [CODE-REVIEW-428-r1.md](CODE-REVIEW-428-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | Первопричина и правка contentstate(); contentmanifest() (exporter); Plan-only проекция (projectplanonlydecor, validateplanonlydocument); Тесты (testsbackend/testhaimportexport.py); Документация и changelog (AC8) | +| #427 | [CODE-REVIEW-427-r1.md](CODE-REVIEW-427-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #426 | [SPEC-REVIEW-426-r1.md](SPEC-REVIEW-426-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #426 | [CODE-REVIEW-426-r1.md](CODE-REVIEW-426-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #425 | [SPEC-REVIEW-425-r1.md](SPEC-REVIEW-425-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #425 | [SPEC-REVIEW-425-r2.md](SPEC-REVIEW-425-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #425 | [CODE-REVIEW-425-r1.md](CODE-REVIEW-425-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #424 | [CODE-REVIEW-424-r2.md](CODE-REVIEW-424-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #423 | [SPEC-REVIEW-423-r1.md](SPEC-REVIEW-423-r1.md) | spec · r1 | ⚪ — | 0 | 0 | — | +| #423 | [CODE-REVIEW-423-r1.md](CODE-REVIEW-423-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #422 | [SPEC-REVIEW-422-r1.md](SPEC-REVIEW-422-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #422 | [CODE-REVIEW-422-r1.md](CODE-REVIEW-422-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #422 | [CODE-REVIEW-422-r2.md](CODE-REVIEW-422-r2.md) | code · r2 | 🔴 красный | 1 | 2 | — | +| #421 | [SPEC-REVIEW-421-r1.md](SPEC-REVIEW-421-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #421 | [SPEC-REVIEW-421-r2.md](SPEC-REVIEW-421-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #421 | [CODE-REVIEW-421-r1.md](CODE-REVIEW-421-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #420 | [CODE-REVIEW-420-r1.md](CODE-REVIEW-420-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #419 | [SPEC-REVIEW-419-r1.md](SPEC-REVIEW-419-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #419 | [CODE-REVIEW-419-r1.md](CODE-REVIEW-419-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #418 | [SPEC-REVIEW-418-r1.md](SPEC-REVIEW-418-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #418 | [CODE-REVIEW-418-r1.md](CODE-REVIEW-418-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #417 | [CODE-REVIEW-417-r1.md](CODE-REVIEW-417-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #415 | [CODE-REVIEW-415-r1.md](CODE-REVIEW-415-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #415 | [CODE-REVIEW-415-r2.md](CODE-REVIEW-415-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #411 | [CODE-REVIEW-411-r1.md](CODE-REVIEW-411-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #406 | [SPEC-REVIEW-406-r1.md](SPEC-REVIEW-406-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #406 | [SPEC-REVIEW-406-r2.md](SPEC-REVIEW-406-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 2 | — | +| #406 | [SPEC-REVIEW-406-r3.md](SPEC-REVIEW-406-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 1 | — | +| #406 | [SPEC-REVIEW-406-r4.md](SPEC-REVIEW-406-r4.md) | spec · r4 | 🟢 зелёный | 0 | 0 | — | +| #406 | [CODE-REVIEW-406-r1.md](CODE-REVIEW-406-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #406 | [CODE-REVIEW-406-r2.md](CODE-REVIEW-406-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #405 | [SPEC-REVIEW-405-r1.md](SPEC-REVIEW-405-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | a (Medium, в скоупе) — таблица «Место» неполна: пропущена обёртка houseplan-card.ts:785…; b (Low) — ссылка «#406 «д»» на несуществующий пункт | +| #405 | [SPEC-REVIEW-405-r2.md](SPEC-REVIEW-405-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #405 | [CODE-REVIEW-405-r1.md](CODE-REVIEW-405-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #403 | [SPEC-REVIEW-403-r1.md](SPEC-REVIEW-403-r1.md) | spec · r1 | ⚪ — | 1 | 0 | — | +| #403 | [SPEC-REVIEW-403-r2.md](SPEC-REVIEW-403-r2.md) | spec · r2 | 🟢 зелёный | 1 | 0 | — | +| #403 | [CODE-REVIEW-403-r1.md](CODE-REVIEW-403-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #402 | [SPEC-REVIEW-402-r1.md](SPEC-REVIEW-402-r1.md) | spec · r1 | ⚪ — | 1 | 1 | — | +| #402 | [SPEC-REVIEW-402-r2.md](SPEC-REVIEW-402-r2.md) | spec · r2 | 🔴 красный | 1 | 1 | — | +| #402 | [CODE-REVIEW-402-r1.md](CODE-REVIEW-402-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | AC2 не покрыт смоком буквально (ветки fixedfloor pending/invalid, !space); AC3 не проверен буквальным сценарием issue (клик по корзине → deleteServerPlan); AC8: тап по scrim не проверен явно; confirmDanger не проверяет языковой гейт warm явно | +| #400 | [SPEC-REVIEW-400-r1.md](SPEC-REVIEW-400-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #400 | [CODE-REVIEW-400-r1.md](CODE-REVIEW-400-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #400 | [CODE-REVIEW-400-r2.md](CODE-REVIEW-400-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #400 | [CODE-REVIEW-400-r3.md](CODE-REVIEW-400-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #399 | [SPEC-REVIEW-399-r1.md](SPEC-REVIEW-399-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #399 | [SPEC-REVIEW-399-r2.md](SPEC-REVIEW-399-r2.md) | spec · r2 | 🟢 зелёный | 0 | 1 | — | +| #399 | [CODE-REVIEW-399-r1.md](CODE-REVIEW-399-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #399 | [CODE-REVIEW-399-r2.md](CODE-REVIEW-399-r2.md) | code · r2 | 🟡 жёлтый | 1 | 0 | — | +| #398 | [SPEC-REVIEW-398-r1.md](SPEC-REVIEW-398-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #398 | [SPEC-REVIEW-398-r2.md](SPEC-REVIEW-398-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #398 | [SPEC-REVIEW-398-r3.md](SPEC-REVIEW-398-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 0 | — | +| #398 | [CODE-REVIEW-398-r1.md](CODE-REVIEW-398-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #397 | [SPEC-REVIEW-397-r1.md](SPEC-REVIEW-397-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #397 | [SPEC-REVIEW-397-r2.md](SPEC-REVIEW-397-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #397 | [CODE-REVIEW-397-r1.md](CODE-REVIEW-397-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #397 | [CODE-REVIEW-397-r2.md](CODE-REVIEW-397-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #396 | [SPEC-REVIEW-396-r1.md](SPEC-REVIEW-396-r1.md) | spec · r1 | 🔴 красный | 1 | 0 | — | +| #396 | [SPEC-REVIEW-396-r2.md](SPEC-REVIEW-396-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | — | +| #396 | [SPEC-REVIEW-396-r3.md](SPEC-REVIEW-396-r3.md) | spec · r3 | 🔴 красный | 0 | 0 | — | +| #396 | [CODE-REVIEW-396-r1.md](CODE-REVIEW-396-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #396 | [CODE-REVIEW-396-r2.md](CODE-REVIEW-396-r2.md) | code · r2 | ⚪ — | 0 | 0 | — | +| #391 | [SPEC-REVIEW-391-r1.md](SPEC-REVIEW-391-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #391 | [CODE-REVIEW-391-r1.md](CODE-REVIEW-391-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #391 | [CODE-REVIEW-391-r2.md](CODE-REVIEW-391-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #390 | [CODE-REVIEW-390-r1.md](CODE-REVIEW-390-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #389 | [CODE-REVIEW-389-r1.md](CODE-REVIEW-389-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #385 | [SPEC-REVIEW-385-r1.md](SPEC-REVIEW-385-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #385 | [SPEC-REVIEW-385-r2.md](SPEC-REVIEW-385-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #385 | [CODE-REVIEW-385-r1.md](CODE-REVIEW-385-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #385 | [CODE-REVIEW-385-r2.md](CODE-REVIEW-385-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #385 | [CODE-REVIEW-385-r3.md](CODE-REVIEW-385-r3.md) | code · r3 | 🟡 жёлтый | 0 | 0 | — | +| #384 | [SPEC-REVIEW-384-r1.md](SPEC-REVIEW-384-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #384 | [SPEC-REVIEW-384-r2.md](SPEC-REVIEW-384-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | — | +| #384 | [CODE-REVIEW-384-r1.md](CODE-REVIEW-384-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #383 | [SPEC-REVIEW-383-r1.md](SPEC-REVIEW-383-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #383 | [CODE-REVIEW-383-r1.md](CODE-REVIEW-383-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #383 | [CODE-REVIEW-383-r2.md](CODE-REVIEW-383-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #381 | [SPEC-REVIEW-381-r1.md](SPEC-REVIEW-381-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #381 | [SPEC-REVIEW-381-r2.md](SPEC-REVIEW-381-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | — | +| #381 | [CODE-REVIEW-381-r1.md](CODE-REVIEW-381-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #378 | [SPEC-REVIEW-378-r1.md](SPEC-REVIEW-378-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #378 | [CODE-REVIEW-378-r1.md](CODE-REVIEW-378-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #378 | [CODE-REVIEW-378-r2.md](CODE-REVIEW-378-r2.md) | code · r2 | 🟢 зелёный | 0 | 1 | — | +| #377 | [SPEC-REVIEW-377-r1.md](SPEC-REVIEW-377-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #377 | [SPEC-REVIEW-377-r2.md](SPEC-REVIEW-377-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #377 | [CODE-REVIEW-377-r1.md](CODE-REVIEW-377-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #377 | [CODE-REVIEW-377-r2.md](CODE-REVIEW-377-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #376 | [SPEC-REVIEW-376-r1.md](SPEC-REVIEW-376-r1.md) | spec · r1 | 🔴 красный | 1 | 2 | — | +| #376 | [SPEC-REVIEW-376-r2.md](SPEC-REVIEW-376-r2.md) | spec · r2 | 🟢 зелёный | 1 | 2 | Тело issue #376, строка сразу после заголовка «# ТЗ (лёгкий трек, редакция 2)»; gh issu…; Тело issue #376, раздел 6 (отсутствовал в редакции 1); Тело issue #376, раздел «2. AC», пункты 2 и 4 (в редакции 1 их не было — было только AC… | +| #376 | [CODE-REVIEW-376-r1.md](CODE-REVIEW-376-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #376 | [CODE-REVIEW-376-r2.md](CODE-REVIEW-376-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #376 | [CODE-REVIEW-376-r3.md](CODE-REVIEW-376-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #375 | [SPEC-REVIEW-375-r1.md](SPEC-REVIEW-375-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #375 | [SPEC-REVIEW-375-r2.md](SPEC-REVIEW-375-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #375 | [CODE-REVIEW-375-r1.md](CODE-REVIEW-375-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #374 | [SPEC-REVIEW-374-r1.md](SPEC-REVIEW-374-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #374 | [CODE-REVIEW-374-r1.md](CODE-REVIEW-374-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #373 | [SPEC-REVIEW-373-r1.md](SPEC-REVIEW-373-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #373 | [CODE-REVIEW-373-r1.md](CODE-REVIEW-373-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #373 | [CODE-REVIEW-373-r2.md](CODE-REVIEW-373-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #372 | [SPEC-REVIEW-372-r1.md](SPEC-REVIEW-372-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #372 | [CODE-REVIEW-372-r1.md](CODE-REVIEW-372-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #371 | [SPEC-REVIEW-371-r1.md](SPEC-REVIEW-371-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #371 | [SPEC-REVIEW-371-r2.md](SPEC-REVIEW-371-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #371 | [CODE-REVIEW-371-r1.md](CODE-REVIEW-371-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #371 | [CODE-REVIEW-371-r2.md](CODE-REVIEW-371-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #369 | [SPEC-REVIEW-369-r1.md](SPEC-REVIEW-369-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 3 | — | +| #369 | [CODE-REVIEW-369-r3.md](CODE-REVIEW-369-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #368 | [CODE-REVIEW-368-r1.md](CODE-REVIEW-368-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #366 | [SPEC-REVIEW-366-r1.md](SPEC-REVIEW-366-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #366 | [SPEC-REVIEW-366-r2.md](SPEC-REVIEW-366-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #366 | [CODE-REVIEW-366-r1.md](CODE-REVIEW-366-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #363 | [SPEC-REVIEW-363-r1.md](SPEC-REVIEW-363-r1.md) | spec · r1 | 🟢 зелёный | 0 | 1 | — | +| #363 | [CODE-REVIEW-363-r2.md](CODE-REVIEW-363-r2.md) | code · r2 | ⚪ — | 0 | 0 | — | +| #362 | [SPEC-REVIEW-362-r1.md](SPEC-REVIEW-362-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #362 | [CODE-REVIEW-362-r1.md](CODE-REVIEW-362-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #361 | [SPEC-REVIEW-361-r1.md](SPEC-REVIEW-361-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #361 | [SPEC-REVIEW-361-r2.md](SPEC-REVIEW-361-r2.md) | spec · r2 | 🟢 зелёный | 0 | 1 | — | +| #361 | [CODE-REVIEW-361-r1.md](CODE-REVIEW-361-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #361 | [CODE-REVIEW-361-r2.md](CODE-REVIEW-361-r2.md) | code · r2 | ⚪ — | 0 | 1 | — | +| #360 | [SPEC-REVIEW-360-r1.md](SPEC-REVIEW-360-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #360 | [SPEC-REVIEW-360-r2.md](SPEC-REVIEW-360-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #360 | [CODE-REVIEW-360-r1.md](CODE-REVIEW-360-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #360 | [CODE-REVIEW-360-r2.md](CODE-REVIEW-360-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #359 | [SPEC-REVIEW-359-r1.md](SPEC-REVIEW-359-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #359 | [SPEC-REVIEW-359-r2.md](SPEC-REVIEW-359-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #359 | [CODE-REVIEW-359-r1.md](CODE-REVIEW-359-r1.md) | code · r1 | 🔴 красный | 1 | 0 | — | +| #359 | [CODE-REVIEW-359-r2.md](CODE-REVIEW-359-r2.md) | code · r2 | 🔴 красный | 0 | 0 | — | +| #358 | [SPEC-REVIEW-358-r1.md](SPEC-REVIEW-358-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #358 | [CODE-REVIEW-358-r1.md](CODE-REVIEW-358-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #357 | [SPEC-REVIEW-357-r1.md](SPEC-REVIEW-357-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #357 | [CODE-REVIEW-357-r1.md](CODE-REVIEW-357-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #356 | [CODE-REVIEW-356-r1.md](CODE-REVIEW-356-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #354 | [SPEC-REVIEW-354-r1.md](SPEC-REVIEW-354-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #354 | [CODE-REVIEW-354-r1.md](CODE-REVIEW-354-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #354 | [CODE-REVIEW-354-r2.md](CODE-REVIEW-354-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #353 | [SPEC-REVIEW-353-r1.md](SPEC-REVIEW-353-r1.md) | spec · r1 | 🟡 жёлтый | 1 | 1 | — | +| #353 | [SPEC-REVIEW-353-r2.md](SPEC-REVIEW-353-r2.md) | spec · r2 | 🟡 жёлтый | 1 | 1 | — | +| #353 | [CODE-REVIEW-353-r1.md](CODE-REVIEW-353-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #348 | [SPEC-REVIEW-348-r1.md](SPEC-REVIEW-348-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | de-CH унаследует «ß», хотя стандартная швейцарская орфография его не использует; явного блока «принято предположительно, поменять свободно» в конце документа нет | +| #348 | [CODE-REVIEW-348-r1.md](CODE-REVIEW-348-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #348 | [CODE-REVIEW-348-r2.md](CODE-REVIEW-348-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #341 | [CODE-REVIEW-341-r1.md](CODE-REVIEW-341-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #340 | [SPEC-REVIEW-340-r1.md](SPEC-REVIEW-340-r1.md) | spec · r1 | 🟢 зелёный | 0 | 1 | — | +| #340 | [CODE-REVIEW-340-r1.md](CODE-REVIEW-340-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #339 | [CODE-REVIEW-339-r1.md](CODE-REVIEW-339-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #337 | [SPEC-REVIEW-337-r1.md](SPEC-REVIEW-337-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #337 | [CODE-REVIEW-337-r1.md](CODE-REVIEW-337-r1.md) | code · r1 | 🔴 красный | 0 | 0 | — | +| #337 | [CODE-REVIEW-337-r2.md](CODE-REVIEW-337-r2.md) | code · r2 | 🔴 красный | 0 | 0 | — | +| #337 | [CODE-REVIEW-337-r3.md](CODE-REVIEW-337-r3.md) | code · r3 | 🔴 красный | 1 | 0 | — | +| #337 | [CODE-REVIEW-337-r4.md](CODE-REVIEW-337-r4.md) | code · r4 | 🟢 зелёный | 1 | 0 | — | +| #335 | [SPEC-REVIEW-335-r1.md](SPEC-REVIEW-335-r1.md) | spec · r1 | 🟡 жёлтый | 1 | 1 | — | +| #335 | [SPEC-REVIEW-335-r2.md](SPEC-REVIEW-335-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #335 | [CODE-REVIEW-335-r1.md](CODE-REVIEW-335-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #333 | [SPEC-REVIEW-333-r1.md](SPEC-REVIEW-333-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | AC4 называет способ доказательства, который не проверяет заявленное (Medium, в скоупе з… | +| #333 | [SPEC-REVIEW-333-r2.md](SPEC-REVIEW-333-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | — | +| #333 | [CODE-REVIEW-333-r1.md](CODE-REVIEW-333-r1.md) | code · r1 | 🟡 жёлтый | 1 | 1 | — | +| #333 | [CODE-REVIEW-333-r2.md](CODE-REVIEW-333-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #331 | [SPEC-REVIEW-331-r1.md](SPEC-REVIEW-331-r1.md) | spec · r1 | 🔴 красный | 1 | 4 | — | +| #331 | [SPEC-REVIEW-331-r2.md](SPEC-REVIEW-331-r2.md) | spec · r2 | 🔴 красный | 0 | 1 | — | +| #331 | [SPEC-REVIEW-331-r3.md](SPEC-REVIEW-331-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 0 | — | +| #331 | [CODE-REVIEW-331-r1.md](CODE-REVIEW-331-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #330 | [SPEC-REVIEW-330-r1.md](SPEC-REVIEW-330-r1.md) | spec · r1 | ⚪ — | 2 | 1 | Перф-бюджеты §5 недостижимы решением из §4: AC2 и AC6 непроверяемы как написаны; AC4 ссылается на бенч, который не вызывает изменяемый код; Отсутствуют обязательные разделы ТЗ по §7.1: i18n, touch, риски, release-артефакты | +| #330 | [SPEC-REVIEW-330-r2.md](SPEC-REVIEW-330-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #330 | [CODE-REVIEW-330-r2.md](CODE-REVIEW-330-r2.md) | code · r2 | ⚪ — | 0 | 1 | — | +| #330 | [CODE-REVIEW-330-r3.md](CODE-REVIEW-330-r3.md) | code · r3 | 🔴 красный | 1 | 0 | докс-гейт красный на HEAD ревью (в скоупе, блокирует) | +| #330 | [CODE-REVIEW-330-r4.md](CODE-REVIEW-330-r4.md) | code · r4 | 🟡 жёлтый | 1 | 0 | — | +| #329 | [SPEC-REVIEW-329-r1.md](SPEC-REVIEW-329-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #329 | [SPEC-REVIEW-329-r2.md](SPEC-REVIEW-329-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #329 | [SPEC-REVIEW-329-r3.md](SPEC-REVIEW-329-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 0 | — | +| #329 | [CODE-REVIEW-329-r1.md](CODE-REVIEW-329-r1.md) | code · r1 | 🔴 красный | 1 | 4 | — | +| #329 | [CODE-REVIEW-329-r2.md](CODE-REVIEW-329-r2.md) | code · r2 | 🔴 красный | 1 | 4 | — | +| #329 | [CODE-REVIEW-329-r3.md](CODE-REVIEW-329-r3.md) | code · r3 | 🔴 красный | 0 | 0 | — | +| #324 | [CODE-REVIEW-324-r1.md](CODE-REVIEW-324-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #323 | [CODE-REVIEW-323-r1.md](CODE-REVIEW-323-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #319 | [SPEC-REVIEW-319-r1.md](SPEC-REVIEW-319-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #319 | [CODE-REVIEW-319-r1.md](CODE-REVIEW-319-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #318 | [SPEC-REVIEW-318-r1.md](SPEC-REVIEW-318-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #318 | [CODE-REVIEW-318-r1.md](CODE-REVIEW-318-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #317 | [SPEC-REVIEW-317-r1.md](SPEC-REVIEW-317-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #317 | [CODE-REVIEW-317-r1.md](CODE-REVIEW-317-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #316 | [SPEC-REVIEW-316-r1.md](SPEC-REVIEW-316-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #316 | [SPEC-REVIEW-316-r2.md](SPEC-REVIEW-316-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 2 | — | +| #316 | [SPEC-REVIEW-316-r3.md](SPEC-REVIEW-316-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 1 | — | +| #316 | [SPEC-REVIEW-316-r4.md](SPEC-REVIEW-316-r4.md) | spec · r4 | 🟢 зелёный | 0 | 0 | — | +| #316 | [CODE-REVIEW-316-r1.md](CODE-REVIEW-316-r1.md) | code · r1 | 🔴 красный | 1 | 1 | — | +| #316 | [CODE-REVIEW-316-r2.md](CODE-REVIEW-316-r2.md) | code · r2 | 🔴 красный | 1 | 1 | — | +| #314 | [SPEC-REVIEW-314-r1.md](SPEC-REVIEW-314-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #314 | [CODE-REVIEW-314-r1.md](CODE-REVIEW-314-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #313 | [SPEC-REVIEW-313-r1.md](SPEC-REVIEW-313-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #313 | [CODE-REVIEW-313-r1.md](CODE-REVIEW-313-r1.md) | code · r1 | 🔴 красный | 0 | 0 | — | +| #313 | [CODE-REVIEW-313-r2.md](CODE-REVIEW-313-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #310 | [SPEC-REVIEW-310-r1.md](SPEC-REVIEW-310-r1.md) | spec · r1 | 🔴 красный | 0 | 0 | — | +| #310 | [SPEC-REVIEW-310-r2.md](SPEC-REVIEW-310-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #310 | [CODE-REVIEW-310-r1.md](CODE-REVIEW-310-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #310 | [CODE-REVIEW-310-r2.md](CODE-REVIEW-310-r2.md) | code · r2 | 🟡 жёлтый | 1 | 1 | 1 Честность нового Baseline-Reviewed; 2 Новый юнит-тест — умеет падать | +| #309 | [SPEC-REVIEW-309-r1.md](SPEC-REVIEW-309-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #309 | [SPEC-REVIEW-309-r2.md](SPEC-REVIEW-309-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | — | +| #309 | [SPEC-REVIEW-309-r3.md](SPEC-REVIEW-309-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 1 | — | +| #309 | [CODE-REVIEW-309-r1.md](CODE-REVIEW-309-r1.md) | code · r1 | 🔴 красный | 0 | 0 | — | +| #309 | [CODE-REVIEW-309-r2.md](CODE-REVIEW-309-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #307 | [SPEC-REVIEW-307-r1.md](SPEC-REVIEW-307-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #307 | [CODE-REVIEW-307-r1.md](CODE-REVIEW-307-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #306 | [SPEC-REVIEW-306-r1.md](SPEC-REVIEW-306-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #306 | [SPEC-REVIEW-306-r2.md](SPEC-REVIEW-306-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #306 | [CODE-REVIEW-306-r1.md](CODE-REVIEW-306-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | 1 Гейты — что прогнано; 2 Что НЕ прогонялось и почему | +| #306 | [CODE-REVIEW-306-r2.md](CODE-REVIEW-306-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | 1 Гейты — что прогнано лично; 2 Что НЕ прогонялось и почему; 3 Разбор смок-выборки по дельте | +| #304 | [SPEC-REVIEW-304-r1.md](SPEC-REVIEW-304-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #304 | [CODE-REVIEW-304-r1.md](CODE-REVIEW-304-r1.md) | code · r1 | 🟡 жёлтый | 1 | 0 | — | +| #304 | [CODE-REVIEW-304-r2.md](CODE-REVIEW-304-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #303 | [SPEC-REVIEW-303-r1.md](SPEC-REVIEW-303-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #303 | [CODE-REVIEW-303-r1.md](CODE-REVIEW-303-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | счёт unit-тестов в отчёте разработчика на единицу отличается от факта | +| #302 | [SPEC-REVIEW-302-r1.md](SPEC-REVIEW-302-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #302 | [CODE-REVIEW-302-r1.md](CODE-REVIEW-302-r1.md) | code · r1 | ⚪ — | 0 | 2 | — | +| #302 | [CODE-REVIEW-302-r2.md](CODE-REVIEW-302-r2.md) | code · r2 | ⚪ — | 0 | 1 | — | +| #302 | [CODE-REVIEW-302-r4.md](CODE-REVIEW-302-r4.md) | code · r4 | 🟢 зелёный | 0 | 0 | — | +| #301 | [SPEC-REVIEW-301-r1.md](SPEC-REVIEW-301-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #301 | [CODE-REVIEW-301-r1.md](CODE-REVIEW-301-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #300 | [SPEC-REVIEW-300-r1.md](SPEC-REVIEW-300-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #300 | [SPEC-REVIEW-300-r2.md](SPEC-REVIEW-300-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | — | +| #300 | [SPEC-REVIEW-300-r3.md](SPEC-REVIEW-300-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 1 | — | +| #300 | [SPEC-REVIEW-300-r4.md](SPEC-REVIEW-300-r4.md) | spec · r4 | 🟡 жёлтый | 0 | 0 | — | +| #299 | [SPEC-REVIEW-299-r1.md](SPEC-REVIEW-299-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #298 | [SPEC-REVIEW-298-r1.md](SPEC-REVIEW-298-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #298 | [SPEC-REVIEW-298-r2.md](SPEC-REVIEW-298-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #298 | [CODE-REVIEW-298-r1.md](CODE-REVIEW-298-r1.md) | code · r1 | 🔴 красный | 2 | 0 | — | +| #296 | [SPEC-REVIEW-296-r1.md](SPEC-REVIEW-296-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #296 | [CODE-REVIEW-296-r1.md](CODE-REVIEW-296-r1.md) | code · r1 | 🔴 красный | 0 | 0 | — | +| #295 | [SPEC-REVIEW-295-r1.md](SPEC-REVIEW-295-r1.md) | spec · r1 | 🟡 жёлтый | 1 | 1 | — | +| #295 | [SPEC-REVIEW-295-r2.md](SPEC-REVIEW-295-r2.md) | spec · r2 | 🟡 жёлтый | 1 | 1 | — | +| #295 | [CODE-REVIEW-295-r1.md](CODE-REVIEW-295-r1.md) | code · r1 | 🟡 жёлтый | 0 | 2 | — | +| #295 | [CODE-REVIEW-295-r2.md](CODE-REVIEW-295-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #294 | [SPEC-REVIEW-294-r1.md](SPEC-REVIEW-294-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #294 | [SPEC-REVIEW-294-r2.md](SPEC-REVIEW-294-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #294 | [CODE-REVIEW-294-r1.md](CODE-REVIEW-294-r1.md) | code · r1 | 🔴 красный | 0 | 2 | — | +| #294 | [CODE-REVIEW-294-r2.md](CODE-REVIEW-294-r2.md) | code · r2 | 🟢 зелёный | 0 | 2 | — | +| #293 | [SPEC-REVIEW-293-r1.md](SPEC-REVIEW-293-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #293 | [SPEC-REVIEW-293-r2.md](SPEC-REVIEW-293-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #293 | [CODE-REVIEW-293-r1.md](CODE-REVIEW-293-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #292 | [SPEC-REVIEW-292-r1.md](SPEC-REVIEW-292-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #292 | [SPEC-REVIEW-292-r2.md](SPEC-REVIEW-292-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #292 | [CODE-REVIEW-292-r1.md](CODE-REVIEW-292-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #291 | [SPEC-REVIEW-291-r1.md](SPEC-REVIEW-291-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #291 | [SPEC-REVIEW-291-r2.md](SPEC-REVIEW-291-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #291 | [SPEC-REVIEW-291-r3.md](SPEC-REVIEW-291-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 0 | — | +| #291 | [CODE-REVIEW-291-r1.md](CODE-REVIEW-291-r1.md) | code · r1 | 🟡 жёлтый | 0 | 2 | — | +| #291 | [CODE-REVIEW-291-r2.md](CODE-REVIEW-291-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #290 | [SPEC-REVIEW-290-r1.md](SPEC-REVIEW-290-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #290 | [SPEC-REVIEW-290-r2.md](SPEC-REVIEW-290-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #290 | [CODE-REVIEW-290-r1.md](CODE-REVIEW-290-r1.md) | code · r1 | 🔴 красный | 1 | 2 | 1 Смок-выборка; 2 Инварианты и AC10 — реальные планы | +| #290 | [CODE-REVIEW-290-r2.md](CODE-REVIEW-290-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | 1 Смок-выборка; 1 Проверка закрытия High H1 (не декларативно — воспроизведением на обоих состояниях кода); 2 Проверка закрытия Medium M1 (mutation-gate); 3 Проверка закрытия Medium M2 (AC10 автотест) | +| #290 | [CODE-REVIEW-290-r3.md](CODE-REVIEW-290-r3.md) | code · r3 | ⚪ — | 0 | 0 | — | +| #290 | [CODE-REVIEW-290-r4.md](CODE-REVIEW-290-r4.md) | code · r4 | 🟢 зелёный | 0 | 0 | — | +| #289 | [SPEC-REVIEW-289-r1.md](SPEC-REVIEW-289-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 3 | — | +| #289 | [SPEC-REVIEW-289-r2.md](SPEC-REVIEW-289-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #289 | [CODE-REVIEW-289-r1.md](CODE-REVIEW-289-r1.md) | code · r1 | 🟢 зелёный | 1 | 1 | Незапланированный код #260 в ветке #289 ломает два golden-эталона, третий не принят (Hi…; Golden-эталон safe-resize-handles-clamp-{dark,light} не обновлён под настоящее поведени… | +| #289 | [CODE-REVIEW-289-r2.md](CODE-REVIEW-289-r2.md) | code · r2 | 🟢 зелёный | 1 | 1 | — | +| #288 | [SPEC-REVIEW-288-r1.md](SPEC-REVIEW-288-r1.md) | spec · r1 | 🟢 зелёный | 0 | 1 | — | +| #288 | [SPEC-REVIEW-288-r2.md](SPEC-REVIEW-288-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #288 | [CODE-REVIEW-288-r1.md](CODE-REVIEW-288-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #288 | [CODE-REVIEW-288-r2.md](CODE-REVIEW-288-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #288 | [CODE-REVIEW-288-r3.md](CODE-REVIEW-288-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #282 | [SPEC-REVIEW-282-r1.md](SPEC-REVIEW-282-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #282 | [SPEC-REVIEW-282-r2.md](SPEC-REVIEW-282-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 2 | — | +| #282 | [CODE-REVIEW-282-r1.md](CODE-REVIEW-282-r1.md) | code · r1 | 🔴 красный | 2 | 4 | #278-guard «отклонить правку деградированного пространства» больше не срабатывает; Широкая регрессия структурных операций редактирования; AC15 добавил новую строку в Optimize-диалог — golden baseline не обновлён; AC5 «executable source/AST guard + bypass mutant for each writer family» реализован уже…; wallmodelclientoutdated не срабатывает в реалистичном сценарии старого клиента; Регрессионный тест issue #248 ослаблен без обновления фикстуры | +| #282 | [CODE-REVIEW-282-r2.md](CODE-REVIEW-282-r2.md) | code · r2 | 🔴 красный | 2 | 0 | npm test красный: диагностика больше не соответствует контракту, который сама же проверяет; AC7 нарушен: opening на обычной стене исчезает со Static-карточки после миграции в v8 | +| #282 | [CODE-REVIEW-282-r3.md](CODE-REVIEW-282-r3.md) | code · r3 | 🔴 красный | 1 | 0 | AC7 всё ещё нарушен: Static-карточка рисует сплошную стену прямо по месту прохода после… | +| #282 | [CODE-REVIEW-282-r4.md](CODE-REVIEW-282-r4.md) | code · r4 | 🟢 зелёный | 0 | 0 | — | +| #278 | [SPEC-REVIEW-278-r1.md](SPEC-REVIEW-278-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #278 | [SPEC-REVIEW-278-r2.md](SPEC-REVIEW-278-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #278 | [CODE-REVIEW-278-r1.md](CODE-REVIEW-278-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #278 | [CODE-REVIEW-278-r2.md](CODE-REVIEW-278-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #277 | [SPEC-REVIEW-277-r1.md](SPEC-REVIEW-277-r1.md) | spec · r1 | ⚪ — | 0 | 3 | нет обязательного раздела «риски» (§7.1); нет обязательного «что человек увидит до и после» одной фразой без терминов реализации …; из плана тестов выпала обязательная анонимизированная fixture реального плана | +| #277 | [SPEC-REVIEW-277-r2.md](SPEC-REVIEW-277-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #277 | [CODE-REVIEW-277-r1.md](CODE-REVIEW-277-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #277 | [CODE-REVIEW-277-r2.md](CODE-REVIEW-277-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #276 | [SPEC-REVIEW-276-r1.md](SPEC-REVIEW-276-r1.md) | spec · r1 | 🔴 красный | 1 | 3 | — | +| #276 | [SPEC-REVIEW-276-r2.md](SPEC-REVIEW-276-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #276 | [SPEC-REVIEW-276-r3.md](SPEC-REVIEW-276-r3.md) | spec · r3 | 🟢 зелёный | 0 | 0 | — | +| #276 | [CODE-REVIEW-276-r1.md](CODE-REVIEW-276-r1.md) | code · r1 | 🟡 жёлтый | 0 | 3 | AC11 (перф-бюджет §10) не доказан ни автотестом, ни явным; AC3 «несколько hosted openings на одной partition» не покрыто тестом; AC5 «targeted golden» не добавлен ни в каком виде | +| #276 | [CODE-REVIEW-276-r2.md](CODE-REVIEW-276-r2.md) | code · r2 | 🟡 жёлтый | 0 | 3 | r2 (Medium, в скоупе, чинится тем же автором) — benchmark:coincident-partitions даёт си… | +| #276 | [CODE-REVIEW-276-r3.md](CODE-REVIEW-276-r3.md) | code · r3 | 🟡 жёлтый | 0 | 1 | — | +| #275 | [SPEC-REVIEW-275-r1.md](SPEC-REVIEW-275-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #275 | [SPEC-REVIEW-275-r2.md](SPEC-REVIEW-275-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #275 | [SPEC-REVIEW-275-r3.md](SPEC-REVIEW-275-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 0 | — | +| #275 | [CODE-REVIEW-275-r1.md](CODE-REVIEW-275-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #274 | [SPEC-REVIEW-274-r1.md](SPEC-REVIEW-274-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #274 | [CODE-REVIEW-274-r1.md](CODE-REVIEW-274-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #273 | [SPEC-REVIEW-273-r1.md](SPEC-REVIEW-273-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #273 | [SPEC-REVIEW-273-r2.md](SPEC-REVIEW-273-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #273 | [CODE-REVIEW-273-r1.md](CODE-REVIEW-273-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #272 | [SPEC-REVIEW-272-r1.md](SPEC-REVIEW-272-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #272 | [SPEC-REVIEW-272-r2.md](SPEC-REVIEW-272-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #272 | [CODE-REVIEW-272-r1.md](CODE-REVIEW-272-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #271 | [SPEC-REVIEW-271-r1.md](SPEC-REVIEW-271-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #271 | [CODE-REVIEW-271-r1.md](CODE-REVIEW-271-r1.md) | code · r1 | 🔴 красный | 1 | 1 | — | +| #271 | [CODE-REVIEW-271-r2.md](CODE-REVIEW-271-r2.md) | code · r2 | 🟢 зелёный | 1 | 1 | — | +| #271 | [CODE-REVIEW-271-r3.md](CODE-REVIEW-271-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #269 | [CODE-REVIEW-269-r1.md](CODE-REVIEW-269-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #267 | [SPEC-REVIEW-267-r1.md](SPEC-REVIEW-267-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #267 | [CODE-REVIEW-267-r1.md](CODE-REVIEW-267-r1.md) | code · r1 | 🔴 красный | 0 | 0 | — | +| #267 | [CODE-REVIEW-267-r2.md](CODE-REVIEW-267-r2.md) | code · r2 | 🔴 красный | 0 | 0 | — | +| #266 | [SPEC-REVIEW-266-r1.md](SPEC-REVIEW-266-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | Medium (в скоупе) — заявленный «объективный критерий» (golden) не видит два реальных CS…; Low — неверная классификация вспомогательного скрипта; Low (наблюдение, не требует правки ТЗ) — лимит «≤ 1200 строк» на файл (AC2) может не вы… | +| #266 | [SPEC-REVIEW-266-r2.md](SPEC-REVIEW-266-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | Medium (в скоупе) — новый AC2 противоречит сам себе: заявленный порог суммы уже нарушен… | +| #266 | [SPEC-REVIEW-266-r3.md](SPEC-REVIEW-266-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 0 | — | +| #266 | [CODE-REVIEW-266-r1.md](CODE-REVIEW-266-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #265 | [SPEC-REVIEW-265-r1.md](SPEC-REVIEW-265-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 4 | «что человек увидит» слито со сценарием и написано языком реализации; поверхность/touch…; AC10 хеджирует «если отчёт станет виден пользователю», хотя §9 безусловно описывает нов…; AC1–AC9 не называют способ доказательства (в отличие от требования DoR и от прямого сос…; нет отдельного обязательного раздела «Риски» | +| #265 | [SPEC-REVIEW-265-r2.md](SPEC-REVIEW-265-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | AC10 называет несуществующий гейт check-i18n как обязательное доказательство (Medium, в… | +| #265 | [SPEC-REVIEW-265-r3.md](SPEC-REVIEW-265-r3.md) | spec · r3 | 🟢 зелёный | 0 | 0 | — | +| #265 | [CODE-REVIEW-265-r1.md](CODE-REVIEW-265-r1.md) | code · r1 | 🔴 красный | 1 | 2 | — | +| #264 | [SPEC-REVIEW-264-r1.md](SPEC-REVIEW-264-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #264 | [SPEC-REVIEW-264-r2.md](SPEC-REVIEW-264-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #264 | [CODE-REVIEW-264-r1.md](CODE-REVIEW-264-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #262 | [SPEC-REVIEW-262-r1.md](SPEC-REVIEW-262-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #262 | [CODE-REVIEW-262-r1.md](CODE-REVIEW-262-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #261 | [SPEC-REVIEW-261-r1.md](SPEC-REVIEW-261-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #261 | [CODE-REVIEW-261-r1.md](CODE-REVIEW-261-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #258 | [SPEC-REVIEW-258-r1.md](SPEC-REVIEW-258-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #258 | [CODE-REVIEW-258-r1.md](CODE-REVIEW-258-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #258 | [CODE-REVIEW-258-r2.md](CODE-REVIEW-258-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #256 | [CODE-REVIEW-256-r1.md](CODE-REVIEW-256-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #253 | [SPEC-REVIEW-253-r1.md](SPEC-REVIEW-253-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #253 | [CODE-REVIEW-253-r1.md](CODE-REVIEW-253-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #252 | [SPEC-REVIEW-252-r1.md](SPEC-REVIEW-252-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #252 | [SPEC-REVIEW-252-r2.md](SPEC-REVIEW-252-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #252 | [CODE-REVIEW-252-r1.md](CODE-REVIEW-252-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #252 | [CODE-REVIEW-252-r2.md](CODE-REVIEW-252-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #251 | [SPEC-REVIEW-251-r1.md](SPEC-REVIEW-251-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | не проговорено соответствие toast'а формулировке владельца «стандартными средствами HA»; не решён смешанный secure + unavailable случай в группе controls | +| #251 | [CODE-REVIEW-251-r1.md](CODE-REVIEW-251-r1.md) | code · r1 | ⚪ — | 1 | 0 | — | +| #251 | [CODE-REVIEW-251-r2.md](CODE-REVIEW-251-r2.md) | code · r2 | 🔴 красный | 1 | 0 | — | +| #251 | [CODE-REVIEW-251-r3.md](CODE-REVIEW-251-r3.md) | code · r3 | 🔴 красный | 1 | 0 | — | +| #250 | [SPEC-REVIEW-250-r1.md](SPEC-REVIEW-250-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #250 | [CODE-REVIEW-250-r1.md](CODE-REVIEW-250-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #250 | [CODE-REVIEW-250-r2.md](CODE-REVIEW-250-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #249 | [SPEC-REVIEW-249-r1.md](SPEC-REVIEW-249-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #249 | [SPEC-REVIEW-249-r2.md](SPEC-REVIEW-249-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #249 | [CODE-REVIEW-249-r1.md](CODE-REVIEW-249-r1.md) | code · r1 | 🔴 красный | 1 | 1 | — | +| #249 | [CODE-REVIEW-249-r2.md](CODE-REVIEW-249-r2.md) | code · r2 | ⚪ — | 0 | 0 | — | +| #248 | [SPEC-REVIEW-248-r1.md](SPEC-REVIEW-248-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #248 | [CODE-REVIEW-248-r1.md](CODE-REVIEW-248-r1.md) | code · r1 | 🟢 зелёный | 1 | 0 | — | +| #244 | [SPEC-REVIEW-244-r1.md](SPEC-REVIEW-244-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #244 | [SPEC-REVIEW-244-r2.md](SPEC-REVIEW-244-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #244 | [CODE-REVIEW-244-r1.md](CODE-REVIEW-244-r1.md) | code · r1 | 🔴 красный | 2 | 0 | — | +| #244 | [CODE-REVIEW-244-r2.md](CODE-REVIEW-244-r2.md) | code · r2 | ⚪ — | 2 | 0 | — | +| #244 | [CODE-REVIEW-244-r3.md](CODE-REVIEW-244-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #243 | [SPEC-REVIEW-243-r1.md](SPEC-REVIEW-243-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #243 | [CODE-REVIEW-243-r1.md](CODE-REVIEW-243-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #243 | [CODE-REVIEW-243-r2.md](CODE-REVIEW-243-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #242 | [SPEC-REVIEW-242-r1.md](SPEC-REVIEW-242-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #242 | [CODE-REVIEW-242-r1.md](CODE-REVIEW-242-r1.md) | code · r1 | 🟡 жёлтый | 1 | 1 | — | +| #242 | [CODE-REVIEW-242-r2.md](CODE-REVIEW-242-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #239 | [SPEC-REVIEW-239-r1.md](SPEC-REVIEW-239-r1.md) | spec · r1 | ⚪ — | 0 | 0 | — | +| #239 | [SPEC-REVIEW-239-r2.md](SPEC-REVIEW-239-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #239 | [CODE-REVIEW-239-r1.md](CODE-REVIEW-239-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #239 | [CODE-REVIEW-239-r2.md](CODE-REVIEW-239-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #238 | [SPEC-REVIEW-238-r1.md](SPEC-REVIEW-238-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #238 | [CODE-REVIEW-238-r1.md](CODE-REVIEW-238-r1.md) | code · r1 | 🔴 красный | 1 | 0 | Продуктовый коммит не обновил скомпилированный бандл; закоммиченный артефакт не содержи… | +| #238 | [CODE-REVIEW-238-r2.md](CODE-REVIEW-238-r2.md) | code · r2 | 🔴 красный | 0 | 0 | — | +| #238 | [CODE-REVIEW-238-r3.md](CODE-REVIEW-238-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #234 | [SPEC-REVIEW-234-r1.md](SPEC-REVIEW-234-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #234 | [SPEC-REVIEW-234-r2.md](SPEC-REVIEW-234-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #234 | [CODE-REVIEW-234-r2.md](CODE-REVIEW-234-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | Диагноз и контракт по коду; Гейты — прогнаны на HEAD (c8e9597) в этой сессии; AC — чем доказано; Взаимодействие с #229 (ребейз) | +| #233 | [SPEC-REVIEW-233-r1.md](SPEC-REVIEW-233-r1.md) | spec · r1 | ⚪ — | 2 | 0 | Контракт §6 не покрывает открытую сторону, к которой ведёт продуктовое решение §4.3 (High); §7 приписывает thicknessCmAt поведение, которого у неё нет: заявлено фактом, не провере… | +| #233 | [SPEC-REVIEW-233-r2.md](SPEC-REVIEW-233-r2.md) | spec · r2 | 🔴 красный | 0 | 0 | — | +| #233 | [CODE-REVIEW-233-r1.md](CODE-REVIEW-233-r1.md) | code · r1 | 🟡 жёлтый | 0 | 2 | тест AC6b не строит сценарий, который он заявляет (и который был H2); не хватает мутанта из принятого §11 ТЗ | +| #233 | [CODE-REVIEW-233-r2.md](CODE-REVIEW-233-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | — | +| #231 | [SPEC-REVIEW-231-r1.md](SPEC-REVIEW-231-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #231 | [SPEC-REVIEW-231-r2.md](SPEC-REVIEW-231-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #231 | [CODE-REVIEW-231-r1.md](CODE-REVIEW-231-r1.md) | code · r1 | ⚪ — | 0 | 1 | — | +| #231 | [CODE-REVIEW-231-r2.md](CODE-REVIEW-231-r2.md) | code · r2 | ⚪ — | 0 | 0 | — | +| #230 | [SPEC-REVIEW-230-r1.md](SPEC-REVIEW-230-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #230 | [SPEC-REVIEW-230-r2.md](SPEC-REVIEW-230-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #230 | [CODE-REVIEW-230-r1.md](CODE-REVIEW-230-r1.md) | code · r1 | 🟡 жёлтый | 0 | 0 | — | +| #230 | [CODE-REVIEW-230-r2.md](CODE-REVIEW-230-r2.md) | code · r2 | 🟡 жёлтый | 2 | 0 | — | +| #229 | [SPEC-REVIEW-229-r1.md](SPEC-REVIEW-229-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #229 | [SPEC-REVIEW-229-r2.md](SPEC-REVIEW-229-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #229 | [SPEC-REVIEW-229-r3.md](SPEC-REVIEW-229-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 0 | — | +| #229 | [CODE-REVIEW-229-r1.md](CODE-REVIEW-229-r1.md) | code · r1 | 🔴 красный | 0 | 0 | — | +| #229 | [CODE-REVIEW-229-r2.md](CODE-REVIEW-229-r2.md) | code · r2 | 🟡 жёлтый | 0 | 1 | — | +| #229 | [CODE-REVIEW-229-r3.md](CODE-REVIEW-229-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #228 | [SPEC-REVIEW-228-r1.md](SPEC-REVIEW-228-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #228 | [CODE-REVIEW-228-r1.md](CODE-REVIEW-228-r1.md) | code · r1 | 🟡 жёлтый | 0 | 2 | — | +| #228 | [CODE-REVIEW-228-r2.md](CODE-REVIEW-228-r2.md) | code · r2 | 🟢 зелёный | 0 | 2 | — | +| #228 | [CODE-REVIEW-228-r3.md](CODE-REVIEW-228-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #226 | [SPEC-REVIEW-226-r1.md](SPEC-REVIEW-226-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #226 | [SPEC-REVIEW-226-r2.md](SPEC-REVIEW-226-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #226 | [CODE-REVIEW-226-r1.md](CODE-REVIEW-226-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #225 | [SPEC-REVIEW-225-r1.md](SPEC-REVIEW-225-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #225 | [SPEC-REVIEW-225-r2.md](SPEC-REVIEW-225-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #225 | [CODE-REVIEW-225-r1.md](CODE-REVIEW-225-r1.md) | code · r1 | 🟡 жёлтый | 0 | 2 | — | +| #225 | [CODE-REVIEW-225-r2.md](CODE-REVIEW-225-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #224 | [SPEC-REVIEW-224-r1.md](SPEC-REVIEW-224-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #224 | [CODE-REVIEW-224-r1.md](CODE-REVIEW-224-r1.md) | code · r1 | 🔴 красный | 1 | 2 | golden не остаётся прежним, вопреки AC12 и §2/§15 ТЗ (High, в скоупе); check-docs.mjs красный: отпечаток скриншотов не обновлён (Medium, в скоупе); AC6 заявляет доказательство, которого нет (Medium, в скоупе); двойной инкремент cfgEpoch в writeConfig (Low) | +| #224 | [CODE-REVIEW-224-r2.md](CODE-REVIEW-224-r2.md) | code · r2 | 🔴 красный | 0 | 0 | — | +| #223 | [SPEC-REVIEW-223-r1.md](SPEC-REVIEW-223-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #223 | [SPEC-REVIEW-223-r2.md](SPEC-REVIEW-223-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 2 | — | +| #223 | [SPEC-REVIEW-223-r3.md](SPEC-REVIEW-223-r3.md) | spec · r3 | 🟢 зелёный | 0 | 1 | — | +| #223 | [CODE-REVIEW-223-r1.md](CODE-REVIEW-223-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #220 | [SPEC-REVIEW-220-r1.md](SPEC-REVIEW-220-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | Medium, в скоупе. Отсутствует обязательная touch-классификация; Medium, в скоупе. §9 утверждает «нет нового поля», хотя §8.3/§17.3 предполагают обратное | +| #220 | [SPEC-REVIEW-220-r2.md](SPEC-REVIEW-220-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | Medium, в скоупе. §17.3 маркирует нормативное требование как «свободно меняемое» | +| #220 | [SPEC-REVIEW-220-r3.md](SPEC-REVIEW-220-r3.md) | spec · r3 | 🟢 зелёный | 0 | 0 | — | +| #220 | [CODE-REVIEW-220-r1.md](CODE-REVIEW-220-r1.md) | code · r1 | 🔴 красный | 1 | 2 | — | +| #219 | [SPEC-REVIEW-219-r1.md](SPEC-REVIEW-219-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #219 | [SPEC-REVIEW-219-r2.md](SPEC-REVIEW-219-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | — | +| #219 | [CODE-REVIEW-219-r1.md](CODE-REVIEW-219-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #218 | [SPEC-REVIEW-218-r1.md](SPEC-REVIEW-218-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #218 | [SPEC-REVIEW-218-r2.md](SPEC-REVIEW-218-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #218 | [CODE-REVIEW-218-r1.md](CODE-REVIEW-218-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #217 | [SPEC-REVIEW-217-r1.md](SPEC-REVIEW-217-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #217 | [CODE-REVIEW-217-r1.md](CODE-REVIEW-217-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #213 | [SPEC-REVIEW-213-r1.md](SPEC-REVIEW-213-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #213 | [CODE-REVIEW-213-r1.md](CODE-REVIEW-213-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #212 | [SPEC-REVIEW-212-r1.md](SPEC-REVIEW-212-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #212 | [CODE-REVIEW-212-r1.md](CODE-REVIEW-212-r1.md) | code · r1 | 🟡 жёлтый | 0 | 2 | три существующих regression-смока сломаны миграцией mouse→pointer событий (Medium, в ск…; hp-device-preview.ts гейтирует hover, но никто не выставляет ему атрибут (Medium, в ско… | +| #212 | [CODE-REVIEW-212-r2.md](CODE-REVIEW-212-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | исправлено, доказано реальным поведением, не только тестом; исправлено, доказано смоком, который умеет упасть | +| #211 | [SPEC-REVIEW-211-r1.md](SPEC-REVIEW-211-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | целевое соотношение «MDI viewport = 0.5 × core» не подтверждено ни одним источником арх…; §4 называет нормативным источником несуществующий файл COMPARISONNOTES.md; обязательный тег Touch editor: … присутствует по смыслу, но не дословно | +| #211 | [SPEC-REVIEW-211-r2.md](SPEC-REVIEW-211-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #211 | [CODE-REVIEW-211-r1.md](CODE-REVIEW-211-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #211 | [CODE-REVIEW-211-r2.md](CODE-REVIEW-211-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #210 | [SPEC-REVIEW-210-r1.md](SPEC-REVIEW-210-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | контракт не говорит явно, что очистка floor в GUI должна удалять ключ, а не записывать …; обязательный раздел «i18n» присутствует по содержанию, но не оформлен отдельным заголовком | +| #210 | [SPEC-REVIEW-210-r2.md](SPEC-REVIEW-210-r2.md) | spec · r2 | 🟢 зелёный | 0 | 1 | — | +| #210 | [CODE-REVIEW-210-r1.md](CODE-REVIEW-210-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #209 | [SPEC-REVIEW-209-r1.md](SPEC-REVIEW-209-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #209 | [CODE-REVIEW-209-r1.md](CODE-REVIEW-209-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #205 | [SPEC-REVIEW-205-r1.md](SPEC-REVIEW-205-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #205 | [CODE-REVIEW-205-r1.md](CODE-REVIEW-205-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #205 | [CODE-REVIEW-205-r2.md](CODE-REVIEW-205-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #204 | [SPEC-REVIEW-204-r1.md](SPEC-REVIEW-204-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #204 | [CODE-REVIEW-204-r1.md](CODE-REVIEW-204-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #203 | [SPEC-REVIEW-203-r1.md](SPEC-REVIEW-203-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #203 | [SPEC-REVIEW-203-r2.md](SPEC-REVIEW-203-r2.md) | spec · r2 | 🟢 зелёный | 0 | 1 | — | +| #203 | [CODE-REVIEW-203-r1.md](CODE-REVIEW-203-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #201 | [SPEC-REVIEW-201-r1.md](SPEC-REVIEW-201-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #201 | [CODE-REVIEW-201-r1.md](CODE-REVIEW-201-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #200 | [SPEC-REVIEW-200-r1.md](SPEC-REVIEW-200-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #200 | [CODE-REVIEW-200-r1.md](CODE-REVIEW-200-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #199 | [SPEC-REVIEW-199-r1.md](SPEC-REVIEW-199-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #199 | [SPEC-REVIEW-199-r2.md](SPEC-REVIEW-199-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #199 | [CODE-REVIEW-199-r1.md](CODE-REVIEW-199-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #198 | [SPEC-REVIEW-198-r1.md](SPEC-REVIEW-198-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #198 | [CODE-REVIEW-198-r1.md](CODE-REVIEW-198-r1.md) | code · r1 | 🟡 жёлтый | 0 | 2 | — | +| #198 | [CODE-REVIEW-198-r2.md](CODE-REVIEW-198-r2.md) | code · r2 | 🟡 жёлтый | 0 | 1 | — | +| #198 | [CODE-REVIEW-198-r3.md](CODE-REVIEW-198-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #197 | [SPEC-REVIEW-197-r1.md](SPEC-REVIEW-197-r1.md) | spec · r1 | 🔴 красный | 1 | 1 | — | +| #197 | [SPEC-REVIEW-197-r3.md](SPEC-REVIEW-197-r3.md) | spec · r3 | 🟢 зелёный | 0 | 0 | — | +| #197 | [CODE-REVIEW-197-r1.md](CODE-REVIEW-197-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #196 | [SPEC-REVIEW-196-r1.md](SPEC-REVIEW-196-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #196 | [CODE-REVIEW-196-r1.md](CODE-REVIEW-196-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #195 | [CODE-REVIEW-195-r1.md](CODE-REVIEW-195-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #195 | [CODE-REVIEW-195-r2.md](CODE-REVIEW-195-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #193 | [SPEC-REVIEW-193-r1.md](SPEC-REVIEW-193-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #193 | [CODE-REVIEW-193-r1.md](CODE-REVIEW-193-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #192 | [SPEC-REVIEW-192-r1.md](SPEC-REVIEW-192-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #192 | [CODE-REVIEW-192-r1.md](CODE-REVIEW-192-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #192 | [CODE-REVIEW-192-r2.md](CODE-REVIEW-192-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #189 | [CODE-REVIEW-189-r1.md](CODE-REVIEW-189-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #187 | [CODE-REVIEW-187-r1.md](CODE-REVIEW-187-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #186 | [SPEC-REVIEW-186-r1.md](SPEC-REVIEW-186-r1.md) | spec · r1 | 🔴 красный | 1 | 0 | — | +| #186 | [SPEC-REVIEW-186-r2.md](SPEC-REVIEW-186-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #186 | [CODE-REVIEW-186-r1.md](CODE-REVIEW-186-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #184 | [CODE-REVIEW-184-r1.md](CODE-REVIEW-184-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #180 | [SPEC-REVIEW-180-r1.md](SPEC-REVIEW-180-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #180 | [CODE-REVIEW-180-r1.md](CODE-REVIEW-180-r1.md) | code · r1 | 🔴 красный | 1 | 0 | — | +| #180 | [CODE-REVIEW-180-r2.md](CODE-REVIEW-180-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #179 | [SPEC-REVIEW-179-r1.md](SPEC-REVIEW-179-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 5 | цвет внешней тени искажён относительно нормативного источника; цвет continuous-пульсации для reason presence не определён; easing для Short и Alert не зафиксирован; стоимость и выбор backdrop-blur для Dark не решены; не назван обязательный тег Touch editor: из TOUCH-SUPPORT.md; Light-only combo-эталоны (Selected + Hover/Working/Green/Alert, | +| #179 | [SPEC-REVIEW-179-r2.md](SPEC-REVIEW-179-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | Обе Light-тени переписаны на rgb(37 40 45 / 12%) / rgb(37 40 45 / 18%); §10.1/§10.4 получили mapping presence→green, running/working/open/unlocked→amber, neutr…; Обе секции получили cubic-bezier(.22,.61,.36,1); §7.1 фиксирует production = Dark/Icon Default No Blur.svg, backdrop-filter запрещён; ри…; Добавлена строка Touch editor: best effort / intentionally degraded в §11 | +| #179 | [CODE-REVIEW-179-r1.md](CODE-REVIEW-179-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #179 | [CODE-REVIEW-179-r2.md](CODE-REVIEW-179-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #178 | [SPEC-REVIEW-178-r1.md](SPEC-REVIEW-178-r1.md) | spec · r1 | 🔴 красный | 1 | 0 | — | +| #178 | [SPEC-REVIEW-178-r2.md](SPEC-REVIEW-178-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #178 | [CODE-REVIEW-178-r1.md](CODE-REVIEW-178-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #176 | [SPEC-REVIEW-176-r1.md](SPEC-REVIEW-176-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #176 | [CODE-REVIEW-176-r1.md](CODE-REVIEW-176-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #174 | [SPEC-REVIEW-174-r1.md](SPEC-REVIEW-174-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #174 | [CODE-REVIEW-174-r1.md](CODE-REVIEW-174-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #173 | [SPEC-REVIEW-173-r1.md](SPEC-REVIEW-173-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #173 | [CODE-REVIEW-173-r1.md](CODE-REVIEW-173-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #173 | [CODE-REVIEW-173-r2.md](CODE-REVIEW-173-r2.md) | code · r2 | 🟢 зелёный | 0 | 2 | — | +| #172 | [SPEC-REVIEW-172-r1.md](SPEC-REVIEW-172-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #172 | [CODE-REVIEW-172-r1.md](CODE-REVIEW-172-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #172 | [CODE-REVIEW-172-r2.md](CODE-REVIEW-172-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #162 | [SPEC-REVIEW-162-r1.md](SPEC-REVIEW-162-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #162 | [SPEC-REVIEW-162-r2.md](SPEC-REVIEW-162-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #162 | [CODE-REVIEW-162-r1.md](CODE-REVIEW-162-r1.md) | code · r1 | 🔴 красный | 1 | 1 | — | +| #162 | [CODE-REVIEW-162-r2.md](CODE-REVIEW-162-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #160 | [SPEC-REVIEW-160-r1.md](SPEC-REVIEW-160-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #160 | [CODE-REVIEW-160-r1.md](CODE-REVIEW-160-r1.md) | code · r1 | 🔴 красный | 1 | 3 | node scripts/check-docs.mjs красный: скриншоты документации не пересняты (High, блокирует); floor anchor opening lock badge вычисляется двумя независимыми формулами (Medium, в ско…; реальный алгоритм isoRoomSafePoint (grid-search fallback) не покрыт ни одним тестом (Me…; leafBasis (src/iso-openings.ts:187-209): flipH не тестируется независимо от flipV (Medi… | +| #160 | [CODE-REVIEW-160-r2.md](CODE-REVIEW-160-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | новый M2-тест не независимо доказывает «вне любой дыры» (Low, запись без блокировки); приёмка скриншотов сменила заявленную платформу на win32 без видимого основания в хендо… | +| #159 | [SPEC-REVIEW-159-r1.md](SPEC-REVIEW-159-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #159 | [CODE-REVIEW-159-r1.md](CODE-REVIEW-159-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #159 | [CODE-REVIEW-159-r2.md](CODE-REVIEW-159-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #156 | [CODE-REVIEW-156-r1.md](CODE-REVIEW-156-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #152 | [SPEC-REVIEW-152-r1.md](SPEC-REVIEW-152-r1.md) | spec · r1 | 🔴 красный | 2 | 2 | — | +| #152 | [SPEC-REVIEW-152-r2.md](SPEC-REVIEW-152-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #152 | [CODE-REVIEW-152-r1.md](CODE-REVIEW-152-r1.md) | code · r1 | 🔴 красный | 1 | 2 | — | +| #152 | [CODE-REVIEW-152-r2.md](CODE-REVIEW-152-r2.md) | code · r2 | 🔴 красный | 1 | 2 | — | +| #150 | [CODE-REVIEW-150-r1.md](CODE-REVIEW-150-r1.md) | code · r1 | 🔴 красный | 1 | 0 | — | +| #146 | [SPEC-REVIEW-146-r1.md](SPEC-REVIEW-146-r1.md) | spec · r1 | 🟢 зелёный | 0 | 1 | — | +| #146 | [CODE-REVIEW-146-r1.md](CODE-REVIEW-146-r1.md) | code · r1 | 🔴 красный | 1 | 0 | — | +| #146 | [CODE-REVIEW-146-r2.md](CODE-REVIEW-146-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #141 | [SPEC-REVIEW-141-r1.md](SPEC-REVIEW-141-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #141 | [CODE-REVIEW-141-r1.md](CODE-REVIEW-141-r1.md) | code · r1 | 🔴 красный | 1 | 0 | — | +| #140 | [CODE-REVIEW-140-r1.md](CODE-REVIEW-140-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #138 | [SPEC-REVIEW-138-r1.md](SPEC-REVIEW-138-r1.md) | spec · r1 | 🔴 красный | 1 | 0 | — | +| #138 | [SPEC-REVIEW-138-r2.md](SPEC-REVIEW-138-r2.md) | spec · r2 | 🔴 красный | 1 | 0 | — | +| #137 | [SPEC-REVIEW-137-r1.md](SPEC-REVIEW-137-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #132 | [SPEC-REVIEW-132-r1.md](SPEC-REVIEW-132-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #132 | [CODE-REVIEW-132-r1.md](CODE-REVIEW-132-r1.md) | code · r1 | 🔴 красный | 1 | 3 | — | +| #132 | [CODE-REVIEW-132-r2.md](CODE-REVIEW-132-r2.md) | code · r2 | 🟢 зелёный | 1 | 3 | — | +| #131 | [SPEC-REVIEW-131-r1.md](SPEC-REVIEW-131-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #126 | [SPEC-REVIEW-126-r1.md](SPEC-REVIEW-126-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #126 | [SPEC-REVIEW-126-r2.md](SPEC-REVIEW-126-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #126 | [CODE-REVIEW-126-r1.md](CODE-REVIEW-126-r1.md) | code · r1 | 🟡 жёлтый | 1 | 1 | — | +| #126 | [CODE-REVIEW-126-r2.md](CODE-REVIEW-126-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #123 | [SPEC-REVIEW-123-r1.md](SPEC-REVIEW-123-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #123 | [CODE-REVIEW-123-r1.md](CODE-REVIEW-123-r1.md) | code · r1 | 🔴 красный | 1 | 0 | — | +| #122 | [SPEC-REVIEW-122-r1.md](SPEC-REVIEW-122-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #122 | [CODE-REVIEW-122-r1.md](CODE-REVIEW-122-r1.md) | code · r1 | 🔴 красный | 2 | 1 | — | +| #122 | [CODE-REVIEW-122-r2.md](CODE-REVIEW-122-r2.md) | code · r2 | 🟢 зелёный | 2 | 1 | — | +| #117 | [SPEC-REVIEW-117-r1.md](SPEC-REVIEW-117-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #117 | [CODE-REVIEW-117-r1.md](CODE-REVIEW-117-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #117 | [CODE-REVIEW-117-r2.md](CODE-REVIEW-117-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #113 | [SPEC-REVIEW-113-r1.md](SPEC-REVIEW-113-r1.md) | spec · r1 | 🟢 зелёный | 0 | 1 | — | +| #113 | [CODE-REVIEW-113-r1.md](CODE-REVIEW-113-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #113 | [CODE-REVIEW-113-r2.md](CODE-REVIEW-113-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #113 | [CODE-REVIEW-113-r3.md](CODE-REVIEW-113-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #107 | [SPEC-REVIEW-107-r1.md](SPEC-REVIEW-107-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #107 | [CODE-REVIEW-107-r1.md](CODE-REVIEW-107-r1.md) | code · r1 | 🔴 красный | 1 | 1 | — | +| #103 | [SPEC-REVIEW-103-r1.md](SPEC-REVIEW-103-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #103 | [CODE-REVIEW-103-r1.md](CODE-REVIEW-103-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #94 | [CODE-REVIEW-issue-094-2026-08-12.md](CODE-REVIEW-issue-094-2026-08-12.md) | code · 2026-08-12 | ⚪ — | 0 | 0 | — | +| #89 | [SPEC-REVIEW-089-isometric-view-stage1.md](SPEC-REVIEW-089-isometric-view-stage1.md) | spec · isometric-view-stage1 | 🔴 красный | 0 | 8 | Spike ADR должен фиксировать больше, чем выбор renderer; Fingerprint перечисляет не все входы iso geometry; since/expires требуют точной version semantics; Не определён runtime owner механизма Labs; Scope houseplan-space-card не указан; Performance contract не совпадает с существующей инфраструктурой | +| #89 | [SPEC-REVIEW-89-r1.md](SPEC-REVIEW-89-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | риски не собраны в отдельный раздел; «нормативная» таблица слоёв формально противоречит собственному запрету ТЗ на новый око… | +| #89 | [CODE-REVIEW-89-r2.md](CODE-REVIEW-89-r2.md) | code · r2 | 🔴 красный | 1 | 0 | обязательный по ТЗ смок demo/smokeisometriclivetouch.mjs красный на сданном коммите; три файла с лишней пустой строкой в конце (git diff --check) | +| #89 | [CODE-REVIEW-89-r3.md](CODE-REVIEW-89-r3.md) | code · r3 | 🟢 зелёный | 0 | 0 | — | +| #86 | [CODE-REVIEW-86-r1.md](CODE-REVIEW-86-r1.md) | code · r1 | ⚪ — | 2 | 1 | Манифест скриншотов документации подделан, а не пересчитан (High, в скоупе); npm run golden:verify красный: 6 эталонов расходятся с рендером (High, в скоупе); Onboarding-путь 5 space-подсказок не доказан исполнением (Medium, в скоупе); .help-ключ не совпадает буквально с ключом видимого label (Low, снимается без правки) | +| #86 | [CODE-REVIEW-86-r2.md](CODE-REVIEW-86-r2.md) | code · r2 | 🟡 жёлтый | 0 | 1 | AC7 «browser zoom 200%» не доказан ничем (Medium, в скоупе); .help-ключ не совпадает буквально с ключом видимого label (Low, снимается без правки) | +| #86 | [CODE-REVIEW-86-r3.md](CODE-REVIEW-86-r3.md) | code · r3 | 🟡 жёлтый | 0 | 1 | AC7 «представительный reviewed golden» для browser zoom 200% так и не появился (Medium,…; .help-ключ не совпадает буквально с ключом видимого label (Low, снимается без правки) | +| #86 | [CODE-REVIEW-86-r4.md](CODE-REVIEW-86-r4.md) | code · r4 | 🟢 зелёный | 0 | 0 | — | +| #86 | [CODE-REVIEW-86-r5.md](CODE-REVIEW-86-r5.md) | code · r5 | 🟢 зелёный | 0 | 0 | — | +| #82 | [SPEC-REVIEW-82-r1.md](SPEC-REVIEW-82-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #82 | [CODE-REVIEW-82-r1.md](CODE-REVIEW-82-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #82 | [CODE-REVIEW-82-r2.md](CODE-REVIEW-82-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #74 | [SPEC-REVIEW-74-r1.md](SPEC-REVIEW-74-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #74 | [CODE-REVIEW-74-r1.md](CODE-REVIEW-74-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #74 | [CODE-REVIEW-74-r2.md](CODE-REVIEW-74-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #68 | [CODE-REVIEW-issue-068-2026-08-12.md](CODE-REVIEW-issue-068-2026-08-12.md) | code · 2026-08-12 | ⚪ — | 0 | 0 | — | +| #62 | [SPEC-REVIEW-62-r1.md](SPEC-REVIEW-62-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #62 | [SPEC-REVIEW-62-r2.md](SPEC-REVIEW-62-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 1 | — | +| #62 | [CODE-REVIEW-62-r1.md](CODE-REVIEW-62-r1.md) | code · r1 | 🔴 красный | 1 | 0 | — | +| #62 | [CODE-REVIEW-62-r2.md](CODE-REVIEW-62-r2.md) | code · r2 | 🔴 красный | 1 | 0 | — | +| #57 | [SPEC-REVIEW-57-r1.md](SPEC-REVIEW-57-r1.md) | spec · r1 | 🟢 зелёный | 0 | 1 | — | +| #57 | [CODE-REVIEW-57-r1.md](CODE-REVIEW-57-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #54 | [SPEC-REVIEW-54-r1.md](SPEC-REVIEW-54-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | — | +| #54 | [SPEC-REVIEW-54-r2.md](SPEC-REVIEW-54-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #54 | [CODE-REVIEW-54-r1.md](CODE-REVIEW-54-r1.md) | code · r1 | 🟡 жёлтый | 0 | 1 | — | +| #54 | [CODE-REVIEW-54-r2.md](CODE-REVIEW-54-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #53 | [SPEC-REVIEW-53-r1.md](SPEC-REVIEW-53-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 4 | — | +| #53 | [SPEC-REVIEW-53-r2.md](SPEC-REVIEW-53-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #53 | [CODE-REVIEW-53-r1.md](CODE-REVIEW-53-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | +| #51 | [SPEC-REVIEW-51-r1.md](SPEC-REVIEW-51-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #51 | [CODE-REVIEW-51-r1.md](CODE-REVIEW-51-r1.md) | code · r1 | ⚪ — | 0 | 0 | — | +| #51 | [CODE-REVIEW-51-r2.md](CODE-REVIEW-51-r2.md) | code · r2 | 🟡 жёлтый | 1 | 3 | — | +| #51 | [CODE-REVIEW-51-r3.md](CODE-REVIEW-51-r3.md) | code · r3 | 🟡 жёлтый | 0 | 1 | — | +| #51 | [CODE-REVIEW-51-r4.md](CODE-REVIEW-51-r4.md) | code · r4 | ⚪ — | 0 | 0 | — | +| #44 | [SPEC-REVIEW-44-r1.md](SPEC-REVIEW-44-r1.md) | spec · r1 | 🔴 красный | 2 | 0 | Сценарий и AC4 построены на неверном факте: причина «исключена; Контракт №3 («оба значения читаются из одного источника всеми; Название вкладки инбокса не совпадает с интерфейсным словарём | +| #44 | [SPEC-REVIEW-44-r2.md](SPEC-REVIEW-44-r2.md) | spec · r2 | 🔴 красный | 0 | 1 | Ревизия 3 не убрала два места, где документ прямо утверждает то, что H1 уже опроверг: п… | +| #44 | [SPEC-REVIEW-44-r3.md](SPEC-REVIEW-44-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 0 | — | +| #44 | [CODE-REVIEW-44-r1.md](CODE-REVIEW-44-r1.md) | code · r1 | 🟡 жёлтый | 0 | 2 | — | +| #43 | [SPEC-REVIEW-43-r1.md](SPEC-REVIEW-43-r1.md) | spec · r1 | ⚪ — | 0 | 0 | — | +| #43 | [SPEC-REVIEW-43-r2.md](SPEC-REVIEW-43-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #43 | [SPEC-REVIEW-43-r3.md](SPEC-REVIEW-43-r3.md) | spec · r3 | 🟢 зелёный | 0 | 0 | — | +| #43 | [SPEC-REVIEW-43-r4.md](SPEC-REVIEW-43-r4.md) | spec · r4 | 🟡 жёлтый | 0 | 0 | — | +| #43 | [SPEC-REVIEW-43-r5.md](SPEC-REVIEW-43-r5.md) | spec · r5 | 🟢 зелёный | 0 | 0 | — | +| #43 | [CODE-REVIEW-43-r1.md](CODE-REVIEW-43-r1.md) | code · r1 | 🟡 жёлтый | 0 | 3 | 1 Дешёвые гейты (прогнаны лично, HEAD 1ba53630); 2 Бэкенд Python (доступность харнесса); 3 Relay (scripts/support-relay/); 4 Смоки и golden; 5 Инварианты модели; 6 Одно число — один источник | +| #43 | [CODE-REVIEW-43-r2.md](CODE-REVIEW-43-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #42 | [SPEC-REVIEW-42-r1.md](SPEC-REVIEW-42-r1.md) | spec · r1 | ⚪ — | 0 | 0 | — | +| #42 | [SPEC-REVIEW-42-r2.md](SPEC-REVIEW-42-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #42 | [SPEC-REVIEW-42-r3.md](SPEC-REVIEW-42-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 0 | — | +| #42 | [SPEC-REVIEW-42-r4.md](SPEC-REVIEW-42-r4.md) | spec · r4 | 🟡 жёлтый | 0 | 0 | — | +| #42 | [CODE-REVIEW-42-r1.md](CODE-REVIEW-42-r1.md) | code · r1 | 🔴 красный | 0 | 0 | — | +| #42 | [CODE-REVIEW-42-r2.md](CODE-REVIEW-42-r2.md) | code · r2 | 🔴 красный | 2 | 0 | — | +| #42 | [CODE-REVIEW-42-r6.md](CODE-REVIEW-42-r6.md) | code · r6 | 🟡 жёлтый | 0 | 1 | — | +| #39 | [SPEC-REVIEW-39-r1.md](SPEC-REVIEW-39-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 4 | нет обязательных продуктовых разделов §7.1 «сценарий» и «что человек увидит»; i18n-раздел не перечисляет ключи, DoR §2.5 не закрыть; нет разделов «Риски» и «Release-артефакты», обязательных по §7.1; AC4 объединяет два разных момента наступления hard без единого UI-контракта | +| #39 | [SPEC-REVIEW-39-r2.md](SPEC-REVIEW-39-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 2 | — | +| #39 | [SPEC-REVIEW-39-r3.md](SPEC-REVIEW-39-r3.md) | spec · r3 | 🟡 жёлтый | 0 | 0 | — | +| #39 | [CODE-REVIEW-39-r1.md](CODE-REVIEW-39-r1.md) | code · r1 | 🟡 жёлтый | 0 | 3 | отмена гард-диалога во время busy не отменяет применение; «одно число, один источник» нарушено для HARDDIMENSION; AC8 (EXIF-ориентация) не доказан ни тестом, ни явной записью | +| #39 | [CODE-REVIEW-39-r2.md](CODE-REVIEW-39-r2.md) | code · r2 | 🟡 жёлтый | 0 | 0 | два из трёх новых stillCurrent()-чеков не имеют собственного мутанта | +| #33 | [SPEC-REVIEW-33-r1.md](SPEC-REVIEW-33-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 3 | статус implemented не существует ни в перечислении, ни в каноне; 4 из 7 «const-деклараций фронта» для parity-теста не существуют; AC6 описывает несуществующее поведение как «расширение теста» | +| #33 | [SPEC-REVIEW-33-r2.md](SPEC-REVIEW-33-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | +| #33 | [CODE-REVIEW-33-r1.md](CODE-REVIEW-33-r1.md) | code · r1 | 🟡 жёлтый | 0 | 3 | AC7-тест не может поймать то, для чего он написан; Дубликат записи в обоих CHANGELOG и в ARCHITECTURE.md; config-audit.mjs расширил exit-code 3 за пределы согласованного контракта, и для decisi… | +| #33 | [CODE-REVIEW-33-r2.md](CODE-REVIEW-33-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | AC6 не имеет штатного регрессионного теста на изолированные decision-required/drop-on-v… | +| #32 | [SPEC-REVIEW-32-r1.md](SPEC-REVIEW-32-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 0 | — | +| #32 | [SPEC-REVIEW-32-r2.md](SPEC-REVIEW-32-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #32 | [CODE-REVIEW-32-r1.md](CODE-REVIEW-32-r1.md) | code · r1 | 🟡 жёлтый | 1 | 0 | миграция ломает существующие смоки: 2 дают неверный вердикт, 3 виснут навсегда; шесть старых односложных ключей i18n остались без потребителей | +| #32 | [CODE-REVIEW-32-r2.md](CODE-REVIEW-32-r2.md) | code · r2 | 🟡 жёлтый | 1 | 0 | — | +| #29 | [SPEC-REVIEW-29-r1.md](SPEC-REVIEW-29-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | — | +| #29 | [SPEC-REVIEW-29-r2.md](SPEC-REVIEW-29-r2.md) | spec · r2 | 🟡 жёлтый | 0 | 0 | — | +| #29 | [CODE-REVIEW-29-r1.md](CODE-REVIEW-29-r1.md) | code · r1 | ⚪ — | 1 | 1 | demo/smokeeditortabs.mjs сломан этим диффом (High, в скоупе); «комната» в каталоге — это имя HA-зоны, а не название комнаты плана (Medium, в скоупе); DeviceInboxRow.canOpenHa — мёртвое поле (Low) | +| #29 | [CODE-REVIEW-29-r2.md](CODE-REVIEW-29-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | +| #20 | [SPEC-REVIEW-20-r1.md](SPEC-REVIEW-20-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | +| #20 | [CODE-REVIEW-20-r1.md](CODE-REVIEW-20-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | diff --git a/scripts/mutation-registry.mjs b/scripts/mutation-registry.mjs index 4f978dba..65b65a28 100644 --- a/scripts/mutation-registry.mjs +++ b/scripts/mutation-registry.mjs @@ -8560,6 +8560,29 @@ const MUTANT_DEFINITIONS = [ replace: " const codeRounds = completed.map((issue) => issue.s7Requests).filter(Boolean); // mutant: rounds = S7 events", }], }, + // #635: индекс ревью — база знаний; молчаливо неполный индекс хуже отсутствующего. + { + id: 'reviews-index-skips-self-check', + guard: 'node --test --test-name-pattern="#635 индекс покрывает" test/reviews-index.test.mjs', + because: 'the index must list every review document and never itself; indexing INDEX.md makes ' + + 'the generator non-deterministic and hides documents behind its own entry (#635)', + patches: [{ + file: 'scripts/reviews-index.mjs', + find: " const names = readdirSync(dir).filter((name) => name.endsWith('.md') && name !== INDEX_FILE).sort();", + replace: " const names = readdirSync(dir).filter((name) => name.endsWith('.md')).sort(); // mutant: indexes itself", + }], + }, + { + id: 'reviews-index-verdict-substring', + guard: 'node --test --test-name-pattern="#635 вердикт" test/reviews-index.test.mjs', + because: 'a colour word inside another word is not a verdict; JS \\b is ASCII-only, so the ' + + 'Cyrillic guard is an explicit negative lookahead (#635)', + patches: [{ + file: 'scripts/reviews-index.mjs', + find: "const VERDICT_LINE_RE = /(?:[Вв]ердикт|[Vv]erdict)[^\\n]{0,60}?\\**\\s*(зелёный|зеленый|жёлтый|желтый|красный|green|yellow|red)(?![а-яёa-z])/i;", + replace: "const VERDICT_LINE_RE = /(?:[Вв]ердикт|[Vv]erdict)[^\\n]{0,60}?\\**\\s*(зелёный|зеленый|жёлтый|желтый|красный|green|yellow|red)/i; // mutant: substring verdict", + }], + }, { id: 'process-reconcile-restarts-healthy-run', guard: 'node --test test/process-reconcile.test.mjs', diff --git a/scripts/reviews-index.mjs b/scripts/reviews-index.mjs new file mode 100644 index 00000000..6d17c14d --- /dev/null +++ b/scripts/reviews-index.mjs @@ -0,0 +1,159 @@ +#!/usr/bin/env node +// #635: индекс документов ревью — база знаний решений, которую можно прочитать +// за одно чтение. +// +// В `docs/reviews/` ≈ 1 000 документов; что находили по файлу, диалогу или +// подсистеме, узнать можно было только перечитав их. Индекс сводит каждый +// документ в одну строку: issue, этап, раунд, вердикт, число High/Medium и +// заголовки находок. Он генерируется (класс C), а не пишется руками, и +// пересобирается конвейером после публикации каждого документа ревью. +// +// node scripts/reviews-index.mjs [--dir=docs/reviews] [--output=docs/reviews/INDEX.md] [--check] +// +// `--check` — не писать, а сравнить с существующим файлом (гейт «индекс свеж»). +import { readdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs'; +import { join } from 'node:path'; +import { isMainModule } from './spawn-portable.mjs'; + +export const INDEX_FILE = 'INDEX.md'; +const DOC_NAME = /^(CODE|SPEC)-REVIEW-(?:issue-)?(\d+)(?:-r(\d+))?(?:-([a-z0-9-]+))?\.md$/i; +const COLOUR = { + 'зелёный': 'зелёный', 'зеленый': 'зелёный', green: 'зелёный', + 'жёлтый': 'жёлтый', 'желтый': 'жёлтый', yellow: 'жёлтый', + 'красный': 'красный', red: 'красный', +}; +const COLOUR_RE = /(зелёный|зеленый|жёлтый|желтый|красный|green|yellow|red)(?![а-яёa-z])/i; +const VERDICT_LINE_RE = /(?:[Вв]ердикт|[Vv]erdict)[^\n]{0,60}?\**\s*(зелёный|зеленый|жёлтый|желтый|красный|green|yellow|red)(?![а-яёa-z])/i; + +/** Разобрать имя документа: этап, issue, раунд. */ +export function parseDocName(name) { + const match = DOC_NAME.exec(String(name)); + if (!match) return null; + return { + stage: match[1].toUpperCase() === 'CODE' ? 'code' : 'spec', + issue: Number(match[2]), + round: match[3] ? Number(match[3]) : null, + suffix: match[4] || null, + }; +} + +/** Вердикт: явная строка «Вердикт: **цвет**», иначе — по разделу «Вердикт», иначе «—». */ +export function parseVerdict(text) { + const explicit = VERDICT_LINE_RE.exec(text); + if (explicit) return COLOUR[explicit[1].toLowerCase()]; + const heading = /^#{1,4}\s*(?:\d+\.\s*)?(?:Вердикт|Verdict|Итог)(?![а-яё])[^\n]*\n([\s\S]*?)(?=\n#{1,4}\s|(?![\s\S]))/m.exec(text); + if (heading) { + const section = heading[1]; + const colour = COLOUR_RE.exec(section); + if (colour) return COLOUR[colour[1].toLowerCase()]; + if (/блокиру|не принят|отклон|red/i.test(section)) return 'красный'; + if (/принят|принимается|готов|без замечаний|можно сливать|регрессий нет|proceed|approved/i.test(section)) return 'зелёный'; + } + // Старые документы пишут «зелёный вердикт» в свободной форме — ищем в хвосте. + const tail = /(зелёный|зеленый|жёлтый|желтый|красный|green|yellow|red)\**\s+вердикт/i.exec(text.slice(-2500)); + if (tail) return COLOUR[tail[1].toLowerCase()]; + return '—'; +} + +/** Числа High/Medium из строки вердикта либо из заголовков находок. */ +export function parseCounts(text) { + const high = /High:\s*(\d+)/.exec(text); + const medium = /Medium:\s*(\d+)/.exec(text); + if (high || medium) return { high: high ? Number(high[1]) : 0, medium: medium ? Number(medium[1]) : 0 }; + const headings = [...text.matchAll(/^#{2,4}\s*(H\d+|M\d+)\b/gm)].map((m) => m[1][0]); + return { high: headings.filter((h) => h === 'H').length, medium: headings.filter((h) => h === 'M').length }; +} + +/** + * Заголовки находок: `### 1. …`, `### H1 — …`, `### M2: …`, строки таблиц с + * severity в первых ячейках. Обрезаются до 90 символов; не больше `limit`. + */ +export function parseFindings(text, limit = 6) { + const out = []; + const seen = new Set(); + const push = (raw) => { + const title = String(raw).replace(/[`*_]/g, '').replace(/\s+/g, ' ').trim().replace(/[.:;—–-]+$/, '').trim(); + if (!title || seen.has(title)) return; + seen.add(title); + out.push(title.length > 90 ? `${title.slice(0, 87)}…` : title); + }; + for (const m of text.matchAll(/^#{3,4}\s*(?:(?:H|M|L)\d+\s*[—–:.-]\s*|\d+\.\s*)([^\n]+)/gm)) push(m[1]); + for (const m of text.matchAll(/^\|\s*(?:\*\*)?(?:H\d+|M\d+|High|Medium)(?:\*\*)?\s*\|(?:[^|\n]*\|)?\s*([^|\n]+)\|/gm)) push(m[1]); + return out.slice(0, limit); +} + +/** Одна запись индекса по документу. */ +export function indexEntry(name, text) { + const meta = parseDocName(name); + if (!meta) return null; + return { + name, ...meta, + verdict: parseVerdict(text), + ...parseCounts(text), + findings: parseFindings(text), + }; +} + +export function collectEntries(dir, read = (name) => readFileSync(join(dir, name), 'utf8')) { + const names = readdirSync(dir).filter((name) => name.endsWith('.md') && name !== INDEX_FILE).sort(); + const entries = []; + const skipped = []; + for (const name of names) { + const entry = indexEntry(name, read(name)); + if (entry) entries.push(entry); else skipped.push(name); + } + return { entries, skipped }; +} + +const badge = (verdict) => ({ 'зелёный': '🟢', 'жёлтый': '🟡', 'красный': '🔴' }[verdict] || '⚪'); + +export function renderIndex({ entries, skipped = [] }) { + const byIssue = new Map(); + for (const entry of entries) { + const list = byIssue.get(entry.issue) || []; + list.push(entry); + byIssue.set(entry.issue, list); + } + const issues = [...byIssue.keys()].sort((a, b) => b - a); + const lines = []; + lines.push('# Индекс ревью'); + lines.push(''); + lines.push(`Генерируется \`node scripts/reviews-index.mjs\` (#635) — не редактировать руками. Документов: ${entries.length}, issue: ${issues.length}. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок.`); + lines.push(''); + lines.push('| Issue | Документ | Этап · раунд | Вердикт | H | M | Находки |'); + lines.push('|---|---|---|---|---:|---:|---|'); + for (const issue of issues) { + const docs = byIssue.get(issue).sort((a, b) => (a.stage === b.stage ? (a.round || 0) - (b.round || 0) : a.stage === 'spec' ? -1 : 1)); + for (const doc of docs) { + const round = doc.round ? `r${doc.round}` : (doc.suffix || '—'); + lines.push(`| #${issue} | [${doc.name}](${doc.name}) | ${doc.stage} · ${round} | ${badge(doc.verdict)} ${doc.verdict} | ${doc.high} | ${doc.medium} | ${doc.findings.join('; ').replace(/\|/g, '\\|') || '—'} |`); + } + } + if (skipped.length) { + lines.push(''); + lines.push(`Вне схемы имён (не индексируются): ${skipped.map((name) => `\`${name}\``).join(', ')}.`); + } + return `${lines.join('\n')}\n`; +} + +export function buildIndex(dir) { + return renderIndex(collectEntries(dir)); +} + +if (isMainModule(import.meta.url)) { + const arg = (name, fallback) => process.argv.find((a) => a.startsWith(`--${name}=`))?.slice(name.length + 3) ?? fallback; + const dir = arg('dir', 'docs/reviews'); + const output = arg('output', join(dir, INDEX_FILE)); + const markdown = buildIndex(dir); + if (process.argv.includes('--check')) { + const current = existsSync(output) ? readFileSync(output, 'utf8') : ''; + if (current !== markdown) { + console.error(`${output} устарел — пересобрать: node scripts/reviews-index.mjs`); + process.exit(1); + } + console.log(`${output} свеж`); + } else { + writeFileSync(output, markdown, 'utf8'); + console.log(`${output}: ${markdown.split('\n').length} строк`); + } +} diff --git a/test/reviews-index.test.mjs b/test/reviews-index.test.mjs new file mode 100644 index 00000000..48088b8d --- /dev/null +++ b/test/reviews-index.test.mjs @@ -0,0 +1,99 @@ +// #635: индекс ревью — одна строка на документ, детерминированно, 100 % каталога. +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { mkdtempSync, writeFileSync, rmSync, readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; +import { + buildIndex, collectEntries, indexEntry, parseCounts, parseDocName, parseFindings, parseVerdict, renderIndex, +} from '../scripts/reviews-index.mjs'; + +test('#635 имена документов: этап, issue, раунд; INDEX и чужое — вне схемы', () => { + assert.deepEqual(parseDocName('CODE-REVIEW-600-r2.md'), { stage: 'code', issue: 600, round: 2, suffix: null }); + assert.deepEqual(parseDocName('SPEC-REVIEW-7-r1.md'), { stage: 'spec', issue: 7, round: 1, suffix: null }); + assert.deepEqual(parseDocName('CODE-REVIEW-issue-5.md'), { stage: 'code', issue: 5, round: null, suffix: null }); + assert.equal(parseDocName('INDEX.md'), null); + assert.equal(parseDocName('README.md'), null); +}); + +test('#635 вердикт: явная строка, раздел «Вердикт», свободная форма хвоста, иначе «—»', () => { + assert.equal(parseVerdict('- Вердикт: **зелёный**'), 'зелёный'); + assert.equal(parseVerdict('Вердикт: жёлтый · заход r1 · High: 0 · Medium: 4'), 'жёлтый'); + assert.equal(parseVerdict('Verdict: **red** · cycle r2/4'), 'красный'); + assert.equal(parseVerdict('## 6. Вердикт\n\nH1 воспроизведён. Это High: блокирует.\n\n## 7. Прочее'), 'красный'); + assert.equal(parseVerdict('## Вердикт\n\nПродуктовый код не менялся, регрессий нет.\n'), 'зелёный'); + assert.equal(parseVerdict('текст без вердикта'), '—'); + assert.equal(parseVerdict('…поэтому **зелёный вердикт**.'), 'зелёный'); + assert.equal(parseVerdict('зелёныйзаголовок вердикта'), '—', 'цвет как часть слова не считается'); + assert.equal(parseVerdict('Вердикт: зелёныйзаголовок без цвета'), '—', 'JS \\b не знает кириллицы — граница слова явная'); +}); + +test('#635 счётчики и находки', () => { + assert.deepEqual(parseCounts('Вердикт: жёлтый · High: 1 · Medium: 3'), { high: 1, medium: 3 }); + assert.deepEqual(parseCounts('### H1 — a\n### M1 — b\n### M2 — c\n### L1 — d'), { high: 1, medium: 2 }); + assert.deepEqual(parseCounts('ничего'), { high: 0, medium: 0 }); + const findings = parseFindings([ + '### 1. Room settings: подпись переносится посреди слова', + '### H1 — диалог невидим после крестика', + '### M2: `rangeLine` клампит каждый символ.', + '| M3 | `src/x.ts:1` | поле пустое, состояние старое | почему |', + '| **High** | `y.ts` | импорт теряет маршруты | … |', + '### 1. Room settings: подпись переносится посреди слова', + `### 2. ${'очень длинный заголовок '.repeat(8)}`, + ].join('\n')); + assert.equal(findings[0], 'Room settings: подпись переносится посреди слова'); + assert.equal(findings[1], 'диалог невидим после крестика'); + assert.equal(findings[2], 'rangeLine клампит каждый символ'); + assert.ok(findings.includes('поле пустое, состояние старое')); + assert.ok(findings.includes('импорт теряет маршруты')); + assert.equal(findings.length, 6, 'дубль снят, потолок 6'); + assert.ok(findings.every((f) => f.length <= 90)); +}); + +test('#635 индекс покрывает каталог целиком, детерминирован и не индексирует сам себя', () => { + const dir = mkdtempSync(join(tmpdir(), 'hp-reviews-index-')); + try { + writeFileSync(join(dir, 'CODE-REVIEW-600-r1.md'), '# x\nВердикт: **жёлтый** · High: 0 · Medium: 4\n### 1. Первая\n### 2. Вторая\n'); + writeFileSync(join(dir, 'CODE-REVIEW-600-r2.md'), '# x\nВердикт: **зелёный** · High: 0 · Medium: 0\n'); + writeFileSync(join(dir, 'SPEC-REVIEW-600-r1.md'), '- Вердикт: **зелёный**\n'); + writeFileSync(join(dir, 'CODE-REVIEW-601-r1.md'), '## Вердикт\n\nсвободная форма\n'); + writeFileSync(join(dir, 'INDEX.md'), 'старый индекс'); + writeFileSync(join(dir, 'notes.md'), 'постороннее'); + const { entries, skipped } = collectEntries(dir); + assert.equal(entries.length, 4); + assert.deepEqual(skipped, ['notes.md']); + const md = buildIndex(dir); + assert.equal(md, buildIndex(dir), 'детерминирован'); + assert.match(md, /Документов: 4, issue: 2/); + const rows = md.split('\n').filter((l) => l.startsWith('| #')); + assert.deepEqual(rows.map((r) => r.split('|')[2].trim()), [ + '[CODE-REVIEW-601-r1.md](CODE-REVIEW-601-r1.md)', + '[SPEC-REVIEW-600-r1.md](SPEC-REVIEW-600-r1.md)', + '[CODE-REVIEW-600-r1.md](CODE-REVIEW-600-r1.md)', + '[CODE-REVIEW-600-r2.md](CODE-REVIEW-600-r2.md)', + ], 'новые issue сверху; внутри issue — ТЗ, затем код по раундам'); + assert.match(md, /\| #600 \| \[CODE-REVIEW-600-r1\.md\][^\n]*\| code · r1 \| 🟡 жёлтый \| 0 \| 4 \| Первая; Вторая \|/); + assert.match(md, /\| #601 \|[^\n]*⚪ — \|/); + assert.match(md, /Вне схемы имён[^\n]*`notes\.md`/); + assert.ok(!md.includes('INDEX.md](INDEX.md)'), 'индекс не индексирует себя'); + assert.equal(indexEntry('INDEX.md', 'x'), null); + assert.equal(renderIndex({ entries: [] }).includes('Документов: 0'), true); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test('#635 живой каталог docs/reviews: индекс свеж и покрывает все документы', () => { + const { entries, skipped } = collectEntries(fileURLToPath(new URL('../docs/reviews/', import.meta.url))); + assert.equal(skipped.length, 0, `вне схемы имён: ${skipped.join(', ')}`); + assert.ok(entries.length > 900); + const recognised = entries.filter((e) => e.verdict !== '—').length; + assert.ok(recognised / entries.length > 0.9, `вердикт распознан у ${recognised} из ${entries.length}`); +}); + +test('#635 конвейер пересобирает индекс тем же коммитом, что и документ ревью', () => { + const wf = new URL('../.github/workflows/process.yml', import.meta.url); + const text = readFileSync(wf, 'utf8'); + assert.match(text, /node scripts\/reviews-index\.mjs --dir=docs\/reviews\n\s+git add -- docs\/reviews\/INDEX\.md/); +});