diff --git a/docs/reviews/CODE-REVIEW-794-r1.md b/docs/reviews/CODE-REVIEW-794-r1.md new file mode 100644 index 00000000..b1d0cab8 --- /dev/null +++ b/docs/reviews/CODE-REVIEW-794-r1.md @@ -0,0 +1,133 @@ +# Code review #794 — static LED disconnect witness + +Вердикт: зелёный · заход r1 · High: 0 · Medium: 0 · Low: 0 · маршрут: `fix`. + +## Материал и границы + +- Issue: https://github.com/Matysh/houseplan-card/issues/794 — прочитаны тело и комментарии. +- Ветка: `issue/794-static-led-disconnect-witness`. +- Материал: `d19bac39663e09285bf034c67ec1d1036f111652`. +- Дерево: `ff6900ce2e53c025488be7f58ba5fe655eefefa2`. +- База: `865312e0b09db8a46f5532655f299606f90166c5`. +- Полный дифф: шесть файлов, классы B/C; продуктовых и сгенерированных изменений нет. + +Независимое локальное ревью агента `/root/review_794`, не автора реализации. +Это не вердикт модели конвейера, не подтверждение CI и не разрешение обходить +штатный порядок слияния. Исключение владельца здесь не заявляется. + +Задача обслуживает J1: доказательство освобождения состояния светового слоя +View у отдельной карточки. Скоуп — исправить свидетель уже существующего +контракта, зафиксированного в `docs/LIGHT.md` (Owner teardown), без изменения +поведения продукта. `track:show` соответствует инфраструктуре; оснований +переклассифицировать задачу нет. + +## Что проверено и корректно + +Новый тест импортирует скомпилированный `HouseplanSpaceCard`, создаёт настоящий +экземпляр и вызывает настоящий `disconnectedCallback()`. Его метод не +подменяется и не копируется; строки исходника не используются как oracle. +Читается созданный конструктором `_glowRuntimeState`, а browser-owned свойства +`ownerDocument` и `isConnected` задаются управляемой фикстурой. + +`loaded()` устанавливает оба настоящих lazy-слота — gate/runtime и field. +Прямого импорта field недостаточно для `ledRelease`, и тест это учитывает. +`renderStaticLed()` заполняет реальную геометрию поля и lifecycle на том же +owner. Проверяются entering, visible и leaving, отмена принадлежащих owner +rAF/таймеров, сохранность соседней карточки, новый cache и новый entering-rAF +при повторном подключении. Сохранённый старый entering callback вызывается +после reconnect и не может обновить owner или затронуть новый entry. + +Сразу после callback, до любого render, таймера, rAF или микрозадачи, +`hasLedField(owner)` должен стать false, статистика — нулевой. Обычный +`disposeGlowRuntime(this._glowRuntimeState)` не маскирует отсутствие +`ledRelease`: LED field хранит отдельный `GlowRuntimeState` в owner-keyed +WeakMap. При удалённом вызове `ledRelease` непосредственный assert теста +остаётся красным по логике кода. Это проверено чтением, не запуском мутанта. + +Именованный отрицательный случай исполняет обычную ветку connection guard без +teardown: позднее обновление подавлено, но cache и lifecycle остаются; после +reconnect новый entering-rAF не появляется. Поэтому отсутствие updates и +CSS transition events больше не принимаются за самостоятельное доказательство +disposal. Существующий browser smoke сохранён: изменены только пояснение и +название проверки, его assertions и последовательность не ослаблены. + +Мутант `glow-static-led-release-skipped` сохранён с тем же продуктовым патчем, +guard перенесён на новый Node suite. `tsconfig.test.json` включает реальный +`space-card.ts`; `guardNeedsTestBuild()` в nightly execution подготавливает +test-build для короткой команды `node --test`. Новый browser guard не добавлен. +Трейлеры коммита корректны: `Issue: #794`, `User-Visible: no`. + +## Проверки и происхождение доказательств + +Самостоятельный запуск: WSL Ubuntu/ext4, Node `22.23.2`. WSL checkout остался +на базе с шестью изменёнными файлами; SHA-256 каждого из этих файлов совпал с +Windows-деревом материала. Остальные tracked-файлы WSL не изменены. Использован +test-build, подготовленный автором; повторная сборка и полный гейт ревьюером +не запускались. + +| Команда / проверка | Результат и источник | +| --- | --- | +| `node --test test/space-card-led-disconnect.test.mjs` | Собственный запуск: PASS, 4/4, 0 skipped/cancelled, около 152 ms | +| `git diff --check 865312e0b..HEAD` | Собственный запуск: PASS | +| `git rev-parse HEAD` и полный дифф базы к материалу | Собственная сверка точного SHA, состава и трейлеров | +| `npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs && node --test test/space-card-led-disconnect.test.mjs` | Принято из авторского handoff и прочитанного `C:/Temp/hp794-scoped.log`: PASS, 4/4 | +| `npm run gate:small -- --base=865312e0b09db8a46f5532655f299606f90166c5` | Прочитан авторский `C:/Temp/hp794-gate-small-final.log`: PASS, 54 s, все 9 проверок — build/typecheck, units, bundle integrity/budget, lint:unused, no-new-any, no synchronous render layout, no-new-private-writes, smoke-select | +| `node scripts/bundle-sync.mjs && node demo/smoke_led_strip_glow.mjs` | Принято из авторского handoff и прочитанного `C:/Temp/hp794-smoke.log`: `OK`; самостоятельно браузер не запускался | +| `node scripts/mutation-gate.mjs --check` | Прочитан авторский `C:/Temp/hp794-mutation-check.log`: PASS, включая нужный mutant anchor; 235 browser guards, 4 предупреждения, без выполнения мутантов | +| `smoke-select` в small gate | Нет исполняемого frontend-диффа; дополнительные смоки по диффу не выбраны. Названный в AC LED smoke выполнен автором отдельно | + +Четыре предупреждения static mutation check: три ранее существовавших +test-name-pattern с динамическими именами (`corpus-loses-its-short-edge`, +`optimize-reports-work-it-did-not-do`, `nightly-reuse-accepts-stale-marker`) и +превышение рекомендательного ориентира browser guards 200. Они не относятся к +новому свидетелю. Static `--check` не выдан за поимку мутанта. + +### Приёмка и защитные случаи — чем краснеет + +| AC / контракт | Чем доказан | Чем краснеет | +| --- | --- | --- | +| AC1–2: именно static-card disconnect синхронно освобождает LED lifecycle, reconnect получает свежий entry | Три phase-case нового Node suite, собственный запуск 3/3; реальный callback и lazy slots проверены чтением | `glow-static-led-release-skipped` удаляет `ledRelease` только из disconnect; непосредственный `hasLedField === false` не выполняется. Поимку подтвердит ночь | +| AC1: connection guard не равнозначен disposal | Именованный negative case нового suite, собственный запуск 1/1 | Без teardown ожидаемо остаются старый cache/lifecycle и ноль новых entry frames; это отрицательный случай без source mutation | +| Owner isolation и отмена старого entry | Те же phase-case: сосед сохраняет cache/stats; pending callbacks удалены; старый rAF после reconnect не меняет updates/cache | Потеря соседнего поля, сохранённый owner callback либо stale update нарушают точные assertions; исполняется stale-rAF negative input | +| AC2: видимый fade остаётся browser-проверкой | Smoke assertions сохранены чтением диффа; авторский smoke `OK` | Существующий `glow-entry-initial-opacity-skipped` остаётся browser guard; выполнение мутанта не повторялось | +| AC3: ночной mutant связан с новым свидетелем, целевые и small checks зелёные | Registry diff, test-build wiring и static check; результаты выше | Статический gate отвергает отсутствующий anchor/guard; новый guard содержит прямой синхронный oracle. Фактическая nightly поимка пока не подтверждена | + +## Риски и ограничения + +- Async/lifecycle: проверены три фазы, synchronous boundary, cancellation, + независимый сосед и replay старого entering-rAF. Replay уже извлечённых + старых timer callbacks этот suite не доказывает и не заявляет. +- Host/input: Node вызывает callback напрямую с browser-property shims; это + не самостоятельная проверка browser-driven removal, полного Lit render или + полного `connectedCallback`. Сохранённый браузерный сценарий принят из + авторского прогона, не из собственного исполнения. +- Визуал: CSS/fade сохраняются; новых пиксельных обещаний нет. Golden не + запускался — визуального продуктового диффа и `ci:golden` нет. +- Данные/права, геометрия, touch и пользовательский UX не изменены; отдельные + проверки миграций, HA-harness и модельных инвариантов здесь неприменимы. +- Объём/performance: browser guard заменён дешёвым Node guard; экономия всего + ночного прогона не измерялась. Полные smokes/performance не запускались. +- Одно число — один источник: изменённые inventory counts 103 и 235 сверяются + существующим `test/mutation-gate.test.mjs` с ID-списками и `MUTANTS`; ориентир + 200 сверяется с `BROWSER_GUARD_LIMIT`. Независимой новой числовой настройки нет. +- Мутанты не применялись и не запускались согласно PROCESS §2.7/#709. + Реальную поимку проверит следующий nightly run. CI на кандидате слияния + этим локальным ревью не подтверждается; штатный gate остаётся обязательным. + +## Находки + +High: 0. Medium: 0. Low: 0. Открытых находок нет. +## Дополнение интегратора: разрешение и объединённый кандидат + +После завершения этого независимого ревью владелец явно разрешил ручное +слияние #794 и #795 по агентским ревью и перевод в S8. Решение зафиксировано +в [комментарии к #794](https://github.com/Matysh/houseplan-card/issues/794#issuecomment-5990879357). +Это разовое исключение при отказе штатной модели, не её зелёный вердикт. +Исходные SHA обоих проверенных материалов сохраняются в истории без rebase. + +Отдельная read-only проверка совместимости агентом `/root/review_795`: +общих изменённых файлов нет; ID и общее число мутантов не меняются; +новый Node guard обеспечен существующей подготовкой test-build; +browser inventory после объединения равен 235. Замечаний нет. +Это проверка чтением, не запуск мутантов. Проверки точного объединённого +кандидата и результат push будут приложены в issue после исполнения. diff --git a/docs/reviews/CODE-REVIEW-795-r1.md b/docs/reviews/CODE-REVIEW-795-r1.md new file mode 100644 index 00000000..709ff2fb --- /dev/null +++ b/docs/reviews/CODE-REVIEW-795-r1.md @@ -0,0 +1,187 @@ +# Code review #795 — nightly mutation headroom and cancellation reporting + +Вердикт: зелёный · заход r1 · High: 0 · Medium: 0 · Low: 0. + +## Основание и материал + +Независимое код-ревью от 2026-10-05: отдельная сессия Codex +`/root/review_795`, не участвовавшая в реализации. Это не саморевью и не +машинный вердикт конвейера. Документ не утверждает разрешение владельца на +ручное слияние или обход штатного конвейера; такой вопрос на момент записи +остаётся отдельно запрошенным. + +- Issue: https://github.com/Matysh/houseplan-card/issues/795; прочитаны тело и + комментарии с приёмкой и обоснованием числа шардов. +- Ветка: `issue/795-mutation-nightly-headroom`. +- Материал: `aef02229c6e00df73daea01afe44a279aec6b68e`. +- Дерево материала: `309dfd9b8c698edf3cfb4fdc900015634f0cb5d4`. +- База: `865312e0b09db8a46f5532655f299606f90166c5`. +- Диапазон: `git diff 865312e0b..aef02229c`. + +HEAD повторно сверен перед записью: совпадает с материалом; рабочее дерево +было чистым. Этот документ добавляется после проверки и не меняет материал +кода. Прочитаны `docs/SCOPE.md`, `AGENTS.md`, `docs/process/REVIEWER.md`, +применимые разделы `PROCESS.md`, строки подсистемы в `docs/reviews/INDEX.md` +и реализация раннера/отчётчика, а не только описание автора. + +## Скоуп + +Ровно пять файлов классов B/C: + +- `.github/workflows/_mutation-gate.yml`; +- `scripts/mutation-gate-report.mjs`; +- `test/mutation-gate-report.test.mjs`; +- `test/mutation-gate.test.mjs`; +- `docs/TESTING.md`. + +Правка сохраняет полный ночной реестр, снижает работу на один шард и уточняет +диагностику отменённого шага. Это инфраструктура защиты продукта в пределах +существующего scope, без новой пользовательской функции. Классов A/D, +продуктовых изменений, тонкого вызывающего workflow в `main`, релизных +гейтов и исправления LED-теста в диффе нет. Трейлеры коммита: +`Issue: #795`, `User-Visible: no`; changelog не требуется. + +## Что проверено и корректно + +### AC1: нагрузка и полный охват + +Матрица содержит последовательные номера 1–10 +(`.github/workflows/_mutation-gate.yml:119`). Делитель раннера `/10`, число +evidence при записи, верификации и отчёте равны десяти (строки 180, 193, 230, +308); CLI default отчётчика также 10 (`scripts/mutation-gate-report.mjs:365`). +Потолок остаётся 60 минут (workflow:128), `fail-fast: false` сохраняется. + +Исполненный тест `#795: десять ночных шардов согласованы с runner/evidence и +покрывают реестр ровно один раз` (`test/mutation-gate.test.mjs:447`) связывает +матрицу с командами и именем job и вызывает реальный `shardMutants` для +полного текущего `MUTANTS`. Сортированный объединённый список равен всем 1164 +ID без потерь/повторов; на шард приходится 116–117 мутантов вместо 194. +Сам алгоритм сортировки по ID и чересполосного деления не изменён. + +Обоснование числа проверено отдельно. Read-only `gh run view 37271421177` +подтвердил длительности шести прежних jobs примерно 42–60 минут и отмену +четвёртого. Прочитан и повторно исполнен предоставленный автором анализатор +`C:/Temp/hp795-estimate.mjs`, читающий шесть скачанных логов +`C:/Temp/hp795-night-logs`: доступны измерения всех 1164 результатов; учтены +уникальные clean guards, их медианы и три минуты bootstrap-запаса. + +| Шардов | Максимум мутантов | Прогноз самого долгого шарда | +| --- | --- | --- | +| 6 | 194 | 61,9 мин | +| 8 | 146 | 48,0 мин | +| 10 | 117 | 38,4 мин | +| 12 | 97 | 35,0 мин | + +Выбор десяти обоснован запасом до прежнего потолка и небольшой добавочной +экономией у двенадцати. Это повторно проверенная модель по прошлым логам, +не исполненный десятишардовый прогон; фактическое время подтвердит следующая +ночь. + +### AC2: отмена не превращается в успех и не стирает итог + +В `parseShardLogs` порядок отказов сохранён: `cancelled`/`skipped` не дают +`ok` даже при полном счётчике, неполный счётчик и `failure` также остаются +отказом (`scripts/mutation-gate-report.mjs:225–226`). Теперь вместе со статусом +сохраняются исход шага и распознанный итог (строка 230). + +`interruptedLabel` (строка 336) различает отмену, пропуск и отсутствие итогов. +Для `cancelled` с `194/194` он называет оба факта и больше не утверждает, +что итоговой строки нет. Причина отмены не выдумывается: таймаут и ручную +отмену различают аннотации job, а не один лог. Telegram сохраняет статус +`interrupted`. + +Валидация evidence по-прежнему отвергает прерывание (строки 131–132), +отсутствующий шард и чужой material; алгоритм identity/partial retry не +менялся. Маркер переиспользования по-прежнему требует одновременно успешные +`mutants` и `evidence` (`_mutation-gate.yml:242`). Отчёт и отсутствие маркера +не подменены ослабленным критерием успеха. + +### Одно число — один источник + +Пользовательских величин дифф не вводит. Число шардов повторяется в матрице, +имени job, командах и документации. Workflow-копии связаны исполняемым +контрактом, берущим `n` из матрицы; фактический охват считается из `MUTANTS` +через существующий `shardMutants`. CLI default и документация дополнительно +сверены чтением. Переноса подсчёта мутантов в новый ручной список нет. + +## Проверки и происхождение доказательств + +| Проверка | Кто исполнил / что прочитано | Результат на материале | +| --- | --- | --- | +| `node --test test/mutation-gate-report.test.mjs test/mutation-gate.test.mjs` | Ревьюер; Windows, абсолютный путь к pinned Node 22.23.2 | PASS: 89/89, 0 fail, 0 skipped; 30,8 с | +| `git diff --check 865312e0b aef02229c` | Ревьюер | PASS, замечаний нет | +| Анализатор длительности, указанный выше | Ревьюер повторно исполнил предоставленный анализатор после чтения его кода | Таблица выше воспроизводится; мутанты не исполняются | +| `npm run gate:small` | Унаследовано от автора, WSL `/home/matysh/hp795`; ревьюер прочитал `C:/Temp/hp795-gate.log`, повторно полный гейт не запускал | PASS: все 9 проверок, 76 с, 0 отказов: build/typecheck, units, bundle integrity/budget, no-new-any, no-sync-layout, no-new-private-writes, lint:unused, smoke-select | +| `node scripts/mutation-gate.mjs --check` | Унаследовано от автора; ревьюер прочитал `C:/Temp/hp795-check.log`, exit 0 передан автором | Статические якоря/guards проверены; 4 прежних предупреждения, browser guards 236/200 | +| `node scripts/process-gate.mjs --range origin/dev..HEAD --issues` | Результат передан автором; ревьюер не переисполнял | PASS с предупреждением об инфраструктурной задаче без S до ревью | +| Новые focused-тесты на старом коде → на исправленном | Сообщено автором и записано в issue; ревьюер pre-fix запуск не воспроизводил | 4 отказа / 1 pass → 5 pass; не выдаётся за собственное red/green доказательство ревьюера | + +Для собственного запуска использован +`C:/Users/Admin/AppData/Local/houseplan-toolchain/node-v22.23.2-win-x64/node.exe`. +Проверки были unit/static: реальные mutation executions не запускались. +Граница унаследования выше относится к выполнению команд, а не к выводу о +корректности: код, тесты и отрицательные случаи прочитаны независимо. + +### Защитные AC — чем доказаны и чем краснеют + +| Контракт | Доказательство | Отрицательный случай / чем краснеет | +| --- | --- | --- | +| AC1: все шарды покрывают полный реестр без пропусков и дубликатов | Исполненный workflow/partition-тест `#795` в `mutation-gate.test.mjs:447` | Пропуск/повтор номера или ID нарушит равенство с полным реестром; несовпадающий делитель/число evidence нарушит контракт команд | +| AC1: часовой потолок не поднят | Тот же тест и чтение job | Отсутствие `timeout-minutes: 60` нарушит assert; текущий diff потолок не меняет | +| AC2: полный итог не превращает отмену в успех | Исполненные тесты `#795` в `mutation-gate-report.test.mjs:179` и `#604` в строках 166, 216 | `cancelled` + `поймано 194 из 194` обязаны дать `failed=true`, `interrupted`; отменённый evidence обязан быть отвергнут | +| AC2: обрыв без итогов не считается успехом | Исполненный `#604` в строке 152 и `#795` в строке 191 | Лог только с зелёными отдельными строками, либо `success` с пустым логом остаются отказом; `skipped` с итогом тоже отказ | +| AC2: нельзя принять неполное/чужое evidence | Исполненный `#795` в строке 204 и прежние `#549` | Девять из десяти evidence и чужой SHA десятого отвергаются; старые тесты отвергают смешанный material при partial retry | +| AC2: escaped/failure/неполный счётчик не становятся зелёными | Исполненные существующие тесты отчётчика | Известный `FAIL`, красный clean guard, `failure` без `FAIL`, итог `2 из 3` остаются отказами; успешный полный набор отдельно даёт `failed=false` | + +Текстовая точность отмены проверена отдельно: тест требует упоминания +`cancelled` и `194/194` и запрещает ложные «без итоговой строки» / «до итоговой +строки». Для этих unit-контрактов достаточно отрицательных случаев; запуск +мутантов не заменялся и по PROCESS §2.7/#709 оставлен ночи. + +## Классы риска + +| Класс | Проверка / применимость | +| --- | --- | +| Async: отмена, порядок, устаревший результат | Применим: `cancelled` после итогов, `skipped`, обрыв, partial retry и чужой material проверены тестами; успешный итог не отменяет outcome | +| Данные и права | Применимы отсутствие/чужое evidence, неизвестные/красные строки и неполные итоги; проверены. Права GitHub jobs и продуктовые права не меняются | +| Геометрия | Неприменима: геометрия и её ссылки не меняются | +| Визуал | Неприменим: рендеринг и golden не затронуты | +| Объём и performance | Применим: полный реестр 1164 ID распределён на 10 шардов, потолок 60 минут сохранён, модель основана на прошлых логах. Фактическое ночное время пока не измерено | +| Host/input | Применим только хост GitHub Actions: матрица/артефакты/исходы прочитаны и защищены контрактами. Реальный timeout/upload цикл не воспроизводился; HA, mouse/touch/keyboard не затронуты | + +## Находки + +High: 0. Medium: 0. Low: 0. Открытых находок нет; ослабления гейта не найдено. +AC1–AC3 подтверждены в описанных границах: код/контракты и локальные гейты +зелёные; issue прямо оставляет реальную длительность следующей ночи. + +## Чего не проверял + +- Полный десятишардовый mutation-run, фактический запас времени, таймаут + GitHub runner и последующую загрузку артефактов: проверит следующая ночь. + Ни расчёт, ни `--check`, ни зелёные unit-тесты не выданы за поимку реестра. +- Полный локальный `gate:small` повторно: использовано явно названное + доказательство автора на точном SHA, а затронутые unit-файлы исполнены + независимо. +- Browser smoke, golden, HA-harness/pytest, геометрические инварианты и + продуктовые performance-профили: классов A/D и соответствующих AC нет; + `smoke-select` в прочитанном gate-log сообщает отсутствие исполняемого + frontend-диффа. +- Новый CI Validate и кандидат слияния этим документом не аттестуются. + Публикация документа не даёт разрешения на ручное слияние, перестановку + статуса, закрытие issue или выпуск; это отдельные этапы. + +## Дополнение интегратора: разрешение и объединённый кандидат + +После завершения этого независимого ревью владелец явно разрешил ручное +слияние #794 и #795 по агентским ревью и перевод в S8. Решение зафиксировано +в [комментарии к #795](https://github.com/Matysh/houseplan-card/issues/795#issuecomment-5990879684). +Это разовое исключение при отказе штатной модели, не её зелёный вердикт. +Исходные SHA обоих проверенных материалов сохраняются в истории без rebase. + +Отдельная read-only проверка совместимости агентом `/root/review_795`: +общих изменённых файлов нет; ID и общее число мутантов не меняются; +новый Node guard обеспечен существующей подготовкой test-build; +browser inventory после объединения равен 235. Замечаний нет. +Это проверка чтением, не запуск мутантов. Проверки точного объединённого +кандидата и результат push будут приложены в issue после исполнения. diff --git a/docs/reviews/INDEX.md b/docs/reviews/INDEX.md index 2d95cbac..69ce90bb 100644 --- a/docs/reviews/INDEX.md +++ b/docs/reviews/INDEX.md @@ -1,11 +1,13 @@ # Индекс ревью -Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 295, issue: 147. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`. +Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 297, issue: 149. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`. | Issue | Документ | Этап · раунд | Вердикт | H | M | Находки | Файлы | |---|---|---|---|---:|---:|---|---| | бета v1.79.0-beta.2 | [SHIP-REVIEW-v1.79.0-beta.2.md](SHIP-REVIEW-v1.79.0-beta.2.md) | пакетное ревью ship · — | ⚪ — | 0 | 0 | — | — | | бета v1.79.0-beta.1 | [SHIP-REVIEW-v1.79.0-beta.1.md](SHIP-REVIEW-v1.79.0-beta.1.md) | пакетное ревью ship · — | ⚪ — | 0 | 0 | — | — | +| #795 | [CODE-REVIEW-795-r1.md](CODE-REVIEW-795-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — | +| #794 | [CODE-REVIEW-794-r1.md](CODE-REVIEW-794-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — | | #793 | [CODE-REVIEW-793-r1.md](CODE-REVIEW-793-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — | | #791 | [CODE-REVIEW-791-r1.md](CODE-REVIEW-791-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — | | #790 | [CODE-REVIEW-790-r1.md](CODE-REVIEW-790-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — |