dsh-tower 2.0.3 → 2.1.0-beta.1

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 (193) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/COMPATIBILITY.md +38 -0
  3. package/CONTRIBUTING.md +150 -2
  4. package/README.en.md +59 -16
  5. package/README.md +57 -16
  6. package/SECURITY.md +31 -1
  7. package/adapters/default.js +10 -0
  8. package/adapters/store-fs.js +6 -0
  9. package/adapters/vcs-git.js +4 -0
  10. package/app/batch.js +79 -0
  11. package/app/board.js +25 -0
  12. package/app/checkCompletion.js +76 -0
  13. package/app/enrichedMissions.js +26 -0
  14. package/app/findings.js +78 -0
  15. package/app/gate.js +108 -0
  16. package/app/init.js +249 -0
  17. package/app/merge.js +567 -0
  18. package/app/messages.js +66 -0
  19. package/app/mission.js +35 -0
  20. package/app/missionContext.js +14 -0
  21. package/app/plan.js +42 -0
  22. package/app/provision.js +193 -0
  23. package/app/read.js +57 -0
  24. package/app/rebase.js +302 -0
  25. package/app/recording.js +72 -0
  26. package/app/review.js +145 -0
  27. package/app/roster.js +123 -0
  28. package/app/spawn.js +125 -0
  29. package/app/teardown.js +184 -0
  30. package/app/tower.js +524 -0
  31. package/cordis.patch.yml +61 -53
  32. package/docs/README.md +33 -1
  33. package/docs/architecture/overview.md +114 -14
  34. package/docs/board.md +72 -0
  35. package/docs/examples/first-mission.md +2 -1
  36. package/docs/faq.md +11 -7
  37. package/docs/guide/hot-reload.md +1 -1
  38. package/docs/guide/install-upgrade-migration.md +22 -6
  39. package/docs/guide/quickstart.md +12 -7
  40. package/docs/guide/release.md +125 -0
  41. package/docs/guide/scenarios-and-philosophy.md +86 -69
  42. package/docs/operations/security.md +65 -14
  43. package/docs/reference/blockers.md +11 -10
  44. package/docs/reference/config.generated.md +66 -0
  45. package/docs/reference/config.md +11 -7
  46. package/docs/reference/errors.generated.md +39 -0
  47. package/docs/reference/errors.md +10 -2
  48. package/docs/reference/events.md +7 -4
  49. package/docs/reference/testing.md +2 -1
  50. package/docs/reference/tools.generated.md +33 -0
  51. package/docs/reference/tools.md +8 -7
  52. package/docs/zh/README.md +4 -3
  53. package/docs/zh/faq.md +9 -6
  54. package/docs/zh/guide/hot-reload.md +1 -1
  55. package/docs/zh/guide/install-upgrade-migration.md +17 -5
  56. package/docs/zh/guide/quickstart.md +13 -8
  57. package/docs/zh/guide/scenarios-and-philosophy.md +55 -50
  58. package/docs/zh/operations/security.md +43 -7
  59. package/docs/zh/reference/blockers.md +10 -10
  60. package/docs/zh/reference/config.md +11 -7
  61. package/docs/zh/reference/errors.md +4 -1
  62. package/docs/zh/reference/events.md +6 -3
  63. package/docs/zh/reference/testing.md +3 -2
  64. package/docs/zh/reference/tools.md +8 -7
  65. package/domain/collab/batch.js +112 -0
  66. package/domain/collab/messages.js +127 -0
  67. package/domain/collab/roster.js +158 -0
  68. package/domain/collab/spawn.js +188 -0
  69. package/domain/config/schema.js +404 -0
  70. package/domain/contracts/outputs.js +279 -0
  71. package/domain/contracts/tool-inputs/collaboration.js +58 -0
  72. package/domain/contracts/tool-inputs/findings.js +29 -0
  73. package/domain/contracts/tool-inputs/index.js +94 -0
  74. package/domain/contracts/tool-inputs/integration.js +37 -0
  75. package/domain/contracts/tool-inputs/missions.js +94 -0
  76. package/domain/contracts/tool-inputs/ops.js +75 -0
  77. package/domain/contracts/tool-inputs/shared.js +25 -0
  78. package/domain/errors.js +96 -0
  79. package/domain/gate.js +125 -0
  80. package/domain/integration/gate.js +176 -0
  81. package/domain/integration/init.js +225 -0
  82. package/domain/integration/liveness.js +31 -0
  83. package/domain/integration/merge.js +377 -0
  84. package/domain/integration/provision.js +124 -0
  85. package/domain/integration/rebase.js +236 -0
  86. package/domain/integration/review.js +52 -0
  87. package/domain/integration/teardown.js +118 -0
  88. package/domain/mission.js +68 -0
  89. package/domain/missions/completion.js +114 -0
  90. package/domain/missions/findings.js +201 -0
  91. package/domain/missions/lifecycle.js +185 -0
  92. package/domain/missions/plan.js +149 -0
  93. package/domain/missions/review.js +179 -0
  94. package/domain/planning.js +118 -0
  95. package/domain/read/enrichedMissions.js +58 -0
  96. package/domain/read/missionContext.js +53 -0
  97. package/domain/read/views.js +128 -0
  98. package/domain/report.js +59 -0
  99. package/domain/roster.js +40 -0
  100. package/domain/scope.js +105 -0
  101. package/domain/state/schema.js +233 -0
  102. package/domain/state/semantics.js +117 -0
  103. package/domain/state.js +33 -0
  104. package/domain/summary.js +114 -0
  105. package/domain/vocabulary.js +179 -0
  106. package/index.js +86 -34
  107. package/lib/briefing.js +176 -0
  108. package/lib/compatibility.js +108 -0
  109. package/lib/config.js +54 -302
  110. package/lib/doctor.js +108 -9
  111. package/lib/git.js +93 -15
  112. package/lib/hmr.js +4 -3
  113. package/lib/leases.js +3 -2
  114. package/lib/legacy.js +20 -9
  115. package/lib/modeState.js +39 -0
  116. package/lib/modeStore.js +35 -33
  117. package/lib/persistence.js +349 -0
  118. package/lib/presets.js +35 -0
  119. package/lib/spawn-prechecks.js +168 -0
  120. package/lib/spawn-support.js +79 -0
  121. package/lib/store.js +57 -85
  122. package/lib/tests.js +46 -11
  123. package/lib/tool-registry.js +544 -0
  124. package/lib/workspaceLock.js +46 -22
  125. package/lib/worktreeOwnership.js +5 -4
  126. package/locale/en.json +23 -1
  127. package/locale/zh.json +22 -0
  128. package/package.json +33 -4
  129. package/ports/clock.js +6 -0
  130. package/ports/ids.js +7 -0
  131. package/ports/index.js +2 -0
  132. package/ports/store.js +11 -0
  133. package/ports/vcs.js +11 -0
  134. package/scripts/architecture.mjs +94 -0
  135. package/scripts/check-docs.mjs +44 -3
  136. package/scripts/check-pack.mjs +49 -0
  137. package/scripts/domain-metrics.mjs +48 -0
  138. package/scripts/generate-config-reference.mjs +46 -0
  139. package/scripts/generate-error-reference.mjs +29 -0
  140. package/scripts/generate-tools-reference.mjs +56 -0
  141. package/scripts/issue-audit.mjs +259 -0
  142. package/scripts/lib/cli.mjs +76 -0
  143. package/scripts/lib/tmp.mjs +20 -0
  144. package/scripts/mount-smoke/fixtures.mjs +162 -0
  145. package/scripts/mount-smoke/host.json +4 -0
  146. package/scripts/mount-smoke/probe.mjs +125 -0
  147. package/scripts/mount-smoke/surface.mjs +141 -0
  148. package/scripts/mount-smoke.mjs +270 -0
  149. package/scripts/pack-manifest.txt +203 -0
  150. package/scripts/release-check.mjs +303 -0
  151. package/skills/tower-recovery.md +7 -5
  152. package/tools/adopt.js +72 -0
  153. package/tools/batch.js +78 -0
  154. package/tools/complete.js +53 -0
  155. package/tools/diff.js +39 -0
  156. package/tools/doctor.js +50 -0
  157. package/tools/finding.js +40 -0
  158. package/tools/findings.js +39 -0
  159. package/tools/import-legacy.js +42 -0
  160. package/tools/inbox.js +39 -0
  161. package/tools/index.js +39 -0
  162. package/tools/init.js +63 -0
  163. package/tools/interrupt.js +42 -0
  164. package/tools/legacy-map.js +44 -0
  165. package/tools/merge.js +39 -0
  166. package/tools/mission.js +41 -0
  167. package/tools/plan.js +39 -0
  168. package/tools/preflight.js +44 -0
  169. package/tools/rebase.js +39 -0
  170. package/tools/recover.js +45 -0
  171. package/tools/reload.js +52 -0
  172. package/tools/resource.js +54 -0
  173. package/tools/review.js +45 -0
  174. package/tools/roster.js +60 -0
  175. package/tools/send.js +147 -0
  176. package/tools/spawn.js +196 -0
  177. package/tools/status.js +94 -0
  178. package/tools/teardown.js +42 -0
  179. package/tools/test.js +61 -0
  180. package/types/cordis.d.ts +6 -0
  181. package/types/index.d.ts +339 -0
  182. package/ui-bundle/README.md +111 -0
  183. package/ui-bundle/client.js +1980 -0
  184. package/ui-bundle/cordis.patch.yml +3 -0
  185. package/ui-bundle/icon.svg +6 -0
  186. package/ui-bundle/index.js +2 -0
  187. package/ui-bundle/locale/en.json +6 -0
  188. package/ui-bundle/locale/zh.json +6 -0
  189. package/ui-bundle/package.json +35 -0
  190. package/lib/errors.js +0 -111
  191. package/lib/tools.js +0 -2250
  192. package/lib/tower.js +0 -2689
  193. /package/{lib → domain}/batch.js +0 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,147 @@
