mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
Проверка (CI) / Классификация изменённых файлов (push) Successful in 22s
Проверка (CI) / Мутанты по диффу (1/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (2/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (3/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (4/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (5/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (6/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Предполёт: документация, провенанс, процесс (push) Failing after 48s
Проверка (CI) / HACS: валидация репозитория (push) Failing after 23s
Проверка (CI) / Hassfest: манифест интеграции (push) Failing after 24s
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Successful in 46s
Проверка (CI) / Геометрия: TS/Python parity исполнена (push) Failing after 34s
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Failing after 44s
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Failing after 8m29s
Проверка (CI) / Смоки в браузере (шард 1 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 2 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 3 из 3) (push) Skipped
Проверка (CI) / Смоки: все шарды зелёные (push) Skipped
Проверка (CI) / Golden-кадры против принятых эталонов (push) Skipped
Проверка (CI) / Перф-смок: бюджет времени кадра (push) Skipped
Проверка (CI) / Доказательство выполненных проверок (push) Failing after 19s
Issue: #649 User-Visible: no
248 lines
25 KiB
Markdown
248 lines
25 KiB
Markdown
# CODE-REVIEW-649-r2
|
||
|
||
Issue: [#649](https://github.com/Matysh/houseplan-card/issues/649) · 2.5D, этап 6:
|
||
объёмные иконки-плитки с тенью, мягкий свет по солнцу, слой декора без
|
||
изменений, режим из experimental в General settings.
|
||
|
||
Материал: `git log --oneline origin/dev..HEAD` / `git diff origin/dev...HEAD`,
|
||
ровно на SHA `106fdc096d5dd7de55d22a88c3a0a2b894b83b49` (рабочая копия уже на
|
||
нём; ветку не переключал, `git fetch`/`pull`/`checkout` на другой коммит не
|
||
выполнял).
|
||
|
||
```
|
||
106fdc09 fix(2.5D): prove the sun beam is cut by bodies; review r1 lows (#649)
|
||
e1f1c7d9 docs: review document for #649
|
||
2ed05031 test(mutants): stage3-w12 guard follows the renamed #649 contract test
|
||
e5ab4659 feat: 2.5D stage 6 — public setting, raised tiles, soft sun, theme-free walls (#649)
|
||
```
|
||
|
||
164 файла в фиче + 73 файла в делте раунда. Трейлеры на всех трёх рабочих
|
||
коммитах корректны: `Issue: #649` везде; `User-Visible: yes` на `e5ab4659`
|
||
(оба changelog в том же коммите — `docs/CHANGELOG.md`/`.ru.md`, +12/+12 строк);
|
||
`User-Visible: no` на `2ed05031` (гейт мутанта) и на `106fdc09` (делта этого
|
||
раунда — см. ниже, почему верно). Заход r2 код-ревью; ТЗ (r2 в теле issue) уже
|
||
прошло зелёное SPEC-REVIEW-649-r2.
|
||
|
||
**SHA материала r1 не резолвится.** `de4521823ba69c390fda4c0534e52e24a63cc66a`
|
||
(коммит) и дерево `571541905136a48c1729755f6d7877e5ac6aece4` из блока
|
||
«Материал раунда» документа r1 отсутствуют в `git log --all` этого клона (не
|
||
shallow — `git rev-parse --is-shallow-repository` → `false`, 3576 коммитов на
|
||
`origin/dev`). Это не находка (§2.10: «SHA не резолвится — обычное дело»): у
|
||
текущего `e5ab4659` тот же commit message, тот же автор/дата инструмента, тот
|
||
же родитель (`origin/dev` tip) и почти идентичная статистика диффа (164 файла,
|
||
+5569/−3766 против заявленных в r1 +5570/−3767 — расхождение в одну строку в
|
||
обе стороны, не содержательное). Это типичная механика конвейера: ветка
|
||
ребейзится на актуальный `origin/dev` перед каждым раундом, что меняет все SHA
|
||
ниже точки ребейза без изменения содержимого. Дельта раунда — ровно то, что
|
||
показывает `git diff e5ab4659..106fdc09` (коммит `2ed05031` между ними не
|
||
трогает продуктовый код, только чинит протухший guard мутанта — унаследован
|
||
как есть). Объём разбора: по дельте (§2.10), она полностью локальна — один
|
||
новый коммит `106fdc09`, который сам называет себя ответом на r1 («review r1
|
||
lows»).
|
||
|
||
## Скоуп
|
||
|
||
Без изменений с r1 (см. `docs/reviews/CODE-REVIEW-649-r1.md` → «Скоуп»).
|
||
Предмет ЭТОГО раунда — единственный новый коммит `106fdc09`: закрывает
|
||
Medium-находку r1 (M1, обрезка 2.5D-луча барьерами не была доказана тестом) и
|
||
пять из шести Low (L1, L3, L4, L5, L6; L2 была снята самим r1 без правки кода
|
||
как опечатка хендоффа — здесь закрывать нечего).
|
||
|
||
## Как проверялось (объём по дельте, §2.10)
|
||
|
||
1. Прочитан документ и вердикт предыдущего раунда:
|
||
`docs/reviews/CODE-REVIEW-649-r1.md` — жёлтый, High 0, Medium 1 (M1), Low 6
|
||
(L2 снята r1 сразу).
|
||
2. Объявлена дельта: `git diff e5ab4659..106fdc09` — 73 файла (в основном
|
||
пересобранный бандл), содержательно: `src/iso-sun.ts` не тронут (только
|
||
`test/`, `demo/`, `scripts/mutation-registry.mjs`, `src/iso-tiles.ts`,
|
||
`src/houseplan-card.ts` комментарий, `docs/DEVELOPMENT.md`,
|
||
`docs/ARCHITECTURE.md`, `demo/benchmark_large_house.mjs`). Полный текст
|
||
диффа коммита `106fdc09` прочитан построчно.
|
||
3. Для M1 — прочитан `src/iso-sun.ts:82-122` (`computeIsoSunBeams`),
|
||
подтверждено, что `directionalOccluders(input.occluders, away, travel)`
|
||
(строка 112) — ровно то место, которое M1 называл риском (перепутанный
|
||
знак направления/длина). Новый юнит
|
||
`test/iso-stage6.test.mjs` (тест «AC7 the 2.5D beam is cut…») ставит
|
||
колонну на пути луча и проверяет три точки (перед колонной светло, за
|
||
колонной темно, рядом с тенью снова светло) плюс площадь тени.
|
||
4. **Мутационное доказательство выполнено исполнением, не чтением.** Для всех
|
||
трёх новых мутантов (`iso-sun-occluders-toward-sun`,
|
||
`iso-sun-occluders-ignored`, `iso-sun-card-drops-occluders`) я вручную
|
||
применил патч из `scripts/mutation-registry.mjs`, прогнал именно тот guard,
|
||
что записан в реестре, убедился в красном результате, затем откатил файл и
|
||
для двух из них (те, что требуют бандл) пересобрал (`npm run bundle:sync`)
|
||
и подтвердил `git status` чист — см. таблицу ниже, результаты все реальные
|
||
прогоны этой сессии.
|
||
5. Для L3 (`src/iso-tiles.ts:109-117`, `isoTileStateCss`) вручную пересчитана
|
||
специфичность CSS: новые правила `.dev.theme-dark.${state}` (3 класса) и
|
||
`.dev.theme-dark.${state}.iso-floor-light` (4 класса) перебивают старые
|
||
`.dev.${state}` (2) / `.dev.${state}.iso-floor-light` (3) по специфичности
|
||
независимо от порядка — правки корректны структурно, не только текстуально.
|
||
Также перепроверено (как и в r1), что для всех 6 `ISO_STATE_BODIES`
|
||
`isoEdgeColor` сегодня возвращает одно и то же значение независимо от
|
||
`theme`, потому что ни один не тёмный (`luma·255<70`) и ни один не
|
||
«near-white» в 0..1-шкале (`luma>0.98`) — значит фикс сегодня не меняет
|
||
видимый результат ни для одного реального состояния, что и объясняет
|
||
`User-Visible: no`.
|
||
6. Для L1 прочитаны все 4 места, названные в r1 (`docs/DEVELOPMENT.md:231`,
|
||
`docs/ARCHITECTURE.md:31`, `src/houseplan-card.ts:627`,
|
||
`demo/benchmark_large_house.mjs:145`) — все четыре переписаны, проверено,
|
||
что переписанный текст не противоречит соседним абзацам того же файла
|
||
(`docs/DEVELOPMENT.md:249-251`, `docs/ARCHITECTURE.md:86-88`,
|
||
`src/houseplan-card.ts:~2305`), и что не осталось других вхождений
|
||
«experimental/alpha-only/opt-in alpha» применительно именно к 2.5D/Stage 4
|
||
(`grep -n alpha` по трём файлам — только легитимные несвязанные
|
||
употребления: общий `hp_alpha`/Labs-инфраструктура и цветовая альфа).
|
||
7. Для L4/L5/L6 прочитаны новые ассерты `demo/smoke_iso_tiles.mjs` и сверены с
|
||
CSS-правилами, на которые они опираются (специфичность
|
||
`.dev.theme-dark`/`.oplock`/`.virtual .device-shell-frame`,
|
||
`iso-overlays?.devices.get(d.id)` опциональная цепочка для L6) — все три
|
||
были задокументированы в r1 как «проверено чтением, корректно, но без
|
||
теста»; теперь тест есть и я лично прогнал его (все ассерты `true`).
|
||
8. Прогнаны реально (не приняты по Validate) — таблица ниже.
|
||
|
||
### Гейты — что прогнано и с каким результатом
|
||
|
||
| Гейт | Прогнан | Результат |
|
||
|---|---|---|
|
||
| `Validate` на `106fdc09` | нет, зачтён по ссылке (условие задачи) | success, https://github.com/Matysh/houseplan-card/actions/runs/36161723663 |
|
||
| `npx tsc -p tsconfig.test.json` + `node scripts/fix-test-build.mjs` | да | 0 ошибок |
|
||
| `npm run bundle:sync` (`tsc --noEmit && rollup -c` + bundle-tree sync) | да, дважды (baseline и после отката мутанта) | чисто оба раза, `git status --porcelain` пуст после отката — закоммиченные `dist/**`, `custom_components/.../frontend`, `demo/srv/assets` побайтово совпадают со свежей сборкой на этом SHA |
|
||
| `node --test test/iso-stage6.test.mjs` (весь файл, 10 тестов) | да | **OK**, 10/10 `pass` |
|
||
| `node --test --test-name-pattern="AC7 the 2.5D beam is cut" test/iso-stage6.test.mjs` на немутированном коде | да | **OK** |
|
||
| Мутант `iso-sun-occluders-toward-sun` (`away`→`toSun`) применён к `src/iso-sun.ts`, тот же guard | да | **красный** — `AssertionError: between the window and the body the floor stays lit`, ожидалось `true`, получено `false` |
|
||
| Мутант `iso-sun-occluders-ignored` (доп. условие `elevation > 90`) применён, тот же guard | да | **красный** — `AssertionError: behind the body, away from the sun, the floor is in shadow` |
|
||
| Мутант `iso-sun-card-drops-occluders` (`occluders: []` в `houseplan-card.ts`) применён, пересобран, `node demo/smoke_iso_sun.mjs` | да | **красный** — `bodyCutsTheBeam: expected true, got false`; после отката и пересборки `git status` снова чист |
|
||
| `node demo/smoke_iso_sun.mjs` на немутированном коде | да | **OK**, все 20 проверок `true`, включая новые `bodyCutsTheBeam`, `beamBackWithoutBody` |
|
||
| `node demo/smoke_iso_tiles.mjs` на немутированном коде | да | **OK**, все 39 проверок `true`, включая новые `alarmBeatsHover`, `alarmBeatsSelected`, `virtualNoDashedRing`, `noBordersKeepsTiles`, `noBordersKeepsShadows` |
|
||
| `node scripts/check-docs.mjs --external --screenshots=warn` (режим обычного пуша, `classify-changes.mjs`/`validate.yml`) | да | **OK**, 7 файлов, 12 внешних ссылок; один `WARN` про устаревший фингерпринт скриншотов — унаследован, предрелизный гейт (#479), делта его не касается |
|
||
| `node scripts/smoke-select.mjs --base 2ed05031 --head HEAD` (скоуп ровно делты раунда) | да | `НЕОПРЕДЕЛЁННОСТЬ` — дифф исполняемый, но инструмент не находит символьной связи (делта — 2 файла `src/**`, изменения в основном текстовые/CSS-generation внутри уже покрытых функций); решение — см. ниже |
|
||
| `node scripts/smoke-select.mjs --base origin/dev --head HEAD` (полный охват фичи, как в r1) | да | те же 78 прямых совпадений, что в r1 (делта раунда их не меняет) — решение r1 унаследовано без повтора |
|
||
| `python -m pytest tests_backend` | нет | делта раунда не трогает ни одного `.py`-файла (`git show 106fdc09 --stat` — 0 совпадений `custom_components/.*\.py`); гейт неприменим к этой делте |
|
||
| `npm run golden:verify` | нет | делта `User-Visible: no`, ни одна золотая сцена не задета |
|
||
| `npm run invariants` | нет | делта не меняет каноническую геометрию (только презентационные occluders/CSS-эмиссию) |
|
||
| Полный `npm test` | нет, принят по Validate на этом SHA | — |
|
||
|
||
**Решение по `smoke-select --base 2ed05031 --head HEAD` (НЕОПРЕДЕЛЁННОСТЬ).**
|
||
Инструмент не связал делту ни с одним смоком символьно (изменения в
|
||
`iso-sun.ts` не тронуты вовсе — только новый юнит-тест их покрывает; изменения
|
||
в `iso-tiles.ts` — две дополнительные CSS-строки внутри уже существующей
|
||
функции). Это не разрешение ничего не прогонять (по инструкции промпта): я сам
|
||
выбрал и прогнал `smoke_iso_sun.mjs` и `smoke_iso_tiles.mjs` — ровно те смоки,
|
||
что физически упражняют изменённые функции (`computeIsoSunBeams`,
|
||
`isoTileStateCss`) через реальный рендер в браузере, и лично воспроизвёл
|
||
падение всех трёх новых мутантов на них. Остальные ~275 смоков матрицы не
|
||
запускал: делта не трогает ничего вне `iso-sun.ts`/`iso-tiles.ts`/тестов/доков.
|
||
|
||
## Находки
|
||
|
||
Нет ни High, ни Medium, ни Low. Делта раунда полностью и по существу
|
||
закрывает M1 (доказано исполнением: тест и три мутанта, все три реально
|
||
покраснели) и пять Low из шести (L1, L3, L4, L5, L6); L2 была снята ещё в r1.
|
||
|
||
## Закрытие раунда r1
|
||
|
||
| Находка | Чем закрыта | Где это видно |
|
||
|---|---|---|
|
||
| **M1** (Medium) — обрезка 2.5D-луча барьерами (физические тела, Solid-перегородки) нигде не доказана тестом; названный риск — перепутанный знак направления или длина экструзии в `directionalOccluders(input.occluders, away, travel)` | Новый юнит `test/iso-stage6.test.mjs` («AC7 the 2.5D beam is cut…», колонна на пути луча: лит перед ней, тень за ней, лит рядом) + новые ассерты `demo/smoke_iso_sun.mjs` (`bodyCutsTheBeam`, `beamBackWithoutBody`, Solid-перегородка в реальном рендере) + три мутанта реестра (`iso-sun-occluders-toward-sun`, `iso-sun-occluders-ignored`, `iso-sun-card-drops-occluders`). Все три лично применены и подтверждены красными в этой сессии (см. таблицу гейтов) | `test/iso-stage6.test.mjs:151-179`; `demo/smoke_iso_sun.mjs:99-130`; `scripts/mutation-registry.mjs:3526-3546`; код неизменён — `src/iso-sun.ts:111-114` |
|
||
| **L1** — устаревшие «alpha/hidden»-формулировки рядом с исправленным текстом | Переписаны все 4 места, проверено на противоречие соседним абзацам | `docs/DEVELOPMENT.md:231-232`, `docs/ARCHITECTURE.md:31`, `src/houseplan-card.ts:627`, `demo/benchmark_large_house.mjs:145` |
|
||
| **L2** — опечатка числа в хендоффе (не код) | Была снята уже в r1 без правки; в этом раунде править нечего | — |
|
||
| **L3** — `isoTileStateCss()` не различал тему для торца состояний | Добавлены `.dev.theme-dark.${state}` / `...iso-floor-light` со своим вызовом `edge(body,'dark',...)`, специфичность выше старых правил (проверено пересчётом), покрыто новым ассертом юнита | `src/iso-tiles.ts:113-116`; `test/iso-stage6.test.mjs:68-70` |
|
||
| **L4** — «Alert > чистый Hover» и «Alert > чистый Selected» не проверялись отдельно | Два новых прогона: реальный `mouse.move` + `alarm` без селекта/фокуса; `sel`+`alarm` без hover/focus | `demo/smoke_iso_tiles.mjs:197-200,207-221` |
|
||
| **L5** — нет теста на `.dev.virtual` (Q1: без пунктира) | Новый ассерт `virtualNoDashedRing` на `.device-shell-frame` с классом `.virtual` | `demo/smoke_iso_tiles.mjs:201-203,222` |
|
||
| **L6** — плитки/тени без стен (`show_borders:false`) не покрыты тестом | Новые ассерты `noBordersKeepsTiles`/`noBordersKeepsShadows` через реальный `setServerConfig` | `demo/smoke_iso_tiles.mjs:224-238` |
|
||
|
||
## Унаследовано из r1
|
||
|
||
Без повторной проверки, документ `docs/reviews/CODE-REVIEW-649-r1.md`,
|
||
материал — фактическое содержимое `e5ab4659`/`2ed05031` (идентично по
|
||
существу тому, что рецензировалось в r1 под старыми SHA `e59c89df`/`de452182`,
|
||
см. обоснование выше — ребейз без изменения содержимого):
|
||
|
||
- AC1 (Flat неизменен, `test/styles-split.test.mjs`, единственный вызов
|
||
`furniturePlanScreenScale`), AC2 (геометрия/торец плитки), AC3 (коллизии
|
||
×1.12), AC4 (тень, таблица параметров), AC5 (рамки/приоритет — базовая
|
||
часть, за вычетом закрытых сейчас L4-пар), AC6 (forced-colors), AC7 (формула
|
||
луча, градиенты, стопы, ворота видимости — за вычетом закрытого сейчас M1),
|
||
AC8 (мебель, `stroke-width`), AC9 (тема не красит стены), AC10/AC11
|
||
(`settings.volumetric_view`, alpha-вход убран, backend-валидация), AC12
|
||
(макеты, side-by-side), Golden/матрица, мутационный реестр (16 старых
|
||
мутантов), бюджет бандла — все приняты по документу r1 без повторного
|
||
прогона в этом раунде, т.к. делта `106fdc09` их не касается.
|
||
- `python -m pytest tests_backend` не прогонялся ни в r1 (нет `pytest` в
|
||
песочнице), ни сейчас (делта не трогает Python) — тот же непокрытый гейт,
|
||
не расширившийся.
|
||
- ~70 «слабых» совпадений `smoke-select --base origin/dev` — решение r1 не
|
||
прогонять сохраняется без изменений (делта их не касается).
|
||
|
||
## Проверено и корректно
|
||
|
||
- M1 закрыта доказательством через исполнение, а не только чтением: юнит и
|
||
смок падают именно там, где ожидается (перед барьером — светло, за ним —
|
||
тень, рядом — снова светло; площадь луча уменьшается минимум на 20×50
|
||
плановых единиц), и все три мутанта реестра лично воспроизведены красными,
|
||
затем код и бандл возвращены в состояние `HEAD` (`git status` чист).
|
||
- L3 — фикс специфичностно корректен (новые селекторы объективно перебивают
|
||
старые) и сегодня не меняет видимый результат ни для одного из 6
|
||
`ISO_STATE_BODIES` (пересчитано вручную по порогам `luma<70`/`luma>0.98`),
|
||
что подтверждает трейлер `User-Visible: no`.
|
||
- L1 — все 4 места переписаны без противоречий с соседним текстом; других
|
||
вхождений «2.5D как alpha/hidden/opt-in» в кодовой базе не осталось
|
||
(`grep` по изменённым и смежным файлам).
|
||
- L4/L5/L6 — только новые тесты поверх неизменного, уже корректного (по
|
||
выводу r1) поведения; лично прогнаны, все зелёные.
|
||
- Три-way синхронизация бандла (`dist/**`, `custom_components/.../frontend`,
|
||
`demo/srv/assets`) чиста на `106fdc09` — подтверждено дважды: один раз как
|
||
есть, один раз после применения и отката мутанта `iso-sun-card-drops-occluders`.
|
||
- Монолит `src/houseplan-card.ts` — 12880 строк, без изменений (правка L1 в
|
||
нём — замена текста комментария в тех же строках, не добавление).
|
||
- Трейлеры всех трёх коммитов раунда корректны; `User-Visible: no` на
|
||
`106fdc09` обоснован построчно (доп. тесты + одна текстуально верная, но
|
||
сегодня недостижимая ветка CSS).
|
||
|
||
## Чего не проверял
|
||
|
||
- `python -m pytest tests_backend` — делта не трогает `custom_components/**/*.py`
|
||
(0 совпадений в статистике коммита); не гейт этой делты.
|
||
- `npm run golden:verify` / `npm run invariants` — делта `User-Visible: no`,
|
||
не меняет золотые сцены и не трогает каноническую геометрию модели.
|
||
- Полный `npm test` и полный `Validate` (мутанты) — приняты по зелёному
|
||
прогону на этом самом SHA (ссылка в задаче); перегонял только те тесты и
|
||
мутанты, что относятся к делте раунда.
|
||
- ~275 смоков вне `smoke_iso_sun.mjs`/`smoke_iso_tiles.mjs` — `smoke-select`
|
||
с базой ровно на делту (`2ed05031..HEAD`) не нашёл символьной связи
|
||
(НЕОПРЕДЕЛЁННОСТЬ) ни с одним из них, и содержательно делта не выходит за
|
||
пределы `iso-sun.ts`/`iso-tiles.ts`/тестов/документации; полный прогон
|
||
матрицы против всей фичи (78 прямых совпадений `origin/dev..HEAD`) унаследован
|
||
из r1 без повтора.
|
||
- Реальный экран/планшет, Safari — как и в r1, недоступны в песочнице.
|
||
|
||
## Вывод
|
||
|
||
High: 0. Medium: 0. Low: 0. Делта раунда (`106fdc09`) полностью закрывает
|
||
единственную блокирующую находку r1 (M1) доказательством через исполнение — я
|
||
лично применил все три новых мутанта реестра и подтвердил, что юнит и смок
|
||
падают именно так, как заявлено в `because`, а не просто прочитал код на
|
||
доверие. Пять Low из шести получили тесты или точечные правки без побочных
|
||
эффектов (L3 пересчитан на отсутствие видимого изменения сегодня — трейлер
|
||
`User-Visible: no` корректен); L2 была снята ещё в r1. Дельта локальна и
|
||
изолирована (`src/iso-sun.ts` не изменён вовсе, `src/iso-tiles.ts` — 2 строки,
|
||
остальное — тесты/доки/бандл), новых находок не внесла.
|
||
|
||
Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0
|
||
|
||
---
|
||
|
||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||
|
||
## Материал раунда
|
||
|
||
- Ветка: `issue/649-25d-stage6`, коммит `106fdc096d5d` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||
- Дерево материала: `77286175d538a146863d0b2f80a63e9ffa20af96`
|
||
```
|
||
git log --all --format='%H %T' | grep 77286175d538
|
||
```
|
||
- Тело issue: `e7d94f185bc9a5d2d7e168933579903e54c88a9819347cb49f0f7c35db60a9e7`
|
||
- Вердикт конвейера: `green` · High 0
|