# Сборка: собрать своё

Дополнение к SKILL.md — методика для сборки С НУЛЯ. Разбор ЧУЖОЙ сборки (пришедшей STEP/импортом) —
`reference/import-playbook.md`.

Плейбук: **создать → вставить → ЗАФИКСИРОВАТЬ базовую → сопрячь → rebuild → проверить.**

1. **Детали сначала сохрани в файлы.** Компонент вставляется ссылкой на `.m3d`/`.a3d`, поэтому
   несохранённую деталь вставить нельзя.
2. **`document_create assembly` → `assembly_add_component(filePath, x, y, z, rx, ry, rz)`.** Углы — в
   градусах, вокруг СОБСТВЕННЫХ осей компонента и по очереди X→Y→Z: второй угол считается уже от
   повёрнутого положения. Пока задан один угол, разницы с мировыми осями нет.
3. **Зафиксируй базовую деталь СРАЗУ: `assembly_fix_component(componentIndex=0, fixedState=true)`.** КОМПАС не закрепляет
   никого сам, включая первый компонент, и решатель двигает того, кого сочтёт нужным — в том числе
   основание, вокруг которого ты собираешь всё остальное. Это самая частая причина «сопряжение
   верное, а сборка расползлась».
4. **Сопрягай:** `assembly_add_mate(mateType, …)` — семь типов, адресация гранью и смысл
   `orientation` в `help(topic="assembly-mates")`. Что решаешь ты, а не справочник:
   - **Грань адресуй индексом, а не координатами:** `list_faces(component=N)` → пара
     `faceIndex`+`componentIndex`. Точки на бумаге считать не нужно, и сервер сам проверит, что
     грань принадлежит этому компоненту.
   - **Сторону зазора у `distance` задаёт ВЫБОР ПАРЫ ГРАНЕЙ, и от стартового положения она не
     зависит.** «Closest» — не «ближайшее»: при сонаправленных нормалях объект 1 садится со стороны
     −n относительно объекта 2 (замерено двойным прогоном). Практическое правило: бери пару граней,
     которые смотрят ДРУГ НА ДРУГА. Вышло зеркально — меняй грань, а не `orientation`.
   - **Первое сопряжение вешай на зафиксированную деталь.** Пока в цепочке нет ни одного
     закреплённого звена, решатель волен двигать любое; связка «каждый новый компонент — к уже
     стоящему на месте» разваливается вдвое реже, чем связка «все ко всем».
5. **`rebuild`,** если авто-валидация написала «ℹ Требуется перестроение». Это НЕ ошибка построения:
   так помечает себя свежий компонент или только что наложенная связь. Бросать работу здесь не надо
   — правило «не продолжай при ⚠» касается строки со знаком ⚠, а не этой.
   **Исключение из правила «⚠ = стоп».** После КАЖДОГО сопряжения авто-валидация пишет
   «⚠ N операц. в ошибке: „Сопряжения“ (код 0: ошибки нет)». Это то же «требуется перестроение»,
   только под знаком ⚠: код 0 и имя операции «Сопряжения» означают, что ломаться нечему. Делай
   `rebuild` и иди дальше. Настоящая беда со связью видна не здесь, а в
   `describe_model(sections=mates)` — по пометке `valid:false`.
6. **Проверь:**
   - `describe_model(sections=components)` — origin, поворот ЧИСЛАМИ (`rx/ry/rz` в градусах, та же
     семантика, что у вставки) и «зафиксирован/свободен» у каждого: этим и видно, КОГО и НА СКОЛЬКО
     подвинул решатель;
   - `describe_model(sections=mates)` — что с чем связано, с каким значением и не выродилось ли.
     Это ключевой детектор: `assembly_add_mate` может ответить успехом, а связь окажется
     вырожденной;
   - `measure(kind1=face, index1=…, componentIndex1=…, kind2=face, index2=…, componentIndex2=…)` —
     зазор между деталями напрямую, без арифметики по габаритам;
   - `assembly_check_interference(componentIndex1, componentIndex2)` — **не налезли ли детали друг
     на друга.** Проверяй все пары, которые должны идти впритирку.

**Неудачную связь не нужно пересобирать с нуля:** `assembly_delete(target="mate"|"component", index)`
снимает сопряжение или компонент вместе с опирающимися на него связями. Индексы после удаления
сдвигаются — иди от больших к меньшим и перечитывай `describe_model(sections=mates)`.

**Пересечение деталей не видно НИГДЕ, кроме `assembly_check_interference`.** Сборка складывается из
готовых тел, и КОМПАС их не вычитает: две детали могут занимать общий объём, а сопряжения при этом
останутся `valid`, раздел `errors` — пустым, объём экспорта — простой суммой компонентов. `measure`
тоже не спасает: нулевой зазор одинаково означает и касание, и взаимопроникновение. Проверяй все
пары, которые должны идти впритирку.

**«Тела: 0» в сборке означает не «пусто», а «спрошено не то»:** тела принадлежат компонентам
(`describe_model(component=N, …)`), а `box` и `mass` без `component` честно дают габарит и МЦХ сборки
целиком. Про разные системы координат внутри паспорта компонента —
`help(topic="model-inspection")`; про локальные координаты граней и `pointOnFace` —
`help(topic="face-addressing")`.
