dsh-tower 2.4.2 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +72 -0
- package/COMPATIBILITY.md +2 -0
- package/CONTRIBUTING.md +5 -3
- package/GLOSSARY.md +73 -0
- package/PRINCIPLES.md +15 -7
- package/README.en.md +16 -15
- package/README.md +18 -10
- package/app/archive.js +16 -1
- package/app/board.js +16 -1
- package/app/gate.js +14 -0
- package/app/review.js +7 -0
- package/app/spawn-prechecks.js +30 -5
- package/app/tower.js +5 -5
- package/client.js +321 -0
- package/cordis.patch.yml +26 -9
- package/docs/adr/0004-surface-budget.md +10 -4
- package/docs/adr/0005-component-surface.md +222 -11
- package/docs/architecture/overview.md +12 -6
- package/docs/examples/first-mission.md +3 -2
- package/docs/faq.md +9 -7
- package/docs/guide/install-upgrade-migration.md +53 -7
- package/docs/guide/quickstart.md +91 -10
- package/docs/guide/release.md +60 -19
- package/docs/guide/scenarios-and-philosophy.md +35 -28
- package/docs/operations/security.md +11 -10
- package/docs/reference/blockers.md +12 -7
- package/docs/reference/component-surface.md +40 -21
- package/docs/reference/config.generated.md +6 -8
- package/docs/reference/config.md +36 -8
- package/docs/reference/doctor.md +11 -5
- package/docs/reference/errors.generated.md +1 -0
- package/docs/reference/errors.md +2 -1
- package/docs/reference/surface-budget.md +47 -12
- package/docs/reference/testing.md +1 -1
- package/docs/reference/tools.generated.md +5 -5
- package/docs/reference/tools.md +5 -5
- package/docs/zh/faq.md +7 -5
- package/docs/zh/guide/install-upgrade-migration.md +26 -7
- package/docs/zh/guide/quickstart.md +17 -10
- package/docs/zh/guide/scenarios-and-philosophy.md +23 -15
- package/docs/zh/operations/security.md +9 -8
- package/docs/zh/reference/blockers.md +11 -7
- package/docs/zh/reference/config.md +28 -8
- package/docs/zh/reference/doctor.md +7 -2
- package/docs/zh/reference/errors.md +2 -1
- package/docs/zh/reference/testing.md +2 -2
- package/docs/zh/reference/tools.md +6 -5
- package/domain/component-rules.js +216 -0
- package/domain/components.js +181 -53
- package/domain/config/schema.js +86 -28
- package/domain/contracts/outputs.js +2 -0
- package/domain/contracts/tool-inputs/missions.js +1 -1
- package/domain/errors.js +6 -0
- package/domain/gate.js +23 -5
- package/domain/integration/review.js +203 -9
- package/domain/scope.js +176 -5
- package/domain/surface.js +21 -7
- package/host/capability-archive.js +18 -0
- package/host/capability-board.js +18 -0
- package/host/capability-registry.js +4 -0
- package/host/config.js +40 -3
- package/host/doctor.js +68 -12
- package/host/presets.js +26 -8
- package/host/tool-registry.js +30 -5
- package/index.js +17 -6
- package/locale/archive/en.json +6 -0
- package/locale/archive/zh.json +6 -0
- package/locale/batch/en.json +1 -1
- package/locale/batch/zh.json +1 -1
- package/locale/board/en.json +6 -0
- package/locale/board/zh.json +6 -0
- package/locale/en.json +37 -22
- package/locale/hmr/en.json +2 -2
- package/locale/hmr/zh.json +1 -1
- package/locale/import/en.json +1 -1
- package/locale/import/zh.json +1 -1
- package/locale/leases/en.json +2 -2
- package/locale/leases/zh.json +2 -2
- package/locale/preset-reviewer/en.json +1 -1
- package/locale/preset-reviewer/zh.json +1 -1
- package/locale/preset-worker/en.json +1 -1
- package/locale/preset-worker/zh.json +1 -1
- package/locale/skills/en.json +1 -1
- package/locale/skills/zh.json +1 -1
- package/locale/tests/en.json +2 -2
- package/locale/tests/zh.json +2 -2
- package/locale/zh.json +36 -21
- package/package.json +17 -2
- package/scripts/check-components.mjs +698 -44
- package/scripts/check-pack.mjs +3 -0
- package/scripts/component-surface.json +101 -37
- package/scripts/generate-capability-locale.mjs +3 -0
- package/scripts/mount-smoke/surface.mjs +2 -0
- package/scripts/pack-manifest.txt +10 -0
- package/scripts/release-notes.mjs +256 -0
- package/scripts/surface-baseline.json +39 -3
- package/scripts/surface-baseline.mjs +15 -5
- package/scripts/types-coverage.mjs +7 -0
- package/skills.js +4 -1
- package/tools/complete.js +3 -1
- package/tools/preflight.js +1 -1
- package/tools/review.js +1 -1
- package/tools/spawn.js +7 -7
- package/tools/status.js +8 -1
- package/tools/test.js +7 -7
- package/types/index.d.ts +10 -0
- package/ui-bundle/locale/en.json +1 -1
- package/ui-bundle/locale/zh.json +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> Generated by `npm run docs:generate:components` from `scripts/component-surface.json` and
|
|
4
4
|
> `scripts/surface-baseline.json`. Do not edit by hand.
|
|
5
5
|
|
|
6
|
-
Decision: [ADR 0005](../adr/0005-component-surface.md) · decision date: 2026-
|
|
6
|
+
Decision: [ADR 0005](../adr/0005-component-surface.md) · decision date: 2026-11-01 · baseline 2026-10-23 · visible components: 11 (`tower`, `tower-skills`, `tower-batch`, `tower-import`, `tower-leases`, `tower-tests`, `tower-board`, `tower-archive`, `tower-hmr`, `preset-tower-reviewer`, `tower-ui`)
|
|
7
7
|
|
|
8
8
|
The component axis answers one question for every config key: may a user turn it
|
|
9
9
|
off, and if so, must that switch be a component? Four classes cover every key, so
|
|
@@ -14,7 +14,7 @@ off, and if so, must that switch be a component? Four classes cover every key, s
|
|
|
14
14
|
| `core` | Removing it breaks the Tower contract. It is never a switch. |
|
|
15
15
|
| `optional-component` | A capability whose off state leaves the rest of Tower intact. It must be a user-visible switch with a documented fallback. |
|
|
16
16
|
| `optional-config` | A tuning knob, not a capability. It stays in the config form and never hides a component. |
|
|
17
|
-
| `forbidden-to-disable` | The host, the health contract, the recovery escape
|
|
17
|
+
| `forbidden-to-disable` | The host, the health contract, the recovery escape and the merge gates. No group, row or boolean key may turn it off. |
|
|
18
18
|
|
|
19
19
|
## Components
|
|
20
20
|
|
|
@@ -29,16 +29,24 @@ row is a section of its parent and is not independently switchable.
|
|
|
29
29
|
| `tower-skills` | visible | row | The 3 skills leave the catalog; the worker/reviewer PROTOCOL sections still ship through systemPrompt.section, so only the tower-recovery guidance is lost. |
|
|
30
30
|
| `tower-batch` | visible | row | tower_batch is not registered; missions, gates and merges are unaffected because no state depends on a batch. |
|
|
31
31
|
| `tower-import` | visible | row | tower_import_legacy and tower_legacy_map are not registered; new repository state is unaffected. |
|
|
32
|
-
| `tower-leases` | visible | row | tower_resource is not registered; advisory leases are coordination only, so
|
|
32
|
+
| `tower-leases` | visible | row | tower_resource is not registered; advisory leases are coordination only, but spawn.requireLease still needs the tool to acquire one, so a config that sets spawn.requireLease is rejected at runtime preconditions instead of failing every spawn at lease-busy. |
|
|
33
33
|
| `tower-tests` | visible | row | tower_test is not registered and no test receipt can be recorded. With gates.requireTests:true planning/mode entry is rejected with an explanation, and the merge gate still reports tests-missing. |
|
|
34
|
-
| `tower-
|
|
34
|
+
| `tower-board` | visible | row | MISSIONS.md is not written or refreshed, so an existing board goes stale. Mission state tools, gates and every other capability keep working; docs.boardLocation still chooses where a board would be written. |
|
|
35
|
+
| `tower-archive` | visible | row | Terminal missions are not moved out of state.json, so the hot state keeps growing and status/doctor keep loading their full history. Merges, gates and every other capability keep working; nothing is deleted. afterDays/maxMissions keep their tuning meaning. |
|
|
36
|
+
| `tower-hmr` | visible | row | Development-only tooling: tower_reload and /tower reload are unavailable, so a linked-profile reload has to be triggered outside Tower. A normal profile behaves exactly as before; the row ships disabled on purpose. |
|
|
35
37
|
| `preset-tower-worker` | internal | row | Declared by dsh-tower/preset-worker: tower_spawn fails without the worker preset. A mandatory dependency, never a switch. |
|
|
36
|
-
| `preset-tower-reviewer` | visible | row | Declared by dsh-tower/preset-reviewer: the reviewer cannot be dispatched. Supported while review.
|
|
38
|
+
| `preset-tower-reviewer` | visible | row | Declared by dsh-tower/preset-reviewer: the reviewer cannot be dispatched. Supported while review.mode is off or optional (merge is unaffected; off does not use the preset at all); with review.mode required, planning/mode entry is locked with an explanatory error. |
|
|
39
|
+
| `tower-ui` | visible | row | The Tower web UI plugin does not load in this profile: no Tower tool cards, composer-dock summary or status strip. Host tools, missions, state and the MISSIONS.md board are unaffected. |
|
|
37
40
|
|
|
38
41
|
## Forbidden to disable
|
|
39
42
|
|
|
40
|
-
The host, the health contract, the recovery escape
|
|
41
|
-
|
|
43
|
+
The host, the health contract, the recovery escape and the merge gates are
|
|
44
|
+
configuration or code, never switches. The review gate in `required` mode is
|
|
45
|
+
also non-disableable: a completed mission still needs the review evidence bound
|
|
46
|
+
to the branch tip, and the exemption path stays explicit. The policy itself is
|
|
47
|
+
not a never-switch — `review.mode` is the one tri-state control on the
|
|
48
|
+
plugin-page config form (`off`, `optional` or `required`) — and only the gate it
|
|
49
|
+
turns on in `required` mode is non-disableable. `npm run
|
|
42
50
|
docs:check:components` derives the live disablers from `host/tool-registry.js`
|
|
43
51
|
and `cordis.patch.yml`; a disabler that is not recorded here fails the gate.
|
|
44
52
|
|
|
@@ -47,7 +55,7 @@ and `cordis.patch.yml`; a disabler that is not recorded here fails the gate.
|
|
|
47
55
|
| the Tower host | Without the host the plugin registers nothing; there is no off state that leaves the rest working, and no config boolean may remove it. The visible `tower` anchor is the all-off boundary, and the host section nested under it may never become its own switch. | none | none may exist |
|
|
48
56
|
| tower_doctor (read-only health contract) | Diagnosis must always exist so a failure message can point at it. #122 removed exposeDoctor and the exposeOperational master switch; the tool is registered for every configuration. | none | none may exist |
|
|
49
57
|
| tower_recover (stale state-lock escape) | The escape hatch must exist whenever the lock it repairs can be left behind. #122 removed exposeRecovery and the exposeOperational master switch; the tool is registered for every configuration. | none | none may exist |
|
|
50
|
-
| the review
|
|
58
|
+
| the review gate in required mode | The policy is one tri-state configuration control on the plugin-page form, not a component row: review.mode is off, optional (default) or required, and off is a documented mode that removes tower_review and makes the merge gate ignore review. What no group, row or boolean key may switch off is the gate once the mode is required: a completed mission still needs a clean review bound to the branch tip, and the exemption path stays explicit. review.requireReview is a deprecated one-release alias, not a switch. The reviewer preset row remains a capability the policy may leave unused, never a way to weaken required mode. | none | none may exist |
|
|
51
59
|
| the merge gates | gates.* decides delivery; a component may not bypass it. | none | none may exist |
|
|
52
60
|
|
|
53
61
|
## Gateable tools
|
|
@@ -71,9 +79,9 @@ All keys from `domain/config/schema.js`, classified on the component axis.
|
|
|
71
79
|
|---|---|---|
|
|
72
80
|
| `section` | `core` | Legacy alias for mode.promptSection: the Tower-mode prompt contract, not a capability. |
|
|
73
81
|
| `allowAgentControl` | `core` | Legacy alias for mode.allowAgentControl: the human-owned mode rule (ADR 0003), not a component. |
|
|
74
|
-
| `init.autoGitInit` | `optional-config` | Bootstrap convenience; off only means tower_init will not run git init. |
|
|
75
|
-
| `init.snapshotDirty` | `optional-config` | Dirty-work baseline convenience; no capability is removed. |
|
|
76
|
-
| `init.autoExclude` | `optional-config` | Keeps Tower artifacts out of .git/info/exclude; safe either way. |
|
|
82
|
+
| `init.autoGitInit` | `optional-config` | Bootstrap convenience; off only means tower_init will not run git init. Tuning exemption: Tuning, not capability: tower_init stays the component and the switch only decides whether it runs git init. (2026-11-03 · #138 · [0005-component-surface.md](../adr/0005-component-surface.md)) |
|
|
83
|
+
| `init.snapshotDirty` | `optional-config` | Dirty-work baseline convenience; no capability is removed. Tuning exemption: Tuning, not capability: the init component remains available with or without a dirty-work snapshot baseline. (2026-11-03 · #138 · [0005-component-surface.md](../adr/0005-component-surface.md)) |
|
|
84
|
+
| `init.autoExclude` | `optional-config` | Keeps Tower artifacts out of .git/info/exclude; safe either way. Tuning exemption: Tuning, not capability: both values keep the init component and only change which artifacts the exclude file names. (2026-11-03 · #138 · [0005-component-surface.md](../adr/0005-component-surface.md)) |
|
|
77
85
|
| `mode.promptSection` | `core` | Replaces the whole Tower-mode prompt text; contract text, not a capability. |
|
|
78
86
|
| `mode.sectionAppend` | `optional-config` | Appends team conventions to the prompt. |
|
|
79
87
|
| `mode.allowAgentControl` | `core` | Decides whether the model may toggle the mode (ADR 0003). A governance rule, never a component. |
|
|
@@ -89,7 +97,8 @@ All keys from `domain/config/schema.js`, classified on the component axis.
|
|
|
89
97
|
| `mode.storePath` | `optional-config` | Custom mode-store location; no capability is removed. |
|
|
90
98
|
| `mode.maxEntries` | `optional-config` | Bounds the persisted mode store. |
|
|
91
99
|
| `mode.lockTimeoutMs` | `optional-config` | Bounded wait for the cross-process mode lock. |
|
|
92
|
-
| `review.
|
|
100
|
+
| `review.mode` | `core` | The review policy: off, optional (default) or required, selected by one tri-state control on the plugin-page configuration form. It is configuration, not a component row and not three mutually exclusive rows. off intentionally removes tower_review, refuses reviewer spawn and makes the merge gate ignore review; optional never blocks a merge; required makes a clean tip-bound review a merge blocker. The reviewer preset row stays a capability the mode may leave unused. |
|
|
101
|
+
| `review.requireReview` | `core` | Deprecated one-release boolean alias for review.mode (true -> required, false -> optional; default optional). It is policy configuration and a deprecated alias, not a switch a row may own; false never overrides an explicit mode. Kept so 2.5 configurations keep working; remove after the 2.6 alias window. |
|
|
93
102
|
| `review.maxRoundsPerReviewer` | `forbidden-to-disable` | Caps review rounds; part of the review contract. |
|
|
94
103
|
| `merge.timeoutMs` | `optional-config` | Default merge timeout; a per-call value wins. |
|
|
95
104
|
| `merge.maxBufferBytes` | `optional-config` | Captured merge output buffer. |
|
|
@@ -104,28 +113,38 @@ All keys from `domain/config/schema.js`, classified on the component axis.
|
|
|
104
113
|
| `spawn.maxLiveWorkers` | `core` | Resource-safety cap on live workers. |
|
|
105
114
|
| `spawn.staleMs` | `optional-config` | Roster staleness window before merge is blocked. |
|
|
106
115
|
| `spawn.maxWaitMs` | `optional-config` | Upper bound for a blocking spawn wait. |
|
|
107
|
-
| `spawn.requireLease` | `optional-config` | Advisory-lease gate before a worker starts. |
|
|
116
|
+
| `spawn.requireLease` | `optional-config` | Advisory-lease gate before a worker starts. Tuning exemption: Tuning the safety level inside the optional tower-leases row; with leases off the Host refuses requireLease instead of removing the spawn capability. (2026-11-03 · #138 · [0005-component-surface.md](../adr/0005-component-surface.md)) |
|
|
108
117
|
| `send.maxPerMinute` | `optional-config` | Message rate limit; zero disables it. |
|
|
109
|
-
| `send.interruptDefault` | `optional-config` | Default for the interrupt-before-deliver flag. |
|
|
110
|
-
| `tests.
|
|
111
|
-
| `tests.admission` | `optional-config` | Chooses when discovery runs inside the optional tests capability. |
|
|
118
|
+
| `send.interruptDefault` | `optional-config` | Default for the interrupt-before-deliver flag. Tuning exemption: Tuning, not capability: the send component always exists and the switch only picks a delivery default. (2026-11-03 · #138 · [0005-component-surface.md](../adr/0005-component-surface.md)) |
|
|
119
|
+
| `tests.admission` | `optional-config` | Chooses when discovery runs inside the optional tests capability. Tuning exemption: Tuning when the admission check runs inside the optional tower-tests row, which still owns whether test tooling exists. (2026-11-03 · #138 · [0005-component-surface.md](../adr/0005-component-surface.md)) |
|
|
112
120
|
| `tests.timeoutMs` | `optional-config` | Per-command discovery timeout. |
|
|
113
121
|
| `tests.maxBytes` | `optional-config` | Captured discovery output cap. |
|
|
114
|
-
| `tests.autoChecks` | `optional-config` | Attaches a tests check to completion when discovery runs. |
|
|
122
|
+
| `tests.autoChecks` | `optional-config` | Attaches a tests check to completion when discovery runs. Tuning exemption: Tuning the default receipt policy inside the optional tower-tests row, which still owns whether any test tooling exists. (2026-11-03 · #138 · [0005-component-surface.md](../adr/0005-component-surface.md)) |
|
|
115
123
|
| `tests.allowPackageDiscovery` | `core` | Allows repository code to execute; a security contract, not a component. |
|
|
116
124
|
| `gates.requireChecks` | `forbidden-to-disable` | Requires completion evidence before merge; a merge gate. |
|
|
117
125
|
| `gates.requireTests` | `forbidden-to-disable` | Requires a test receipt before merge; a merge gate. With the tower-tests row off, planning/mode entry refuse the combination. |
|
|
118
126
|
| `gates.allowReviewExemption` | `forbidden-to-disable` | The review-exemption path is part of the review and merge contract. |
|
|
119
127
|
| `gates.maxReportBytes` | `forbidden-to-disable` | The report cap is part of the completion contract; the refusal itself is never configurable away. |
|
|
120
|
-
| `docs.
|
|
121
|
-
| `
|
|
122
|
-
| `archive.enabled` | `optional-config` | Archival is maintenance, not a tool capability; off only leaves terminal missions hot. |
|
|
123
|
-
| `archive.afterDays` | `optional-config` | Archive age threshold. |
|
|
128
|
+
| `docs.boardLocation` | `optional-config` | Chooses where the board is written; whether it is written is the tower-board component row (#133). |
|
|
129
|
+
| `archive.afterDays` | `optional-config` | Archive age threshold inside the optional tower-archive component (#135). |
|
|
124
130
|
| `archive.maxMissions` | `optional-config` | Hot terminal-mission cap. |
|
|
125
131
|
| `leases.ttlMs` | `optional-config` | Tuning inside the optional tower-leases component. |
|
|
126
132
|
| `dev.reloadDelayMs` | `optional-config` | Delay so a tool result is delivered before a scheduled reload inside the optional tower-hmr component. |
|
|
127
133
|
| `dev.reloadRequiredIds` | `optional-config` | Loader ids passed to reconcileProfile as required by the tower-hmr component. |
|
|
128
134
|
|
|
135
|
+
## Behaviour knobs
|
|
136
|
+
|
|
137
|
+
Non-boolean keys that gate behaviour are reviewed on the same axis as booleans
|
|
138
|
+
(#138): each must be `core`, `optional-component`, `forbidden-to-disable`, or an
|
|
139
|
+
`optional-config` key with a dated tuning-only exemption. A string or union that
|
|
140
|
+
hides a capability cannot use the tuning class without that record.
|
|
141
|
+
|
|
142
|
+
| Key |
|
|
143
|
+
|---|
|
|
144
|
+
| `review.mode` |
|
|
145
|
+
| `spawn.requireLease` |
|
|
146
|
+
| `tests.admission` |
|
|
147
|
+
|
|
129
148
|
## Non-goals
|
|
130
149
|
|
|
131
150
|
This page does not decide whether a key is useful, and it is not an approval
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Config reference (generated)
|
|
2
2
|
|
|
3
3
|
> Generated by `npm run docs:generate:config` from `domain/config/schema.js`. Values are defaults before repo config.
|
|
4
|
-
> Surface: config keys:
|
|
4
|
+
> Surface: config keys: 54 · sections: 14 · tools: 29 · baseline 2026-10-23. Generated from `scripts/surface-baseline.json`; do not edit by hand.
|
|
5
5
|
|
|
6
6
|
| Key | Default | Description |
|
|
7
7
|
|---|---|---|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
| `init.autoGitInit` | `false` | tower_init may run git init and create an empty initial commit when the directory is not a repository. |
|
|
11
11
|
| `init.snapshotDirty` | `false` | tower_init may snapshot uncommitted tracked changes into a dangling commit used as the mission baseline. |
|
|
12
12
|
| `init.autoExclude` | `true` | Add in-repo Tower artifacts (MISSIONS.md, in-repo worktree roots) to .git/info/exclude so they do not look like user dirt. |
|
|
13
|
-
| `mode.promptSection` |
|
|
13
|
+
| `mode.promptSection` | `""` | Replacement Tower-mode system prompt text. Empty keeps the built-in reminder. |
|
|
14
14
|
| `mode.sectionAppend` | `` | Extra text appended to the Tower-mode system prompt section (team conventions). |
|
|
15
15
|
| `mode.allowAgentControl` | `true` | Register tower_control so the model may enter/leave the mode with a reason. |
|
|
16
16
|
| `mode.autoPauseGoal` | `true` | Pause an active goal when entering Tower mode. |
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
| `mode.storePath` | `` | Directory for modes.json; defaults to $DSH_HOME/tower-mode. |
|
|
26
26
|
| `mode.maxEntries` | `200` | Maximum persisted mode entries. |
|
|
27
27
|
| `mode.lockTimeoutMs` | `5000` | How long saveModes waits for the cross-process modes.lock before giving up. |
|
|
28
|
-
| `review.
|
|
28
|
+
| `review.mode` | `"optional"` | Review policy. off = review is intentionally unavailable (recording a review is refused, reviewer spawn is refused, and the merge gate ignores review; a profile-level off also does not register tower_review); optional = review is available but never blocks a merge (default); required = a clean review bound to the branch tip is a merge blocker. |
|
|
29
|
+
| `review.requireReview` | `false` | Deprecated alias for review.mode (true -> required, false -> optional); prefer review.mode. true next to a non-required mode is rejected; false never overrides an explicit mode (off, optional or required). The Plugins-page control shows the alias when it is the only key and always writes review.mode with review.requireReview:false. |
|
|
29
30
|
| `review.maxRoundsPerReviewer` | `5` | Review-round cap per reviewer per mission branch. |
|
|
30
31
|
| `merge.timeoutMs` | `900000` | Default tower_merge timeout, including pre-merge hooks. |
|
|
31
32
|
| `merge.maxBufferBytes` | `67108864` | Captured merge output buffer. |
|
|
@@ -43,7 +44,6 @@
|
|
|
43
44
|
| `spawn.requireLease` | `` | Optional lease name tower_spawn must hold before starting a worker. |
|
|
44
45
|
| `send.maxPerMinute` | `120` | Per-repository tower_send rate limit per minute (0 disables). |
|
|
45
46
|
| `send.interruptDefault` | `false` | Default value of tower_send.interrupt when the caller omits it. |
|
|
46
|
-
| `tests.enabled` | `true` | Run declared test discovery when a mission configures it. |
|
|
47
47
|
| `tests.admission` | `"off"` | Run discovery before spawning a worker, when completing, or only on explicit tower_test (default off: repository code runs only on an explicit tower_test). |
|
|
48
48
|
| `tests.timeoutMs` | `60000` | Per discovery command timeout. |
|
|
49
49
|
| `tests.maxBytes` | `16777216` | Maximum captured stdout bytes per command. |
|
|
@@ -53,10 +53,8 @@
|
|
|
53
53
|
| `gates.requireTests` | `false` | Require a passing test-discovery receipt for the reviewed worktree tip before merge. |
|
|
54
54
|
| `gates.allowReviewExemption` | `false` | Allow reviewPolicy:none missions with a non-empty reviewExemptionReason to skip review blockers. |
|
|
55
55
|
| `gates.maxReportBytes` | `32768` | Hard cap, in UTF-8 bytes, for tower_complete.report; a larger report is rejected with an actionable nextAction, never truncated. |
|
|
56
|
-
| `docs.
|
|
57
|
-
| `
|
|
58
|
-
| `archive.enabled` | `true` | Move terminal (merged/abandoned) missions whose worktree is gone into <git-common-dir>/dsh-tower/archive/ at the next mutation, so state.json stops carrying their full history. |
|
|
59
|
-
| `archive.afterDays` | `30` | Terminal missions untouched for at least this many days are archived on the next mutation (0 archives them immediately). |
|
|
56
|
+
| `docs.boardLocation` | `"common"` | common = <git-common-dir>/dsh-tower/MISSIONS.md; repo = <repoRoot>/MISSIONS.md. Whether the board is written at all is the tower-board component row (#133). |
|
|
57
|
+
| `archive.afterDays` | `30` | Terminal missions untouched for at least this many days are archived on the next mutation (0 archives them immediately). Archival itself is the tower-archive component row (#135). |
|
|
60
58
|
| `archive.maxMissions` | `100` | Maximum terminal missions kept hot; once exceeded, the oldest eligible terminal missions are archived until the cap holds (0 archives every eligible terminal mission). |
|
|
61
59
|
| `leases.ttlMs` | `900000` | Default advisory lease lifetime; expiry never auto-releases. |
|
|
62
60
|
| `dev.reloadDelayMs` | `50` | Delay before a scheduled reload, so the tool result is delivered first. |
|
package/docs/reference/config.md
CHANGED
|
@@ -5,6 +5,11 @@ Every key is optional; defaults reproduce the shipped behavior. Unknown top-leve
|
|
|
5
5
|
`mode.allowAgentControl`. A legacy alias only fills a gap: when both forms are set, the canonical
|
|
6
6
|
`mode.*` key wins.
|
|
7
7
|
|
|
8
|
+
Two component-shaped switches left the config form (#133/#135): `docs.board` is now the `tower-board`
|
|
9
|
+
component row and `archive.enabled` is now the `tower-archive` component row. A config that still sets
|
|
10
|
+
either one fails with `config-unknown-key` and an upgrade note naming the replacing row; the tuning
|
|
11
|
+
keys (`docs.boardLocation`, `archive.afterDays`, `archive.maxMissions`) stay here.
|
|
12
|
+
|
|
8
13
|
## Layers and precedence
|
|
9
14
|
|
|
10
15
|
Lowest precedence first:
|
|
@@ -22,13 +27,38 @@ win, profile keys survive when the repo does not set them. `tower_status` report
|
|
|
22
27
|
|
|
23
28
|
```json
|
|
24
29
|
{
|
|
25
|
-
"review": { "
|
|
30
|
+
"review": { "mode": "required", "maxRoundsPerReviewer": 3 },
|
|
26
31
|
"merge": { "timeoutMs": 1800000 },
|
|
27
32
|
"worktrees": { "root": "D:/tower-worktrees" },
|
|
28
33
|
"spawn": { "reviewerPreset": "tower-reviewer", "maxDepth": 2 }
|
|
29
34
|
}
|
|
30
35
|
```
|
|
31
36
|
|
|
37
|
+
`review.mode` is the review policy: `off`, `optional` or `required`. `review.requireReview` is a
|
|
38
|
+
deprecated alias kept for one release (`true` → `required`, `false` → `optional`); prefer
|
|
39
|
+
`review.mode`. When both are set in the same layer, `requireReview:true` next to any mode other than
|
|
40
|
+
`required` is a contradiction and is rejected with `config-invalid` naming both keys.
|
|
41
|
+
`requireReview:false` never contradicts an explicit mode: `off`, `optional` and `required` all stay
|
|
42
|
+
accepted and the mode wins (`required` still enforces the gate; the resolved alias is mirrored back as
|
|
43
|
+
`true` only for that mode). A repository layer is resolved on its own: it may relax review, but a
|
|
44
|
+
profile with `review.mode:off` never registered `tower_review`, so a repository cannot raise its own
|
|
45
|
+
mode.
|
|
46
|
+
|
|
47
|
+
The Plugins-page control shows the policy exactly as the runtime resolves it: `review.mode` decides
|
|
48
|
+
when present, and a profile that still sets only `review.requireReview` displays Required (`true`) or
|
|
49
|
+
Optional (`false`). Saving a choice always writes `review.mode` and sets `review.requireReview` to
|
|
50
|
+
`false` in the same profile patch, so the alias can never contradict the selected mode.
|
|
51
|
+
|
|
52
|
+
The same three choices are available on the Plugins page, in the `dsh-tower` configuration
|
|
53
|
+
section: **Off**, **Optional** (the default) and **Required**, each with a one-line description of
|
|
54
|
+
its effect. That control writes the profile config through the Host settings form, so the accepted
|
|
55
|
+
value is reflected on the page without a manual file edit.
|
|
56
|
+
|
|
57
|
+
One live change cannot rebuild the mounted tool surface: a profile that started with review off did
|
|
58
|
+
not register `tower_review`. Switching to Optional or Required through the page immediately changes
|
|
59
|
+
the merge gate, but the review tool itself appears only after a restart (or a profile reload); the
|
|
60
|
+
control states that after such a save.
|
|
61
|
+
|
|
32
62
|
The `mode.*` group is session-scoped, not repository-scoped: it is accepted in the repository file
|
|
33
63
|
for schema compatibility but only the profile config controls the Tower-mode prompt and the
|
|
34
64
|
model-controlled toggle. A malformed file, a non-object value, unknown top-level keys, or a value that
|
|
@@ -56,7 +86,8 @@ violates the declared schema (for example `merge.timeoutMs` below 1000) fail wit
|
|
|
56
86
|
| `mode.denyTools` | goal/todo/team/workflow/subagent tools | Tools refused while the mode is active (12 defaults). |
|
|
57
87
|
| `mode.storePath` | `$DSH_HOME/tower-mode` | Directory for `modes.json`. |
|
|
58
88
|
| `mode.maxEntries` | `200` | Maximum persisted mode entries (1–2000). |
|
|
59
|
-
| `review.
|
|
89
|
+
| `review.mode` | `optional` | Review policy. `off` = review is intentionally unavailable: recording a review is refused, `tower_spawn` refuses the reviewer role, the merge gate ignores review, and a profile-level `off` does not register `tower_review` (a repository-level `off` refuses the submission instead); `optional` = review is available but never blocks a merge (default); `required` = a clean review bound to the branch tip is a merge blocker (`no-review`/`not-clean`/`review-hold`/`review-not-approved`/`tip-moved`/`review-mission-mismatch`). |
|
|
90
|
+
| `review.requireReview` | `false` | Deprecated alias for `review.mode`: `true` → `required`, `false` → `optional`. Prefer `review.mode`. `true` next to a non-`required` `review.mode` is rejected with `config-invalid` naming both keys; `false` is accepted next to any explicit `review.mode` (`off`/`optional`/`required`) and never overrides it. The Plugins-page control shows the alias when it is the only key and always writes `review.mode` with `review.requireReview:false`. |
|
|
60
91
|
| `review.maxRoundsPerReviewer` | `5` | Review-round cap per reviewer per branch. |
|
|
61
92
|
| `merge.timeoutMs` | `900000` (15 min) | Default `tower_merge` timeout, including hooks. A per-call `timeoutMs` wins. |
|
|
62
93
|
| `merge.maxBufferBytes` | `67108864` (64 MB) | Merge output buffer. |
|
|
@@ -74,7 +105,6 @@ violates the declared schema (for example `merge.timeoutMs` below 1000) fail wit
|
|
|
74
105
|
| `spawn.requireLease` | `''` | Optional lease name that must be held before a worker can spawn. |
|
|
75
106
|
| `send.maxPerMinute` | `120` | Per-repository `tower_send` rate limit (0 disables); exceeding it returns `rate-limited`. |
|
|
76
107
|
| `send.interruptDefault` | `false` (bundled patch: `true`) | Default for `tower_send.interrupt` (interrupt the running turn before delivering). |
|
|
77
|
-
| `tests.enabled` | `true` | Run declared test discovery when the worktree `package.json` configures it. It only tunes the optional `tower-tests` component: with that row off there is no `tower_test` tool and no receipt, and `gates.requireTests:true` is refused rather than silently unmet. |
|
|
78
108
|
| `tests.admission` | `off` | `spawn` runs discovery before starting a worker, `complete` at `tower_complete`, `off` only on explicit `tower_test` (default; repository code is not executed automatically). |
|
|
79
109
|
| `tests.timeoutMs` | `60000` | Per discovery command timeout. |
|
|
80
110
|
| `tests.maxBytes` | `16777216` (16 MiB) | Captured stdout cap per command. |
|
|
@@ -82,12 +112,10 @@ violates the declared schema (for example `merge.timeoutMs` below 1000) fail wit
|
|
|
82
112
|
| `tests.allowPackageDiscovery` | `true` | Allow repo-controlled `tower.testDiscovery` commands (executes worktree code; see security). |
|
|
83
113
|
| `gates.requireChecks` | `false` | Require at least one `tower_complete.checks` entry before merge (`no-check-evidence`). |
|
|
84
114
|
| `gates.requireTests` | `false` | Require a passing discovery receipt for the current mission branch tip (`tests-missing`); independent of the review gate. With the `tower-tests` component row off, planning/mode entry refuse this combination with an explanation. |
|
|
85
|
-
| `gates.allowReviewExemption` | `false` | With `review.
|
|
115
|
+
| `gates.allowReviewExemption` | `false` | With `review.mode:required`, allow `reviewPolicy:"none"` + a non-empty reason to skip review blockers. Off deliberately; irrelevant when `review.mode` is `off` or `optional` (no review blocker is produced). |
|
|
86
116
|
| `gates.maxReportBytes` | `32768` (32 KiB) | Hard cap in UTF-8 bytes for `tower_complete.report`. A larger report is rejected with `invalid-argument` and an actionable `nextAction` (file the full text in the mission worktree and reference its path); it is never truncated. Existing larger reports still load. |
|
|
87
|
-
| `docs.
|
|
88
|
-
| `
|
|
89
|
-
| `archive.enabled` | `true` | Move terminal (`merged`/`abandoned`) missions whose worktree is gone into `<git-common-dir>/dsh-tower/archive/` at a mutation boundary. Archived missions keep tasks, reviews, findings and the completion report; they are moved, never deleted. |
|
|
90
|
-
| `archive.afterDays` | `30` | Age trigger: a terminal mission untouched for at least this many days is archived on the next mutation (`0` archives immediately). |
|
|
117
|
+
| `docs.boardLocation` | `common` | `common` = `<git-common-dir>/dsh-tower/MISSIONS.md`; `repo` = `<repoRoot>/MISSIONS.md`. Whether the board is written at all is the `tower-board` component row (#133); `docs.board` was removed and is rejected as an unknown key. |
|
|
118
|
+
| `archive.afterDays` | `30` | Age trigger: a terminal mission untouched for at least this many days is archived on the next mutation (`0` archives immediately). Archival itself is the `tower-archive` component row (#135); `archive.enabled` was removed and is rejected as an unknown key. |
|
|
91
119
|
| `archive.maxMissions` | `100` | Cap trigger: once more than this many terminal missions are hot, the oldest eligible ones are archived until the cap holds (`0` archives every eligible terminal mission). A mission with a live worktree is never archived. |
|
|
92
120
|
| `leases.ttlMs` | `900000` | Default advisory lease lifetime; expiry never auto-releases. |
|
|
93
121
|
| `dev.reloadDelayMs` | `50` | Delay before a scheduled reload so the tool result is delivered first. Tuning inside the optional `tower-hmr` component. |
|
package/docs/reference/doctor.md
CHANGED
|
@@ -8,17 +8,17 @@ It returns:
|
|
|
8
8
|
"ok": true,
|
|
9
9
|
"checks": [
|
|
10
10
|
{ "id": "git", "status": "pass", "detail": "git version 2.55.0" },
|
|
11
|
-
{ "id": "surface", "status": "pass", "detail": "config keys:
|
|
11
|
+
{ "id": "surface", "status": "pass", "detail": "config keys: 53 · sections: 14 · tools: 29 (baseline 2026-10-23)" },
|
|
12
12
|
{ "id": "state", "status": "pass", "detail": ".../state.json revision 7" },
|
|
13
13
|
{ "id": "state-schema", "status": "pass", "detail": "schemaVersion 3 is current" },
|
|
14
14
|
{ "id": "case-mode", "status": "pass", "detail": "caseInsensitive: true; scope matching uses case-insensitive comparison" }
|
|
15
15
|
],
|
|
16
16
|
"surface": {
|
|
17
|
-
"configKeys":
|
|
17
|
+
"configKeys": 53,
|
|
18
18
|
"sections": 14,
|
|
19
19
|
"tools": 29,
|
|
20
20
|
"baselineDate": "2026-10-23",
|
|
21
|
-
"line": "config keys:
|
|
21
|
+
"line": "config keys: 53 · sections: 14 · tools: 29"
|
|
22
22
|
},
|
|
23
23
|
"stateLock": {
|
|
24
24
|
"held": true,
|
|
@@ -100,9 +100,15 @@ it for the configured merge timeout (15 minutes by default), so a held lock is a
|
|
|
100
100
|
failure; the holder's phase and age make a stalled tower diagnosable. `tower_status` returns the same
|
|
101
101
|
`gitLock` object.
|
|
102
102
|
|
|
103
|
+
`board` names the `MISSIONS.md` location (`docs.boardLocation`) and whether the `tower-board` component
|
|
104
|
+
row is mounted. With the row off the check is a `warn` that says the board is not written (an existing
|
|
105
|
+
board goes stale) and names the row; every state tool and gate still works.
|
|
106
|
+
|
|
103
107
|
`archive` reports the terminal-mission archive: active `missions`, `archivedMissions` and `stateBytes`,
|
|
104
|
-
the archive directory's `archiveBytes`/`years`/`files`, every `unreadable` diagnostic, and the
|
|
105
|
-
`policy` (`
|
|
108
|
+
the archive directory's `archiveBytes`/`years`/`files`, every `unreadable` diagnostic, and the tuning
|
|
109
|
+
`policy` (`afterDays`, `maxMissions`). `policy.enabled` reflects the `tower-archive` component row, not a
|
|
110
|
+
config key: with the row off the policy line says archival is disabled by the component, terminal
|
|
111
|
+
missions stay hot and `state.json` keeps growing. A missing archive directory is a healthy empty archive
|
|
106
112
|
(`archive` check `pass`); a truncated index or year file is a `warn` with the diagnostic, and archiving is
|
|
107
113
|
paused until it is repaired rather than overwriting it. Nothing is lost while the archive is unreadable:
|
|
108
114
|
the eligible missions stay in `state.json`.
|
|
@@ -29,6 +29,7 @@ single recovery step appended to the message as ` — next: …` (with ` (retrya
|
|
|
29
29
|
| `provider-cooldown` | The provider is throttled; spawn is in a short cooldown. | yes | wait for the cooldown to expire, then retry |
|
|
30
30
|
| `preset-missing` | The configured agent preset is not registered. | no | install the tower-worker and tower-reviewer agent presets and restart DSH |
|
|
31
31
|
| `reviewer-prerequisite` | The reviewer spawn prerequisite is not met. | no | complete the mission first, then spawn the reviewer |
|
|
32
|
+
| `review-disabled` | Review is disabled by `review.mode:off`; no reviewer, verdict, or review blocker exists in this configuration. | no | set review.mode to optional or required in the Tower config, then retry |
|
|
32
33
|
| `not-authorized` | The calling agent role may not perform this action. | no | call the tool as the tower, or as the worker/reviewer it is assigned to |
|
|
33
34
|
| `workspace-busy` | Another Tower session owns this repository workspace lock. | yes | run /tower off in the other session or wait for its lock to become stale |
|
|
34
35
|
| `worker-limit` | The repository reached spawn.maxLiveWorkers live workers. | no | wait for a live worker to finish or raise spawn.maxLiveWorkers |
|
package/docs/reference/errors.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Tool failures are thrown errors, not `{ok:false}` results (gate outcomes are the exception: see
|
|
4
4
|
[blockers.md](blockers.md)). Every thrown error carries a stable code as a message prefix. This
|
|
5
|
-
release defines
|
|
5
|
+
release defines 36 tool error codes.
|
|
6
6
|
|
|
7
7
|
Every `TowerToolError` carries `retryable` (boolean), `nextAction` (string or `null`) and `details`
|
|
8
8
|
(structured context or `null`) for programmatic callers. The model-visible surface is the message:
|
|
@@ -43,6 +43,7 @@ plain `Error` without a structured code is reported as `tool-internal`.
|
|
|
43
43
|
| `provider-cooldown` | The provider is throttled; spawn is in a short cooldown. | yes | wait for the cooldown to expire, then retry | repeated retryable provider failures; wait for `blockedUntil` |
|
|
44
44
|
| `preset-missing` | The configured agent preset is not registered. | no | install the tower-worker and tower-reviewer agent presets and restart DSH | missing `tower-worker`/`tower-reviewer` preset rows, DSH not restarted |
|
|
45
45
|
| `reviewer-prerequisite` | The reviewer spawn prerequisite is not met. | no | complete the mission first, then spawn the reviewer | reviewer spawned before the mission is `completed`, or before a worktree exists |
|
|
46
|
+
| `review-disabled` | Review is disabled by `review.mode:off`; no reviewer, verdict, or review blocker exists in this configuration. | no | set review.mode to optional or required in the Tower config, then retry | `tower_spawn` with `role:"reviewer"`, or a review submission, while `review.mode` is `off` |
|
|
46
47
|
| `not-authorized` | The calling agent role may not perform this action. | no | call the tool as the tower, or as the worker/reviewer it is assigned to | a worker calling a tower-only tool or changing mission status/scope; a reviewer mutating findings |
|
|
47
48
|
| `workspace-busy` | Another Tower session owns this repository workspace lock. | yes | run /tower off in the other session or wait for its lock to become stale | `mode.workspaceLock:true` and a second session calls a repo-scoped tool |
|
|
48
49
|
| `worker-limit` | The repository reached spawn.maxLiveWorkers live workers. | no | wait for a live worker to finish or raise spawn.maxLiveWorkers | spawning a fifth worker with the default limit |
|
|
@@ -19,10 +19,15 @@ these tables fails `npm run surface:check`.
|
|
|
19
19
|
A second axis asks who may turn a key off. It is generated from
|
|
20
20
|
[`scripts/component-surface.json`](../../scripts/component-surface.json) into
|
|
21
21
|
[component-surface.md](component-surface.md): every key is `core`,
|
|
22
|
-
`optional-component`, `optional-config` or `forbidden-to-disable
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
`optional-component`, `optional-config` or `forbidden-to-disable`. A boolean (or a
|
|
23
|
+
recorded non-boolean behaviour knob) cannot stay `optional-config` without a dated
|
|
24
|
+
tuning-only exemption. Both the component count and the config-key count are
|
|
25
|
+
baselined two-way: an increase or a contraction needs a dated decision
|
|
26
|
+
(`npm run docs:check:components`, [ADR 0005](../adr/0005-component-surface.md)).
|
|
27
|
+
Every decision on those two surfaces must carry a real `date` (`YYYY-MM-DD`); a
|
|
28
|
+
from/to record without one fails the brake even when it reaches the committed
|
|
29
|
+
count.
|
|
30
|
+
The same source of truth records
|
|
26
31
|
the `gateableTools` allowlist; it is empty since #122, so a future
|
|
27
32
|
`tools.expose*` route to any tool is refused before it can remove a review,
|
|
28
33
|
merge or host tool.
|
|
@@ -44,8 +49,40 @@ profile's 28 loaded tools and pins 29 for the all-rows-on surface.
|
|
|
44
49
|
|
|
45
50
|
#121 also shrank the config surface: the seven capability `tools.expose*` keys
|
|
46
51
|
and `dev.hotReload` became component rows, so config keys went 64 → 56 and
|
|
47
|
-
sections 15 → 14.
|
|
48
|
-
|
|
52
|
+
sections 15 → 14. Sections keep the ADR 0004 cheap-shrink rule. #136 shrank the
|
|
53
|
+
config keys once more, 56 → 55: `tests.enabled` was a second switch over the
|
|
54
|
+
`tower-tests` component row, so the row is now the only switch and the key is
|
|
55
|
+
gone (a config that still writes it fails with `config-unknown-key`). #133 and
|
|
56
|
+
#135 shrank it again, 55 → 53: `docs.board` and `archive.enabled` were the same
|
|
57
|
+
kind of second switch over the new `tower-board` and `tower-archive` rows, so
|
|
58
|
+
each row is now the only switch and both keys are gone (a config that still sets
|
|
59
|
+
either fails with `config-unknown-key`, and the upgrade note names the replacing
|
|
60
|
+
row).
|
|
61
|
+
|
|
62
|
+
Since #138 that 64 → 53 history is one dated decision in
|
|
63
|
+
[`scripts/surface-baseline.json`](../../scripts/surface-baseline.json), and the
|
|
64
|
+
config-key count is two-way: a later increase **or** decrease needs its own
|
|
65
|
+
decision naming an ADR, unlike sections and tools. The same wave makes the gate
|
|
66
|
+
import `OPERATIONAL_TOOLS` instead of scraping it, discover every shipped
|
|
67
|
+
`cordis.patch.yml` in both directions, review the non-boolean behaviour knobs
|
|
68
|
+
(`spawn.requireLease`, `tests.admission`, and now `review.mode`), and refuse a row
|
|
69
|
+
config outside its component's `allowedOverrides`.
|
|
70
|
+
|
|
71
|
+
#M39 grew the config surface by exactly one, 53 → 54: `review.mode`
|
|
72
|
+
(`off`/`optional`/`required`) is the review policy, and the deprecated
|
|
73
|
+
`review.requireReview` boolean stays accepted for one release as a normalized
|
|
74
|
+
alias. It is not a fifth component row: `preset-tower-reviewer` remains a
|
|
75
|
+
capability row and `review.mode:off` simply leaves it unused (no `tower_review`,
|
|
76
|
+
no reviewer spawn, no review blockers). The dated decision records both the new
|
|
77
|
+
key and why the old boolean was not removed in the same release.
|
|
78
|
+
|
|
79
|
+
#M42 reclassifies the deprecated alias from `forbidden-to-disable` to `core` in
|
|
80
|
+
the component surface: it is policy configuration and a compatibility alias, not
|
|
81
|
+
a capability a row may switch. The policy is one tri-state control on the
|
|
82
|
+
plugin-page form, not a row and not one row per mode; the review gate in
|
|
83
|
+
`required` mode, its tip-bound evidence and the exemption path remain
|
|
84
|
+
non-disableable. The component and config-key counts do not move (11 visible
|
|
85
|
+
components, 54 keys).
|
|
49
86
|
|
|
50
87
|
## Config keys
|
|
51
88
|
|
|
@@ -71,7 +108,8 @@ not free growth: a later increase still needs its own decision.
|
|
|
71
108
|
| `mode.storePath` | niche | Custom mode-store location added for one deployment workflow. Removal note: derive the path from `$DSH_HOME` in the next minor; no in-tree caller sets this key. |
|
|
72
109
|
| `mode.maxEntries` | tuning | Bounds the persisted mode store. |
|
|
73
110
|
| `mode.lockTimeoutMs` | tuning | Bounded wait for the cross-process mode lock. |
|
|
74
|
-
| `review.
|
|
111
|
+
| `review.mode` | core | The review policy itself (`off`/`optional`/`required`); it replaces the deprecated boolean as the single gate switch. |
|
|
112
|
+
| `review.requireReview` | core | Deprecated alias for `review.mode` (`true` → `required`, `false` → `optional`); retained for one release. |
|
|
75
113
|
| `review.maxRoundsPerReviewer` | core | Caps review rounds; changes the review lifecycle. |
|
|
76
114
|
| `merge.timeoutMs` | tuning | Default merge timeout; a per-call value wins. |
|
|
77
115
|
| `merge.maxBufferBytes` | tuning | Captured merge output buffer. |
|
|
@@ -89,7 +127,6 @@ not free growth: a later increase still needs its own decision.
|
|
|
89
127
|
| `spawn.requireLease` | niche | Advisory-lease gate added for one multi-agent workflow. Removal note: fold into `tower_resource` policy in the next minor unless a second workflow uses it. |
|
|
90
128
|
| `send.maxPerMinute` | tuning | Message rate limit; zero disables it. |
|
|
91
129
|
| `send.interruptDefault` | tuning | Default for the interrupt-before-deliver flag. |
|
|
92
|
-
| `tests.enabled` | core | Whether declared discovery may run at all. |
|
|
93
130
|
| `tests.admission` | core | Chooses when repository code is executed. |
|
|
94
131
|
| `tests.timeoutMs` | tuning | Per-command discovery timeout. |
|
|
95
132
|
| `tests.maxBytes` | tuning | Captured discovery output cap. |
|
|
@@ -99,10 +136,8 @@ not free growth: a later increase still needs its own decision.
|
|
|
99
136
|
| `gates.requireTests` | core | Requires a test receipt before merge. |
|
|
100
137
|
| `gates.allowReviewExemption` | core | Allows a review exemption and its reason. |
|
|
101
138
|
| `gates.maxReportBytes` | tuning | Report size cap; the refusal itself is not configurable. |
|
|
102
|
-
| `docs.
|
|
103
|
-
| `
|
|
104
|
-
| `archive.enabled` | core | Controls whether terminal missions leave the hot state. |
|
|
105
|
-
| `archive.afterDays` | tuning | Archive age threshold. |
|
|
139
|
+
| `docs.boardLocation` | tuning | Chooses where the board is written; whether it is written is the tower-board row. |
|
|
140
|
+
| `archive.afterDays` | tuning | Archive age threshold; archival itself is the tower-archive row. |
|
|
106
141
|
| `archive.maxMissions` | tuning | Hot terminal-mission cap. |
|
|
107
142
|
| `leases.ttlMs` | tuning | Default advisory lease lifetime. |
|
|
108
143
|
| `dev.reloadDelayMs` | tuning | Delay so a tool result is delivered before reload. |
|
|
@@ -51,7 +51,7 @@ discovery fails.
|
|
|
51
51
|
- `tower_complete` adds a `tests` check when `tests.autoChecks` is on.
|
|
52
52
|
- `gates.requireChecks` requires any completion check; `gates.requireTests` requires a passing receipt
|
|
53
53
|
for the current mission branch tip (`tests-missing`). Review is independent of this gate: with
|
|
54
|
-
`review.
|
|
54
|
+
`review.mode:optional` (the default) or `off`, the tests gate still checks the branch tip.
|
|
55
55
|
|
|
56
56
|
## Trust boundary
|
|
57
57
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Tools overview (generated)
|
|
2
2
|
|
|
3
3
|
> Generated by `npm run docs:generate:tools` from the registered tool definitions. Do not edit by hand.
|
|
4
|
-
> Surface: config keys:
|
|
4
|
+
> Surface: config keys: 54 · sections: 14 · tools: 29 · baseline 2026-10-23. Generated from `scripts/surface-baseline.json`; do not edit by hand.
|
|
5
5
|
|
|
6
6
|
| Tool | Description |
|
|
7
7
|
|---|---|
|
|
@@ -22,15 +22,15 @@
|
|
|
22
22
|
| `tower_mission` | Update one mission: lifecycle status, decision note, blocker, clear blockers, mark a task done, drop a task with a reason, or widen the scope (tower only). "merged" is never set here; only a future tower_merge records it. Merged and abandoned are terminal. |
|
|
23
23
|
| `tower_mode` | Report whether Tower mode is active for the calling agent, who changed it last, whether the model may toggle it, any goal paused on entry, the workspace lock, HMR availability and the plugin version. |
|
|
24
24
|
| `tower_plan` | Plan one or more missions in an initialized repository. Each mission has a printable-Unicode title (NFC-normalized, at most 120 characters), relative scope globs, optional task checklist, optional mission-id dependencies, and kind build|survey. Assigns M1.. ids and auto-adds a dependency when build scopes overlap (serializes merges); rejects dependency cycles. |
|
|
25
|
-
| `tower_preflight` | Read-only merge gate for one mission: returns ok plus stable blocker codes (branch-missing, deps-unmerged, read-only-survey, stale-base, out-of-scope, base-dirty, branch-owned-by-closed-missions, and mission/worker guards). Review blockers (no-review, review-hold, not-clean, review-not-approved, tip-moved, review-mission-mismatch) apply only when review.
|
|
25
|
+
| `tower_preflight` | Read-only merge gate for one mission: returns ok plus stable blocker codes (branch-missing, deps-unmerged, read-only-survey, stale-base, out-of-scope, base-dirty, branch-owned-by-closed-missions, and mission/worker guards). Review blockers (no-review, review-hold, not-clean, review-not-approved, tip-moved, review-mission-mismatch) apply only when review.mode is required; review.mode off and optional skip them. Never changes anything. |
|
|
26
26
|
| `tower_rebase` | Rebase a mission branch onto the current base tip inside its own worktree. Refuses while its worker is running or the worktree is dirty. A clean conflict-free rebase waives re-review for the old review; a conflict is aborted and the mission is marked blocked. If the branch is already contained in the base, it returns alreadyMerged and leaves the branch untouched so tower_merge can record the landed merge. |
|
|
27
27
|
| `tower_recover` | Read-only recovery diagnostics and an explicit stale-state-lock repair. status reports the state lock owner with its age, heartbeat age and staleness; clear-stale-lock requires confirm:"CLEAR_STALE_LOCK" and only moves a lock whose PID is provably dead to a backup file. force:true additionally clears a live-but-silent PID (a recycled PID) and still renames the record to a .recovered-<ts> backup. It never resets Tower state or worktrees. |
|
|
28
28
|
| `tower_reload` | Dev-only: ask DSH HMR to reconcile this profile and reload the Tower Host plugin from disk. Registered only while the tower-hmr component is on (the row is shipped off). The reload is scheduled a few milliseconds after this call returns (so the result reaches the model first). Use after editing the plugin source in a linked profile; plugin_manager install/update already triggers reconciliation by itself. Fails with reload-unavailable when the HMR service is absent. |
|
|
29
29
|
| `tower_resource` | Advisory resource leases per repository (coordination only; no OS CPU/memory hard cap). acquire/release require a token and are tower-only; status is read-only. Expired leases are never taken over silently. |
|
|
30
|
-
| `tower_review` | Submit an independent review verdict bound to the current branch tip: a completed mission starts a review round, and while the mission is active a later route may still record a verdict for that same reviewed tip, appending to the same round without changing the status (a moved tip is rejected with an explanation). status is "clean", "p1-Nitems", or "p2-Nitems"; merge is "merge", "fix-then-merge", or "hold" (only clean + merge is later approval). Non-clean returns the mission to active. Reviewers are capped at 5 rounds per branch; a tower-submitted review is break-glass and requires breakGlassReason. |
|
|
30
|
+
| `tower_review` | Submit an independent review verdict bound to the current branch tip: a completed mission starts a review round, and while the mission is active a later route may still record a verdict for that same reviewed tip, appending to the same round without changing the status (a moved tip is rejected with an explanation). status is "clean", "p1-Nitems", or "p2-Nitems"; merge is "merge", "fix-then-merge", or "hold" (only clean + merge is later approval). Non-clean returns the mission to active. Reviewers are capped at 5 rounds per branch; a tower-submitted review is break-glass and requires breakGlassReason. The tool is not registered when review.mode is off. |
|
|
31
31
|
| `tower_roster` | List tower roster agents with authoritative live activity from the subagent catalog (running/inactive/missing/unknown). Read-only; never resumes children. Use before completion or merge decisions. |
|
|
32
32
|
| `tower_send` | Send a durable inbox message to a tower participant: a roster agent name, "tower", or "all" (broadcast). The message is persisted first; when the recipient is a currently running roster agent the tower also steers it into that child's current turn. Returns delivery results. |
|
|
33
|
-
| `tower_spawn` | Spawn a worker or reviewer for a mission. Workers use the tower-worker preset; reviewers require a completed mission and use the read-only tower-reviewer preset. Provisions (or reuses) the mission git worktree and branch, generates a self-contained briefing, and starts a background subagent in that worktree. The child final report is delivered back to this session; tower_roster shows authoritative activity. Records the agent in the roster; reviewer spawns record the branch tip so a reviewer that modifies the branch cannot submit a review. Returns immediately unless wait=true. |
|
|
34
|
-
| `tower_status` | Read the Tower dashboard for a repository: state path, base branch, revision, mission status counts, paged mission summaries (scope/deps/tasks/worktree/owner), roster size, findings, archive count, config path and the activity tail. Also reports the repo-scoped git lock holder (`gitLock: {held,pid,phase,heldMs}`) so a merge holding the mutex is visible. Pass missionId for one full mission (tasks, notes, reviews, findings, completion report; archived missions are loaded from their year file); offset/limit page the mission list (default 20, max 100); includeGate adds preflight gate digests for the page; includeArchived appends compact archived summaries after the active page; format:"markdown" returns the human-readable mission board (active missions plus an archived count). Read-only. |
|
|
33
|
+
| `tower_spawn` | Spawn a worker or reviewer for a mission. Workers use the tower-worker preset; reviewers require a completed mission and use the read-only tower-reviewer preset, and the reviewer role is refused when review.mode is off. Provisions (or reuses) the mission git worktree and branch, generates a self-contained briefing, and starts a background subagent in that worktree. The child final report is delivered back to this session; tower_roster shows authoritative activity. Records the agent in the roster; reviewer spawns record the branch tip so a reviewer that modifies the branch cannot submit a review. Returns immediately unless wait=true. |
|
|
34
|
+
| `tower_status` | Read the Tower dashboard for a repository: state path, base branch, revision, mission status counts, paged mission summaries (scope/deps/tasks/worktree/owner), roster size, findings, archive count, config path and the activity tail. Also reports the repo-scoped git lock holder (`gitLock: {held,pid,phase,heldMs}`) so a merge holding the mutex is visible, and the resolved review policy (`review: {mode, available, required, detail}`; mode off means review is intentionally unavailable). Pass missionId for one full mission (tasks, notes, reviews, findings, completion report; archived missions are loaded from their year file); offset/limit page the mission list (default 20, max 100); includeGate adds preflight gate digests for the page; includeArchived appends compact archived summaries after the active page; format:"markdown" returns the human-readable mission board (active missions plus an archived count). Read-only. |
|
|
35
35
|
| `tower_teardown` | Tear down eligible mission worktrees after their missions are merged or abandoned. A worktree whose roster agent is running is always kept (force cannot override); dirty worktrees need force; exclude keeps named missions. dry_run previews the remove/keep decisions without changing anything. Terminal missions whose worktree is gone and whose configured archive policy is due (archive.afterDays / archive.maxMissions) are moved to <git-common-dir>/dsh-tower/archive/ so state.json stops rewriting them; nothing is deleted. |
|
|
36
36
|
| `tower_test` | Run the mission worktree's declared tower.testDiscovery (package.json) and record the receipt. Returns ok:false with diagnostics when discovery fails; does not change mission status. Use tower_complete for automatic admission checks. |
|
package/docs/reference/tools.md
CHANGED
|
@@ -38,7 +38,7 @@ findings but not mutate them. Violations throw `[tower:not-authorized]` or
|
|
|
38
38
|
| `tower_plan` | `missions[]` (`title` — printable Unicode, NFC-normalized, ≤120 characters, `scope[]`, `tasks[]?`, `deps[]?`, `kind?`, `context?`, `references[]?` ≤20, `reviewPolicy?`, `reviewExemptionReason?`) | Create missions `M1..`; overlapping build scopes auto-add a dependency; rejects unknown deps and cycles. Mission ids stay ASCII (`M1`, `M2`); invisible characters and over-long titles are rejected with `invalid-argument`. `reviewPolicy:"none"` requires a non-empty reason; when the review gate is on, preflight also requires `gates.allowReviewExemption`. |
|
|
39
39
|
| `tower_mission` | `id` (alias `missionId`), `status?`, `task_done?`, `task_drop?`+`task_drop_reason?`, `blocker?`, `clear_blockers?`, `note?`, `scope?`, `pause_cause?`, `authorization?` | Update one mission. Worker-safe fields are task/blocker/note; `merged` cannot be set here. Paused missions record `pause_cause` (user/failure/resource/dependency); resuming a user-paused mission honors `mode.requireResumeAuthorization`. |
|
|
40
40
|
\|\ `tower_status`\ \|\ `missionId\?`,\ `offset\?`,\ `limit\?`,\ `includeGate\?`,\ `includeArchived\?`,\ `format\?`\ \|\ Dashboard\ by\ default\.\ Paged\ missions\ carry\ `reportBytes`\ only;\ `missionId`\ returns\ full\ detail\ \(tasks/notes/reviews/findings/completionReport\ body\),\ and\ an\ archived\ mission\ is\ loaded\ from\ its\ year\ file\.\ `offset`/`limit`\ page\ active\ missions\ \(default\ 20,\ max\ 100\),\ `includeGate`\ adds\ gate\ digests,\ `includeArchived`\ appends\ compact\ archived\ summaries\ \(flagged\ `archived:true`,\ no\ report\ body\)\ after\ the\ active\ page,\ and\ `format:"markdown"`\ returns\ the\ board\ \(active\ rows\ plus\ an\ archived\ count\)\.\ Includes\ `mode`\ with\ `lastChange`\ and\ recent\ `history`\ when\ the\ host\ mode\ hook\ is\ available\.\ `archive`\ reports\ `\{count,\ years,\ unreadable,\ message}`;\ a\ corrupt\ archive\ degrades\ to\ `unreadable:true`\ instead\ of\ failing\ the\ dashboard\.\ Also\ returns\ `gitLock`\ \(`\{held,\ pid,\ phase,\ heldMs}`\)\ for\ the\ repo-scoped\ git\ mutex,\ and\ `\{held:\ false}`\ when\ it\ is\ free\.\ \|
|
|
41
|
-
| `tower_spawn` | `missionId` (alias `id`), `role` (`worker`/`reviewer`), `name?`, `instructions?`, `worktreeRoot?`, `wait?`, `waitMs?`, `takeover?`, `includeDirtyBase?` | Provision/reuse the worktree and start a subagent. Worker uses `tower-worker`; reviewer requires a `completed` mission and uses the read-only `tower-reviewer`. `takeover:true` replaces a live worker entry that is no longer running (increments ownerGeneration). Only one live worker per mission; `spawn.maxLiveWorkers` caps the repository. |
|
|
41
|
+
| `tower_spawn` | `missionId` (alias `id`), `role` (`worker`/`reviewer`), `name?`, `instructions?`, `worktreeRoot?`, `wait?`, `waitMs?`, `takeover?`, `includeDirtyBase?` | Provision/reuse the worktree and start a subagent. Worker uses `tower-worker`; reviewer requires a `completed` mission and uses the read-only `tower-reviewer`; with `review.mode:off` the reviewer role is refused with `[tower:review-disabled]`. `takeover:true` replaces a live worker entry that is no longer running (increments ownerGeneration). Only one live worker per mission; `spawn.maxLiveWorkers` caps the repository. |
|
|
42
42
|
| `tower_roster` | — | Roster with live activity (`running`/`inactive`/`missing`/`unknown`) and `stale` flags from the subagent catalog. Read-only. |
|
|
43
43
|
| `tower_adopt` | `retireMissing?` | Reconcile roster entries with the durable subagent catalog. Provably gone agents are retired (default) or marked `missing` (`retireMissing:false`) so they can be inspected/taken over. |
|
|
44
44
|
| `tower_diff` | `missionId` (alias `id`), `maxBytes?` | Read-only merge-base diff: files, commits, capped patch. Safe for reviewers. |
|
|
@@ -48,11 +48,11 @@ findings but not mutate them. Violations throw `[tower:not-authorized]` or
|
|
|
48
48
|
| `tower_interrupt` | `to?` (roster name), `agentId?`, `reason?` | Interrupt one running roster agent via `subagents.interrupt`, record `state:"interrupted"`. Idempotent (not running → `interrupted:false`). |
|
|
49
49
|
| `tower_finding` | `type`, `title`, `summary`, `details`, `suggested_fix`, `severity?`, `missionId?`, `location?` | Record an out-of-scope issue; reviewers use this instead of editing. Finds start as `open`. |
|
|
50
50
|
| `tower_findings` | `id?`, `status?`, `note?`, filters (`type?`, `severity?`, `missionId?`, `open?`, `limit?`) | Lifecycle tool: list/read/transition. Statuses `open → triaged → fixed`/`wontfix`, reopen allowed. Only the tower or a worker may mutate; a reviewer can list/read. |
|
|
51
|
-
| `tower_review` | `missionId` (alias `id`), `status` (`clean`/`p1-Nitems`/`p2-Nitems`), `merge` (`merge`/`fix-then-merge`/`hold`), `findings`, `decision`, `checks?`, `breakGlassReason?` | Reviewer verdict bound to the current tip. Reviewer must be the assigned reviewer (or tower break-glass). A later route may record a verdict for the same reviewed tip while the mission is active: it appends to that round (`appended:true`) without changing the status; a verdict for a moved tip is rejected with an explanation. |
|
|
52
|
-
| `tower_preflight` | `missionId` (alias `id`) | Read-only gate: returns `{ok, blockers[], branchTip, baseTip, changedFiles, review}`. |
|
|
51
|
+
| `tower_review` | `missionId` (alias `id`), `status` (`clean`/`p1-Nitems`/`p2-Nitems`), `merge` (`merge`/`fix-then-merge`/`hold`), `findings`, `decision`, `checks?`, `breakGlassReason?` | Reviewer verdict bound to the current tip. Reviewer must be the assigned reviewer (or tower break-glass). Not registered when `review.mode:off`; a repository-level `off` refuses the submission with `[tower:review-disabled]`. A later route may record a verdict for the same reviewed tip while the mission is active: it appends to that round (`appended:true`) without changing the status; a verdict for a moved tip is rejected with an explanation. |
|
|
52
|
+
| `tower_preflight` | `missionId` (alias `id`) | Read-only gate: returns `{ok, blockers[], branchTip, baseTip, changedFiles, review}`. Review blockers appear only with `review.mode:required`; `off`/`optional` skip them. |
|
|
53
53
|
| `tower_merge` | `missionId` (alias `id`), `timeoutMs?` (alias `timeout_ms`) | Merge after a green gate. Records already-landed merges (`reconciled`), recovers a failed call whose commit exists (`recoveredFromFailure`), and separates `merge-hook-failed` from `merge-conflict`. |
|
|
54
54
|
| `tower_rebase` | `missionId` (alias `id`) | Rebase the branch onto the base in its worktree. Refuses while the worker runs or the worktree is dirty. Already-contained branches return `alreadyMerged` untouched. |
|
|
55
|
-
| `tower_teardown` | `force?`, `exclude?`, `dry_run?` (alias `dryRun`) | Remove eligible merged/abandoned worktrees and branches; running agents are always kept. The final pass applies the `archive` policy: terminal missions whose worktree is gone are moved to `<git-common-dir>/dsh-tower/archive/` once `archive.afterDays`/`archive.maxMissions` says so, and `archived` lists their ids. Nothing is deleted; `archive.afterDays:0` archives a just-torn-down mission immediately. |
|
|
55
|
+
| `tower_teardown` | `force?`, `exclude?`, `dry_run?` (alias `dryRun`) | Remove eligible merged/abandoned worktrees and branches; running agents are always kept. The final pass applies the `archive` policy while the `tower-archive` component row is mounted: terminal missions whose worktree is gone are moved to `<git-common-dir>/dsh-tower/archive/` once `archive.afterDays`/`archive.maxMissions` says so, and `archived` lists their ids. Nothing is deleted; `archive.afterDays:0` archives a just-torn-down mission immediately. |
|
|
56
56
|
| `tower_mode` | — | Report mode, last change, paused goal, denied tools, store path, plugin version, HMR availability, workspace-lock holder. |
|
|
57
57
|
| `tower_control` | `action` (`enter`/`exit`), `reason` | Model-initiated mode change; registered by default; disable with `mode.allowAgentControl:false`; reason is mandatory. |
|
|
58
58
|
| `tower_reload` | `repoRoot?` | Dev-only: ask DSH HMR to reconcile the profile and reload the Host plugin from disk. Registered while the `tower-hmr` component row is on (the row ships off); scheduled after the result (default 50 ms); fails with `reload-unavailable` without HMR. When `repoRoot` is supplied the caller must be the tower. |
|
|
@@ -77,4 +77,4 @@ Code default is off; the shipped patch is on for Kimi-compatible single-active-t
|
|
|
77
77
|
Gate tools return `{ok:false, blockers:[{code, detail}]}`; the code list is in
|
|
78
78
|
[blockers.md](blockers.md). Thrown tool errors carry a stable `[tower:<code>]` prefix
|
|
79
79
|
([errors.md](errors.md)). Successful mutations emit process-local events ([events.md](events.md)),
|
|
80
|
-
and, with `
|
|
80
|
+
and, with the `tower-board` component row mounted, refresh `<git-common-dir>/dsh-tower/MISSIONS.md`. The board location stays `docs.boardLocation`; the old `docs.board` switch is a component row now (#133).
|
package/docs/zh/faq.md
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
分析与决策,再启动 Tower 执行(可选审查)仓库变更。详见[完整指南](guide/scenarios-and-philosophy.md#4-tower-与其他-dsh-工作流模式的区别)。
|
|
13
13
|
|
|
14
14
|
**为什么 worker 刚完成,`tower_preflight` 就因 `no-review` 失败?**
|
|
15
|
-
|
|
16
|
-
reviewer;干净审查后门禁就会变绿。默认 `review.
|
|
15
|
+
只有在审查策略为 `required`(`review.mode:required`)时才会如此:已完成的 mission 在等待独立审查。
|
|
16
|
+
spawn 一个 reviewer;干净审查后门禁就会变绿。默认 `review.mode:optional` 时审查可选,不会产生
|
|
17
17
|
`no-review` blocker。
|
|
18
18
|
|
|
19
19
|
**需要轮询 worker 吗?**
|
|
@@ -35,9 +35,11 @@ reviewer;干净审查后门禁就会变绿。默认 `review.requireReview:fals
|
|
|
35
35
|
正确:分支已经包含在基线中。此时调用 `tower_merge` 记录已落地的合并,而不是 rebase。
|
|
36
36
|
|
|
37
37
|
**可以要求强制独立审查吗?**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
做更窄的豁免。`
|
|
38
|
+
可以。把审查策略设为 `required` —— 插件页上的一个三态控件,或在 profile/仓库配置中写
|
|
39
|
+
`review.mode:required`(默认 `optional`)。策略为 `required` 时,可用 mission 级
|
|
40
|
+
`reviewPolicy:"none"` + 理由 + `gates.allowReviewExemption:true` 做更窄的豁免。`optional` 模式下
|
|
41
|
+
`tower_review` 同样可用并记录结论。旧布尔开关是已弃用、保留一个版本的别名;映射关系见
|
|
42
|
+
[安装、升级与迁移](guide/install-upgrade-migration.md)。
|
|
41
43
|
|
|
42
44
|
**reviewer 能修改文件吗?**
|
|
43
45
|
不能。reviewer 使用只读的 `tower-reviewer` preset(没有 write/edit/pwsh);reviewer spawn 之后分支
|