# Публикация ассетов в SAM

Перед публикацией ответьте на два вопроса. Что разработчик сможет сделать сразу после импорта? Какой именно ресурс художник сможет взять отдельно? Обе операции относятся к одной версии. Разработчик получает пакет с точкой входа, художник выбирает нужный файл в составе той же версии.

Руководство открыто по `/agent-guide.md`. Контракт запросов находится в [справочнике API](/docs) и [OpenAPI](/openapi.json). Через MCP это руководство доступно как ресурс `sam://guides/publication`.

## Выберите единицу публикации

| Задача | Что выбрать |
| --- | --- |
| Поставить горящий дом в сцену | Один корень `HouseOnFire.prefab`, где автор уже разместил дом и эффект. В пакет входят вложенные префабы, меши, материалы, шейдеры и текстуры. |
| Использовать огонь отдельно | Если эффект должен жить и обновляться отдельно, опубликуйте версию с корнем `Fire.prefab`. В рецепте дома оставьте необходимые файлы эффекта с их исходными GUID. |
| Назначить один из трёх материалов | Один пакет с тремя точками входа `.mat`, если материалы распространяются вместе. Общий шейдер и каждая общая текстура входят в снимок по одному пути. |
| Скачать только альбедо стены | Откройте материал или дом в «Публикациях» либо найдите файл на вкладке «Файлы», найдите `.png` в составе точной версии и скачайте это вхождение. Отдельная карточка для каждого файла не требуется. |
| Собрать подборку существующих объектов | Используйте коллекцию. Если важно взаимное положение объектов, сохраните его в префабе или сцене. |

**Версия** фиксирует состав и байты поставки. **Точка входа** — конкретный путь и действие пользователя. У одной версии может быть несколько точек входа. Все корни рецепта v1 поставляются вместе, даже если пользователь выбрал лишь один из них. Разделяйте поставки, если пользователи должны скачивать, обновлять или назначать права на них независимо.

**Атом** в SAM идентифицирует байты файла по SHA-256. Один и тот же PNG может встречаться в нескольких пакетах или с разными параметрами импорта. Для ссылки из Unity сохраняйте исходные GUID, пути и `.meta`. Для скачивания конкретного вхождения используйте его версию и `download_url` из состава. Совпадение хеша само по себе не устанавливает зависимость между пакетами. Отдельные версии могут физически хранить одинаковые байты в своих снимках.

## Назначьте категорию и действие

Прочитайте `GET /api/catalog/taxonomy` с токеном пользователя, либо вызовите `get_catalog_taxonomy` через MCP. Ответ содержит дерево `categories` и действия `actions` с поддерживаемыми расширениями. Используйте стабильный `id`; отображаемое название и уровень вложенности получайте с сервера.

Основные ветви — `3d`, `2d`, `audio`, `vfx`, `tools`, `templates`. Выбирайте наиболее точную ветвь по тому, что ищет потребитель. Например, готовый автомобиль относится к `3d/vehicles`, предмет обстановки к `3d/props`, дом к `3d/environments/buildings`, самостоятельный эффект огня к `vfx/fire`. Если дом содержит огонь, основная категория дома остаётся категорией здания. Фильтр родителя включает вложенные категории.

Храните технический тип (`prefab`, `material`, `texture` и др.) в `type`, целевую среду в `engine`, область доступа в `scope` и `project`. Жанр, стиль и дополнительные признаки указывайте в `theme`, `style` и тегах. Категория и тип отвечают на разные вопросы. Для описания точки входа выберите действие из таксономии, задайте существующий путь и короткую понятную метку, например `place` + `Assets/City/HouseOnFire.prefab` + «Горящий дом».

## Подготовьте пакет для Unity

