mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-03 21:28:59 +00:00
Issue: #426 Issue: #427 Issue: #428 Issue: #431 Issue: #432 Issue: #434 User-Visible: no
183 lines
16 KiB
Markdown
183 lines
16 KiB
Markdown
# SPEC-REVIEW-434-r2
|
||
|
||
- Issue: https://github.com/Matysh/houseplan-card/issues/434
|
||
- Этап: ревью ТЗ (PROCESS.md §2.4)
|
||
- ТЗ: `docs/specs/434-v171-polish-audit.md`
|
||
- Проверяемый SHA: `904a989af70481eb7f34c1e3cfc99de115ba869f` (= `git rev-parse HEAD`,
|
||
совпадает с SHA, названным автором в хендоффе r2)
|
||
- Заход: r2 · блокирующих циклов израсходовано 1 из 4 (полный трек, лимит 4)
|
||
- Вердикт: **зелёный**
|
||
|
||
## Скоуп раунда
|
||
|
||
Повторный раунд по §2.9/2.10: предмет разбора — дельта ТЗ между материалом r1
|
||
(`5566d6f9898e3c6d21f3e92a2ddf94536c86edc4`) и текущим `904a989a`, а не задача
|
||
целиком. Дельта проверена командой:
|
||
|
||
```
|
||
git diff 5566d6f9898e3c6d21f3e92a2ddf94536c86edc4..904a989af70481eb7f34c1e3cfc99de115ba869f -- docs/specs/434-v171-polish-audit.md
|
||
```
|
||
|
||
`git diff --stat` на этом же диапазоне подтверждает, что дельта не касается
|
||
ничего, кроме файла ТЗ, — второй изменённый файл в диапазоне,
|
||
`docs/reviews/SPEC-REVIEW-434-r1.md`, это публикация документа предыдущего
|
||
раунда конвейером (класс C, не предмет разбора).
|
||
|
||
Дельта локальна: она правит ровно одну тему — контракт job-level
|
||
`timeout-minutes` для job `smoke` (пункт 8 / AC9 / раздел 7.7 / «Производительность»
|
||
/ «Риски» / таблица «чем краснеет» / тест-план / «Принятые предположения»).
|
||
Условия «разбор остаётся полным» (ребейз на ушедший вперёд `dev`, смена
|
||
контракта поведения, новая подсистема, объём дельты сопоставим с исходной
|
||
задачей) не выполнены: ни ребейза, ни новой подсистемы, дельта — 40 строк
|
||
правки внутри уже существующего AC9, без изменения продуктового контракта
|
||
(изменение чисто CI/gate, не видимое пользователю). Сокращённый объём разбора
|
||
обоснован.
|
||
|
||
## Закрытие раунда r1
|
||
|
||
| Находка r1 | Чем закрыта | Где это видно |
|
||
|---|---|---|
|
||
| [Medium, в скоупе] ТЗ утверждало неизменный `timeout-minutes: 20` у job `smoke`, хотя эти 20 минут принадлежат `performance_smoke`, а у `smoke` собственного лимита нет вовсе (дефолт GitHub 360 мин) | Текст ТЗ переписан по всем перечисленным в находке местам: п.8 «Подтверждённые причины», раздел 7.7 «Bounded smoke execution», «Производительность», «Риски», AC9, «чем краснеет» (AC9), тест-план (шаг 8), «Принятые технические предположения». Факт теперь сформулирован верно, и **в скоуп задачи добавлено решение**: job `smoke` получает собственный `timeout-minutes: 20` — новая независимая граница, отдельная от `performance_smoke` | `docs/specs/434-v171-polish-audit.md:73-76` («сама job `smoke` не имеет `timeout-minutes` и потому наследует 360-минутный default… Единственные 20 минут в workflow относятся к другой job — `performance_smoke`»); `:247-250` («Job `smoke` получает собственный `timeout-minutes: 20`: это отдельная граница, которой сейчас нет…»); `:384-389` (AC9 текст); `:420` (таблица «чем краснеет», мутация «удаляет `smoke.timeout-minutes`»); `:446-448` (тест-план п.8); `:470-472` (риски); `:517-519` (принятые предположения) |
|
||
|
||
Факт перепроверен независимо от заявления автора: `grep -n timeout
|
||
.github/workflows/validate.yml` на актуальном HEAD даёт ровно одно
|
||
совпадение — строка 715, job `performance_smoke` (заголовок job на строке 705,
|
||
`timeout-minutes: 20` на 715). Job `smoke` (заголовок строка 486, цикл
|
||
`for f in demo/smoke_*.mjs` строка 554) действительно не содержит
|
||
`timeout-minutes` нигде в своём теле. Формулировка ТЗ после правки точна.
|
||
|
||
## Проверка дельты по существу (не только «текст поменялся»)
|
||
|
||
Так как дельта меняет именно AC9 (появляется новое обязательство — job-level
|
||
timeout), доказательство которого дельта задевает, AC9 разобран заново, а не
|
||
унаследован:
|
||
|
||
- **Согласованность формулировок.** Все шесть затронутых мест (см. таблицу
|
||
выше) описывают одну и ту же границу одинаково: 20 минут, независимая от
|
||
`performance_smoke`, покрывает весь shard «при системном зависании либо
|
||
серии отдельных отказов» (`:470-472`), а не заменяет per-file timeout.
|
||
Противоречий между разделами не найдено.
|
||
- **Реалистичность значения 20 минут.** Комментарий в самом
|
||
`.github/workflows/validate.yml:492-494` фиксирует, что последовательный
|
||
прогон всех смоков занимал ~7.5 минут до шардирования на 3 части, то есть
|
||
штатный шард сейчас укладывается в ~2.5 минуты — у 20-минутного лимита
|
||
восьмикратный запас на холодный раннер, сопоставимый с обоснованием того же
|
||
значения у `performance_smoke` (`:711-713`, «15 минут не хватало... холодный
|
||
кэш»). Патологический сценарий, где job-level timeout прервёт shard до того,
|
||
как последовательно доработают все per-file timeout (215 файлов / 3 шарда
|
||
≈ 72 на шард × 190 c ≈ 228 минут в предельном случае «все зависли») —
|
||
ТЗ его не скрывает, а называет прямо как ожидаемое поведение защиты «при
|
||
серии отдельных отказов», а не как гарантию довести до конца каждый файл.
|
||
Технической ошибки или недосказанности здесь нет.
|
||
- **Проверяемость AC9 после правки.** «Чем краснеет» (`:420`) называет два
|
||
независимых свидетеля — мутацию per-file wrapper (`plain node "$f"`) и
|
||
отдельно мутацию `smoke.timeout-minutes` (удаление поля) — и требует, чтобы
|
||
workflow-контрактный тест реагировал на оба независимо
|
||
(«не находит одну из двух независимых границ»). Это не создаёт пустого
|
||
столбца и не путает test-time симуляцию (per-file probe) со статическим
|
||
YAML-контрактом (job-level timeout, аналог которому — `test/validate-workflow.test.mjs`,
|
||
подтверждённый как рабочий прецедент ещё в r1). Инженерно реализуемо тем же
|
||
способом, что и остальной AC9.
|
||
- **Затронутые модули учитывают файл workflow.** `docs/specs/434-v171-polish-audit.md:339-340`
|
||
прямо называет `.github/workflows/validate.yml` в списке затронутых модулей —
|
||
правка AC9 не «повисает» без соответствующего файла в скоупе.
|
||
|
||
Новых дефектов дельта не вносит.
|
||
|
||
## Унаследовано из r1
|
||
|
||
Всё, что не задето дельтой, принимается без повторной проверки — документ
|
||
`docs/reviews/SPEC-REVIEW-434-r1.md` (материал: ветка `issue/434-v171-polish-audit`,
|
||
коммит `5566d6f9898e`, дерево `1e1861969610794ffa6a9d8458b52ef3695496b8`,
|
||
блоб ТЗ `e0229c0cd6550a1c44b978335c52bd2b264b1c52`):
|
||
|
||
- Обязательные разделы §7.1 присутствуют полностью; продуктовые «Сценарий» и
|
||
«Что человек увидит до и после» отвечают на оба обязательных вопроса
|
||
(персона/поверхность/момент; видимое изменение без терминов реализации) —
|
||
и эти разделы дельтой r2 не тронуты (см. diff — правки только в §7.7,
|
||
«Производительность», «Риски», AC9, таблице и тест-плане).
|
||
- Восемь из девяти пунктов «Подтверждённые причины» (все, кроме п.8, который
|
||
и есть предмет дельты) построчно сверены с `origin/dev` двумя независимыми
|
||
агентами и подтверждены дословно, включая: `read_catalog`/`blob.is_file()`
|
||
(п.1), отсутствие capability-гарда в `space-card.ts:732` (п.2), кэш по
|
||
id-set без ревизии config (п.3), отсутствующий негативный тест «sidecar без
|
||
blob» (п.4), `reused:true` без catalog-записи (п.5), locale gate снимок
|
||
прошлого рендера (п.6, номера строк помечены как приблизительные), отсутствие
|
||
отдельного witness у `snapshotBindings.has(binding)` (п.7), throw до discard
|
||
в support preview (п.9).
|
||
- AC1–AC8, AC10–AC12 однозначны, способ доказательства назван для каждого;
|
||
таблица «чем краснеет» (#435) заполнена без пустых столбцов для всех, кроме
|
||
AC9, которую r2 перепроверил заново (см. выше).
|
||
- Маршрут (full) обоснован верно — критерий лёгкого трека не проходит из-за
|
||
нескольких независимых поверхностей и подсистем.
|
||
- Не-скоуп корректно исключает соседние более крупные рефакторинги
|
||
(LanguageRuntime/support pipeline/шардирование, изменения schema/API version,
|
||
видимого текста/UI).
|
||
- Изменение семантики `reused` не является изменением видимого контракта —
|
||
поле нигде не читается в `src/**`.
|
||
- AC3 (удаление orphan-блобов) не противоречит правилу SCOPE.md «никогда не
|
||
удалять файл по предположению»: причина удаления — явный вызов
|
||
`houseplan/assets/delete` с точным id, а не вывод из отсутствия ссылок.
|
||
- Раздел «Откат» корректно называет границы (миграции нет, восстановленные
|
||
sidecar остаются валидными записями); эти строки дельтой не менялись.
|
||
- Технические прецеденты (`scripts/mutation-gate.mjs` уже содержит Python-мутанты,
|
||
`test/validate-workflow.test.mjs` — рабочий образец YAML-контрактного теста)
|
||
подтверждены на `origin/dev` в r1 и остаются в силе — AC9 после правки
|
||
использует тот же прецедент.
|
||
|
||
## Что проверено в r2 (сверх наследования)
|
||
|
||
- Дельта ТЗ (`docs/specs/434-v171-polish-audit.md`, r1→r2) построчно прочитана
|
||
целиком.
|
||
- Факт из находки r1 перепроверен заново на актуальном HEAD:
|
||
`grep -n timeout .github/workflows/validate.yml` (1 совпадение, строка 715,
|
||
job `performance_smoke`); контекст job `smoke` (строки 486–586) и
|
||
`performance_smoke` (строки 705–720) прочитан целиком, `timeout-minutes` в
|
||
теле `smoke` действительно отсутствует.
|
||
- Реалистичность выбранного значения 20 минут сверена с комментарием в самом
|
||
workflow-файле (фактическое время шардированного прогона) и числом реальных
|
||
smoke-файлов (`ls demo/smoke_*.mjs | wc -l` → 215).
|
||
- Согласованность формулировки новой границы across всех шести затронутых
|
||
разделов ТЗ.
|
||
- `git diff --stat` между SHA r1 и r2 — подтверждено, что кроме ТЗ и
|
||
публикации документа r1 больше ничего не менялось (продуктовый код не
|
||
затронут, разбор кода в этом раунде не требуется).
|
||
|
||
## Чего не проверял
|
||
|
||
- Гейты (`typecheck`/`test`/`build`/backend pytest) не запускались: диапазон
|
||
правок — документация класса C (ТЗ + документ предыдущего ревью), кода нет.
|
||
- Не проверялся сам код реализации — его по-прежнему нет, это ревью ТЗ.
|
||
- Разделы, не затронутые дельтой (см. «Унаследовано из r1»), не разбирались
|
||
заново по существу — они наследуются от r1 согласно §2.10.
|
||
- Не оценивался точный сценарий частичного отказа (например, ровно 6–7
|
||
зависших файлов из 72 в шарде) на предмет того, какие именно файлы успеют
|
||
отработать до срабатывания job-level timeout — ТЗ прямо не гарантирует
|
||
«довести шард до конца при серии отказов», и это осознанно названо
|
||
принятым поведением защиты, а не заявленным AC.
|
||
|
||
## Материал раунда
|
||
|
||
- Ветка: `issue/434-v171-polish-audit`, SHA `904a989af70481eb7f34c1e3cfc99de115ba869f`.
|
||
- Дельта: `git diff 5566d6f9898e3c6d21f3e92a2ddf94536c86edc4..904a989af70481eb7f34c1e3cfc99de115ba869f -- docs/specs/434-v171-polish-audit.md`.
|
||
- Предыдущий раунд: `docs/reviews/SPEC-REVIEW-434-r1.md`, материал —
|
||
ветка `issue/434-v171-polish-audit`, коммит `5566d6f9898e`, дерево
|
||
`1e1861969610794ffa6a9d8458b52ef3695496b8`, блоб ТЗ
|
||
`e0229c0cd6550a1c44b978335c52bd2b264b1c52`.
|
||
|
||
---
|
||
|
||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||
|
||
## Материал раунда
|
||
|
||
- Ветка: `issue/434-v171-polish-audit`, коммит `904a989af704` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||
- Дерево материала: `37d24151e91b534280e7ce0d4a437cd082afdc30`
|
||
```
|
||
git log --all --format='%H %T' | grep 37d24151e91b
|
||
```
|
||
- ТЗ `docs/specs/434-v171-polish-audit.md`, блоб `e235fcef2bcd817c39c0c5a9134fd2ec8f6eb2f1`
|
||
```
|
||
git log --all --find-object=e235fcef2bcd817c39c0c5a9134fd2ec8f6eb2f1 -- docs/specs/434-v171-polish-audit.md
|
||
```
|