13 KiB
Raw Blame History

Design: установка DSH без зависимости от ~/.claude

Issue: #49. Спека: openspec/changes/dsh-install-source/specs/dsh-install/spec.md. Исходные факты: dsh/PORT.md §3/§5/§7, issue #37/#38 (runtime-отвязка уже сделана), ответ владельца 24.10.2026 («нормально так» — без вендоринга).

1. Что уже сделано и чего не хватает

Слой Источник в runtime Зависимость от ~/.claude
bootstrap using-superpowers ~/.dsh/skills → ~/.agents/skills → кэш CC → ~/.claude/skills только fallback
роли /role, skill role ~/.dsh/roles → ~/.claude/roles → repo roles/ только fallback
установка --skills кэш плагина CC жёсткая
установка --roles repo roles/ нет
установленные инструкции global/CLAUDE.md + adapter-шапка текст путей ролей

То есть #37 снял зависимость в том, что исполняется, и оставил её в том, что устанавливается. Первая же команда --skills на чистой машине не находит источник, печатает WARN и оставляет bootstrap без скилла — при этом установка формально успешна.

2. Решения

D-1 (решение владельца). Без вендоринга superpowers

Копию апстрима в репозиторий не кладём. Обоснование: superpowers живёт своей версией (в кэше уже 6.4.1 и 7.0.0), репозиторий обвязки не должен превращаться в зеркало; обновление вендорной копии — ручная синхронизация, которая стареет молча. Цена: install-time источник нужно получить один раз (клон), это лишний шаг в quick start.

D-2. Три уровня источника, один формат

Порядок: --superpowers-from → DSH_SUPERPOWERS_SOURCE → $DSH_HOME/superpowers/skills → legacy-кэш CC → legacy ~/.claude/skills.

  • Флаг и переменная принимают оба каталога: корень superpowers (с skills/) и сам skills/. Клон git clone … ~/.dsh/superpowers даёт первый вид, кэш CC — второй; пользователь не должен помнить, какой именно.
  • $DSH_HOME/superpowers — рекомендуемое место пиннинга: один клон на машину, дальше --skills воспроизводим без флага и без ~/.claude.
  • Явно заданный невалидный источник — FAIL, а не fallback: иначе опечатка в пути тихо даёт установку из legacy-кэша, и на чистой машине это снова «установка прошла, bootstrap пуст».

D-3. FAIL только когда ставить нечего

--skills без источника:

  • $DSH_HOME/skills/using-superpowers/SKILL.md есть → WARN + код 0 (идемпотентный повтор, старые копии не трогаем);
  • нет → FAIL + код 1 + готовая команда клона.

Локальные скиллы (role, sdd) ставятся в обоих случаях: они из репозитория и от superpowers не зависят. Из-за этого --skills может вернуть 1, уже записав часть файлов — это сказано в FAIL-сообщении.

D-4. Legacy-пути остаются, но говорят о себе

~/.claude поддерживается как fallback: машины, где --skills --roles ещё не перезапускали, не должны сломаться на этом change. Предупреждения:

  • --skills из legacy-источника → WARN: legacy superpowers source;
  • --check → WARN для legacy и для «источника нет»;
  • bootstrap/resolveRolesDir при выборе ~/.claude → одно предупреждение за процесс (module-level Set), не на каждый agent/pre-step.

Удаление fallback'ов — отдельная задача: сначала все машины должны переехать на ~/.dsh (и это видно по WARN), потом код можно чистить. Цена: путь ~/.claude остаётся в bundle, и code review видит его ещё один цикл.

D-5. Инструкции переписываются при установке, не в источнике

global/CLAUDE.md — общий с Claude Code, его правит только CC-контур. Поэтому install_global пишет AGENTS.md в два шага: adapter-шапка (как сейчас, плюс явная строка про формат инструкций) и тело global/CLAUDE.md, прогнанное через замену ~/.claude/roles → ~/.dsh/roles.

Замена точечная и проверяемая тестом: после --global в AGENTS.md есть ~/.dsh/roles/dev.md и нет ~/.claude/roles; сам global/CLAUDE.md не меняется (тест сравнивает содержимое до/после). Цена: расхождение между источником и установленной копией теперь intentional, и его надо помнить при правках CC-текста.

D-6. --check не падает без источника

