@joekytc/dsh-swarm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +504 -0
  3. package/README.zh-CN.md +452 -0
  4. package/client/BoardCard.tsx +41 -0
  5. package/client/ConnectionBanner.tsx +16 -0
  6. package/client/KanbanBoard.tsx +168 -0
  7. package/client/KanbanTab.tsx +26 -0
  8. package/client/RenameModal.tsx +38 -0
  9. package/client/TaskDrawer.tsx +212 -0
  10. package/client/WorkflowRail.tsx +175 -0
  11. package/client/board-store.ts +196 -0
  12. package/client/css.d.ts +4 -0
  13. package/client/index.ts +31 -0
  14. package/client/kanban.css +653 -0
  15. package/client/useKanbanBoard.ts +7 -0
  16. package/client/workflow-model.ts +229 -0
  17. package/cordis.patch.yml +88 -0
  18. package/lib/client.js +1205 -0
  19. package/lib/config.d.ts +46 -0
  20. package/lib/config.js +43 -0
  21. package/lib/dispatcher/agent-runner.d.ts +23 -0
  22. package/lib/dispatcher/agent-runner.js +429 -0
  23. package/lib/dispatcher/chain-auditor.d.ts +47 -0
  24. package/lib/dispatcher/chain-auditor.js +194 -0
  25. package/lib/dispatcher/dispatcher.d.ts +50 -0
  26. package/lib/dispatcher/dispatcher.js +280 -0
  27. package/lib/dispatcher/event-waker.d.ts +13 -0
  28. package/lib/dispatcher/event-waker.js +24 -0
  29. package/lib/dispatcher/git-credentials.d.ts +33 -0
  30. package/lib/dispatcher/git-credentials.js +78 -0
  31. package/lib/dispatcher/merge-gate.d.ts +28 -0
  32. package/lib/dispatcher/merge-gate.js +74 -0
  33. package/lib/dispatcher/model-candidates.d.ts +13 -0
  34. package/lib/dispatcher/model-candidates.js +31 -0
  35. package/lib/dispatcher/session-events.d.ts +28 -0
  36. package/lib/dispatcher/session-events.js +33 -0
  37. package/lib/dispatcher/target-repo.d.ts +15 -0
  38. package/lib/dispatcher/target-repo.js +42 -0
  39. package/lib/dispatcher/v-orchestrator.d.ts +74 -0
  40. package/lib/dispatcher/v-orchestrator.js +452 -0
  41. package/lib/dispatcher/watchdog.d.ts +15 -0
  42. package/lib/dispatcher/watchdog.js +30 -0
  43. package/lib/dispatcher/workspace-attach.d.ts +40 -0
  44. package/lib/dispatcher/workspace-attach.js +112 -0
  45. package/lib/domain/delivery-contract.d.ts +18 -0
  46. package/lib/domain/delivery-contract.js +80 -0
  47. package/lib/domain/delivery-evidence.d.ts +12 -0
  48. package/lib/domain/delivery-evidence.js +41 -0
  49. package/lib/domain/event-store.d.ts +20 -0
  50. package/lib/domain/event-store.js +53 -0
  51. package/lib/domain/kanban-service.d.ts +94 -0
  52. package/lib/domain/kanban-service.js +430 -0
  53. package/lib/domain/permissions.d.ts +7 -0
  54. package/lib/domain/permissions.js +54 -0
  55. package/lib/domain/planning-checklist.d.ts +22 -0
  56. package/lib/domain/planning-checklist.js +81 -0
  57. package/lib/domain/prefetch-manifest.d.ts +21 -0
  58. package/lib/domain/prefetch-manifest.js +73 -0
  59. package/lib/domain/projection.d.ts +3 -0
  60. package/lib/domain/projection.js +165 -0
  61. package/lib/domain/review-evidence.d.ts +13 -0
  62. package/lib/domain/review-evidence.js +76 -0
  63. package/lib/domain/state-machine.d.ts +4 -0
  64. package/lib/domain/state-machine.js +32 -0
  65. package/lib/domain/task-parents.d.ts +17 -0
  66. package/lib/domain/task-parents.js +39 -0
  67. package/lib/domain/tdd-classify.d.ts +5 -0
  68. package/lib/domain/tdd-classify.js +20 -0
  69. package/lib/domain/types.d.ts +142 -0
  70. package/lib/domain/types.js +2 -0
  71. package/lib/index.d.ts +5 -0
  72. package/lib/index.js +56 -0
  73. package/lib/roles/preset-installer.d.ts +8 -0
  74. package/lib/roles/preset-installer.js +53 -0
  75. package/lib/roles/toolsets.d.ts +62 -0
  76. package/lib/roles/toolsets.js +332 -0
  77. package/lib/roles/wiki-worker.d.ts +20 -0
  78. package/lib/roles/wiki-worker.js +44 -0
  79. package/lib/routes/kanban-http.d.ts +6 -0
  80. package/lib/routes/kanban-http.js +159 -0
  81. package/lib/routes/kanban-sse.d.ts +8 -0
  82. package/lib/routes/kanban-sse.js +58 -0
  83. package/lib/routes/planning-driver.d.ts +16 -0
  84. package/lib/routes/planning-driver.js +52 -0
  85. package/lib/routes/prefix-router.d.ts +29 -0
  86. package/lib/routes/prefix-router.js +28 -0
  87. package/lib/services/kanban-provider.d.ts +16 -0
  88. package/lib/services/kanban-provider.js +14 -0
  89. package/lib/tools/kanban-tools.d.ts +11 -0
  90. package/lib/tools/kanban-tools.js +169 -0
  91. package/lib/tools/main-session-tools.d.ts +20 -0
  92. package/lib/tools/main-session-tools.js +170 -0
  93. package/lib/tools/planning-tools.d.ts +32 -0
  94. package/lib/tools/planning-tools.js +102 -0
  95. package/lib/tools/prefetch-tools.d.ts +5 -0
  96. package/lib/tools/prefetch-tools.js +59 -0
  97. package/lib/tools/spec-card-tools.d.ts +4 -0
  98. package/lib/tools/spec-card-tools.js +75 -0
  99. package/lib/tools/wiki-tools.d.ts +4 -0
  100. package/lib/tools/wiki-tools.js +57 -0
  101. package/lib/wiki/kb-linkage.d.ts +9 -0
  102. package/lib/wiki/kb-linkage.js +87 -0
  103. package/lib/wiki/page-path.d.ts +6 -0
  104. package/lib/wiki/page-path.js +28 -0
  105. package/lib/wiki/wiki-vault-client.d.ts +28 -0
  106. package/lib/wiki/wiki-vault-client.js +48 -0
  107. package/package.json +83 -0
  108. package/personas/kanban-d/agent.cordis.yml +157 -0
  109. package/personas/kanban-d/preset.yml +2 -0
  110. package/personas/kanban-dt/agent.cordis.yml +71 -0
  111. package/personas/kanban-dt/preset.yml +2 -0
  112. package/personas/kanban-p/agent.cordis.yml +66 -0
  113. package/personas/kanban-p/preset.yml +2 -0
  114. package/personas/kanban-pt/agent.cordis.yml +47 -0
  115. package/personas/kanban-pt/preset.yml +2 -0
  116. package/personas/kanban-v/agent.cordis.yml +47 -0
  117. package/personas/kanban-v/preset.yml +2 -0
  118. package/personas/kanban-w/agent.cordis.yml +47 -0
  119. package/personas/kanban-w/preset.yml +2 -0
  120. package/personas/persona-d.md +26 -0
  121. package/personas/persona-dt.md +18 -0
  122. package/personas/persona-p.md +13 -0
  123. package/personas/persona-pt.md +13 -0
  124. package/personas/persona-v.md +18 -0
  125. package/personas/persona-w.md +12 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-swarm contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,504 @@