1. В авторском проекте сохраните префаб, материал или другой корень. Откройте **Window → Studio → Publish Project Assets**, выберите Unity и «Собирать на сервере по рецепту». Выберите именно те корни, которые хотите выдать пользователю. Проверьте собранный редактором список файлов и рекурсивных зависимостей.
2. Для рецепта v1 все нужные файлы должны быть внутри `Assets/`. Сохраните исходное дерево и оригинальные `.meta`. Если двум материалам нужна одна текстура, оба должны ссылаться на один GUID. В снимке нужен один экземпляр этой текстуры и её `.meta`. Если зависимость отсутствует, находится в другом пакете или требует внешнего SDK, устраните причину или оставьте версию без назначения: её файлы останутся доступны в «Файлах».
3. Выберите область доступа и версию. `scope=generic` открывает материалы команде; `scope=project` с именем проекта ограничивает доступ его участниками. При публикации существующей карточки используйте её `target_asset_id`. Сначала можно разместить версию в истории и испытать именно её.
4. Для прямого HTTP-клиента используйте `POST /api/project-publications` с `engine:"unity"`, `provenance.export_kind:"unity_recipe"`, именем `project-resources.zip` и полем `content_base64`. В `unity_recipe` передайте `schema:1`, выбранные пути `roots` и массив `files`. Каждая запись содержит `path`, `guid`, `sha256` и `meta_sha256`. ZIP должен содержать ровно объявленные файлы и соответствующие `.meta`. Передайте те же корни в `provenance.selected_files`. Список остальных обязательных полей и правила повтора по `request_id` приведены в [описании публикации](/docs#publishProjectAssets).
5. Откройте ссылку `version.representations.unitypackage` с авторизацией. SAM соберёт временный `.unitypackage` из снимка этой версии. Оригинальный `version.download_url` отдаёт ZIP. У загруженного готового `.unitypackage` оригинал уже является импортируемым файлом. Связи SAM между версиями не добавляют в нативный пакет файлы из другой карточки.
6. Импортируйте выданный пакет в чистый проект нужной версии Unity и render pipeline. Выполните каждое заявленное действие. Для горящего дома перетащите префаб в сцену, проверьте, что эффект играет, текстуры на месте и отсутствуют потерянные ссылки и ошибки консоли. SAM проверяет рецепт и пути. Проверку поведения выполняет человек в Unity, а результат записывается отдельно.

Файлы из пакетов Unity (`Packages/...`) в снимок не входят. Издатель указывает такие пакеты в `unity_recipe` со `schema:2` и полем `packages`: имя, версия и GUID использованных файлов. Например, материал на URP/Lit требует `com.unity.render-pipelines.universal`. Получатель устанавливает их сам, карточка показывает это в `requires_packages`. Локальные, встроенные в проект и Git-пакеты по имени не установить, такие зависимости нужно устранить.

## Покажите проверенную версию в разделе «Ассеты»

Версия попадает в каталог за два шага: назначение описывает, что можно сделать после импорта, а проверка подтверждает, что это работает. Сохраните описание для точной пары `asset_id` и `version_id`. Например, для дома:

```json
{
  "category": "3d/environments/buildings",
  "entrypoints": [
    {"path": "Assets/City/HouseOnFire.prefab", "action": "place", "label": "Горящий дом"}
  ]
}
```

В мастере Unity выберите «Назначить точки входа», укажите категорию и действия для корней. При публикации нового рецепта через HTTP это описание можно передать целиком в поле `catalog`. Рецепт и описание принимаются в одной операции. Если версия уже существует, прочитайте `GET /api/assets/{asset_id}/versions/{version_id}/catalog`. Добавьте `expected_revision` из ответа к показанному JSON и отправьте его на тот же URL методом `PUT`. Запрос заменяет весь список точек входа. У ещё не описанной версии `catalog:null`; считывайте `revision` и в этом случае. При конфликте `409` перечитайте состояние и проверьте смысл изменения. Для снятия описания отправьте `DELETE` на тот же URL с JSON `{"expected_revision": N}`, где `N` — прочитанная ревизия.

У рецепта каждая точка входа должна быть одним из объявленных корней. У нативного `.unitypackage` выбирайте проиндексированный путь под `Assets/` с GUID. Версия должна иметь пригодную для импорта поставку и содержать все необходимые проектные файлы. Новая ревизия рецепта наследует назначение предыдущей версии в пределах своих корней, а у других версий прежнее назначение возвращается в `suggestion`. Для смены текущей версии прочитайте её указатель и используйте `POST /api/assets/{asset_id}/current` с `kind:"unity"`, выбранным `version_id` и точным `expected_current_id` (либо `null`, если указателя нет). Для новой публикации ту же операцию можно выполнить через `publish_current:true` и `expected_current_id`.

После назначения версия появляется в `GET /api/catalog/review`. Импортируйте её в чистый проект, выполните все точки входа и запишите результат:

```json
{
  "expected_revision": 1,
  "unity_version": "6000.3.11f1",
  "render_pipeline": "urp",
  "result": "passed",
  "checked_paths": ["Assets/City/HouseOnFire.prefab"],
  "notes": "Эффект играет, потерянных ссылок нет."
}
```

Отправьте этот JSON методом `POST` на `/api/assets/{asset_id}/versions/{version_id}/catalog/verifications` или через MCP-инструмент `record_catalog_verification`. Записывайте только проверку, которая действительно выполнена: агент без доступа к Unity этого сделать не может. Публикующий не подтверждает собственную версию. Исключение — администратор, такая запись помечается `self_confirmed`. Если что-то не работает, отправьте `result:"failed"` с описанием в `notes`: новый провал снимает успешный результат этой среды, а при отсутствии других успешных сред версия покидает каталог. Изменение назначения снимает подтверждение. Каталог показывает текущую версию, если она подтверждена, иначе последнюю подтверждённую, поэтому публикация новой ревизии не убирает карточку из выдачи.

Затем проверьте `GET /api/catalog/ready?category=3d`: карточка должна предлагать проверенную точку входа, проверенные среды и ссылку на пакет. В «Публикациях» откройте состав этой же версии через `GET /api/assets/{asset_id}/versions/{version_id}/contents`. Скачайте отдельно нужный файл через `entries[].download_url`. Для поиска всех доступных вхождений того же файла используйте `GET /api/atoms/{atom_id}`.

## Покажите ассет настоящим движком

Приближённый просмотр не выполняет шейдеры и частицы Unity. Чтобы показать версию как в Unity, соберите для неё пакет WebGL на машине с Unity и модулем Web Build Support: `python3 webgl-viewer/build.py --work ПАПКА bundle --package project-resources.zip --entry "Assets/Car.prefab" --output preview`. Загрузите его методом `PUT /api/assets/{asset_id}/versions/{version_id}/webgl-preview` с полями `entry_path`, `render_pipeline` и `content_base64`. Точкой открытия может быть префаб, материал, меш (`.asset`) или сцена. Плеер рассчитан на Built-in Render Pipeline и выбранную версию Unity, код C# из ассета в нём не выполняется. Ограничения перечислены в `webgl-viewer/README.md`.

## Когда поручено разобрать существующую библиотеку

1. Обойдите доступные карточки постранично через `GET /api/v2/assets`. Сохраните для каждой пары ID текущее назначение, тип файла, состав, внешние зависимости и область доступа. Пустое назначение означает, что версия ещё не классифицирована.
2. Разделите кандидатов на версии для описания без перезагрузки, ресурсы для самостоятельного использования и версии, которым нужен новый снимок. Приведите основание для категории, конкретное действие и способ проверки. Одинаковое имя, тег или хеш файла недостаточны для такой классификации.
3. Если нативный пакет ещё не испытан, назначьте описание и передайте версию на проверку. Записывайте `passed` только по результату реального импорта. Нативный пакет с зависимостями от пакетов Unity опубликуйте заново по рецепту. Если отсутствует необходимая деталь или точка входа, опубликуйте исправленный состав новой версией, испытайте и только затем переключите текущую. Сохраняйте область доступа и проверяйте конфликты ревизий.
4. Отчитайтесь по ID карточки и версии. Запишите категории и проверенные пути, какие материалы оставлены без назначения, какие требуют исходного проекта или участия автора. Для уже совпадающего назначения пропустите запись.
