Files
houseplan-card/docs/reviews/CODE-REVIEW-515-r1.md
T
2026-09-09 21:13:52 +00:00

150 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CODE-REVIEW-515-r1
Материал: `5a1cddeaf0fbac3e96bdc7ace20091a4549691c1`, ветка
`issue/515-material-anchors-after-rebase`, `origin/dev` не ушёл с этой темы
дальше — единственный коммит в диапазоне `origin/dev..HEAD`.
## Скоуп
Issue #515 (light track, ТЗ в теле issue): якоря материала ревью (`sha`,
`tree`, `specs`), которые публикация зашивает в машинный блок документа,
снимались в шаге «Перейти на ветку задачи» — **до** шага «Привести ветку к
dev». Когда `dev` ушёл вперёд, конвейер ребейзит ветку и делает
force-push; коммит и дерево, снятые до ребейза, становятся недостижимыми
на origin. Это ломало reuse (#499, всегда `reuse=false` в свежем клоне) и
красило пост-шаг «Материал раунда воспроизводим (#413)» на зелёном раунде.
Диффа два файла, оба класса B (Гейты и инструменты, PROCESS.md §1):
`.github/workflows/process.yml` (перенос снятия `tree`/`specs` из шага
`branch` в шаг `material`, который уже существовал и уже снимал `sha`
после ребейза; смена источника `MATERIAL_SHA/TREE/SPECS` в шаге публикации
с `steps.branch.outputs.*` на `steps.material.outputs.*`) и
`test/review-doc-guard.test.mjs` (новый контрактный тест на текст
workflow). Класса A (продуктовый код) диффа не задевает.
Замечание не по существу проверки: задача помечена `infra`/`process` и по
механическому признаку §1 (ни одного файла класса A) могла бы идти «вне
флоу» без код-ревью вовсе — но провести пайплайн-ревью пайплайна через сам
пайплайн разумно именно потому, что часть AC (см. ниже) в принципе
проверяется только живым прогоном следующей задачи. Решение маршрутизации
не моё, и на итог ревью не влияет.
## Как проверялось
- Полное чтение диффа (`git diff origin/dev...HEAD`) и контекста вокруг
него в `process.yml`: шаги `branch` (id=branch), `rebase` (id=rebase),
`material` (id=material), `reuse` (id=reuse, #499), «Материал раунда
воспроизводим» (#413), «Опубликовать документ ревью», «Слить ветку в
dev», «dev ушёл вперёд, пока шло ревью» — все места, где раньше или
теперь читаются `steps.branch.outputs.*` / `steps.material.outputs.*`.
- Чтение `scripts/review-doc-guard.mjs`: `anchorTreeFrom`, `anchorLiveness`,
`reusableGreenVerdict`, ветки `--reuse` и `--doc=` в CLI — то есть
фактическую механику, которая потребляет якоря, записанные новым кодом.
- `grep -rn "steps\.branch\.outputs\.\(tree\|specs\)"` по всему репозиторию
(md/yml/mjs) — ноль совпадений, старый источник нигде не остался.
- **Тест умеет падать** — исполнено, не заявлено: временно подменил
`.github/workflows/process.yml` на версию `origin/dev` (до фикса),
прогнал `node --test test/review-doc-guard.test.mjs`. Новый тест `#515`
красный:
`error: 'дерево — из шага material'`, актуальный текст шага `material`
в выводе ассерта — старая версия без `tree=`. Вернул файл на HEAD
(`git status --short` — пусто, рабочая копия чистая), прогнал тест ещё
раз: `49/49 pass`, включая новый.
- `python3 -c "import yaml; yaml.safe_load(...)"` — YAML `process.yml`
валиден.
- `bash -n` на извлечённых телах трёх изменённых `run:`-блоков (`branch`,
`material`, «Опубликовать документ ревью») — синтаксис чист.
- `node scripts/smoke-select.mjs --base origin/dev --head HEAD` →
«Исполняемого frontend-диффа нет… Тронуто файлов: 2» — смоки не
выбираются, подтверждено инструментом, не по названию.
- Дешёвые гейты (`tsc --noEmit`, `npm test`, `npm run build` со сверкой
бандла) на этом SHA уже зелёные в Validate
(https://github.com/Matysh/houseplan-card/actions/runs/34404945290) —
не перегонял.
### Что не проверял и почему
- `npm run golden:verify`, `check-docs.mjs`, `npm run invariants`,
`pytest tests_backend` — diff не трогает `src/**`, рендер, геометрию или
`custom_components/**/*.py`; неприменимо.
- Полный набор browser-smoke — инструмент явно сказал «выбирать нечего».
- **AC2 и AC3 живым прогоном** — не выполнено и не могло быть выполнено в
рамках этого ревью: оба требуют повторного `S7` конвейера на реальной
задаче после зеркалирования `process.yml` в `main` (#454, п.3 ТЗ, вне
этого коммита). Сам текст ТЗ отдаёт эту проверку живому прогону, а не
ревью. Ниже — «проверено чтением, не исполнением» с прослеженной
цепочкой вызовов, но не заменяющее реальный прогон.
## Разбор по AC
**AC1** (после ребейза блок якорей называет пост-ребейзный коммит/дерево,
достижимые с origin; тест краснеет на возврате к `steps.branch.outputs`).
Доказано автотестом, тест проверен на падение (см. выше). Дополнительно
прочитан путь данных: `material.sha/tree/specs` → env `MATERIAL_*` шага
«Опубликовать документ ревью» → `review-doc-guard.mjs --anchor=... --sha=
--tree= --specs=` → `materialAnchorBlock` в тексте документа. Момент снятия
(`material`, после `git push --force-with-lease` в шаге `rebase`) гарантирует,
что дерево уже лежит в запушенном коммите. **Выполнен.**
**AC2** (повторный `S7` без изменений кода поверх зелёного документа даёт
`reuse=true` без вызова модели). Проверено чтением: `differs(tree)` в
`--reuse` сначала делает `git cat-file -e <tree>^{tree}` — до фикса дерево
было пре-ребейзным и в свежем клоне не существовало физически (осиротело
force-push'ем), это и давало жёсткое `differs=true` независимо от
реального содержимого, что дословно совпадает с симптомом в issue.
После фикса дерево — то, что реально запушено на ветку, и `cat-file`
находит его в клоне следующего прогона; дальше сравнение идёт по
содержимому, как и задумано. Логика заявленный дефект устраняет. Живым
прогоном не подтверждено — по объективной причине (см. выше).
**AC3** (пост-шаг #413 не красит зелёный раунд с ребейзом). Проверено
чтением: `anchorLiveness` для типа `tree` ищет объект перебором `%T` по
`--remotes=origin --tags`; при верном (запушенном) дереве это ровно тот
поиск, который срабатывает. До фикса — то же дерево, что и в AC2,
осиротевшее и не входящее ни в один `%T` с origin. Логика согласуется.
Живым прогоном не подтверждено — та же причина.
**AC4** (`User-Visible: no`). Коммит содержит трейлеры `Issue: #515` и
`User-Visible: no` — соответствует. Продуктовых CHANGELOG-файлов дифф не
трогает, что и требуется при `User-Visible: no`.
## Что проверено и корректно
- Единственное place, где раньше писались `tree`/`specs` (шаг `branch`),
теперь только поясняющий комментарий, само снятие убрано полностью — не
осталось дублирующего, конфликтующего источника.
- `material`-шаг выполняется на обеих стадиях (`spec` и `code`): его
`if: steps.rebase.outputs.conflict != 'true'` истинен и когда `rebase`
пропущен целиком (стадия `spec`, где ребейза не бывает) — поведение для
`spec` не регрессирует, дерево там совпадает что до, что после (ребейза
нет, снимать раньше или позже — один и тот же коммит).
- Через `material.sha` (не через сегодняшний дифф — использовалось и
раньше) уже строился системный промпт ревьюера («Материал ревью — ровно
`…`»); эта правка выравнивает и опубликованный якорь с тем же источником
— раньше `MATERIAL_SHA` в анкоре и SHA в промпте ревьюера могли
расходиться, теперь один источник для обоих.
- Комментарии перенесены по смыслу, а не скопированы бездумно: у `branch`
теперь короткое объяснение «почему не здесь», у `material` — полное
объяснение «почему здесь».
## Находки
Нет ни High, ни Medium.
## Вердикт
Зелёный.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/515-material-anchors-after-rebase`, коммит `5a1cddeaf0fb` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `ec563a09b642fd1e7742dbcfa089c25ef7b8b8df`
```
git log --all --format='%H %T' | grep ec563a09b642
```
- Вердикт конвейера: `green` · High 0