8.9 KiB
Raw Blame History

Design: dsh-prestep — harness-check и role-hint через agent/pre-step

Issue: #43 (+#62). Статус: черновик до builder-check; утверждение — /approve dsh-prestep design.

Контекст

CC-хуки harness-check/role-hint — SessionStart; в DSH аналог — инъекции agent/pre-step (уже используем для superpowers-bootstrap). Гейты #59 живые: этот change идёт первым по гейтованному циклу (acceptance #59).

Механика

Обе инъекции — раз за сессию, с маркером <!-- DSH-HARNESS-PRESTEP:<hash> --> и тем же механизмом видимости, что у bootstrap (isBootstrapVisible переименован в общий isInjectionVisible(agent, messages, marker)). Порядок в pre-step: другие инъекции → harness-check → role-hint (обе неблокирующие; reject/abort — пропуск; сабагенты — пропуск, как bootstrap).

harness-check

  • Сигнал подключения (проектные, без CC-артефактов): upward-walk от sessionCwd до корня ФС: ближайший каталог с .git (или без него до упора) — «корень проекта»; сигналы: openspec/, .codegraph/, секция ## OpenSpec и superpowers в CLAUDE.md. Всё на месте — молчим. Вне git-репозитория (.git не найден до корня) — пропуск. Чего-то нет — неблокирующая инъекция: «[dsh-harness] Проект не подключён к обвязке: нет: … . Предложи владельцу настройку: openspec init --tools agents, codegraph init и секцию «OpenSpec и superpowers» в CLAUDE.md.» (DSH-нативный текст: без ~/.claude-путей и adopt-скрипта CC — их порт под DSH — отдельная фича, D-9). HARNESS_AUTO_ADOPT — вне скоупа (D-2).
  • Реализация — fs-чтения (stat/readFile) по путям, без spawn git.

role-hint

  • Триггер: новое пользовательское сообщение текущего pre-step (decision.messages), source.kind === 'user', И текст не начинается с <!-- DSH-HARNESS- и не содержит [dsh-harness] (фильтр собственных инъекций — bootstrap/harness-check не считаются «первым сообщением»). Нет такого — инъекции нет (в т.ч. автономные раунды без нового сообщения).
  • Ключевые слова (точно как в AGENTS.md; builder-check нашёл опечатку — «грант» убран):
    • admin: jira, гант, спринт;
    • qa: стенд, прогон, скриншот, макет;
    • analyst: тз, oq, спека, методика;
    • dev: срез, issue, pr, тест, миграция. Совпадение — инъекция [dsh-harness] Роль сессии: <r>. Вызови skill role. Если не так — скажи. Нет — инъекция вопроса (текст сочинён здесь, в репо его не было): «Роль сессии неясна. Вызови skill role: dev — код, issue, PR, тест, миграция; admin — Jira, Гант, спринт; analyst — ТЗ, OQ, спека, методика; qa — стенд, прогон, скриншот, макет. Задай владельцу один вопрос с этими четырьмя вариантами.»
  • Матчинг: нижний регистр; pr — только отдельным словом; остальные — подстроки.

Decisions made without discussion

# Решение Обоснование Цена ошибки
D-1 Роль определяется по первому сообщению (не по заголовку — его в DSH нет) Ровно то правило AGENTS.md, которое модель сейчас исполняет «по памяти»; переносим в технику Ложная подсказка роли при неоднозначном сообщении — поправима (/role), не блокирует
D-2 harness-check — только подсказка, без HARNESS_AUTO_ADOPT YAGNI; авто-подключение — отдельная фича при запросе Сессия в неподключённом проекте продолжает работу — как и сейчас
D-3 Общий хелпер инъекций (рефакторинг bootstrap) без изменения поведения bootstrap Два новых pre-step не должны дублировать маркер/дедуп Регрессия bootstrap — страхуется существующими тестами bootstrap
D-4 #62-пункты едут задачами этого change (не отдельными фичами) Мелкие, один PR-цикл дешевле трёх Смешивание тем в одном PR — компенсируется отдельными задачами плана
D-5 Конфиг-ключи harnessCheck/roleHint — по умолчанию true; гейт — прямое typeof ctx.on === 'function' (не bootstrapOk: selfcheck пропускает запись bootstrap при config.bootstrap=false, а инъекциям нужен ctx.on независимо от bootstrap) Симметрия с bootstrap; корректность при выключенном bootstrap Нет
D-6 Маркер инъекций — <!-- DSH-HARNESS-PRESTEP:<hash> --> (формат bootstrap, не <--); isBootstrapVisible → общий isInjectionVisible (переименование трогает imports bootstrap.test.mjs — учтено в плане) Одна машинерия видимости на все инъекции Регрессия bootstrap — страхуется его тестами
D-7 Текст вопроса с четырьмя вариантами сочинён в этом дизайне (в репо его не было) Builder-check подтвердил отсутствие Нет
D-8 T3.4: в sdd-скилле предложения про TDD-порядок нет — текст добавляется с нуля (пин в тесте), а не «усиливается» Факт builder-check Нет
D-9 DSH-автономность (замечание владельца 25.10): в текстах и сигналах дизайна нет CC-артефактов — ни ~/.claude-путей, ни adopt-project.sh, ни .claude/code-paths; CC-хуки harness-check.sh/role-hint.sh живут только для CC-сессий; DSH-порт adopt-скрипта — отдельная фича (non-goal этого change) Порт должен жить самостоятельно; CC-сторона — параллельный мир Подключение проекта в DSH остаётся ручным до порта adopt-скрипта — инъекция даёт точный список шагов
D-10 Префикс DSH-инъекций — [dsh-harness] (не [claude-harness]); фильтр собственных сообщений — по нему и по <!-- DSH-HARNESS- Имя бандла — dsh-harness; старый префикс — след CC-происхождения Установленный AGENTS.md пока содержит [claude-harness] — фильтр учитывает оба префикса до обновления install.sh

Тесты (идеи для плана)

  • pure: detectAdoption(files), detectRole(firstUserText) → role|null, buildRoleHintText(role), buildHarnessCheckText(), маркер-дедуп;
  • wiring: apply с fake ctx — инъекции появляются при наличии пользовательского сообщения, один раз, пропускаются для сабагентов;
  • T2.1: негативный тест openspec — PATH-изоляция (временный каталог без openspec-бинарника);
  • O-2: install.sh WARN при DSH_HOME→существующий каталог + нестандартный HOME (тест в install.test.mjs);
  • T3.4: пин текста в sdd-скилле («шаг „тест падает“ обязателен»).

На потом (в план)

  • Разбивка: Task 1 prestep.js (harness-check+role-hint pure) → Task 2 wiring+bootstrap-рефакторинг → Task 3 #62 (T2.1/O-2/T3.4) → Task 4 спека-дельта session-prestep + доки.