mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
@@ -0,0 +1,89 @@
|
||||
# SPEC-REVIEW-593-r2 — «Обновить встроенную библиотеку мебели до houseplan-0.4.0»
|
||||
|
||||
Issue: [#593](https://github.com/Matysh/houseplan-card/issues/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`](../../docs/reviews/SPEC-REVIEW-593-r1.md), вердикт красный, материал зафиксирован в его собственном машинном блоке якорей: ветка `dev`, коммит `073c45b04308cd613182c700f30c75ebfa8d2ca7`, дерево `393fc8925392ed600ae675587da91f34ba6b3372`, тело issue sha256 `8be001d5a0b2efa4aeb3f2bcd347a9253252dd47c38b091473e6686ca24f91d6`. Текущее тело issue (редакция 3) очевидно отличается от этого текста — задача прошла через редакции 2 и 3 в ответ на вердикт r1; байтового совпадения с r1 не проверял, поскольку различие заведомо есть (сам факт правки — предмет этого раунда).
|
||||
- Прецедент [#159](https://github.com/Matysh/houseplan-card/issues/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](https://github.com/Matysh/houseplan-card/issues/159#issuecomment-5454085168)):
|
||||
> «Я являюсь автором всех 77 SVG из архива `houseplan-furniture-custom-0.3.0` и разрешаю House Plan использовать, изменять и распространять их на условиях MIT License репозитория без обязательной отдельной атрибуции в интерфейсе».
|
||||
|
||||
Три обязательных элемента: (а) явное заявление об авторстве, (б) явное разрешение на использование/изменение/распространение, (в) именование конкретного архива/версии.
|
||||
3. Фактический ответ владельца на Q3 в #593 ([issuecomment-5739749394](https://github.com/Matysh/houseplan-card/issues/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`.
|
||||
|
||||
<!-- pipeline appends its own material-anchors block below on publish -->
|
||||
|
||||
---
|
||||
|
||||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||||
|
||||
## Материал раунда
|
||||
|
||||
- Ветка: `dev`, коммит `dcd6657581ba` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||||
- Дерево материала: `0b48707268e99ee38221c8e74fb1c48ce6793e35`
|
||||
```
|
||||
git log --all --format='%H %T' | grep 0b48707268e9
|
||||
```
|
||||
- Тело issue: `d3303755f955b90dcdf74279c7b76a98de9814103c65cfeb2a5455a49201d457`
|
||||
- Вердикт конвейера: `red` · High 1
|
||||
Reference in New Issue
Block a user