mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-03 05:08:53 +00:00
200 lines
16 KiB
Markdown
200 lines
16 KiB
Markdown
# CODE-REVIEW — issue #441 · заход r2
|
||
|
||
Ветка `issue/441-vacuum-route-draft`, HEAD на ревью `75478905` (проверено
|
||
`git rev-parse HEAD` непосредственно перед выводом, §2.7).
|
||
|
||
## Почему это заход r2, а не r1
|
||
|
||
Заголовок задачи, переданный этой сессии, утверждает «заход r1 ·
|
||
блокирующих циклов 0 из 2». Это не подтверждается материалом:
|
||
|
||
- в комментариях issue #441 уже есть терминальный вердикт код-ревью:
|
||
`Вердикт: жёлтый · заход r1 · блокирующих циклов 1/2 · High: 0 ·
|
||
Medium: 1 → в задаче` (комментарий `claude`,
|
||
`IC_kwDOTOcLQM8AAAABSa2lLw`, 19:35:18 UTC);
|
||
- следом (19:35:28 UTC) владелец подтвердил: «Автоматическое ревью не
|
||
отработало… Статусная метка не менялась» — то есть вердикт реальный,
|
||
но шаг конвейера, переставляющий метку после вердикта (§10.4), не
|
||
выполнился, и задача осталась висеть в `S7-code-review`, откуда её
|
||
забрала эта сессия повторно;
|
||
- полный документ того раунда уже закоммичен в ветку самим конвейером
|
||
(`75478905 docs: review document for #441`, автор `claude[bot]`) —
|
||
это штатный механизм публикации (§10.4: «кладёт документ в
|
||
`docs/reviews/` ветки задачи»), не постороннее вмешательство;
|
||
- SHA того раунда назван в документе — `fa41d298` — и он не осиротел:
|
||
`git diff fa41d298..HEAD` резолвится и виден ниже.
|
||
|
||
Таким образом это второй заход ревью, разбор — по дельте (§2.10), а не
|
||
заново. Файл `docs/reviews/CODE-REVIEW-441-r1.md` уже существует, второй
|
||
документ с тем же номером затёр бы его, поэтому этот — `r2`.
|
||
Присвоенный этой сессии заголовок «r1 · 0/2», видимо, следствие того же
|
||
сбоя конвейера, который не переставил метку: трекер раундов не увидел
|
||
уже опубликованный r1. Само по себе это не код-находка issue #441 и не
|
||
блокирует вердикт, но чтобы бюджет §4 не потерялся — ниже он посчитан по
|
||
факту (см. «Вердикт»), а не по переданному заголовку.
|
||
|
||
## Материал предыдущего раунда
|
||
|
||
- Документ: `docs/reviews/CODE-REVIEW-441-r1.md` (в этой же ветке, коммит `75478905`).
|
||
- SHA раунда: `fa41d298` (`fix: keep vacuum routes pending until a floor is chosen`).
|
||
- Вердикт r1: жёлтый, High 0, Medium 1 в скоупе — красный `check-docs.mjs`
|
||
на `fa41d298` (устаревший отпечаток скриншотов документации).
|
||
|
||
## Дельта r1 → r2
|
||
|
||
```
|
||
git diff fa41d298..HEAD --stat
|
||
docs/images/screenshots.json | 25 ++--
|
||
docs/reviews/CODE-REVIEW-441-r1.md | 246 +++++++++++++
|
||
```
|
||
|
||
Два файла, оба класса C (документация/артефакт ревью). Продуктовый код
|
||
(`src/**`), тесты и смоки, тронутые в `fa41d298`, дельтой r1→r2 не
|
||
задеты — `git diff fa41d298..HEAD -- src test demo scripts
|
||
docs/CHANGELOG.md docs/CHANGELOG.ru.md` пуст. Это делает дельту
|
||
локальной по §2.10: полный повторный разбор AC не требуется, только
|
||
проверка закрытия находки r1 плюс дешёвые гейты.
|
||
|
||
`docs/reviews/CODE-REVIEW-441-r1.md` — служебный артефакт публикации
|
||
предыдущего раунда, не правка автора; содержательного разбора не
|
||
требует.
|
||
|
||
## Закрытие раунда r1
|
||
|
||
| Находка r1 | Чем закрыта | Где это видно |
|
||
|---|---|---|
|
||
| Medium: `node scripts/check-docs.mjs` красный на `fa41d298` — отпечаток скриншотов документации устарел после правки `src/editors/vacuum-maps-section.ts` | Коммит `0ea9c0f5` `docs: refresh canonical screenshots for #441` обновил `sourceFingerprint`/`sourceSha256` в `docs/images/screenshots.json` на все 10 сценариев; `imageSha256` каждого сценария не изменился (диалог, который правит #441, ни в одном сценарии не рендерится — пиксели те же), манифест получил `acceptance.lastWriteWasFingerprintOnly: true` — след легитимной приёмки `scripts/docs-accept.mjs` (fingerprint-only ветка `acceptedDocsManifest`, docs-accept.mjs:107), а не самодельной правки JSON | `git diff fa41d298..HEAD -- docs/images/screenshots.json`; перепрогнан `node scripts/check-docs.mjs` на HEAD `75478905` → `Documentation checks passed (7 files, 10 external links)` (было `ERROR screenshot source fingerprint is stale`) |
|
||
|
||
Находка закрыта корректно и через штатный инструмент приёмки
|
||
(`docs-accept.mjs`, а не ручная правка `screenshots.json` мимо
|
||
проверки совпадения пикселей) — именно та дисциплина, которую требует
|
||
§8 («скриншоты снимаются только… принимаются локально `docs:accept`»).
|
||
|
||
## Унаследовано из r1
|
||
|
||
Без повторной проверки принято (документ `docs/reviews/CODE-REVIEW-441-r1.md`,
|
||
материал — SHA `fa41d298`, тот же SHA подтверждён этой сессией как
|
||
резолвящийся предок HEAD):
|
||
|
||
- **AC1** (черновик до выбора этажа) — доказательство `beginVacuumRouteDraft`
|
||
(`src/vacuum-route-edit.ts:34`) и `test/vacuum-routes.test.mjs:279` —
|
||
код не менялся дельтой; для собственного спокойствия эта сессия
|
||
всё же **независимо повторила** мутацию r1 (не обязательна по §2.10,
|
||
сделана как дешёвая проверка): временно заменила
|
||
`space: routes.length ? '' : dockSpace` на `space: dockSpace`,
|
||
пересобрала `test-build` (`npx tsc -p tsconfig.test.json &&
|
||
node scripts/fix-test-build.mjs`) и прогнала
|
||
`node --test test/vacuum-routes.test.mjs` → `not ok 25 - новый
|
||
маршрут живёт в черновике до выбора этажа (#441 AC1)`, `# fail 1`;
|
||
откат мутации вернул `# pass 32 / # fail 0`. Рабочее дерево после
|
||
отката чисто (`git status --short` пуст). AC1 подтверждён на текущем
|
||
SHA, а не только унаследован по документу.
|
||
- **AC2** (только валидная атомарная запись) — `commitVacuumRouteDraft`
|
||
(`src/vacuum-route-edit.ts:39-56`), `test/vacuum-routes.test.mjs:287`,
|
||
смоки `noEmptySpaceEverSubmitted`/`rejectedPayloadWasValid`/
|
||
`retryKeepsExactIdentity` — код не менялся, наследуется по документу r1.
|
||
- **AC3** (отмена/отказ не оставляют фантом) — `persistRoutes`/`confirmDraft`
|
||
(`src/editors/vacuum-maps-section.ts`), смоки
|
||
`cancelLeavesAcceptedRoutes`/`rejectionRestoresAcceptedMarker`/
|
||
`rejectionKeepsEditorUsable` — код не менялся, наследуется по документу r1.
|
||
- Наблюдение r1 про побочное усиление `retarget`/`drop` (переход на общий
|
||
`persistRoutes`/`rollbackOptimistic`) и про отсутствие новых ключей i18n —
|
||
без изменений, код не тронут дельтой.
|
||
- Оценка `docs/SCOPE.md` (J4/J6) — без изменений, продуктовый скоуп не
|
||
менялся.
|
||
|
||
## Гейты — что прогнал в этом раунде и с каким результатом
|
||
|
||
Дельта r1→r2 не трогает `src/**`/`test/**`, поэтому по §2.10 достаточно
|
||
дешёвых гейтов; они прогнаны заново на HEAD `75478905`, а не унаследованы:
|
||
|
||
| Гейт | Команда | Результат |
|
||
|---|---|---|
|
||
| Typecheck | `npx tsc --noEmit` | чисто, без вывода |
|
||
| Тесты | `npm test` | `# tests 1857 · # pass 1856 · # fail 0 · # skipped 1` — совпадает с заявленным в r1 |
|
||
| Сборка + синхронизация бандла | `npm run build`, затем `git status --short` | `dist` пересобран, working tree чист после сборки — `dist/houseplan-card.js` и `custom_components/houseplan/frontend/houseplan-card.js` совпадают в коммите (`cmp` → без вывода, идентичны) |
|
||
| Документация | `node scripts/check-docs.mjs` | **зелёный**: `Documentation checks passed (7 files, 10 external links)` — находка r1 закрыта (см. выше) |
|
||
| Мутация AC1 (доп. проверка этого раунда) | временный откат `src/vacuum-route-edit.ts:34` → `space: dockSpace`, `npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs && node --test test/vacuum-routes.test.mjs` | `not ok 25` на мутанте, `# pass 32 / # fail 0` после отката — дисциплина «тест умеет падать» подтверждена независимо |
|
||
| Смок AC1–AC3 | `node demo/smoke_vacuum_route_draft.mjs` (после `npm run bundle:sync`, иначе смок падает на отсутствующем `demo/srv/assets/houseplan-card.js` — сгенерированный, не коммитится, #255) | все 14 проверок `true`, `OK` |
|
||
|
||
Не прогонялись повторно (дельта их не задевает, обоснование см. в
|
||
документе r1, наследуется): `smoke-select.mjs` по полному диффу,
|
||
`golden:verify`, `pytest tests_backend`, `npm run invariants`,
|
||
performance-профили, остальные точечные смоки (`smoke_vacuum_firstuse`,
|
||
`smoke_vacuum_multifloor`, `smoke_save_race`, `smoke_config_writer`) —
|
||
их результат на `fa41d298` уже зафиксирован в r1 и код, который они
|
||
покрывают, не менялся.
|
||
|
||
Трейлеры дельты: `0ea9c0f5` — `Issue: #441`, `User-Visible: no` (верно:
|
||
обновление служебного отпечатка, а не пользовательского поведения,
|
||
changelog не требуется); `75478905` — `Issue: #441`, `User-Visible: no`
|
||
(служебный артефакт публикации ревью, класс C).
|
||
|
||
## Находки
|
||
|
||
Нет. Единственная находка r1 (Medium, в скоупе) закрыта корректно и
|
||
через штатный инструмент. Новых находок дельта r1→r2 не вносит — она
|
||
состоит из точечного обновления отпечатка документации и служебного
|
||
коммита самого конвейера.
|
||
|
||
## Что проверено и корректно (без отдельной находки)
|
||
|
||
- `docs/images/screenshots.json`: у всех 10 сценариев `imageSha256`
|
||
не изменился при обновлении `sourceFingerprint` — подтверждает
|
||
заявление r1, что правка не задевает ни один заскриншоченный экран
|
||
(диалог второго маршрута пылесоса нигде не снимается для документации).
|
||
- Приёмка прошла через `scripts/docs-accept.mjs` (ветка
|
||
`lastWriteWasFingerprintOnly`), а не через ручную правку JSON —
|
||
соответствует требованию §8 «коммит скриншотов делает человек» и
|
||
«приёмка отказывает, если кандидат снят не с этого дерева»: сам
|
||
скрипт это проверяет (`verifyDocsCandidate`), а не декларация автора.
|
||
- `docs/reviews/CODE-REVIEW-441-r1.md` корректно оформлен как документ
|
||
своего раунда и не требует правок задним числом (документы ревью не
|
||
переписываются, только дополняются новым заходом).
|
||
|
||
## Чего не проверял
|
||
|
||
- Полный повторный разбор AC1–AC3 по коду и всем смокам — не требуется
|
||
по §2.10, т.к. дельта не трогает `src/**`/`test/**`/`demo/**`; AC1
|
||
дополнительно перепроверен мутацией (см. «Гейты»), AC2/AC3 унаследованы.
|
||
- `npm run golden:verify`, `pytest tests_backend`, `npm run invariants`,
|
||
performance-профили — не прогонял; обоснование то же, что в r1
|
||
(View, backend `.py`, геометрия/ссылки на неё не затронуты ни
|
||
`fa41d298`, ни дельтой r1→r2).
|
||
- `node scripts/smoke-select.mjs --base origin/dev --head HEAD` по
|
||
полному диффу — не перегонял: набор символов на изменённых строках
|
||
не изменился с r1 (дельта не трогает код), решение по каждому пункту
|
||
из r1 остаётся в силе.
|
||
- Реальность прогона CI-джобы `Docs screenshots` для коммита `0ea9c0f5`
|
||
(не видна из репозитория) — доверие основано на структурном
|
||
доказательстве самого `scripts/docs-accept.mjs` (совпадение
|
||
`imageSha256` с уже закоммиченными файлами и наличие
|
||
`acceptance.lastWriteWasFingerprintOnly`), а не на слепом заявлении
|
||
автора; независимый пересчёт `visualFingerprint(root)` на HEAD не
|
||
делал отдельно от `check-docs.mjs`, который эту сверку и выполняет.
|
||
|
||
## Вердикт
|
||
|
||
Зелёный. AC1–AC3 подтверждены (AC1 — независимо на HEAD этой сессией,
|
||
AC2/AC3 — унаследованы из r1 по неизменному коду), единственная
|
||
Medium-находка r1 закрыта штатным инструментом и подтверждена зелёным
|
||
`check-docs.mjs` на HEAD `75478905`. Новых находок нет.
|
||
|
||
Бюджет циклов (§4): r1 был жёлтым и потратил 1 цикл из 2 (лёгкий/короткий
|
||
трек, лимит код-ревью — 2, §5.1). Этот заход (r2) зелёный и цикл не
|
||
образует (#227) — израсходовано остаётся **1 из 2**, а не 0 из 2, как
|
||
было в заголовке этой сессии; расхождение объяснено в разделе «Почему
|
||
это заход r2».
|
||
|
||
---
|
||
|
||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||
|
||
## Материал раунда
|
||
|
||
- Ветка: `issue/441-vacuum-route-draft`, коммит `75478905d5eb` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||
- Дерево материала: `6e2f7f4813e3b45927eb282b1e5fdd62b872d019`
|
||
```
|
||
git log --all --format='%H %T' | grep 6e2f7f4813e3
|
||
```
|