2
2
 
3
3
  > 自 2.0.0 起,每个版本按 **新增 / 变更 / 修复** 三类组织;更早的版本保留原始流水式记录。
4
4
 
5
+ ## 未发布(Unreleased)
6
+
7
+ ## 2.1.0-beta.1(2026-10-22)
8
+
9
+ **预发布**:本版本发布到 npm 的 `next` dist-tag,`latest` 继续保持 `2.0.4`,直到出现稳定版本。
10
+ 从 2.0.4 直接跳到 2.1.0-beta.1,是为了让 #94 的交付闸门(mount smoke、发布闸门、证据约定)有一个
11
+ 可验证的预发布载体;从 2.0.4 升级不需要改动配置。
12
+
13
+ ### 新增
14
+
15
+ - **#95 mount smoke**:新增 `npm run smoke:mount`——打包本包、把 `scripts/mount-smoke/host.json` 中
16
+ 唯一 pin 的 host 装进一次性目录、创建一次性 `DSH_HOME` 与 profile、用真实 loader 启动,并断言
17
+ **加载后的真实 surface**(29 个 `tower_*` 工具 id、3 个 skill、2 个 preset、`/tower` 命令,以及恰好
18
+ 一个顶层 `tower-bundle` 行,#66 不变量);CI 增加 `mount` job(Node 22,5 分钟预算),
19
+ `tests/mount-smoke.test.mjs` 锁定 oracle,并保持 host pin 等于 `COMPATIBILITY.md` 最新行。
20
+ - **#95 覆盖率可见化**:CI 每次 push 运行 `npm run test:coverage` 并输出覆盖率表(只接线:无阈值、
21
+ 无 baseline、无徽章);`CONTRIBUTING.md` 写明 CI 能证明什么、不能证明什么。
22
+ - **#97 发布闸门**:新增 `npm run release:check`(`scripts/release-check.mjs`)。每次运行都打印上一个
23
+ tag、`git describe`、自该 tag 起的提交数与 Unreleased 是否有内容;PR/分支 push 只警告(exit 0),
24
+ tag push 在版本没有前进、tag 不是 `v<version>`、或 CHANGELOG 没有该版本内容时失败(exit 1)。
25
+ CI 在每次 push 与 tag push 上运行它。
26
+ - **#97 发布清单**:`docs/guide/release.md` 把发布收敛为六步(版本 → CHANGELOG → COMPATIBILITY →
27
+ `npm publish --tag next` → 注释 tag → `gh release create`),含里程碑 P0/P1 规则、CHANGELOG 段落的
28
+ bash/PowerShell 提取命令,以及 `--verify-tag`、`--prerelease --latest=false` 与“先发布后打 tag”的
29
+ 原因。
30
+ - **#96 离线 issue 审计**:新增 `npm run audit:issues`(`scripts/issue-audit.mjs`),读取本地
31
+ `tracker/issues.json` 导出,检查已关闭 P0/P1 的 `verified-by:`/`commit:` 两行契约,全程不访问
32
+ GitHub;`--since` 只看约定日期之后的关闭,`tracker/` 是 gitignored 的本地快照。
33
+ - **#97 追踪看板**:新增 `docs/board.md`,把 owner 每周要问的三个问题写成可执行的 `gh` 命令,并记录
34
+ 标签/搜索语义(`--label P0,P1` 匹配单个字面标签,`--search 'label:P0,P1'` 才是 OR)。
35
+
36
+ ### 变更
37
+
38
+ - **#96 受保护 `main` 与证据约定**:`main` 只能经 PR 合入(管理员同样受限),必需检查与“two-key
39
+ 路径 + 四行 PR body”写进 `CONTRIBUTING.md`;P0/P1 关闭必须带 `verified-by:` 与 `commit:` 两行。
40
+ “某些路径允许直推”的例外被删除,因为 GitHub 的分支保护无法表达它——留一条服务器不会执行的规则
41
+ 比没有规则更糟。
42
+ - **#94/#97 追踪策略**:优先级只存在于 `P0`–`P3` 标签、领域在 `area/*`,issue 标题不再带 `[P*]`
43
+ 前缀;milestone 仍然只表示发布范围(一个 release 一个 milestone)。
44
+ - **README 徽章**:中英文 README 增加 “Listed on dsh-plugin.org” 徽章,链接到插件市场条目。
45
+ - **#66 all-or-nothing bundle**:`cordis.patch.yml` 改为单个 `tower-bundle` `cordis:group`,Host、
46
+ skills 与 worker/reviewer preset 成为内部 section;子行不再可独立启停,避免 host 关闭后的 orphan。
47
+ - **#64 preset 前置校验**:模式进入与 `tower_plan` 前解析 worker/reviewer preset;缺失时以稳定
48
+ `preset-missing` 拒绝并点名 `preset-tower-worker` / `preset-tower-reviewer`,不再先规划后死锁。
49
+ - **#65 可选能力开关**:`tools.exposeBatch/exposeImport/exposeLeases/exposeTests/exposeRecovery/exposeDoctor`
50
+ 可独立关闭;`gates.requireTests:true` 与 tests/tools 关闭的组合在配置解析时直接拒绝。
51
+ - **#67 组件文案**:host locale 增加 `components.{tower-bundle,tower-skills,preset-*}` 的
52
+ title/summary/disables 中英文案;预设仍为内部 section,不再作为独立开关暴露。
53
+ - **#52 继续拆分 tools**:review/preflight/merge-rebase-teardown 拆为 per-tool 模块;
54
+ `lib/tools.js` 降至约 503 行。
55
+ - **#51 two-phase 试点**:`tower_complete` 改为锁外 prepare + 锁内 revision 复核;`submitReview`
56
+ 同样拆为锁外准备(actor/worktree/tip/round)与锁内 revision 复核;新增
57
+ `lock-holds.jsonl` 遥测与 doctor p50/p95 检查(>1s warn)。
58
+ - **#51 git 互斥基础**:新增 `withGitLock` / `git.lock`,用与 state lock 相同的 token/PID-dead
59
+ 回收协议串行化 git 操作,为把 git 移出 state lock 做准备。
60
+ - **#51 merge/rebase/teardown**:三者改为 prepare/act/commit;长 git 操作在 `git.lock` 下进行,
61
+ state lock 只做 revision 复核与写回;rebase 增加可对账的 `rebaseIntent`。
62
+ - **#51 init/provision/merge reconcile**:init/provision 同样锁外 prepare + `git.lock` act +
63
+ 短 state lock 复核;merge 的 intent/landed 对账改为锁外 git 观察 + revision CAS,任何
64
+ state lock 内不再启动 git。
65
+ - **#51 init/provision**:`tower_init` 与 worktree provision 同样改为锁外 prepare + `git.lock`
66
+ 下 act + 短 state lock 复核;provision 失败/并发变更时回滚 worktree 与 branch。
67
+ - **#68 owner 恢复提示**:`towerAgentId` 缺失时写工具现在返回可操作错误
68
+ “run tower_init to re-claim”,`tower_status` 暴露 `towerAgentId/ownerMissing`,doctor 增加
69
+ `tower-owner` 检查;`tower_init` 重新绑定后恢复写权限。
70
+ - **#52 Tower 拆分**:`lib/tower.js` 从约 2.9k 行降到 **535** 行;plan/mission/checkCompletion/
71
+ missionContext/enrichedMissions/board/spawn/recording 拆为 `lib/tower/*.js` use-case 模块,
72
+ `Tower` 方法仅作委托,行为、错误码与锁顺序不变。
73
+ - **#50 I/O 边界**:`node:fs` 仅保留在 `lib/adapters/store-fs.js`,`node:child_process` 仅保留在
74
+ `lib/adapters/vcs-git.js`;其余 lib 模块经 adapter 导入,`package.json files` 补齐 adapters;
75
+ 新增架构回归锁定该不变量。
76
+ - **#50 依赖注入**:新增 `lib/adapters/default.js` 组合根;`Tower` 构造器接受
77
+ `deps.store/vcs/clock/ids`,类内不再直接调用 git/store 模块;回归证明注入 adapter 生效。
78
+ - **#56 前置工作**:架构 fitness 规则按实际 `lib/adapters` 边界修正并全部升级为 error
79
+ (domain-pure / child-process / fs / ambient-state / aggregate-size / tools-thin / tools-vocabulary),
80
+ baseline 零违规;`docs/architecture/overview.md` 更新为实际分层、state/modes 契约和剩余差距。
81
+ - **#45 路线 A:TypeBox 单一 schema 源**:经调研 Schemastery 的 `toJSON()` 只序列化自身结构、不输出 JSON Schema,因此选用 TypeBox。29 个工具输入 schema 改为 `Type.Object(...)`;`define` 泛型从 schema 推导 `execute(args)` 类型;`tsc` 覆盖全部 tool 模块并加入 required/enum 负向 fixture。运行时注册前 JSON 化,wire schema 与迁移前逐项一致。
82
+ - **#50 test doubles**:`tests/support/memory-store.js` 与 `scripted-vcs.js` 提供无磁盘/无 git 的
83
+ Store/Vcs 契约;新增完整 scripted lifecycle 与 10 项 adapter contract 测试。
84
+ - **#45 domain 类型**:`lib/domain/*.js` 纳入 checkJs,新增 `tests/type-contracts.ts` 正/负类型
85
+ fixture;types 契约与 domain 签名对齐。
86
+ - **packaging**:`package.json files` 补齐 `lib/tower/*.js`,避免发布包遗漏 use-case 模块。
87
+ - **#45 错误结构化继续**:纯 domain、review/findings/read/batch/rebase、`lib/tower.js` 与 infra
88
+ (store/config/git/tests)均改为抛出带 code 的 `TowerToolError`;`classifyError` 的 26 条
89
+ prose `RULES` 表已删除,纯文本错误统一落到 `tool-internal`。
90
+ - **#54 mode 锁等待**:`mode.lockTimeoutMs`(默认 5000ms)统一 modes.lock 的有界等待与死锁回收语义。
91
+ - **#59 兼容性记录**:新增随包发布 `COMPATIBILITY.md`,doctor 增加 `host-version` 检查(verified /
92
+ untested-newer / untested-older / unknown,仅警告)。
93
+ - **#61 共存冲突**:README 增加 “Known conflicts”,doctor 增加 `loader-conflicts` 检查。
94
+ - **#58 仓库产物排除**:`docs.boardLocation:"repo"` 与仓库内 `worktrees.root` 自动写入
95
+ `.git/info/exclude`(幂等,绝不改 `.gitignore`);新增 `init.autoExclude`。
96
+ - **#44 模式进入审计**:`tower_status` 增加 `mode.lastChange`/`mode.history`;新增
97
+ `mode.requireHumanAckOnFirstEntry` 与 `/tower ack`;安全文档补充模型主动进入的威胁模型。
98
+ - **#43 默认值提醒**:doctor 的 `default-reversals` 检查提示仍依赖旧默认值的 profile。
99
+ - **#60 提供商冷却**:spawn 在连续可重试失败后进入进程内冷却并收缩预算;
100
+ `tower_status.spawn` 暴露 `{inflight, budget, blockedUntil}`。
101
+ - **#48 发布文件守卫**:新增 `scripts/pack-manifest.txt`、`npm run pack:check` 与 CI 步骤。
102
+ - **#62 工具面预算**:`tools.exposeOperational:false` 可跳过 batch/resource/import/legacy/recover/test/doctor 工具。
103
+ - **#47 live 守卫**:`tests/live` 需要显式 `DSH_LIVE=1` 与隔离的 `DSH_HOME`,不再可能误跑真实 profile。
104
+ - **#63**:doctor 不再把正常 live worker 视为 warn。
105
+
106
+ ## 2.0.4(2026-10-21)
107
+
108
+ ### 变更
109
+
110
+ - **文档索引补齐**:根 README 的“文档”区补上《什么时候用 Tower》链接;`docs/README.md` 新增
111
+ “All pages”完整页面索引(指南 / 参考 / 运维与背景),与中文文档索引对齐。
112
+ - **全量文档与产品一致性审查(功能/哲学)**:把 `review.requireReview` 默认 `false`(审查可选)
113
+ 同步到快速开始、示例、FAQ、blocker/测试/架构/配置/迁移说明与中英文页面;修正测试门禁描述为
114
+ 当前分支 tip(而非“被审查 tip”);修正单 mission 审查豁免需要
115
+ `gates.allowReviewExemption:true`;补齐 2.0.1–2.0.3 升级说明;README 的“何时使用/工作流对比”
116
+ 不再把独立审查写成默认必经步骤。
117
+ - **模型可见工具描述同步**:`tower_plan` 的 `reviewPolicy`、`tower_complete` 的完成语义、
118
+ `tower_preflight` 的 review blocker 条件,以及 reviewer 错误文案不再暗示“完成即等待审查”。
119
+ - **模式控制默认改为 Agent 可操作**:`mode.allowAgentControl` 默认 `true`,`tower_control` 默认注册,
120
+ 模型可带理由进出 Tower 模式;设 `false` 可恢复仅人类控制。同步删除文档中的“human-owned/人类掌握”
121
+ 表述,改为“可配置、显式且可归因”;模式提示、工具参考、安全模型与 UI 说明一并更新。
122
+
123
+ ### 修复
124
+
125
+ - **安全审计 2026-10-21(#13)**:授权解析改为 fail-closed(未登记/无法解析的调用者不再被视为
126
+ tower);`tower_merge` 只合并 gate 校验过的 SHA 并在 gate→merge 间重新校验 base/tip;
127
+ 已落地对账要求分支 tip 真正包含在 base;reviewer 身份与 spawn tip 缺失均拒绝;完成时拒绝 dirty
128
+ worktree;spawn 先预留 roster 再启动并在失败时回收;provision/teardown 与状态写入增加回滚/归属
129
+ 校验;state/tower 锁改为可安全回收死 PID 并避免删除活跃锁;Git 子进程剥离 `GIT_*` 重定向,
130
+ 测试发现使用最小环境;lease status 不再回显 token;仓库级配置只能收紧、不能放宽 `mode.*`;
131
+ 恢复 skill 删除破坏性 rm 指引。
132
+ - **GitHub Issues #14–#42 的回归项**:stale roster 可恢复;dirty completion、adopt 重复观测、
133
+ spawn 失败、配置嵌套未知键、doctor 坏配置、teardown 分支删除失败、batch receipt 文件校验、
134
+ mode/control/reload 事件、Git 超时配置、不可变 SHA merge、测试 receipt 体积上限等。
135
+ - **模式状态未知时显式提示**:UI 在无法从已加载历史证明模式时显示 `mode unknown` chip,
136
+ 不再静默伪装为关闭;同一 session 还会持久化最近已知模式(7 天过期,仅展示,不单独驱动 denied
137
+ 行);新增未知调用者对 `tower_mission` / `tower_test` / finding 工具的身份拒绝。
138
+ - **`tower_recover`**:新增恢复诊断/显式 stale state-lock 清理工具;只移动 PID 已死的锁到备份,
139
+ 需要 `confirm:"CLEAR_STALE_LOCK"`,绝不重置 state/worktree。
140
+ - **#30 工具层显式错误码**:tools 层的校验/前置条件不再依赖文本 regex,改为直接抛出带稳定 code 的
141
+ `TowerToolError`(invalid-argument / reviewer-prerequisite / spawn-unavailable / preset-missing /
142
+ not-authorized 等)。
143
+ - **文档与实现不一致**:`gates.requireTests` 文档原写“reviewed tip”,实现校验当前 mission 分支
144
+ tip;已同步英文/中文配置、测试与 blocker 参考。
145
+
5
146
  ## 2.0.3(2026-10-18)