--check — диагностика, а не установка: отсутствие superpowers-источника остаётся WARN, код возврата меняют только жёсткие FAIL (python3, git, node, хуки, бандл). Так же уже сделано для openspec (#42, R-4). Исключение — невалидный явный источник (--superpowers-from/DSH_SUPERPOWERS_SOURCE): это ошибка конфигурации пользователя, --check даёт FAIL, иначе опечатка выглядит как «источника нет».

3. Формулировки, которые попадут в установленный AGENTS.md

  • DSH adapter: role files are ~/.dsh/roles (installed by --roles); legacy ~/.claude/roles is a migration fallback.
  • DSH adapter: superpowers skills are ~/.dsh/skills (installed by --skills from --superpowers-from, DSH_SUPERPOWERS_SOURCE, or ~/.dsh/superpowers); the ~/.claude cache is a legacy fallback.
  • DSH adapter: a project CLAUDE.md/AGENTS.md is the instruction-file format of this harness; it does not imply a Claude Code runtime.
  • Тело global/CLAUDE.md — с заменой ~/.claude/roles → ~/.dsh/roles; строка про role-hint остаётся, adapter-шапка говорит, что хука в DSH нет и роль выводится из первого сообщения.

4. Решения, принятые без обсуждения

  • 2026-10-24. Клон для пиннинга — git clone --depth 1 https://github.com/obra/superpowers ~/.dsh/superpowers. Это апстрим superpowers (public repo). install.sh его не запускает: сеть в установке запрещена (D-1/спека). Цена ошибки: неверный URL — баг документации, ловится первой же установкой на чистой машине; лечится одной строкой в README/PORT.
  • 2026-10-24. --superpowers-from принимает и корень, и skills/. Обоснование: два реальных источника дают разные формы (клон и кэш CC). Цена: две ветки разбора и два теста; ошибка разбора видна сразу по FAIL.
  • 2026-10-24. Предупреждение о legacy в runtime — один раз за процесс. agent/pre-step вызывается на каждом шаге; предупреждение на каждый шаг утопило бы лог. Цена: после перезапуска процесса предупреждение повторится — это ожидаемо; диагностика — --check.
  • 2026-10-24. --check проверяет у пиннинга только наличие using-superpowers. Полная инвентаризация набора — работа --skills (он копирует всё, что есть). Цена: неполный клон пройдёт --check и даст частичный набор скиллов; признак — отсутствие ожидаемого имени в каталоге скиллов сессии.
  • 2026-10-24. tasks.md OpenSpec не заполняется. Задачи живут в плане superpowers, как требует CLAUDE.md репозитория. Цена: openspec status показывает незаполненный tasks.md; archive это не блокирует (уже так в dsh-openspec).

5. Риски

  • set -euo pipefail против «частичного успеха». install_skills при FAIL возвращает 1; в --skills он вызывается через || status=1, иначе install_local_skills не выполнится. Тест на «первую установку без источника» проверяет оба файла и код возврата.
  • Legacy-fallback маскирует неправильную настройку. Лечится WARN в установке, --check и runtime-предупреждением; полное удаление — D-4.
  • Замена ~/.claude/roles → ~/.dsh/roles в тексте. Заменяем только этот префикс, а не ~/.claude целиком: в тексте есть упоминания хуков/CC, которые DSH-версии не касаются. Тест проверяет и наличие нового пути, и отсутствие старого.
  • Проверка «чистый HOME» — юнит-уровень. Реальный fresh-session smoke в DSH делает владелец: сессия должна подняться без ~/.claude и показать superpowers-скиллы. В PR это отмечено как единственный ручной шаг приёмки.
  • Апстрим superpowers может переехать. Явный источник проверяется по using-superpowers/SKILL.md; если апстрим переименует каталог, FAIL скажет, что искать.

6. Приёмка

  • openspec validate dsh-install-source --strict — change валиден.
  • bash dsh/tests/run.sh — зелёный, включая новые тесты источника, AGENTS.md и legacy-предупреждений.
  • bash tests/run.sh — регрессий в CC-контуре нет.
  • Temp HOME без ~/.claude: --global --skills --roles с --superpowers-from; --check; prepareBootstrap({home}) — маркер на месте.
  • Живой fresh-session smoke без ~/.claude — за владельцем (в PR отмечено).

Приёмка: прогон 24.10.2026

  • openspec validate dsh-install-source --strict → Change 'dsh-install-source' is valid.
  • bash dsh/tests/run.sh → 61/61 pass (до изменения — 57).
  • Temp HOME без ~/.claude: копия реальных superpowers 7.0.0 в <tmp>/superpowers, --global --skills --roles --superpowers-from → 15 superpowers-скиллов, 2 локальных (role, sdd), 4 роли; $HOME/.claude не создан; prepareBootstrap({ home }) выбрал ~/.dsh/skills/using-superpowers/SKILL.md, маркер <!-- DSH-HARNESS-BOOTSTRAP:412d797495fc8765 -->; --check — ok: dsh skills, ok: role skill, ok: roles, WARN: no superpowers source (источник был временным каталогом, копии на месте).
  • Машина владельца: --check — WARN: legacy superpowers source (~/.claude/plugins/cache/.../7.0.0/skills), ok: dsh skills, ok: roles. В установленном ~/.dsh/AGENTS.md ещё три пути ~/.claude/roles — нужен повторный --global (ручной шаг, отмечен в PR).
  • Fresh-session smoke без ~/.claude — за владельцем: юнит-уровень (prepareBootstrap с temp HOME) пройден, реальную сессию DSH поднимает владелец.