dsh-tower 2.0.0 → 2.0.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,38 @@
2
2
 
3
3
  > 自 2.0.0 起,每个版本按 **新增 / 变更 / 修复** 三类组织;更早的版本保留原始流水式记录。
4
4
 
5
+ ## 2.0.1(2026-10-18)
6
+
7
+ ### 新增
8
+
9
+ - **`tower_batch` 的 `abandon` 动作**:可带 `reason` 退掉过期的 open batch,不修改 mission 状态;
10
+ 用于"先 merge 后 close"或证据窗口已过的场景。
11
+
12
+ ### 修复
13
+
14
+ - **#3 `allowAgentControl` 被 schema 默认值遮蔽**:移除 `mode.allowAgentControl` 的 schema 默认值,
15
+ 在 legacy 别名解析后再应用默认;随包 patch 改用 canonical `mode.allowAgentControl: true`。
16
+ Cordis 校验后的配置现在能正确注册 `tower_control`。
17
+ - **#4 `tower_preflight` 未知 mission 产生非 lossless 输出**:`evaluateGate` 的 early return 与工具
18
+ 包装补齐 `branchTip/baseTip/changedFiles/review` 的 `null`/`[]` 形状。
19
+ - **#5 未知依赖被分类为 `tool-internal`**:`depends on unknown mission` 归入 `invalid-argument`。
20
+ - **#6 worker 可 spawn 到 merged/abandoned mission**:spawn 前拒绝终态 mission,不再 provision/
21
+ activation,不再改写已合并 mission 的 owner/generation。
22
+ - **#7 live `tests/live/ui-slots.cjs` 在新会话 dock 隐藏时误报**:先提交 host-handled `/tower`
23
+ 命令绑定会话,再断言 dock;`DSH_TEST_EXPECT_HINT=1` 才要求 dock 可见。
24
+ - **#8 `plan-mode.test.mjs` 写入真实 `$DSH_HOME`**:测试前设置临时 `DSH_HOME` 并在 after 清理;
25
+ 清理了历史 `session-plan-test` 残留。
26
+ - **#9 `tower_batch` close 拒绝 merged 成员/无法退掉 stale batch**:`merged` 与 completed 一样
27
+ 可 close(checks 仍从完成报告/testRuns/close-time checks 解析);新增 `abandon` 且 batch 状态
28
+ 可退,不会永久卡死。
29
+ - **#10 `tower_doctor` 在成功 teardown 后误报 worktree missing**:跳过带 `worktreeRemovedAt` 的
30
+ mission,并在检查详情中注明已移除数量。
31
+
32
+ ### 变更
33
+
34
+ - 离线套件增至 212 项(新增 issue 回归覆盖);`docs:check` 仍为 28 tools / 32 blockers /
35
+ 30 errors / 25 events。
36
+
5
37
  ## 2.0.0(2026-10-18)
6
38
 
7
39
  ### 新增
