chore(openspec): архив change dsh-install-source (#49) #65

Merged
aep merged 1 commits from chore/archive-dsh-install-source into main 2026-10-24 20:24:56 +03:00
5 changed files with 149 additions and 0 deletions

View File

@ -0,0 +1,149 @@
# dsh-install Specification
## Purpose
Слой установки DSH-обвязки: откуда берутся superpowers-скиллы и роли, что
делает `dsh/install.sh --skills/--roles`, какие пути попадают в установленные
инструкции. В Claude Code источником служит кэш плагина в `~/.claude`; в DSH
источник задаётся установщиком явно (аргумент, переменная или пиннинг в
`~/.dsh`), а `~/.claude` остаётся только legacy-fallback'ом с предупреждением.
Контракт нужен, чтобы свежая установка на машине без Claude Code проходила
полный цикл #48 и чтобы путь `~/.claude` не возвращался в runtime незаметно.
## Requirements
### Requirement: Источник superpowers задаётся при установке
`dsh/install.sh --skills` SHALL разрешать источник superpowers-скиллов в
порядке приоритета и MUST NOT исполнять `git clone` и любые сетевые операции.
- Порядок: `--superpowers-from <dir>` → `$DSH_SUPERPOWERS_SOURCE` →
`$DSH_HOME/superpowers/skills` (пиннинг; допускается и корень
`$DSH_HOME/superpowers` с подкаталогом `skills/`) → legacy-кэш Claude Code →
legacy `~/.claude/skills`.
- Для явного источника (`--superpowers-from`, `$DSH_SUPERPOWERS_SOURCE`)
допускаются обе формы: корень superpowers (есть `skills/using-superpowers/SKILL.md`)
или сам каталог `skills`.
- Явно заданный, но невалидный источник MUST приводить к `FAIL` и коду
возврата 1: тихий откат на legacy запрещён.
- Копирование MUST быть заменой каталога назначения (`rm -rf` + `cp -R`), а не
симлинком: `~/.dsh/skills` не должен зависеть от каталога-источника.
#### Scenario: Явный источник без ~/.claude
- **WHEN** `HOME` — пустой temp-каталог без `~/.claude`, а `--superpowers-from`
указывает на каталог superpowers
- **THEN** `--skills` копирует скиллы в `$DSH_HOME/skills`, печатает источник и
завершается кодом 0
#### Scenario: Переменная окружения вместо флага
- **WHEN** задан `DSH_SUPERPOWERS_SOURCE=<dir>` и флаг `--superpowers-from` не передан
- **THEN** источник берётся из переменной; поведение совпадает с флагом
#### Scenario: Флаг важнее переменной
- **WHEN** заданы и `--superpowers-from`, и `DSH_SUPERPOWERS_SOURCE`
- **THEN** используется каталог из флага
#### Scenario: Невалидный явный источник
- **WHEN** `--superpowers-from` указывает на каталог без
`using-superpowers/SKILL.md`
- **THEN** `--skills` печатает `FAIL` с ожидаемой структурой и завершается
кодом 1, ничего не копируя из legacy-источников
#### Scenario: Пиннинг в ~/.dsh
- **WHEN** существует `$DSH_HOME/superpowers/skills/using-superpowers/SKILL.md`
и флаг/переменная не заданы
- **THEN** источником служит этот каталог, `--check` печатает
`ok: superpowers source (<path>)`
### Requirement: Отсутствие источника не бывает тихим
Если источник superpowers не найден, `--skills` MUST различать первичную и
повторную установку и MUST NOT завершаться успехом молча.
- Если `$DSH_HOME/skills/using-superpowers/SKILL.md` уже существует —
`WARN` «источник не найден, копии сохранены» и код 0 (идемпотентный повтор).
- Если копий нет — `FAIL` с готовой командой получения источника
(`git clone --depth 1 https://github.com/obra/superpowers ~/.dsh/superpowers`)
и код возврата 1.
- Локальные скиллы обвязки (`dsh/skills/*`: `role`, `sdd`) MUST ставиться в
обоих случаях: они не зависят от superpowers.
#### Scenario: Первая установка без источника
- **WHEN** `HOME` пуст, `$DSH_HOME/skills` не существует, источника нет
- **THEN** `--skills` печатает `FAIL`, код 1, но `$DSH_HOME/skills/role/SKILL.md`
и `$DSH_HOME/skills/sdd/SKILL.md` на месте
#### Scenario: Повторная установка без источника
- **WHEN** `$DSH_HOME/skills/using-superpowers/SKILL.md` уже есть, источника нет
- **THEN** `--skills` печатает `WARN`, код 0, установленные скиллы не удалены
### Requirement: ~/.claude — только legacy-fallback с предупреждением
superpowers-скиллы и роли MUST иметь рабочий путь `~/.dsh`; пути `~/.claude`
SHALL оставаться fallback'ом для машин, где `--skills --roles` ещё не
перезапускали, и MUST сопровождаться предупреждением.
- Установка: legacy-кэш Claude Code и `~/.claude/skills` допустимы как
источник, но `--skills` печатает `WARN: legacy superpowers source` с
подсказкой перейти на пиннинг/флаг.
- `--check`: legacy-источник — `WARN`, явный/пиннинг — `ok`, отсутствие —
`WARN` с подсказкой, невалидный явный источник — `FAIL` и код 1
(пользователь задал его сам, подсказка «нет источника» здесь неверна).
- Runtime: bootstrap и резолвер ролей сохраняют порядок `~/.dsh` → `~/.claude`,
но при выборе legacy-пути один раз за процесс пишут в лог предупреждение.
- Удаление fallback'ов — отдельное изменение; в этом change они не удаляются.
#### Scenario: Legacy-кэш как источник установки
- **WHEN** в `HOME` есть только кэш Claude Code с superpowers, `$DSH_HOME` пуст
- **THEN** `--skills` копирует скиллы и печатает `WARN: legacy superpowers source`
#### Scenario: Runtime-предупреждение
- **WHEN** bootstrap резолвит `using-superpowers` из `~/.claude/...`
- **THEN** в лог один раз за процесс попадает предупреждение с предложением
выполнить `dsh/install.sh --skills`
#### Scenario: ~/.dsh приоритетнее
- **WHEN** существуют и `~/.dsh/skills/using-superpowers/SKILL.md`, и legacy-копия
- **THEN** используется `~/.dsh`, предупреждения нет
### Requirement: Установленные инструкции DSH не ссылаются на ~/.claude
`dsh/install.sh --global` MUST писать `$DSH_HOME/AGENTS.md` так, чтобы пути
ролей указывали на DSH-слой, а формат инструкций не выдавался за runtime
Claude Code.
- Строки `~/.claude/roles` в установленной копии MUST заменяться на
`~/.dsh/roles`, а adapter-шапка MUST NOT называть пути `~/.claude`: в
установленном файле не остаётся ни одного такого пути. `global/CLAUDE.md` в
репозитории MUST NOT меняться (его читает Claude Code).
- Шапка установленного файла MUST говорить, что проектный `CLAUDE.md` /
`AGENTS.md` — это формат файлов инструкций DSH, а не запущенный Claude Code,
и что роли лежат в `~/.dsh/roles` (`--roles`), а superpowers-скиллы — в
`~/.dsh/skills` (`--skills`).
- Повторный `--global` обязан делать бэкап прежнего `AGENTS.md` (как сейчас).
#### Scenario: Пути ролей после установки
- **WHEN** выполнен `--global` во временный `DSH_HOME`
- **THEN** в `AGENTS.md` есть `~/.dsh/roles/dev.md` и нет ни одного пути
`~/.claude`
#### Scenario: global/CLAUDE.md не тронут
- **WHEN** выполнен `--global`
- **THEN** `global/CLAUDE.md` в репозитории побайтно не изменился
### Requirement: Чистый HOME без ~/.claude проходит установку
Установка, bootstrap и skill `role` MUST работать на машине, где Claude Code
никогда не устанавливался.
- `--global --skills --roles` с явным источником superpowers в пустом `HOME`
MUST создавать `$DSH_HOME/{AGENTS.md,skills/using-superpowers/SKILL.md,skills/role/SKILL.md,skills/sdd/SKILL.md,roles/dev.md}`.
- bootstrap MUST резолвить скилл из `$DSH_HOME/skills` при `home` этого HOME и
MUST NOT обращаться к `~/.claude`.
- Резолвер ролей MUST находить `$DSH_HOME/roles` при `home` этого HOME.
#### Scenario: Установка в чистый HOME
- **WHEN** `HOME` — пустой temp-каталог, `DSH_HOME=$HOME/.dsh`,
`--superpowers-from` указывает на каталог superpowers
- **THEN** `--global --skills --roles` завершается кодом 0, все перечисленные
файлы на месте, `--check` зелёный по skills и roles
#### Scenario: Bootstrap в чистом HOME
- **WHEN** `prepareBootstrap({ home: <temp HOME> })` вызывается после установки
- **THEN** выбранный путь — `$HOME/.dsh/skills/using-superpowers/SKILL.md`,
текст содержит маркер `<!-- DSH-HARNESS-BOOTSTRAP:... -->`, предупреждений о
legacy нет