diff --git a/openspec/changes/dsh-port-v2/.openspec.yaml b/openspec/changes/archive/2026-10-25-dsh-port-v2/.openspec.yaml
similarity index 100%
rename from openspec/changes/dsh-port-v2/.openspec.yaml
rename to openspec/changes/archive/2026-10-25-dsh-port-v2/.openspec.yaml
diff --git a/openspec/changes/dsh-port-v2/design.md b/openspec/changes/archive/2026-10-25-dsh-port-v2/design.md
similarity index 100%
rename from openspec/changes/dsh-port-v2/design.md
rename to openspec/changes/archive/2026-10-25-dsh-port-v2/design.md
diff --git a/openspec/changes/dsh-port-v2/proposal.md b/openspec/changes/archive/2026-10-25-dsh-port-v2/proposal.md
similarity index 100%
rename from openspec/changes/dsh-port-v2/proposal.md
rename to openspec/changes/archive/2026-10-25-dsh-port-v2/proposal.md
diff --git a/openspec/changes/dsh-port-v2/specs/dsh-install/spec.md b/openspec/changes/archive/2026-10-25-dsh-port-v2/specs/dsh-install/spec.md
similarity index 100%
rename from openspec/changes/dsh-port-v2/specs/dsh-install/spec.md
rename to openspec/changes/archive/2026-10-25-dsh-port-v2/specs/dsh-install/spec.md
diff --git a/openspec/specs/dsh-install/spec.md b/openspec/specs/dsh-install/spec.md
index d27e6b5..52982de 100644
--- a/openspec/specs/dsh-install/spec.md
+++ b/openspec/specs/dsh-install/spec.md
@@ -13,25 +13,40 @@
### Requirement: Источник superpowers задаётся при установке
`dsh/install.sh --skills` SHALL разрешать источник superpowers-скиллов в
-порядке приоритета и MUST NOT исполнять `git clone` и любые сетевые операции.
+порядке приоритета. Сеть нужна только ветке пина; явный источник
+синхронизируется локально.
- Порядок: `--superpowers-from
` → `$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 запрещён.
+ клон `$DSH_HOME/superpowers` на `$DSH_SUPERPOWERS_REF` →
+ legacy-кэш Claude Code → legacy `~/.claude/skills`.
+- Явный источник (`--superpowers-from`, `$DSH_SUPERPOWERS_SOURCE`) MUST NOT
+ требовать сети и MUST NOT проверять пин. Допускаются две формы: корень
+ checkout (есть `skills/using-superpowers/SKILL.md`) или каталог `skills`
+ такого корня. Синк явного источника MUST идти в staging вне каталога клона
+ (`$DSH_HOME/.skills.synced`), потому что `$DSH_HOME/superpowers` зарезервирован
+ под клон пина.
+- Каталог скиллов, который не является ни checkout-корнем, ни его `skills/`,
+ MUST быть `FAIL` (код 1) и в `--skills`, и в `--check`: вердикты двух команд
+ MUST совпадать. Тихий откат на legacy запрещён.
+- Клон-пин: отсутствующий или пустой каталог MUST клонироваться
+ `git clone --depth 1 --branch [ `; неудачный клон MUST
+ удалять недоклонированный каталог (полусостояние не остаётся).
+- Существующий клон MUST обновляться fetch'ем от `$DSH_SUPERPOWERS_REPO` (не от
+ origin клона) с force-рефспеком тега
+ (`+refs/tags/][:refs/tags/][`) и checkout `FETCH_HEAD`: смена
+ репозитория и переехавший тег MUST подхватываться следующим прогоном.
+ Офлайн клон MAY переиспользоваться, только если HEAD уже ровно на пине, и
+ тогда MUST печататься `WARN`; иначе — `FAIL` с командой клона.
+- Непустой каталог на месте клона без рабочего `.git` MUST быть `FAIL` (код 1)
+ без изменения каталога.
- Копирование MUST быть заменой каталога назначения (`rm -rf` + `cp -R`), а не
симлинком: `~/.dsh/skills` не должен зависеть от каталога-источника.
#### Scenario: Явный источник без ~/.claude
- **WHEN** `HOME` — пустой temp-каталог без `~/.claude`, а `--superpowers-from`
указывает на каталог superpowers
-- **THEN** `--skills` копирует скиллы в `$DSH_HOME/skills`, печатает источник и
- завершается кодом 0
+- **THEN** `--skills` синхронизирует и копирует скиллы в `$DSH_HOME/skills`,
+ печатает источник и завершается кодом 0, не обращаясь в сеть
#### Scenario: Переменная окружения вместо флага
- **WHEN** задан `DSH_SUPERPOWERS_SOURCE=` и флаг `--superpowers-from` не передан
@@ -47,51 +62,81 @@
- **THEN** `--skills` печатает `FAIL` с ожидаемой структурой и завершается
кодом 1, ничего не копируя из legacy-источников
+#### Scenario: Голый каталог скиллов
+- **WHEN** явный источник — каталог `my-skills` с
+ `using-superpowers/SKILL.md`, но без checkout-корня
+- **THEN** и `--skills`, и `--check` печатают `FAIL: explicit superpowers source
+ must be a checkout root or its skills/ directory` и завершаются кодом 1
+
#### Scenario: Пиннинг в ~/.dsh
-- **WHEN** существует `$DSH_HOME/superpowers/skills/using-superpowers/SKILL.md`
- и флаг/переменная не заданы
-- **THEN** источником служит этот каталог, `--check` печатает
- `ok: superpowers source ()`
+- **WHEN** существует клон `$DSH_HOME/superpowers` с
+ `skills/using-superpowers/SKILL.md` и флаг/переменная не заданы
+- **THEN** источником служит этот клон, а `--check` офлайн печатает
+ `ok: superpowers source ()` и ref из `.sync-meta.json`
+
+#### Scenario: Обновление клона от DSH_SUPERPOWERS_REPO
+- **WHEN** клон уже есть, а `DSH_SUPERPOWERS_REPO` сменился или тег переехал
+- **THEN** прогон фетчит от `DSH_SUPERPOWERS_REPO` с force-рефспеком, делает
+ checkout `FETCH_HEAD` и устанавливает текст новой версии тега
+
+#### Scenario: Офлайн-переиспользование клона
+- **WHEN** клон существует, HEAD ровно на пине, сеть недоступна
+- **THEN** `--skills` печатает `WARN: offline; reusing the existing clone` и
+ завершается кодом 0
+
+#### Scenario: Каталог без .git на месте клона
+- **WHEN** `$DSH_HOME/superpowers` непустой и не является git-клоном
+- **THEN** `--skills` завершается `FAIL` (код 1), каталог не изменяется
### Requirement: Отсутствие источника не бывает тихим
-Если источник superpowers не найден, `--skills` MUST различать первичную и
-повторную установку и MUST NOT завершаться успехом молча.
+Если источник superpowers недоступен, `--skills` MUST завершаться `FAIL`, кроме
+случая, когда существует legacy-источник.
-- Если `$DSH_HOME/skills/using-superpowers/SKILL.md` уже существует —
- `WARN` «источник не найден, копии сохранены» и код 0 (идемпотентный повтор).
-- Если копий нет — `FAIL` с готовой командой получения источника
- (`git clone --depth 1 https://github.com/obra/superpowers ~/.dsh/superpowers`)
- и код возврата 1.
+- Офлайн, без явного источника, без клона на пине и без legacy MUST печататься
+ `FAIL: no superpowers source` с готовой командой клона
+ (`git clone --depth 1 --branch ][ /superpowers`) и код
+ возврата 1. Тихий успех запрещён даже при уже установленных копиях.
+- Уже установленные копии в `$DSH_HOME/skills` MUST NOT удаляться или
+ портиться при таком `FAIL`.
+- Если legacy-источник существует — скиллы копируются сырым текстом с
+ `WARN: legacy superpowers source` и кодом 0 (путь живёт до #66).
- Локальные скиллы обвязки (`dsh/skills/*`: `role`, `sdd`) MUST ставиться в
- обоих случаях: они не зависят от superpowers.
+ любом исходе: они не зависят от superpowers.
#### Scenario: Первая установка без источника
-- **WHEN** `HOME` пуст, `$DSH_HOME/skills` не существует, источника нет
-- **THEN** `--skills` печатает `FAIL`, код 1, но `$DSH_HOME/skills/role/SKILL.md`
- и `$DSH_HOME/skills/sdd/SKILL.md` на месте
+- **WHEN** `HOME` пуст, `$DSH_HOME/skills` не существует, клон получить не
+ удалось, legacy нет
+- **THEN** `--skills` печатает `FAIL: no superpowers source` и команду клона,
+ код 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, установленные скиллы не удалены
+- **WHEN** `$DSH_HOME/skills/using-superpowers/SKILL.md` уже есть, а клон
+ получить не удалось
+- **THEN** `--skills` печатает `FAIL: no superpowers source`, код 1,
+ установленный файл не изменён (раньше здесь был `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
+- Установка: legacy-кэш Claude Code и `~/.claude/skills` MAY быть источником
+ только после того, как клон пина получить не удалось; копирование в этом
+ случае — сырое (без sync и verify), с
+ `WARN: legacy superpowers source` и подсказкой перейти на пин/явный источник.
+- `--check`: legacy-источник — `WARN`, явный/пин — `ok`, отсутствие — `WARN` с
+ подсказкой клона, невалидный явный источник — `FAIL` и код 1
(пользователь задал его сам, подсказка «нет источника» здесь неверна).
- Runtime: bootstrap и резолвер ролей сохраняют порядок `~/.dsh` → `~/.claude`,
но при выборе legacy-пути один раз за процесс пишут в лог предупреждение.
-- Удаление fallback'ов — отдельное изменение; в этом change они не удаляются.
+- Удаление fallback'ов — отдельное изменение (#66); в этом change они не
+ удаляются.
#### Scenario: Legacy-кэш как источник установки
- **WHEN** в `HOME` есть только кэш Claude Code с superpowers, `$DSH_HOME` пуст
-- **THEN** `--skills` копирует скиллы и печатает `WARN: legacy superpowers source`
+- **THEN** `--skills` копирует скиллы и печатает `WARN: legacy superpowers
+ source`
#### Scenario: Runtime-предупреждение
- **WHEN** bootstrap резолвит `using-superpowers` из `~/.claude/...`
@@ -147,3 +192,96 @@ Claude Code.
- **THEN** выбранный путь — `$HOME/.dsh/skills/using-superpowers/SKILL.md`,
текст содержит маркер ``, предупреждений о
legacy нет
+
+### Requirement: Sync по пину
+`dsh/install.sh --skills` без явного источника MUST получить superpowers из
+клона `$DSH_HOME/superpowers` на пине `$DSH_SUPERPOWERS_REF` (default
+`v7.0.1`) из `$DSH_SUPERPOWERS_REPO` (default
+`https://github.com/obra/superpowers`) и MUST прогнать sync
+(`dsh/sync/sync-from-upstream.mjs`) и verify (`dsh/sync/verify-skills.mjs`) до
+копирования в `$DSH_HOME/skills`.
+
+- Копирование MUST брать только synced-дерево; сырой текст апстрима при пине
+ MUST NOT попадать в `$DSH_HOME/skills`. Адаптации: `superpowers:`-префиксы
+ сняты, ссылки ведут на `using-superpowers/references/dsh-tools.md` из
+ `dsh/bundle/`, чужие harness-референсы не копируются.
+- Sync MUST завершаться `FAIL` (код 1) на несовпадении якоря transform-а
+ (ноль или больше одного совпадения), на изменении набора скиллов и на
+ отсутствии `skills/` в checkout. При любом падении назначение (`--out`)
+ MUST остаться прежним, а staging-каталог MUST быть удалён.
+- Verify MUST проверять YAML frontmatter (`name` и `description` непусты, без
+ дублей ключей), отсутствие остаточных `superpowers:`-префиксов в телах
+ `SKILL.md` и резолв всех относительных ссылок внутри synced-дерева. Дерево,
+ прошедшее verify с нулём скиллов (`verified 0 skills`), MUST трактоваться
+ как `FAIL`: пустое дерево не устанавливается.
+- В `/.sync-meta.json` MUST записываться `ref` (значение `--ref`) и
+ `version` (версия из `.claude-plugin/plugin.json` апстрима); `--check` MUST
+ печатать ref и version из этого файла, не обращаясь в сеть.
+- Повторный `--skills` MUST быть идемпотентным: каталоги `*.tmp-*` не
+ остаются, установленные копии заменяются целиком (`rm -rf` + `cp -R`), а не
+ дополняются.
+
+#### Scenario: Свежий HOME, установка по пину
+- **WHEN** `HOME` пуст, флаг и `DSH_SUPERPOWERS_SOURCE` не заданы, а
+ `DSH_SUPERPOWERS_REPO` указывает на репозиторий с тегом `v7.0.1`
+- **THEN** `--skills` клонирует пин, печатает `synced N files` и
+ `verified 15 skills`, копирует адаптированные скиллы; в
+ `using-superpowers/SKILL.md` есть `references/dsh-tools.md` и нет
+ `superpowers:`, а `references/dsh-tools.md` совпадает с
+ `dsh/bundle/dsh-tools.md`
+
+#### Scenario: Сломанный якорь апстрима
+- **WHEN** checkout на пине не содержит якоря одного из transform-ов
+- **THEN** sync печатает `FAIL` с именем transform-а и завершается кодом 1,
+ прежнее `--out` и `$DSH_HOME/skills` не изменены
+
+#### Scenario: Изменился набор скиллов
+- **WHEN** в checkout на пине появился или исчез каталог скилла относительно
+ ожидаемого списка
+- **THEN** sync печатает `added upstream:` / `removed upstream:` и завершается
+ кодом 1; установка ничего не копирует
+
+#### Scenario: Verify нашёл висячую ссылку
+- **WHEN** synced-дерево содержит ссылку на несуществующий файл (markdown или
+ inline-код)
+- **THEN** verify печатает `/SKILL.md: dangling relative reference: …` и
+ завершается кодом 1; в `$DSH_HOME/skills` ничего не копируется
+
+#### Scenario: Нулевое дерево
+- **WHEN** verify завершился кодом 0, но сообщил `verified 0 skills`
+- **THEN** установка завершается `FAIL`, ни один скилл не скопирован
+
+#### Scenario: Повторный прогон
+- **WHEN** `--skills` запущен второй раз на том же пине
+- **THEN** установленные скиллы заменены, в `$DSH_HOME/superpowers` нет
+ каталогов `*.tmp-*`, а `.sync-meta.json.ref` равен пину
+
+### Requirement: Самопроверка контракта бандла
+Бандл `dsh-harness` MUST при активации проверять контракт DSH, на который
+опирается, и MUST предоставлять команду `dsh-harness-check` для повторного
+прогона той же проверки.
+
+- Проверка MUST покрывать `ctx.tools.guard`, `ctx.on` для `agent/pre-step`,
+ `ctx.inject` (регистрация команд) и `workingDirectory.get`. Каждая
+ отсутствующая служба MUST логироваться строкой
+ `[dsh-harness] self-check: not-ok: `.
+- Отсутствие службы MUST отключать только свою фичу: без `ctx.tools.guard`
+ guard-обвязка пропускается, bootstrap и `/role` продолжают работать при
+ своих службах. Активация бандла MUST NOT падать целиком из-за несовпадения
+ контракта.
+- Команда `dsh-harness-check` MUST возвращать текстовый отчёт с одной строкой
+ `ok`/`not-ok` на каждую проверяемую фичу.
+- Ключи конфига `selfCheck` и `checkCommand` (default `true`) MUST позволять
+ выключить активационную проверку и команду соответственно.
+- Терминальный `--check` MUST оставаться офлайн-проверкой файлов и окружения:
+ SDK внутри `app.asar` ему недоступен, контракт проверяется только изнутри
+ DSH.
+
+#### Scenario: Сломанный контракт при активации
+- **WHEN** бандл активируется в `ctx` без `tools.guard`
+- **THEN** в лог попадает `self-check: guards not-ok: tools.guard`, guard-wiring
+ пропускается, bootstrap и команды продолжают работать, бандл не падает
+
+#### Scenario: Проверка по требованию
+- **WHEN** пользователь вызывает `/dsh-harness-check`
+- **THEN** отчёт перечисляет каждую фичу со статусом `ok` или `not-ok`
]