dsh-tower 1.2.0 → 2.0.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 (61) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/CONTRIBUTING.md +36 -0
  3. package/README.en.md +60 -23
  4. package/README.md +54 -21
  5. package/SECURITY.md +20 -0
  6. package/cordis.patch.yml +32 -0
  7. package/docs/README.md +17 -0
  8. package/docs/architecture/overview.md +70 -0
  9. package/docs/examples/first-mission.md +58 -0
  10. package/docs/faq.md +74 -0
  11. package/docs/guide/hot-reload.md +189 -0
  12. package/docs/guide/install-upgrade-migration.md +59 -0
  13. package/docs/guide/quickstart.md +97 -0
  14. package/docs/operations/runbook.md +80 -0
  15. package/docs/operations/security.md +62 -0
  16. package/docs/reference/blockers.md +58 -0
  17. package/docs/reference/config.md +114 -0
  18. package/docs/reference/doctor.md +31 -0
  19. package/docs/reference/errors.md +50 -0
  20. package/docs/reference/events.md +45 -0
  21. package/docs/reference/operations.md +52 -0
  22. package/docs/reference/testing.md +61 -0
  23. package/docs/reference/tools.md +75 -0
  24. package/docs/zh/README.md +58 -0
  25. package/docs/zh/faq.md +72 -0
  26. package/docs/zh/guide/hot-reload.md +178 -0
  27. package/docs/zh/guide/install-upgrade-migration.md +57 -0
  28. package/docs/zh/guide/quickstart.md +96 -0
  29. package/docs/zh/operations/security.md +42 -0
  30. package/docs/zh/reference/blockers.md +62 -0
  31. package/docs/zh/reference/config.md +113 -0
  32. package/docs/zh/reference/doctor.md +30 -0
  33. package/docs/zh/reference/errors.md +52 -0
  34. package/docs/zh/reference/events.md +46 -0
  35. package/docs/zh/reference/operations.md +48 -0
  36. package/docs/zh/reference/testing.md +60 -0
  37. package/docs/zh/reference/tools.md +75 -0
  38. package/index.js +293 -49
  39. package/lib/batch.js +578 -0
  40. package/lib/config.js +381 -0
  41. package/lib/doctor.js +165 -0
  42. package/lib/errors.js +111 -0
  43. package/lib/hmr.js +196 -0
  44. package/lib/leases.js +317 -0
  45. package/lib/legacy.js +413 -0
  46. package/lib/modeStore.js +47 -16
  47. package/lib/store.js +24 -1
  48. package/lib/tests.js +628 -0
  49. package/lib/tools.js +1594 -160
  50. package/lib/tower.js +1025 -101
  51. package/lib/workspaceLock.js +117 -0
  52. package/lib/worktreeOwnership.js +166 -0
  53. package/package.json +21 -4
  54. package/reviewer.js +40 -0
  55. package/scripts/check-docs.mjs +135 -0
  56. package/scripts/enable-dev-hmr.mjs +347 -0
  57. package/skills/tower-recovery.md +40 -0
  58. package/skills/tower-review.md +16 -0
  59. package/skills/tower-worker-protocol.md +15 -0
  60. package/skills.js +63 -0
  61. package/worker.js +7 -4