1
+ # dsh-swarm
2
+
3
+ [简体中文](README.zh-CN.md) · [English](README.md)
4
+
5
+ ---
6
+
7
+ **A governed swarm of six specialist DSH agents that turns one requirement into a strict, evidence-verified pipeline.**
8
+
9
+ An orchestrator (V) decomposes an approved spec into a strictly ordered phase chain (`p → (pt?) → w2 → d → dt → w3 → summary`); six single-purpose roles (V / P / W / D / PT / DT) run each phase with isolated, permission-gated tool faces; every handoff is machine-verified against an evidence contract; failures recover through idempotent retry and human-gated reviews; and a live Workflow kanban tab streams all state to the browser via SSE. Design inspired by the Hermes Agent kanban.
10
+
11
+ ![TypeScript](https://img.shields.io/badge/TypeScript-5.8-blue)
12
+ ![License](https://img.shields.io/badge/license-MIT-green)
13
+ ![npm](https://img.shields.io/npm/v/@joekytc/dsh-swarm)
14
+
15
+ ---
16
+
17
+ ## Why
18
+
19
+ Coordinating several AI agents on one task typically fails in three ways:
20
+
21
+ 1. **Role drift** — a "planner" starts writing code, an "executor" reviews its own work, and nobody owns the outcome.
22
+ 2. **Unverifiable handoffs** — an agent claims "done" with no reproducible evidence, and the next agent builds on sand.
23
+ 3. **Silent deadlocks** — an agent stops without finishing and the pipeline hangs, or bad code is merged before anyone reviewed it.
24
+
25
+ dsh-swarm encodes a *contract* against all three: one machine-enforced responsibility per role; every handoff must carry structured evidence or the phase will not close; and every stall or review failure lands in a visible, recoverable state with a human as the trust anchor. It is built **correctness-first** — deterministic state machines, append-only event sourcing, idempotent schedulers, and a red-team test suite that replays the event log and rejects any illegal transition.
26
+
27
+ ---
28
+
29
+ ## Roles & the execution pipeline
30
+
31
+ Six roles are dispatched by the scheduler as one-shot agent sessions (deterministic session id `kbn-<taskId>`, resumed on retry/rework via `resumeSessionId`). Each role-agent session is bound to exactly one task (`boundTaskId`) and gets a trimmed tool face. V is the exception: a chain-scoped orchestrator session (`kbn-v-<chainId>`) with no `boundTaskId`.
32
+
33
+ | Role | Alias | Responsibility | Tool face (highlights) |
34
+ |---|---|---|---|
35
+ | **V** | Orchestrator | Drives the phase machine, creates one card per phase, posts `[blocked-review]` guidance on stalls. Never executes. | `kanban_create` + task tools + spec view |
36
+ | **P** | Planner | Reads spec + repo facts (incl. read-only self-checks), writes an OpenSpec implementation plan, opts into PT via `pt_decision.needed`. Never executes. | Task tools + spec view, read-only (writes only `openspec/changes/`) |
37
+ | **PT** | Plan reviewer | Read-only review of P's plan (requirements alignment, completeness, logic). Outputs verdict + issues. | Task tools + spec view, **read-only ToolGuard** |
38
+ | **W** | Wiki bridge | W2/W3 KB sync (`w:kb`). Never touches code/git. | Task tools + `wiki_search/read/write` + read-only spec view |
39
+ | **D** | Executor | The *only* role that writes code: worktree → implement → verify → `[AI-GEN]` commit → push feature branch (merging into TARGET_BRANCH is done by the system only after DT passes). | Task tools + wiki read + bash/fs/run_code (full dev) + subagent (spawn/fork/list-agents) + goal |
40
+ | **DT** | Implementation reviewer | Empirically verifies D's work (test/build/typecheck/diff/git + open-code-review), writes review page to KB. Read-only against the repo. | Task tools + wiki read/write (review namespace) + bash/fs/run_code, **read-only ToolGuard** |
41
+
42
+ The pipeline (R20 phase order, strictly serial within a chain, parallel across chains):
43
+
44
+ ```text
45
+ p ──> (pt?) ──> w2 ──> d ──> dt ──> w3 ──> summary
46
+ | | | | | | |
47
+ plan plan plan impl impl KB wrap-up
48
+ (P) review sync (D) review sync (system)
49
+ (only when P (W2) (fixed) (W3)
50
+ opts in)
51
+ ```
52
+
53
+ - `pt` is created only when P's handoff delivers `pt_decision = { needed: true, reason }` — V only creates the card, the system never overrides the decision. `needed: false` skips straight to `w2`.
54
+ - `dt` is always created after `d`.
55
+ - Repo facts are gathered by the phase-0 planning session (`planning_prefetch`, read-only), not by a W phase.
56
+ - The chain is completed by a mechanical rule, not by an agent: last completed task is W3 (`w/kb`), the D (`execute`) task is done with delivery evidence, and no open tasks remain.
57
+
58
+ ---
59
+
60
+ ## Install
61
+
62
+ ### Prerequisites
63
+
64
+ - A working [DSH](https://github.com/deepseek-ai) installation (the `@deepseek-ai/*` runtime packages: cordis, dsh-agent, dsh-tools, dsh-persona, dsh-session).
65
+ - Node.js ≥ 22.19 and npm (match DSH's runtime requirement).
66
+ - Peer dependencies shipped with DSH: `@deepseek-ai/dsh-tool-bash`, `@deepseek-ai/dsh-tool-fs`, `@deepseek-ai/dsh-tool-fs-search`, `@deepseek-ai/schemastery`.
67
+ - An optional wiki-vault HTTP service for W/P/D KB reads and W2/W3 syncs (see [Configuration](#configuration)).
68
+
69
+ ### Build
70
+
71
+ ```bash
72
+ npm install
73
+ npm run build # tsc -p tsconfig.build.json (lib/*.js) + client bundle (lib/client.js)
74
+ ```
75
+
76
+ ### Install as a DSH plugin
77
+
78
+ ```bash
79
+ # From npm — a Web profile also adds the kanban browser tab
80
+ dsh plugin --profile web add @joekytc/dsh-swarm
81
+
82
+ # From the local checkout (development)
83
+ dsh plugin --profile <name> add ./dsh-swarm
84
+ ```
85
+
86
+ > From GitHub source: `dsh plugin --profile web add github:joekytc/dsh-swarm`.
87
+ >
88
+ > `storageDir` must be set with the **unquoted** `!!js dshHomePath("storages/kanban")`
89
+ > form. Quoting it degrades the path into a literal string (a known footgun).
90
+
91
+ ### Quickstart
92
+
93
+ 1. Start a DSH session and type:
94
+
95
+ ```
96
+ /plan: <requirement> / <project> / <API>
97
+ ```
98
+
99
+ This enters phase-0 planning (zero side effects — no cards yet): `grill-me` asks
100
+ one clarifying question at a time, `planning_prefetch` gathers read-only repo
101
+ facts, and the conversation converges on a planning checklist with the six spec
102
+ sections (`problem / solution / user_stories / impl_decisions / testing /
103
+ out_of_scope`) plus a repo manifest. `planning_checklist_save` schema-validates
104
+ the checklist — an invalid or incomplete one blocks approval.
105
+
106
+ 2. Confirm and launch:
107
+
108
+ ```
109
+ /openspec: 确认执行
110
+ ```
111
+
112
+ The chain and spec card are created from the saved checklist; the `file-prefetch`
113
+ (repo path) and `kb` (checklist page) attachments are mounted, the spec is
114
+ approved, the chain transitions to `executing`, and the dispatcher wakes the V
115
+ orchestrator, which builds the pipeline one phase at a time.
116
+
117
+ 3. Watch progress in the **kanban tab** (the third tab of the conversation center:
118
+ Conversation → Trajectory → Kanban). Click a card for Overview / Trajectory /
119
+ Handoff / Spec / Comments.
120
+
121
+ 4. When a chain completes, the system audits the workspace for out-of-chain writes
122
+ and (for D chains) merges D's feature branch into `TARGET_BRANCH`. If an audit
123
+ warning is raised, confirm ownership in the GUI before the final summary is shown.
124
+
125
+ ---
126
+
127
+ ## Configuration
128
+
129
+ All keys are optional; defaults shown. Schema lives in `src/config.ts`.
130
+
131
+ | Key | Default | Description |
132
+ |---|---|---|
133
+ | `storageDir` | `$DSH_HOME/storages/kanban` | Event log (`events.jsonl`), orchestration state, per-task workspaces, `dispatcher.log` |
134
+ | `wikiVault.baseUrl` | `http://192.168.122.111:3000` | wiki-vault HTTP service for KB reads/writes |
135
+ | `wikiVault.pagePrefix` | `projects/` | Whitelist prefix for W page writes |
136
+ | `roles.models.<role>` | `{}` | Per-role model: `{ provider, model, reasoningEffort?, fallbacks?[] }` |
137
+ | `roles.models.<role>.reasoningEffort` | `high` | Default reasoning effort for all roles |
138
+ | `roles.models.<role>.fallbacks` | `[]` | Silent fallback candidates (audited via `[model-fallback]` comment) |
139
+ | `dispatcher.staleTimeoutSeconds` | `14400` | Heartbeat timeout; running task without heartbeat is reclaimed |
140
+ | `dispatcher.maxRetries` | `3` | Failure retries before circuit → `blocked(gave_up)` |
141
+ | `dispatcher.heartbeatIntervalSeconds` | `300` | Watchdog heartbeat period |
142
+ | `dispatcher.maxProtocolViolations` | `2` | Protocol-violation guardrail: after this many consecutive violations the next one is final (`gave_up`) |
143
+ | `dispatcher.maxReworksPerRole` | `{ pt: 2, dt: 3 }` | Max review rework rounds before `review/gave-up` + `[review-final]` |
144
+ | `prefixRoutes.plan` | `/plan:` | Phase-0 planning prefix |
145
+ | `prefixRoutes.openspec` | `/openspec:` | Approve-and-execute prefix |
146
+ | `ui.enabled` | `true` | Enable the kanban web tab |
147
+ | `ui.contentMinWidth` | `715` | Minimum kanban content width (px) |
148
+ | `ui.contentMaxWidth` | `780` | Maximum kanban content width (px) |
149
+ | `ui.sseHeartbeatSeconds` | `20` | SSE heartbeat interval |
150
+
151
+ ---
152
+
153
+ ## Guardrails
154
+
155
+ ### Permission matrix
156
+
157
+ `can(action, actor, task, { boundTaskId })` in `src/domain/permissions.ts`.
158
+ "Bound" means the actor is the role agent session spawned for *that exact task*
159
+ (`boundTaskId === task.id` and, for `complete`, also `actor === task.assignee`).
160
+
161
+ | Action | V | P | W | D | PT | DT | Human | System |
162
+ |---|---|---|---|---|---|---|---|---|
163
+ | create-chain / create-task | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
164
+ | claim | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
165
+ | complete | ❌ | bound | bound | bound | bound | bound | ✅ (GUI) | ✅ |
166
+ | block | ❌ | bound | bound | bound | bound | bound | ✅ | ✅ |
167
+ | heartbeat | ❌ | bound | bound | bound | bound | bound | ❌ | ❌ |
168
+ | comment | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
169
+ | unblock | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
170
+ | archive | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
171
+ | spec-approve | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
172
+ | spec-edit | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
173
+ | spec-attach | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
174
+ | update-title | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
175
+ | delete-chain | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
176
+ | wiki-write | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ (review ns) | ❌ | ❌ |
177
+ | wiki-read | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ |
178
+ | prefetch | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
179
+ | audit-confirm | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
180
+ | create-rework-task | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
181
+
182
+ Key guarantees (two):
183
+
184
+ - **The main session cannot execute.** It only gets `kanban_show`/`kanban_list`/
185
+ `kanban_comment` + `spec_card_view` + `kanban_route` — never
186
+ `kanban_create`/`kanban_complete`/`kanban_block`. Chains/specs are created only
187
+ via `/plan:`+`/openspec:`; the GUI observes and mutates task state but never
188
+ creates chains or tasks — "who decided to run what" stays explicit and auditable.
189
+ - **Session binding prevents cross-task escalation** (a W agent bound to task A
190
+ cannot complete/block task B even though both are W tasks); DT writes are
191
+ confined to the `projects/<chain>/review/` namespace by a ToolGuard on top of
192
+ the matrix; and no role agent can approve specs, unblock, or confirm audits —
193
+ those are human trust anchors; `system` handles only mechanical bookkeeping.
194
+
195
+ ### Delivery contract (upstream owes downstream)
196
+
197
+ Each phase's handoff must carry the keys its downstream actually reads
198
+ (`src/domain/delivery-contract.ts`). Missing keys block the current role's card
199
+ immediately (and the orchestrator never builds a downstream card on a blocked
200
+ parent):
201
+
202
+ | Card | Required handoff keys |
203
+ |---|---|
204
+ | W2 / W3 (`w:kb`) | `kb_url` + `page_path` |
205
+ | P (`p:openspec`) | `artifacts_path` + `pt_decision` (`needed` boolean required; when `needed: true`, `reason` is required) |
206
+ | D (`d:execute`) | `changed_files` + (`commit_hash` or `push`) — `hasDeliveryEvidence`; `branch` (feature branch) is expected for the merge gate, not a hard-complete blocker; `tdd` (`test_files` or `skipped.reason`, XOR) |
207
+ | PT / DT | `review_evidence` (schema-valid) — `validateReviewEvidence` |
208
+
209
+ ### TDD hard gate (evidence threshold)
210
+
211
+ D completes only with `tdd` — `test_files` (with `test_first`) or `skipped.reason`
212
+ (XOR, `delivery-evidence.ts`). DT's `review_evidence` must carry `tdd`; on a
213
+ `pass` verdict the runner must be `vitest` (`test.runner`) and `test_first === true`
214
+ must hold (`review-evidence.ts`). This makes "tests actually ran, and were written
215
+ first" a machine-checked property rather than a claim.
216
+
217
+ ### Phase-0 planning checklist
218
+
219
+ `/plan:` runs a read-only planning session (`grill-me` → `planning_prefetch` →
220
+ `planning_checklist_save`, `planning-driver.ts`). The checklist carries a structured
221
+ manifest (repo facts + file baseline, `prefetch-manifest.ts`); an invalid manifest
222
+ blocks the save, and `/openspec:` mounts the checklist as the `file-prefetch` +
223
+ `kb` attachments on the spec card (`prefix-router.ts`).
224
+
225
+ ### Review quality chain
226
+
227
+ - After **P** completes, **PT** is created only when P's handoff delivers
228
+ `pt_decision.needed = true`; the orchestrator never overrides the decision
229
+ (V only creates the card).
230
+ - After **D** completes, a **DT** card is *always* created.
231
+ - **PT/DT** are read-only: a ToolGuard mechanically denies writes to the repo
232
+ sources, git mutations, and (for DT) wiki writes outside the review namespace.
233
+ - **DT** review engine: `open-code-review` (ocr, delegation mode, diff
234
+ `--from TARGET_BRANCH --to <feature branch>`) → fallback `superpowers
235
+ code-review` → block `review-tool-unavailable` only if both are unavailable.
236
+ - `review_evidence` must pass `validateReviewEvidence` or the review card cannot
237
+ complete: PT needs verdict + issues + plan ref; DT additionally needs
238
+ test (exit 0 on pass), build/typecheck, lint, non-empty diff, git,
239
+ ocr/fallback conclusion, and `tdd`.
240
+
241
+ ### Rework (review failure)
242
+
243
+ A failed review never mutates a `done` card. Instead the system records
244
+ `review/failed`, creates a **rework task** (`[返工] ...`) that inherits the source's
245
+ session (`resumeSessionId`), `reviewAttempt + 1`, and starts as `todo`
246
+ (`reviewStatus: 'pending'`), then re-dispatches a fresh review card for the rework.
247
+ When `reviewAttempt` reaches `maxReworksPerRole` (PT 2 / DT 3), the system records
248
+ `review/gave-up` and posts a `[review-final]` evidence-chain comment; the pipeline
249
+ stalls at the review stage for human intervention.
250
+
251
+ ### Failure recovery
252
+
253
+ Two orthogonal failure paths, both human-recoverable:
254
+
255
+ - **Protocol violation** (agent idle without `complete`/`block`): role agent →
256
+ `blocked(protocol_violation)` → V posts idempotent `[blocked-review]` guidance →
257
+ human unblocks → same-session resume (NOT a fresh start). After
258
+ `maxProtocolViolations` (2) recoverable cycles, the next violation →
259
+ `blocked(gave_up)` + system posts `[blocked-final]` evidence chain (block
260
+ timeline + review/comment timeline + final reason).
261
+ - **Hard failures & circuit**: `task/failed` increments `attempts`; the dispatcher
262
+ re-dispatches (same-session resume) while `attempts < maxRetries`, then circuits
263
+ to `blocked(gave_up: max retries)`. The watchdog reclaims `running` tasks that
264
+ stop heartbeating after `staleTimeoutSeconds` (heartbeats are a *status* signal,
265
+ never a business mutation; SSE heartbeats never carry board state). Per-role
266
+ model candidates (primary + fallbacks, `reasoningEffort: high` default) fall
267
+ back silently (audited via `[model-fallback]` comment); if *all* candidates fail
268
+ it blocks `model-unavailable` for the human. A single hanging V wake cannot
269
+ stall the scheduler — every dispatch is wrapped in a timeout.
270
+
271
+ ### Chain completion: audit gate + merge gate
272
+
273
+ When the mechanical chain-complete rule fires, two gates run in the
274
+ `chain/completed` hook:
275
+
276
+ 1. **Completion audit gate (D23)**: the `ChainAuditor` cross-checks the chain
277
+ workspace for artifacts written outside the known task outputs. Orphaned writes
278
+ emit `chain/audit-warning`; the UI shows a warning banner and blocks the final
279
+ summary until the human confirms ownership (`chain/audit-confirmed`, human-only).
280
+ 2. **Merge gate (post-DT system merge)**: D never merges to `TARGET_BRANCH` and
281
+ never pushes it — it only commits to (and optionally pushes) its feature branch,
282
+ carrying `branch` in its handoff. After DT approves and the chain completes,
283
+ `merge-gate.ts` performs, as `system`: `git checkout TARGET_BRANCH → git merge
284
+ --no-ff <feature-branch> → git push`. Outcomes are recorded as idempotent
285
+ comments: `[merge-done]` (with hash), `[merge-skip]` (merge input unresolvable),
286
+ or `[merge-failed]` (checkout/merge/push failed, e.g. a conflict). Failures never
287
+ throw — a bad merge is never performed, which is the safe direction; humans can
288
+ repair afterwards.
289
+
290
+ ---
291
+
292
+ ## Event sourcing & domain model
293
+
294
+ Every state change is appended to `<storageDir>/events.jsonl`, one JSON event per
295
+ line. The `seq` is assigned by the store (re-read from the file tail on every
296
+ append, so concurrent instances never collide). The **trajectory is the event log
297
+ itself**; restart replays it to rebuild the board.
298
+
299
+ ```jsonc
300
+ // one line in events.jsonl
301
+ { "seq": 12, "chainId": "ch_x_...", "taskId": "t_y_...",
302
+ "kind": "task/completed",
303
+ "payload": { "summary": "...", "metadata": { /* handoff evidence */ } },
304
+ "author": "w", "at": 1760000000000 }
305
+ ```
306
+
307
+ Event families: `chain/*` (created, executing, completed, aborted, root-task-set,
308
+ audit-warning, audit-confirmed, title-updated), `spec-card/*` (created, edited,
309
+ approved), `task/*` (created, claimed, heartbeat, commented, completed, blocked,
310
+ unblocked, failed, archived, renamed), and `review/*` (passed, failed, gave-up).
311
+
312
+ Replay is **strict**: the projection applies every event through the state machine
313
+ and throws on any illegal transition, so a corrupted or tampered log fails loudly
314
+ instead of silently producing an inconsistent board (covered by
315
+ `tests/redteam/anti-escalation.test.ts` and `tests/domain/projection.test.ts`).
316
+
317
+ The service emits events through a serialized queue (append-then-publish), and
318
+ subscribers (SSE) receive every event exactly once in order. UI and dispatcher both
319
+ consume the same persisted events — there is no secondary source of truth.
320
+
321
+ ---
322
+
323
+ ## Web client (Workflow kanban tab)
324
+
325
+ A browser-half React tab registered as the third `conversation.view` slot
326
+ (`id=kanban`, `order=20`, after Conversation and Trajectory). It registers **no
327
+ shell-level overlays, sidebars, or detail panes**.
328
+
329
+ - **Data path**: initial snapshot (`GET /kanban/board`) → SSE stream
330
+ (`GET /kanban/events?after=<seq>`) → board-store applies events incrementally,
331
+ deduplicates by `seq`, and re-pulls the full snapshot on any gap. **No business
332
+ polling.**
333
+ - **Layout**: multi-chain vertical rails; fixed content width 715–780 px, full
334
+ height; the active chain is expanded, blocked chains always show a warning
335
+ summary. In-page rename/delete use a lightweight modal (no shell overlays);
336
+ no drag-and-drop, no width memory.
337
+ - **Cards**: compact two-line cards with profile-colored nodes; status lines are
338
+ green solid (done) / blue solid (current) / gray dashed (pending) / red broken
339
+ (blocked).
340
+ - **Detail drawer**: five sections — Overview / Trajectory / Handoff / Spec /
341
+ Comments; `Esc` or back returns to the list.
342
+ - **Actions** (`POST /kanban/action`): block / unblock / retry / complete /
343
+ archive / comment, plus chain-level `confirm-audit`, `rename` (chain or task),
344
+ and `delete` (chain, human-only, double-confirmed in the GUI). Human actions
345
+ apply optimistic updates with rollback; the store reconciles against the
346
+ authoritative snapshot on any divergence.
347
+ - **Build**: `npm run build:client` produces `lib/client.js` in the
348
+ `window.__ModuleLoader__.load()` format (identical convention to `dsh-client-*`).
349
+ Adding dsh-swarm to a web profile auto-embeds it into `__DSH_BOOT__`.
350
+
351
+ ---
352
+
353
+ ## Architecture
354
+
355
+ Five layers, with the domain layer kept **free of any DSH dependency** so it can be
356
+ fully unit-tested and replayed in isolation.
357
+
358
+ ```mermaid
359
+ flowchart TB
360
+ subgraph Client
361
+ Tab["conversation.view tab (id=kanban, order=20)"]
362
+ Store["board-store: snapshot + SSE + seq gap resync"]
363
+ Model["workflow-model: pure view projection"]
364
+ end
365
+
366
+ subgraph Domain ["domain/ (pure TS, zero DSH deps)"]
367
+ ES["event-store (JSONL append-only, monotonic seq)"]
368
+ SM["state-machine (task/chain/spec transitions)"]
369
+ PJ["projection (events → BoardState)"]
370
+ PM["permissions (actor × session-bound matrix)"]
371
+ KS["kanban-service (three-interface facade)"]
372
+ EC["delivery-contract / delivery-evidence / review-evidence / prefetch-manifest"]
373
+ end
374
+
375
+ subgraph Integration ["integration (cordis)"]
376
+ TOOLS["tools: kanban_* / spec_card_* / wiki_* / prefetch_* / kanban_route"]
377
+ ROUTES["prefix-router + planning-driver (/plan: /openspec:)"]
378
+ HTTP["kanban-http + kanban-sse (/kanban/board, /kanban/events, /kanban/action)"]
379
+ end
380
+
381
+ subgraph Dispatcher ["dispatcher/"]
382
+ WAKER["event-waker (events → wake V)"]
383
+ VORCH["v-orchestrator (R20 phase machine)"]
384
+ RUNNER["agent-runner (one-shot role sessions, presets, ToolGuards)"]
385
+ WD["watchdog (heartbeat / stale reclaim / circuit)"]
386
+ AUDIT["chain-auditor (D23 completion audit)"]
387
+ MG["merge-gate (post-DT system merge)"]
388
+ end
389
+
390
+ subgraph Roles ["roles/ + personas/"]
391
+ PRESETS["preset-installer (6 trimmed presets)"]
392
+ TOOLSETS["toolsets (per-role tool faces + write guards)"]
393
+ WK["wiki-worker (W prefetch worker)"]
394
+ end
395
+
396
+ subgraph Wiki ["wiki/"]
397
+ WVC["wiki-vault-client (search/read/write)"]
398
+ end
399
+
400
+ Store <-->|HTTP/SSE| HTTP
401
+ Tab --> Store --> Model
402
+ ROUTES --> KS
403
+ TOOLS --> KS
404
+ HTTP --> KS
405
+ WAKER --> VORCH
406
+ VORCH --> KS
407
+ VORCH --> RUNNER
408
+ RUNNER --> TOOLSETS --> PRESETS
409
+ RUNNER --> WVC
410
+ WK --> WVC
411
+ AUDIT --> KS
412
+ MG --> KS
413
+ KS --> ES --> PJ --> SM --> PM
414
+ EC --> KS
415
+ ```
416
+
417
+ ### Layer responsibilities
418
+
419
+ - **Domain** (`src/domain/`) — the entire business model as pure TypeScript:
420
+ event store, state machines, projection, permission matrix, delivery/review/
421
+ manifest validators, and the `KanbanService` facade that routes every write from
422
+ tools, CLI, and UI through one authority. Extensively unit-tested.
423
+ - **Integration** (`src/tools/`, `src/routes/`) — cordis tools and routes:
424
+ the role tool faces, main-session tools (`kanban_route` + read-only subset), and
425
+ the `/kanban/*` HTTP/SSE bridge.
426
+ - **Dispatcher** (`src/dispatcher/`) — event wake, R20 orchestration, one-shot
427
+ agent runner (persona preset mounting, model candidate chain, ToolGuard
428
+ installation), watchdog, chain auditor, and merge gate.
429
+ - **Roles** (`src/roles/`, `personas/`) — trimmed agent presets installed into
430
+ `$DSH_HOME/.agent-presets/`, per-role tool assembly, and write-guard logic.
431
+ - **Wiki** (`src/wiki/`) — thin HTTP client for wiki-vault.
432
+
433
+ ---
434
+
435
+ ## Development
436
+
437
+ Quality gates (see `AGENTS.md`):
438
+
439
+ ```bash
440
+ npm run typecheck # tsc -p tsconfig.json --noEmit (0 errors)
441
+ npm test # npx vitest run (currently 450 tests / 52 files, all green)
442
+ npm run build # tsc -p tsconfig.build.json + build:client (lib/client.js)
443
+ ```
444
+
445
+ GUI verification (only when a dsh web instance is already running on port 3080;
446
+ do **not** start a second instance):
447
+
448
+ ```bash
449
+ python tests/e2e/gui-check.py --url http://127.0.0.1:3080/
450
+ ```
451
+
452
+ > Deploying to a running DSH instance requires a plugin reload/restart; building
453
+ > alone does not hot-reload the running plugin.
454
+
455
+ ---
456
+
457
+ ## Roadmap & known limitations
458
+
459
+ ### Implemented (v0.1.0)
460
+
461
+ - [x] Event-sourced domain + deterministic state machines (red-team replay)
462
+ - [x] 6-role R20 pipeline with trimmed presets and session-bound permissions
463
+ - [x] Delivery contract + review evidence gates + rework lifecycle
464
+ - [x] TDD hard gate (D `tdd` handoff + DT `test_first` / `runner=vitest` verification)
465
+ - [x] Protocol-violation recovery, heartbeat watchdog, failure circuit
466
+ - [x] Chain completion audit gate (D23) + human confirm
467
+ - [x] Post-DT merge gate (D pushes feature branch only)
468
+ - [x] Phase-0 planning checklist + `file-prefetch` attachment
469
+ - [x] GUI chain/task rename + chain delete (T7, human-only)
470
+ - [x] Model candidate chain with silent fallback + high reasoning effort
471
+ - [x] Live SSE kanban tab (Conversation → Trajectory → Kanban)
472
+
473
+ ### Planned
474
+
475
+ - [ ] Per-task budget guardrails (max tokens / tool calls / wall-clock) and
476
+ failure-classified backoff
477
+ - [ ] Reproducible DT verification (replayed commands + stdout evidence) and
478
+ dual-model arbitration on hard flags
479
+ - [ ] Structured metrics + per-chain audit trace aggregation
480
+ - [ ] V context compaction / state-summary injection + session self-healing
481
+ - [ ] End-to-end contract test harness for multi-agent flows
482
+ - [ ] More human intervention points (before push / on hard flags) and
483
+ system-assisted hard-flag detection
484
+
485
+ ### Known limitations
486
+
487
+ - **Write guards are string-heuristic, not hard isolation.** PT/DT ToolGuards
488
+ rely on path/command regex and reviewers get no git credentials; a soft
489
+ constraint plus audit trail, not a mount-level sandbox.
490
+ - **`open-code-review` CLI was not available** in the verification environment:
491
+ the fallback path (superpowers `code-review`) is implemented and tested, but
492
+ ocr delegation-mode output parsing awaits verification on a machine with ocr.
493
+ - **Review evidence is existence-checked, not replay-proven.** Fields must be
494
+ present and well-formed; proving the tests actually ran is on the Roadmap.
495
+ - **Single default wiki-vault host** in the config default — point
496
+ `wikiVault.baseUrl` at your deployment.
497
+ - **PT creation depends on P's self-reported `pt_decision.needed`** —
498
+ system-assisted detection from repo signals is on the Roadmap.
499
+
500
+ ---
501
+
502
+ ## License
503
+
504
+ [MIT](LICENSE)