mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
@@ -0,0 +1,264 @@
|
||||
# CODE-REVIEW-651-r1
|
||||
|
||||
Материал раунда: `git log --oneline origin/dev..HEAD` / `git diff origin/dev...HEAD`,
|
||||
SHA `c40266431ca1335ed09ebbaace9656b55ee84e23` (рабочая копия уже на нём).
|
||||
Коммиты: `4ab7ecb4` (`fix(2.5D): keep device overlays stable while zooming`,
|
||||
Issue #651, User-Visible: yes) и `c4026643` (`test(2.5D): retire superseded
|
||||
zoom mutant`, Issue #651, User-Visible: no).
|
||||
|
||||
## Скоуп
|
||||
|
||||
Задача переводит вычисление положения HTML-overlays устройств и lock badges в
|
||||
2.5D в стабильный масштаб «вписать всё» (`referenceView`) и заменяет
|
||||
независимое попарное разведение значков (`resolveIsoOverlayCollisions`) на
|
||||
жёсткую групповую раскладку (`resolveIsoOverlayRigidGroups`): все значки одной
|
||||
комнаты, чьи расширенные на 12 CSS px базовые bounds пересекаются, получают
|
||||
один общий вектор сдвига. Затронуты `src/iso-overlays.ts` (+338 строк, новый
|
||||
резолвер), `src/iso-scene-render.ts` (кэш/подпись раскладки берёт
|
||||
`referenceView`, а не live `view`), `src/houseplan-card.ts` (передаёт
|
||||
структурный `_baseVb('iso', structural)` как `referenceView`), тесты,
|
||||
`docs/ISOMETRIC.md`, оба changelog, `docs/STATUS.md`, `scripts/mutation-registry.mjs`.
|
||||
Список затронутых модулей совпадает с §14 ТЗ, выхода за список нет.
|
||||
|
||||
Соответствие `docs/SCOPE.md`: правка чинит J1/J6 (спатиальный обзор дома
|
||||
остаётся читаемым при зуме, план остаётся «true» после действий пользователя)
|
||||
в рамках уже принятого J1-исключения #89 на публичный 2.5D-режим; новых
|
||||
поверхностей, настроек или охвата не добавляет.
|
||||
|
||||
## Как проверялось
|
||||
|
||||
Валидация на `c4026643` уже зелёная
|
||||
(https://github.com/Matysh/houseplan-card/actions/runs/36206722475) —
|
||||
дешёвые гейты (`tsc`, `npm test`, `build`, мутанты по диффу) не перегонялись
|
||||
повторно вручную сверх точечной проверки ниже; тяжёлые прогнаны мной лично на
|
||||
этом же SHA, т.к. Validate явно пропустил их (`skipped`, reuse-маркер) для
|
||||
обоих коммитов диапазона.
|
||||
|
||||
| Гейт | Статус | Кем/чем подтверждено |
|
||||
|---|---|---|
|
||||
| `npx tsc --noEmit` | 🟢 | часть `npm run bundle:sync`, прогнан лично; 0 ошибок |
|
||||
| `npm test` (3099 тестов) | 🟢 (не дублировал) | job «Фронтенд: типы, юниты, мутанты, синхрон бандла» зелёной Validate на этом SHA; лично прогнал целиком только `test/iso-overlays.test.mjs` + `test/iso-scene-render.test.mjs` (50/50) при верификации мутаций ниже |
|
||||
| `npm run build` + сверка 3 копий бандла | 🟢 | прогнан лично (`bundle:sync`); `git status` после сборки — 0 диффов в `dist/`, `custom_components/.../frontend/`, `demo/srv/assets/` |
|
||||
| `node scripts/check-docs.mjs` | 🟢 (warn, как в CI) | прогнан лично с `--screenshots=$(node scripts/classify-changes.mjs --screenshots-mode)` → `warn`; единственное сообщение — ожидаемый WARN «screenshot source fingerprint is stale… before the beta candidate (#479)», это системное следствие любой правки `src/**` и не блокирует ни в CI, ни здесь (в `strict`-режиme без флага та же строка попадает в errors — не имеет отношения к #651, это дефолт скрипта, а не регресс) |
|
||||
| `demo/smoke_isometric_contract.mjs` | 🟢 | прогнан лично: все 46 проверок true, включая новую `desktopZoomKeepsOverlayScene` |
|
||||
| `demo/smoke_isometric_live_touch.mjs` | 🟢 | прогнан лично: все проверки true, включая новую `touchPinchKeepsOverlayScene` |
|
||||
| `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | 🟢, изучен | 8 прямых совпадений — все на `_openingsR` (проброшен без изменений в новый вызов, не новая логика); 35 слабых связей — все на `_baseVb` (реализация функции не менялась, только новый вызывающий код с явными аргументами); ни один из смоков сверх двух уже прогнанных изометрических не выбран для запуска — риск ограничен `iso-overlays.ts`/`iso-scene-render.ts`/4 строк в `houseplan-card.ts`, что уже покрыто прогнанными изометрическими смоками |
|
||||
| `npm run golden:verify` (полная матрица, видимое изменение) | 🟡 см. ниже | прогнан лично целиком (245 сценариев) на этом SHA после `npm run bundle:sync` |
|
||||
| `npm run benchmark:large-house-isometric` | 🟢 | прогнан лично на этом SHA: `sourceSha=c4026643…`, `panZoomMs` median 67.3 / p95 133.7 мс — бюджет `budgets-isometric-smoke.json` (`panZoomMs.hardMaxMs=600`, absolute) кратно не задет; сам absolute-гейт уже зелёный в Validate |
|
||||
| `npm run benchmark:isometric-stage3-dense` | 🟢 | прогнан лично на этом SHA: `panZoomMs` median 73.4 / p95 75.8 мс, тот же запас |
|
||||
| `npm run invariants -- --config …` | не прогонял | диффа геометрии модели/конфигурации нет — сдвиг overlay - runtime-only presentation state, не сериализуется (§7 ТЗ, подтверждено чтением: `nudgeScene`/`visualScene` нигде не пишутся в `RoomCfg`/`marker`) |
|
||||
| `python -m pytest tests_backend` | не прогонял | диффа `custom_components/**/*.py` нет |
|
||||
| Мутационные свидетели #651 (3 новых) | 🟢 | зафиксированы «Мутанты по диффу (1–6/6)» зелёной Validate на `c4026643`; дополнительно перечитаны сами патчи (см. находки о непокрытых веток ниже) |
|
||||
|
||||
### Golden: что показал полный прогон
|
||||
|
||||
5 из 245 сценариев вышли `different` — все прямо относятся к 2.5D-оверлеям
|
||||
(остальные 240, включая `isometric-geometry-view-*`, `isometric-touch-kiosk-dark`,
|
||||
`isometric-wall-junctions-dark`, Flat-эквивалент `large-house-warm-remount-dark`
|
||||
на той же `perf-floor-2` фикстуре — не изменились):
|
||||
|
||||
- `isometric-stage3-overlays-light` (diffRatio 1.76 %), `-dark` (2.10 %),
|
||||
`isometric-stage3-forced-colors-dark` (0.94 %), `isometric-stage3-no-filter-dark`
|
||||
(0.97 %) — визуально проверены: значок/бейдж и связанный с ним lock badge той
|
||||
же комнаты сдвигаются вместе на несколько CSS px в сторону от стены; ряд и
|
||||
взаимные интервалы сохраняются, диагонального распада нет.
|
||||
- `isometric-large-warm-remount-dark` (8.21 %) — самый крупный диффа. Проверено
|
||||
по кропам (`Room 2.1` и соседние): **старый** эталон рисовал в этой
|
||||
стресс-фикстуре по 2–3 визуально разведённых значка на комнату, где во Flat
|
||||
(`large-house-warm-remount-dark`, тот же `perf-floor-2`, не изменился и
|
||||
остался `passed`) на то же место приходится **ровно один** значок —
|
||||
т.е. старый попарный резолвер `resolveIsoOverlayCollisions` расталкивал
|
||||
канонически совпадающие точки, которых в Flat не существует. Новый код
|
||||
показывает ровно один значок на комнату, как во Flat. Это не регрессия, а
|
||||
прямое следствие требования §5.2.3 ТЗ («горизонтальный ряд… остаётся…»,
|
||||
обобщённо — совпадающие точки остаются совпадающими) и корректный результат.
|
||||
|
||||
Новые baseline-PNG в диффе НЕ приняты — это правильно и соответствует AC7
|
||||
(«новый baseline принимается только из reviewed Linux artifact в ближайшем
|
||||
beta-candidate, а не самовольно в author-коммите»). Формулировка автора в
|
||||
комментарии к реализации («визуальный контракт проверяется существующими
|
||||
Linux golden-сценами isometric-stage3-*») неточна: существующие эталоны сейчас
|
||||
**не совпадают** с рендером этого SHA — обязательство §10.4 ТЗ («golden — перед
|
||||
beta по процессу») выполнено правильно тем, что PR не подделал их
|
||||
самостоятельно, но фраза «проверяется существующими сценами» вводит в
|
||||
заблуждение (эталоны как раз показывают ожидаемое расхождение, а не
|
||||
подтверждение). На вердикт это не влияет — сути AC7 (проверка ревьюером
|
||||
«одного общего сдвига без диагонального распада») сделанный мной прогон и
|
||||
разбор изображений закрывают.
|
||||
|
||||
## Находки
|
||||
|
||||
### Medium-1 (в скоупе, возврат автору) — AC1 «другая комната исключена» не доказан ни тестом, ни мутацией
|
||||
|
||||
`resolveIsoOverlayRigidGroups`/`rigidOverlayGroups` (`src/iso-overlays.ts:1738`)
|
||||
объединяет в одну жёсткую группу значки только при совпадении
|
||||
`placement.owner?.id`. Это ключевая часть AC1 («device markers и lock badges
|
||||
одной комнаты образуют ожидаемые компоненты; другая комната… исключены») и
|
||||
явно обещанного теста в §10.1 ТЗ («матрица групп… перестановок»). Единственный
|
||||
новый тест (`test/iso-overlays.test.mjs`, «rigid overlay groups preserve a
|
||||
row…») проверяет только один сценарий — три значка ОДНОЙ комнаты. Ни один тест
|
||||
не ставит рядом значки двух РАЗНЫХ комнат.
|
||||
|
||||
**Воспроизведение (чем краснеет — точнее, чем не краснеет).** Закомментировал
|
||||
проверку владельца в join-цикле:
|
||||
|
||||
```ts
|
||||
// было: if (stable[right].placement.owner?.id !== owner) continue;
|
||||
// стало: // mutated: room ownership check removed
|
||||
```
|
||||
|
||||
Пересобрал `test-build` (`npx tsc -p tsconfig.test.json && node
|
||||
scripts/fix-test-build.mjs`) и прогнал `node --test test/iso-overlays.test.mjs
|
||||
test/iso-scene-render.test.mjs` — **50/50 тестов прошли**, ни один не заметил,
|
||||
что значки двух разных комнат теперь могут объединяться в одну жёсткую группу
|
||||
и сдвигаться единым вектором через границу комнаты. Правка отменена
|
||||
(`git diff` пуст, репозиторий не тронут).
|
||||
|
||||
Риск: будущий рефакторинг рядом (например, изменение условия соединения) может
|
||||
незаметно позволить группе «перетекать» через границу комнаты — именно то,
|
||||
для защиты от чего ТЗ явно требует этот тест.
|
||||
|
||||
### Medium-2 (в скоупе, возврат автору) — AC4 «деградированный fallback группы» не доказан ни тестом, ни мутацией
|
||||
|
||||
§5.3.5 ТЗ — самая рискованная новая ветка (собственный риск №1/2 из §11 ТЗ):
|
||||
если ни один кандидат-сдвиг не свободен от конфликтов, `resolveIsoOverlayRigidGroups`
|
||||
выбирает «ближайший вектор, который сохраняет принадлежность комнате и
|
||||
минимизирует сначала конфликт со стеной, затем с другой группой» — реализовано
|
||||
в `rigidFallbackOrder` (`src/iso-overlays.ts:1690`): сравнение по
|
||||
`roomViolations`, затем `wallViolations`, затем `overlapPenalty`. §10.1 ТЗ прямо
|
||||
обещает тест на «невозможного размещения и границы 48 px» в
|
||||
`test/iso-overlays.test.mjs`. Такого теста нет; нет и мутанта в
|
||||
`scripts/mutation-registry.mjs`, нацеленного на эту функцию (три новых мутанта
|
||||
бьют по: масштабу раскладки, факту соединения в группу, факту использования
|
||||
нового резолвера в проде — не по порядку приоритетов деградации).
|
||||
|
||||
**Воспроизведение.** Заменил тело `rigidFallbackOrder` на «игнорировать
|
||||
room/wall/overlap, сравнивать только по расстоянию»:
|
||||
|
||||
```ts
|
||||
function rigidFallbackOrder(left: RigidCandidate, right: RigidCandidate): number {
|
||||
return rigidOffsetOrder(left.offset, right.offset);
|
||||
}
|
||||
```
|
||||
|
||||
Пересобрал test-build и прогнал те же два файла — **50/50 тестов прошли**.
|
||||
Ни один существующий тест не ставит группу в положение, где после перебора всех
|
||||
кандидатов остаётся конфликт, поэтому приоритет «сначала не покинуть комнату,
|
||||
потом не задеть стену, потом не пересечься с другой группой» сейчас ничем не
|
||||
защищён: обратный порядок или полное игнорирование прошли бы CI незамеченными.
|
||||
Правка отменена, `git diff` пуст.
|
||||
|
||||
Обе находки — Medium, в скоупе задачи (тесты этого самого файла, обещанные в
|
||||
уже одобренном ТЗ), без High. По правилу §3 п.8/§2.7 это жёлтый вердикт и
|
||||
возврат автору в этом же issue, а не отдельная задача.
|
||||
|
||||
### Low — неточная формулировка в комментарии к реализации (снято без правки)
|
||||
|
||||
Комментарий автора к реализации утверждает, что визуальный контракт
|
||||
«проверяется существующими Linux golden-сценами» — на деле эти сцены сейчас
|
||||
показывают ожидаемое расхождение (см. раздел Golden выше), а не подтверждение.
|
||||
Не меняет вердикт и не требует действий в коде/ТЗ — фиксирую только для
|
||||
точности учёта golden-цикла на бете; правку не считаю нужной.
|
||||
|
||||
## Что проверено и признано корректным
|
||||
|
||||
- **AC3 (инвариантность zoom/pan).** `layoutView = input.referenceView ||
|
||||
input.view` (`src/iso-scene-render.ts:806`); `_baseVb('iso', structural)`
|
||||
берёт `scene.frame`/`_frameOf().rect`, т.е. геометрию фита, а не live
|
||||
`_view` (`src/houseplan-card.ts:6054`, `6075-6092`) — подтверждено чтением и
|
||||
тестом `#651 supersedes #473 W3` (`test/iso-scene-render.test.mjs`), который
|
||||
меняет `view` без изменения `referenceView` и требует `strictEqual` того же
|
||||
объекта размещения; мутант `iso-rigid-groups-use-live-zoom-scale` ловится этим
|
||||
же тестом (подтверждено зелёной Validate). Браузерные смоки
|
||||
`desktopZoomKeepsOverlayScene`/`touchPinchKeepsOverlayScene` подтверждают то
|
||||
же в реальном DOM (прогнано лично).
|
||||
- **AC2 (один вектор на группу, ряд остаётся горизонтальным).** Тест «rigid
|
||||
overlay groups preserve a row…» и «render scene keeps a close device cluster
|
||||
rigid…» проверяют `nudgeCss`/`visualScene` идентичность внутри группы и
|
||||
инвариантность к порядку входа; мутант `iso-rigid-groups-split-close-row`
|
||||
(отключение join) ловится первым тестом (зелёная Validate).
|
||||
- **Производственная проводка (AC — переключение на новый резолвер).**
|
||||
`src/iso-scene-render.ts:987` вызывает `resolveIsoOverlayRigidGroups` только
|
||||
в `mode === 'live'`; `resolveIsoOverlayCollisions` в проде более не
|
||||
вызывается (`grep` подтверждает единственное вхождение — сама декларация).
|
||||
Мутант `iso-scene-restores-per-marker-collision-resolver` возвращает старый
|
||||
вызов и ловится тестом «keeps a close device cluster rigid» (зелёная
|
||||
Validate).
|
||||
- **Room labels не входят в группу.** `entries.flatMap` в
|
||||
`buildIsoOverlayRenderScene` явно отфильтровывает `entry.kind ===
|
||||
'room-label'` перед передачей в `resolveIsoOverlayRigidGroups`
|
||||
(`src/iso-scene-render.ts:988-989`) — проверено чтением, не тестом
|
||||
(структурная гарантия, не защитный AC — низкий риск регрессии, т.к. типы
|
||||
`IsoOverlayCollisionKind` уже исключают `'room-label'` на уровне TypeScript).
|
||||
- **48 px cap и safety-gap глобально.** `addOffset` в
|
||||
`resolveIsoOverlayRigidGroups` отбрасывает кандидатов с `distance > maxNudge
|
||||
+ EPS`; кандидаты строятся из `buildIsoOverlayBoundaryCandidates`/
|
||||
`addBoundaryCircle`, оба уже покрыты существующими unit-тестами на границу.
|
||||
Не покрыт именно ВЫБОР среди легальных-по-cap, но конфликтующих кандидатов —
|
||||
см. Medium-2.
|
||||
- **AC8 (перформанс).** Оба профиля прогнаны мной лично на точном SHA
|
||||
`c4026643`, бюджетные файлы (`demo/performance/*.json`) не менялись в диффе.
|
||||
- **Трейлеры и changelog.** `Issue: #651` + `User-Visible: yes|no` на обоих
|
||||
коммитах; `docs/CHANGELOG.md`/`docs/CHANGELOG.ru.md` правлены в том же
|
||||
коммите `4ab7ecb4`, что и код (не в последующем). Одно число с одним
|
||||
источником: константы `ISO_OVERLAY_MAX_NUDGE_CSS_PX` (48),
|
||||
`ISO_OVERLAY_RIGID_GROUP_GAP_CSS_PX` (12), `ISO_OVERLAY_SAFETY_GAP_CSS_PX` (4)
|
||||
объявлены один раз и переиспользованы; `docs/ISOMETRIC.md` описывает те же
|
||||
числа словами, не дублирует их как отдельный источник правды.
|
||||
- **Три копии бандла.** После `npm run bundle:sync` `git status` не показал
|
||||
диффов ни в `dist/`, ни в `custom_components/houseplan/frontend/`, ни в
|
||||
`demo/srv/assets/` — сборка детерминирована и уже соответствует материалу.
|
||||
- **docs/reviews/INDEX.md** обновлён автогенератором и синхронен со SPEC-REVIEW
|
||||
r1/r2 этого issue.
|
||||
|
||||
## Чего не проверял и почему
|
||||
|
||||
- **Полный `npm test`** — не перегонял целиком лично (только два целевых
|
||||
файла много раз при верификации мутаций); полагаюсь на зелёный job
|
||||
«Фронтенд…» в уже подтверждённой Validate на этом самом SHA (дешёвый гейт,
|
||||
инструкция явно разрешает не дублировать).
|
||||
- **35 «слабых» смоков из `smoke-select.mjs`** (все на `_baseVb`) — не
|
||||
прогонял: сигнатура и реализация `_baseVb` не менялись, только добавлен один
|
||||
новый вызывающий код; риск ограничен уже прогнанными изометрическими
|
||||
смоками.
|
||||
- **`npm run invariants`** — геометрия плана/стен/проёмов не меняется (это
|
||||
presentation-only runtime cache для overlay), поэтому гейт не применим; не
|
||||
формальный пропуск, а обоснованный чтением §7 ТЗ и кода (`nudgeScene`
|
||||
нигде не попадает в `RoomCfg`/config).
|
||||
- **`pytest tests_backend`** — нет диффа в `custom_components/**/*.py`.
|
||||
- **Golden `main`/предрелизный accept** — не выполнял и не должен: AC7 прямо
|
||||
запрещает принимать новый baseline в author-коммите; свою часть (просмотр
|
||||
diff-изображений, суждение «один сдвиг, не диагональ») выполнил вручную по
|
||||
выгруженным PNG этого прогона.
|
||||
- **`isometric-stage3-openings-dark` и другие golden с opening/lock фокусом** —
|
||||
прошли (`passed`) сами, детально не разбирал сверх этого.
|
||||
|
||||
## Вердикт
|
||||
|
||||
Оба AC, вынесенных в находки, входят в собственный (уже одобренный, зелёный на
|
||||
ревью ТЗ) тестовый план задачи (§10.1) и являются защитными/инвариантными по
|
||||
характеру (границы группы по комнате; порядок приоритетов деградированного
|
||||
fallback) — по правилу REVIEWER.md пустая графа «чем краснеет» для такого AC
|
||||
есть находка Medium сама по себе, а обе находки здесь дополнительно
|
||||
подтверждены явным мутационным экспериментом (50/50 тестов проходят при
|
||||
удалении защиты). High-находок нет, реализация в остальном корректна,
|
||||
инвариантность к zoom/pan и групповая раскладка проверены чтением, тестом и
|
||||
исполнением (unit + 2 браузерных смока + перформанс на точном SHA + ручной
|
||||
разбор golden-диффов). Вердикт — жёлтый, работа возвращается автору: дополнить
|
||||
`test/iso-overlays.test.mjs` (и по необходимости `mutation-registry.mjs`)
|
||||
сценариями «две разные комнаты не объединяются» и «невозможное размещение
|
||||
внутри 48 px», как было обещано в §10.1 уже одобренного ТЗ.
|
||||
|
||||
---
|
||||
|
||||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||||
|
||||
## Материал раунда
|
||||
|
||||
- Ветка: `issue/651-iso-device-layout-stability`, коммит `c40266431ca1` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||||
- Дерево материала: `7afb4dcc4787ceabd952ead5915616bd806a98b2`
|
||||
```
|
||||
git log --all --format='%H %T' | grep 7afb4dcc4787
|
||||
```
|
||||
- Тело issue: `699e063899bf674ad775879fc1a0c889489a2f90b14fde467497787cc8f69442`
|
||||
- Вердикт конвейера: `yellow` · High 0
|
||||
@@ -1,11 +1,12 @@
|
||||
# Индекс ревью
|
||||
|
||||
Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 1066, issue: 376. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`.
|
||||
Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 1067, issue: 376. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`.
|
||||
|
||||
| Issue | Документ | Этап · раунд | Вердикт | H | M | Находки | Файлы |
|
||||
|---|---|---|---|---:|---:|---|---|
|
||||
| #651 | [SPEC-REVIEW-651-r1.md](SPEC-REVIEW-651-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | устаревшая формулировка «экспериментальный 2.5D-вид» противоречит текущему статусу функции | `CHANGELOG.md` `CHANGELOG.ru.md` `docs/ISOMETRIC.md` `docs/USER-GUIDE.ru.md` `docs/STATUS.md` `USER-GUIDE.ru.md` |
|
||||
| #651 | [SPEC-REVIEW-651-r2.md](SPEC-REVIEW-651-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | — |
|
||||
| #651 | [CODE-REVIEW-651-r1.md](CODE-REVIEW-651-r1.md) | code · r1 | 🟡 жёлтый | 0 | 2 | AC1 «другая комната исключена» не доказан ни тестом, ни мутацией; AC4 «деградированный fallback группы» не доказан ни тестом, ни мутацией; неточная формулировка в комментарии к реализации (снято без правки) | `src/iso-overlays.ts` `test/iso-overlays.test.mjs` `scripts/mutation-registry.mjs` |
|
||||
| #650 | [CODE-REVIEW-650-r1.md](CODE-REVIEW-650-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — |
|
||||
| #649 | [SPEC-REVIEW-649-r1.md](SPEC-REVIEW-649-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | в скоупе задачи (возвращается автору); принято ревьюером с записью, правки не требует | `lab.js` `houseplan-card.ts` |
|
||||
| #649 | [SPEC-REVIEW-649-r2.md](SPEC-REVIEW-649-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | — |
|
||||
|
||||
Reference in New Issue
Block a user