This commit is contained in:
parent
6800550cd5
commit
727a9dd9bc
67
dsh/PORT.md
67
dsh/PORT.md
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
Status: **spike 0.2.1, live guard/require-plan/bootstrap verified; `/role`
|
Status: **spike 0.2.1, live guard/require-plan/bootstrap verified; `/role`
|
||||||
command works, the model-invocable `role` skill and the asynchronous SDD
|
command works, the model-invocable `role` skill and the asynchronous SDD
|
||||||
protocol are verified live (three-task trial run)**. This directory is the
|
protocol are verified live (three-task trial run); the pre-step
|
||||||
|
`harness-check`/`role-hint` injections are implemented and tested but not yet
|
||||||
|
verified live — they need the reinstalled bundle**. This directory is the
|
||||||
DeepSeek Harness adapter for `claude-harness`; the Claude Code installation and
|
DeepSeek Harness adapter for `claude-harness`; the Claude Code installation and
|
||||||
the repository's source of truth are unchanged.
|
the repository's source of truth are unchanged.
|
||||||
|
|
||||||
@ -16,6 +18,7 @@ This is a port, not a copy. Three layers map differently:
|
|||||||
| Skills | `~/.claude/skills`, `.claude/skills` | `~/.dsh/skills`, `.agents/skills`, `customSkillDirs` | 1:1 format |
|
| Skills | `~/.claude/skills`, `.claude/skills` | `~/.dsh/skills`, `.agents/skills`, `customSkillDirs` | 1:1 format |
|
||||||
| Roles | `roles/`, `/role` skill | same files + `/role` command + model-invocable `role` skill | close |
|
| Roles | `roles/`, `/role` skill | same files + `/role` command + model-invocable `role` skill | close |
|
||||||
| Hard rules | `PreToolUse` shell hooks | `ctx.tools.guard` Cordis guard + approval gates A/B/C | logic reused, wrapper new |
|
| Hard rules | `PreToolUse` shell hooks | `ctx.tools.guard` Cordis guard + approval gates A/B/C | logic reused, wrapper new |
|
||||||
|
| Session-start checks | `harness-check.sh`, `role-hint.sh` | `agent/pre-step` injections `harness-check`, `role-hint` | checks ported, texts new |
|
||||||
| Superpowers | plugin + `SessionStart` hook | pinned sync of tag `v7.0.1` (`DSH_SUPERPOWERS_REF`) into `~/.dsh/skills` + `agent/pre-step` bootstrap | content adapted by sync, bootstrap new |
|
| Superpowers | plugin + `SessionStart` hook | pinned sync of tag `v7.0.1` (`DSH_SUPERPOWERS_REF`) into `~/.dsh/skills` + `agent/pre-step` bootstrap | content adapted by sync, bootstrap new |
|
||||||
| SDD | `Task` (synchronous) | `subagent` (asynchronous) / `workflow` | semantics differ |
|
| SDD | `Task` (synchronous) | `subagent` (asynchronous) / `workflow` | semantics differ |
|
||||||
| Gitea | CLI + skills | `bash` + skills | 1:1 |
|
| Gitea | CLI + skills | `bash` + skills | 1:1 |
|
||||||
@ -61,11 +64,22 @@ file has to be installed as `$DSH_HOME/AGENTS.md` because DSH does not read
|
|||||||
проекта, а копия старела бы молча. Слэш-команд нет — только скиллы.
|
проекта, а копия старела бы молча. Слэш-команд нет — только скиллы.
|
||||||
Установка в проект — `bash dsh/install.sh --openspec`; проверка — `--check`.
|
Установка в проект — `bash dsh/install.sh --openspec`; проверка — `--check`.
|
||||||
|
|
||||||
|
6. **`harness-check` and `role-hint`** through `agent/pre-step` (change
|
||||||
|
`dsh-prestep`, capability `session-prestep`). `harness-check` walks up to the
|
||||||
|
nearest `.git` and looks for `openspec/`, `.codegraph/`, and the
|
||||||
|
`## OpenSpec и superpowers` section in `CLAUDE.md`; a missing signal produces
|
||||||
|
one non-blocking `[dsh-harness]` message with the setup steps. `role-hint`
|
||||||
|
reads the first non-harness user message of the step and injects either
|
||||||
|
`Роль сессии: <r>` or one question with the four variants. Both are text-only
|
||||||
|
(`lib/prestep.js` uses fs reads, no spawn), skipped for subagents, gated by
|
||||||
|
`harnessCheck` / `roleHint` and by the same marker/visibility machinery as the
|
||||||
|
bootstrap. The Claude Code `SessionStart` scripts are not run under DSH.
|
||||||
|
|
||||||
## 3. What is deliberately deferred
|
## 3. What is deliberately deferred
|
||||||
|
|
||||||
- **`harness-check` and `role-hint`**: non-blocking SessionStart hooks. They can
|
- **CC `SessionStart` scripts**: `hooks/harness-check.sh` and
|
||||||
become `agent/pre-step` injections later; the first message role rule already
|
`hooks/role-hint.sh` stay for Claude Code sessions; DSH runs the ported checks
|
||||||
covers most of `role-hint`.
|
itself (§2 item 6), so the scripts and `settings.hooks.json` are not reused.
|
||||||
- **`briefing`**: needs an agent preset with read-only tools and a model route;
|
- **`briefing`**: needs an agent preset with read-only tools and a model route;
|
||||||
not part of the guard/bootstrap spike.
|
not part of the guard/bootstrap spike.
|
||||||
- **SDD parity**: the Claude `Task` is synchronous, while DSH `subagent` returns
|
- **SDD parity**: the Claude `Task` is synchronous, while DSH `subagent` returns
|
||||||
@ -107,6 +121,7 @@ dsh/
|
|||||||
command-role.js # /role command glue (DSH imports live only here)
|
command-role.js # /role command glue (DSH imports live only here)
|
||||||
lib/guards.js # Claude-hook JSON adapter, fail-open
|
lib/guards.js # Claude-hook JSON adapter, fail-open
|
||||||
lib/bootstrap.js # skill resolution, marker, dedupe, compaction
|
lib/bootstrap.js # skill resolution, marker, dedupe, compaction
|
||||||
|
lib/prestep.js # harness-check/role-hint detection and texts (pure)
|
||||||
lib/role.js # role parsing and text building (pure)
|
lib/role.js # role parsing and text building (pure)
|
||||||
dsh-tools.md # superpowers tool mapping
|
dsh-tools.md # superpowers tool mapping
|
||||||
hooks -> ../../hooks # local-install symlink to the tested bash/Python hooks
|
hooks -> ../../hooks # local-install symlink to the tested bash/Python hooks
|
||||||
@ -153,7 +168,7 @@ agent/pre-step
|
|||||||
-> next() (other injections happen first)
|
-> next() (other injections happen first)
|
||||||
-> skip if reject/aborted, or if subagent and bootstrapSubagents=false
|
-> skip if reject/aborted, or if subagent and bootstrapSubagents=false
|
||||||
-> prepareBootstrap(): resolve skill, read mapping, hash content, build text
|
-> prepareBootstrap(): resolve skill, read mapping, hash content, build text
|
||||||
-> isBootstrapVisible(agent, decision.messages, marker)?
|
-> isInjectionVisible(agent, decision.messages, marker)?
|
||||||
yes -> no-op
|
yes -> no-op
|
||||||
no -> append createUserMessage({ source: {kind:"user"} })
|
no -> append createUserMessage({ source: {kind:"user"} })
|
||||||
```
|
```
|
||||||
@ -162,6 +177,28 @@ The marker is an HTML comment at the top of the message. The visible-surface
|
|||||||
scan uses `agent.session.surface.nodes` + `eventAt(seq)`, the same mechanism the
|
scan uses `agent.session.surface.nodes` + `eventAt(seq)`, the same mechanism the
|
||||||
skill catalog uses, so a compacted message is re-injected on the next step.
|
skill catalog uses, so a compacted message is re-injected on the next step.
|
||||||
|
|
||||||
|
### Pre-step injection flow (harness-check, role-hint)
|
||||||
|
|
||||||
|
```
|
||||||
|
agent/pre-step (after next(), same listener as the bootstrap)
|
||||||
|
-> harness-check: sessionCwd -> detectAdoption()
|
||||||
|
no .git up to the fs root -> silent
|
||||||
|
all signals present -> silent
|
||||||
|
otherwise -> [dsh-harness] Проект … не подключён …
|
||||||
|
-> role-hint: first user message of this step that is not a harness injection
|
||||||
|
no such message -> silent (autonomous round)
|
||||||
|
keyword match -> [dsh-harness] Роль сессии: <r> …
|
||||||
|
no match -> the four-variant question
|
||||||
|
-> skip both for subagents (origin=subagent, delegationDepth>0, subagentDepth>0)
|
||||||
|
-> dedupe by the PRESTEP marker, which is the hash of the injection text
|
||||||
|
```
|
||||||
|
|
||||||
|
The checks are gated by `typeof ctx.on === 'function'` rather than by the
|
||||||
|
`bootstrap` self-check (D-5), so `bootstrap: false` keeps them alive. The
|
||||||
|
`harness-check` texts contain no Claude Code paths or adopt script (D-9); the
|
||||||
|
pre-step prefix is `[dsh-harness]`, and the message filter also accepts the
|
||||||
|
legacy `[claude-harness]` (D-10).
|
||||||
|
|
||||||
## 5. Configuration
|
## 5. Configuration
|
||||||
|
|
||||||
The bundle row can be configured from the profile patch. Defaults are suitable
|
The bundle row can be configured from the profile patch. Defaults are suitable
|
||||||
@ -178,6 +215,8 @@ for the repository layout:
|
|||||||
selfCheck: true # activation check of the DSH contract; not-ok is logged
|
selfCheck: true # activation check of the DSH contract; not-ok is logged
|
||||||
checkCommand: true # register the dsh-harness-check command
|
checkCommand: true # register the dsh-harness-check command
|
||||||
bootstrapSubagents: false
|
bootstrapSubagents: false
|
||||||
|
harnessCheck: true # pre-step: unadopted project -> setup steps (non-blocking)
|
||||||
|
roleHint: true # pre-step: first user message -> role hint or question
|
||||||
hooksDir: '' # empty -> bundle/hooks -> ../../hooks
|
hooksDir: '' # empty -> bundle/hooks -> ../../hooks
|
||||||
rolesDir: '' # empty -> ~/.dsh/roles, legacy ~/.claude/roles, repo roles/
|
rolesDir: '' # empty -> ~/.dsh/roles, legacy ~/.claude/roles, repo roles/
|
||||||
projectRolesDir: '' # empty -> <cwd>/.claude/roles
|
projectRolesDir: '' # empty -> <cwd>/.claude/roles
|
||||||
@ -401,7 +440,17 @@ profile was removed; the real `desktop` profile was not touched.
|
|||||||
is still pending.
|
is still pending.
|
||||||
- **Subagent bootstrap.** `isSubagent` probes common fields; if none are present
|
- **Subagent bootstrap.** `isSubagent` probes common fields; if none are present
|
||||||
on this version, the bootstrap reaches children too. The skill's
|
on this version, the bootstrap reaches children too. The skill's
|
||||||
`<SUBAGENT-STOP>` makes that harmless but token-expensive.
|
`<SUBAGENT-STOP>` makes that harmless but token-expensive. The pre-step
|
||||||
|
injections use the same predicate and are skipped for subagents; a fork
|
||||||
|
(`parentSession` without `origin`) is not a subagent and keeps them (T2.3).
|
||||||
|
- **Pre-step injection limits.** `harness-check` and `role-hint` are text
|
||||||
|
injections, not enforcement: nothing stops a model from ignoring them, and they
|
||||||
|
only exist in a session with the reinstalled bundle (D-13). `role-hint` matches
|
||||||
|
keywords as substrings, so a different stem («миграцию», «спеку») does not
|
||||||
|
match. Dedupe is per content (marker = hash of the text), not "exactly once per
|
||||||
|
session": a later step whose first user message has no keyword adds the
|
||||||
|
four-variant question even after an earlier role hint (D-11). `harness-check`
|
||||||
|
never adopts a project — there is no DSH port of `adopt-project.sh` (D-9).
|
||||||
- **Model tiers.** `sonnet`/`haiku`/`opus` are not DSH models. The port uses two
|
- **Model tiers.** `sonnet`/`haiku`/`opus` are not DSH models. The port uses two
|
||||||
live routes (`deepseek-flash`, `deepseek-v4-pro`) from the opt-in overlay in
|
live routes (`deepseek-flash`, `deepseek-v4-pro`) from the opt-in overlay in
|
||||||
`dsh/subagent-models.yml`; without it every dispatch runs on the session model.
|
`dsh/subagent-models.yml`; without it every dispatch runs on the session model.
|
||||||
@ -427,8 +476,10 @@ profile was removed; the real `desktop` profile was not touched.
|
|||||||
|
|
||||||
1. Finish the live smoke test: the fresh-session `role` skill acceptance, plus
|
1. Finish the live smoke test: the fresh-session `role` skill acceptance, plus
|
||||||
the bootstrap and `/compact` checks already verified.
|
the bootstrap and `/compact` checks already verified.
|
||||||
2. Add `harness-check` and optional `role-hint` as non-blocking pre-step
|
2. `harness-check` and optional `role-hint` as non-blocking pre-step injections —
|
||||||
injections.
|
**done** (change `dsh-prestep`, capability `session-prestep`): implemented and
|
||||||
|
tested in `dsh/bundle/lib/prestep.js` + `index.js`; live verification waits for
|
||||||
|
the bundle reinstall.
|
||||||
3. Build `briefing` as an agent preset (read-only tools + model route).
|
3. Build `briefing` as an agent preset (read-only tools + model route).
|
||||||
4. SDD — **done** (change `dsh-sdd`): the `sdd` skill (async controller,
|
4. SDD — **done** (change `dsh-sdd`): the `sdd` skill (async controller,
|
||||||
reports, review, fix-loop, ledger) plus the overlay in §5; proven by a
|
reports, review, fix-loop, ledger) plus the overlay in §5; proven by a
|
||||||
|
|||||||
@ -15,6 +15,11 @@ separate Cordis bundle plus a thin install helper.
|
|||||||
`/approve <change-id> design|plan`;
|
`/approve <change-id> design|plan`;
|
||||||
- superpowers bootstrap — `using-superpowers` is injected once per session from
|
- superpowers bootstrap — `using-superpowers` is injected once per session from
|
||||||
`agent/pre-step` and re-injected after compaction;
|
`agent/pre-step` and re-injected after compaction;
|
||||||
|
- pre-step injections — `harness-check` (a project that is not wired into the
|
||||||
|
harness is offered the setup steps: `openspec/`, `.codegraph/`, the
|
||||||
|
`## OpenSpec и superpowers` section in `CLAUDE.md`) and `role-hint` (the first
|
||||||
|
user message of a step names a role or asks one question with the four
|
||||||
|
variants); both are non-blocking, text-only, and skipped for subagents;
|
||||||
- roles — the model loads them with the `role` skill (`~/.dsh/roles`, then
|
- roles — the model loads them with the `role` skill (`~/.dsh/roles`, then
|
||||||
`~/.claude/roles`, then repo `roles/`); `/role dev|admin|analyst|qa` remains
|
`~/.claude/roles`, then repo `roles/`); `/role dev|admin|analyst|qa` remains
|
||||||
the user-facing command and injects the same text, without Claude's
|
the user-facing command and injects the same text, without Claude's
|
||||||
@ -31,9 +36,11 @@ separate Cordis bundle plus a thin install helper.
|
|||||||
and logs every missing service; the `dsh-harness-check` command re-runs the
|
and logs every missing service; the `dsh-harness-check` command re-runs the
|
||||||
same check on demand.
|
same check on demand.
|
||||||
|
|
||||||
**Not yet:** `briefing`, the `harness-check` SessionStart hook, `role-hint`,
|
**Not yet:** `briefing`, `harness-doctor`, auto-memory, caveman. The Claude Code
|
||||||
`harness-doctor`, auto-memory, caveman. See [PORT.md](PORT.md) for the design,
|
`SessionStart` hooks themselves stay in `hooks/` for Claude Code sessions; the
|
||||||
mapping table, and risks.
|
DSH port runs the same two checks as pre-step injections (see above) and does
|
||||||
|
not run the CC scripts. See [PORT.md](PORT.md) for the design, mapping table,
|
||||||
|
and risks.
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
@ -119,6 +126,9 @@ step, never a background pull (design D-2, cadence D-7):
|
|||||||
any code.
|
any code.
|
||||||
6. In a project with `.agents/skills`, ask `what skills do you have?` — the six
|
6. In a project with `.agents/skills`, ask `what skills do you have?` — the six
|
||||||
`openspec-*` skills must be listed.
|
`openspec-*` skills must be listed.
|
||||||
|
7. In a repository with `.git` but no `openspec/`, expect one
|
||||||
|
`[dsh-harness] Проект … не подключён к обвязке` message; a first message like
|
||||||
|
`Напиши тест для модуля` must be followed by `[dsh-harness] Роль сессии: dev`.
|
||||||
|
|
||||||
The injected bootstrap starts with a marker:
|
The injected bootstrap starts with a marker:
|
||||||
|
|
||||||
@ -129,6 +139,13 @@ The injected bootstrap starts with a marker:
|
|||||||
After compaction the marker disappears from `session.surface.nodes` and the next
|
After compaction the marker disappears from `session.surface.nodes` and the next
|
||||||
step injects a fresh copy.
|
step injects a fresh copy.
|
||||||
|
|
||||||
|
The two pre-step injections use the same shape; the hash is of the injection
|
||||||
|
text, so a different hint is a different marker:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<!-- DSH-HARNESS-PRESTEP:<16-hex-hash> -->
|
||||||
|
```
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -153,6 +170,7 @@ dsh/
|
|||||||
lib/guards.js DSH tool call -> claude-harness hook JSON
|
lib/guards.js DSH tool call -> claude-harness hook JSON
|
||||||
lib/gates.js approval gates A/B/C and approval.md/plan io (pure)
|
lib/gates.js approval gates A/B/C and approval.md/plan io (pure)
|
||||||
lib/bootstrap.js superpowers skill resolution, marker, dedupe
|
lib/bootstrap.js superpowers skill resolution, marker, dedupe
|
||||||
|
lib/prestep.js harness-check/role-hint detection and texts (pure)
|
||||||
lib/role.js role layers and text (pure)
|
lib/role.js role layers and text (pure)
|
||||||
lib/selfcheck.js bundle contract self-check (pure)
|
lib/selfcheck.js bundle contract self-check (pure)
|
||||||
dsh-tools.md superpowers tool mapping
|
dsh-tools.md superpowers tool mapping
|
||||||
@ -190,6 +208,8 @@ repository layout:
|
|||||||
selfCheck: true # activation check of the DSH contract; not-ok is logged
|
selfCheck: true # activation check of the DSH contract; not-ok is logged
|
||||||
checkCommand: true # register the dsh-harness-check command
|
checkCommand: true # register the dsh-harness-check command
|
||||||
bootstrapSubagents: false
|
bootstrapSubagents: false
|
||||||
|
harnessCheck: true # pre-step: unadopted project -> setup steps (non-blocking)
|
||||||
|
roleHint: true # pre-step: first user message -> role hint or question
|
||||||
hooksDir: '' # empty -> bundle/hooks -> ../../hooks
|
hooksDir: '' # empty -> bundle/hooks -> ../../hooks
|
||||||
rolesDir: '' # empty -> ~/.dsh/roles, legacy ~/.claude/roles, repo roles/
|
rolesDir: '' # empty -> ~/.dsh/roles, legacy ~/.claude/roles, repo roles/
|
||||||
projectRolesDir: '' # empty -> <cwd>/.claude/roles
|
projectRolesDir: '' # empty -> <cwd>/.claude/roles
|
||||||
@ -240,6 +260,22 @@ repository layout:
|
|||||||
skill and `openspec/changes/dsh-sdd/`.
|
skill and `openspec/changes/dsh-sdd/`.
|
||||||
- OpenSpec-скиллы проект получает сам (`openspec init --tools agents` или
|
- OpenSpec-скиллы проект получает сам (`openspec init --tools agents` или
|
||||||
`--openspec`); обвязка ставит их только в проект, не в `~/.dsh`.
|
`--openspec`); обвязка ставит их только в проект, не в `~/.dsh`.
|
||||||
|
- Pre-step injections (`harness-check`, `role-hint`) are active only in a session
|
||||||
|
with the reinstalled bundle (D-13): an old install has neither the texts nor the
|
||||||
|
wiring, and a session in an unadopted project stays silent.
|
||||||
|
- `role-hint` matches keywords as substrings, so a word form that changes the stem
|
||||||
|
does not match: «миграцию»/«миграции» do not hit `миграция`, «спеку» does not
|
||||||
|
hit `спека`. Forms that contain the keyword whole («теста», «issue-трекер») do
|
||||||
|
match; `pr` matches only as a whole word, so `preview` is not dev.
|
||||||
|
- Injection dedupe is per content (marker = hash of the text), not "exactly once
|
||||||
|
per session": a later step whose first user message carries no keyword adds the
|
||||||
|
four-variant question even after an earlier role hint. Both texts stay in the
|
||||||
|
session and the model follows the one that fits; this is the declared contract
|
||||||
|
(D-11), not a defect.
|
||||||
|
- `harness-check` is a hint, never adoption: it writes nothing, and there is no
|
||||||
|
DSH port of `adopt-project.sh`; the owner runs `openspec init --tools agents`,
|
||||||
|
`codegraph init`, and adds the `## OpenSpec и superpowers` section to
|
||||||
|
`CLAUDE.md`.
|
||||||
- The bundle self-checks the DSH contract it depends on at activation:
|
- The bundle self-checks the DSH contract it depends on at activation:
|
||||||
`ctx.tools.guard`, `ctx.on` for `agent/pre-step`, `ctx.inject` for commands,
|
`ctx.tools.guard`, `ctx.on` for `agent/pre-step`, `ctx.inject` for commands,
|
||||||
`workingDirectory.get`. Each missing service is logged as
|
`workingDirectory.get`. Each missing service is logged as
|
||||||
|
|||||||
@ -21,6 +21,7 @@ On DeepSeek Harness these actions resolve to the tools below.
|
|||||||
| Load a skill | `skill` |
|
| Load a skill | `skill` |
|
||||||
| Present a deliverable | `present` |
|
| Present a deliverable | `present` |
|
||||||
| Approve a plan | `exit_plan_mode` |
|
| Approve a plan | `exit_plan_mode` |
|
||||||
|
| Request an approval | `request_approval {change_id, kind}` — the model initiates it; the owner clicks Approve/Cancel |
|
||||||
| Change directory | `working_directory` |
|
| Change directory | `working_directory` |
|
||||||
| Create a worktree | `create_worktree` when the bundle is enabled, otherwise `bash` with `git worktree` |
|
| Create a worktree | `create_worktree` when the bundle is enabled, otherwise `bash` with `git worktree` |
|
||||||
|
|
||||||
@ -42,5 +43,10 @@ On DeepSeek Harness these actions resolve to the tools below.
|
|||||||
working directory. The `CLAUDE.md` files of this harness need no rewrite.
|
working directory. The `CLAUDE.md` files of this harness need no rewrite.
|
||||||
- **`todo_write` is the task-tracking action.** Older `TodoWrite` references
|
- **`todo_write` is the task-tracking action.** Older `TodoWrite` references
|
||||||
mean this tool.
|
mean this tool.
|
||||||
|
- **An approval is the owner's click, not the model's claim.**
|
||||||
|
`request_approval({change_id, kind})` starts the flow: the harness writes the
|
||||||
|
approval marker only after the owner answers Approve; Cancel writes nothing.
|
||||||
|
Never treat "I asked" or "the tool was called" as approval. `/approve` in the
|
||||||
|
Claude Code text is the same idea through a slash command.
|
||||||
- **Plan mode is a real mode.** Use `exit_plan_mode` to submit a plan; prose
|
- **Plan mode is a real mode.** Use `exit_plan_mode` to submit a plan; prose
|
||||||
agreement does not leave plan mode.
|
agreement does not leave plan mode.
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
# Design: dsh-prestep — harness-check и role-hint через agent/pre-step
|
# Design: dsh-prestep — harness-check и role-hint через agent/pre-step
|
||||||
|
|
||||||
Issue: #43 (+#62). Статус: черновик до builder-check; утверждение —
|
Issue: #43 (+#62). Статус: утверждён владельцем 25.10.2026
|
||||||
`/approve dsh-prestep design`.
|
(`/approve dsh-prestep design`); реализация — ветка `feat/43-dsh-prestep`,
|
||||||
|
статусы решений — в конце файла. Спека-дельта —
|
||||||
|
`openspec/changes/dsh-prestep/specs/session-prestep/spec.md`.
|
||||||
|
|
||||||
## Контекст
|
## Контекст
|
||||||
|
|
||||||
@ -71,6 +73,7 @@ bootstrap).
|
|||||||
| D-8 | T3.4: в sdd-скилле предложения про TDD-порядок нет — текст добавляется с нуля (пин в тесте), а не «усиливается» | Факт 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-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-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-флаг потребовал бы нового состояния и не переживал бы компакцию |
|
||||||
|
|
||||||
## Тесты (идеи для плана)
|
## Тесты (идеи для плана)
|
||||||
|
|
||||||
@ -89,3 +92,23 @@ bootstrap).
|
|||||||
- Разбивка: Task 1 prestep.js (harness-check+role-hint pure) → Task 2
|
- Разбивка: Task 1 prestep.js (harness-check+role-hint pure) → Task 2
|
||||||
wiring+bootstrap-рефакторинг → Task 3 #62 (T2.1/O-2/T3.4) → Task 4
|
wiring+bootstrap-рефакторинг → Task 3 #62 (T2.1/O-2/T3.4) → Task 4
|
||||||
спека-дельта `session-prestep` + доки.
|
спека-дельта `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`.
|
||||||
|
|||||||
272
openspec/changes/dsh-prestep/specs/session-prestep/spec.md
Normal file
272
openspec/changes/dsh-prestep/specs/session-prestep/spec.md
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
# Spec Delta: session-prestep
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Две неблокирующие инъекции шага `agent/pre-step` в DSH-бандле: `harness-check`
|
||||||
|
(проект не подключён к обвязке → список отсутствующего и шаги настройки) и
|
||||||
|
`role-hint` (первое пользовательское сообщение шага подсказывает роль или задаёт
|
||||||
|
один вопрос с четырьмя вариантами). В Claude Code это SessionStart-хуки
|
||||||
|
`harness-check.sh` и `role-hint.sh`; в DSH слоя shell-хуков нет, поэтому та же
|
||||||
|
логика живёт в бандле и использует общую с bootstrap машинерию маркера и
|
||||||
|
видимости. Обе инъекции — текстовые, неблокирующие и отключаются конфигом.
|
||||||
|
Здесь же едут follow-ups #62 того же цикла: изоляция PATH в негативном тесте,
|
||||||
|
WARN смешанного HOME/DSH_HOME и обязательный TDD-порядок в `sdd`-скилле.
|
||||||
|
Тексты собираются модулем `dsh/bundle/lib/prestep.js` (чистые функции: только
|
||||||
|
fs-чтения, без `ctx`-сервисов и без `spawn`), а решает об инъекции
|
||||||
|
`dsh/bundle/index.js`.
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Инъекция harness-check
|
||||||
|
|
||||||
|
Инъекция SHALL проверять подключение проекта к обвязке и MUST оставаться
|
||||||
|
неблокирующей: при неподключённом проекте шаг продолжается, к сообщениям шага
|
||||||
|
добавляется одно пользовательское сообщение с текстом подсказки.
|
||||||
|
|
||||||
|
Корень проекта MUST определяться upward-walk от рабочей директории сессии до
|
||||||
|
ближайшего каталога с `.git` (каталог или файл — worktree-чекаут тоже корень);
|
||||||
|
при отсутствии `.git` до корня ФС проект считается вне git и инъекции нет.
|
||||||
|
Сигналы подключения MUST быть ровно три и проверяться в этом порядке:
|
||||||
|
`openspec/` (каталог), `.codegraph/` (каталог), секция `## OpenSpec и
|
||||||
|
superpowers` в `CLAUDE.md` (строка заголовка второго уровня). Все сигналы на
|
||||||
|
месте — инъекция MUST NOT появляться; часть отсутствует — инъекция SHALL
|
||||||
|
перечислять отсутствующие в этом порядке.
|
||||||
|
|
||||||
|
Текст инъекции MUST быть: `[dsh-harness] Проект <root> не подключён к обвязке:
|
||||||
|
нет: <список отсутствующего через `; `>. Предложи владельцу настройку:
|
||||||
|
openspec init --tools agents, codegraph init и секцию «OpenSpec и superpowers» в
|
||||||
|
CLAUDE.md.` В тексте MUST NOT быть Claude Code-артефактов: ни путей `~/.claude`,
|
||||||
|
ни `adopt-project.sh`, ни `.claude/code-paths` (D-9).
|
||||||
|
|
||||||
|
Инъекция SHALL управляться конфигом `harnessCheck` (по умолчанию `true`) вместе
|
||||||
|
с общим `enabled` бандла и MUST требовать только наличия `ctx.on`
|
||||||
|
(`typeof ctx.on === 'function'`, D-5), а не успеха self-check `bootstrap`: при
|
||||||
|
`bootstrap: false` harness-check обязан работать. Реализация MUST использовать
|
||||||
|
fs-чтения (`stat`/`readFile`) и MUST NOT запускать `git`.
|
||||||
|
|
||||||
|
#### Scenario: Проект не подключён
|
||||||
|
|
||||||
|
- **WHEN** сессия стартует в git-репозитории, где нет `openspec/` и секции в
|
||||||
|
`CLAUDE.md`, а `.codegraph/` есть
|
||||||
|
- **THEN** в сообщения шага добавляется `[dsh-harness] Проект <root> не
|
||||||
|
подключён к обвязке: нет: openspec/; секция «OpenSpec и superpowers» в
|
||||||
|
CLAUDE.md.` и готовые команды настройки
|
||||||
|
|
||||||
|
#### Scenario: Проект подключён
|
||||||
|
|
||||||
|
- **WHEN** в корне проекта есть `openspec/`, `.codegraph/` и секция `## OpenSpec
|
||||||
|
и superpowers` в `CLAUDE.md`
|
||||||
|
- **THEN** harness-check молчит, сообщений не добавляется
|
||||||
|
|
||||||
|
#### Scenario: Вне git-репозитория
|
||||||
|
|
||||||
|
- **WHEN** рабочая директория сессии не имеет `.git` ни в одном родительском
|
||||||
|
каталоге до корня ФС
|
||||||
|
- **THEN** инъекции нет: harness-check не подсказывает настройку вне проекта
|
||||||
|
|
||||||
|
#### Scenario: Рабочая директория в подкаталоге проекта
|
||||||
|
|
||||||
|
- **WHEN** сессия стартует в `<repo>/dsh/bundle`, а сигналы лежат в `<repo>`
|
||||||
|
- **THEN** корнем считается `<repo>`, и текст инъекции называет `<repo>`
|
||||||
|
|
||||||
|
#### Scenario: Отключённый bootstrap не выключает harness-check
|
||||||
|
|
||||||
|
- **WHEN** профиль задаёт `bootstrap: false` и `harnessCheck: true`
|
||||||
|
- **THEN** harness-check по-прежнему инъецирует подсказку: гейт — `ctx.on`, а не
|
||||||
|
`bootstrapOk`
|
||||||
|
|
||||||
|
#### Scenario: Конфиг выключает инъекцию
|
||||||
|
|
||||||
|
- **WHEN** профиль задаёт `harnessCheck: false`
|
||||||
|
- **THEN** harness-check не инъецируется, остальные pre-step инъекции не
|
||||||
|
затрагиваются
|
||||||
|
|
||||||
|
### Requirement: Инъекция role-hint
|
||||||
|
|
||||||
|
Инъекция SHALL выбирать роль по первому пользовательскому сообщению шага; она
|
||||||
|
MUST NOT блокировать шаг и MUST NOT утверждать роль как факт.
|
||||||
|
|
||||||
|
Триггер MUST быть таким: в сообщениях текущего шага есть сообщение с
|
||||||
|
`source.kind === 'user'`, чей текст непуст и не является собственной инъекцией
|
||||||
|
обвязки. Собственной инъекцией MUST считаться текст, начинающийся с
|
||||||
|
`<!-- DSH-HARNESS-`, либо содержащий `[dsh-harness]`, либо содержащий
|
||||||
|
`[claude-harness]` (legacy-префикс до обновления install.sh, D-10). Фильтр MUST
|
||||||
|
применяться до выбора роли: иначе текст вопроса сам содержит все ключевики.
|
||||||
|
Сообщений без нового пользовательского текста (в т.ч. автономный раунд) —
|
||||||
|
инъекции нет.
|
||||||
|
|
||||||
|
Ключевики SHALL проверяться по нижнему регистру, в порядке приоритета
|
||||||
|
admin → qa → analyst → dev; побеждает первый совпавший класс:
|
||||||
|
|
||||||
|
- admin: `jira`, `гант`, `спринт`;
|
||||||
|
- qa: `стенд`, `прогон`, `скриншот`, `макет`;
|
||||||
|
- analyst: `тз`, `oq`, `спека`, `методика`;
|
||||||
|
- dev: `срез`, `issue`, `pr`, `тест`, `миграция`.
|
||||||
|
|
||||||
|
Совпадение MUST быть подстрокой вхождения целиком, кроме `pr`, который MUST
|
||||||
|
матчиться только отдельным словом (`\bpr\b`): `PR`, `pr.` — совпадение,
|
||||||
|
`preview`, `пр` — нет. Семантика подстрок заявлена явно (T1.2): словоформы,
|
||||||
|
меняющие основу, ключевик не содержат и MUST NOT матчиться — «миграцию»,
|
||||||
|
«миграции», «спеку» остаются без совпадения, тогда как формы, содержащие
|
||||||
|
ключевик целиком («теста», «issue-трекер», «ТЗ-документ»), матчатся.
|
||||||
|
|
||||||
|
При совпадении инъекция MUST быть: `[dsh-harness] Роль сессии: <r>. Вызови
|
||||||
|
skill role. Если не так — скажи.` Без совпадения инъекция MUST быть вопросом:
|
||||||
|
`[dsh-harness] Роль сессии неясна. Вызови skill role: dev — код, issue, PR,
|
||||||
|
тест, миграция; admin — Jira, Гант, спринт; analyst — ТЗ, OQ, спека, методика;
|
||||||
|
qa — стенд, прогон, скриншот, макет. Задай владельцу один вопрос с этими
|
||||||
|
четырьмя вариантами.`
|
||||||
|
|
||||||
|
Инъекция SHALL управляться конфигом `roleHint` (по умолчанию `true`) и, как
|
||||||
|
harness-check, MUST требовать только `ctx.on` (D-5), независимо от `bootstrap`.
|
||||||
|
|
||||||
|
#### Scenario: Первое сообщение с ключевиком
|
||||||
|
|
||||||
|
- **WHEN** первое пользовательское сообщение шага — «Напиши тест для модуля»
|
||||||
|
- **THEN** добавляется `[dsh-harness] Роль сессии: dev. Вызови skill role. Если
|
||||||
|
не так — скажи.`
|
||||||
|
|
||||||
|
#### Scenario: Приоритет классов ролей
|
||||||
|
|
||||||
|
- **WHEN** сообщение содержит и `jira`, и `спринт`, и `тест`
|
||||||
|
- **THEN** подсказка называет admin: admin выигрывает у qa, analyst и dev
|
||||||
|
|
||||||
|
#### Scenario: Бесключевое сообщение
|
||||||
|
|
||||||
|
- **WHEN** первое пользовательское сообщение шага не содержит ни одного
|
||||||
|
ключевика
|
||||||
|
- **THEN** добавляется вопрос с четырьмя вариантами ролей
|
||||||
|
|
||||||
|
#### Scenario: Своя инъекция не считается сообщением
|
||||||
|
|
||||||
|
- **WHEN** единственное пользовательское сообщение шага дословно равно вопросу
|
||||||
|
role-hint
|
||||||
|
- **THEN** инъекции нет: фильтр собственных сообщений срабатывает до выбора роли
|
||||||
|
|
||||||
|
#### Scenario: Legacy-префикс Claude Code
|
||||||
|
|
||||||
|
- **WHEN** единственное пользовательское сообщение начинается с
|
||||||
|
`[claude-harness]`
|
||||||
|
- **THEN** роль по нему не выбирается и инъекции нет
|
||||||
|
|
||||||
|
#### Scenario: Шаг без нового пользовательского сообщения
|
||||||
|
|
||||||
|
- **WHEN** в сообщениях шага нет пользовательского сообщения с непустым текстом
|
||||||
|
- **THEN** role-hint молчит
|
||||||
|
|
||||||
|
#### Scenario: Флексия ключевика не матчится (T1.2)
|
||||||
|
|
||||||
|
- **WHEN** первое сообщение шага — «Сделай миграцию и обнови спеку»
|
||||||
|
- **THEN** ни `миграция`, ни `спека` не совпадают, подсказки роли нет — выдаётся
|
||||||
|
вопрос с четырьмя вариантами; это заявленный контракт подстрок, а не дефект
|
||||||
|
|
||||||
|
#### Scenario: `pr` только отдельным словом
|
||||||
|
|
||||||
|
- **WHEN** первое сообщение содержит `preview`, но не содержит слова `pr`
|
||||||
|
- **THEN** dev не выбирается; `PR` или `pr.` в сообщении дают dev
|
||||||
|
|
||||||
|
### Requirement: Инъекции пропускаются в сабагентах
|
||||||
|
|
||||||
|
Обе инъекции MUST пропускаться на делегированном шаге: шаг сабагента не должен
|
||||||
|
получать ни harness-check, ни role-hint. Делегирование SHALL определяться по
|
||||||
|
реальным сигналам рантайма: `agent.session.header.origin === 'subagent'`, либо
|
||||||
|
`agent.session.header.delegationDepth > 0`, либо
|
||||||
|
`agent.options.subagentDepth > 0` (T2.1). Форк сессии MUST NOT считаться
|
||||||
|
сабагентом: форк (в т.ч. `parentSession` без `origin`) получает все инъекции
|
||||||
|
(T2.3).
|
||||||
|
|
||||||
|
#### Scenario: Делегированный шаг
|
||||||
|
|
||||||
|
- **WHEN** pre-step выполняется для агента с `origin: 'subagent'` (или с
|
||||||
|
положительной глубиной делегирования)
|
||||||
|
- **THEN** ни harness-check, ни role-hint не инъецируются, даже если проект не
|
||||||
|
подключён, а сообщение содержит ключевик
|
||||||
|
|
||||||
|
#### Scenario: Форк сессии получает инъекции
|
||||||
|
|
||||||
|
- **WHEN** pre-step выполняется для форка (`parentSession` без `origin`)
|
||||||
|
- **THEN** пропуска нет: инъекции работают как в главной сессии
|
||||||
|
|
||||||
|
### Requirement: Повторная инъекция и видимость
|
||||||
|
|
||||||
|
Инъекции SHALL дедуплицироваться по маркеру
|
||||||
|
`<!-- DSH-HARNESS-PRESTEP:<16-hex> -->`, который строится из хэша текста
|
||||||
|
инъекции и стоит первой строкой тела; одна и та же видимая инъекция MUST NOT
|
||||||
|
добавляться дважды. Видимость SHALL проверяться общим хелпером
|
||||||
|
`isInjectionVisible(agent, messages, marker)`: сначала сообщения текущего шага,
|
||||||
|
затем durable-поверхность `session.surface.nodes` + `eventAt(seq)`; после
|
||||||
|
компакции маркер исчезает с поверхности и инъекция повторяется (D-6).
|
||||||
|
|
||||||
|
Дедупликация MUST быть по содержимому (маркеру), а не «ровно один раз за
|
||||||
|
сессию» (T2.2). Фактическое поведение, которое и заявляется контрактом:
|
||||||
|
|
||||||
|
- роль, определённая на другом шаге иначе, даёт другой текст и другой маркер —
|
||||||
|
повторная подсказка роли возможна;
|
||||||
|
- если на более позднем шаге первый пользовательский текст бесключевой,
|
||||||
|
role-hint может выдать вопрос с четырьмя вариантами после уже выданной
|
||||||
|
подсказки роли: это следствие per-text дедупа, а не дефект (D-11);
|
||||||
|
- harness-check при неизменном состоянии проекта даёт тот же текст и тот же
|
||||||
|
маркер — повтор возможен только после компакции или изменения набора
|
||||||
|
отсутствующих сигналов.
|
||||||
|
|
||||||
|
#### Scenario: Та же инъекция не дублируется
|
||||||
|
|
||||||
|
- **WHEN** тот же текст инъекции уже виден в сообщениях шага или на поверхности
|
||||||
|
сессии
|
||||||
|
- **THEN** вторая копия не добавляется
|
||||||
|
|
||||||
|
#### Scenario: Другой текст даёт вторую инъекцию (T2.2)
|
||||||
|
|
||||||
|
- **WHEN** подсказка роли уже выдана на первом шаге, а на следующем шаге первое
|
||||||
|
пользовательское сообщение бесключевое
|
||||||
|
- **THEN** добавляется вопрос с четырьмя вариантами: маркер другого текста не
|
||||||
|
виден, и дедуп по содержимому его не подавляет
|
||||||
|
|
||||||
|
#### Scenario: Повтор после компакции
|
||||||
|
|
||||||
|
- **WHEN** маркер инъекции пропал с поверхности сессии после `/compact`
|
||||||
|
- **THEN** следующий шаг добавляет инъекцию заново
|
||||||
|
|
||||||
|
#### Scenario: Смена роли на новом шаге
|
||||||
|
|
||||||
|
- **WHEN** на первом шаге роль была dev, а на следующем сообщение содержит
|
||||||
|
`jira`
|
||||||
|
- **THEN** добавляется подсказка admin: текст и маркер другие
|
||||||
|
|
||||||
|
### Requirement: Follow-ups #62 в этом же change
|
||||||
|
|
||||||
|
Пункты #62 SHALL ехать задачами этого change, а не отдельными фичами (D-4), и
|
||||||
|
каждый MUST быть закрыт тестом или пином текста:
|
||||||
|
|
||||||
|
- T2.1/M-2: негативный тест `install.sh --check` без `openspec` MUST изолировать
|
||||||
|
PATH (каталог-заглушка как весь PATH), а не наследовать host PATH с
|
||||||
|
фиксированным системным префиксом: глобальный npm-префикс на Linux кладёт
|
||||||
|
`openspec` в `/usr/bin`, и тест «бинарника нет» иначе проверяет не то.
|
||||||
|
- O-2: `install.sh` MUST предупреждать (WARN, прогон продолжается) о смешанном
|
||||||
|
состоянии, когда `DSH_HOME` пришёл из окружения, указывает на существующий
|
||||||
|
каталог и не равен `$HOME/.dsh`, при нестандартном HOME: изоляция только HOME
|
||||||
|
пишет в реальный `DSH_HOME`. WARN MUST быть один на прогон и MUST
|
||||||
|
срабатывать и в `--global` (`install_global`), а не только в `--skills`.
|
||||||
|
- T3.4: `dsh/skills/sdd/SKILL.md` MUST содержать порядок TDD: шаг «напиши тест и
|
||||||
|
убедись, что он падает (RED)» обязателен до кода; текст добавляется с нуля и
|
||||||
|
пинится тестом (D-8).
|
||||||
|
|
||||||
|
#### Scenario: Негативный тест `--check` без openspec
|
||||||
|
|
||||||
|
- **WHEN** тест запускает `install.sh --check` с PATH, состоящим только из
|
||||||
|
каталога-заглушки без `openspec`
|
||||||
|
- **THEN** проверка сообщает об отсутствии `openspec`, а host-бинарник не влияет
|
||||||
|
на результат
|
||||||
|
|
||||||
|
#### Scenario: Смешанный HOME и DSH_HOME
|
||||||
|
|
||||||
|
- **WHEN** `--global` запускается с нестандартным HOME и `DSH_HOME`, указывающим
|
||||||
|
на существующий каталог
|
||||||
|
- **THEN** печатается `WARN: DSH_HOME=… both HOME and DSH_HOME must be isolated
|
||||||
|
together`, и установка продолжается
|
||||||
|
|
||||||
|
#### Scenario: TDD-порядок в sdd-скилле
|
||||||
|
|
||||||
|
- **WHEN** модель читает `sdd`-скилл перед первым диспатчем
|
||||||
|
- **THEN** в нём есть требование обязательного падающего теста до кода, и это
|
||||||
|
требование закреплено тестом
|
||||||
@ -61,8 +61,13 @@ MUST отказать с перечислением отсутствующих
|
|||||||
Подтверждение владельца MUST идти через `ctx.userQuestions.ask` (D-12): вопрос с
|
Подтверждение владельца MUST идти через `ctx.userQuestions.ask` (D-12): вопрос с
|
||||||
вариантами Approve и Cancel; без сервиса команда MUST отказать, а не писать. При
|
вариантами Approve и Cancel; без сервиса команда MUST отказать, а не писать. При
|
||||||
подтверждении пишутся `by: owner` и `at` в ISO 8601 UTC; при отмене или
|
подтверждении пишутся `by: owner` и `at` в ISO 8601 UTC; при отмене или
|
||||||
неподтверждении — отказ `not confirmed`, файл не меняется. Команда MUST NOT
|
неподтверждении — отказ `not confirmed`, файл не меняется. Сбой самого диалога
|
||||||
создавать план и не трогать `proposal.md`, `design.md`, `builder-check.md`.
|
(исключение из `ctx.userQuestions.ask`) MUST давать отказ с текстом ошибки —
|
||||||
|
`userQuestions failed: <message>`, — а не литерал `not confirmed` (T1.5 #77):
|
||||||
|
владелец должен видеть причину, по которой вопрос не дошёл, а отмена и сбой
|
||||||
|
различаются в логе. `approval.md` не меняется ни в одном из этих случаев. Команда
|
||||||
|
MUST NOT создавать план и не трогать `proposal.md`, `design.md`,
|
||||||
|
`builder-check.md`.
|
||||||
|
|
||||||
#### Scenario: /approve design без builder-check
|
#### Scenario: /approve design без builder-check
|
||||||
|
|
||||||
@ -93,6 +98,17 @@ MUST отказать с перечислением отсутствующих
|
|||||||
- **THEN** команда отказывает с `userQuestions service unavailable; /approve
|
- **THEN** команда отказывает с `userQuestions service unavailable; /approve
|
||||||
requires it` и `approval.md` не пишется
|
requires it` и `approval.md` не пишется
|
||||||
|
|
||||||
|
#### Scenario: Сбой диалога вопросов
|
||||||
|
|
||||||
|
- **WHEN** `ctx.userQuestions.ask` завершается исключением
|
||||||
|
- **THEN** команда отказывает с `userQuestions failed: <message>`, а не с
|
||||||
|
литералом `not confirmed`, и `approval.md` не меняется
|
||||||
|
|
||||||
|
#### Scenario: Отмена в диалоге
|
||||||
|
|
||||||
|
- **WHEN** владелец ответил Cancel (или вопрос остался без ответа)
|
||||||
|
- **THEN** команда отказывает с `not confirmed` и `approval.md` не меняется
|
||||||
|
|
||||||
### Requirement: Гейт A — план до утверждения дизайна
|
### Requirement: Гейт A — план до утверждения дизайна
|
||||||
|
|
||||||
Для write/edit/multi_edit/`str_replace_editor` с целевым путём под
|
Для write/edit/multi_edit/`str_replace_editor` с целевым путём под
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user