diff --git a/openspec/changes/dsh-openspec/design.md b/openspec/changes/dsh-openspec/design.md new file mode 100644 index 0000000..1a11d1c --- /dev/null +++ b/openspec/changes/dsh-openspec/design.md @@ -0,0 +1,137 @@ +# 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-сессии проверяет контроллер отдельным сабагентом; + результат дописывается сюда после прогона.