117 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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] Проект <root> не подключён к обвязке: нет: … .
Предложи владельцу настройку: `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 |
| 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`.