500 lines
27 KiB
Markdown
500 lines
27 KiB
Markdown
# DSH port — design and spike notes
|
||
|
||
Status: **spike 0.2.1, live guard/require-plan/bootstrap verified; `/role`
|
||
command works, the model-invocable `role` skill and the asynchronous SDD
|
||
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
|
||
the repository's source of truth are unchanged.
|
||
|
||
## 1. Verdict
|
||
|
||
This is a port, not a copy. Three layers map differently:
|
||
|
||
| Layer | Claude Code | DeepSeek Harness | Status |
|
||
|---|---|---|---|
|
||
| Instructions | `~/.claude/CLAUDE.md`, project `CLAUDE.md` | `$DSH_HOME/AGENTS.md`, project `CLAUDE.md`/`AGENTS.md`, nested | almost 1:1 |
|
||
| 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 |
|
||
| 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 |
|
||
| SDD | `Task` (synchronous) | `subagent` (asynchronous) / `workflow` | semantics differ |
|
||
| Gitea | CLI + skills | `bash` + skills | 1:1 |
|
||
| OpenSpec | CLI + `.claude/skills` + `/opsx:*` | CLI + проектные `.agents/skills` | close: нет слэш-команд |
|
||
| codegraph | MCP | MCP (`mcp__codegraph__*`) | 1:1 with names |
|
||
| auto-memory | Claude Code feature | none | dropped/replaced with files |
|
||
| install | `install.sh` + `settings.json` | bundle + profile manager | new |
|
||
|
||
The important discovery: **DSH loads `CLAUDE.md` as a project instruction file**,
|
||
so the project templates and nested instructions need no rewrite. The global
|
||
file has to be installed as `$DSH_HOME/AGENTS.md` because DSH does not read
|
||
`~/.claude/CLAUDE.md`.
|
||
|
||
## 2. What this spike implements
|
||
|
||
1. **`guard-main` and `require-plan`** through `ctx.tools.guard`. The adapter
|
||
synthesizes the Claude hook payload (`{cwd, tool_name, tool_input}`), runs
|
||
the existing `hooks/guard-main.sh` / `hooks/require-plan.sh`, turns `exit 2`
|
||
into a denial reason, and fails open on every other error. The Python core
|
||
(`hooks/harness_target.py`) and its tests remain the source of truth. A guard
|
||
is synchronous and monotonic: it runs after `tools/pre-execute`, and no later
|
||
listener can turn the denial back into permission.
|
||
2. **Superpowers bootstrap** through `agent/pre-step`. It reads the installed
|
||
`using-superpowers/SKILL.md`, strips frontmatter, appends
|
||
`bundle/dsh-tools.md`, wraps the result in `<EXTREMELY_IMPORTANT>`, and
|
||
injects it once per session. A content-hash marker handles dedupe; scanning
|
||
`session.surface.nodes` re-injects after compaction.
|
||
3. **Roles** — `/role` is a real command (`ctx.commands.register`); it reads
|
||
`~/.dsh/roles/<role>.md`, falls back to `~/.claude/roles/<role>.md`, then to
|
||
the repository's `roles/`, and reads the project `.claude/roles/<role>.md`.
|
||
It injects the role text as a user message and starts a turn for the
|
||
five-line summary. Because a model cannot call a slash command,
|
||
`skills/role/SKILL.md` is the model-invocable loader: it infers the role,
|
||
reads the file, and tells the model to follow it. Auto-memory is explicitly
|
||
dropped; the role's "Читать на старте" section is left to the model.
|
||
4. **Display metadata and an optional CodeGraph MCP overlay** so the bundle can
|
||
be installed through the Plugins page and codegraph stays available.
|
||
|
||
5. **OpenSpec** — воркфлоу идут проектными скиллами `.agents/skills/openspec-*`,
|
||
которые пишет `openspec init --tools agents`. DSH читает этот каталог нативно;
|
||
проверено сабагентом с cwd в проекте — все шесть имён в каталоге скиллов.
|
||
Глобальной копии в `~/.dsh/skills` нет: генератор обновляется вместе с CLI
|
||
проекта, а копия старела бы молча. Слэш-команд нет — только скиллы.
|
||
Установка в проект — `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
|
||
|
||
- **CC `SessionStart` scripts**: `hooks/harness-check.sh` and
|
||
`hooks/role-hint.sh` stay for Claude Code sessions; DSH runs the ported checks
|
||
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;
|
||
not part of the guard/bootstrap spike.
|
||
- **SDD parity**: the Claude `Task` is synchronous, while DSH `subagent` returns
|
||
a child id and reports later. The `sdd` skill implements the asynchronous
|
||
analogue (report files, notification as the wait, `send_message` for fix
|
||
rounds, ledger as memory); there is no parity and the skill says so.
|
||
- **Codegraph by default**: `dsh/mcp.codegraph.yml` is an opt-in overlay; the
|
||
bundle does not assume the binary is installed.
|
||
- **`harness-doctor`**: its scripts inspect `~/.claude`; a DSH variant is a
|
||
separate task.
|
||
- **Auto-memory, caveman**: no direct DSH equivalents; caveman is off by default
|
||
anyway.
|
||
- **Full installer**: `install.sh` prepares global instructions and skills; the
|
||
bundle itself is installed by `plugin_manager` because the Desktop profile is
|
||
Electron-managed.
|
||
- **Pinned sync of superpowers (change `dsh-port-v2`, D-2)**: "vendored
|
||
superpowers" is gone — no upstream skill text is copied as-is. The default
|
||
install source is the clone at `~/.dsh/superpowers` pinned to
|
||
`$DSH_SUPERPOWERS_REF` (default `v7.0.1`). `install.sh --skills` clones or
|
||
updates it, runs the vendored `dsh/sync/sync-from-upstream.mjs` (asserted
|
||
transforms, namespace strip, `dsh-tools.md` override) and
|
||
`dsh/sync/verify-skills.mjs`, and copies only the adapted tree; a broken or
|
||
empty synced tree is never installed. Explicit sources (`--superpowers-from`,
|
||
`$DSH_SUPERPOWERS_SOURCE`) are synced directly with no network. Upstream
|
||
moves independently, so each release is a deliberate pin bump (an issue per
|
||
release, D-7; the procedure is in `dsh/README.md`). Removing the legacy
|
||
`~/.claude` fallbacks is deferred until every machine has rerun
|
||
`--skills --roles`; until then both runtime and installer warn when they use
|
||
one.
|
||
|
||
## 4. Architecture
|
||
|
||
```
|
||
dsh/
|
||
bundle/
|
||
package.json # dsh.bundle.patch, exports, display resources
|
||
cordis.patch.yml # inserts the dsh-harness row
|
||
index.js # ctx.tools.guard + agent/pre-step + /role wiring
|
||
command-role.js # /role command glue (DSH imports live only here)
|
||
lib/guards.js # Claude-hook JSON adapter, fail-open
|
||
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)
|
||
dsh-tools.md # superpowers tool mapping
|
||
hooks -> ../../hooks # local-install symlink to the tested bash/Python hooks
|
||
locale/{en,ru}.json
|
||
icon.svg
|
||
tests/ # Node tests, no DSH runtime needed
|
||
skills/role/SKILL.md # model-invocable role loader (installed to ~/.dsh)
|
||
install.sh # --check / --global / --skills / --roles / --all
|
||
mcp.codegraph.yml # optional CodeGraph MCP row
|
||
```
|
||
|
||
The bundle imports only Node builtins and files inside this package. It
|
||
deliberately does not import `@deepseek-ai/dsh-llm` or
|
||
`@deepseek-ai/schemastery`: a local-directory profile install resolves imports
|
||
from the profile, not from the DSH installation, so a DSH-internal import fails
|
||
at activation with `ERR_MODULE_NOT_FOUND`. `lib/message.js` is a local copy of
|
||
the tiny `createUserMessage` constructor, and `Config` was replaced by defaults
|
||
normalized in `apply()`. The pure modules are testable with plain Node; this is
|
||
why the port's 26 tests run without a DSH process.
|
||
|
||
### Guard flow
|
||
|
||
```
|
||
model calls write/edit/bash
|
||
-> tools/pre-execute (approval, extensible policy)
|
||
-> ctx.tools.guard (dsh-harness, synchronous, monotonic)
|
||
-> lib/guards.js maps tool + args
|
||
-> bash hooks/guard-main.sh (cwd, tool_name, tool_input)
|
||
-> python3 hooks/harness_target.py guard-main
|
||
-> exit 2 => deny reason (final)
|
||
-> exit 0 or any failure => allow
|
||
-> require-plan.sh for write/edit
|
||
-> execution
|
||
```
|
||
|
||
The guard returns a denial; the tool pipeline materializes it as the model-facing
|
||
error. Other listeners cannot turn it back into permission. A script failure,
|
||
missing Python, or malformed JSON fails open, matching the harness invariant.
|
||
|
||
### Bootstrap flow
|
||
|
||
```
|
||
agent/pre-step
|
||
-> next() (other injections happen first)
|
||
-> skip if reject/aborted, or if subagent and bootstrapSubagents=false
|
||
-> prepareBootstrap(): resolve skill, read mapping, hash content, build text
|
||
-> isInjectionVisible(agent, decision.messages, marker)?
|
||
yes -> no-op
|
||
no -> append createUserMessage({ source: {kind:"user"} })
|
||
```
|
||
|
||
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
|
||
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
|
||
|
||
The bundle row can be configured from the profile patch. Defaults are suitable
|
||
for the repository layout:
|
||
|
||
```yaml
|
||
- id: dsh-harness
|
||
config:
|
||
enabled: true
|
||
guards: true
|
||
approvalGates: true # gates A/B/C; active only inside the guard chain
|
||
bootstrap: true
|
||
roleCommand: true
|
||
selfCheck: true # activation check of the DSH contract; not-ok is logged
|
||
checkCommand: true # register the dsh-harness-check command
|
||
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
|
||
rolesDir: '' # empty -> ~/.dsh/roles, legacy ~/.claude/roles, repo roles/
|
||
projectRolesDir: '' # empty -> <cwd>/.claude/roles
|
||
roleNames: [dev, admin, analyst, qa]
|
||
bootstrapSkillPath: '' # empty -> ~/.dsh/skills, then legacy Claude Code paths
|
||
toolMappingPath: '' # empty -> bundle/dsh-tools.md
|
||
bootstrapMaxBytes: 65536
|
||
```
|
||
|
||
### Model routes and delegation depth
|
||
|
||
SDD needs two things the default profile does not give: a model choice for
|
||
`subagent` and a second level of delegation for a background controller.
|
||
`dsh/subagent-models.yml` is the opt-in overlay for both:
|
||
|
||
```bash
|
||
# 1. copy the two entries into the profile patch (desktop is Electron-managed)
|
||
open -e ~/.dsh/profiles/desktop/cordis.patch.yml # append dsh/subagent-models.yml
|
||
# 2. restart DSH, then check
|
||
bash dsh/install.sh --check # "ok: subagent maxDepth=2 (...)"
|
||
```
|
||
|
||
After the restart the `subagent` and `subagent_fork` schemas accept
|
||
`provider`, `model`, `reasoning_effort`, and a background controller subagent
|
||
can dispatch an implementer. Routes measured in 0.2.1-rc.3:
|
||
`deepseek-official/deepseek-flash` (transcription and mechanics) and
|
||
`deepseek-official/deepseek-v4-pro` (integration and every review). Without the
|
||
overlay the agent runs everything on the session model and at depth 1; the
|
||
`sdd` skill must say so in its report instead of pretending the routes exist.
|
||
|
||
The overlay stays out of `dsh/bundle/cordis.patch.yml` on purpose: the
|
||
`subagent-model-selection-settings` row exists in the web/desktop profile and is
|
||
absent in headless, and enabling it from the bundle would break headless boot
|
||
with "requires ... in the Host scope".
|
||
|
||
`bootstrapSkillPath` resolution order: explicit path, `DSH_SUPERPOWERS_SKILL`,
|
||
`~/.dsh/skills`, `~/.agents/skills`, the newest
|
||
`~/.claude/plugins/cache/claude-plugins-official/superpowers/*/`, then
|
||
`~/.claude/skills`. The DSH copy wins because `dsh/install.sh --skills` copies
|
||
real files there; the Claude Code locations are a legacy fallback and the
|
||
bundle logs one warning per process when it resolves one of them.
|
||
|
||
Install-time source resolution is separate and stricter (change
|
||
`dsh-install-source`, pin in change `dsh-port-v2`): `--superpowers-from` →
|
||
`$DSH_SUPERPOWERS_SOURCE` → the pinned clone `~/.dsh/superpowers` at
|
||
`$DSH_SUPERPOWERS_REF` (default `v7.0.1`, cloned or updated from
|
||
`$DSH_SUPERPOWERS_REPO`) → the legacy cache → `~/.claude/skills`. Explicit
|
||
sources are synced in place with no network and must be a checkout root or
|
||
its `skills/` directory — a bare skills directory outside that layout is
|
||
invalid, and `--check` and `--skills` fail the same way (narrower than change
|
||
`dsh-install-source` #49; the spec delta records the MODIFIED). Explicit
|
||
syncs stage into `~/.dsh/.skills.synced` beside the clone dir, which stays
|
||
reserved for the pin; an existing clone is updated by fetching
|
||
`$DSH_SUPERPOWERS_REPO` (not its origin) and reused offline only when already
|
||
on the ref. An invalid explicit source is a hard `FAIL`; a machine offline
|
||
without a clone is a `FAIL` with the clone hint, falling back to legacy only
|
||
when a legacy source exists.
|
||
|
||
### OpenSpec skills
|
||
|
||
Воркфлоу OpenSpec ставит генератор CLI, а не обвязка:
|
||
|
||
```bash
|
||
cd <project>
|
||
openspec init --tools agents # или: bash <harness>/dsh/install.sh --openspec
|
||
openspec update # обновить скиллы после апгрейда CLI
|
||
```
|
||
|
||
Скиллы лежат в `.agents/skills/openspec-{propose,apply-change,archive-change,explore,sync-specs,update-change}/SKILL.md`
|
||
и перезаписываются генератором: править их руками нельзя. Команды адаптер
|
||
`agents` не ставит, поэтому `/openspec` в DSH нет — модель вызывает скилл по
|
||
фразе «openspec propose» / «openspec archive».
|
||
|
||
Цикл без Claude Code:
|
||
|
||
```bash
|
||
openspec new change <id>
|
||
openspec status --change <id> --json
|
||
openspec instructions proposal --change <id> --json
|
||
openspec validate <id> --strict
|
||
openspec archive <id> -y
|
||
```
|
||
|
||
## 6. Install and verify
|
||
|
||
Prerequisites: the `claude-harness` checkout, DSH 0.2.1-rc.3, `python3`,
|
||
`git`, Node. Claude Code is not required at all: `--skills` clones the pinned
|
||
superpowers checkout itself (`DSH_SUPERPOWERS_REF`, default `v7.0.1`) and runs
|
||
the DSH sync + verify before installing, so a manual clone is no longer the
|
||
first step; `--roles` copies the role files; the hooks come from this
|
||
checkout.
|
||
|
||
```bash
|
||
# 1. Static checks (offline; reports the configured pin and the source ref)
|
||
bash dsh/install.sh --check
|
||
|
||
# 2. Global instructions, skills, and roles (run from a terminal; writes
|
||
# outside the session workspace). --skills clones the pinned superpowers
|
||
# checkout (DSH_SUPERPOWERS_REF, default v7.0.1) and syncs it; or pass
|
||
# --superpowers-from / DSH_SUPERPOWERS_SOURCE to sync a checkout directly.
|
||
bash dsh/install.sh --global
|
||
bash dsh/install.sh --skills # or: --skills --superpowers-from DIR
|
||
bash dsh/install.sh --roles
|
||
|
||
# or all three at once:
|
||
bash dsh/install.sh --global --skills --roles
|
||
|
||
# 3. Install the bundle in DSH:
|
||
# Plugins -> install bundle from dsh/bundle
|
||
# or ask the agent:
|
||
# plugin_manager install_bundle target=/absolute/path/to/claude-harness/dsh/bundle
|
||
|
||
# 4. Optional: CodeGraph MCP
|
||
# apply dsh/mcp.codegraph.yml as a --patch overlay; the tool is
|
||
# mcp__codegraph__codegraph_explore.
|
||
```
|
||
|
||
`--openspec` добавляет agents-адаптер в текущий проект (`$PWD`) и требует
|
||
существующий `openspec/`: без него скрипт отказывается и печатает готовую
|
||
команду. В живой сессии проверка такая: в проекте с `.agents/skills`
|
||
спросить «what skills do you have?» — должны быть шесть `openspec-*`.
|
||
|
||
`--roles` also appends the DSH SDD note to `~/.dsh/roles/dev.md` (the bullet that
|
||
points `dev` at the `sdd` skill). The note lives only in the installed copy: the
|
||
shared `roles/dev.md` stays Claude Code-compatible, so a Claude session never
|
||
sees DSH-only instructions. The insertion is idempotent — every `--roles` run
|
||
recopies the role from the repository and re-inserts exactly one note.
|
||
|
||
Live checks:
|
||
|
||
1. New session: ask "what skills do you have?" — superpowers skills and the
|
||
DSH catalog should be visible.
|
||
2. On `main`, ask the agent to create a file — expect
|
||
`BLOCKED [guard-main]`.
|
||
3. In a worktree on a feature branch, edit `src/...` without a plan — expect
|
||
`BLOCKED [require-plan]`; add `.bounded` and retry.
|
||
4. `/role dev` — expect the five-line summary and the injected role rules.
|
||
5. The acceptance prompt "Let's make a react todo list" must trigger
|
||
`brainstorming` before code. If it does not, the bootstrap is not visible;
|
||
check the plugin row and the skill path.
|
||
6. Guard marker: the injected message starts with
|
||
`<!-- DSH-HARNESS-BOOTSTRAP:<hash> -->`; after compaction a new marker is
|
||
appended.
|
||
|
||
Verified live on 2026-10-24 in the desktop profile:
|
||
|
||
- the bootstrap marker `<!-- DSH-HARNESS-BOOTSTRAP:6b676dd9bb8a0234 -->` and the
|
||
full `using-superpowers` body arrived in a fresh session, and the skill
|
||
catalog showed the superpowers skills;
|
||
- `write` to a file on `main` returned `BLOCKED [guard-main]`;
|
||
- a Bash redirect write on `main` returned `BLOCKED [guard-main]`;
|
||
- a `write` on a feature branch was allowed;
|
||
- `write` to `src/probe.ts` on the branch returned `BLOCKED [require-plan]`,
|
||
and the same write succeeded after creating `.bounded`.
|
||
|
||
Still pending: `/role dev`, the fresh-session "react todo list" acceptance, and
|
||
re-injection after `/compact`.
|
||
|
||
Automated tests:
|
||
|
||
```bash
|
||
bash dsh/tests/run.sh
|
||
```
|
||
|
||
They cover main/detached guards, branch allowance, Bash writes, `require-plan`
|
||
with `.bounded` and a branch plan, bootstrap frontmatter/marker/dedupe,
|
||
role-layer composition, and package metadata.
|
||
|
||
Composition smoke test (no model call): the bundle was installed with
|
||
`dsh plugin --profile test add dsh/bundle` into a temporary `DSH_HOME` based on
|
||
the shipped `headless` profile. `--dump-config` shows the `dsh-harness` row, the
|
||
profile lists `@local/dsh-harness` in `dsh.profile.bundles`, and the package's
|
||
internal `hooks` symlink resolves to the repository `hooks/`. The temporary
|
||
profile was removed; the real `desktop` profile was not touched.
|
||
|
||
## 7. Risks and open questions
|
||
|
||
- **Instructions are not enforcement — closed by gates A/B/C.** The upstream loop
|
||
(builder-check → `/approve <id> design` → plan → `/approve <id> plan` → sdd)
|
||
exists in superpowers as text, and the 24.10.2026 session skipped builder-check
|
||
while the plan was written before design approval. Change
|
||
`enforce-upstream-loop` adds `lib/gates.js` + `command-approve.js`: gate A
|
||
denies a plan for an unapproved design, gate B denies an sdd start (a dispatch
|
||
or a `.superpowers/sdd/**` write) for an unapproved or changed plan (sha256),
|
||
gate C keeps `approval.md` `/approve`-only, and bash write targets are parsed
|
||
by `harness_target.py` and gated too. Residual risk: only a session with the
|
||
reinstalled bundle is gated (D-13), and `terminal_*` tools stay outside the
|
||
chain (T3.6); the contract is pinned in
|
||
`openspec/changes/enforce-upstream-loop/specs/approval-gates/spec.md`.
|
||
- **DSH is 0.2.1-rc.3.** The plugin/event API is not frozen; pin the version and
|
||
re-run the tests after upgrades.
|
||
- **Contract self-check.** The plugin/event API drift is now caught from inside
|
||
DSH: at activation the bundle checks the contract it depends on
|
||
(`ctx.tools.guard`, `ctx.on` for `agent/pre-step`, `ctx.inject` for commands,
|
||
`workingDirectory.get`), logs
|
||
`[dsh-harness] self-check: <feature> not-ok: <detail>` for each missing
|
||
service, and skips the feature whose service is missing (guards, bootstrap,
|
||
or commands — each gated by its own check), so a DSH version without one of
|
||
the services does not crash the bundle.
|
||
`/dsh-harness-check` re-runs the same check on demand. This is the in-DSH
|
||
compat check that closes RV-11 (D-6) — the terminal `--check` cannot see the
|
||
SDK inside `app.asar`.
|
||
- **Local profile imports are not resolved from the DSH installation.** The
|
||
first install attempt failed at activation with
|
||
`ERR_MODULE_NOT_FOUND: Cannot find package '@deepseek-ai/schemastery'`. The
|
||
bundle now imports only Node builtins and local files: `lib/message.js`
|
||
replaces `createUserMessage` from `@deepseek-ai/dsh-llm`, and `Config` was
|
||
dropped in favor of defaults normalized in `apply()`. Any future DSH service
|
||
must be reached through `ctx` (injected service), never through an
|
||
`@deepseek-ai/*` import; a package test enforces this.
|
||
- **`ctx.tools.guard` is synchronous.** The guard resolves cwd through
|
||
`workingDirectory.get(session)` (sync) and runs the hooks with
|
||
`execFileSync`. If a future DSH version changes the guard contract or makes
|
||
cwd resolution async, the adapter needs a `tools/pre-execute` fallback. The
|
||
guard is global and monotonic; it runs after `tools/pre-execute`, so an earlier
|
||
user approval cannot turn a `guard-main` denial back into permission.
|
||
- **`agent/pre-step` injection.** Verified live: the bootstrap marker and body
|
||
arrived in a fresh session, and the skill catalog showed the superpowers
|
||
skills. The local `createUserMessage` copy works with `source.kind: "user"`,
|
||
which avoids an unknown session source variant. Re-injection after compaction
|
||
is still pending.
|
||
- **Subagent bootstrap.** `isSubagent` probes common fields; if none are present
|
||
on this version, the bootstrap reaches children too. The skill's
|
||
`<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
|
||
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.
|
||
- **One-sided model route.** The trial's «child LLM provider and model must be
|
||
supplied together» was not a race: the logged tool call carried only `model`
|
||
(issue #52). DSH rejects a one-sided route correctly, so no upstream report is
|
||
needed; the `sdd` skill now says to retry with both fields.
|
||
- **`~/.claude` dependence.** No longer needed for install, bootstrap, or
|
||
roles: the install-time source is explicit, and `--skills --roles` copy real
|
||
files into `~/.dsh`. The Claude Code locations remain a **legacy migration
|
||
fallback**; the installer, `--check`, and the runtime warn when they are used
|
||
(runtime — once per process). An existing installation must rerun
|
||
`--skills --roles` once to stop relying on them; removal of the fallbacks is
|
||
a separate task.
|
||
- **Superpowers porting rules.** The upstream guide requires a session-start
|
||
bootstrap with no per-session opt-in; `agent/pre-step` satisfies that shape,
|
||
but a real acceptance transcript is still required before claiming a port.
|
||
- **Hooks symlink.** Local-directory installation links the checkout, so
|
||
`bundle/hooks -> ../../hooks` works. A packed/published bundle must include a
|
||
real copy of `hooks/`; the `files` field already lists it.
|
||
|
||
## 8. Next phases
|
||
|
||
1. Finish the live smoke test: the fresh-session `role` skill acceptance, plus
|
||
the bootstrap and `/compact` checks already verified.
|
||
2. `harness-check` and optional `role-hint` as non-blocking pre-step 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).
|
||
4. SDD — **done** (change `dsh-sdd`): the `sdd` skill (async controller,
|
||
reports, review, fix-loop, ledger) plus the overlay in §5; proven by a
|
||
three-task trial run (`openspec/changes/dsh-sdd/trial/`, results in
|
||
`design.md`).
|
||
5. Enable CodeGraph MCP by default when the binary is present; update role and
|
||
project instructions to `mcp__codegraph__codegraph_explore`.
|
||
6. Port `harness-doctor` checks to DSH paths.
|
||
7. Decide the long-term home: keep `dsh/` in this repository while it shares
|
||
hooks/roles/skills, then extract a `dsh-harness` repository when the bundle
|
||
gets its own release cadence.
|
||
8. OpenSpec — **done** (change `dsh-openspec`): проектные скиллы, `--openspec`,
|
||
`--check`; приёмка — в дизайне change.
|
||
9. Install without `~/.claude` — **done** (change `dsh-install-source`):
|
||
`--superpowers-from`/`DSH_SUPERPOWERS_SOURCE`/`~/.dsh/superpowers`, hard
|
||
`FAIL` instead of a silent empty bootstrap, DSH-owned role paths in the
|
||
installed `AGENTS.md`, legacy warnings at install and runtime.
|