# Blockwright
Инструменты для сборки зданий в Minecraft, по одному зданию за раз.
Здание собирается из того, что есть: словесного описания, чертежей, векторного
плана, полноценной 3D-модели, захвата Google Earth, фотографий, кропа плоской
карты — в любом сочетании. На выходе схематика с верным пятном застройки, верным
углом, верными высотами и главными объёмами. Дальше её берут живые строители.
Это не попытка генерировать законченные здания. Это измерительно-чертёжный
набор: он снимает ту часть работы, которая является арифметикой, и оставляет ту,
которая является решением.
## Раскладка
```
blockwright/ библиотека: схематики, маски, рамы, меши, примитивы блоков,
модель входных данных, дорожная сеть плоской карты,
аппаратура обмера и фото-ревью
tools/ отдельные CLI: конвертация Google Earth, рендеры Blender,
самопроверки, синтетическое здание для тестов
buildings/ по пакету на здание — свои входы, рецепт и проверки
_template/ скелет, из которого заводят новое здание
docs/ как устроен пайплайн и что лежит в папке здания
.claude/ скиллы, команды и агент, которые ведут сборку
```
## Запуск
Скрипты импортируют `blockwright` абсолютно и запускаются как модули из этого
каталога, поэтому от текущей директории не зависит ничего:
```sh
python -m buildings.<имя>.probes.derive # обмерить входы
python -m buildings.<имя>.build --greybox # объёмы, ворота 4
python -m buildings.<имя>.review --greybox --mesh # посмотреть на форму
python -m buildings.<имя>.build # начертить здание целиком
python -m buildings.<имя>.gate # сверить с эталоном
python -m buildings.<имя>.review --mesh # и посмотреть на это
```
Именно в таком порядке. На круге правок первые три удобнее гонять одной командой:
```sh
python -m tools.run <имя> --quick
```
Она делает то же самое и печатает **только то, что сдвинулось** против прошлого
прогона — и по отчёту гейта, и построчно по выводу обмера и чертежа. Прогон, в
котором ничего не поменялось, это три строки вместо трёхсот. `--quick` вдобавок
пропускает рендеры, планы и листы сравнения: на реальном здании это большая часть
времени и ни одного числа, которое читает гейт. Перед тем как на что-то смотреть
и перед ревью — прогнать без него. Обмер пропускается сам, если ни один вход, ни
пробник, ни файл библиотеки не двигались; `--remeasure` заставляет.
Круг ревью — три команды:
```sh
python -m tools.run <имя> --review # числа, потом рендеры для глаз
python -m tools.review_diff <имя> # что вообще сдвинулось с прошлого круга
python -m tools.review_verify <имя> # «до/после» для проверяющего агента
```
Разведку делает `blockwright-photo-reviewer` — слепой, ему дают только картинки и
ничего больше. Подтверждение делает `blockwright-fix-verifier` — **другой** агент,
ему говорят, что чинили, и он отвечает на закрытый вопрос «устранён или нет».
Замечание закрывается только по свежей картинке, а не по факту правки в коде: до
этого «исправлено» означало «изменился код», и починка, не изменившая ничего,
сохраняла галочку.
Чертёж читает то, что намерил обмер, и без этого файла
не стартует — поэтому ни одна константа в скрипте здания не может продолжать
говорить то, что было верно до перерисовки карты. Ревью отказывается работать
против схематики старше рецепта; папку, которую оно оставляет в `out/review/`,
читает модель в сессии, а находки ложатся в `buildings/<имя>/findings.md` —
**над** `out/`, потому что `out/` удаляется целиком, а журнал ничем не
пересобирается.
Пути к данным разрешаются из `paths.py` каждого здания, так что текущий каталог
важен только импортам.
**Результат — `out/build.schem` в локальных координатах, и это законченный
результат.** Вставляется куда надо руками. `build --greybox` пишет рядом
`out/greybox.schem` — одни объёмы, без деталей; на них смотрят ворота 4, до
того как написан код деталей.
Нужен Python 3.11+ и Pillow. numpy необязателен и только ускоряет — обе
реализации обязаны давать побайтово одинаковый результат, и `mask_selftest` это
проверяет, включая растеризацию прямоугольника и диска под произвольным углом.
Ставить стоит: на реальном здании он режет прогон вдвое. Blender нужен для
конвертации Google Earth и рендеров эталона.
Самопроверки, которые стоит прогонять после любой правки библиотеки:
```sh
python -m tools.pipeline_selftest # все четыре ветки входов, от обмера до гейта
python -m tools.coverage_selftest # чем измерена каждая часть, и что держит участок
python -m tools.schedule_selftest # манифест видит все режимы отказа
python -m tools.lattice_selftest # стена на решётке повторяется, штамп копирует
python -m tools.mask_selftest # быстрый путь совпадает с обычным
python -m tools.greybox_selftest # два списка секций; деталь не течёт в объёмы
python -m tools.topsheet_selftest # три панели одного охвата, план в пиксель на блок
python -m tools.findings_selftest # журнал, аудит и остановки ручного режима
python -m tools.fixture buildings/<имя>/input # синтетическое здание целиком
```
Первая — главная. Она собирает синтетическое здание четырьмя способами (карта,
модель, описание, вектор), прогоняет каждый через `derive`, `build` и `gate` и
падает, если хоть одна строка гейта покраснела. Ветки ломаются именно так: правку
пишут против той, что под рукой, а остальные три продолжают «работать» ровно до
того дня, когда их кто-нибудь запустит.
Вторая — единственное место, где проверено всё, на чём держится «единица работы
— участок»: строка покрытия во всех трёх состояниях и обоими входами, правило
асимметрии, по которому решается, на каком конце участка встанет добранная
часть, пороги мелочи, строка `the clip holds the site`, отказ `--clip-up` и
`prove_register_floor` — материал выше пола регистрации, которого нарисованный
план не рисовал. Ни одна из этих проверок не входит в первую.
## Что читать
- **[docs/sources.md](docs/sources.md)** — какой вход на какой вопрос отвечает,
что измеряется, а что объявляется, и почему вердиктов три, а не два.
- **[docs/pipeline.md](docs/pipeline.md)** — метод целиком и обоснования тех его
частей, которые выглядят произвольно.
- **[docs/data-contract.md](docs/data-contract.md)** — что лежит в папке здания
и как завести новое.
## Три правила, из которых следует всё остальное
**Один блок — один метр**, и один пиксель плоской карты — один метр. Никаких
масштабных коэффициентов нигде.
**Каждый вход решает только то, в чём он силён.** Карта авторитетна для
положения, размера и поворота — и никогда для отдельного ребра, потому что она
рисована от руки. Захват Google Earth авторитетен для объёма — и никогда для
формы, потому что это фотограмметрия. Фотографии авторитетны для материала и
ритма — и никогда для размеров.
**Постройка живёт на решётке, а не на плоскости.** Азимут сажается на наклон
`p:q` с коротким мотивом, и тогда лестница вдоль стены периодична, а секция,
повторённая через целое число периодов, — это те же клетки, сдвинутые
целочисленно. Цена — доли градуса — измеряется и печатается. Вне решётки такого
вектора не существует, и восемь одинаковых вилл выходят восемью разными растрами
одной виллы.
**Постройка проверяется тем входом, которого ей не давали.** Сравнение результата
с картой, из которой он выведен, доказывает только, что растеризатор работает.
Меш читается ради чисел и никогда ради геометрии — поэтому он и может быть
свидетелем. Когда свидетеля нет, гейт печатает `ungraded`, а не `pass`.
## Состояние
Пайплайн принимает семь видов входа в любом сочетании и проверен на пяти
сценариях: карта с захватом, только модель, только описание, только чертежи,
векторный план с захватом.
Не сделано: плагин для Paper (удалённые get/set блока через FAWE) и MCP-сервер,
который вёл бы всё это из чата, чтобы правки появлялись в живом мире, а не в
файле. Схематик достаточно, чтобы разрабатывать против них, и начинать стоило
именно с них.