6
147
 
7
148
  ### 新增
@@ -0,0 +1,38 @@
1
+ # Compatibility
2
+
3
+ Tower records the DeepSeek Harness host build each release was actually verified against. This file
4
+ is shipped in the npm package; doctor reads it locally and never contacts a registry.
5
+
6
+ | dsh version | tag / commit | verified on | what was run |
7
+ |---|---|---|---|
8
+ | 0.2.1-rc.2 | v2.1.0-beta.1 | 2026-10-22 | mount smoke (29 tower tools, `/tower`, 3 skills, 2 presets, single bundle row), `npm test` (413), `npm run docs:check`, generated docs checks |
9
+ | 0.2.1-rc.2 | 6ee19de | 2026-10-21 | `npm test` (229), `npm run docs:check`, offline `ui-bundle` tests |
10
+ | 0.2.1-rc.2 | 5b0accd | 2026-10-18 | `npm test` (219), `npm run docs:check` |
11
+
12
+ ## How to read this
13
+
14
+ - **verified**: exact host version match; the row names the date and what ran.
15
+ - **untested-newer**: newer than every row. Normally safe to try; include the host version and the
16
+ latest row in a bug report.
17
+ - **untested-older**: older than every row; upgrade the host or expect missing APIs.
18
+ - **unknown**: the host did not expose a parseable version.
19
+
20
+ `tower_doctor` reports this as a warning only: a newer host is the normal case and must not block
21
+ Tower work. This is provenance, not a feature-detection mechanism.
22
+
23
+ ## Release process
24
+
25
+ Add a row in the release PR, before tagging, after running the offline suite and docs check on the
26
+ host build being shipped. Name the release tag in cell 1 and the host build in cell 0; the other
27
+ columns are read positionally by `lib/compatibility.js`, so do not insert or reorder them. Do not
28
+ claim a live run that was not performed. The 2.1.0-beta.1 row is a pre-release: it becomes
29
+ installable on the `next` dist-tag only after the checklist's publish step, and `latest` stays on
30
+ 2.0.4 until a stable release. The full six-step procedure is the
31
+ [release checklist](docs/guide/release.md).
32
+
33
+ ## Pack provenance
34
+
35
+ Releases are currently published locally with npm 2FA. `publishConfig.provenance` is intentionally
36
+ not enabled because provenance requires a CI OIDC publisher; enabling it locally would fail the
37
+ release. The reviewed `scripts/pack-manifest.txt` and CI `pack:check` guard the published file list.
38
+ Move publishing to GitHub Actions before enabling provenance.
package/CONTRIBUTING.md CHANGED
@@ -2,6 +2,59 @@
2
2
 
