mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-28 19:01:34 +00:00
docs: review document for #637
Проверка (CI) / Классификация изменённых файлов (push) Successful in 38s
Проверка (CI) / HACS: валидация репозитория (push) Skipped
Проверка (CI) / Hassfest: манифест интеграции (push) Skipped
Проверка (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) Skipped
Проверка (CI) / Предполёт: документация, провенанс, процесс (push) Failing after 57s
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Successful in 2m0s
Проверка (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) / Геометрия: TS/Python parity исполнена (push) Skipped
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Skipped
Проверка (CI) / Доказательство выполненных проверок (push) Failing after 28s
Проверка (CI) / Классификация изменённых файлов (push) Successful in 38s
Проверка (CI) / HACS: валидация репозитория (push) Skipped
Проверка (CI) / Hassfest: манифест интеграции (push) Skipped
Проверка (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) Skipped
Проверка (CI) / Предполёт: документация, провенанс, процесс (push) Failing after 57s
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Successful in 2m0s
Проверка (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) / Геометрия: TS/Python parity исполнена (push) Skipped
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Skipped
Проверка (CI) / Доказательство выполненных проверок (push) Failing after 28s
Issue: #637 User-Visible: no
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
# CODE-REVIEW · issue #637 · заход r2
|
||||
|
||||
Материал: `44aa35806050e3f3c663b317aab6016b60f88888` (рабочая копия на нём,
|
||||
`git status --porcelain` пуст). Ветка `issue/637-process-metrics`, 3 коммита
|
||||
поверх текущего `origin/dev` (`0b55163d`): `46edcb1f` (ребейз-эквивалент
|
||||
материала r1), `5df8cfc0` (документ ревью r1, публикация), `44aa3580` (фикс
|
||||
находки r1). Класс B: `scripts/process-metrics.mjs`,
|
||||
`test/process-metrics.test.mjs`. Трейлеры `Issue: #637`, `User-Visible: no` —
|
||||
верно, видимого поведения продукта нет.
|
||||
|
||||
Трек — инфраструктурный (#562, §1): ни одного файла класса A, задача входит в
|
||||
общий флоу сразу на `S7-code-review`.
|
||||
|
||||
## Раунд по дельте (§2.10)
|
||||
|
||||
1. **Вердикт и материал r1.** `docs/reviews/CODE-REVIEW-637-r1.md` (закоммичен
|
||||
в `5df8cfc0`) — жёлтый, заход r1, блокирующих циклов 0/4, High 0, Medium 1
|
||||
в скоупе. Материал раунда объявлен в самом документе:
|
||||
SHA `966928135b6a40fd6a73c7631bd514a79015d48a`, дерево
|
||||
`0687eddba719e669212123bf80bf8c6d683bbf50`, хеш тела issue
|
||||
`187c1bf4bbdea65f4114112ea0ee62c313cb015d343fbb44d68507420f204e41`.
|
||||
|
||||
2. **SHA не резолвится** — `git cat-file -t 966928135b6a...` и поиск дерева
|
||||
`0687eddba719` по `git log --all --format='%H %T'` дают пустой результат.
|
||||
Это обычное дело (§2.10, п.2): автор явно пишет «Материал:
|
||||
`44aa3580…` после rebase на актуальный `dev`». Проверил, что это чистый
|
||||
ребейз, а не скрытая правка: родитель `46edcb1f` (эквивалент старого
|
||||
материала на новой базе) — это ровно текущий `origin/dev` (`0b55163d`,
|
||||
`git merge-base origin/dev 46edcb1f` совпадает), диапазон файлов и их
|
||||
размер (`process-metrics.mjs` 291 строка, `test` 116 строк до фикса)
|
||||
соответствуют тому, что описано и процитировано в документе r1
|
||||
(`buildReport`, строки 162–165, тот же код). Значит дельта раунда — это
|
||||
ровно коммит `44aa3580` (`scripts/process-metrics.mjs` +8/−4,
|
||||
`test/process-metrics.test.mjs` +23), а не результат смены поведения из-за
|
||||
рейбейза. Разбор по дельте оправдан: `dev` не «ушёл вперёд» относительно
|
||||
материала — ребейз тривиальный, чужой скоуп не задет, новая подсистема не
|
||||
затронута, объём дельты (31 строка) несопоставим с исходной задачей.
|
||||
|
||||
3. **Дельта объявлена:** `git diff 46edcb1f..44aa3580` = сам коммит
|
||||
`44aa3580` (см. ниже «Закрытие раунда r1»).
|
||||
|
||||
## Закрытие раунда r1
|
||||
|
||||
| Находка r1 | Чем закрыта | Где это видно |
|
||||
|---|---|---|
|
||||
| Medium: `hours(median(...) ?? NaN) \|\| null` в `buildReport` схлопывает настоящую медиану 0 ч в `null`/«—» (`scripts/process-metrics.mjs:162-165` на материале r1) | Введена `medianHours(values)` (`scripts/process-metrics.mjs:35-38`): различает «нет данных» (`median` вернул `null` → `null`) и любой числовой результат, включая 0, через `value === null ? null : hours(value)` — без финального `\|\| null`, который путал `0` (falsy) с отсутствием данных. Все четыре медианы (`medianLeadToS7Hours`, `medianReviewToMergeHours`, `medianLeadToS8Hours`, `medianSpecLeadHours`, `scripts/process-metrics.mjs:166-169`) переведены на неё | `scripts/process-metrics.mjs:35-38,166-169`; новый тест `test/process-metrics.test.mjs:108-129` — воспроизводит ровно сценарий из репро r1 (переходы короче ≈3 минут) и проверяет `[0, 0, 0.1, 0]` вместо `[null, null, null, null]`, плюс отдельно проверяет, что пустая выборка по-прежнему даёт `null`/«—» (не путает две ветки местами) |
|
||||
|
||||
Мутационное доказательство сделал сам (мутация не входит в реестр — фикс в
|
||||
чистом юните, п.8 §2.7 разрешает прогон со снятой защитой прямо в ревью, без
|
||||
`scripts/mutation-gate.mjs`): вручную вернул старое выражение
|
||||
`hours(median(...) ?? NaN) || null` во все четыре поля, прогнал
|
||||
`node --test test/process-metrics.test.mjs` — новый тест красный
|
||||
(`AssertionError [ERR_ASSERTION]` на `deepStrictEqual`, строка 118), остальные
|
||||
7 тестов проходят. Откатил патч (`cp` из бэкапа), `git status --porcelain`
|
||||
снова пуст, повторный прогон — 8/8 pass. Тест умеет падать.
|
||||
|
||||
## Унаследовано из r1
|
||||
|
||||
Без повторной проверки приняты (документ `docs/reviews/CODE-REVIEW-637-r1.md`,
|
||||
материал которого эквивалентен текущему `46edcb1f`, см. п.2 выше) — дельта
|
||||
`44aa3580` этих путей кода не касается:
|
||||
|
||||
- `issueMetrics` — вход по первой статусной метке, различение `entered`/S4/S5/
|
||||
S7/S8, счётчики повторных постановок.
|
||||
- `reviewRounds` — строгий регэксп имён файлов ревью, раунды по документам, а
|
||||
не по событиям S7-метки (мутант `metrics-rounds-by-s7-events`, пойман).
|
||||
- `runMetrics` — группировка прогонов конвейера `process #NN · …` в одну
|
||||
строку; остальные workflow — по имени.
|
||||
- `pipelineMetrics` — skipped-прогоны не считаются в wall-time (мутант
|
||||
`metrics-count-skipped-pipeline-runs`, пойман).
|
||||
- `jobMinutes` — доля «Мутанты» по имени job, устойчивость к сломанным полям
|
||||
времени.
|
||||
- `fetchSnapshot` — только чтение (`gh api` issues/timeline/actions/runs +
|
||||
`git ls-tree`), пагинация, фильтр по `closed_at` внутри окна.
|
||||
- `.github/workflows/process-metrics.yml` — `permissions` только на чтение,
|
||||
расписание понедельник 05:00 UTC + `workflow_dispatch`, код и `ref: dev`.
|
||||
- Трейлеры коммита и структура §2.10-блока материала.
|
||||
|
||||
Инвариант этого наследования: сама дельта (`buildReport`, только четыре поля
|
||||
медиан плюс новый хелпер) не может задеть ни один из перечисленных путей —
|
||||
они не вызывают и не вызываются из изменённых строк. Проверил чтением диффа
|
||||
`44aa3580`, других правок в `scripts/process-metrics.mjs` нет.
|
||||
|
||||
## AC — что дельта задевает
|
||||
|
||||
- **AC1** (скрипт воспроизводит цифры аудита ±10 %): фикстура аудита
|
||||
(`test/process-metrics.test.mjs:60-106`, unchanged) по-прежнему проходит —
|
||||
значения там не нулевые, фикс их не касается численно, только путь для
|
||||
нулевого случая. Дельта расширяет доказательство AC1 на нулевой случай,
|
||||
который сама формулировка AC подразумевает («самый быстрый переход» —
|
||||
ровно то, что баг маскировал). Живой прогон против реального `gh api` за
|
||||
15–22.09 остаётся не выполненным — это унаследовано из r1 (см. ниже «Чего
|
||||
не проверял»), делта на это не влияет.
|
||||
- **AC2** (тест на парсинг фикстур): дельта добавляет восьмой тест, число
|
||||
тестов выросло с 7 до 8, все проходят — доказательство AC2 усилилось, не
|
||||
ослабло.
|
||||
- **AC3** (первый отчёт опубликован автоматически): не задета дельтой,
|
||||
наследуется из r1 как открытая до пост-мерж действия владельца
|
||||
(зеркалирование workflow в `main`) — это явно вне контроля код-ревью и не
|
||||
являлось находкой в r1.
|
||||
|
||||
## Гейты
|
||||
|
||||
- `node --test test/process-metrics.test.mjs` — прогнал сам: **8 pass, 0
|
||||
fail** (7 из r1 + новый регрессионный).
|
||||
- Мутация находки r1 (снятие `medianHours`, возврат старого выражения) —
|
||||
прогнал сам: новый тест краснеет, остальные держатся; откатил, дерево чистое.
|
||||
- Мутанты `metrics-count-skipped-pipeline-runs`,
|
||||
`metrics-rounds-by-s7-events` — не перепрогонял: код, который они проверяют
|
||||
(`pipelineMetrics`, `reviewRounds`), дельтой не тронут (см. «Унаследовано»),
|
||||
а сам реестр `scripts/mutation-registry.mjs` не менялся между `46edcb1f` и
|
||||
`44aa3580` (дифф двух файлов, реестра нет). Инвариант r1 переносится.
|
||||
- `npx tsc --noEmit`, `npm test` (полный), `npm run build` со сверкой копий
|
||||
бандла — не гонял: Validate на этом самом SHA (`44aa3580`) зелёный
|
||||
(https://github.com/Matysh/houseplan-card/actions/runs/35839128726), дифф с
|
||||
тех пор не менялся (рабочая копия чистая на этом SHA).
|
||||
- `node scripts/check-docs.mjs` — не требуется: дифф не трогает `src/**`
|
||||
(только `scripts/`, `test/`).
|
||||
- `npm run invariants` — не требуется: геометрия, `layout`, толщина стен,
|
||||
`marker.space`/`open_spans` не затронуты.
|
||||
- `demo/smoke_*.mjs`, `npm run golden:verify` — не требуется: дифф не меняет
|
||||
рендер, карточку, стили или слои; это чистые функции над снимком GitHub.
|
||||
- `python -m pytest tests_backend -q` — не требуется: `custom_components/**`
|
||||
не тронут.
|
||||
- `test/single-source-numbers.test.mjs` — не относится: метрики процесса не
|
||||
пользовательские величины карточки.
|
||||
|
||||
## Что проверено и корректно
|
||||
|
||||
- Фикс сохраняет форму данных: `medianHours` возвращает `null` только когда
|
||||
`median()` сам вернул `null` (пустая выборка после `filter(Number.isFinite)`
|
||||
в `median`, `scripts/process-metrics.mjs:29-34`), в остальных случаях —
|
||||
всегда `hours(value)`, включая 0. Прочитано и подтверждено регрессионным
|
||||
тестом на обеих ветках (пустая выборка и нулевая медиана в одном тесте, не
|
||||
раздельно — так тест не может «повезти» и спутать ветки местами).
|
||||
- `renderMarkdown`/`fmt` (`scripts/process-metrics.mjs:185`) корректно
|
||||
отображает `0` как `0 ч`, а не как «—»: `value == null` ложно для `0`,
|
||||
`Number.isNaN(0)` ложно — код `fmt` не менялся в этой дельте и уже был
|
||||
правильным, баг был только в вычислении значения выше по цепочке, не в
|
||||
форматировании. Подтверждено новым тестом
|
||||
(`/Медиана вход → S7 \| 0 ч/`).
|
||||
- Округление `hours()` (`Math.round((ms/3_600_000)*10)/10`) — проверил вручную
|
||||
арифметику теста: 2 мин → 0.0(3) ч → округление до 0; 3 мин → 0.05 ч →
|
||||
`Math.round(0.5)=1` (JS всегда вверх на `.5`) → 0.1 ч. Ожидаемый вектор
|
||||
`[0, 0, 0.1, 0]` в тесте математически верен, не «подогнан» под реализацию.
|
||||
- Трейлеры `44aa3580`: `Issue: #637`, `User-Visible: no` — соответствуют
|
||||
`git show -s --format=full`.
|
||||
- Хендофф-комментарий владельца/исполнителя точно называет материал раунда
|
||||
(`44aa35806050e3f3c663b317aab6016b60f88888`) и он совпадает с
|
||||
`git rev-parse HEAD` рабочей копии — соответствует §2.7 (вердикт привязан к
|
||||
SHA, сверено непосредственно перед итогом).
|
||||
|
||||
## Чего не проверял
|
||||
|
||||
- Живой прогон `fetchSnapshot`/CLI против реального `gh api` — наследуется из
|
||||
r1, не задет дельтой; автор по-прежнему планирует сверку с таблицей аудита
|
||||
первым живым запуском после зеркалирования workflow в `main`.
|
||||
- Мутанты `metrics-count-skipped-pipeline-runs`,
|
||||
`metrics-rounds-by-s7-events` — не перепрогонял в этом раунде, обоснование
|
||||
выше («Гейты»): код, который они проверяют, дельтой не задет.
|
||||
- `npx tsc --noEmit`/`npm test` (полный)/`npm run build` — не гонял, опираюсь
|
||||
на зелёный Validate на точном SHA материала (ссылка выше), как разрешает
|
||||
сокращение гейтов ревью.
|
||||
|
||||
## Вывод
|
||||
|
||||
Единственная находка r1 (Medium, в скоупе) закрыта точечным фиксом:
|
||||
`medianHours` корректно различает «данных нет» и «медиана равна 0»,
|
||||
регрессионный тест воспроизводит исходное репро и умеет падать (проверено
|
||||
мутацией вручную). Делта локальна, не задевает ни один из путей, признанных
|
||||
корректными в r1, AC1/AC2 доказательство усилено, AC3 не в скоупе дельты.
|
||||
High-находок нет, новых Medium/Low не нашёл.
|
||||
|
||||
**Вердикт: зелёный.**
|
||||
|
||||
## Материал раунда
|
||||
|
||||
- Ветка: `issue/637-process-metrics`, коммит `44aa35806050e3f3c663b317aab6016b60f88888`.
|
||||
- Рабочая копия ревью — на этом SHA, `git status --porcelain` пуст.
|
||||
- Предыдущий материал (r1) осиротел ребейзом (ожидаемо, §2.10 п.2): SHA
|
||||
`966928135b6a40fd6a73c7631bd514a79015d48a` не резолвится; рабочий эквивалент
|
||||
на текущей базе — `46edcb1f37a7bad93de97e04e2307f67d0d310d4`
|
||||
(`git merge-base origin/dev 46edcb1f` = `origin/dev` = `0b55163d`).
|
||||
|
||||
---
|
||||
|
||||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||||
|
||||
## Материал раунда
|
||||
|
||||
- Ветка: `issue/637-process-metrics`, коммит `44aa35806050` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||||
- Дерево материала: `c5dd9f105b40966232d871cb5b2a8dfbb5cf54fb`
|
||||
```
|
||||
git log --all --format='%H %T' | grep c5dd9f105b40
|
||||
```
|
||||
- Тело issue: `187c1bf4bbdea65f4114112ea0ee62c313cb015d343fbb44d68507420f204e41`
|
||||
- Вердикт конвейера: `green` · High 0
|
||||
Reference in New Issue
Block a user