Files
houseplan-card/docs/reviews/SPEC-REVIEW-593-r2.md
2026-09-19 06:03:36 +00:00

25 KiB
Raw Permalink Blame History

SPEC-REVIEW-593-r2 — «Обновить встроенную библиотеку мебели до houseplan-0.4.0»

Issue: #593 Этап: spec (полный трек, унаследовано из r1 — критерий «одна поверхность» нарушен явно) Заход: r2 · блокирующих циклов израсходовано на входе в раунд: 0 из 4 (дано конвейером); после публикации этого вердикта — 1 из 4 (вердикт ниже не зелёный)

Вердикт

Красный. High: 1. Medium в скоупе: 0. Medium вне скоупа: 0. Low: 0.

Разбор по дельте (§2.10): найдены обе закрытые находки r1 (H1, M1) — закрытие подтверждено по существу, а не по заявлению автора. Но закрытие H1 расширило раздел §3 ТЗ третьим вопросом (Q3, авторство/лицензия), которого r1 не проверял по содержанию (в редакции r1 AC10 требовал будущего подтверждения и был признан корректным именно как требование). Проверка того, что фактически произошло по Q3/AC10 в этом раунде, вскрыла новую находку той же серьёзности, что и закрытая H1.

Материал раунда (входные данные разбора)

  • Тело issue #593 на момент разбора: gh issue view 593 --json body (редакция 3 от 19.09.2026, раздел ## ТЗ), сохранённое предложение автора выше него.
  • Все 5 комментариев issue: gh issue view 593 --json comments.
  • Полная временная шкала: gh api repos/Matysh/houseplan-card/issues/593/timeline.
  • Предыдущий раунд: docs/reviews/SPEC-REVIEW-593-r1.md, вердикт красный, материал зафиксирован в его собственном машинном блоке якорей: ветка dev, коммит 073c45b04308cd613182c700f30c75ebfa8d2ca7, дерево 393fc8925392ed600ae675587da91f34ba6b3372, тело issue sha256 8be001d5a0b2efa4aeb3f2bcd347a9253252dd47c38b091473e6686ca24f91d6. Текущее тело issue (редакция 3) очевидно отличается от этого текста — задача прошла через редакции 2 и 3 в ответ на вердикт r1; байтового совпадения с r1 не проверял, поскольку различие заведомо есть (сам факт правки — предмет этого раунда).
  • Прецедент #159 — та же подсистема, предыдущее внедрение библиотеки мебели (0.3.0): его r1-ревью, его находка High-1, его remediation-комментарии, финальный код (test/furniture-assets.test.mjs, assets/furniture/houseplan-0.3.0/README.md). Читал заново специально для этого раунда, поскольку именно на него ссылается текущий ТЗ (AC10) как на образец, и именно его конкретный текст стал мерилом находки H1(new) ниже.
  • Код: test/furniture-assets.test.mjs (тест 'release provenance is normalized to the repository MIT grant'), assets/furniture/houseplan-0.3.0/README.md, src/furniture-art-runtime.ts, src/houseplan-card.ts:2693, src/i18n/{ru,en,fr,de}.json (ключ toast.furniture_art_load_failed) — прочитаны, чтобы проверить два конкретных утверждения редакции 3 (см. «Как проверялось»).
  • Ветка/код продукта: не создавались. git log подтверждает отсутствие любых коммитов или веток issue/593-*; рабочая копия на HEAD = dcd6657581ba205edb63713e011a498b51983a20 (последний коммит — публикация документа r1), продуктовый код не менялся с r1. Гейты typecheck/test/build/bundle:budget не запускал — на этапе ревью ТЗ они не относятся к предмету раунда (см. «Чего не проверял», и это же практика самого r1).

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