3
3
  Thanks for your interest in `dsh-tower`. This is a DeepSeek Harness plugin; the repository root is the bundle itself (`package.json` + `cordis.patch.yml`).
4
4
 
5
+ ## Landing changes
6
+
7
+ `main` is protected. Every change lands through a pull request; direct pushes to `main` are rejected,
8
+ including the maintainer's, because protection is enforced for admins. To merge, a PR must pass these
9
+ required checks:
10
+
11
+ | Required context | What it proves |
12
+ |---|---|
13
+ | `test (22)` | offline suite, docs/pack/architecture/types/domain checks on Node 22 |
14
+ | `test (24)` | the same jobs on Node 24 |
15
+ | `mount` | the packed tarball mounts in a throwaway profile and registers its surface (`npm run smoke:mount`) |
16
+
17
+ The context names come from the workflow job id and matrix values. Renaming a job or a matrix value
18
+ changes the required context; update the protection setting in the same PR, or the check never reports
19
+ and every PR stays unmergeable. The setting and its verification command are in
20
+ [SECURITY.md](SECURITY.md#repository-protection-on-main). The `mount-fixtures` job runs the seeded
21
+ smoke failures for evidence but is deliberately **not** a required context.
22
+
23
+ There are no mandatory approvals, no CODEOWNERS gate, and **merging your own PR is allowed**: there is
24
+ one maintainer, and a mandatory self-approval would be theatre. What replaces the second reviewer is a
25
+ written contract.
26
+
27
+ ### Two-key paths and the four-line PR body
28
+
29
+ "Two-key" here means two independent keys of evidence — the written failure mode and the named test,
30
+ on top of the green required checks — not two approvals. A change touching any of these paths carries a
31
+ four-line body, in this order:
32
+
33
+ - `domain/**`
34
+ - `app/**`
35
+ - `lib/store.js`, `lib/persistence.js`, `lib/*lock*.js`
36
+ - `scripts/architecture.mjs`
37
+ - `cordis.patch.yml`
38
+
39
+ ```markdown
40
+ Failure mode: <what breaks for a user if this is wrong>
41
+ Change: <what changed, one sentence>
42
+ Test: <test file and case that fails without this change>
43
+ Blast radius: <repositories, state files or tools that can be affected>
44
+ ```
45
+
46
+ Those paths decide whether a user's repository is modified or a gate can be bypassed. Every other path
47
+ still lands as a PR, but a one-line description is enough. The four lines are evidence, not ceremony:
48
+ if the "Test" line cannot name a file, the change needs one first.
49
+
50
+ ### Direct push
51
+
52
+ There is none. Before the delivery gate (#94 / #96), `main` accepted direct pushes, and #77 proposed
53
+ the two-key rule as a path-scoped exception. GitHub branch protection cannot express "direct push
54
+ allowed for these paths only", so the exception was dropped instead of documented as a rule the server
55
+ would not enforce. An emergency bypass is not a workflow: repairing `main` means changing the setting
56
+ deliberately and recording why, not silently pushing.
57
+
5
58
  ## Development setup
6
59
 
7
60
  ```bash
@@ -21,15 +74,110 @@ Requirements: Node.js 22+, Git. No package installation is needed for the offlin
21
74
 
22
75
  ## Pull request checklist
23
76
 
24
- 1. `npm test` passes locally (206 tests today, including protocol end-to-end against a real temporary Git repository and mock spawn coverage) and `npm run docs:check` passes.
77
+ 1. `npm test` passes locally (416 tests today, including protocol end-to-end against a real temporary Git repository and mock spawn coverage) and `npm run docs:check` passes.
25
78
  2. New behavior has a test. Bug fixes should add a test that fails before the fix.
26
79
  3. No secrets, tokens, personal paths, session logs, or generated artifacts are added. `.gitignore` covers local acceptance output; do not force-add it.
27
80
  4. Keep the public API and state-file formats compatible; if a change is breaking, call it out in `CHANGELOG.md` and bump the version.
28
81
  5. Update `README.md` / `README.en.md` when the user-visible behavior changes.
82
+ 6. If you touched `cordis.patch.yml`, `index.js`, `skills.js`, or `tools/`, run `npm run smoke:mount` (see below).
83
+ 7. A commit carries `Refs #n` or `Fixes #n`; a P0/P1 closure carries `verified-by:` and `commit:`, and `npm run audit:issues` reports no new failures.
84
+
85
+ ## Mount smoke
86
+
87
+ The offline suite never loads a host, so it cannot see a bundle that fails to mount. The smoke
88
+ packs this package, installs the pinned host into a temp directory, adds the tarball through
89
+ `dsh plugin`, and boots the throwaway profile through the real loader:
90
+
91
+ ```bash
92
+ npm run smoke:mount # asserts the loaded surface; under two minutes
93
+ npm run smoke:fixtures # proves the smoke still fails for the right seeded reasons
94
+ ```
95
+
96
+ It needs network access the first time (the host install) and `pnpm` on `PATH` (the plugin CLI's
97
+ package manager). `scripts/mount-smoke/host.json` is the single host build pin: bump it and the
98
+ newest `COMPATIBILITY.md` row together — a unit test fails when the two disagree. The fixtures
99
+ rename one tool id and remove the `tower-skills` row to prove the red run names the cause rather
100
+ than only counting rows; CI runs them in the `mount-fixtures` job, sharing one cached host install
101
+ through `--host-dir`.
102
+
103
+ Coverage is reported by `npm run test:coverage` in CI. It is a signal, not a gate: no threshold,
104
+ no baseline, no badge.
105
+
106
+ ## Releasing
107
+
108
+ A release is six steps: bump → changelog → COMPATIBILITY row → publish → annotated tag → GitHub
109
+ Release. The one-page procedure with exact commands is [docs/guide/release.md](docs/guide/release.md).
110
+ It carries the two rules that are easy to get wrong: **publish before tagging** (or delete the tag if
111
+ publish fails), and a release **may not be tagged while its milestone holds an open P0/P1** unless the
112
+ release notes say why. `npm publish` is the only human-only step (2FA).
113
+
114
+ `npm run release:check` prints `git describe`, the previous tag, the commits since it and whether
115
+ Unreleased is non-empty on every run. It warns on a PR and fails on a tag push whose
116
+ `package.json` version did not move past the last tag or whose tag is not `v<version>`.
117
+
118
+ ## Tracker board
119
+
120
+ Priority lives in the `P0`–`P3` labels; open titles carry no `[P*]` prefix. The saved queries — open
121
+ P1, unmilestoned P0/P1, closed this week by area, and the milestone check that blocks a release — are
122
+ in [docs/board.md](docs/board.md).
123
+
124
+ ## Commits, closures and evidence
125
+
126
+ ### Trailers
127
+
128
+ Put the issue trailer on its own line at the end of the commit message:
129
+
130
+ - `Refs #n` — the commit touches issue n but does not complete it.
131
+ - `Fixes #n` — the commit completes the issue; GitHub closes it when the change reaches `main`.
132
+
133
+ One trailer per issue, and exactly one of the two per commit. `Fixes` is not decoration for work that a
134
+ later commit still has to finish.
135
+
136
+ ### The two-line closure contract
137
+
138
+ A closure on a P0/P1 is a pointer, not prose. The closing comment starts with these two lines:
139
+
140
+ ```text
141
+ verified-by: tests/<file>.test.mjs
142
+ commit: <sha>
143
+ ```
144
+
145
+ - `verified-by:` names a test file that exists in the repository (under `tests/`, or named `*.test.*` /
146
+ `*.spec.*`). A script or a doc is not a test.
147
+ - `commit:` names the commit that landed the change.
148
+
149
+ Prose may follow. A closure without the two lines is treated as unverified, and the audit below fails
150
+ on it.
151
+
152
+ ### Checking closures offline
153
+
154
+ The audit reads a local export; it never calls GitHub, so it cannot pass because of a network outage or
155
+ a token. Produce the export once (network), then audit it offline:
156
+
157
+ ```powershell
158
+ # 1) Export (PowerShell; no jq needed)
159
+ New-Item -ItemType Directory -Force tracker | Out-Null
160
+ $all = foreach ($n in (gh issue list --repo louifinn/dsh-tower --state all --limit 500 --json number --jq '.[].number')) {
161
+ gh issue view $n --repo louifinn/dsh-tower --json number,title,state,labels,closedAt,body,comments | ConvertFrom-Json
162
+ }
163
+ @{ issues = @($all) } | ConvertTo-Json -Depth 8 | Set-Content tracker/issues.json -Encoding utf8
164
+
165
+ # 2) Audit (offline)
166
+ npm run audit:issues # every closed P0/P1 in tracker/issues.json
167
+ npm run audit:issues -- --since 2026-10-22 # only closures on/after the convention date
168
+ npm run audit:issues -- --tracker path/to/export.json --json
169
+ ```
170
+
171
+ The bash equivalent of step 1 is the same `gh` calls piped through `jq -s '{issues: .}'`.
172
+
173
+ Exit codes: `0` — every checked closure names an existing test file (a missing `commit:` is a warning);
174
+ `1` — at least one pointer is missing, nonexistent, not a test, or outside the repository; `2` — the
175
+ export is missing or malformed. `--strict` promotes the missing-`commit:` warning to an error.
176
+ `tracker/` is a local snapshot and is gitignored; regenerate it instead of committing it.
29
177
 
30
178
  ## Commit style
31
179
 
32
- Conventional-commit prefixes are preferred: `feat:`, `fix:`, `docs:`, `test:`, `refactor:`, `chore:`. Keep the subject line in the imperative mood.
180
+ Conventional-commit prefixes are preferred: `feat:`, `fix:`, `docs:`, `test:`, `refactor:`, `chore:`. Keep the subject line in the imperative mood. Add the issue trailer (`Refs #n` / `Fixes #n`) described above.
33
181
 
34
182
  ## Reporting issues
35
183
 
package/README.en.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![CI](https://github.com/louifinn/dsh-tower/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/louifinn/dsh-tower/actions/workflows/test.yml)
4
4
  [![npm](https://img.shields.io/npm/v/dsh-tower.svg)](https://www.npmjs.com/package/dsh-tower)
5
+ [![Listed on dsh-plugin.org](https://dsh-plugin.org/badges/listed.svg)](https://dsh-plugin.org/plugins/louifinn/dsh-tower)
5
6
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
7
  [中文说明](README.md)
7
8
 
@@ -13,9 +14,9 @@ The repository root **is** the DSH bundle: `package.json` declares `dsh.bundle.p
13
14
 
14
15
  ## Features
15
16
 
16
- - **Human-owned Tower mode.** `/tower` toggles a session-level stance that narrows the tool surface and injects the Tower protocol. `/tower on|off <instruction>` also forwards your instruction to the model in the same step.
17
+ - **Configurable Tower mode.** `/tower` toggles a session-level stance that narrows the tool surface and injects the Tower protocol. `/tower on|off <instruction>` also forwards your instruction to the model in the same step. By default the model may also enter/leave with `tower_control` and a concrete reason; set `mode.allowAgentControl:false` for human-only control.
17
18
  - **Missions with real isolation.** Every mission gets its own branch and Git worktree; workers never touch your checkout.
18
- - **Review-gated merges.** A reviewer bound to the current branch tip can submit a `clean` / `merge` verdict; the merge gate re-checks scope, staleness, task completion, review history, and worktree ownership before a `--no-ff` merge. Independent review is optional by default; set `review.requireReview:true` to enforce it globally (or use `reviewPolicy:"none"` + reason to exempt a single mission when it is on).
19
+ - **Review-gated merges.** When `review.requireReview:true`, a reviewer bound to the current branch tip must submit a `clean` / `merge` verdict before merge; the merge gate re-checks scope, staleness, task completion, review history, and worktree ownership before a `--no-ff` merge. Independent review is optional by default (the gate is off); use `reviewPolicy:"none"` + reason plus `gates.allowReviewExemption:true` for a single mission when the global gate is on.
19
20
  - **Durable coordination.** Roster, inbox, findings, reviews, and mission state are persisted under `.git/dsh-tower/`, so a restart does not lose the plan.
20
21
  - **Safe by default.** While Tower mode is active, `create_goal`, `update_goal(resume)`, `todo_write`, the Team task board, and `spawn_teammate` are refused; a paused goal is never resumed automatically.
21
22
  - **Traceable finding lifecycle.** `tower_findings` lists, reads, and moves findings through `open → triaged → fixed` / `wontfix` (and can reopen); only the tower or a worker may change state, while reviewers can only read.
@@ -33,7 +34,7 @@ Tower is a **delivery protocol for changes to a Git repository**, not the defaul
33
34
 
34
35
  - a feature or refactor splits into 2–8 missions with clear scopes that can be reviewed independently;
35
36
  - ordering matters (migrations, dependencies, branch freshness, scope reservations);
36
- - the repository is sensitive or release-bound, and you need a reviewed commit, test evidence, and an auditable merge;
37
+ - the repository is sensitive or release-bound, and you need an auditable merge plus test evidence (enable independent review when required);
37
38
  - work spans context compaction, restarts, or multiple days and must be recoverable from local state;
38
39
  - "done" means tasks + tests (and optionally an independent review), not just a confident message in chat.
39
40
 
@@ -51,7 +52,7 @@ Tower mode refuses Team/Workflow/Ralph/ordinary subagent tools and clears plan m
51
52
 
52
53
  ## Requirements
53
54
 
54
- - DeepSeek Harness `0.2.1-rc.2` (or a compatible newer build)
55
+ - DeepSeek Harness `0.2.1-rc.2` (see [COMPATIBILITY.md](COMPATIBILITY.md) for the verified host builds)
55
56
  - Node.js 22 or newer
56
57
  - Git (worktrees are created locally; no network access is required)
57
58
 
@@ -78,6 +79,17 @@ dsh plugin --profile <profile> add <absolute path to this repository>
78
79
 
79
80
  The Host plugin is loaded when the DSH process starts, so **restart DSH after installing or updating**. Remove it with `remove_bundle` and the package name `dsh-tower`.
80
81
 
82
+ The optional Web UI ships inside the npm package at `ui-bundle/`: for an npm install the target is
83
+ `<profile>/node_modules/dsh-tower/ui-bundle`, or `ui-bundle/` in a source checkout. Install it with
84
+ `plugin_manager({action:"install_bundle", target:"<that directory>"})`, restart DSH, then refresh the page.
85
+
86
+ ### Known conflicts
87
+
88
+ Do not install `dsh-plugins-tower` (third-party) alongside this package. Its loader row also uses
89
+ `name: dsh-tower`, so it resolves to this package: this package may load twice or the other package
90
+ never runs. `tower_doctor` reports this as `loader-conflicts`; uninstall the conflicting package and
91
+ restart DSH.
92
+
81
93
  ## Quick start
82
94
 
83
95
  ```text
@@ -87,16 +99,17 @@ tower_init repoRoot=D:\work\repo # records the base (current bra
87
99
  tower_plan repoRoot=D:\work\repo missions=[{"title":"Feature A","scope":["src/a/**"],"tasks":["implement","commit"],"kind":"build"}]
88
100
  tower_spawn repoRoot=D:\work\repo missionId=M1 role=worker
89
101
  # the worker implements and commits in its worktree, calls tower_complete; its final report returns here
102
+ # Optional: enable review.requireReview:true first if you need mandatory independent review
90
103
  tower_spawn repoRoot=D:\work\repo missionId=M1 role=reviewer
91
104
  # the reviewer inspects read-only and calls tower_review (clean+merge, or p1-Nitems/p2-Nitems)
92
- tower_preflight repoRoot=D:\work\repo missionId=M1 # expect no-review before review; ok:true after it
105
+ tower_preflight repoRoot=D:\work\repo missionId=M1 # review optional by default; with review.requireReview expect no-review before review and ok:true after
93
106
  tower_merge repoRoot=D:\work\repo missionId=M1
94
107
  tower_teardown repoRoot=D:\work\repo
95
108
 
96
109
  /tower off
97
110
  ```
98
111
 
99
- A small change usually takes one worker mission and one reviewer round. A full copy-paste example is in [docs/examples/first-mission.md](docs/examples/first-mission.md); per-tool parameters are in [docs/reference/tools.md](docs/reference/tools.md).
112
+ A small change usually takes one worker mission; add one reviewer round when independent review is required. A full copy-paste example is in [docs/examples/first-mission.md](docs/examples/first-mission.md); per-tool parameters are in [docs/reference/tools.md](docs/reference/tools.md).
100
113
 
101
114
  ## Commands
102
115
 
@@ -107,10 +120,23 @@ A small change usually takes one worker mission and one reviewer round. A full c
107
120
  | `/tower on <instruction>` | Enter/stay **and** forward `<instruction>` as the next user message |
108
121
  | `/tower off` | Leave Tower mode |
109
122
  | `/tower off <instruction>` | Leave **and** forward `<instruction>` |
123
+ | `/tower ack` | Record human acknowledgement for `mode.requireHumanAckOnFirstEntry` |
110
124
  | `/tower <instruction>` | Ensure on, then forward `<instruction>` |
111
125
 
112
126
  Image and file attachments are forwarded together with the instruction as one complete `UserMessage`.
113
127
 
128
+ ## Component model
129
+
130
+ This bundle is **all-or-nothing**: `cordis.patch.yml` inserts one `tower-bundle`
131
+ `cordis:group` row, and the Host plugin, skills, and worker/reviewer presets are internal sections
132
+ of it. The plugin page should treat that group as one component; disabling it disables all sections
133
+ together, so no orphan preset or skill points at missing `tower_*` tools. Do not enable/disable the
134
+ child rows independently.
135
+
136
+ Mode entry and `tower_plan` validate both presets before work starts. A missing preset is refused
137
+ with `preset-missing` and names `preset-tower-worker` / `preset-tower-reviewer`, instead of planning
138
+ or spawning first and dead-ending at review/merge.
139
+
114
140
  ## Configuration
115
141
 
116
142
  ```yaml
@@ -124,6 +150,7 @@ Image and file attachments are forwarded together with the instruction as one co
124
150
  autoPauseGoal: true
125
151
  denyTools: [create_goal, todo_write, team_task_create, team_task_update, team_task_reopen, spawn_teammate]
126
152
  review:
153
+ requireReview: false # review optional by default; set true to enforce independent review
127
154
  maxRoundsPerReviewer: 5
128
155
  merge:
129
156
  timeoutMs: 900000
@@ -135,21 +162,21 @@ Image and file attachments are forwarded together with the instruction as one co
135
162
  maxDepth: 1
136
163
  ```
137
164
 
138
- - `mode.allowAgentControl` defaults to `false` in code; the bundled `cordis.patch.yml` enables it (`true`) so the model may enter/leave when the user intent or task state warrants it, always stating a reason. The bundled patch also enables `mode.workspaceLock` (single-active tower), `send.interruptDefault`, `init.autoGitInit`, and sets `spawn.staleMs` to 15s.
165
+ - `mode.allowAgentControl` defaults to `true` in code; the bundled `cordis.patch.yml` also sets it explicitly to `true` so the model may enter/leave when the user intent or task state warrants it, always stating a reason. Set it to `false` for human-only control. The bundled patch also enables `mode.workspaceLock` (single-active tower), `send.interruptDefault`, `init.autoGitInit`, and sets `spawn.staleMs` to 15s.
139
166
  - `/tower base <branch>` records a session-level init base preference; `/tower on` clears an active plan mode (`mode.planModePolicy:"refuse"` instead refuses entry), keeping plan and Tower mutually exclusive.
140
167
  - Repository-level config: put the same JSON shape in `<git-common-dir>/dsh-tower/config.json` (usually `.git/dsh-tower/config.json`) to override `review`/`merge`/`worktrees`/`spawn`. Precedence is code defaults < profile config < repository config < tool arguments. `mode.*` is session-scoped and only the profile layer controls it.
141
168
  - Every key and default is in [docs/reference/config.md](docs/reference/config.md); unknown top-level keys are rejected so typos cannot silently disable security-relevant settings, while unknown keys inside a group are ignored. Tool error codes are in [docs/reference/errors.md](docs/reference/errors.md).
142
169
 
143
- ## Tools (26 core)
170
+ ## Tools (27 core)
144
171
 
145
- `tower_init` · `tower_plan` · `tower_mission` · `tower_status` · `tower_spawn` · `tower_roster` · `tower_adopt` · `tower_diff` · `tower_send` · `tower_inbox` · `tower_complete` · `tower_interrupt` · `tower_finding` · `tower_findings` · `tower_review` · `tower_preflight` · `tower_merge` · `tower_rebase` · `tower_teardown` · `tower_mode` · `tower_doctor` · `tower_test` · `tower_batch` · `tower_resource` · `tower_import_legacy` · `tower_legacy_map`
172
+ `tower_init` · `tower_plan` · `tower_mission` · `tower_status` · `tower_spawn` · `tower_roster` · `tower_adopt` · `tower_diff` · `tower_send` · `tower_inbox` · `tower_complete` · `tower_interrupt` · `tower_finding` · `tower_findings` · `tower_review` · `tower_preflight` · `tower_merge` · `tower_rebase` · `tower_teardown` · `tower_mode` · `tower_doctor` · `tower_recover` · `tower_test` · `tower_batch` · `tower_resource` · `tower_import_legacy` · `tower_legacy_map`
146
173
 
147
- `tower_control` exists only when `allowAgentControl: true`; `tower_reload` is registered while `dev.hotReload` is on (default true) and returns `reload-unavailable` without an HMR service (so up to 28 tools). Mission-scoped tools also accept `id` as an alias for `missionId`; snake/camel spellings such as `task_done`/`taskDone` are both accepted.
174
+ `tower_control` is registered by default (`allowAgentControl:true`); set it to `false` to disable it. `tower_reload` is registered while `dev.hotReload` is on (default true) and returns `reload-unavailable` without an HMR service (so up to 29 tools). Mission-scoped tools also accept `id` as an alias for `missionId`; snake/camel spellings such as `task_done`/`taskDone` are both accepted.
148
175
 
149
176
  ## Safety and guardrails
150
177
 
151
178
  - **No merge without a green gate.** `tower_preflight` is read-only and reports stable blocker codes; `tower_merge` refuses when any blocker remains.
152
- - **Review is bound to a commit.** A moved tip reports `tip-moved` and requires a fresh review; reviewers use a read-only preset, and a branch change during review is rejected.
179
+ - **Review is bound to a commit when enabled.** With `review.requireReview:true`, a moved tip reports `tip-moved` and requires a fresh review; reviewers use a read-only preset, and a branch change during review is rejected.
153
180
  - **Scope enforcement.** A mission may only change files matched by its declared scope globs; `out-of-scope` blocks the merge.
154
181
  - **Worktree ownership.** Dirty worktrees, a running worker, or a branch owned by a closed mission block teardown/merge.
155
182
  - **Goal isolation.** Entering Tower mode pauses the active goal and records it; leaving does not resume it. Use `/tower off` and resume the goal yourself.
@@ -173,14 +200,14 @@ Workers use the bundled `tower-worker` preset (writable worktree surface); revie
173
200
 
174
201
  | Path | Contents |
175
202
  |---|---|
176
- | `package.json`, `cordis.patch.yml` | bundle manifest and loader patch (inserts the Host plugin and the `tower-worker` preset) |
203
+ | `package.json`, `cordis.patch.yml` | bundle manifest and loader patch (one `tower-bundle` group containing the Host, skills, and both presets) |
177
204
  | `index.js`, `lib/`, `worker.js`, `reviewer.js` | Host entry, protocol/config implementation, worker and reviewer preset policies |
178
205
  | `skills.js`, `skills/` | bundled agent skills (worker protocol, review checklist, recovery playbook) |
179
206
  | `docs/` | guides, reference, architecture, operations, FAQ and examples |
180
207
  | `locale/`, `icon.svg` | localization and icon |
181
208
  | `tests/` | pure-function, protocol end-to-end, and plugin-level tests; optional isolated Web-runtime scripts |
182
209
  | `ui-bundle/` | optional Client widget (Tower tool cards, persistent Tower-mode chip, mode-aware denied rows, dock summary, transient overlay strip), not installed by default |
183
- | `.github/workflows/test.yml` | CI running the offline suite on Node 22/24 |
210
+ | `.github/workflows/test.yml` | CI running the offline suite on Node 22/24, the release and coverage checks, and the Node 22 mount smoke and seeded fixtures |
184
211
 
185
212
  ## Development and testing
186
213
 
@@ -194,10 +221,26 @@ Hot-reload development: `npm run dev:hmr` adds this checkout to the current prof
194
221
 
195
222
  The offline suite needs no DSH process and creates throwaway Git repositories in the OS temp directory. `tests/README.md` documents the isolated Web-runtime checks, model-driven acceptance, and the session-diagnostic scripts used while hardening the plugin.
196
223
 
224
+ ## What CI proves
225
+
226
+ Every push and PR runs the same set of jobs; each job backs one checkable claim instead of a README promise.
227
+
228
+ | Job | Claim | Evidence |
229
+ |---|---|---|
230
+ | `test` (Node 22/24) | the offline suite, documentation consistency, generated references, the published file list, architecture, types, and domain purity all hold | `npm test`, `npm run docs:check`, the three `docs:check:generated*` checks, `pack:check`, `architecture:check`, `types:check`, `domain:check` |
231
+ | `test`'s `Coverage (Node 22)` step | coverage is actually measured in CI (reported only: no threshold, no badge) | `npm run test:coverage` |
232
+ | `test`'s `Release check` step (Node 22) | a tag is a claim: the version moved past the previous version tag, a pushed tag is `v<version>`, and the changelog carries release content | `npm run release:check` |
233
+ | `mount` (Node 22) | the packed tarball, added through the plugin CLI to a throwaway profile, mounts in the real loader and registers the surface the bundle claims | `npm run smoke:mount` |
234
+ | `mount-fixtures` (Node 22, not required) | the mount smoke still goes red for the right seeded reasons (a renamed tool id, a removed skills section), with one cached host install shared by all runs | `npm run smoke:fixtures -- --host-dir <cached host>` |
235
+
236
+ `mount` asserts the throwaway profile's **loaded surface**, never the text of `cordis.patch.yml`: 29 `tower_*` tools (27 `tools/*.js` modules plus `tower_mode` / `tower_control`), the 3 bundled skills, both the `tower-worker` and `tower-reviewer` presets, the `/tower` command, and exactly one top-level `tower-bundle` group row (the #66 invariant). It calls no model and opens no browser. Measured on the maintainer's Windows machine: about 100 seconds on a cold host (install included) and about 85 seconds with a cached host; the CI step allows 3 minutes (`timeout-minutes: 3`) inside the 5-minute `mount` job. A red run prints the resolved profile path, the host build, the loaded counts, and every unmet claim.
237
+
238
+ The host build is pinned in exactly one place, `scripts/mount-smoke/host.json`; a unit test keeps the newest `COMPATIBILITY.md` row equal to it, so a release never retypes it. `npm run smoke:fixtures` proves the smoke still fails for the right seeded reasons (a renamed tool id, a removed skills section). CI runs it as the non-required `mount-fixtures` job, where the control and both seeds share one cached host install through `--host-dir` (about 2 minutes warm here).
239
+
197
240
  ## Documentation
198
241
 
199
242
  - [docs/README.md](docs/README.md) — documentation index (new user / operator / maintainer paths)
200
- - [Chinese docs](docs/zh/README.md) · [Quick start](docs/guide/quickstart.md) · [Install, upgrade and migration](docs/guide/install-upgrade-migration.md)
243
+ - [Chinese docs](docs/zh/README.md) · [Quick start](docs/guide/quickstart.md) · [When to use Tower](docs/guide/scenarios-and-philosophy.md) · [Install, upgrade and migration](docs/guide/install-upgrade-migration.md)
201
244
  - [Tools reference](docs/reference/tools.md) · [Config reference](docs/reference/config.md) · [Blockers and failure codes](docs/reference/blockers.md) · [Error codes](docs/reference/errors.md) · [Events](docs/reference/events.md) · [Testing](docs/reference/testing.md) · [Batches/leases/legacy](docs/reference/operations.md) · [Doctor](docs/reference/doctor.md) · [Hot reload](docs/guide/hot-reload.md)
202
245
  - [Architecture and state machine](docs/architecture/overview.md) · [Operations runbook](docs/operations/runbook.md) · [Security model](docs/operations/security.md)
203
246
  - [FAQ](docs/faq.md) · [Examples](docs/examples/first-mission.md)
@@ -205,7 +248,7 @@ The offline suite needs no DSH process and creates throwaway Git repositories in
205
248
  ## Troubleshooting
206
249
 
207
250
  - **The plugin does not appear after install/update.** Host plugins load at DSH startup; restart DSH. Confirm the entry point targets this repository root (the folder with `package.json` and `cordis.patch.yml`), not a subdirectory.
208
- - **`tower_control` is missing.** The config `allowAgentControl` is `false`; set it to `true` and restart.
251
+ - **`tower_control` is missing.** The config sets `allowAgentControl:false`; set it back to `true` and restart.
209
252
  - **Merge is blocked.** Run `tower_preflight` and read the blocker codes above; `stale-base` means run `tower_rebase` first.
210
253
  - **A mission branch is stale after base moved.** `tower_rebase` rebases inside the worktree; a clean conflict-free rebase waives re-review for the previous verdict.
211
254
  - **A merge commit exists but `tower_merge` reported a failure.** Re-run `tower_merge`: it detects the landed merge commit (or a branch already contained in the base) and records it as reconciled instead of merging twice.
@@ -217,7 +260,7 @@ Issues and pull requests are welcome. Please run `npm test` before opening a PR;
217
260
 
218
261
  ## Acknowledgements
219
262
 
220
- - **Kimi Code (Moonshot AI)** — the Tower mode concept, including human-owned activation, an isolated Git worktree per mission, and the review-gated merge protocol, is inspired by the experimental Tower mode in [Kimi Code](https://www.kimi.com/code) ([source](https://github.com/MoonshotAI/kimi-code)). This project re-implements those ideas on top of DeepSeek Harness services; it contains no Kimi Code source code and is not affiliated with or endorsed by Moonshot AI.
263
+ - **Kimi Code (Moonshot AI)** — the Tower mode concept, including an explicit session-level mode switch, an isolated Git worktree per mission, and the review-gated merge protocol, is inspired by the experimental Tower mode in [Kimi Code](https://www.kimi.com/code) ([source](https://github.com/MoonshotAI/kimi-code)). This project re-implements those ideas on top of DeepSeek Harness services; it contains no Kimi Code source code and is not affiliated with or endorsed by Moonshot AI.
221
264
  - **DeepSeek Harness** — for the plugin, tool, session, and subagent APIs this project is built on.
222
265
 
223
266
  ## License