Files
houseplan-card/docs/reviews/SPEC-REVIEW-385-r2.md
T
2026-08-30 07:48:57 +00:00

13 KiB
Raw Blame History

SPEC-REVIEW-385-r2

  • Issue: https://github.com/Matysh/houseplan-card/issues/385
  • Этап: ТЗ на ревью (PROCESS.md §2.4)
  • Артефакт под ревью: docs/specs/385-audit-lows.md, ревизия 2 (2026-08-30), коммит 687b2966 ("docs: #385 spec revision 2 per SPEC-REVIEW-385-r1")
  • Заход: r2 · блокирующих циклов израсходовано 1 из 4 (до этого вердикта)
  • Предыдущий раунд: docs/reviews/SPEC-REVIEW-385-r1.md, вердикт жёлтый на ревизии 1, коммит c28b2f2d ("docs: specify #385 audit-lows batch")

Скоуп раунда

Дельта — git diff c28b2f2d..687b2966 -- docs/specs/385-audit-lows.md: правка текстовая, ровно по требованиям вердикта r1 (M1 + Low L2/L3). Код продукта не менялся ни в r1, ни в r2 — стадия ТЗ. Единственный изменённый файл: docs/specs/385-audit-lows.md (+23/-9 строк), затронуты четыре места: строка «Ревизия», раздел «Проблема / Контракты» пункт (в), «DoR-примечания», «Release-артефакты». Разбор по дельте, не заново (PROCESS.md §2.9): полный повторный разбор ТЗ, продуктовой рамки и AC1–AC6 не требуется — дельта локальна, новую подсистему не задевает, ребейза не было.

Как проверялось

По каждой находке r1 — построчное сравнение старого и нового текста (git diff) плюс сверка новых утверждений с источником истины (кодом), а не с заявлением автора о том, что правка внесена.

Закрытие раунда r1

Находка (r1) Чем закрыта Где это видно
M1 — контракт (в) называет только первый дизъюнкт isRelease, риск ложного отказа CI на бета-коммитах приёмки эталонов Абзац «Контракт» переписан: назван весь предикат ((/^Release v\d/...) || Boolean(one('Release'))), явно оговорено «НЕ пересказ по памяти и НЕ только первый дизъюнкт», приведён конкретный контрпример-коммит и путь отказа через evaluateCommit; реализация явно требует одну функцию, используемую и makeCommit, и гейтом в parseRecords, AC4 привязан к переиспользованию docs/specs/385-audit-lows.md:45-59. Выражение сверено дословно с scripts/process-gate.mjs:133-135 — совпадает посимвольно (проверено sed -n '125,136p' scripts/process-gate.mjs). Контрпример 5f6ee657 перепроверён заново командой git show -s --format='%s%n---%n%B' 5f6ee657: subject test: accept explicit value source golden не матчит /^Release v\d/, трейлер Release: v1.69.0-beta.5 присутствует — вторым дизъюнктом isRelease действительно становится true. Утверждение точное
L2 — DoR-примечания не называют влияние на производительность Добавлена третья фраза: «производительность — (в) сокращает работу pre-push/CI-гейта на длинных диапазонах (только выигрыш, бенч не требуется)» docs/specs/385-audit-lows.md:134-136
L3 — «(б)–(г) — мелкие уточнения» в Release-артефактах двусмысленно относительно changelog Переписано прямо: changelog получает запись только про (а); (б)–(г) явно исключены из changelog («видимого поведения не меняют… НЕ входят»), с явной привязкой к User-Visible: no docs/specs/385-audit-lows.md:140-143
L1 — «план автотестов» ссылается на несуществующий тестовый паттерн _valueBadgeForBinding Не требовала правки — в r1 снята решением ревьюера без возврата («технический вопрос — домен автора, не блокирует») Текст не менялся, docs/specs/385-audit-lows.md:115-117 — по-прежнему присутствует нетронутым; закрытие r1 было «оставляю как есть», а не «поправить», так что отсутствие правки — ожидаемо, не регресс

M1 и L2/L3 закрыты по существу и корректно. Однако при сверке дельты нашёл остаточную несогласованность текста — новая находка ниже.

Находки

Low (правится или снимается решением ревьюера — не блокирует)

L4. Абзац-обоснование перед «Контрактом» (в) сохраняет старую неточную формулировку предиката, которую r1 требовал заменить.

docs/specs/385-audit-lows.md:41-44:

parseRecords зовёт дорогую проверку (2×git show на каждый src-файл) для КАЖДОГО коммита, хотя потребляется она только для стабильных релизных (/^Release v\d/ без beta/candidate, :134).

Это почти дословно та же фраза, которую вердикт r1 (M1, «Требуется на этом раунде») просил заменить на точную — и она действительно заменена, но только в следующем абзаце («Контракт: …»). Абзац-обоснование перед ним остался нетронутым и по-прежнему называет лишь первый дизъюнкт, без трейлера Release:. Формально в документе сейчас два соседних утверждения о том, при каких коммитах включается проверка: неточное (проблема) и точное (контракт).