@@ -0,0 +1,46 @@
1
+ # Tower 事件
2
+
3
+ [English](../../reference/events.md) | **简体中文**
4
+
5
+ Tower 在工具成功调用(变更与只读)后发出进程内 Cordis 事件,其他插件因此可以观察仓库,而不必轮询。
6
+ 事件**不会**通过网络发送,也不包含遥测。
7
+
8
+ ```js
9
+ ctx.on('tower/merge', (payload) => {
10
+ // payload.tool, payload.repoRoot, payload.value (the tool's JSON result)
11
+ })
12
+ ```
13
+
14
+ | 事件 | 触发时机 |
15
+ |---|---|
16
+ | `tower/init` | `tower_init` 创建或重新锚定 state 之后 |
17
+ | `tower/plan` | `tower_plan` 创建 mission 之后 |
18
+ | `tower/mission` | `tower_mission` 修改单个 mission 之后 |
19
+ | `tower/spawn` | `tower_spawn` 记录 worker/reviewer 之后 |
20
+ | `tower/complete` | `tower_complete` 记录完成报告之后 |
21
+ | `tower/review` | `tower_review` 记录审查结论之后 |
22
+ | `tower/merge` | `tower_merge` 返回之后(包括门禁失败;检查 `value.ok`) |
23
+ | `tower/rebase` | `tower_rebase` 返回之后 |
24
+ | `tower/teardown` | `tower_teardown` 移除 worktree 之后 |
25
+ | `tower/finding` | `tower_finding` 创建单条 finding 之后 |
26
+ | `tower/findings` | `tower_findings` 读取/列出/更新 finding 之后 |
27
+ | `tower/send` | `tower_send` 持久化消息之后 |
28
+ | `tower/inbox` | `tower_inbox` 确认消息之后 |
29
+ | `tower/interrupt` | `tower_interrupt` 停止运行中的 agent 之后 |
30
+ | `tower/diff` | `tower_diff` 读取 mission diff 之后 |
31
+ | `tower/adopt` | `tower_adopt` 对账 roster 之后 |
32
+ | `tower/roster` | `tower_roster` 读取 roster 之后 |
33
+ | `tower/status` | `tower_status` 读取面板之后 |
34
+ | `tower/preflight` | `tower_preflight` 评估门禁之后 |
35
+ | `tower/doctor` | `tower_doctor` 完成只读检查之后 |
36
+ | `tower/test` | `tower_test` 运行测试发现之后 |
37
+ | `tower/batch` | `tower_batch` 创建、查看或关闭 batch 之后 |
38
+ | `tower/resource` | `tower_resource` 获取/释放/查看租约之后 |
39
+ | `tower/import_legacy` | `tower_import_legacy` 保存旧版快照之后 |
40
+ | `tower/legacy_map` | `tower_legacy_map` 映射或列出旧 id 之后 |
41
+
42
+ `tower_mode`、`tower_control` 与 `tower_reload` 不发出 Tower 事件。payload 是 JSON:
43
+ `{tool, repoRoot, value}`。监听器抛错不会影响工具结果。
44
+
45
+ 需要可持久审计的调用链时,请使用 `<git-common-dir>/dsh-tower/activity.jsonl` 与 `MISSIONS.md`;
46
+ 事件是进程内的,不会重放。
@@ -0,0 +1,48 @@
1
+ # 批次、租约与旧版导入
2
+
3
+ [English](../../reference/operations.md) | **简体中文**
4
+
5
+ ## 证据批次
6
+
7
+ `tower_batch` 把有限的 mission 完成证据分组。
8
+
9
+ - `create` 冻结 `missionIds`、`exclusions`、`requiredChecks`(默认 `["check"]`)与精确的相对
10
+ `receiptPaths`。成员必须是已知 mission,且只有暂停的旧版 mission 可以被排除;创建后成员关系不可变。
11
+ - `status` 汇总成员与待处理/失败的检查。
12
+ - `close` 校验已完成成员的每条必需检查(来自 `completionReport.checks`、`testRuns` 或 close 时的
13
+ `checks`),在提供 `treeTip` 时校验它,并要求每个冻结的回执路径都在
14
+ `receiptHashes`/`receipts` 中提供(或通过 `receiptHash` 绑定到通过的检查)。缺少证据会返回
15
+ `{ok:false, blockers}`;不会删除任何内容。
16
+
17
+ close 失败不是部分关闭:batch 保持 `open`,修复证据后可以重试。
18
+
19
+ ## 咨询式租约
20
+
21
+ `tower_resource` 管理每个仓库的租约,位于 `<git-common-dir>/dsh-tower/leases.json`:
22
+
23
+ ```text
24
+ tower_resource({action:"acquire", name:"heavy-ci", ttlMs:900000})
25
+ tower_resource({action:"status"})
26
+ tower_resource({action:"release", name:"heavy-ci", token:"<token from acquire>"})
27
+ ```
28
+
29
+ - acquire 仅限控制塔;调用方会得到 token、pid、holder、创建/过期时间戳;
30
+ - 同一个 token 可以续租;另一个持有者会得到 `[tower:lease-busy]`;
31
+ - release 需要 token;**已过期**的租约仍需要 token 与显式 `force:true`(不会静默接管);
32
+ - `spawn.requireLease` 要求持有该租约,否则 worker spawn 失败。
33
+
34
+ 租约用于协调本地任务与 CI 窗口。它们**不是** OS CPU/内存硬上限:DSH 没有公开的 Job Object 配额
35
+ API,Tower 无法强制每进程资源限制;这类限制请使用 live-worker 上限以及你的 OS/CI 调度器。
36
+
37
+ ## 旧版导入与映射
38
+
39
+ `tower_import_legacy({repoRoot,sourcePath})` 接受 Tower Codex `schemaVersion:1` state 文件或 DSH
40
+ 旧版 state。它会:
41
+
42
+ - 要求绝对源路径,且位于 `<git-common-dir>/dsh-tower` 之外;
43
+ - 校验 schema(上限 16 MiB),把原始 payload 逐字节保存到
44
+ `<git-common-dir>/dsh-tower/legacy/<sha256>.json`,并在 `state.legacySnapshots` 中只记录摘要;
45
+ - 绝不激活、接管或恢复暂停的 mission。
46
+
47
+ `tower_legacy_map` 记录仅审计用途的 `sourceHash + oldId → newMissionId?` 映射。映射只追加;把已有
48
+ 映射改写到另一个 mission 会被拒绝。映射不会改变 mission 状态。
@@ -0,0 +1,60 @@
1
+ # 测试、发现与证据
2
+
3
+ [English](../../reference/testing.md) | **简体中文**
4
+
5
+ Tower 2.1 可以执行仓库声明的测试发现,并把结果变成持久的完成证据。发现是**按配置选择性启用**的:
6
+ worktree 没有 `package.json.tower.testDiscovery` 块时,行为与之前完全一致。
7
+
8
+ ## 配置
9
+
10
+ `package.json`(与 Tower Codex 兼容):
11
+
12
+ ```json
13
+ {
14
+ "tower": {
15
+ "testDiscovery": {
16
+ "version": 1,
17
+ "commands": [
18
+ {
19
+ "id": "unit",
20
+ "command": "node --test dist/tests/*.test.js",
21
+ "format": "node-tap",
22
+ "minCount": 1,
23
+ "required": ["smoke"]
24
+ }
25
+ ]
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ | 字段 | 含义 |
32
+ |---|---|
33
+ | `id` | 唯一,`[A-Za-z0-9][A-Za-z0-9._-]{0,63}`;也用于诊断文件名。 |
34
+ | `command` | 以 worktree 为 cwd 执行的 shell 命令(Windows 用 `pwsh`,POSIX 用 `bash`)。 |
35
+ | `format` | `node-tap`(TAP 的 `ok N - name` / `not ok`)或 `json-array`(`{name, ok?}` 的 JSON 数组)。 |
36
+ | `minCount` | 该命令至少应发现的测试数(默认 1)。 |
37
+ | `required` | 必须出现且通过的测试名。 |
38
+
39
+ Tower 配置中的 `tests.*` 控制准入、超时、输出上限以及是否允许 package discovery。
40
+ `tests.admission` 取 `spawn`(worker 启动前运行;fail-closed,不启动子代理)、`complete`
41
+ (在 `tower_complete` 时运行;默认)或 `off`(只在显式 `tower_test` 时运行)。
42
+
43
+ ## 回执与门禁
44
+
45
+ 每次运行都会在 `<git-common-dir>/dsh-tower/test-runs/<mission>-<stamp>/` 下保存回执,包含
46
+ stdout/stderr 文件、SHA-256 哈希、退出码、计数、required 名称、worktree tip 以及总体 `ok`。
47
+ `state.missions[].testRuns` 保留最近的 50 条回执。失败时工具返回带诊断的 `ok:false`;自动发现失败时
48
+ `tower_complete` 会抛出 `[tower:tests-failed]`。
49
+
50
+ - `tower_test({repoRoot,missionId})` 按需运行发现并记录回执。
51
+ - `tests.autoChecks` 开启时,`tower_complete` 会添加一条 `tests` 检查。
52
+ - `gates.requireChecks` 要求任意完成检查;`gates.requireTests` 要求针对被审查 tip 的通过回执
53
+ (`tests-missing`)。
54
+
55
+ ## 信任边界
56
+
57
+ 发现命令是仓库控制的代码。`tests.allowPackageDiscovery:false` 会禁用 package 块;CI 或策略 profile
58
+ 在克隆不受信任仓库时应设置它。命令以与 DSH 相同的 OS 权限、在 mission worktree 中运行,Tower 不会
59
+ 为它增加网络访问。输出上限为 `tests.maxBytes`(默认 16 MiB),每条命令受 `tests.timeoutMs`
60
+ (默认 60 秒)限制;超出预算的运行 fail closed。
@@ -0,0 +1,75 @@
1
+ # 工具参考
2
+
3
+ [English](../../reference/tools.md) | **简体中文**
4
+
5
+ 所有工具都接受 `repoRoot`(目标 Git 仓库内的绝对路径)并返回 JSON。
6
+ 本构建最多 28 个工具:下列 19 个核心仓库工具(含 `tower_interrupt`)、`tower_mode`、
7
+ `mode.allowAgentControl` 开启时注册的 `tower_control`、`dev.hotReload` 开启时注册的
8
+ `tower_reload`,以及 2.1 的验证/运维工具 `tower_doctor`、`tower_test`、`tower_batch`、
9
+ `tower_resource`、`tower_import_legacy`、`tower_legacy_map`。
10
+
11
+ `tower_mission` 使用 `id`(别名 `missionId`);其他所有 mission 级工具使用 `missionId`
12
+ (别名 `id`)。其他输入别名:`task_done`/`taskDone`、`task_drop`/`taskDrop`、
13
+ `task_drop_reason`/`taskDropReason`、`clear_blockers`/`clearBlockers`、
14
+ `include_read`/`includeRead`、`dry_run`/`dryRun`、`timeoutMs`/`timeout_ms`、
15
+ `waitMs`/`wait_ms`、finding 上的 `mission_id`/`missionId`、interrupt 上的 `agent_id`/`agentId`、
16
+ status 上的 `include_gate`/`includeGate`、adopt 上的 `retire_missing`/`retireMissing`。
17
+ 输出始终使用规范的 camelCase 字段名。
18
+
19
+ ## 角色
20
+
21
+ roster 角色由 `exec.agent` 与 roster 共同决定。mission 级工具(`tower_interrupt`、
22
+ `tower_init`、`tower_plan`、`tower_spawn`、`tower_merge`、`tower_rebase`、`tower_teardown`、
23
+ `tower_adopt`、`tower_reload`)**仅限控制塔**。`tower_batch` 的 create/close、`tower_resource`
24
+ 的 acquire/release、`tower_import_legacy`、`tower_legacy_map` 的 map 以及 review-exemption 规划
25
+ 仅限控制塔;它们的只读 status 动作对 roster 角色开放。worker 调用 `tower_mission` 时只能使用
26
+ `task_done`/`task_drop`/`blocker`/`note`;状态变更、扩大 scope、`clear_blockers` 与 `completed`
27
+ 仅限控制塔(完成必须走 `tower_complete`)。roster 中的 reviewer 可以读取 finding,但不能修改。
28
+ 违规会抛出 `[tower:not-authorized]` 或 `[tower:invalid-transition]`。
29
+
30
+ ## 工具
31
+
32
+ | 工具 | 参数 | 用途 / 说明 |
33
+ |---|---|---|
34
+ | `tower_init` | `base?`、`autoGitInit?`、`snapshotDirty?`、`reanchor?` | 创建 `<git-common-dir>/dsh-tower/state.json`。`autoGitInit` 执行 `git init` 并创建空的初始提交;`snapshotDirty` 记录一个游离快照提交作为 mission 基线;`reanchor` 仅在没有非终态 mission 时修改基线。返回 `dirty`、`snapshotCommit`、`reanchored`。 |
35
+ | `tower_plan` | `missions[]`(`title`、`scope[]`、`tasks[]?`、`deps[]?`、`kind?`、`context?`、`references[]?` ≤20、`reviewPolicy?`、`reviewExemptionReason?`) | 创建 mission `M1..`;重叠的 build scope 会自动加依赖;拒绝未知依赖与依赖循环。`reviewPolicy:"none"` 需要理由,并且要求 `gates.allowReviewExemption`。 |
36
+ | `tower_mission` | `id`(别名 `missionId`)、`status?`、`task_done?`、`task_drop?`+`task_drop_reason?`、`blocker?`、`clear_blockers?`、`note?`、`scope?`、`pause_cause?`、`authorization?` | 更新单个 mission。worker 可安全使用的字段是 task/blocker/note;不能在这里设置 `merged`。暂停的 mission 记录 `pause_cause`(user/failure/resource/dependency);恢复用户暂停的 mission 会遵循 `mode.requireResumeAuthorization`。 |
37
+ | `tower_status` | `missionId?`、`offset?`、`limit?`、`includeGate?`、`format?` | 默认返回面板。`missionId` 返回完整详情(任务/备注/审查/finding/完成报告)。`offset`/`limit` 对 mission 分页(默认 20,最大 100),`includeGate` 附带门禁摘要,`format:"markdown"` 返回看板。 |
38
+ | `tower_spawn` | `missionId`(别名 `id`)、`role`(`worker`/`reviewer`)、`name?`、`instructions?`、`worktreeRoot?`、`wait?`、`waitMs?`、`takeover?`、`includeDirtyBase?` | 准备或复用 worktree 并启动子代理。worker 使用 `tower-worker`;reviewer 要求 mission 为 `completed`,并使用只读的 `tower-reviewer`。`takeover:true` 替换已不再运行的 worker 条目(ownerGeneration 递增)。每个 mission 只能有一个活跃 worker;`spawn.maxLiveWorkers` 限制整个仓库。 |
39
+ | `tower_roster` | — | roster 及来自子代理目录的实时活动(`running`/`inactive`/`missing`/`unknown`)与 `stale` 标记。只读。 |
40
+ | `tower_adopt` | `retireMissing?` | 把 roster 条目与持久子代理目录对账。可证明已消失的 agent 会被标记为 retired(默认),或标记为 `missing`(`retireMissing:false`)以便检查/接管。 |
41
+ | `tower_diff` | `missionId`(别名 `id`)、`maxBytes?` | 只读 merge-base diff:文件、提交、有上限的 patch。供 reviewer 安全使用。 |
42
+ | `tower_send` | `to`、`subject`、`body`、`scope?`、`interrupt?`、`consentRef?` | 向 roster 名称、`tower` 或 `all` 发送持久消息;对运行中的 agent 会即时投递。`interrupt:true` 先中断运行中的回合(Kimi 风格)再投递;`consentRef` 记录用于审计。受 `send.maxPerMinute` 限速。 |
43
+ | `tower_inbox` | `limit?`、`include_read?` | 读取并确认发给调用者的消息;未读消息会阻止 `tower_complete`。 |
44
+ | `tower_complete` | `missionId`(别名 `id`)、`report`、`checks?` | worker 报告完成(mission 变为 `completed`,等待审查)。要求当前 owner、无未完成任务/blocker、≥1 个提交、无未读消息。`checks` 记录 `{name, command?, exitCode?, evidence?}` 证据;`gates.requireChecks` 可以强制要求。配置了测试发现时还会自动运行并添加 `tests` 检查;失败会抛出 `[tower:tests-failed]`。 |
45
+ | `tower_interrupt` | `to?`(roster 名称)、`agentId?`、`reason?` | 通过 `subagents.interrupt` 中断一个运行中的 roster agent,并记录 `state:"interrupted"`。幂等(未运行 → `interrupted:false`)。 |
46
+ | `tower_finding` | `type`、`title`、`summary`、`details`、`suggested_fix`、`severity?`、`missionId?`、`location?` | 记录 scope 之外的问题;reviewer 用它代替直接改文件。新 finding 初始为 `open`。 |
47
+ | `tower_findings` | `id?`、`status?`、`note?`、过滤(`type?`、`severity?`、`missionId?`、`open?`、`limit?`) | 生命周期工具:列出/读取/流转。状态为 `open → triaged → fixed`/`wontfix`,允许重新打开。只有控制塔或 worker 能修改;reviewer 只能列出/读取。 |
48
+ | `tower_review` | `missionId`(别名 `id`)、`status`(`clean`/`p1-Nitems`/`p2-Nitems`)、`merge`(`merge`/`fix-then-merge`/`hold`)、`findings`、`decision`、`checks?`、`breakGlassReason?` | 绑定当前 tip 的 reviewer 结论。reviewer 必须是获派的 reviewer(或由控制塔使用 break-glass)。 |
49
+ | `tower_preflight` | `missionId`(别名 `id`) | 只读门禁:返回 `{ok, blockers[], branchTip, baseTip, changedFiles, review}`。 |
50
+ | `tower_merge` | `missionId`(别名 `id`)、`timeoutMs?`(别名 `timeout_ms`) | 门禁全绿后合并。若合并提交已落地则记录(`reconciled`);若命令失败但提交已存在则恢复(`recoveredFromFailure`);区分 `merge-hook-failed` 与 `merge-conflict`。 |
51
+ | `tower_rebase` | `missionId`(别名 `id`) | 在 worktree 内把分支 rebase 到基线。worker 运行中或 worktree 脏时拒绝。分支已包含在基线中时返回 `alreadyMerged` 且保持分支不动。 |
52
+ | `tower_teardown` | `force?`、`exclude?`、`dry_run?`(别名 `dryRun`) | 移除符合条件的 merged/abandoned worktree 与分支;运行中的 agent 始终保留。 |
53
+ | `tower_mode` | — | 报告模式、最近修改、被暂停的 goal、被拒绝的工具、store 路径、插件版本、HMR 可用性、workspace 锁持有者。 |
54
+ | `tower_control` | `action`(`enter`/`exit`)、`reason` | 模型主动切换模式;仅当 `mode.allowAgentControl` 为 `true` 时可用;`reason` 必填。 |
55
+ | `tower_reload` | `repoRoot?` | 仅开发用:请求 DSH HMR 对账 profile 并从磁盘重载 Host 插件。`dev.hotReload` 开启时注册;在工具结果返回后调度(默认 50 ms);没有 HMR 时以 `reload-unavailable` 失败。传入 `repoRoot` 时调用者必须是控制塔。 |
56
+ | `tower_doctor` | —(需要 `repoRoot`) | 只读健康契约:git、state schema、config、preset/subagents/HMR、workspace 锁、roster、worktree realpath/归属、测试配置、leases。返回 `{ok, checks[{id,status,detail}], environmentContract}`。从不写状态。 |
57
+ | `tower_test` | `missionId`(别名 `id`) | 运行 worktree 的 `package.json` 中 `tower.testDiscovery` 配置的发现命令并记录回执;失败时 `ok:false`。自动准入检查请用 `tower_complete`。 |
58
+ | `tower_batch` | `action`(`create`/`status`/`close`)、`missionIds?`、`exclusions?`、`requiredChecks?`、`receiptPaths?`、`batchId?`、`checks?`、`treeTip?`、`receiptHashes?` | 有限证据批次。create 冻结成员/检查/回执;close 校验每条必需检查与回执哈希。成员关系不可变;close 从不删除 mission。 |
59
+ | `tower_resource` | `action`(`acquire`/`release`/`status`)、`name?`、`holder?`、`token?`、`ttlMs?`、`force?` | 每个仓库的咨询式租约,位于 `<common>/dsh-tower/leases.json`。没有 OS CPU/内存硬上限;过期的租约需要显式 `force` 与正确 token。 |
60
+ | `tower_import_legacy` | `sourcePath` | 只读导入 Tower Codex `schemaVersion:1` 或 DSH 旧版 state。完整 payload 存到 `<common>/dsh-tower/legacy/<sha256>.json`;state 中只保存摘要。绝不激活/接管/恢复暂停的 mission。 |
61
+ | `tower_legacy_map` | `action`(`map`/`status`)、`sourceHash?`、`oldId?`、`newMissionId?` | 仅审计用途的旧 id 映射;映射只追加,绝不容纳 mission。 |
62
+
63
+ ## workspace 锁
64
+
65
+ 当 `mode.workspaceLock:true` 时(随包 patch 开启),Tower 模式会话的第一次仓库级 Tower 工具调用会
66
+ 获取 `<git-common-dir>/dsh-tower/tower.lock`;另一个会话会得到 `[tower:workspace-busy]`,直到持有
67
+ 者执行 `/tower off` 或锁超过 `mode.workspaceStaleMs`。`mode.workspaceLockScope:"mutations"` 会跳过
68
+ 只读工具的锁。代码默认关闭;随包 patch 开启,以提供与 Kimi 兼容的“单活跃控制塔”行为。
69
+
70
+ ## 失败结果
71
+
72
+ 门禁类工具返回 `{ok:false, blockers:[{code, detail}]}`;code 列表见 [blockers.md](blockers.md)。
73
+ 抛出的工具错误带稳定的 `[tower:<code>]` 前缀(见 [errors.md](errors.md))。成功的变更会发出进程内
74
+ 事件(见 [events.md](events.md)),并且在 `docs.board:true` 时刷新
75
+ `<git-common-dir>/dsh-tower/MISSIONS.md`。