86 lines
7.5 KiB
Markdown
86 lines
7.5 KiB
Markdown
# 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:...>`
|
||
и тем же механизмом видимости, что у bootstrap (`isBootstrapVisible`
|
||
переименован в общий `isInjectionVisible(agent, messages, marker)`).
|
||
Порядок в pre-step: другие инъекции → harness-check → role-hint (обе
|
||
неблокирующие; reject/abort — пропуск; сабагенты — пропуск, как
|
||
bootstrap).
|
||
|
||
### harness-check
|
||
|
||
- Сигнал подключения: upward-walk от sessionCwd до корня ФС: ближайший
|
||
каталог с `.git` (или без него до упора) — «корень проекта»; в нём
|
||
`CLAUDE.md`/`AGENTS.md` содержит `OpenSpec и superpowers` ИЛИ есть
|
||
`openspec/` ИЛИ `.claude/code-paths` ИЛИ `CLAUDE.harness-section.md`.
|
||
Вне git-репозитория (`.git` не найден до корня) — пропуск, как у
|
||
CC-хука (молчит). Нет сигнала — инъекция:
|
||
«[claude-harness] Проект не подключён к обвязке: предложи владельцу
|
||
`adopt-project.sh` или спроси, нужна ли обвязка». Не блокирует.
|
||
- Реализация — fs-чтения (stat/readFile) по путям, без spawn git.
|
||
|
||
### role-hint
|
||
|
||
- Триггер: **новое** пользовательское сообщение текущего pre-step
|
||
(`decision.messages`), source.kind === 'user', И текст не начинается с
|
||
`<!-- DSH-HARNESS-` и не содержит `[claude-harness]` (фильтр собственных
|
||
инъекций — bootstrap/harness-check не считаются «первым сообщением»).
|
||
Нет такого — инъекции нет (в т.ч. автономные раунды без нового
|
||
сообщения).
|
||
- Ключевые слова (точно как в AGENTS.md; builder-check нашёл опечатку —
|
||
«грант» убран):
|
||
- admin: `jira`, `гант`, `спринт`;
|
||
- qa: `стенд`, `прогон`, `скриншот`, `макет`;
|
||
- analyst: `тз`, `oq`, `спека`, `методика`;
|
||
- dev: `срез`, `issue`, `pr`, `тест`, `миграция`.
|
||
Совпадение — инъекция `[claude-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 | Нет |
|
||
|
||
## Тесты (идеи для плана)
|
||
|
||
- 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` + доки.
|