86 lines
7.5 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). Статус: черновик до 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` + доки.