13 KiB
Design: dsh-prestep — harness-check и role-hint через agent/pre-step
Issue: #43 (+#62). Статус: утверждён владельцем 25.10.2026
(/approve dsh-prestep design); реализация — ветка feat/43-dsh-prestep,
статусы решений — в конце файла. Спека-дельта —
openspec/changes/dsh-prestep/specs/session-prestep/spec.md.
Контекст
CC-хуки harness-check/role-hint — SessionStart; в DSH аналог — инъекции
agent/pre-step (уже используем для superpowers-bootstrap). Гейты #59
живые: этот change идёт первым по гейтованному циклу (acceptance #59).
Механика
Каждая инъекция — по содержимому и маркеру (per-text dedupe); повторная
инъекция с другим текстом возможна — см. D-11. Маркер
<!-- 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 — стенд, прогон, скриншот, макет. Задай владельцу один вопрос с этими четырьмя вариантами.»
- admin:
- Матчинг: нижний регистр;
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 |
| D-11 | Дедуп инъекций — по содержимому (маркер = хэш текста), а не «ровно раз за сессию»: role-hint на более позднем шаге с бесключевым первым сообщением может добавить вопрос с четырьмя вариантами после уже выданной подсказки роли | Это фактическое поведение общей машинерии bootstrap (маркер + isInjectionVisible), переиспользованной без изменения поведения (D-3); «раз за сессию» в разделе «Механика» было неточной формулировкой (T2.2) |
Лишний вопрос-подсказка в сессии — не блокирует и правится моделью; глобальный session-флаг потребовал бы нового состояния и не переживал бы компакцию |
Тесты (идеи для плана)
- 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+ доки.
Статус реализации (25.10.2026, ветка feat/43-dsh-prestep)
| # | Решение | Статус | Где |
|---|---|---|---|
| D-1 | Роль по первому сообщению | реализовано | lib/prestep.js detectRole; пин в dsh/tests/prestep.test.mjs |
| D-2 | harness-check без HARNESS_AUTO_ADOPT | реализовано | lib/prestep.js — только текст, без записи |
| D-3 | Общий хелпер инъекций, поведение bootstrap не меняется | реализовано (6e0828e, a2453e3) |
lib/bootstrap.js isInjectionVisible (+ алиас isBootstrapVisible); тесты bootstrap зелёные |
| D-4 | #62-пункты — задачи этого change | реализовано (183f8d8, 6800550) |
dsh/tests/openspec.test.mjs, dsh/install.sh, dsh/skills/sdd/SKILL.md |
| D-5 | harnessCheck/roleHint по умолчанию true; гейт — typeof ctx.on === 'function' |
реализовано (a2453e3) |
index.js wiring; тест «bootstrap:false оставляет harness-check» |
| D-6 | Маркер <!-- DSH-HARNESS-PRESTEP:<hash> --> + общий isInjectionVisible |
реализовано (a2453e3) |
lib/prestep.js makePrestepMarker, lib/bootstrap.js |
| D-7 | Текст вопроса с четырьмя вариантами | реализовано | buildRoleHintText(null); пины в тестах |
| D-8 | TDD-текст в sdd-скилле добавлен с нуля |
реализовано (183f8d8) |
dsh/skills/sdd/SKILL.md, «Порядок TDD» |
| D-9 | DSH-автономность: без ~/.claude-путей и adopt-скрипта |
реализовано | тексты lib/prestep.js |
| D-10 | Префикс [dsh-harness], фильтр обоих префиксов |
реализовано | isHarnessMessage |
| D-11 | Дедуп по содержимому, не «ровно раз за сессию» | реализовано как факт (a2453e3) |
заявлено в спеке session-prestep, требование «Повторная инъекция и видимость» |
Статусы T1.2, T2.2, T1.3 и T1.5 (оба из #77) — в спеке-дельте
session-prestep и openspec/specs/approval-gates/spec.md; ledger —
.superpowers/sdd/2026-10-25-dsh-prestep/progress.md.