Находка r1 Чем закрыта Где это видно
H1 — Р1 (cactus → plant) и Р2 (поведение при отказе чанка) помечены «Принято» без независимого голоса владельца, blocked не применялась Автор признал находку текстуально (#issuecomment-5741…, 05:46:43): «молчание — согласие... я подменил одно другим». §3 ТЗ переписан: оба варианта стали «ОТКРЫТЫ, ждём владельца» с ценой каждой ветки; blocked поставлена поверх S3-spec. Владелец ответил отдельным, более поздним и стилистически иным комментарием (#issuecomment-5739749394, 05:48:51, через ≈2 минуты и после смены статуса на blocked): «1. Принимаю default · 2. Принимаю default · 3. Подтверждаю собственное авторство иконок». blocked снята комментарием 05:51:27 после получения ответа. Таймлайн issue (gh api .../timeline): commented(Matysh,05:46:43) → labeled(blocked? — см. ниже) → commented(Matysh,05:48:51) → commented(Matysh,05:51:27). Текст §3 ТЗ, редакция 3: «Ревью r1 (H1) справедливо забраковало прежнюю редакцию... Вопросы были заданы заново, issue нёс blocked, ответы получены отдельным комментарием владельца». Формат (отдельный комментарий, задержка, blocked между вопросом и ответом) совпадает с протоколом §7.1 и с прецедентом, который сам r1 привёл (SPEC-REVIEW-588-r1). Считаю закрытым: структурная часть находки (самопровозглашённое согласие в одном сообщении без паузы) не повторилась. Проверил содержательно оба ответа (Q1, Q2) отдельно — оба воспроизведены корректно в §3, §4 п.2/4, §6, §10 (AC3/AC8) без остаточных противоречий со старой формулировкой «33 непустые категории» (расхождение с предложением автора названо явно).
M1 — AC9 заявляет защиту трёх бюджетов, а assertBundleBudget реально ограничивает потолком только initialViewGzipBytes; два ленивых графа только печатаются §7 ТЗ переписан: в скоуп добавлена чистая функция lazyGraphCeilingViolation(bytes, {ceiling, label}) по образцу initialViewCeilingViolation, две новые константы LAZY_FURNITURE_ART_GZIP_CEILING/LAZY_EDITOR_GZIP_CEILING (полоса 2000 B, зазор >500 B), обе сверки — в assertBundleBudget. AC9 переписан: «защищены гейтом, а не печатью», третий столбец таблицы AC называет мутант bundle-budget-lazy-ceiling-never-fires. §4 п.5, §7, §8 «Мутанты» (таблица), §10 AC9 (таблица «AC · чем доказан · чем краснеет») редакции 3. Прочитал текст: описанный механизм (чистая функция + throw в assertBundleBudget) — реалистичная, проверяемая надстройка над кодом, который сам же r1 прочитал построчно (scripts/bundle-budget.mjs:399-450); ничего в редакции 3 не противоречит структуре существующего кода. Числа ценностей (полоса/зазор) корректно вынесены в §12 п.5 как техническое предположение, а не как факт — ревьюер вправе оспорить, но оспаривать нечего: значения снимаются с реального замера после сборки, что и должно быть так. Закрыто полностью.

Новая находка (delta r1→r2): Q3/AC10 не достигает собственной планки ТЗ

High (блокирует)

H1(new). Раздел §3 Q3 и AC10 заявляют, что владелец «публично подтвердил... авторство и MIT для всех 93 SVG» по образцу прецедента #159, но фактически данный владельцем комментарий заметно уже, чем требует и сам этот прецедент, и собственная формулировка AC10; при этом ТЗ не планирует восстановление механизма провенанса (README + SHA-256 + ссылки на комментарии), на который сейчас опирается исполняемый тест той же подсистемы.

Воспроизведение:

  1. Issue #593 подан аккаунтом nikitaevfz-commits, author_association: NONE (gh api repos/Matysh/houseplan-card/issues/593 --jq .author_association → NONE) — сторонний контрибьютор без каких-либо прав в репозитории, приложивший архив houseplan-furniture-0.4.0.zip прямо в теле issue. Это тот же самый аккаунт, что подавал архив для предыдущего внедрения библиотеки: в #159 r1-ревью цитирует «комментарий nikitaevfz-commits (authorAssociation: NONE)» как источник архива 0.3.0 (gh api repos/Matysh/houseplan-card/issues/159 --jq .author_association → OWNER, потому что issue #159 создал сам владелец — но приложенный архив внутри неё дал именно nikitaevfz-commits). Это не гипотетический риск «а вдруг сторонний» — это второй раз подряд один и тот же посторонний аккаунт поставляет «готовый» набор иконок с заявленными правами.

  2. Планка, которую сам этот ТЗ называет образцом (AC10: «АС10 корректно учитывает урок #159»), — дословный текст, который #159 признал необходимым и достаточным (issuecomment-5454085168):

    «Я являюсь автором всех 77 SVG из архива houseplan-furniture-custom-0.3.0 и разрешаю House Plan использовать, изменять и распространять их на условиях MIT License репозитория без обязательной отдельной атрибуции в интерфейсе».

    Три обязательных элемента: (а) явное заявление об авторстве, (б) явное разрешение на использование/изменение/распространение, (в) именование конкретного архива/версии.

  3. Фактический ответ владельца на Q3 в #593 (issuecomment-5739749394, 2026-09-19T05:48:51Z), пункт 3 из трёх: «Подтверждаю собственное авторство иконок». Из трёх элементов присутствует только (а). Нет разрешения на использование/изменение/распространение под MIT — ни слова про лицензию или условия использования. Нет привязки к конкретному архиву/версии (houseplan-furniture-0.4.0.zip) — фраза говорит об «иконках» вообще.

  4. Это не мелочь формулировки: авторство и разрешение на использование — разные юридические факты. Автор произведения не обязан автоматически разрешать его использование под конкретной лицензией; #159 явно развёл эти два факта и потребовал оба, поэтому текущий ответ закрывает Q3 только частично относительно того, что сам ТЗ обещает читателю в AC10.

  5. Механизм, ранее закрывавший этот же риск исполняемо, в новом ТЗ не продолжен. test/furniture-assets.test.mjs содержит тест 'release provenance is normalized to the repository MIT grant', который не просто сверяет pack.json.author/pack.json.license со строками — он проверяет, что assets/furniture/houseplan-0.3.0/README.md содержит конкретные ссылки на issuecomment-5454085168 (полный грант) и issuecomment-5449707137 (комментарий с архивом) и точный SHA-256 архива 9E969016EE3B4B4E3DB776FEC53C8B387B91368B118EB5E39911483DEF1B0953. Этот README — не файл из присланного пакета, а документ, специально написанный при внедрении 0.3.0 (закрытие Medium-1 из CODE-REVIEW-159-r1, коммит b688714f): в нём House Plan фиксирует свою собственную цепочку доказательства права на использование картинок. Редакция 3 §4 п.1 ТЗ #593 говорит: «Вендорить пакет целиком: pack.json, LICENSE.md, README.md, svg/menu/* (33), svg/plan/* (60). Каталог houseplan-0.3.0 удаляется». Это стирает файл-носитель провенанса 0.3.0 (тест на него ссылается по фиксированному пути assets/furniture/houseplan-0.3.0/pack.json/README.md — путь тоже исчезнет), а на его место ставится «README.md» пакета — неясно, из архива это файл, специально написанный вендором-художником про сам рисунок, или это должен быть новый провенанс-документ по образцу 0.3.0. Ни то, ни другое явно не названо. Ни AC10, ни §8 «Доказательства», ни §9 «Release-артефакты» не упоминают ни SHA-256 нового архива, ни ссылку на нужный комментарий, ни необходимость переписать/воссоздать тест провенанса с новыми значениями.

  6. Следствие для код-ревью: если это не поправить сейчас, у исполнителя есть ровно два пути и оба плохие — (а) отредактировать тест провенанса так, чтобы он снова сверял что-то конкретное, но выдумав, на какие ссылки/хеш ссылаться, поскольку ТЗ этого не говорит (ровно тот класс дефекта — «догадка, выданная за факт» — который сам процесс называет «худшим видом»); либо (б) тихо удалить или ослабить этот тест вместе с директорией 0.3.0, и тогда единственная исполняемая защита от повторения истории #159 пропадает молча, а «чем краснеет» у AC10 (сейчас: «подмена автора или лицензии в манифесте») продолжает проверять только внутреннюю согласованность строк pack.json, а не то, откуда взялось само право на эти 93 файла.

Почему это продуктовый/юридический вопрос, а не то, что ревьюер вправе решить сам: ровно как и H1(r1) — это факт, который может подтвердить только владелец лично, и вердикт ревью не может выдать чтение кода за такое подтверждение (PROCESS.md §7.1: «Технический спор... решается вердиктом... продуктовое — нет»). Отличие от закрытой H1 в том, что здесь ответ уже дан, но не покрывает то, что заявляет AC10.

Чем закрывается: один явный публичный комментарий владельца, называющий архив по имени/версии (houseplan-furniture-0.4.0.zip, версия пакета 0.4.0) и содержащий разрешение на использование, изменение и распространение под MIT License репозитория — по образцу текста #159, приведённого выше. Плюс явный пункт в скоупе/AC10/§8, требующий воссоздать провенанс-документ (README нового пакета либо docs/-заметку) с этим комментарием и SHA-256 архива, и обновить (а не молча снести) тест 'release provenance is normalized...' под новый путь и новые значения — так же, как это было сделано в #159 для Medium-1.

Что проверено и корректно (унаследовано + новое)

  • Закрытие H1(r1) и M1(r1) — подтверждено по существу, детали в таблице выше.
  • Числовые и структурные факты редакции 3, которые дельта не изменила по сравнению с r1 (состав RETAINED_IDS, default-размеры 12 retained-символов, число ключей i18n 29/56 в исходном состоянии, ловушка двойного объединения FURNITURE/BY_ID, существование golden-сцен и npm-скриптов, отсутствие коллизий id мутантов) — не проверял заново, см. «Унаследовано из r1».
  • Механизм subscribeFurnitureArtLoadFailures/тост toast.furniture_art_load_failed, на который опирается новое поведение Q2 (§3, AC8), — проверил в коде (src/furniture-art-runtime.ts:212-235, src/houseplan-card.ts:2693, ключ уже существует в src/i18n/{ru,en,fr,de}.json:798) — это существующая с #474 инфраструктура «один тост на страницу при уходе арта в fallback», её распространение на все 60 предметов вместо нынешних 48 (44 designer) корректно описано в ТЗ и не требует нового i18n-ключа, вопреки моему первоначальному опасению при чтении §6 — список из 7 новых ключей в §6 полон и точен.
  • §3 Q1/Q2 корректно и последовательно проведены через весь документ: §4 п.2/4, §5, §6, §10 (AC3/AC8), §11 — расхождение со старым тезисом «33 непустые категории» названо явно везде, где он встречается в сохранённом предложении автора.
  • §7/AC9 (закрытие M1) технически состоятельно при чтении существующего scripts/bundle-budget.mjs/bundle-manifest.mjs — сама структура (чистая функция + throw) реализуема без противоречий с текущим кодом.

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

  • npm run typecheck, npm test, npm run build, npm run bundle:budget, golden:verify, любые смоки — этап ревью ТЗ, продуктовый код не менялся с r1 (нет ни одного коммита в issue/593-*, HEAD — публикация документа r1). Эти гейты относятся к код-ревью, не к этому раунду; так же поступил и r1.
  • Байтовое содержимое присланного houseplan-furniture-0.4.0.zip — как и в r1, полагаюсь на прогон генератора аналитикой S2 (OK: 60 plan symbols, 33 menu icons); фактическая проверка — предмет код-ревью, когда пакет окажется в дереве.
  • Не пересчитывал SHA-256 самого архива houseplan-furniture-0.4.0.zip — H1(new) не о том, что хеш неверный, а о том, что ни хеш, ни ссылка на него нигде не зафиксированы как требование ТЗ.
  • Не проверял французский и немецкий переводы новых строк на смысл — они появятся при реализации.
  • Не проверял, применялась ли метка blocked физически (в timeline она видна как unlabeled/labeled без явного текста имени метки в JSON-выводе --json); полагался на текстовые подтверждения обоих комментариев автора («ставлю blocked», «blocked снят») и на факт, что между вопросом и ответом действительно прошло отдельное, самостоятельное сообщение — это и есть контролируемый по процессу факт, а не сама метка.

Материал раунда (для конвейера)

  • Issue #593, тело редакции 3 на момент разбора (gh issue view 593 --json body), все 5 комментариев (gh issue view 593 --json comments), полная временная шкала (gh api repos/Matysh/houseplan-card/issues/593/timeline).
  • Заход r2, циклов ревью ТЗ израсходовано до этого раунда: 0 из 4 (значение дано конвейером на входе в раунд); лимит для полного трека — 4 (§4). Текущий красный вердикт израсходует цикл после публикации.
  • Код сверялся на рабочей копии в состоянии HEAD = dcd6657581ba205edb63713e011a498b51983a20 — продуктовый код под задачу не менялся, все ссылки на строки относятся к текущему dev.

Материал раунда

  • Ветка: dev, коммит dcd6657581ba — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 0b48707268e99ee38221c8e74fb1c48ce6793e35
    git log --all --format='%H %T' | grep 0b48707268e9
    
  • Тело issue: d3303755f955b90dcdf74279c7b76a98de9814103c65cfeb2a5455a49201d457
  • Вердикт конвейера: red · High 1