# Design: OpenSpec в DSH Issue: #42. Спека: `openspec/changes/dsh-openspec/specs/dsh-openspec/spec.md`. Спайк: 24.10.2026, temp-проект `/tmp/openspec-spike-dPkz`, openspec 1.13.1. ## 1. Что показал спайк | Проверка | Результат | |---|---| | `openspec init --tools agents` в пустом git-проекте | 6 скиллов в `.agents/skills/`, `openspec/config.yaml`, `openspec/{specs,changes/archive}`; AGENTS.md не создаётся | | Команды | `Commands skipped for: agents (no adapter)` — слэш-команд адаптер не ставит | | `--tools claude`, затем `--tools agents` | `.claude/skills` и `.claude/commands/opsx` остаются нетронутыми, `.agents/skills` добавляется рядом | | Отличие agents-скиллов от claude-скиллов | одна строка подсказки (`/opsx:apply` → `/openspec-apply-change`) и `allowed-tools: Bash(openspec:*)` в frontmatter | | `openspec new change` → `instructions` → `validate --strict` → `archive -y` | весь цикл без интерактивных вопросов; спека `probe` создалась, change ушёл в `openspec/changes/archive/2026-10-24-spike-test/` | | `openspec change new` | команды нет; группа `new` скрыта из верхнего `--help`, но `openspec new change ` работает | | DSH видит `.agents/skills` | да: сабагент, запущенный с cwd в worktree после `openspec init --tools agents`, получил в каталоге скиллов все шесть `openspec-*` | Последняя строка — главная: проектный корень скиллов DSH действительно читает `.agents/skills`, поэтому отдельный слой совместимости не нужен. ## 2. Решения ### R-1 (решение владельца). Скиллы живут в проекте, не глобально `openspec init --tools agents` пишет `.agents/skills` в проект; в `~/.dsh/skills` копии нет. Обоснование: - версия скиллов совпадает с версией CLI проекта, `openspec update` их обновляет; - глобальная копия стареет молча и не знает про `--store` и профили конкретного проекта; - если проект сделал init сам, глобальная копия даёт два скилла с одним именем; поведение DSH при дублях не проверено, и проверять его незачем. Цена: новый проект нужно один раз подключить (`--openspec` или `openspec init`). Это осознанно: OpenSpec — свойство проекта, а не машины. ### R-2 (решение владельца). Слэш-команды нет Вход — model-invocable скиллы: их `description` содержит «openspec propose», «openspec archive» и т.п., поэтому фраза владельца запускает воркфлоу без команды. `/role` остаётся единственной командой бандла. Цена: в DSH нет аналога `/opsx:*`, набирать команды вручную нельзя — только просить модель. Это укладывается в то, как DSH уже работает с ролями. ### R-3. `--openspec` не создаёт OpenSpec-корень Флаг добавляет agents-адаптер к проекту, который уже использует OpenSpec: если `openspec/` нет, скрипт отказывается и печатает готовую команду. Иначе `--openspec` в чужом каталоге молча завёл бы `openspec/`, `config.yaml` и changes-каталог — побочный эффект, который не читается из названия флага. ### R-4. `--check` предупреждает, а не падает Отсутствие `openspec` в PATH — `WARN`: guard, bootstrap, роли и SDD от него не зависят. Код возврата `--check` меняют только `FAIL`-пункты (python3, git, node, хуки, бандл). ### R-5. Проверка «DSH видит скиллы» — живая, не в тестах Юнит-тест может проверить наличие и frontmatter файлов, но не то, что DSH подхватил каталог. Поэтому в тестах — файлы и поведение `install.sh`, а видимость проверяется один раз живой сессией (сабагент с cwd в проекте) и фиксируется здесь. ## 3. Отличия от Claude Code | Что | Claude Code | DSH | |---|---|---| | Куда ставит `openspec init` | `.claude/skills` + `.claude/commands/opsx/*` | `.agents/skills`, без команд | | Вход | `/opsx:propose`, `/opsx:apply`, … | скиллы по имени/фразе; команды нет | | Обновление | `openspec update` | `openspec update`, тот же генератор | | Глобальная копия | нет | нет (R-1) | Обе копии в одном репозитории — не дублирование ради дублирования: это два клиента одного генератора. Расхождение ловится повторным `openspec init` / `openspec update`, а не ручной синхронизацией. ## 4. Решения, принятые без обсуждения - **2026-10-24. Скиллы `.agents/skills` коммитятся в репозиторий обвязки.** Обвязка сама ведёт спеки через OpenSpec, а dogfooding — единственный способ держать слой проверенным. Обоснование: если скиллы не закоммичены, первый же DSH-прогон в этом репозитории их не увидит. Цена ошибки: лишние 6 каталогов в диффе при обновлении openspec; лечится `openspec update` и отдельным коммитом. - **2026-10-24. `.openspec-target` (`agents`) коммитится вместе со скиллами.** Это маркер адаптера, который пишет генератор; без него `openspec update` не знает, какой адаптер обновлять. Цена ошибки: одна строка в диффе. - **2026-10-24. Тест `--openspec` подставляет фейковый бинарь `openspec` в PATH.** Тесты DSH-порта идут без внешних CLI (см. `dsh/tests/run.sh`); живой openspec в тесте сделал бы набор зависимым от машины. Цена ошибки: тест не проверяет настоящий генератор — это делает живая приёмка. - **2026-10-24. Проверка скиллов в `--check` смотрит репозиторий обвязки (`$ROOT`), а не cwd.** `install.sh` — скрипт установки обвязки, его `--check` отвечает за состояние обвязки; проект проверяется флагом `--openspec`. Цена ошибки: из чужого проекта `--check` покажет `WARN` о скиллах обвязки — сообщение говорит, что делать. ## 5. Риски - **Версия скиллов против CLI.** Скиллы генерирует CLI; если проект обновит `openspec` и не выполнит `openspec update`, скиллы отстанут от CLI. `--check` сравнивать версии не будет: он не знает, какой CLI у проекта. Признак — `metadata.generatedBy` во frontmatter. - **`.claude/commands/opsx` в DSH не работают** и остаются в репозитории для Claude Code. Это не мёртвый груз, а второй клиент; в PORT.md написано явно. - **DSH при дублях скиллов.** Не проверено и не проверяется, потому что глобальной копии нет (R-1). Если кто-то поставит `openspec init --tools agents` при уже скопированных вручную скиллах, поведение определит DSH. - **`openspec init` пишет в проект.** Флаг `--openspec` запускается только по явной команде и только при существующем `openspec/`; `adopt-project.sh` и `new-project.sh` его не вызывают. ## 6. Приёмка - `openspec validate dsh-openspec --strict` — change валиден. - `bash dsh/tests/run.sh` — зелёный, включая новый `openspec.test.mjs`. - Живая проверка DSH: сессия/сабагент с cwd в проекте после `openspec init --tools agents` видит шесть `openspec-*` в каталоге скиллов; полный цикл `new change → validate --strict → archive -y` проходит без Claude Code (спайк 24.10.2026, транскрипт — в отчёте задачи). - `/role` и bootstrap не изменились: тесты `role-skill.test.mjs`, `bootstrap.test.mjs` и `sdd-skill.test.mjs` зелёные. ### Приёмка: прогон 24.10.2026 - CLI: openspec 1.13.1, temp-проект `/tmp/dsh-openspec-accept`, ветка `feat/probe`. - `openspec init --tools agents`: шесть скиллов в `.agents/skills`; повторный прогон идемпотентен, файлы не задвоились. - `openspec new change probe` → `status` → `instructions proposal` → написаны `proposal.md` и дельта `specs/probe/spec.md`. - `openspec validate probe --strict` → `Change 'probe' is valid`. - `openspec archive probe -y` → `Specs updated successfully.` / `Change 'probe' archived as '2026-10-24-probe'.`, основная спека `openspec/specs/probe/spec.md` создана. - Видимость скиллов в DSH: сабагент с cwd `/tmp/dsh-openspec-accept` увидел в каталоге скиллов все шесть `openspec-*`; источник — проектный `.agents/skills/` этого проекта.