dsh-tower 1.3.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 +109 -0
- package/CONTRIBUTING.md +1 -1
- package/README.en.md +44 -8
- package/README.md +42 -8
- package/cordis.patch.yml +11 -1
- package/docs/README.md +10 -5
- package/docs/architecture/overview.md +13 -9
- package/docs/faq.md +45 -2
- package/docs/guide/hot-reload.md +189 -0
- package/docs/guide/install-upgrade-migration.md +11 -0
- package/docs/guide/quickstart.md +33 -1
- package/docs/guide/scenarios-and-philosophy.md +309 -0
- package/docs/operations/runbook.md +23 -0
- package/docs/operations/security.md +23 -0
- package/docs/reference/blockers.md +10 -2
- package/docs/reference/config.md +65 -2
- package/docs/reference/doctor.md +33 -0
- package/docs/reference/errors.md +50 -0
- package/docs/reference/events.md +45 -0
- package/docs/reference/operations.md +55 -0
- package/docs/reference/testing.md +61 -0
- package/docs/reference/tools.md +65 -23
- package/docs/zh/README.md +60 -0
- package/docs/zh/faq.md +81 -0
- package/docs/zh/guide/hot-reload.md +178 -0
- package/docs/zh/guide/install-upgrade-migration.md +57 -0
- package/docs/zh/guide/quickstart.md +96 -0
- package/docs/zh/guide/scenarios-and-philosophy.md +211 -0
- package/docs/zh/operations/security.md +42 -0
- package/docs/zh/reference/blockers.md +62 -0
- package/docs/zh/reference/config.md +113 -0
- package/docs/zh/reference/doctor.md +31 -0
- package/docs/zh/reference/errors.md +52 -0
- package/docs/zh/reference/events.md +46 -0
- package/docs/zh/reference/operations.md +50 -0
- package/docs/zh/reference/testing.md +60 -0
- package/docs/zh/reference/tools.md +75 -0
- package/index.js +261 -13
- package/lib/batch.js +578 -0
- package/lib/config.js +275 -9
- package/lib/doctor.js +171 -0
- package/lib/errors.js +111 -0
- package/lib/hmr.js +196 -0
- package/lib/leases.js +317 -0
- package/lib/legacy.js +413 -0
- package/lib/modeStore.js +28 -1
- package/lib/store.js +24 -1
- package/lib/tests.js +628 -0
- package/lib/tools.js +1566 -184
- package/lib/tower.js +986 -103
- package/lib/workspaceLock.js +117 -0
- package/lib/worktreeOwnership.js +166 -0
- package/package.json +4 -1
- package/reviewer.js +1 -0
- package/scripts/check-docs.mjs +135 -0
- package/scripts/enable-dev-hmr.mjs +347 -0
- package/skills/tower-review.md +4 -2
- package/skills/tower-worker-protocol.md +3 -1
- package/worker.js +4 -2
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Hot reload (development)
|
|
2
|
+
|
|
3
|
+
Tower is a Host plugin: its tools and prompt sections are loaded when DSH boots. DSH already ships
|
|
4
|
+
`@deepseek-ai/dsh-hmr`, a service that **reconciles the profile in place** — it re-reads the profile
|
|
5
|
+
patch layers, replaces modules whose files changed (including a linked package such as a `dsh-tower`
|
|
6
|
+
checkout), restarts the affected Loader entries and reports anything that needs a full restart. Tower
|
|
7
|
+
uses that service in development so Host-side changes apply without restarting DSH.
|
|
8
|
+
|
|
9
|
+
This page covers the development loop. It is not required to use Tower.
|
|
10
|
+
|
|
11
|
+
## What HMR does and does not do
|
|
12
|
+
|
|
13
|
+
`dsh --profile <name> --dump-config` prints the composed hmr row:
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
- id: hmr
|
|
17
|
+
name: '@deepseek-ai/dsh-hmr'
|
|
18
|
+
disabled: !!js '!ctx.get(''profileContext'')'
|
|
19
|
+
config:
|
|
20
|
+
root: []
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- `root: []` is the shipped default: **no source files are watched**. HMR is mounted but idle for
|
|
24
|
+
file-watching, so a running DSH does not reload when you edit the checkout.
|
|
25
|
+
- A reconciliation can still be requested explicitly (`/tower reload`, `tower_reload` or
|
|
26
|
+
`plugin_manager`); it compares already-loaded modules with disk and replaces the changed ones even
|
|
27
|
+
with `root: []`. Watching only makes that happen automatically after a save.
|
|
28
|
+
- HMR is only mounted for profile boots (`disabled` when there is no `profileContext`). A process
|
|
29
|
+
booted without a profile reports reload as unavailable (`tower_reload` stays registered while
|
|
30
|
+
`dev.hotReload` is on, so the tool returns `reload-unavailable` instead of disappearing).
|
|
31
|
+
- The profile patch layer is loaded when DSH boots. Editing it — in practice the hmr `root` list —
|
|
32
|
+
takes effect after **one DSH restart**. Nothing in the running process re-reads the HMR row's own
|
|
33
|
+
configuration.
|
|
34
|
+
|
|
35
|
+
## Enable watching for a checkout
|
|
36
|
+
|
|
37
|
+
From a checkout of this repository:
|
|
38
|
+
|
|
39
|
+
```powershell
|
|
40
|
+
npm run dev:hmr
|
|
41
|
+
# equivalent to:
|
|
42
|
+
node scripts/enable-dev-hmr.mjs
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The script appends one marked block to the profile's user patch layer,
|
|
46
|
+
`<DSH_HOME>/profiles/<profile>/cordis.patch.yml` (`DSH_HOME` defaults to `~/.dsh`; on Windows
|
|
47
|
+
usually `C:\Users\<you>\.dsh`):
|
|
48
|
+
|
|
49
|
+
```yaml
|
|
50
|
+
# dsh-tower:dev-hmr
|
|
51
|
+
- id: hmr
|
|
52
|
+
config:
|
|
53
|
+
root:
|
|
54
|
+
- 'D:/work/dsh-tower'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Options:
|
|
58
|
+
|
|
59
|
+
| Flag | Meaning |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `--profile <name>` | Profile to edit. Default `desktop` (the profile the desktop app boots). |
|
|
62
|
+
| `--root <dir>` | Repository directory to watch. Default: the current working directory, canonicalised. |
|
|
63
|
+
| `--dry-run` | Print a diff of the patch file and exit without writing. |
|
|
64
|
+
| `--remove` | Delete the marked block again. |
|
|
65
|
+
|
|
66
|
+
Notes:
|
|
67
|
+
|
|
68
|
+
- The script is idempotent: re-running it replaces the marked block and leaves every other line of the
|
|
69
|
+
patch file untouched. Run it from the repository root, or pass `--root D:\work\dsh-tower`.
|
|
70
|
+
- The value written is the `realpath` of the root, with forward slashes. **HMR `root` accepts
|
|
71
|
+
absolute paths**; relative entries would resolve against the profile directory and are easy to get
|
|
72
|
+
wrong, so the script always writes an absolute one.
|
|
73
|
+
- The profile must already exist. The script never creates a profile and never creates directories
|
|
74
|
+
outside `<DSH_HOME>/profiles/<profile>/`.
|
|
75
|
+
- Pick the profile the desktop app actually boots (`desktop` by default). The desktop profile is
|
|
76
|
+
managed by the Electron app, so restart it by quitting and relaunching DSH.
|
|
77
|
+
|
|
78
|
+
**Restart DSH once** after writing the block. After that restart:
|
|
79
|
+
|
|
80
|
+
- HMR watches the checkout (except `node_modules`, dot-directories, `cache` and `data`, which the
|
|
81
|
+
shipped `ignored` list excludes) and reconciles the profile after edits while DSH stays open.
|
|
82
|
+
- `/tower reload` (and the `tower_reload` tool, when registered) reconciles on demand even for files
|
|
83
|
+
the watcher does not cover.
|
|
84
|
+
|
|
85
|
+
To stop watching, run `npm run dev:hmr -- --remove` and restart DSH once.
|
|
86
|
+
|
|
87
|
+
## The edit loop
|
|
88
|
+
|
|
89
|
+
1. Edit `lib/*.js`, `index.js`, `worker.js`, `reviewer.js` or `skills.js`.
|
|
90
|
+
2. Save the file. The watcher debounces (DSH default 100 ms) and reconciles the profile; changed
|
|
91
|
+
modules are replaced and the affected Loader entries restart.
|
|
92
|
+
3. Watch the DSH log for `reload plugin at <path>` / `restart required: …` messages.
|
|
93
|
+
4. Optionally call `/tower reload` or `tower_reload` to reconcile on demand instead of waiting.
|
|
94
|
+
|
|
95
|
+
Editing the repository's bundle patch (`cordis.patch.yml`) changes the plugin declaration itself;
|
|
96
|
+
restart DSH after such a change rather than assuming a hot replacement.
|
|
97
|
+
|
|
98
|
+
The bridge behind the tool follows the `dev` config group:
|
|
99
|
+
|
|
100
|
+
| Config key | Meaning |
|
|
101
|
+
|---|---|
|
|
102
|
+
| `dev.hotReload` | Register `tower_reload`; `/tower reload` is disabled when off (default `true`). Without the HMR service the tool is still registered so it can return `reload-unavailable`. |
|
|
103
|
+
| `dev.reloadDelayMs` | Delay before the scheduled reconciliation runs, in milliseconds (default `50`), so the tool/command result reaches the model before the plugin is replaced. |
|
|
104
|
+
| `dev.reloadRequiredIds` | Loader entry ids the profile must keep active (default `tower`, `tower-skills`, `preset-tower-worker`, `preset-tower-reviewer`). An inactive required entry is reported as a reload failure even if it was already inactive. |
|
|
105
|
+
|
|
106
|
+
A reload is **scheduled, not awaited**: `/tower reload` and `tower_reload` return immediately with
|
|
107
|
+
`scheduled`, `delayMs`, `requiredIds` (and the current `lastError` when one is pending), then HMR
|
|
108
|
+
reconciles the profile after the delay. This is deliberate — the tool result must be delivered before
|
|
109
|
+
the Tower plugin replaces itself. Two consequences:
|
|
110
|
+
|
|
111
|
+
- a burst of reload requests inside the delay coalesces into a single reconciliation;
|
|
112
|
+
- a reconciliation failure is recorded as `lastError` instead of coming back from the tool call, so
|
|
113
|
+
check `tower_mode.hotReload` and the DSH log after a reload.
|
|
114
|
+
|
|
115
|
+
### Reading a reload result
|
|
116
|
+
|
|
117
|
+
The outcome of the scheduled reconciliation surfaces in:
|
|
118
|
+
|
|
119
|
+
- **the DSH log** — `reload plugin at <path>` for replaced modules, plus HMR's warnings and
|
|
120
|
+
`restart required: <reason>` messages from its own watcher.
|
|
121
|
+
- **`tower_mode.hotReload`** — `{available, requiredIds, delayMs, scheduled, lastRunAt, lastError}`;
|
|
122
|
+
`lastError` keeps the most recent failure and is cleared by the next successful reload.
|
|
123
|
+
|
|
124
|
+
The reconciliation itself also returns `warnings`, `pending` (modules still on their previous code)
|
|
125
|
+
and an optional `restartReason`. When the reload was triggered from Tower those are not returned to
|
|
126
|
+
the model, so treat "new code does not appear" as a signal to restart DSH rather than to retry in a
|
|
127
|
+
loop.
|
|
128
|
+
|
|
129
|
+
If HMR is absent, `/tower reload` answers with an unavailable message and `tower_reload` is not
|
|
130
|
+
registered at all (both are gated on the HMR service at session start).
|
|
131
|
+
|
|
132
|
+
## Plugin manager and linked packages
|
|
133
|
+
|
|
134
|
+
`plugin_manager` actions (`install_bundle`, `set_plugin`, upgrades) apply to the running profile
|
|
135
|
+
immediately — they reconcile the profile themselves, so installing or updating a bundle does not
|
|
136
|
+
require a restart. This is the intended way to install `dsh-tower` while developing: add the checkout
|
|
137
|
+
as a bundle (`dsh plugin --profile <profile> add <repo-root>` or `install_bundle`), then let HMR and
|
|
138
|
+
the reload tool pick up code edits.
|
|
139
|
+
|
|
140
|
+
With the checkout linked as a package target, a changed file inside the checkout is replaced by the
|
|
141
|
+
next `reconcileProfile` or `plugin_manager` operation **without a restart**, even when `root: []`
|
|
142
|
+
means the change was not watched. What does need a restart:
|
|
143
|
+
|
|
144
|
+
- the hmr row's own configuration (`root`), because that list is read when the service activates;
|
|
145
|
+
- modules reported under `pending` or with a `restartReason`.
|
|
146
|
+
|
|
147
|
+
## Failure recovery
|
|
148
|
+
|
|
149
|
+
| Symptom | What to do |
|
|
150
|
+
|---|---|
|
|
151
|
+
| `[tower:reload-unavailable]` / "Tower reload is unavailable" | This process has no HMR service (booted without a profile, HMR disabled in the profile, or the service was disposed during shutdown). Restart DSH. To inspect the hmr row, run `dsh --profile <name> --dump-config` on a non-desktop profile (the desktop profile is managed by the Electron app and refuses the dump). |
|
|
152
|
+
| `[tower:reload-failed]` in `hotReload.lastError` with a syntax/import error | Fix the file, then restart DSH. A replacement that fails to import stops the previous plugin instance and its old code no longer runs; the reload itself cannot roll back. |
|
|
153
|
+
| `HMR is disposed` | The service shut down with the process. Restart DSH. |
|
|
154
|
+
| `HMR transactions cannot be nested` | An explicit reload was requested from inside another HMR transaction; call the tool from a normal tool turn instead. |
|
|
155
|
+
| `pending` is non-empty or `restartReason` is set | Restart DSH before trusting the reload result. |
|
|
156
|
+
| Edit saved but nothing reloads | Confirm the file is under the configured `root` (absolute path), is not excluded by `ignored` (`node_modules`, dot-directories, `cache`, `data`), and that the profile patch contains the marked block. Run `/tower reload` to reconcile manually. |
|
|
157
|
+
| Reload keeps reporting an inactive required entry | That Loader entry is disabled or failing: check the profile's plugins/bundles (a `preset-tower-*` entry is missing when a Tower preset bundle was removed) and restart after fixing it. |
|
|
158
|
+
|
|
159
|
+
The offline bridge tests are part of the normal suite:
|
|
160
|
+
|
|
161
|
+
```powershell
|
|
162
|
+
node --test tests/hmr.test.mjs
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Client bundles (`ui-bundle`) are different
|
|
166
|
+
|
|
167
|
+
HMR replaces **Host** plugins and prompt/tool code. The optional `ui-bundle` is a Client widget: its
|
|
168
|
+
JavaScript is downloaded and evaluated by the Web page, not by the Host process. After a Host reload
|
|
169
|
+
or a Client bundle install/update, the open page may keep the old widget — refresh the Web page (and,
|
|
170
|
+
if a tool schema changed, open a new session) before assuming the change failed. The Tower UI widget
|
|
171
|
+
is a hint only and is not required for hot reload.
|
|
172
|
+
|
|
173
|
+
For a live check of the reload path against an isolated profile, use
|
|
174
|
+
[`tests/live/hmr.cjs`](../../tests/live/hmr.cjs) with `DSH_TEST_URL` set to the throwaway server URL
|
|
175
|
+
(see [tests/README.md](../../tests/README.md)); it never touches the desktop profile or live sessions.
|
|
176
|
+
|
|
177
|
+
## Security
|
|
178
|
+
|
|
179
|
+
HMR executes the code it finds in the watched roots **inside the DSH Host process**, with your OS user
|
|
180
|
+
privileges, and it reloads it on save. Treat the profile patch layer like shell startup files:
|
|
181
|
+
|
|
182
|
+
- only point `root` at repositories you trust (this checkout), and keep the list small and absolute;
|
|
183
|
+
- do not add your home directory, a downloads folder, or a shared/network path;
|
|
184
|
+
- the patched profile is same-user trusted configuration; anyone who can write it can run code in
|
|
185
|
+
your DSH process;
|
|
186
|
+
- remove the block (`--remove`) and restart once when you no longer need watching.
|
|
187
|
+
|
|
188
|
+
See also: [install, upgrade and migration](install-upgrade-migration.md),
|
|
189
|
+
[configuration reference](../reference/config.md), [error codes](../reference/errors.md).
|
|
@@ -29,6 +29,17 @@ The Host plugin loads at DSH startup: **restart DSH after install or update**.
|
|
|
29
29
|
|
|
30
30
|
## Migration notes
|
|
31
31
|
|
|
32
|
+
- **1.x → 2.0 (unreleased)**: no state migration is needed (`schemaVersion` stays 1; new fields are
|
|
33
|
+
optional). Behavior changes to review: `mode.denyTools` now also denies `workflow`, `ralph`, `subagent`,
|
|
34
|
+
`subagent_fork`, `wait_agent` and `team_task_list` while Tower mode is active (override with an explicit
|
|
35
|
+
list); `spawn.maxLiveWorkers` defaults to 4; `mode.workspaceLock` is off by default. Workers can no
|
|
36
|
+
longer complete/abandon/scope a mission directly — use `tower_complete`. New tools: `tower_interrupt`,
|
|
37
|
+
and `tower_reload` when HMR is available. Restart DSH once after upgrading; see
|
|
38
|
+
[hot reload](hot-reload.md).
|
|
39
|
+
- **1.3 → 1.4**: no state migration is needed. New: repository-level config
|
|
40
|
+
(`<git-common-dir>/dsh-tower/config.json`), stable `[tower:<code>]` tool errors, `tower_findings`,
|
|
41
|
+
and `id`/`missionId` + snake/camel input aliases. Existing calls keep working; restart DSH to load
|
|
42
|
+
the new tool surface (`tower_findings` and the newer output schemas).
|
|
32
43
|
- **1.2 → 1.3**: reviewers moved from the shared `tower-worker` preset to the new read-only
|
|
33
44
|
`tower-reviewer` preset. Reinstall/restart so both preset rows exist. `tower_spawn role=reviewer`
|
|
34
45
|
now requires the mission to be `completed`.
|
package/docs/guide/quickstart.md
CHANGED
|
@@ -58,8 +58,40 @@ worktrees and branches.
|
|
|
58
58
|
|
|
59
59
|
## If something fails
|
|
60
60
|
|
|
61
|
-
Read the blocker code and use [the blocker reference](reference/blockers.md). The most common cases:
|
|
61
|
+
Read the blocker code and use [the blocker reference](../reference/blockers.md). The most common cases:
|
|
62
62
|
`stale-base` (run `tower_rebase`), `no-review` (spawn a reviewer), `merge-hook-failed` (retry, or raise
|
|
63
63
|
`timeoutMs`), `tip-moved` (the branch changed after review; review again).
|
|
64
64
|
|
|
65
|
+
Thrown tool failures carry a `[tower:<code>]` prefix; see [error codes](../reference/errors.md)
|
|
66
|
+
(e.g. `preset-missing` means restart DSH after installing the bundle, `not-initialized` means run
|
|
67
|
+
`tower_init` first).
|
|
68
|
+
|
|
69
|
+
## Optional: per-repository config
|
|
70
|
+
|
|
71
|
+
A repository can tune review, merge, worktree and spawn policy without touching the DSH profile. Put a
|
|
72
|
+
JSON file at `<git-common-dir>/dsh-tower/config.json` (usually `.git/dsh-tower/config.json`):
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"review": { "maxRoundsPerReviewer": 3 },
|
|
77
|
+
"merge": { "timeoutMs": 1800000 },
|
|
78
|
+
"worktrees": { "root": "D:/tower-worktrees" }
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The repository layer overrides the profile config; per-call tool arguments override both. See the
|
|
83
|
+
[config reference](../reference/config.md) for the full table and precedence.
|
|
84
|
+
|
|
85
|
+
## Optional: findings
|
|
86
|
+
|
|
87
|
+
Out-of-scope observations go to `tower_finding`. Later, list or close them without editing code:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
tower_findings repoRoot=D:\work\repo
|
|
91
|
+
tower_findings repoRoot=D:\work\repo id=F1 status=fixed note="fixed in M2"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Statuses are `open → triaged → fixed` / `wontfix`, and `fixed`/`wontfix` can reopen to `open`. Only the
|
|
95
|
+
tower or a worker may change a finding; reviewers can list and read.
|
|
96
|
+
|
|
65
97
|
A complete copy-paste example: [examples/first-mission.md](../examples/first-mission.md).
|
|
@@ -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,29 @@
|
|
|
23
23
|
leaves the branch untouched — then call `tower_merge` to record it. A conflict marks the mission
|
|
24
24
|
`blocked`; resolve in the worktree, set the mission back to `active` (or `completed`), retry.
|
|
25
25
|
|
|
26
|
+
## Interrupt, stale roster and takeover
|
|
27
|
+
|
|
28
|
+
A runaway agent: `tower_interrupt({to:"worker-m1",reason:"..."})` interrupts the host subagent turn and
|
|
29
|
+
records `state:"interrupted"`. `tower_roster` shows `stale:true` when an active entry has no catalog
|
|
30
|
+
activity beyond `spawn.staleMs`; `tower_adopt` retires such entries (`retireMissing:false` marks them
|
|
31
|
+
`missing` instead). After the old owner is interrupted/failed/missing, spawn a replacement with
|
|
32
|
+
`takeover:true`; a still-running owner returns `takeover-refused`.
|
|
33
|
+
|
|
34
|
+
## Workspace lock
|
|
35
|
+
|
|
36
|
+
With `mode.workspaceLock:true`, `<git-common-dir>/dsh-tower/tower.lock` names the owning session. A second
|
|
37
|
+
Tower session gets `[tower:workspace-busy]`. Let the owner run `/tower off`; if the owner is gone, wait
|
|
38
|
+
`mode.workspaceStaleMs` (default 5 min) for the stale takeover, or delete the lock deliberately only after
|
|
39
|
+
confirming no Tower session is active. Heartbeats refresh the lock every `mode.heartbeatMs`.
|
|
40
|
+
|
|
41
|
+
## Hot reload
|
|
42
|
+
|
|
43
|
+
`/tower reload` or `tower_reload` asks DSH HMR to reconcile the profile and reload the Host plugin; the
|
|
44
|
+
reload is scheduled after the tool result (`dev.reloadDelayMs`). It fails with `reload-unavailable` on a
|
|
45
|
+
profile without HMR. Run `npm run dev:hmr` once to add this checkout to the profile's HMR watch roots
|
|
46
|
+
(one DSH restart after the profile patch change); see [hot reload](../guide/hot-reload.md). If a reload
|
|
47
|
+
fails, the previous plugin code no longer runs: restart DSH and check the HMR log.
|
|
48
|
+
|
|
26
49
|
## State lock
|
|
27
50
|
|
|
28
51
|
`tower state is locked by another writer` means an operation holds `state.lock`. Wait and retry. Only if
|
|
@@ -7,6 +7,11 @@ Tower's guarantees are **coordination guarantees**, not a sandbox. Understand wh
|
|
|
7
7
|
- **Review binding.** `tower_review` records the branch tip it inspected; the gate rejects a moved tip
|
|
8
8
|
(`tip-moved`) unless a clean conflict-free rebase waived it. A reviewer that modifies the branch after
|
|
9
9
|
spawning is rejected (`reviewer must not modify the mission branch`).
|
|
10
|
+
- **Role enforcement.** The caller is resolved against the roster. Worker mission updates are limited to
|
|
11
|
+
task/blocker/note; `completed`, `abandoned`, scope widening and blocker clearing are tower-only, and
|
|
12
|
+
completion (with its unread-inbox/commit gates) must go through `tower_complete`. Tower-only tools reject
|
|
13
|
+
roster workers with `[tower:not-authorized]`. One live worker per mission; taking over a stopped worker
|
|
14
|
+
is explicit.
|
|
10
15
|
- **Read-only reviewer preset.** The `tower-reviewer` preset removes `write`, `edit`, `pwsh`,
|
|
11
16
|
`tower_mission` and `tower_complete` from the tool surface; reviewers can only read, diff, message,
|
|
12
17
|
file findings, and submit a review.
|
|
@@ -18,6 +23,20 @@ Tower's guarantees are **coordination guarantees**, not a sandbox. Understand wh
|
|
|
18
23
|
but must state a reason; the shipped patch enables that by default — set it to `false` to make the
|
|
19
24
|
mode human-only.
|
|
20
25
|
- **No automatic goal resume.** Entering pauses the active goal; leaving does not resume it.
|
|
26
|
+
- **Advisory workspace lock.** `mode.workspaceLock` prevents two Tower sessions from accidentally driving
|
|
27
|
+
one repository; it is a same-user coordination lock, not authentication between users.
|
|
28
|
+
- **Test discovery is repository code.** `tower_test`/`tower_complete` execute `package.json`
|
|
29
|
+
`tower.testDiscovery` commands in the mission worktree with the DSH process's OS permissions. Set
|
|
30
|
+
`tests.allowPackageDiscovery:false` for untrusted repositories; output/time budgets fail closed but are
|
|
31
|
+
not a sandbox.
|
|
32
|
+
- **Review exemption is opt-in.** `reviewPolicy:"none"` requires `gates.allowReviewExemption:true` and a
|
|
33
|
+
non-empty reason. It is off by default because a model-driven tower could otherwise self-exempt; the
|
|
34
|
+
exemption and its reason are recorded in state/events and never granted to workers.
|
|
35
|
+
- **Leases are coordination, not quotas.** `tower_resource` prevents accidental co-scheduling via a local
|
|
36
|
+
JSON ledger; it does not cap CPU, memory, or process count. DSH exposes no public Job-object quota API,
|
|
37
|
+
so OS-level isolation remains a deployment concern.
|
|
38
|
+
- **Legacy import is read-only.** Imported snapshots live under the canonical state directory and are never
|
|
39
|
+
activated, adopted, or resumed; an operator must explicitly re-plan/migrate work.
|
|
21
40
|
|
|
22
41
|
## Not enforced by the plugin (deployment concerns)
|
|
23
42
|
|
|
@@ -29,6 +48,10 @@ Tower's guarantees are **coordination guarantees**, not a sandbox. Understand wh
|
|
|
29
48
|
high-value branches.
|
|
30
49
|
- **Repository hooks.** `git merge` runs the target repository's hooks (`pre-merge-commit` etc.). Tower
|
|
31
50
|
cannot make those safe; review hooks before running Tower on an untrusted repository.
|
|
51
|
+
- **Package test commands.** Declared discovery commands have the same trust as repository hooks; a
|
|
52
|
+
malicious `package.json` can run code. Disable `tests.allowPackageDiscovery` and review the file.
|
|
53
|
+
- **HMR reload.** With `dev.hotReload`, `/tower reload`/`tower_reload` asks DSH to re-import the plugin
|
|
54
|
+
from disk. Only enable HMR watch roots for checkouts you trust; a reload executes the current files.
|
|
32
55
|
- **Prompt injection.** The reviewer reads untrusted branch content (diff/comments). The read-only preset
|
|
33
56
|
limits impact, but no model-side protection is absolute.
|
|
34
57
|
- **Base branch protection.** Tower writes to the local base branch; server-side branch protection/push
|