Почему это не Medium: нормативная часть — именно «Контракт», не «Проблема»; контракт сформулирован недвусмысленно, с конкретным контрпримером и файл:строка, и явно предупреждает «это не то же, что первый дизъюнкт». Риск, из-за которого M1 был возвращён (реализатор напишет упрощённую версию, прочитав только описание проблемы) кода не создаёт критического пути, потому что реализатор будет реализовывать по «Контракту» и AC4, а не по абзацу «Проблема». Но самопротиворечие в тексте ухудшает читаемость и не помешало бы поправить заодно, раз уж абзац переписывался.

Не блокирует переход в S5-ready; снимаю записью, правка на усмотрение автора при следующей правке этого файла (не стоит отдельного раунда).

Унаследовано из r1

Без повторной проверки в r2 приняты факты, установленные в docs/reviews/SPEC-REVIEW-385-r1.md на коммите c28b2f2d — дельта их не затрагивает:

  • Обоснование полного трека (критерий «одна поверхность» §5 не выполняется, прецедент #369 / SPEC-REVIEW-376-r1 H1).
  • Точность описаний дефектов (а), (б), (г) — построчная сверка с houseplan-editor-runtime.ts:12252-12312, devices.ts:868-892, import_export.py:504-519 соответственно; текст этих пунктов в дельте r2 не менялся.
  • Наличие всех обязательных разделов §7.1, однозначность и доказуемость AC1, AC2, AC3, AC5, AC6 (AC4 перепроверен в r2 отдельно — см. ниже), реалистичность плана автотестов и мутантов (кроме отмеченного L1), корректность отката, отсутствие незаявленных догадок, обоснованный отказ от эскалации продуктовых вопросов владельцу.
  • Ссылка контракта (а) на букву docs/specs/378-value-face-source.md:85.
  • Единственность пути записи value_source/value_badge через _valueBadgeForBinding (два вызова, :12260/:12308, третьего нет) — проверено в r1 через grep, риск в разделе «Риски» не менялся дельтой.
  • Отсутствие задвоенности AC5 относительно существующих pytest-тестов.
  • Неприменимость гейтов typecheck/test/build/check-docs/invariants/ смоков/golden:verify на этой стадии — код продукта не существует; в r2 подтверждено, что диапазон c28b2f2d..687b2966 тоже не касается кода (git show --stat — единственный файл, docs/specs/385-audit-lows.md).

Что проверено заново в r2 (сверх таблицы закрытия)

  • AC4 остаётся согласован с новым текстом контракта. AC4 говорит «классификация релизности — та же функция, что в гейте (юнит сверяет идентичность/переиспользование предиката)» — это не изменилось и корректно описывает исправленный контракт (полное выражение, а не первый дизъюнкт). Расхождения между новым текстом (в) и AC4 нет.
  • Точность нового текста контракта (в) против кода. Выражение (/^Release v\d/.test(subject) && !/-(beta|rc|alpha)\.|candidate/i.test(subject)) || Boolean(one('Release')) сверено с scripts/process-gate.mjs:133-135 — совпадает дословно.
  • Точность контрпримера 5f6ee657. Перепроверено заново (не полагался на заявление r1): git show -s --format='%s%n---%n%B' 5f6ee657 — subject не матчит regex, трейлер Release: v1.69.0-beta.5 присутствует, значит isRelease истинно вторым дизъюнктом. Пример в спеке верен.
  • Файловый скоуп дельты. git show --stat 687b2966 — единственный файл docs/specs/385-audit-lows.md; других изменений в этом коммите нет.

Чего не проверял

  • Полный повторный разбор AC1, AC2, AC3, AC5, AC6, разделов «Сценарий», «Что человек увидит», «Скоуп/не-скоуп», «UX», «Модель данных», «i18n», «Риски» кроме их незатронутых дельтой частей — они не менялись между r1 и r2, наследую вывод r1 (раздел «Унаследовано из r1» выше).
  • Гейты typecheck/test/build/check-docs/invariants/смоки/ golden:verify — неприменимы, код продукта не существует (стадия ТЗ), дельта r2 тоже не код (подтверждено git show --stat).
  • Реальные числовые издержки 2×git show на типичный диапазон коммитов — не требуется AC4, только факт отсутствия лишних вызовов.

Вердикт

Зелёный: находка M1 предыдущего раунда закрыта по существу и корректно (текст контракта точен, сверен с кодом заново), L2 и L3 закрыты полностью. Новая находка L4 — Low, самопротиворечие соседних абзацев — не блокирует и снимается записью на усмотрение автора. High нет, Medium нет. ТЗ готово к переходу в S5-ready.