package/README.en.md CHANGED
@@ -27,6 +27,28 @@ The repository root **is** the DSH bundle: `package.json` declares `dsh.bundle.p
27
27
  - **Hot reload.** In a profile with DSH HMR, `/tower reload`, `tower_reload` and `npm run dev:hmr` apply linked plugin changes without a restart; `plugin_manager` install/update already applies live.
28
28
  - **Evidence gate and events.** Workers can record `checks` in `tower_complete` and `gates.requireChecks` can require them; every mutation emits `tower/*` events and refreshes `MISSIONS.md`.
29
29
 
30
+ ## When to use Tower
31
+
32
+ Tower is a **delivery protocol for changes to a Git repository**, not the default answer for every multi-agent task. It fits when:
33
+
34
+ - a feature or refactor splits into 2–8 missions with clear scopes that can be reviewed independently;
35
+ - 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
+ - work spans context compaction, restarts, or multiple days and must be recoverable from local state;
38
+ - "done" means tasks + tests + independent review, not just a confident message in chat.
39
+
40
+ It is a poor fit for one-file or one-line hotfixes, requirements still being discovered, open-ended research, many tiny independent tasks, interactive UI pairing, or cross-repository work. Rule of thumb: **if you would not open a PR for it, Tower is probably too heavy.**
41
+
42
+ How it differs (full guide: [When to use Tower](docs/guide/scenarios-and-philosophy.md)):
43
+
44
+ - **Ordinary parallel subagents**: cheap and flexible for read-only exploration or trying approaches; no worktree, review, or merge gate.
45
+ - **Agent Teams**: shared task board and peer collaboration for design, research, and cross-cutting decisions; integration is usually human, with no hard Git gate.
46
+ - **Ralph loop**: fresh agents retry one objective, good for iterative search/repair; no mission contract or merge review.
47
+ - **Workflow orchestration**: scripted high fan-out/pipelines for audits, batch analysis, and verification; no branch isolation or merge gate.
48
+ - **Tower**: a few missions + isolated writers + independent review + evidence + a merge gate, for "we have decided; land it safely".
49
+
50
+ Tower mode refuses Team/Workflow/Ralph/ordinary subagent tools and clears plan mode so two orchestration protocols do not drive one session. Compose by phase instead: use Workflow/Team to analyze and decide, then Tower to execute the repository change.
51
+
30
52
  ## Requirements
31
53
 
32
54
  - DeepSeek Harness `0.2.1-rc.2` (or a compatible newer build)
package/README.md CHANGED
@@ -27,6 +27,28 @@ Tower 模式与 mission/审查工作流的灵感来自 Moonshot AI 的 **Kimi Co
27
27
  - **测试证据门与事件。** worker 可在 `tower_complete` 记录 `checks`,`gates.requireChecks` 可强制;每次 mutation 后发射 `tower/*` 事件并刷新 `MISSIONS.md`。
28
28
  - **2.1 验证与运营。** `tower_test` 执行 worktree 声明的 `tower.testDiscovery`(TAP/json-array,准入可设 spawn/complete),`tower_doctor` 只读健康契约,`tower_batch` 批次证据收口,`tower_resource` advisory lease,`tower_import_legacy` / `tower_legacy_map` 只读旧版导入与审计映射;新增 `worker-stale`、tests 门禁、plan/tower 互斥、`workspaceLockScope`、`docs.boardLocation` 与交付 ack。
29
29
 
30
+ ## 何时使用 Tower
31
+
32
+ Tower 是**面向 Git 仓库变更的交付协议**,不是所有多代理场景的默认答案。它适合:
33
+
34
+ - 一个特性/重构能拆成 2–8 个 scope 清晰、可独立审查的 mission;
35
+ - 有依赖顺序、需要分支新鲜度与 scope 约束的迁移;
36
+ - 敏感仓库、发布绑定变更,或需要"被审查 commit + 测试证据 + 可审计合并"的工作;
37
+ - 跨上下文压缩、重启、多天协作,需要从本地状态恢复的工作;
38
+ - 需要把"完成"定义成任务 + 测试 + 独立审查,而不是聊天里的一句"好了"。
39
+
40
+ 不适合:单文件/一行 hotfix、需求仍在探索、开放式研究、大量极小的独立任务、交互式 UI 配对、或跨仓库工作。判断标准:**如果你不会为它开 PR,Tower 很可能太重。**
41
+
42
+ 与其他模式的区别(详见[完整指南](docs/guide/scenarios-and-philosophy.md)):
43
+
44
+ - **普通并行子代理**:便宜、灵活,适合只读探索/试方案;没有 worktree、审查与合并门禁。
45
+ - **Agent Teams**:共享任务板与多人/多 agent 协作,适合设计、研究、横切决策;集成通常由人做,没有 Git 隔离的硬门禁。
46
+ - **Ralph 循环**:用新鲜 agent 对同一目标反复尝试,适合迭代搜索/修复;没有 mission 契约与合并审查。
47
+ - **Workflow 编排**:脚本化高扇出/流水线,适合批量分析、审计、验证;不做 Git 分支隔离与合并门禁。
48
+ - **Tower**:少量 mission + 隔离写入 + 独立审查 + 证据 + 合并门禁,适合"决定要做、需要安全落地"的仓库变更。
49
+
50
+ Tower 模式会拒绝 Team/Workflow/Ralph/普通 subagent 工具并清除 plan mode,避免两套编排协议同时驱动一个会话;请按阶段组合,例如先用 Workflow/Team 分析决策,再用 Tower 执行仓库变更。
51
+
30
52
  ## 环境要求
31
53
 
32
54
  - DeepSeek Harness `0.2.1-rc.2`(或兼容的新版本)
package/cordis.patch.yml CHANGED
@@ -2,13 +2,13 @@
2
2
  - id: tower
3
3
  name: 'dsh-tower'
4
4
  config:
5
- allowAgentControl: true
6
- init:
7
- autoGitInit: true
8
5
  mode:
6
+ allowAgentControl: true
9
7
  workspaceLock: true
10
8
  workspaceLockScope: all
11
9
  planModePolicy: clear
10
+ init:
11
+ autoGitInit: true
12
12
  send:
13
13
  interruptDefault: true
14
14
  spawn:
package/docs/README.md CHANGED
@@ -6,7 +6,8 @@ Choose a path:
6
6
 
7
7
  | You are | Start here |
8
8
  |---|---|
9
- | New user | [Quick start](guide/quickstart.md) → [Tools](reference/tools.md) → [Example](examples/first-mission.md) |
9
+ | New user | [When to use Tower](guide/scenarios-and-philosophy.md) → [Quick start](guide/quickstart.md) → [Tools](reference/tools.md) → [Example](examples/first-mission.md) |
10
+ | Choosing a workflow | [When to use Tower, and how it differs from Agent Teams, Ralph, Workflow and parallel subagents](guide/scenarios-and-philosophy.md) |
10
11
  | Operator | [Runbook](operations/runbook.md) → [Blockers](reference/blockers.md) → [Error codes](reference/errors.md) → [Events](reference/events.md) → [Doctor](reference/doctor.md) → [Testing](reference/testing.md) → [Batches/leases/legacy](reference/operations.md) → [Security](operations/security.md) |
11
12
  | Maintainer | [Architecture](architecture/overview.md) → [Config](reference/config.md) → [Hot reload](guide/hot-reload.md) → [Contributing](../CONTRIBUTING.md) |
12
13
 
@@ -14,4 +15,4 @@ Reference pages are the source of truth for the current release; the [tools refe
14
15
  [blocker list](reference/blockers.md) and [error codes](reference/errors.md) are checked against the
15
16
  implementation by `npm run docs:check` in CI (including the Chinese pages).
16
17
 
17
- Other useful pages: [Install, upgrade and migration](guide/install-upgrade-migration.md), [Hot reload](guide/hot-reload.md), [FAQ](faq.md), [CHANGELOG](../CHANGELOG.md).
18
+ Other useful pages: [When to use Tower](guide/scenarios-and-philosophy.md), [Install, upgrade and migration](guide/install-upgrade-migration.md), [Hot reload](guide/hot-reload.md), [FAQ](faq.md), [CHANGELOG](../CHANGELOG.md).
package/docs/faq.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # FAQ
2
2
 
3
+ **When should I use Tower instead of Agent Teams, Ralph, Workflow, or ordinary subagents?**
4
+ Use Tower when the output is a reviewed, integrated change to one Git repository. Use ordinary
5
+ subagents for read-only exploration or quick independent subtasks, Agent Teams for cross-cutting human
6
+ collaboration, Ralph for repeated fresh attempts at one objective, and Workflow for programmatic
7
+ high-fan-out analysis. The full decision guide, scenarios, and comparison table are in
8
+ [When to use Tower](guide/scenarios-and-philosophy.md).
9
+
10
+ **Can I combine Tower with Team/Workflow/Ralph?**
11
+ Not concurrently in the same session: Tower mode refuses their tools and clears plan mode. Compose by
12
+ phase instead—use Workflow/Team to analyze and decide, then start Tower to execute and review the
13
+ repository change. See the [full guide](guide/scenarios-and-philosophy.md#4-how-tower-differs-from-other-dsh-workflow-patterns).
14
+
3
15
  **Why did `tower_preflight` fail with `no-review` right after the worker finished?**
4
16
  That is expected: a completed mission awaits review. Spawn a reviewer; after a clean review the gate
5
17
  turns green.
@@ -0,0 +1,309 @@
1
+ # When to use Tower, and how it differs from other workflows
2
+
3
+ [简体中文](../zh/guide/scenarios-and-philosophy.md)
4
+
5
+ Tower is not a replacement for every multi-agent pattern. It is a **delivery protocol for changes to a
6
+ Git repository**: a small number of bounded missions, isolated writers, independent review, evidence,
7
+ and a merge gate. This page explains the design choices behind it, the situations where it pays off,
8
+ and when Agent Teams, Ralph loops, Workflow orchestration, or ordinary parallel subagents are a better
9
+ fit.
10
+
11
+ If you only remember one sentence: **use Tower when the output is a reviewed, integrated repository
12
+ change; use the other patterns when the output is exploration, repeated attempts, or programmatic
13
+ fan-out.**
14
+
15
+ ## 1. The design philosophy
16
+
17
+ Tower makes several deliberate trade-offs. They explain both its strengths and its cost.
18
+
19
+ ### 1.1 The repository is the unit of accountability
20
+
21
+ A mission is not a chat thread or a task card. It is a contract attached to a branch and a worktree,
22
+ with a declared write scope, dependencies, completion evidence, and a review record. The question
23
+ "what changed, who changed it, and what proved it?" has a durable answer in
24
+ `<git-common-dir>/dsh-tower/state.json` and the Git history, not only in a conversation.
25
+
26
+ ### 1.2 One writer per mission
27
+
28
+ Two agents writing the same branch at the same time produce races that no review can repair. Tower
29
+ enforces one live worker per mission, explicit takeover after a stop/interrupt, and a per-repository
30
+ worker cap. Parallelism comes from **separate missions**, not from several writers on one mission.
31
+
32
+ ### 1.3 Contract before code
33
+
34
+ Planning fixes the scope, tasks, dependencies, and review policy before a worker starts. That
35
+ front-loading is intentional: it lets the tower detect overlapping scopes, serialize dependent work,
36
+ and tell a reviewer exactly what "done" means. It is also why Tower is a poor fit for work whose
37
+ requirements are still being discovered.
38
+
39
+ ### 1.4 Integration is a separate, privileged step
40
+
41
+ Workers never merge. A reviewer inspects a specific branch tip; the tower runs preflight; only then
42
+ does `tower_merge` perform a `--no-ff` merge. The gate re-checks scope, base freshness, tasks, review
43
+ binding, worktree ownership, worker state, tests, and evidence. Separation of duties is the point:
44
+ the agent that wrote the code is not the agent that approves it, and neither is the agent that merges.
45
+
46
+ ### 1.5 Evidence over assertion
47
+
48
+ "Done" is a claim; a commit, a diff, a passing test receipt, and an independent review are evidence.
49
+ Tower records all of them. `tower_doctor` checks the environment, `tower_test` records discovery,
50
+ `tower_complete` records checks, `tower_review` binds a verdict to a commit, and `tower_batch` can
51
+ close a finite set of missions only when their required evidence exists.
52
+
53
+ ### 1.6 Fail closed, recover explicitly
54
+
55
+ Tower prefers a visible blocker over a silent repair. A stale worker is marked, not assumed dead; a
56
+ dirty base blocks a merge; a moved review tip blocks a merge; a crash during merge leaves a persistent
57
+ intent that the next `tower_merge` reconciles. The cost is occasional manual cleanup; the benefit is
58
+ that state never lies about what was integrated.
59
+
60
+ ### 1.7 The human owns the mode
61
+
62
+ Tower mode changes the session's stance: it narrows the tool surface and injects the mission protocol.
63
+ The shipped patch allows the model to toggle it with a stated reason, but the mode exists because a
64
+ human asked for delivery discipline. Entering pauses an active goal and clears plan mode; leaving does
65
+ not silently resume either. Tower intentionally refuses `goal`, `todo`, Team task-board, Workflow,
66
+ Ralph, and ordinary subagent tools while it is active, so two orchestration protocols do not fight over
67
+ one session.
68
+
69
+ ### 1.8 Local-first, no hidden service
70
+
71
+ Tower is a Host plugin. It uses local Git, local files, and the DSH subagent service. It does not push,
72
+ does not call a cloud service, and does not send telemetry. The durable state lives in the repository's
73
+ Git metadata, so it survives restarts and is visible to a human with ordinary Git tools.
74
+
75
+ ## 2. Situations where Tower is a good fit
76
+
77
+ ### 2.1 A feature that naturally splits into independent slices
78
+
79
+ Example: add OAuth login, split into "token model", "callback route", "UI button", and "migration +
80
+ tests". The slices touch mostly different files, can run in parallel, and need a final integration
81
+ review. Tower's scope reservation and automatic dependency for overlapping scopes keep the slices from
82
+ colliding.
83
+
84
+ **Why Tower:** parallel writers are isolated; each slice has an owner and a review; the merge is
85
+ gated once, not improvised per slice.
86
+
87
+ ### 2.2 A migration or refactor with ordering constraints
88
+
89
+ Example: rename a package, update imports, regenerate snapshots, then update documentation. Some work
90
+ can proceed in parallel, but later missions depend on earlier merges. Tower records `deps`, refuses to
91
+ merge a mission whose dependency is not merged, and lets the tower check `tower_status`/preflight
92
+ instead of remembering the graph in chat.
93
+
94
+ **Why Tower:** the dependency graph and branch freshness are enforced, not advisory.
95
+
96
+ ### 2.3 Work that needs a read-only investigation before a change
97
+
98
+ Example: "find every caller of this API and write a migration plan" followed by "apply the migration".
99
+ A `survey` mission reserves no write scope and is rejected if it changes files; its findings can feed
100
+ the build missions. Tower can also keep findings (`tower_finding`) outside a mission's scope without
101
+ silently widening it.
102
+
103
+ **Why Tower:** investigation and mutation have different contracts; the survey cannot accidentally
104
+ "fix" something.
105
+
106
+ ### 2.4 A sensitive repository or a release-bound change
107
+
108
+ Example: a payments service, a library with external users, or a release branch. You want an
109
+ independent reviewer, a specific reviewed commit, test evidence, and a human-visible audit trail.
110
+
111
+ **Why Tower:** review binding, evidence gates, `MISSIONS.md`, activity log, and optional test discovery
112
+ give you a defensible record. The reviewer is a separate read-only agent, not the author.
113
+
114
+ ### 2.5 Long-running work that spans context compaction or restarts
115
+
116
+ Example: a multi-day refactor interrupted by restarts, new sessions, or a context reset. The mission
117
+ state, roster, inbox, reviews, and worktrees are durable; `tower_adopt` reconciles a lost roster;
118
+ `tower_doctor` reports what is broken; `tower_merge` reconciles an already-landed merge.
119
+
120
+ **Why Tower:** the protocol is reconstructable from local state and Git, not from a conversation
121
+ window.
122
+
123
+ ### 2.6 Parallel work with a hard integration boundary
124
+
125
+ Example: four independent services in one monorepo, each with its own owner, all needing a final
126
+ release review. Tower gives each mission a branch/worktree and reserves write scopes; the tower can
127
+ then audit a finite batch (`tower_batch`) before declaring the release ready.
128
+
129
+ **Why Tower:** parallelism is real, but integration remains a single, gated step.
130
+
131
+ ### 2.7 When you need a "definition of done" that another agent can check
132
+
133
+ Example: a task list plus tests plus a required review verdict. Tower's mission contract and
134
+ completion/review tools make "done" observable. If your definition of done is "the user likes it in
135
+ the chat", Tower is overhead.
136
+
137
+ ## 3. Situations where Tower is a poor fit
138
+
139
+ | Situation | Better fit | Why |
140
+ |---|---|---|
141
+ | One-file edit, typo, small hotfix | Ordinary agent in a single session | Worktree, review, and merge overhead dominate. |
142
+ | Requirements are still being discovered | Ordinary agent, Plan mode, or Agent Teams | Tower asks for scope/tasks before work; a wrong contract causes re-planning. |
143
+ | Open-ended research or brainstorming | Agent Teams, Workflow, or plain subagents | There is no repository change to isolate or merge. |
144
+ | Many tiny independent tasks | Workflow or plain parallel subagents | Per-mission review/merge cost is too high. |
145
+ | Interactive UI pairing / exploratory debugging | Ordinary agent (or a single worker) | Tight feedback loops and frequent human steering; the review gate adds latency. |
146
+ | A one-shot generation with no integration | Plain subagent/Workflow | No branch, no review, no merge decision. |
147
+ | Cross-repository product work | Agent Teams + ordinary tools | Tower is scoped to one Git repository. |
148
+
149
+ A useful heuristic: **if you would not open a pull request for it, Tower is probably too heavy.**
150
+
151
+ ## 4. How Tower differs from other DSH workflow patterns
152
+
153
+ All of these patterns can use the same subagent runtime. They differ in **what is durable, who owns
154
+ integration, and what "done" means.**
155
+
156
+ | Dimension | Tower | Agent Teams | Ralph loop | Workflow orchestration | Ordinary parallel subagents |
157
+ |---|---|---|---|---|---|
158
+ | Primary goal | Deliver reviewed repository changes | Collaborate across people/agents on a shared goal | Repeated fresh attempts at one objective | Programmatic fan-out/pipeline of agent work | Explore or implement in parallel |
159
+ | Unit of work | Mission (branch + worktree + scope + tasks + review) | Task/teammate on a shared board | Round/attempt | Scripted step/stage | Prompt/task per child |
160
+ | Isolation | Git worktree + branch per mission; one writer per mission | Shared workspace; coordination by task ownership | Fresh child per round; no built-in branch/merge contract | Per-step child; no Git isolation | Child sessions; may share workspace |
161
+ | Durable state | Git metadata + JSON state + activity + board | Team task board + sessions | Minimal; each round starts fresh | Run record/results; not a merge ledger | Session/subagent records |
162
+ | Integration authority | Tower only, after review + gate | Humans/teammates merge or edit manually | Usually the parent/worker applies results | Script applies results | Parent applies results |
163
+ | Review / gate | Mandatory independent review + preflight/merge gate | Review is a collaboration convention, not a hard gate | No built-in review gate | No built-in review gate | No built-in review gate |
164
+ | Parallelism | Multiple missions, disjoint scopes, serialized merges | Many teammates, shared board | Sequential rounds (or external fan-out) | High fan-out, script-controlled | High fan-out |
165
+ | Human role | Owns the mode; can inspect evidence and release | Collaborator/coordinator | Author of the repeated objective | Author of the orchestration script | Delegator |
166
+ | Best for | Feature delivery, migrations, release-bound changes | Design, research, cross-cutting collaboration | Iterative search/repair with fresh context | Large audits, batch analysis, deterministic pipelines | Quick exploration or independent subtasks |
167
+ | Main cost | Mission/review/merge overhead; merge serialization | Coordination overhead; no isolation guarantee | Repeated work; weak integration contract | Script complexity; no review/merge semantics | No isolation/review; manual integration |
168
+
169
+ ### 4.1 Tower vs ordinary parallel subagents
170
+
171
+ Ordinary `subagent`/`subagent_fork` calls are cheap and flexible. They are the right tool for "read
172
+ these five files in parallel" or "try three approaches". They do not provide a write scope, a
173
+ dedicated worktree, a durable mission, an independent review, or a merge gate. Tower wraps the same
174
+ subagent primitive in those contracts; it also costs more per child.
175
+
176
+ Choose subagents when the work is read-mostly, short-lived, or you will integrate the result
177
+ yourself. Choose Tower when the result must land on a branch and survive a review.
178
+
179
+ ### 4.2 Tower vs Agent Teams
180
+
181
+ Agent Teams is a **collaboration** pattern: a shared task board, durable teammates, messages, wait
182
+ and interrupt, and a human/Lead coordinating work. It is excellent when the work is cross-cutting,
183
+ exploratory, or needs several perspectives, and when the integration decision is human.
184
+
185
+ Tower is a **delivery** pattern: mission contracts, Git isolation, review, and a hard merge gate. It
186
+ has less flexible peer-to-peer coordination and more structure. Agent Teams can decide *what* to
187
+ build and *whether* to proceed; Tower can then execute that decision as missions. They are sequential
188
+ or complementary, not concurrent: Tower mode refuses the Team task-board tools so the two protocols do
189
+ not compete for the same session.
190
+
191
+ ### 4.3 Tower vs Ralph loop
192
+
193
+ Ralph runs the **same objective** through fresh agents, round after round, with the workspace as
194
+ memory. It is useful for iterative search, flaky repair, or "try again with fresh context". It has no
195
+ mission contract, no independent review, and no merge gate.
196
+
197
+ Tower is incremental: each mission has a bounded deliverable and lands through review. If the problem
198
+ is "I do not know whether this can be done, and repeated fresh attempts may find a way", Ralph is
199
+ often better. If the problem is "I know the slices; build and integrate them safely", Tower is better.
200
+ You can use Ralph outside Tower mode to find an approach, then encode the result as Tower missions.
201
+
202
+ ### 4.4 Tower vs Workflow orchestration
203
+
204
+ Workflow is **programmatic orchestration**: a JavaScript script fans work out across many subagents
205
+ with phases, pipelines, and structured results. It is excellent for audits, migrations of many
206
+ independent items, multi-angle research, and adversarial verification. It does not give each step a
207
+ Git worktree, a review verdict, or a merge gate.
208
+
209
+ Tower is a **repository delivery protocol** with a small number of missions, not a high-fan-out data
210
+ plane. Use Workflow for "analyze 200 files in parallel"; use Tower for "make the 6 changes those
211
+ analyses justify, with review and a merge". Tower mode refuses Workflow so a script cannot spawn
212
+ competing writers under the Tower contract.
213
+
214
+ ### 4.5 Tower vs Plan mode
215
+
216
+ Plan mode is for deciding what to do; Tower is for executing a decided plan against a repository.
217
+ Tower entry clears an active plan (or refuses entry under `mode.planModePolicy:"refuse"`), and a
218
+ Tower session cannot use the goal/todo/team/workflow tools. Plan first, then execute; do not expect
219
+ Tower to discover the plan for you.
220
+
221
+ ## 5. Worked scenarios
222
+
223
+ ### Scenario A: four-slice feature
224
+
225
+ A team wants "organization invitations": token model, email delivery, accept/decline API, and UI.
226
+
227
+ 1. Plan four build missions with scopes `src/tokens/**`, `src/email/**`, `src/api/invites/**`,
228
+ `src/ui/invites/**`; make the API depend on tokens and email.
229
+ 2. Spawn workers for the independent missions; the API waits for its deps.
230
+ 3. Each worker commits on its branch, completes with checks.
231
+ 4. Spawn reviewers; a clean+merge review is required.
232
+ 5. `tower_preflight` and `tower_merge` each mission; resolve scope/base/review blockers.
233
+ 6. `tower_teardown`; `MISSIONS.md` is the human-readable record.
234
+
235
+ **Why not Team/Workflow?** The work is a known set of repository changes that must land in a specific
236
+ order with independent review. Team tasks would not isolate writes; Workflow would analyze but not
237
+ gate the merge.
238
+
239
+ ### Scenario B: migration with a survey
240
+
241
+ 1. Survey mission: "map all callers of `oldApi`"; scope empty, read-only.
242
+ 2. Build missions: codemod, test updates, docs; depend on the survey's findings.
243
+ 3. Reviewers check the diff against the survey; tests must pass for the reviewed tip.
244
+ 4. Merge in dependency order.
245
+
246
+ **Why not plain subagents?** The survey must not accidentally mutate; the codemod must not touch
247
+ out-of-scope files; the review must bind to the exact commit.
248
+
249
+ ### Scenario C: incident hotfix
250
+
251
+ A one-line fix in production. Use an ordinary agent, commit and push through your normal process.
252
+ Tower would add a worktree, a reviewer, a gate, and a merge step; for a one-line urgent fix that is
253
+ mostly latency. If the "hotfix" is actually several coordinated changes, Tower becomes reasonable.
254
+
255
+ ### Scenario D: broad codebase audit
256
+
257
+ "Find every use of deprecated crypto and summarize risk." Use Workflow or parallel subagents; there
258
+ is no branch to merge and no single reviewed change. If the audit produces a set of fixes, create
259
+ Tower missions from those findings.
260
+
261
+ ### Scenario E: sensitive release
262
+
263
+ A library release with external consumers. Plan missions for the change, migration guide, and
264
+ release notes; require tests and review; use `tower_batch` to close the release evidence only when
265
+ all required checks are present; merge and tag outside Tower as your release process requires.
266
+
267
+ ## 6. Choosing in practice: a short decision path
268
+
269
+ 1. **Is the output a Git change?** No → use ordinary agents, Agent Teams, Workflow, or Ralph.
270
+ 2. **Would you open a PR for it?** No → Tower is too heavy; use a normal agent.
271
+ 3. **Do the slices have stable scopes and a known definition of done?** No → plan/explore first; then
272
+ return to Tower.
273
+ 4. **Do you need independent review and a merge gate?** Yes → Tower. If no, plain subagents may be
274
+ enough.
275
+ 5. **Do you need many parallel analyses or repeated fresh attempts?** Use Workflow/Ralph for that
276
+ phase; encode the resulting changes as Tower missions.
277
+ 6. **Do you need cross-cutting human collaboration?** Use Agent Teams to decide; execute the decided
278
+ repository work with Tower.
279
+
280
+ ## 7. Cost, limits, and anti-patterns
281
+
282
+ - **Per-mission overhead is real**: worktree creation, spawn, completion checks, review round, gate,
283
+ merge. Keep missions meaningful (roughly a PR-sized slice), not one task per function.
284
+ - **Merges serialize**: overlapping scopes auto-add dependencies; independent missions still merge
285
+ one at a time onto the base. Do not expect linear speedup for merge-heavy work.
286
+ - **Review is not free**: every mission needs a reviewer and a clean verdict. Exemptions are opt-in
287
+ and require a reason; they are not the default.
288
+ - **Too-fine missions** increase coordination and merge cost. **Too-coarse missions** lose the benefit
289
+ of parallel review and make scope conflicts likely.
290
+ - **Tower is not a sandbox**: it is a coordination protocol. Host permissions, repository hooks, and
291
+ test commands still execute with the DSH user's OS permissions.
292
+ - **Do not run two orchestration protocols at once**: Tower mode denies Team/Workflow/Ralph/subagent
293
+ tools and clears plan mode. Sequence the phases instead.
294
+ - **Do not use Tower to hide missing requirements**: a wrong mission contract produces a wrong branch
295
+ that then fails review. Explore first.
296
+
297
+ ## 8. Checklist before you start
298
+
299
+ - [ ] The work produces a repository change that deserves a PR-style review.
300
+ - [ ] I can name 2–8 missions with non-overlapping scopes and a rough dependency order.
301
+ - [ ] Each mission has a testable definition of done (tasks, checks/tests, review).
302
+ - [ ] I know which missions can run in parallel and which must wait.
303
+ - [ ] I accept the review/merge overhead and will inspect `MISSIONS.md`/the branch.
304
+ - [ ] I am not currently relying on a Team task board, Workflow script, Ralph loop, or plan mode in
305
+ the same session.
306
+ - [ ] I have a recovery path if a worker stalls (interrupt/adopt) or a merge conflicts.
307
+
308
+ If most boxes are checked, Tower is likely a good fit. If not, use the lighter patterns and come back
309
+ when the change is shaped.
@@ -23,6 +23,8 @@ It returns:
23
23
  Checks cover: git availability, repository identity, state schema/load, repo config presence,
24
24
  worker/reviewer preset names, subagents and HMR services, workspace lock holder/staleness, live roster,
25
25
  worktree existence/ownership/dirtiness, test-discovery policy, legacy snapshots, batches and leases.
26
+ Missions intentionally cleaned up by `tower_teardown` are skipped (the check notes the removed count)
27
+ instead of being reported as missing.
26
28
  `warn` is advisory; `fail` means the repository needs attention before spawning/merging. A state-load
27
29
  failure is reported as a failed check rather than thrown so doctor remains useful on a broken
28
30
  repository.
@@ -8,10 +8,13 @@
8
8
  relative `receiptPaths`. Members must be known missions and only paused legacy missions may be
9
9
  excluded; membership is immutable after create.
10
10
  - `status` summarizes members and pending/failed checks.
11
- - `close` verifies every required check for completed members (from `completionReport.checks`,
12
- `testRuns`, or close-time `checks`), checks `treeTip` when provided, and requires every frozen
13
- receipt path to be supplied in `receiptHashes`/`receipts` (or bound to a passing check with
14
- `receiptHash`). Missing evidence returns `{ok:false, blockers}`; nothing is deleted.
11
+ - `close` verifies every required check for completed, **merged**, or abandoned members (from
12
+ `completionReport.checks`, `testRuns`, or close-time `checks`), checks `treeTip` when provided, and
13
+ requires every frozen receipt path to be supplied in `receiptHashes`/`receipts` (or bound to a
14
+ passing check with `receiptHash`). Missing evidence returns `{ok:false, blockers}`; nothing is
15
+ deleted.
16
+ - `abandon` retires a stale open batch (`batchId`, optional `reason`) without touching mission state;
17
+ it is the escape hatch when the evidence window has passed (for example after merging before close).
15
18
 
16
19
  A failed close is not a partial close: the batch stays `open` and can be retried after evidence is
17
20
  fixed.
@@ -18,7 +18,7 @@ canonical camelCase names.
18
18
 
19
19
  Roster roles are resolved from `exec.agent` + the roster. Mission-scoped tools (`tower_interrupt`,
20
20
  `tower_init`, `tower_plan`, `tower_spawn`, `tower_merge`, `tower_rebase`, `tower_teardown`,
21
- `tower_adopt`, `tower_reload`) are **tower only**. `tower_batch` create/close, `tower_resource`
21
+ `tower_adopt`, `tower_reload`) are **tower only**. `tower_batch` create/close/abandon, `tower_resource`
22
22
  acquire/release, `tower_import_legacy`, `tower_legacy_map` map and review-exemption planning are
23
23
  tower-only; their read-only status actions are open to roster roles. A worker may call `tower_mission`
24
24
  only for `task_done`/`task_drop`/`blocker`/`note`; status changes, scope widening, `clear_blockers` and
@@ -54,7 +54,7 @@ findings but not mutate them. Violations throw `[tower:not-authorized]` or
54
54
  | `tower_reload` | `repoRoot?` | Dev-only: ask DSH HMR to reconcile the profile and reload the Host plugin from disk. Registered while `dev.hotReload` is on; scheduled after the result (default 50 ms); fails with `reload-unavailable` without HMR. When `repoRoot` is supplied the caller must be the tower. |
55
55
  | `tower_doctor` | — (takes `repoRoot`) | Read-only health contract: git, state schema, config, presets/subagents/HMR, workspace lock, roster, worktree realpath/ownership, test config, leases. Returns `{ok, checks[{id,status,detail}], environmentContract}`. Never writes. |
56
56
  | `tower_test` | `missionId` (alias `id`) | Run the worktree's `package.json` `tower.testDiscovery` and record a receipt; `ok:false` on failure. Use `tower_complete` for automatic admission checks. |
57
- | `tower_batch` | `action` (`create`/`status`/`close`), `missionIds?`, `exclusions?`, `requiredChecks?`, `receiptPaths?`, `batchId?`, `checks?`, `treeTip?`, `receiptHashes?` | Finite evidence batch. Create freezes members/checks/receipts; close verifies every required check and receipt hash. Membership is immutable; close never deletes missions. |
57
+ | `tower_batch` | `action` (`create`/`status`/`close`/`abandon`), `missionIds?`, `exclusions?`, `requiredChecks?`, `receiptPaths?`, `batchId?`, `checks?`, `treeTip?`, `receiptHashes?`, `reason?` | Finite evidence batch. Create freezes members/checks/receipts; close verifies every required check and receipt hash; merged members are eligible; abandon retires a stale open batch without deleting missions. Membership is immutable. |
58
58
  | `tower_resource` | `action` (`acquire`/`release`/`status`), `name?`, `holder?`, `token?`, `ttlMs?`, `force?` | Advisory per-repository lease in `<common>/dsh-tower/leases.json`. No OS CPU/memory hard cap; expired leases require explicit `force` and the correct token. |
59
59
  | `tower_import_legacy` | `sourcePath` | Read-only import of a Tower Codex `schemaVersion:1` or DSH legacy state. Full payload goes under `<common>/dsh-tower/legacy/<sha256>.json`; state stores a summary only. Never activates/adopts/resumes paused missions. |
60
60
  | `tower_legacy_map` | `action` (`map`/`status`), `sourceHash?`, `oldId?`, `newMissionId?` | Audit-only legacy-id mapping; mappings are append-only and never adopt a mission. |
package/docs/zh/README.md CHANGED
@@ -26,7 +26,8 @@
26
26
 
27
27
  | 你是 | 从这里开始 |
28
28
  |---|---|
29
- | 新用户 | [快速开始](guide/quickstart.md) → [工具参考](reference/tools.md) → [第一个 mission 示例(英文)](../examples/first-mission.md) |
29
+ | 新用户 | [什么时候用 Tower](guide/scenarios-and-philosophy.md) → [快速开始](guide/quickstart.md) → [工具参考](reference/tools.md) → [第一个 mission 示例(英文)](../examples/first-mission.md) |
30
+ | 正在选择工作流 | [Tower 与 Agent Teams、Ralph、Workflow、普通并行子代理的区别](guide/scenarios-and-philosophy.md) |
30
31
  | 运维 | [运维 runbook(英文)](../operations/runbook.md) → [blocker 与失败码](reference/blockers.md) → [错误码](reference/errors.md) → [事件](reference/events.md) → [Doctor](reference/doctor.md) → [测试](reference/testing.md) → [批次/租约/旧版](reference/operations.md) → [安全模型](operations/security.md) |
31
32
  | 维护者 | [架构与状态机(英文)](../architecture/overview.md) → [配置参考](reference/config.md) → [热重载](guide/hot-reload.md) → [贡献指南](../../CONTRIBUTING.md) |
32
33
 
@@ -35,6 +36,7 @@
35
36
  ## 全部中文页面
36
37
 
37
38
  - [快速开始](guide/quickstart.md)
39
+ - [什么时候用 Tower,与 Agent Teams / Ralph / Workflow / 并行子代理的区别](guide/scenarios-and-philosophy.md)
38
40
  - [安装、升级与迁移](guide/install-upgrade-migration.md)
39
41
  - [热重载(开发)](guide/hot-reload.md)
40
42
  - [工具参考](reference/tools.md)
package/docs/zh/faq.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  [English](../faq.md) | **简体中文**
4
4
 
5
+ **什么时候该用 Tower,而不是 Agent Teams、Ralph、Workflow 或普通子代理?**
6
+ 当输出是单个 Git 仓库中需要审查并合入的变更时,用 Tower。只读探索或快速独立子任务用普通子代理;
7
+ 横切的人类协作/设计决策用 Agent Teams;对同一目标反复新鲜尝试用 Ralph;程序化高扇出分析用 Workflow。
8
+ 完整选择路径、场景与对比表见[什么时候用 Tower](guide/scenarios-and-philosophy.md)。
9
+
10
+ **可以同时把 Tower 和 Team/Workflow/Ralph 组合使用吗?**
11
+ 同一会话中不能并发:Tower 模式会拒绝这些工具并清除 plan mode。请按阶段组合——先用 Workflow/Team
12
+ 分析与决策,再启动 Tower 执行并审查仓库变更。详见[完整指南](guide/scenarios-and-philosophy.md#4-tower-与其他-dsh-工作流模式的区别)。
13
+
5
14
  **为什么 worker 刚完成,`tower_preflight` 就因 `no-review` 失败?**
6
15
  这是预期行为:`completed` 的 mission 正在等待审查。spawn 一个 reviewer;干净审查后门禁就会变绿。
7
16
 
@@ -0,0 +1,211 @@
1
+ # 什么时候用 Tower,以及它与其他工作流的区别
2
+
3
+ [English](../guide/scenarios-and-philosophy.md)
4
+
5
+ Tower 不是所有多代理模式的替代品。它是**面向 Git 仓库变更的交付协议**:少量边界清晰的 mission、隔离的写入者、独立审查、证据与合并门禁。本页解释这些设计取舍、适用场景,以及什么时候 Agent Teams、Ralph 循环、Workflow 编排或普通并行子代理更合适。
6
+
7
+ 如果只记一句话:**输出是需要审查并合入仓库的变更时用 Tower;输出是探索、重复尝试或程序化扇出时用其他模式。**
8
+
9
+ ## 1. 设计哲学
10
+
11
+ Tower 做了一组刻意的取舍,这解释了它的优势和成本。
12
+
13
+ ### 1.1 仓库是责任单位
14
+
15
+ mission 不是聊天线程或任务卡,而是绑定到分支与 worktree 的契约:有声明式写范围、依赖、完成证据和审查记录。"改了什么、谁改的、凭什么说改对了"有持久答案,位于 `<git-common-dir>/dsh-tower/state.json` 与 Git 历史,而不只在对话里。
16
+
17
+ ### 1.2 一个 mission 一个写入者
18
+
19
+ 两个 agent 同时写同一分支会产生审查无法修复的竞争。Tower 强制每个 mission 只有一个 live worker;停止/中断后接管必须显式;每个仓库有 worker 上限。并行来自**不同 mission**,不是同一 mission 的多个写入者。
20
+
21
+ ### 1.3 先契约后代码
22
+
23
+ 计划阶段固定 scope、任务、依赖与审查策略,之后 worker 才开始。这种前置是有意的:控制塔可以检测 scope 重叠、串行化依赖,并告诉 reviewer "完成"具体意味着什么。这也是 Tower 不适合需求仍在探索阶段的工作的原因。
24
+
25
+ ### 1.4 集成是独立的特权步骤
26
+
27
+ worker 永远不合并。reviewer 审查特定分支 tip;控制塔运行 preflight;只有之后 `tower_merge` 才执行 `--no-ff` 合并。门禁重新检查 scope、base 新鲜度、任务、审查绑定、worktree 归属、worker 状态、测试与证据。职责分离是重点:写代码的 agent 不批准,批准的 agent 也不合并。
28
+
29
+ ### 1.5 证据优先于断言
30
+
31
+ "完成了"是声明;commit、diff、通过的测试回执、独立审查才是证据。Tower 全部记录:`tower_doctor` 检查环境,`tower_test` 记录发现,`tower_complete` 记录 checks,`tower_review` 把结论绑定到提交,`tower_batch` 只有在有限 mission 集合的必需证据齐全时才允许收口。
32
+
33
+ ### 1.6 失败关闭,显式恢复
34
+
35
+ Tower 宁可显示 blocker,也不静默修复。stale worker 被标记而不是假设已死;脏 base 阻止合并;审查后 tip 变化阻止合并;merge 期间崩溃会留下持久 intent,由下一次 `tower_merge` 对账。代价是偶尔需要人工清理,收益是状态不会谎报已集成的内容。
36
+
37
+ ### 1.7 人类拥有模式
38
+
39
+ Tower 模式改变会话姿态:收窄工具面并注入 mission 协议。随包 patch 允许模型给出理由后切换,但该模式存在的前提是人类需要交付纪律。进入会暂停活跃 goal 并清除 plan mode;退出不会静默恢复。Tower 激活期间刻意拒绝 `goal`、`todo`、Team 任务板、Workflow、Ralph 与普通 subagent 工具,避免两套编排协议争夺同一会话。
40
+
41
+ ### 1.8 本地优先,无隐藏服务
42
+
43
+ Tower 是 Host 插件,只使用本地 Git、本地文件和 DSH subagent 服务;不 push、不调用云服务、不发送遥测。持久状态位于仓库 Git 元数据,重启后仍在,人类用普通 Git 工具即可查看。
44
+
45
+ ## 2. Tower 适合的场景
46
+
47
+ ### 2.1 能自然拆成独立切片的特性
48
+
49
+ 例:OAuth 登录拆成"token 模型"、"callback 路由"、"UI 按钮"、"迁移 + 测试"。切片大多改不同文件,可以并行,最后需要集成审查。Tower 的 scope 预留与重叠自动依赖可以避免碰撞。
50
+
51
+ **为什么用 Tower:** 并行写入被隔离;每个切片有 owner 与审查;合并在最后统一门禁,而不是逐段即兴处理。
52
+
53
+ ### 2.2 有顺序约束的迁移或重构
54
+
55
+ 例:重命名包、更新 import、重生成快照、再更新文档。部分工作可并行,但后续 mission 依赖前面的合并。Tower 记录 `deps`,依赖未合并时拒绝合并当前 mission,控制塔用 `tower_status`/preflight 检查图,而不是靠聊天记忆。
56
+
57
+ **为什么用 Tower:** 依赖图和分支新鲜度是被执行的,不是建议。
58
+
59
+ ### 2.3 变更前需要只读调查
60
+
61
+ 例:"找出这个 API 的所有调用者并写迁移计划" 之后 "执行迁移"。`survey` mission 不预留写范围,改了文件会被拒绝;findings 可以喂给 build mission。Tower 还允许用 `tower_finding` 记录 mission scope 之外的问题,而不静默扩界。
62
+
63
+ **为什么用 Tower:** 调查与修改有不同契约;survey 不会"顺手修一下"。
64
+
65
+ ### 2.4 敏感仓库或与发布绑定的变更
66
+
67
+ 例:支付服务、有外部用户的库、release 分支。你需要独立 reviewer、特定被审查 commit、测试证据和人类可审计记录。
68
+
69
+ **为什么用 Tower:** 审查绑定、证据门禁、`MISSIONS.md`、活动日志和可选测试发现提供了可辩护的记录;reviewer 是独立的只读 agent,不是作者。
70
+
71
+ ### 2.5 跨上下文压缩或重启的长期工作
72
+
73
+ 例:多日重构,期间重启、开新会话或上下文重置。mission 状态、roster、inbox、reviews、worktree 都是持久的;`tower_adopt` 对账丢失的 roster;`tower_doctor` 报告问题;`tower_merge` 对账已落地的合并。
74
+
75
+ **为什么用 Tower:** 协议可以从本地状态与 Git 重建,而不是从对话窗口重建。
76
+
77
+ ### 2.6 有硬集成边界的并行工作
78
+
79
+ 例:monorepo 中四个独立服务,各有 owner,最后统一发布审查。Tower 给每个 mission 分支/worktree 并预留写范围;控制塔可用 `tower_batch` 审计有限的发布证据。
80
+
81
+ **为什么用 Tower:** 并行是真的,但集成仍然是一个统一的、带门禁的步骤。
82
+
83
+ ### 2.7 需要另一个 agent 能检查的"完成定义"
84
+
85
+ 例:任务清单 + 测试 + 必需审查结论。Tower 的 mission 契约与 completion/review 工具让"完成"可观察。如果你的完成定义只是"用户在聊天里满意",Tower 是过度设计。
86
+
87
+ ## 3. Tower 不适合的场景
88
+
89
+ | 场景 | 更合适的模式 | 原因 |
90
+ |---|---|---|
91
+ | 单文件修改、typo、小 hotfix | 普通会话中的单个 agent | worktree、审查与合并开销大于收益。 |
92
+ | 需求仍在发现阶段 | 普通 agent、Plan mode 或 Agent Teams | Tower 要求先给 scope/tasks;错误契约会导致重新规划。 |
93
+ | 开放式研究或头脑风暴 | Agent Teams、Workflow 或普通子代理 | 没有要隔离和合并的仓库变更。 |
94
+ | 很多极小的独立任务 | Workflow 或普通并行子代理 | 每个 mission 的审查/合并成本太高。 |
95
+ | 交互式 UI 配对或探索式调试 | 普通 agent(或单个 worker) | 需要紧密反馈与频繁人工引导,审查门禁增加延迟。 |
96
+ | 一次性生成、没有集成 | 普通子代理/Workflow | 不需要分支、审查、合并决策。 |
97
+ | 跨仓库产品工作 | Agent Teams + 普通工具 | Tower 只作用于单个 Git 仓库。 |
98
+
99
+ 一个有用的判断:**如果你不会为它开 PR,Tower 很可能太重了。**
100
+
101
+ ## 4. Tower 与其他 DSH 工作流模式的区别
102
+
103
+ 这些模式可以共用同一个 subagent 运行时;区别在于**什么是持久的、谁拥有集成、以及"完成"意味着什么。**
104
+
105
+ | 维度 | Tower | Agent Teams | Ralph 循环 | Workflow 编排 | 普通并行子代理 |
106
+ |---|---|---|---|---|---|
107
+ | 主要目标 | 交付经审查的仓库变更 | 围绕共享目标协作 | 对同一目标用新鲜 agent 反复尝试 | 程序化扇出/流水线 | 并行探索或实现 |
108
+ | 工作单位 | mission(分支 + worktree + scope + 任务 + 审查) | 共享任务板上的任务/teammate | 轮次/尝试 | 脚本步骤/阶段 | 每个子代理一个 prompt/任务 |
109
+ | 隔离 | 每 mission 独立 worktree/分支;单写入者 | 共享工作区;靠任务归属协调 | 每轮新 child;无内置分支/合并契约 | 每步 child;无 Git 隔离 | 子会话;可能共享工作区 |
110
+ | 持久状态 | Git 元数据 + JSON state + 活动日志 + 看板 | Team 任务板 + 会话 | 很少;每轮从零开始 | 运行记录/结果;不是合并账本 | 会话/子代理记录 |
111
+ | 集成权限 | 仅控制塔,且需审查 + 门禁 | 人类/teammate 手动合并或编辑 | 通常由父 agent/worker 应用结果 | 脚本应用结果 | 父 agent 应用结果 |
112
+ | 审查/门禁 | 强制独立审查 + preflight/merge 门禁 | 审查是协作约定,不是硬门禁 | 无内置审查门禁 | 无内置审查门禁 | 无内置审查门禁 |
113
+ | 并行度 | 多 mission、互斥 scope、合并串行 | 多 teammate、共享任务板 | 顺序轮次(或外部扇出) | 高扇出,由脚本控制 | 高扇出 |
114
+ | 人的角色 | 拥有模式;可检查证据与发布 | 协作者/协调者 | 重复目标的作者 | 编排脚本作者 | 委派者 |
115
+ | 最适合 | 特性交付、迁移、发布绑定变更 | 设计、研究、横切协作 | 迭代搜索/修复,需要新鲜上下文 | 大规模审计、批量分析、确定性流水线 | 快速探索或独立子任务 |
116
+ | 主要成本 | mission/审查/合并开销;合并串行 | 协调开销;无隔离保证 | 重复劳动;集成契约弱 | 脚本复杂度;无审查/合并语义 | 无隔离/审查;手动集成 |
117
+
118
+ ### 4.1 Tower vs 普通并行子代理
119
+
120
+ 普通 `subagent`/`subagent_fork` 便宜灵活,适合"并行读五个文件"或"试三种方案"。它们不提供写范围、专用 worktree、持久 mission、独立审查或合并门禁。Tower 用这些契约包装同一个子代理原语,因此每个 child 更贵。
121
+
122
+ 只读、短生命周期、或你准备自己集成结果时,用普通子代理;结果必须落到分支并经过审查时,用 Tower。
123
+
124
+ ### 4.2 Tower vs Agent Teams
125
+
126
+ Agent Teams 是**协作**模式:共享任务板、持久 teammate、消息、wait/interrupt,以及人类/Lead 协调。它适合横切、探索性或需要多视角的工作,且集成决定通常由人类做。
127
+
128
+ Tower 是**交付**模式:mission 契约、Git 隔离、审查与硬合并门禁。它的点对点协作更少、结构更多。Agent Teams 可以决定"做什么、是否继续";Tower 可以把决定执行成 mission。二者应顺序或互补,而非并发:Tower 模式会拒绝 Team 任务板工具,避免两套协议争抢同一会话。
129
+
130
+ ### 4.3 Tower vs Ralph 循环
131
+
132
+ Ralph 用新鲜 agent 一轮轮执行**同一目标**,以工作区为长期记忆。它适合迭代搜索、修复 flaky、以及"换上下文再试一次"。它没有 mission 契约、独立审查或合并门禁。
133
+
134
+ Tower 是增量的:每个 mission 有边界清晰的交付物,并经审查落地。如果问题是"我不知道能不能做成,反复新鲜尝试也许能找到路",Ralph 往往更合适;如果问题是"我知道切片,需要安全地构建并集成",Tower 更合适。可以在 Tower 模式之外先用 Ralph 找到方案,再把结果编码成 Tower mission。
135
+
136
+ ### 4.4 Tower vs Workflow 编排
137
+
138
+ Workflow 是**程序化编排**:JS 脚本把工作扇出到许多 subagent,带阶段、pipeline 与结构化结果。它适合审计、批量迁移、多角度研究与对抗性验证。它不为每一步提供 Git worktree、审查结论或合并门禁。
139
+
140
+ Tower 是**仓库交付协议**,mission 数量少,不是高扇出数据平面。用 Workflow "并行分析 200 个文件";用 Tower "把分析证明需要的 6 个修改,经审查与门禁合入"。Tower 模式会拒绝 Workflow,避免脚本在 Tower 契约下制造竞争写入。
141
+
142
+ ### 4.5 Tower vs Plan mode
143
+
144
+ Plan mode 决定做什么;Tower 把已决定的计划执行到仓库。进入 Tower 会清除活跃 plan(`mode.planModePolicy:"refuse"` 时拒绝进入),且 Tower 会话不能使用 goal/todo/team/workflow 工具。先规划,再执行;不要指望 Tower 替你发现计划。
145
+
146
+ ## 5. 具体场景示例
147
+
148
+ ### 场景 A:四切片特性
149
+
150
+ 要做"组织邀请":token 模型、邮件发送、接受/拒绝 API、UI。
151
+
152
+ 1. 规划四个 build mission,scope 分别 `src/tokens/**`、`src/email/**`、`src/api/invites/**`、`src/ui/invites/**`;让 API 依赖 token 与 email。
153
+ 2. 对独立 mission 启动 worker;API 等依赖完成。
154
+ 3. 每个 worker 在自己分支提交,带 checks 完成。
155
+ 4. 启动 reviewer;要求 clean + merge。
156
+ 5. 逐 mission `tower_preflight` + `tower_merge`,处理 scope/base/review blocker。
157
+ 6. `tower_teardown`;`MISSIONS.md` 是人类可读记录。
158
+
159
+ **为什么不用 Team/Workflow?** 这是已知的仓库变更集合,需要按顺序落地并独立审查。Team 任务板不隔离写入;Workflow 会分析但不会门禁合并。
160
+
161
+ ### 场景 B:带 survey 的迁移
162
+
163
+ 1. Survey mission:"映射 `oldApi` 的所有调用者",scope 为空,只读。
164
+ 2. Build mission:codemod、测试更新、文档;依赖 survey 结果。
165
+ 3. Reviewer 对照 survey 检查 diff;被审查 tip 的测试必须通过。
166
+ 4. 按依赖顺序合并。
167
+
168
+ **为什么不用普通子代理?** survey 不能误改;codemod 不能越界;审查必须绑定精确提交。
169
+
170
+ ### 场景 C:事故 hotfix
171
+
172
+ 生产一行修复。用普通 agent,按你正常的提交/推送流程处理。Tower 会额外增加 worktree、reviewer、门禁与合并步骤;对一行紧急修复主要是延迟。如果"hotfix"实际是多个协调变更,Tower 才合理。
173
+
174
+ ### 场景 D:大范围代码审计
175
+
176
+ "找出所有已弃用 crypto 的用法并总结风险。" 用 Workflow 或并行子代理;没有要合并的分支,也没有单一被审查变更。如果审计产出一组修复,再从这些 findings 创建 Tower mission。
177
+
178
+ ### 场景 E:敏感发布
179
+
180
+ 有外部用户的库发布。为变更、迁移指南、release notes 规划 mission;要求测试与审查;用 `tower_batch` 只有在所有必需检查齐全时才收口发布证据;按你的发布流程在 Tower 之外合并/打 tag。
181
+
182
+ ## 6. 实践中的选择路径
183
+
184
+ 1. **输出是 Git 变更吗?** 否 → 普通 agent、Agent Teams、Workflow 或 Ralph。
185
+ 2. **你会为它开 PR 吗?** 否 → Tower 太重,用普通 agent。
186
+ 3. **切片有稳定 scope 和已知完成定义吗?** 否 → 先规划/探索,再回到 Tower。
187
+ 4. **需要独立审查与合并门禁吗?** 是 → Tower;否,普通子代理可能足够。
188
+ 5. **需要大量并行分析或重复新鲜尝试吗?** 那个阶段用 Workflow/Ralph;把结果编码成 Tower mission。
189
+ 6. **需要横切的人类协作吗?** 用 Agent Teams 决定;用 Tower 执行决定的仓库工作。
190
+
191
+ ## 7. 成本、限制与反模式
192
+
193
+ - **每个 mission 有真实开销**:worktree、spawn、完成检查、审查轮次、门禁、合并。mission 应有意义(大致 PR 大小),不要一个函数一个 mission。
194
+ - **合并会串行**:重叠 scope 自动加依赖;独立 mission 仍然逐个合入 base。合并密集的工作不要期待线性加速。
195
+ - **审查不是免费**:每个 mission 需要 reviewer 与 clean 结论;豁免是显式开启且需要理由,不是默认。
196
+ - **过细的 mission** 增加协调与合并成本;**过粗的 mission** 失去并行审查收益,scope 冲突概率高。
197
+ - **Tower 不是沙箱**:它是协调协议;Host 权限、仓库 hooks、测试命令仍以 DSH 用户的 OS 权限执行。
198
+ - **不要同时跑两套编排协议**:Tower 模式会拒绝 Team/Workflow/Ralph/subagent 工具并清除 plan mode;请顺序分阶段。
199
+ - **不要用 Tower 掩盖需求缺失**:错误的 mission 契约会产出错误分支,然后在审查失败。先探索。
200
+
201
+ ## 8. 开始前的检查清单
202
+
203
+ - [ ] 工作会产生值得 PR 式审查的仓库变更。
204
+ - [ ] 我能列出 2–8 个 scope 不重叠、依赖顺序大致的 mission。
205
+ - [ ] 每个 mission 有可测试的完成定义(任务、checks/测试、审查)。
206
+ - [ ] 我知道哪些 mission 可并行、哪些必须等待。
207
+ - [ ] 我接受审查/合并开销,并会检查 `MISSIONS.md`/分支。
208
+ - [ ] 当前没有在同一会话依赖 Team 任务板、Workflow 脚本、Ralph 循环或 plan mode。
209
+ - [ ] 如果 worker 卡住(interrupt/adopt)或合并冲突,我有恢复路径。
210
+
211
+ 多数勾选时,Tower 很可能合适;否则先用更轻的模式,等变更成形后再回来。
@@ -23,7 +23,8 @@
23
23
 
24
24
  检查覆盖:git 可用性、仓库身份、state schema/加载、仓库配置是否存在、worker/reviewer preset 名称、
25
25
  subagents 与 HMR 服务、workspace 锁持有者/是否过期、实时 roster、worktree 是否存在/归属/是否脏、
26
- test-discovery 策略、旧版快照、batch 与租约。`warn` 是提示;`fail` 表示仓库在 spawn/merge 前需要
26
+ test-discovery 策略、旧版快照、batch 与租约。被 `tower_teardown` 正常清理的 mission worktree 会被
27
+ 跳过(检查中会注明已移除数量),不会被误报为 missing。`warn` 是提示;`fail` 表示仓库在 spawn/merge 前需要
27
28
  处理。state 加载失败会作为失败的检查报告而不是抛出,因此 doctor 在损坏的仓库上仍然有用。
28
29
 
29
30
  环境契约仅供参考;它不是身份或授权证明。`tower_init` 仍要求控制塔角色,`tower_spawn` 仍会检查
@@ -9,10 +9,12 @@
9
9
  - `create` 冻结 `missionIds`、`exclusions`、`requiredChecks`(默认 `["check"]`)与精确的相对
10
10
  `receiptPaths`。成员必须是已知 mission,且只有暂停的旧版 mission 可以被排除;创建后成员关系不可变。
11
11
  - `status` 汇总成员与待处理/失败的检查。
12
- - `close` 校验已完成成员的每条必需检查(来自 `completionReport.checks`、`testRuns` 或 close 时的
13
- `checks`),在提供 `treeTip` 时校验它,并要求每个冻结的回执路径都在
12
+ - `close` 校验已完成、**merged** 或 abandoned 成员的每条必需检查(来自 `completionReport.checks`、
13
+ `testRuns` 或 close 时的 `checks`),在提供 `treeTip` 时校验它,并要求每个冻结的回执路径都在
14
14
  `receiptHashes`/`receipts` 中提供(或通过 `receiptHash` 绑定到通过的检查)。缺少证据会返回
15
15
  `{ok:false, blockers}`;不会删除任何内容。
16
+ - `abandon` 用 `batchId`(可选 `reason`)退掉过期的 open batch,不修改 mission 状态;当证据窗口
17
+ 已过(例如先 merge 后 close)时用它收尾。
16
18
 
17
19
  close 失败不是部分关闭:batch 保持 `open`,修复证据后可以重试。
18
20
 
@@ -20,7 +20,7 @@ status 上的 `include_gate`/`includeGate`、adopt 上的 `retire_missing`/`reti
20
20
 
21
21
  roster 角色由 `exec.agent` 与 roster 共同决定。mission 级工具(`tower_interrupt`、
22
22
  `tower_init`、`tower_plan`、`tower_spawn`、`tower_merge`、`tower_rebase`、`tower_teardown`、
23
- `tower_adopt`、`tower_reload`)**仅限控制塔**。`tower_batch` 的 create/close、`tower_resource`
23
+ `tower_adopt`、`tower_reload`)**仅限控制塔**。`tower_batch` 的 create/close/abandon、`tower_resource`
24
24
  的 acquire/release、`tower_import_legacy`、`tower_legacy_map` 的 map 以及 review-exemption 规划
25
25
  仅限控制塔;它们的只读 status 动作对 roster 角色开放。worker 调用 `tower_mission` 时只能使用
26
26
  `task_done`/`task_drop`/`blocker`/`note`;状态变更、扩大 scope、`clear_blockers` 与 `completed`
@@ -55,7 +55,7 @@ roster 角色由 `exec.agent` 与 roster 共同决定。mission 级工具(`tow
55
55
  | `tower_reload` | `repoRoot?` | 仅开发用:请求 DSH HMR 对账 profile 并从磁盘重载 Host 插件。`dev.hotReload` 开启时注册;在工具结果返回后调度(默认 50 ms);没有 HMR 时以 `reload-unavailable` 失败。传入 `repoRoot` 时调用者必须是控制塔。 |
56
56
  | `tower_doctor` | —(需要 `repoRoot`) | 只读健康契约:git、state schema、config、preset/subagents/HMR、workspace 锁、roster、worktree realpath/归属、测试配置、leases。返回 `{ok, checks[{id,status,detail}], environmentContract}`。从不写状态。 |
57
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。 |
58
+ | `tower_batch` | `action`(`create`/`status`/`close`/`abandon`)、`missionIds?`、`exclusions?`、`requiredChecks?`、`receiptPaths?`、`batchId?`、`checks?`、`treeTip?`、`receiptHashes?`、`reason?` | 有限证据批次。create 冻结成员/检查/回执;close 校验每条必需检查与回执哈希;`merged` 成员也可参与 close;abandon 可退掉过期的 open batch 且不删除 mission。成员关系不可变。 |
59
59
  | `tower_resource` | `action`(`acquire`/`release`/`status`)、`name?`、`holder?`、`token?`、`ttlMs?`、`force?` | 每个仓库的咨询式租约,位于 `<common>/dsh-tower/leases.json`。没有 OS CPU/内存硬上限;过期的租约需要显式 `force` 与正确 token。 |
60
60
  | `tower_import_legacy` | `sourcePath` | 只读导入 Tower Codex `schemaVersion:1` 或 DSH 旧版 state。完整 payload 存到 `<common>/dsh-tower/legacy/<sha256>.json`;state 中只保存摘要。绝不激活/接管/恢复暂停的 mission。 |
61
61
  | `tower_legacy_map` | `action`(`map`/`status`)、`sourceHash?`、`oldId?`、`newMissionId?` | 仅审计用途的旧 id 映射;映射只追加,绝不容纳 mission。 |
package/lib/batch.js CHANGED
@@ -438,8 +438,8 @@ export function validateClose(state, batch, input = {}) {
438
438
  blockers.push(blocker(`batch mission "${missionId}" does not exist`))
439
439
  continue
440
440
  }
441
- if (mission.status !== 'completed' && mission.status !== 'abandoned') {
442
- blockers.push(blocker(`mission ${missionId} is ${mission.status}, not completed or abandoned`))
441
+ if (!['completed', 'merged', 'abandoned'].includes(mission.status)) {
442
+ blockers.push(blocker(`mission ${missionId} is ${mission.status}, not completed, merged or abandoned`))
443
443
  continue
444
444
  }
445
445
  for (const name of requiredChecks) {
@@ -530,8 +530,8 @@ export function summarize(state, batch) {
530
530
  blockers.push(blocker(`batch mission "${missionId}" does not exist`))
531
531
  continue
532
532
  }
533
- if (mission.status !== 'completed' && mission.status !== 'abandoned') {
534
- blockers.push(blocker(`mission ${missionId} is ${mission.status}, not completed or abandoned`))
533
+ if (!['completed', 'merged', 'abandoned'].includes(mission.status)) {
534
+ blockers.push(blocker(`mission ${missionId} is ${mission.status}, not completed, merged or abandoned`))
535
535
  continue
536
536
  }
537
537
  for (const name of requiredChecks) {
package/lib/config.js CHANGED
@@ -60,7 +60,7 @@ export const Config = Schema.object({
60
60
  mode: Schema.object({
61
61
  promptSection: Schema.string().description('Replacement Tower-mode system prompt text. Empty keeps the built-in reminder.'),
62
62
  sectionAppend: Schema.string().description('Extra text appended to the Tower-mode system prompt section (team conventions).'),
63
- allowAgentControl: Schema.boolean().default(false).description('Register tower_control so the model may enter/leave the mode with a reason.'),
63
+ allowAgentControl: Schema.boolean().description('Register tower_control so the model may enter/leave the mode with a reason.'),
64
64
  autoPauseGoal: Schema.boolean().default(true).description('Pause an active goal when entering Tower mode.'),
65
65
  requireResumeAuthorization: Schema.boolean()
66
66
  .default(false)
@@ -278,6 +278,10 @@ export function resolveTowerConfig(rawConfig) {
278
278
  if (rawMode.allowAgentControl === undefined && raw.allowAgentControl !== undefined) {
279
279
  mode.allowAgentControl = raw.allowAgentControl
280
280
  }
281
+ // Apply the canonical default only after alias resolution: a validated
282
+ // Cordis config may omit this key, and an explicit legacy top-level
283
+ // `allowAgentControl:true` must not be shadowed by a schema default.
284
+ if (mode.allowAgentControl === undefined) mode.allowAgentControl = false
281
285
  if (!Array.isArray(mode.denyTools) || mode.denyTools.length === 0) mode.denyTools = [...DEFAULT_DENY_TOOLS]
282
286
  return {
283
287
  section: mode.promptSection ?? '',
package/lib/doctor.js CHANGED
@@ -99,8 +99,13 @@ export async function runDoctor({ tower, ctx, agent }) {
99
99
 
100
100
  let missingWorktrees = 0
101
101
  let dirtyWorktrees = 0
102
+ let removedWorktrees = 0
102
103
  for (const mission of state.missions ?? []) {
103
104
  if (!mission.worktree) continue
105
+ if (mission.worktreeRemovedAt) {
106
+ removedWorktrees += 1
107
+ continue
108
+ }
104
109
  try {
105
110
  await tower.assertWorktree(mission.worktree)
106
111
  if (await exists(mission.worktree.path)) {
@@ -113,12 +118,13 @@ export async function runDoctor({ tower, ctx, agent }) {
113
118
  missingWorktrees += 1
114
119
  }
115
120
  }
121
+ const removedNote = removedWorktrees > 0 ? ` (${removedWorktrees} removed by teardown)` : ''
116
122
  checks.push(
117
123
  missingWorktrees > 0
118
- ? check('worktrees', 'fail', `${missingWorktrees} mission worktree(s) are missing or invalid`, 'run tower_preflight or tower_recover')
124
+ ? check('worktrees', 'fail', `${missingWorktrees} mission worktree(s) are missing or invalid${removedNote}`, 'run tower_preflight or tower_recover')
119
125
  : dirtyWorktrees > 0
120
- ? check('worktrees', 'warn', `${dirtyWorktrees} worktree(s) have uncommitted changes`)
121
- : check('worktrees', 'pass', 'all registered worktrees are valid'),
126
+ ? check('worktrees', 'warn', `${dirtyWorktrees} worktree(s) have uncommitted changes${removedNote}`)
127
+ : check('worktrees', 'pass', `all registered worktrees are valid${removedNote}`),
122
128
  )
123
129
 
124
130
  const testConfigured = state.missions?.some((mission) => mission.testRuns !== undefined)
package/lib/errors.js CHANGED
@@ -79,11 +79,11 @@ const RULES = [
79
79
  ['not-authorized', /only the tower|only an assigned reviewer|only the tower or a worker|does not own mission|not authorized|may not perform this action/i],
80
80
  [
81
81
  'invalid-transition',
82
- /cannot move from|cannot be completed from that state|is already completed|merged is recorded only|can only be widened|only a completed mission|reviewers may list and read|still has \d+ open task|has unresolved blockers|has no worktree|has no commits since|cannot complete with \d+ unread inbox|not assigned to mission|is a [a-z]+, not a worker|cannot reanchor base|worktree is missing|worktree .* is not a Git worktree|belongs to a different repository|is on .* expected|has \d+ live worker|already has a live worker|cannot reopen a terminal mission|stopped worker cannot report completion|violates? (?:the )?mission (?:state|transition)/i,
82
+ /cannot move from|cannot be completed from that state|is already completed|merged is recorded only|can only be widened|only a completed mission|reviewers may list and read|still has \d+ open task|has unresolved blockers|has no worktree|has no commits since|cannot complete with \d+ unread inbox|not assigned to mission|is a [a-z]+, not a worker|cannot reanchor base|worktree is missing|worktree .* is not a Git worktree|belongs to a different repository|is on .* expected|has \d+ live worker|already has a live worker|cannot reopen a terminal mission|stopped worker cannot report completion|can only be spawned|violates? (?:the )?mission (?:state|transition)/i,
83
83
  ],
84
84
  [
85
85
  'invalid-argument',
86
- /requires |is required|must be (?:a|an|one|an integer|non-empty|printable)|must not be|must be one of|no open task matching|unknown mission status|unknown finding severity|unknown finding type|not an array|is not an array|scope must|title must|checks must|role must|timeoutMs must|repoRoot is required|only a list filter|snapshotDirty was requested|refusing to send a message to yourself|mission has \d+ live worker|roster name already used|is already in the roster/i,
86
+ /requires |is required|must be (?:a|an|one|an integer|non-empty|printable)|must not be|must be one of|no open task matching|unknown mission status|unknown finding severity|unknown finding type|not an array|is not an array|scope must|title must|checks must|role must|timeoutMs must|repoRoot is required|only a list filter|snapshotDirty was requested|refusing to send a message to yourself|mission has \d+ live worker|roster name already used|is already in the roster|depends on unknown mission/i,
87
87
  ],
88
88
  ]
89
89
 
package/lib/tools.js CHANGED
@@ -1038,6 +1038,11 @@ export function registerTowerTools(ctx, options = {}) {
1038
1038
  fail(`reviewer requires a completed mission awaiting review; ${context.mission.id} is ${context.mission.status}`)
1039
1039
  }
1040
1040
  }
1041
+ if (role === 'worker' && ['merged', 'abandoned'].includes(context.mission.status)) {
1042
+ fail(
1043
+ `mission ${context.mission.id} is ${context.mission.status}; a worker can only be spawned for a planned or active mission`,
1044
+ )
1045
+ }
1041
1046
  const spawned = await enqueueSpawn(tower.root, async () => {
1042
1047
  const view = await tower.rosterView()
1043
1048
  const inactiveStates = ROSTER_INACTIVE_STATES
@@ -1834,9 +1839,9 @@ export function registerTowerTools(ctx, options = {}) {
1834
1839
  ok: gate.ok,
1835
1840
  blockers: gate.blockers,
1836
1841
  missionId: args.missionId,
1837
- branchTip: gate.branchTip,
1838
- baseTip: gate.baseTip,
1839
- changedFiles: gate.changedFiles,
1842
+ branchTip: gate.branchTip ?? null,
1843
+ baseTip: gate.baseTip ?? null,
1844
+ changedFiles: gate.changedFiles ?? [],
1840
1845
  review: gate.review
1841
1846
  ? {
1842
1847
  round: gate.review.round,
@@ -2019,17 +2024,18 @@ export function registerTowerTools(ctx, options = {}) {
2019
2024
  define(ctx, {
2020
2025
  name: 'tower_batch',
2021
2026
  description:
2022
- 'Manage finite evidence batches: create freezes missionIds/requiredChecks/receiptPaths, status summarizes members, close verifies every required check and receipt hash. Membership and exclusions are immutable after create; close never removes missions.',
2027
+ 'Manage finite evidence batches: create freezes missionIds/requiredChecks/receiptPaths, status summarizes members, close verifies every required check and receipt hash, abandon retires a stale open batch. Membership and exclusions are immutable after create; close/abandon never remove missions.',
2023
2028
  parameters: {
2024
2029
  type: 'object',
2025
2030
  properties: {
2026
2031
  repoRoot: { type: 'string', description: 'Absolute path inside the target Git repository.' },
2027
- action: { type: 'string', enum: ['create', 'status', 'close'], description: 'Batch operation.' },
2032
+ action: { type: 'string', enum: ['create', 'status', 'close', 'abandon'], description: 'Batch operation.' },
2028
2033
  missionIds: { type: 'array', items: { type: 'string' }, description: 'Exact mission ids in this batch (create).' },
2029
2034
  exclusions: { type: 'array', items: { type: 'string' }, description: 'Paused legacy mission ids excluded from the batch.' },
2030
2035
  requiredChecks: { type: 'array', items: { type: 'string' }, description: 'Check names every build member must record (default ["check"]).' },
2031
2036
  receiptPaths: { type: 'array', items: { type: 'string' }, description: 'Exact relative receipt files checked at close.' },
2032
- batchId: { type: 'string', description: 'Batch id (status/close).' },
2037
+ batchId: { type: 'string', description: 'Batch id (status/close/abandon).' },
2038
+ reason: { type: 'string', description: 'Reason recorded when abandoning a batch.' },
2033
2039
  checks: { type: 'array', items: { type: 'object' }, description: 'Close-time extra check evidence.' },
2034
2040
  treeTip: { type: 'string', description: 'Expected reviewed/tested commit for close evidence.' },
2035
2041
  receiptHashes: { type: 'object', description: 'Relative receipt path -> sha256 observed by the caller.' },
@@ -2056,13 +2062,17 @@ export function registerTowerTools(ctx, options = {}) {
2056
2062
  ...(args.batchId ? { summary: summarizeBatch(state, batch) } : {}),
2057
2063
  }
2058
2064
  }
2059
- requireTowerActor(actor, args.action === 'create' ? 'create a batch' : 'close a batch')
2065
+ requireTowerActor(actor, args.action === 'create' ? 'create a batch' : args.action === 'abandon' ? 'abandon a batch' : 'close a batch')
2060
2066
  if (args.action === 'create') {
2061
2067
  const batch = await tower.createBatch(args, actor.name)
2062
2068
  return { ok: true, action: 'create', batch, batches: (await tower.batchStatus()).batches }
2063
2069
  }
2064
2070
  if (typeof args.batchId !== 'string' || args.batchId.trim().length === 0) {
2065
- fail('tower_batch close requires batchId')
2071
+ fail(`tower_batch ${args.action} requires batchId`)
2072
+ }
2073
+ if (args.action === 'abandon') {
2074
+ const result = await tower.abandonBatch(args.batchId.trim(), actor.name, args.reason)
2075
+ return { ok: true, action: 'abandon', batch: result.batch, blockers: [] }
2066
2076
  }
2067
2077
  const result = await tower.closeBatch(args.batchId.trim(), {
2068
2078
  checks: args.checks,
package/lib/tower.js CHANGED
@@ -1268,6 +1268,26 @@ export class Tower {
1268
1268
  })
1269
1269
  }
1270
1270
 
1271
+ async abandonBatch(batchId, actor = 'tower', reason = null) {
1272
+ return withLock(this.paths, async () => {
1273
+ const state = await this.load()
1274
+ const batch = (state.batches ?? []).find((entry) => entry.id === batchId)
1275
+ if (!batch) fail(`unknown batch "${batchId}"`)
1276
+ if (batch.status === 'closed') fail(`batch ${batchId} is already closed`)
1277
+ if (batch.status === 'abandoned') return { ok: true, batch, alreadyAbandoned: true }
1278
+ batch.status = 'abandoned'
1279
+ batch.abandonedAt = Date.now()
1280
+ batch.abandonedBy = actor
1281
+ batch.abandonReason = typeof reason === 'string' && reason.trim() ? reason.trim() : null
1282
+ state.revision += 1
1283
+ state.updatedAt = batch.abandonedAt
1284
+ validateState(state, this.paths.state)
1285
+ await writeJsonAtomic(this.paths.state, state)
1286
+ await appendJsonl(this.paths.activity, { at: batch.abandonedAt, type: 'batch', text: `abandoned ${batch.id}` })
1287
+ return { ok: true, batch, alreadyAbandoned: false }
1288
+ })
1289
+ }
1290
+
1271
1291
  async batchStatus(batchId) {
1272
1292
  const state = await this.load()
1273
1293
  const batch = batchId
@@ -1991,7 +2011,15 @@ export class Tower {
1991
2011
  const state = await this.load()
1992
2012
  const mission = state.missions.find((entry) => entry.id === input.missionId)
1993
2013
  if (!mission) {
1994
- return { ok: false, blockers: [{ code: 'mission-missing', detail: `unknown mission "${input.missionId}"` }] }
2014
+ return {
2015
+ ok: false,
2016
+ blockers: [{ code: 'mission-missing', detail: `unknown mission "${input.missionId}"` }],
2017
+ missionId: input.missionId,
2018
+ branchTip: null,
2019
+ baseTip: null,
2020
+ changedFiles: [],
2021
+ review: null,
2022
+ }
1995
2023
  }
1996
2024
  const activity = input.activity ?? {}
1997
2025
  const blockers = []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-tower",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "type": "module",
5
5
  "description": "DSH-native Tower: mission planning, worktree isolation, review-gated merges, and a human-owned Tower mode.",
6
6
  "license": "MIT",