diff --git a/docs/reviews/CODE-REVIEW-86-r5.md b/docs/reviews/CODE-REVIEW-86-r5.md new file mode 100644 index 00000000..84738186 --- /dev/null +++ b/docs/reviews/CODE-REVIEW-86-r5.md @@ -0,0 +1,205 @@ +# CODE-REVIEW-86-r5 + +- **Issue:** #86 — Тексты подсказок к настройкам: очередь и правила (Party 1) +- **Заход:** r5 · блокирующих циклов израсходовано 3 из 4 +- **Материал:** `git log --oneline origin/dev..HEAD`, `git diff origin/dev...HEAD` +- **HEAD:** `fa146fb1` · **origin/dev:** `2b0ac0c0` (включает #44, merge-base == origin/dev) +- **Спецификация:** `docs/specs/086-settings-help-content-party1.md` (актуализирована 2026-08-30, коммит `20883c31` до ребейза) + +## Почему разбор снова полный, а не по дельте + +После зелёного вердикта r4 (на `7408f8af`) `dev` продвинулся на 5 коммитов — +слияние #44 «discovery filters become visible, explained and previewable» +(`d14cf769..2b0ac0c0`, включая собственный цикл ревью #44 с фиксами +M1‑M2). Ветка `issue/86-settings-help-party1` перебазирована на этот новый +`dev`. Все SHA из документов r1–r4 (`7408f8af`, `96bd3e3e`, `18ab3779` и т.д.) +более не существуют в истории (`git cat-file -t` → `fatal: Not a valid object +name` для каждого) — это не косметика, а другое дерево (PROCESS.md §2.10/§7.2). +Это ровно случай, для которого прямо предписан полный разбор. + +Важное обстоятельство: `git merge-base HEAD origin/dev == origin/dev`, то есть +диапазон `origin/dev...HEAD` — это **весь** вклад ветки #86 целиком (в dev уже +влит #44, в ветке сверх него — только #86). Это даёт полный и единственный +источник правды для разбора, без необходимости реконструировать историю по +кускам. + +Дополнительная проверка ребейза: #44 и #86 модифицируют одни и те же файлы +(`src/houseplan-editor-runtime.ts`, `src/i18n/{en,ru,de,fr}.json`, +`src/styles/dialogs.styles.ts`, `docs/images/screenshots.json`, +`docs/CHANGELOG*.md`). Список конфликтов, зафиксированный в issue перед +финальным ребейзом, содержал только сгенерированные бандлы и changelog — +никаких конфликтов в `src/**`/`i18n/**`. Прочитал итоговый diff этих файлов: +изменения #86 (add `_help(...)` triggers, новые `.help`/`.help.aria` ключи) +корректно легли поверх кода #44 (`_renderDiscoveryFilters`, новые i18n-ключи +дискавери) без потерь с обеих сторон — рекомбинация чистая. + +## Что проверялось и как + +### Гейты, прогнанные лично на `fa146fb1` + +| Гейт | Результат | +| --- | --- | +| `npx tsc --noEmit` | зелёный, без ошибок | +| `npm test` | 1615 тестов, 1614 pass, 0 fail, 1 skip (тот же приватный `#281`-скип, что и в r1–r4) | +| `npm run build` + `git status --porcelain` после build | 0 diff — закоммиченный `dist/` побайтово совпадает со свежей сборкой | +| `node scripts/bundle-sync.mjs` | 0 diff в `custom_components/houseplan/frontend` и `demo/srv/assets` — три канонические копии синхронны | +| `node scripts/bundle-budget.mjs` | initial View 280 678 B / 300 000 B бюджета, headroom 19 322 B — в норме | +| `node scripts/no-new-any.mjs` | «Новых any нет» (119 добавленных строк в 3 файлах) | +| `node scripts/check-docs.mjs --external` | «Documentation checks passed (7 files, 10 external links)» — фингерпринт скриншотов не протух, sha256 каждого PNG совпадает с манифестом (тот самый канал, что в r1 поймал H1 — здесь чисто) | +| `node scripts/mutation-gate.mjs --id=settings-help-party1-placement-removed` | мутант поймал `test/i18n.test.mjs` — «покраснел, как обязан» | +| `npm run golden:verify` | 147/147 `passed`, 0 different/missing (полный лог сохранён) | +| 13/13 smoke «прямого совпадения» из `smoke-select.mjs` | все `OK`, все party1-флаги в `smoke_help_affordance.mjs` — `true` | + +Почему прогнал сам, а не принял «Validate зелёный» на веру: разобрался в +устройстве последнего прогона (run 33310161342, `headSha=fa146fb1`) — +job «Классификация изменённых файлов» сравнивал только `04da7eb1..fa146fb1` +(3 файла `docs/images/**`), потому что предыдущий push (`04da7eb1`, реальный +ребилд бандла) получил **cancelled** прогон (run 33310044199, перекрыт +следующим push тремя минутами позже), а не зелёный. В итоге `frontend`, +`golden`, `smoke`, `backend` в финальном прогоне — не «переиспользованы по +хешу» (шаг reuse-маркеров явно вернул `Cache not found` по всем четырём), а +**skipped** классификацией. То есть ни один реальный CI-прогон после +финального ребейза не исполнил тяжёлые гейты по-настоящему. Заявление +«Validate на `fa146fb1` зелёный ⇒ дешёвые гейты подтверждены» из вводных +раунда не подтвердилось при проверке — пришлось прогонять самому. Отдельно +завёл **#387** (infra, P2, вне скоупа #86) с точным описанием механизма и +предложением чинки; на вердикт #86 не влияет, так как содержимое проверено +мной исполнением напрямую и оказалось корректным. + +### Не прогонял и почему + +- **18 «слабых связей» смоков** (`smoke-select.mjs`, общий символ `_config`) — + контент партии 1 не менялся с r3, риск для этих смоков уже признавался + низким в r2/r3/r4; не нашёл оснований пересматривать. +- **`python -m pytest tests_backend`** — diff не касается ни одного `.py` + файла (`git diff origin/dev...HEAD --stat` не содержит `custom_components/**/*.py`). +- **`npm run invariants`** / модельные инварианты — diff не трогает геометрию, + `layout`, `marker.space`, `open_spans`, толщину стен; ТЗ §11 прямо фиксирует + «Config, backend, layout и storage не меняются», подтверждено чтением diff + (только шаблоны рендера и i18n). +- **`performance_smoke`** — ни ТЗ, ни diff не касаются путей, чувствительных + к перфу (чисто DOM/CSS-триггеры уже существующего `hp-help` из #68). +- Полный `demo/smoke_*.mjs` набор (206 файлов) — не запускал целиком; + `smoke-select.mjs` даёт 13 прямых совпадений (все прогнаны) и не находит + оснований для более широкого прогона. + +### Одно число — один источник + +Diff не добавляет и не меняет ни одной пользовательской величины (числа, +единицы, отображаемые значения) — только текстовые подсказки и разметку +(`id=`/`