Зачем это нужно
Расширения, пресеты, workflows и шаги - примитивы (уроки 16.12-16.14). Бандл решает следующую задачу: собрать курированный стек для команды или роли и развернуть его одним шагом - с пиновкой версий, проверкой конфликтов и трекингом происхождения для чистого удаления и обновления.
Примеры из репозитория - наборы для продакт-менеджера, бизнес-аналитика, security-исследователя и разработчика. First-party бандлы bugfix и assess мы уже встречали как строительный материал собственного SDLC проекта (урок 16.4).
Основная идея
Бандл - распределительный и композиционный слой поверх примитивов, без собственной рантайм-поведения. Он описывается манифестом bundle.yml, обнаруживается через стек каталогов и при установке резолвит объявленные компоненты против пинов, проверяет единственную точку межбандлового конфликта (активную интеграцию) и применяет каждый компонент идемпотентно, с полным provenance-трекингом.
First-party бандлы
Специфицируемый каталог bundles/catalog.json: курированные, verified: true, резолвятся через встроенный источник builtin://default.
| Бандл | Роль | Компоненты | Сценарий |
|---|---|---|---|
bugfix |
developer | расширение bug + workflow bugfix |
ведомый assess -> gate -> fix -> test |
assess |
developer | расширение assess + workflow assess |
триаж идей до SDD |
add - алиас install:
specify bundle install bugfix
specify bundle add assess
First-party каталог качается из репозитория онлайн и падает в офлайн-снапшот пакета wheel - обнаружение работает без сети. Локальный манифест может ставить bundled-расширения и workflows с --offline; каталогные манифесты резолвятся из своих download_url, поэтому bundle add <id> сегодня требует сеть - полностью офлайновая каталоговая установка в планах.
Поиск и информация
specify bundle search [query] # --offline, --json
specify bundle info <bundle_id> # --offline, --json
Search выводит доступные бандлы с версией, ролью, источником и индикатором доверия (verified для курированных организацией записей, community для прочих) - чтобы оценивать доверие до установки.
Info показывает полные метаданные и полностью развернутый набор компонентов: каждое расширение, пресет, шаг и workflow с пином версии, приоритетом и стратегией пресета. Этот превью - тот же план, который применит install: видно ровно, что будет добавлено, включая предвидимые пересечения с компонентами уже установленных бандлов.
Установка
specify bundle install <bundle_id | path>
| Опция | Описание |
|---|---|
--integration |
интеграция при инициализации/установке |
--offline |
без сети |
--refresh |
обновить owned-компоненты из переданного источника бандла |
Аргумент - ID каталога или локальный путь (собранный .zip, каталог бандла или bundle.yml); локальные источники ставятся напрямую, минуя стек каталогов. Если текущий каталог - еще не проект Spec Kit, install сначала инициализирует его: свежий checkout приходит в рабочее состояние одной командой.
Интеграции: --integration выбирает ее при инициализации нового проекта и подтверждает цель, когда бандл пинит конкретную интеграцию, а активная не определима (отсутствует/нечитаем .specify/integration.json). Активную интеграцию уже инициализированного проекта опция не перекрывает: бандл с другой целью - отказ без изменений. Интеграция-агностичные бандлы наследуют активную.
Идемпотентность и границы: без --refresh уже присутствующие компоненты пропускаются. Компоненты, установленные вне бандлов, пропускаются и никогда не усыновляются - их установленная версия обязана совпасть с пином манифеста; при несовпадении или нечитаемости install и refresh останавливаются до изменений, называя компонент (удалите его или поставьте пинованную версию сами). При сбое запись provenance не пишется, поставленное в этом прогоне удаляется best-effort (ошибки удаления глотаются - частичное состояние на диске возможно).
Обновление записанного бандла: обычный install отвергает изменение версии или метаданных owned-компонентов (версия, источник, приоритет пресета, стратегия), включая удаление owned-компонента - даже при неизменной версии локального манифеста. Перестановка неизменных компонентов и добавление новых refresh не требуют. Применить изменения локального бандла без добавления в каталог:
specify bundle install ./new-release/bundle.yml --refresh
Источник может быть и каталогом, и .zip. Refresh идет тем же путем обновления примитивов, что и bundle update, повторно применяет owned-компоненты и удаляет исключенные из нового манифеста (если они не нужны другому бандлу); независимо установленные компоненты не трогаются и не усыновляются. Запись бандла продвигается только после успеха; измененные при неудачном refresh компоненты не откатываются.
Офлайн: локальный источник дает манифест, не payload-ы компонентов - каталогные компоненты требуют сети даже установленными. --offline добавляйте, только когда компоненты шипятся со Spec Kit; иначе команда назовет компонент, требующий сети, - перезапустите без флага.
Step-payload-ы резолвятся только через step-каталог. provides.steps бандла резолвятся исключительно через активные step-каталоги; bundle-local steps/<id>/ и относительные provides.steps[].source-оверрайды в этом релизе не резолвятся - так объявленный шаг офлайн не поставить. Чтобы доставить шаг с бандлом сегодня - опубликуйте его в step-каталоге, достижимом для пользователей бандла.
Обновление и удаление
specify bundle update [<bundle_id>] # --all, --integration, --offline
specify bundle remove <bundle_id>
specify bundle list [--json]
update перерезолвит бандл и refresh-ит компоненты через путь обновления каждого примитива, поднимая установленные до новых пинов с сохранением примитивных оверрайдов (например, приоритета пресета). Без ID - один бандл, с --all - все установленные.
Важно: контроль пинов - только на установке. Идемпотентные проверки - по id, не по версии: owned-компонент, уже присутствующий, пропускается без сравнения версии на диске с пином манифеста. Гарантия пинов - только при первой установке или refresh. Для принудительного приведения к пинам: specify bundle update <bundle_id> (каталоговые) или specify bundle install <path> --refresh (локальные).
remove удаляет только компоненты, внесенные этим бандлом; нужные другому установленному бандлу остаются (сопутствующих удалений нет). list выводит версии, счетчики компонентов и таймстемпы установки.
Инициализация проекта бандлом
specify bundle init [<bundle_id>] # --integration, --offline
Гарантирует, что текущий каталог - проект Spec Kit (идемпотентно инициализирует при необходимости), затем опционально ставит бандл. Явный one-step bootstrap для нового checkout.
Валидация, сборка, публикация
specify bundle validate # --path (каталог или bundle.yml; дефолт - текущий), --offline
specify bundle build # --path, --output
Validate проверяет корректность bundle.yml и резолвимость каждой ссылки против bundled-компонентов, установленных в проекте и - онлайн - активных каталогов. Отказ - только при доказанном отсутствии везде, где можно проверить (каталог достижим и подтверждает отсутствие); непроверяемые ссылки (офлайн или недостижимый каталог) понижаются до предупреждений, чтобы авторинг продолжался.
Build собирает единственный версионируемый дистрибутив .zip из каталога бандла; артефакт встраивает манифест и ставится напрямую specify bundle install <artifact.zip>.
Публикация: авторы валидируют и пакуют бандл локально, затем хостят артефакт и метаданные каталога там, где пользователи их достанут. Запись каталога указывает на артефакт, но компоненты внутри bundle.yml резолвятся через bundled/установленные компоненты или активные каталоги расширений, пресетов, workflows и шагов. Если бандл ссылается на компоненты не-дефолтных каталогов - документируйте их URL и тестируйте установку из чистого проекта с подключенными каталогами; сабмишены сообщества прикладывают это доказательство резолюции зависимостей в issue Bundle Submission.
Каталоги
Бандлы обнаруживаются через приоритизированный стек источников (проект, пользователь, встроенные):
builtin://default- first-party бандлы изbundles/catalog.json(bugfix,assess, ...), install-allowed;builtin://community- сабмишены сообщества изbundles/catalog.community.json, discovery-only.
Политики: install-allowed разрешает установку; discovery-only виден в search/info, установку отвергает.
specify bundle catalog list # стек с политиками
specify bundle catalog add <url> # --policy, --priority (меньше=выше; дефолт 10), --id
specify bundle catalog remove <id_or_url> # встроенные удалить нельзя
Повторное добавление источника с теми же ID, URL, политикой и приоритетом - no-op; с другими настройками - отказ.
Предупреждение документации: вэттьте и источник бандла, и каталоги его компонентов. Проект может нести собственный каталог бандлов и install-allowed каталоги компонентов под .specify/ - наличие этой конфигурации не доказывает, что в ней что-то ревьюилось. В незнакомом проекте перед установкой: specify bundle catalog list, плюс extension/preset/workflow catalog list и specify workflow step catalog list для тянущихся компонентов; источник, который добавляли не вы, считайте непроверенным до ревью.
Область действия команд: search и info работают где угодно (без проекта - fallback на встроенный/пользовательский стек); меняющие состояние (list, update, remove, catalog) требуют проект; install и init инициализируют проект по требованию в неинициализированном каталоге.
Практика
- В чистом каталоге выполните
specify bundle init bugfix --integration copilot- одной командой получите проект с расширениемbugи его workflow; проверьтеspecify bundle listи provenance. - Прогоните
specify bundle info assessдо установки и сверьте развернутый набор компонентов с тем, что реально появится послеinstall. - Соберите собственный минимальный бандл:
bundle.ymlс одним пресетом и одним расширением;validate,build, затем установка артефакта из.zipв другом проекте.
Краткое резюме
Бандлы - версионируемые пакеты поверх примитивов: bundle.yml пинит компоненты, установка идемпотентна и отслеживает происхождение каждого компонента, удаление убирает только вклад бандла. First-party каталог (bugfix, assess) верифицирован и работает офлайн через wheel-снапшот. Обновление - refresh через механизмы примитивов; контроль пинов гарантируется только при установке/refresh, не при пропуске идемпотентности.
Главное из урока:
- конфликтная точка между бандлами одна - активная интеграция;
--integrationне перекрывает активную существующего проекта; - независимо установленные компоненты не усыновляются: версия обязана совпасть с пином, иначе стоп;
--offlineреален только для bundled-компонентов; step-payload-ы бандла сегодня требуют step-каталога;infoпоказывает точный план установки - превью до обязательства;- доверие:
verifiedпротивcommunity, вэттинг каталогов проекта и компонентов.
Вопросы для самопроверки
- Что добавляет бандл поверх примитивов и чего он не добавляет? Почему он называется распределительным слоем?
- Что произойдет, если бандл пинит интеграцию, отличную от активной в проекте? А если активная нечитаема?
- Почему независимо установленный компонент с несовпадающей версией останавливает установку? Какие варианты действия называет команда?
- В чем ограничение идемпотентности установки по отношению к пинам версий? Когда пины гарантируются?
- Что удаляет
bundle removeи почему «сопутствующих удалений» нет? - Какие источники каталогов бандлов встроены и каковы их политики? Что нужно вэттить перед установкой бандла в незнакомом проекте?
Что дальше
Все слои кастомизации замкнуты в один вопрос: что фактически лежит в проекте и какой слой победил? Урок 16.16 разбирает specify artifact - read-only интроспекцию полного инвентаря команд, шаблонов и скриптов.