Skip to main content

blockwright-review

Этап 5 Blockwright, глаза — фото-ревью. Используй при работе с review.py, out/review/, findings.md, перспективными камерами, рендерами меша под теми же ракурсами, и когда нужно вынести вердикт по находкам ревью или отклонить находку измеренным числом.

Source facts

Repository
MelonHell/VibeBuilding
Last source activity
August 17, 2026 at 16:54
Detected SKILL.md language
Russian
Stars
1
Forks
1

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
blockwright-review
description
Этап 5 Blockwright, глаза — фото-ревью. Используй при работе с review.py, out/review/, findings.md, перспективными камерами, рендерами меша под теми же ракурсами, и когда нужно вынести вердикт по находкам ревью или отклонить находку измеренным числом.
# Этап 5 — глаза Листы ставят сборку рядом с мешем, и кто-то всё равно должен на них посмотреть. Никто этого не делает — каждый раз, для семи ракурсов и восьми фотографий. Фото-ревью — это то самое смотрение, превращённое в шаг, который запускается. ## Что оно делает `buildings/<name>/review.py` рендерит сборку из перспективных камер, рендерит эталонную геометрию из тех же камер через Blender, копирует туда же чертежи и фотографии и пишет промпт, называющий всё это. Всё приземляется в `out/review/`. Эта папка — вся доказательная база ревью, и читается она **за один раз** моделью, ведущей сессию. ```sh python -m buildings.<name>.review --mesh ``` Без `--mesh` рендерится только постройка — быстрее, но сравнивать её будет не с чем, кроме чертежей и фотографий. С `--mesh` рендерится то, что есть: модель или захват. Если нет ни того ни другого, скрипт так и говорит и не притворяется. **Промпт описывает только то, что в папке действительно лежит,** и говорит о каждом виде картинок, чего он стоит. Захват — надёжен для объёма, врёт про материал, под крышу не заглядывал. Модель — авторитет по форме, молчит про материал. Чертёж — намерение, а не постройка. Фотография — материал и ритм, ни одного размера. Ревьюер, которому пообещали фотографии и не дали ни одной, идёт искать материал в рендере, где материала нет, и докладывает палитру блоков как цвет здания. **Ревью без референсов не бывает.** Если в папке нет ни рендеров эталона, ни чертежей, ни фотографий, скрипт говорит `not enough for a review` и ревью не просит. Тогда единственной опорой ревьюера остаётся его собственное представление о том, как здание должно выглядеть, и на выходе получаются не слабые находки, а выдуманные. Увидел эту строку — скажи, чего не хватает, и остановись. ## Ревьюер — отдельный агент, и это часть этапа Ревью делает `blockwright-photo-reviewer`, а не та сессия, которая писала рецепт. Сессия знает каждое число в `derived.json`, и половина находок незагрязнённого ревьюера ей недоступна: она смотрит на рендер и видит то, что собиралась построить. Два здания сделали ревью сами и оба это записали как нарушение. **Вызов субагента здесь — не самодеятельность, а шаг этапа**; если инструкции сессии запрещают агентов вообще, это стоит сказать вслух и пометить находки как зрячие, а не молча сделать вид, что правило соблюдено. ## Что промпт теперь говорит сам Три вещи, которых в нём не было и из-за которых ревью промахивалось. Все три пишет `blockwright.reviewing`, править их в здании не надо: **Про свет.** Две итерации подряд выдали «одна длинная грань — глухая серая стена» и «тональность инвертирована». Обе — тень рендера: `render.py` намеренно светит так, чтобы крыша и стена разошлись по яркости. Промпт теперь говорит судить материал **по оттенку, не по светлоте**. **Про цвет.** Строка «игнорируй ограниченность палитры» читалась как «цвет не смотри», и здание осталось не того цвета с формулировкой «нет подходящего блока». Теперь: игнорировать **ступеньки и сетку блоков**, а цвет и материал докладывать всегда. Назвать увиденный цвет — работа ревьюера; искать под него блок — не его. **Про части, которые должны отличаться.** Каждый прочий вопрос — «постройка против эталона». Одинаковость двух башен — отношение **внутри** здания, и её не видит ни гейт (тот же силуэт, тот же профиль), ни ревью, пока не спросишь. Промпт спрашивает в обе стороны: одинаковость там, где в оригинале разница, и разница там, где повтор. Плюс отдельный вопрос про территорию — зонтики, лежаки, изгороди, посадки: пустая палуба докладывается как пропущенный этаж. Повторяющийся шум, который остаётся отклонять руками: **«поднять навес выше»**. Ревьюер видит фотографию изнутри, где фермы читаются арками, и не видит сечения. Отклонять числом каждый раз заново и записывать отказ. ## Пары кадров могут быть зеркальными Промпт обещал: «пара с одним номером — одна и та же камера в обеих моделях». Три здания из пяти стоят к своему захвату зеркально или на пол-оборота (`registration.turned` в `derived.json`), камеры задаются долями рамы — и пары показывали **разные стороны здания**. Ревьюер честно сравнивал дворовый фасад с уличным и объяснял разницу дефектом постройки. `shot_spec` теперь отражает доли по флипам, которые намерил `derive`, а раму и датум берёт **из `derived.json`**, а не подгоняет собственные — это та же болезнь «две регистрации», что была у гейта. Если фит не записан, промпт вместо обещания печатает предупреждение: сверь, что кадры показывают одну сторону. ## Одна папка, один запрос, один ревьюер **Один запрос**, потому что ревьюер, видящий все ракурсы разом, способен понять, что дефект на одном ракурсе и дефект на другом — это один и тот же дефект, и взвесить, какой ракурс показывает его лучше. Разбитый по запросам, он доложит одно и то же трижды в трёх разных размерах, и ни у одного запроса не будет доказательств, чтобы решить, какой прав. **Один ревьюер**, потому что второй был там, чтобы проверять первого, — а это проверка инструмента, а не здания. Стоила она эндпоинта, учётных данных и имени модели, прибитого в репозитории (три вещи, которые протухают), а покупала оценку согласия, по которой никто ничего не делал. **Ревьюер — та модель, которая уже работает. Имени модели в этом шаге нет нигде** — ни в `review.py`, ни в промпте, ни в конфигурации агента. ## Ревьюеру дают картинки и больше ничего Не `derived.json`, не константы, не вывод гейта. Это то же правило, на котором работает гейт, применённое к другой половине работы: **постройка оценивается входом, который ей не давали**. Ревьюер, держащий измеренные числа, объясняет сборку ей же самой, и находка, которая важна — та, где сборка уверенно и измеримо неправа насчёт чего-то, чего числа никогда не покрывали, — ровно та, которую он перестаёт делать. | Ревью решает | Ревью не решает | |---|---| | материал, цвет, ритм, пропорцию | ни одного размера | | какие части существуют, а каких нет | насколько они высокие или широкие | | читается ли часть тем, чем она является | стоит ли часть там, где говорит эталон | Одно исключение по авторитету: если эталон — **полноценная 3D-модель**, то она решает форму, и расхождение постройки с ней это ошибка постройки. У захвата такого права нет. ## Измеренное число бьёт находку Ревьюер не видит сечения по мешу и будет называть вещь низкой всякий раз, когда настоящее здание выглядит выше, чем захват. Две находки с реального примера, обе верные как наблюдения и обе отклонённые: - *«крона навеса должна стоять на 6–8 м выше линии крыши»* — крона измерена в 25.2 м, и меш заканчивается на 25.2 м. Не так была читаемость, а не высота, и чинилось это понижением хлама на кровельных террасах, пока свод не начал читаться сводом. - *«навес вылетает на несколько метров за обе длинные стены»* — меш не читает ничего выше 21.9 м над передним крылом, поэтому вынос на 23 м — это +2.2 м на станции, которую сечение оценивает, и она падает. Обе видны на фотографиях. Ни одной нет в захвате. Спор решает правило этапа 1: фотографии авторитетны для материала и ритма и никогда для размеров. **Отклонённая так находка записывается как отклонённая — вместе с числом, которое её отклонило.** Иначе её найдут снова в следующем раунде, и в раунде после него. ## Цикл, который сходится Ревью, у которого нет второго прохода, — это не проверка, а мнение. Пять зданий подряд сделали один раунд, записали половину находок как «исправлено» и на результат правки не посмотрел никто. «Исправлено» при этом означало «изменился **код**», а не «изменилась **постройка**» — и на одном здании починка честно записана как «changed nothing», потому что кто-то случайно глянул. Раунд k: **1. Числа.** `python -m tools.run <имя>`. Красный гейт останавливает цикл: глаза не тратятся на постройку, которую уже отвергли числа. **2. Рендер.** `python -m buildings.<имя>.review --mesh`. Прошлый раунд уезжает в `out/review/rounds/NN/`, `DESCRIPTION` обязан быть написан — иначе отказ. **3. Разведка.** `blockwright-photo-reviewer`, слепой: без чисел, без кода, без находок прошлых раундов. Один запрос на всю папку. **4. Триаж.** Каждая сырая находка ложится в журнал `buildings/<имя>/findings.md` с идентификатором. Ревьюер слеп по построению и поднимает одно и то же каждый раунд — счётчик `seen` отличает повтор от новизны. **5. Починки.** По одной правке на находку, гейт после каждой. `tools/run.py` с его дельтой делает это дёшево: правка глаз не смеет двигать станции сечения. **6. Подтверждение.** `python -m tools.review_verify <имя>` собирает `out/review/verify/` — «до», «после» и референс по каждому **сдвинувшемуся** ракурсу. Ракурс, который не сдвинулся ни на пиксель, туда не попадает и называется отдельно: правка до него не дошла. Папка уходит `blockwright-fix-verifier` — **другому** агенту, с закрытым вопросом «устранён или нет». Вердикт возвращается в журнал. `tools/review_diff.py` отвечает на тот же вопрос механически и раньше: рендерер детерминирован, поэтому неизменившийся кадр — это доказательство, что правка не легла, а изменившийся кадр там, где никто не работал, — коллатераль. **7. Когда остановиться.** Цикл закрыт, когда очередная разведка не приносит ничего выше «мелкой детали», а в журнале каждая находка либо `built`, либо `rejected` с числом, либо `ungraded` с названным недостающим входом. Или исчерпан бюджет раундов — тогда это говорится вслух. **Почему сходится, а не колеблется:** закрытие только по картинке (нет «исправлено, но ничего не поменялось»); отказ только числом плюс счётчик рецидивов (нет пережёвывания); одна правка на находку и гейт после каждой (глаза не ломают размеры); идентификаторы (один дефект не переименовывается в три). ## Проверяющий — не тот же агент, что ревьюер Разведка не должна знать, что искали, иначе перестаёт находить неожиданное. Проверка обязана знать — она судит правку, а не здание. Это единственное место, где правило «ревьюеру только картинки» гнётся, и гнётся оно осознанно: утечка намерения безопасна ровно потому, что задача другая. Два разных агента — структурная гарантия, что наводящий вопрос не попадёт в открытый поиск. ## Находки — это файл, а не разговор Журнал — `buildings/<имя>/findings.md`, **над** `out/`: всё под `out/` удаляется целиком, а журнал ничем не пересобирается. Полный формат — в `/blockwright-review`, раздел «Записать». Хроника раундов сверху (пустой раунд — заголовок без пунктов: это сходимость), блоки находок снизу: ```markdown ### F-03 | open | gate 5 | round 1 | seen 2 | by: reviewer **The two towers are identical; the left one is balconied.** **Fixed:** **Arose from:** the skeleton offers no balcony primitive. **Prevented by:** a `build.balcony` primitive and a line in the skeleton. ``` Закрытая находка без **Prevented by** — починка, которую нельзя обобщить. `by` по умолчанию `reviewer`; `by: human` — нашёл человек. Четыре диспозиции, и они не взаимозаменяемы: - **built** — изменился код **и** изменилась картинка. Обе половины обязательны: `tools/review_diff.py` существует потому, что раньше «built» означало только первую. - **rejected** — отклонено **числом**, и число записано. Не «мы не согласны», а измерение с указанием, откуда оно. - **open** — увидено, признано, ещё не построено. - **ungraded** — ничто из поданного не решает. Тот же серый вердикт, что у гейта, и он обязан назвать недостающий вход. `seen` — сколько раундов слепой ревьюер это поднимал. **Отклонённая находка, поднятая трижды, эскалируется человеку.** Ревьюер не упрямится — он не видит прошлых находок; значит либо число, которым её отклонили, не покрывает того, что он видит, либо отклонение было неверным. `blockwright.findings` печатает это на каждом прогоне. **Отказ «нет подходящего блока» — не аргумент, пока не назван список.** ```python from blockwright import blocks print(" ".join(blocks.why_not((185, 205, 215)))) ``` печатает пять ближайших с расстояниями. Дважды здание оставили не того цвета с формулировкой «в палитре нет такого оттенка», и оба раза кандидаты нашлись — один из них в собственном захвате того же здания. Брать ближайший по **оттенку**, даже если он темнее: цвет верного тона и неверной светлоты читается зданием, а серый верной светлоты — нет. Это журнал для глаз ровно так же, как `report.json` — журнал для чисел, и по той же причине: дефект, который только произносили вслух, нельзя посчитать, отследить между раундами и показать исправленным. ## Рендерить свежее или не ревьюить `review.py` отказывается работать против схематики старше рецепта, который её сделал. Версия этого правила у гейта защищает оценку; эта защищает час чужого времени, потраченный на починку дефектов, починенных на прошлой неделе. ## Когда запускать После каждого раунда работы над деталями — это та половина пайплайна, у которой нет замкнутого цикла. У объёмного этюда есть гейт; у деталей есть это. ## Последний метр: схематика против мира Ревью рендерит `build.schem` (а на воротах 4, с `--greybox`, — `greybox.schem`). Человек смотрит в мир, куда кто-то её вставил. Между этим — ничего, и починка, не доехавшая до мира, читается ровно как починка, которой не было. Один разбор ушёл в никуда именно так. `finish` пишет `out/<имя>.stamp.md` — перечень блоков по типам. Мир не хешируется, но умеет считать: ``` //pos1, //pos2 вокруг вставленной постройки //count minecraft:white_concrete ``` Совпало — та же сборка. Не совпало — вставляй заново, прежде чем судить скриншот. Ревью печатает напоминание каждый прогон. ## Камеры `PLAN` в `review.py` — список кадров. Держи там несколько наружных ракурсов с разных сторон и **несколько камер прямо внутри территории**: двор, проезд, уровень земли — это то, что захват реконструирует хуже всего (он снят с воздуха) и что фотографии показывают лучше всего. Кадры перспективные, а не ортографические. Ортография даёт силуэт, а силуэт — это ровно то, что уже проверено числами и что уже один раз соврало. **Одна камера обязательна и не про здание** — `05-approach` в шаблоне: уровень глаз, широкий объектив, с той стороны, откуда к зданию подходят пешком. Все остальные кадры выстроены так, чтобы здание влезло целиком, — а такого вида не бывает ни у кого. Человек видит первым делом первый этаж и то, как здание встречает тротуар: вход, стилобат, бордюр. Это и та часть, о которой меньше всего знает захват с воздуха, и та, которую сборка чаще всего оставляет голым цоколем. `DESCRIPTION` — одна-две фразы о том, что это за здание. Больше ревьюер о постройке не знает и знать не должен. ### `azimuth` — не компас `Outside.azimuth` крутится вокруг **длинной оси самого здания**: 0 — за дальним торцом по +u, 90 — за фланком по +v. Крой карты стоит к северу под своим углом (на одном здании 33°), поэтому имя камеры, выведенное из азимута как «east» или «beach», выведено не из того угла — и переживает всё, кроме взгляда на рендер. Три камеры одного круга были названы не той стороной. Две строки печатаются до рендера, чтобы это не проверялось картинкой: - **где стоит глаз** — доли рамы, и за каким фланком или торцом это. Камера `beach`, чей глаз за тем фланком, где палубы нет, названа неверно. - **отказ, если глаз внутри постройки.** Камера в дворе или под навесом стоит в воздухе и проходит; камера внутри пилона арки рендерит коричневую стену во весь кадр, и это уже уезжало ревьюеру вместе с описанием того, что кадр якобы показывает.
View on GitHub