dsh-tower 2.4.1 → 2.5.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 +51 -0
- package/COMPATIBILITY.md +2 -0
- package/CONTRIBUTING.md +11 -5
- package/GLOSSARY.md +67 -0
- package/README.md +6 -0
- package/app/archive.js +16 -1
- package/app/board.js +16 -1
- package/app/gate.js +14 -0
- package/app/spawn-prechecks.js +18 -3
- package/cordis.patch.yml +27 -8
- package/docs/adr/0004-surface-budget.md +10 -4
- package/docs/adr/0005-component-surface.md +166 -0
- package/docs/architecture/overview.md +47 -20
- package/docs/guide/install-upgrade-migration.md +21 -0
- package/docs/guide/quickstart.md +71 -0
- package/docs/guide/release.md +60 -19
- package/docs/reference/component-surface.md +29 -17
- package/docs/reference/config.generated.md +4 -7
- package/docs/reference/config.md +7 -5
- package/docs/reference/doctor.md +11 -5
- package/docs/reference/surface-budget.md +29 -11
- package/docs/reference/tools.generated.md +1 -1
- package/docs/reference/tools.md +2 -2
- package/docs/zh/guide/install-upgrade-migration.md +1 -1
- package/docs/zh/reference/config.md +7 -5
- package/docs/zh/reference/doctor.md +7 -2
- package/docs/zh/reference/tools.md +3 -2
- package/domain/component-rules.js +216 -0
- package/domain/components.js +169 -49
- package/domain/config/schema.js +46 -23
- package/domain/gate.js +17 -2
- 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/doctor.js +50 -5
- package/host/preset-reviewer.js +27 -0
- package/host/preset-worker.js +32 -0
- package/host/presets.js +17 -0
- package/host/tool-registry.js +21 -4
- 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 +41 -21
- 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 +6 -0
- package/locale/preset-reviewer/zh.json +6 -0
- package/locale/preset-worker/en.json +6 -0
- package/locale/preset-worker/zh.json +6 -0
- 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 +40 -20
- package/package.json +15 -2
- package/scripts/check-components.mjs +642 -42
- package/scripts/check-pack.mjs +7 -3
- package/scripts/component-surface.json +87 -29
- package/scripts/generate-capability-locale.mjs +100 -21
- package/scripts/mount-smoke/probe.mjs +33 -3
- package/scripts/mount-smoke/surface.mjs +144 -1
- package/scripts/mount-smoke.mjs +24 -4
- package/scripts/pack-manifest.txt +15 -0
- package/scripts/release-notes.mjs +256 -0
- package/scripts/surface-baseline.json +32 -3
- package/scripts/surface-baseline.mjs +15 -5
- package/skills.js +4 -1
- package/tools/complete.js +3 -1
- package/tools/spawn.js +5 -5
- package/tools/test.js +7 -7
- package/ui-bundle/locale/en.json +1 -1
- package/ui-bundle/locale/zh.json +1 -1
|
@@ -23,7 +23,7 @@ to a tool that is not on the recorded allowlist (empty since #122) and any
|
|
|
23
23
|
|
|
24
24
|
### Row display metadata (#121)
|
|
25
25
|
|
|
26
|
-
The Plugins page renders
|
|
26
|
+
The Plugins page renders a row as `row.meta.title ?? row.moduleName`
|
|
27
27
|
plus `row.meta.description`. The Host fills `row.meta` by resolving
|
|
28
28
|
`<specifier>/locale/en.json` and then every sibling `<lang>.json` in that
|
|
29
29
|
directory, reading `meta.title` / `meta.description`; title falls back to the full
|
|
@@ -31,7 +31,7 @@ module specifier and description to empty when no resource resolves. That is why
|
|
|
31
31
|
the mandatory `tower` card explains itself (the package root ships
|
|
32
32
|
`locale/*.json`) while a bare subpath row would show only `dsh-tower/batch`.
|
|
33
33
|
|
|
34
|
-
Each optional capability therefore ships its own resolvable subpath resource:
|
|
34
|
+
Each optional capability row therefore ships its own resolvable subpath resource:
|
|
35
35
|
`dsh-tower/skills/locale/{en,zh}.json` and the same for `batch`, `import`,
|
|
36
36
|
`leases`, `tests` and `hmr`. `package.json` `exports` maps every
|
|
37
37
|
`./<capability>/locale/*.json` to `./locale/<capability>/*.json`, and
|
|
@@ -39,31 +39,56 @@ Each optional capability therefore ships its own resolvable subpath resource:
|
|
|
39
39
|
package. The root `./locale/*.json` export stays so the Tower group keeps its own
|
|
40
40
|
`meta`.
|
|
41
41
|
|
|
42
|
-
The
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
The two preset-declaring rows need the same treatment but cannot share a module.
|
|
43
|
+
DSH labels a row from its own `moduleName`, so while both rows mounted
|
|
44
|
+
`@deepseek-ai/dsh-agent-preset` they resolved one locale resource and rendered the
|
|
45
|
+
same title: the mandatory worker dependency and the conditional reviewer switch
|
|
46
|
+
were indistinguishable. That package is also supplied by the DSH profile, not by
|
|
47
|
+
this bundle, so Tower cannot add per-row locale files beside its label. Tower
|
|
48
|
+
therefore owns one tiny declaration module per preset row:
|
|
49
|
+
`host/preset-worker.js` (row specifier `dsh-tower/preset-worker`) and
|
|
50
|
+
`host/preset-reviewer.js` (`dsh-tower/preset-reviewer`). Each is a plain object
|
|
51
|
+
plugin with `inject: ['agentPresets']` whose async `apply` returns
|
|
52
|
+
`ctx.agentPresets.register(config)`; Cordis collects the resolved function as the
|
|
53
|
+
plugin's disposer, so disabling the row or an HMR unload unregisters the preset.
|
|
54
|
+
The registry still owns preset semantics — it mounts the definition's `plugins`
|
|
55
|
+
list itself — and the modules import nothing, so they are renderer-free and do
|
|
56
|
+
not depend on `@deepseek-ai/dsh-agent-preset`. In `cordis.patch.yml` only the two
|
|
57
|
+
`name:` lines changed; the id/name/description/order/plugins config blocks are
|
|
58
|
+
unchanged.
|
|
59
|
+
|
|
60
|
+
The row label files are generated, never hand-written. `npm run
|
|
61
|
+
docs:generate:locale` renders every row label — capability and preset — from the
|
|
62
|
+
`components` block of `locale/en.json` and `locale/zh.json` (the same source
|
|
63
|
+
`docs:check:components` audits): `meta.title` is the component `title`, and
|
|
64
|
+
`meta.description` joins the component `summary` (what the row provides) with its
|
|
65
|
+
`disables` text (what turning it off removes) behind a per-language "When off /
|
|
66
|
+
关闭后" label. The mandatory worker preset has no off state, so its description
|
|
67
|
+
uses a "Mandatory dependency, not a switch / 强制依赖,不是开关" label instead; the
|
|
68
|
+
conditional reviewer keeps the off-effect wording. The row is therefore decidable
|
|
69
|
+
from the page alone. `npm run docs:check:generated:locale` fails on drift, runs in
|
|
70
|
+
`npm run check`, and is its own step in the `test` CI job
|
|
71
|
+
(`.github/workflows/test.yml`). `npm run docs:check:components` also covers the
|
|
72
|
+
generated rows: a label whose `components` entry is missing, or whose `mandatory`
|
|
73
|
+
flag disagrees with the component scope, is refused there. The real
|
|
74
|
+
`npm run smoke:mount` calls the same host `readPluginMeta` the Plugins page uses
|
|
75
|
+
and fails unless the two preset rows resolve distinct title/description maps, so
|
|
76
|
+
the shared-label defect cannot return unnoticed.
|
|
77
|
+
|
|
78
|
+
To label a new row:
|
|
53
79
|
|
|
54
80
|
1. Classify it in `scripts/component-surface.json`, ship it in
|
|
55
81
|
`cordis.patch.yml`, and add `title` / `summary` / `disables` to the
|
|
56
82
|
`components` block of both `locale/en.json` and `locale/zh.json`.
|
|
57
|
-
2. Add `{ specifier, component }` to `CAPABILITY_LOCALES`
|
|
58
|
-
`
|
|
59
|
-
|
|
60
|
-
|
|
83
|
+
2. Add `{ specifier, component }` to `CAPABILITY_LOCALES` — or to
|
|
84
|
+
`PRESET_LOCALES` with `mandatory` matching the component scope for a
|
|
85
|
+
preset-declaring row — in `scripts/generate-capability-locale.mjs`, the
|
|
86
|
+
matching `./<specifier>/locale/*.json` export in `package.json`, and the row
|
|
87
|
+
to the coverage map in `tests/capability-locales.test.mjs`.
|
|
61
88
|
3. Run `npm run docs:generate:locale`, then regenerate
|
|
62
89
|
`scripts/pack-manifest.txt` with `npm run pack:check -- --write`.
|
|
63
90
|
|
|
64
|
-
The
|
|
65
|
-
specifier: its label comes from the `cordis.patch.yml` preset `config.name` /
|
|
66
|
-
`description`. The generated capability files never touch the root locale `meta`.
|
|
91
|
+
The generated row files never touch the root locale `meta`.
|
|
67
92
|
|
|
68
93
|
```
|
|
69
94
|
Host plugin (index.js)
|
|
@@ -90,6 +115,8 @@ Host plugin (index.js)
|
|
|
90
115
|
host/modeStore.js per-agent Tower mode persistence
|
|
91
116
|
host/modeState.js owned in-memory mode state
|
|
92
117
|
host/presets.js cross-field preset validation before work starts
|
|
118
|
+
host/preset-worker.js mandatory worker preset declaration (dsh-tower/preset-worker)
|
|
119
|
+
host/preset-reviewer.js conditional reviewer preset declaration (dsh-tower/preset-reviewer)
|
|
93
120
|
host/compatibility.js COMPATIBILITY.md provenance
|
|
94
121
|
host/tower.js default-deps Tower binding for tests/embedders
|
|
95
122
|
ports (explicit runtime contracts, no implementation):
|
|
@@ -29,6 +29,27 @@ The Host plugin loads at DSH startup: **restart DSH after install or update**.
|
|
|
29
29
|
|
|
30
30
|
## Migration notes
|
|
31
31
|
|
|
32
|
+
- **2.4.2 → 2.5.0 (#131/#133/#135/#136/#142)**: three component-shaped config
|
|
33
|
+
keys are gone — `tests.enabled`, `docs.board`, and `archive.enabled`. The
|
|
34
|
+
`tower-tests`, `tower-board`, and `tower-archive` component rows are the only
|
|
35
|
+
switches. A profile or repository config that still writes any of these keys is
|
|
36
|
+
rejected as `config-unknown-key` with an upgrade note and the host stops
|
|
37
|
+
mounting, so delete the key and enable or disable the row in the plugin page
|
|
38
|
+
instead. `docs.boardLocation` and `archive.afterDays`/`archive.maxMissions`
|
|
39
|
+
remain tuning keys. With `tower-tests` off there is no `tower_test` tool and no
|
|
40
|
+
receipt, which also means `gates.requireTests` cannot be satisfied: planning and
|
|
41
|
+
mode entry refuse that combination, and the merge gate's `tests-missing` blocker
|
|
42
|
+
names the disabled row and the fix. `tower_test` no longer reports "not
|
|
43
|
+
configured" for a component cause; it reports `disabled by component
|
|
44
|
+
tower-tests` with the remedy.
|
|
45
|
+
Two more runtime rules became explicit: a config that sets `spawn.requireLease`
|
|
46
|
+
while the `tower-leases` row is off is refused up front with `config-invalid`
|
|
47
|
+
naming the row and both remedies (previously every spawn failed at `lease-busy`
|
|
48
|
+
advising an unregistered `tower_resource`), and `/tower` re-validates the runtime
|
|
49
|
+
preconditions only when it enters the mode, so `/tower off` and a forwarded
|
|
50
|
+
`/tower <text>` keep working while an unrelated row is disabled. No state
|
|
51
|
+
migration; see [component rows and messages](quickstart.md#component-rows-runtime-preconditions-and-message-language).
|
|
52
|
+
|
|
32
53
|
- **state schema v1 → v2 (unreleased)**: `state.json` migrates automatically. The first **write** after
|
|
33
54
|
upgrading keeps a `state.json.v1.bak` backup, advances `revision` once, and appends
|
|
34
55
|
`state.migrated {from,to}` to `activity.jsonl`; read-only tools keep reading a v1 file without
|
package/docs/guide/quickstart.md
CHANGED
|
@@ -100,3 +100,74 @@ Statuses are `open → triaged → fixed` / `wontfix`, and `fixed`/`wontfix` can
|
|
|
100
100
|
tower or a worker may change a finding; reviewers can list and read.
|
|
101
101
|
|
|
102
102
|
A complete copy-paste example: [examples/first-mission.md](../examples/first-mission.md).
|
|
103
|
+
|
|
104
|
+
## Component rows, runtime preconditions and message language
|
|
105
|
+
|
|
106
|
+
The optional component rows are the only switches in this bundle (#121, #136). Three runtime rules
|
|
107
|
+
follow from that, plus the language policy for everything a tool prints.
|
|
108
|
+
|
|
109
|
+
### A row is the only switch
|
|
110
|
+
|
|
111
|
+
`tests.enabled` was removed (#136). Discovery is on exactly while the `tower-tests` row is mounted:
|
|
112
|
+
with the row off there is no `tower_test` tool and no test receipt to gate on, so a second boolean
|
|
113
|
+
could only disagree with the mounted profile. A repository config that still writes
|
|
114
|
+
`tests.enabled` is rejected as `config-unknown-key`; delete the key and enable or disable the row
|
|
115
|
+
instead.
|
|
116
|
+
|
|
117
|
+
The same reasoning covers the other rows:
|
|
118
|
+
|
|
119
|
+
- `tower-leases` owns `tower_resource`. `spawn.requireLease` is satisfied through that tool, so a
|
|
120
|
+
config that requires a lease while the row is off is refused by the runtime preconditions with
|
|
121
|
+
`config-invalid`, naming the row and both remedies ("re-enable the tower-leases component or clear
|
|
122
|
+
spawn.requireLease") instead of failing every spawn at `lease-busy` with advice to use a tool that
|
|
123
|
+
is not registered (#131).
|
|
124
|
+
- `tower-tests` must stay mounted while `gates.requireTests` is true. Planning and mode entry refuse
|
|
125
|
+
the combination; the merge gate's `tests-missing` blocker also names the disabled row as the cause
|
|
126
|
+
(#140).
|
|
127
|
+
- `tower-hmr` ships off; `/tower reload` reports it by row name.
|
|
128
|
+
|
|
129
|
+
No runtime path may claim a repository is "not configured" when a component row is the reason.
|
|
130
|
+
Runtime refusals report the cause as `disabled by component <id>` and come with the fix (`enable the
|
|
131
|
+
<id> row in the plugin page and restart DSH`).
|
|
132
|
+
|
|
133
|
+
### Preconditions run on mode entry, not on every `/tower` input (#142)
|
|
134
|
+
|
|
135
|
+
`/tower` re-validates the runtime preconditions (presets, component/config contradictions) only when
|
|
136
|
+
it **enters** Tower mode:
|
|
137
|
+
|
|
138
|
+
| Input | Preconditions |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `/tower`, `/tower on`, `/tower <text>` while the mode is off | validated |
|
|
141
|
+
| `/tower off`, `/tower off <text>` | not validated |
|
|
142
|
+
| `/tower <text>` while the mode is already on | not validated |
|
|
143
|
+
| `/tower ack`, `/tower base <branch>`, `/tower reload` | not validated |
|
|
144
|
+
|
|
145
|
+
The reason: a row disabled after the session started (or a profile reloaded with a stricter config)
|
|
146
|
+
must not make `/tower off` — the command a human needs to leave the mode — or an ordinary forwarded
|
|
147
|
+
instruction fail. Capability guards still run where the work happens: `tower_spawn`, planning and
|
|
148
|
+
merge/preflight evaluate their own preconditions, so the checks cannot be bypassed by entering the
|
|
149
|
+
mode first.
|
|
150
|
+
|
|
151
|
+
### Language of tool output
|
|
152
|
+
|
|
153
|
+
Machine-facing output is **English only**, regardless of the request locale:
|
|
154
|
+
|
|
155
|
+
- tool errors (`[tower:<code>] ...`), `nextAction`, blocker `detail` strings, `tower_status` /
|
|
156
|
+
`tower_doctor` detail lines, and completion reports;
|
|
157
|
+
- anything that a script, a log search or another agent is expected to pattern-match.
|
|
158
|
+
|
|
159
|
+
Localized, human-facing copy lives in `locale/*.json` (component descriptions, UI labels) and is the
|
|
160
|
+
only translated surface. Two rules keep the split honest:
|
|
161
|
+
|
|
162
|
+
1. New detail/error strings are written in English even when the surrounding UI is Chinese; they are
|
|
163
|
+
not added to `locale/*.json`.
|
|
164
|
+
2. A localized label never shadows a machine-readable cause. The row id, the error code and the
|
|
165
|
+
English remedy phrase stay verbatim inside the message, so a grep for
|
|
166
|
+
`disabled by component tower-tests` works in every locale.
|
|
167
|
+
|
|
168
|
+
`tower_status` and `tower_doctor` follow this policy but are not textually identical: `tower_status`
|
|
169
|
+
returns the cause only — `components.disabled` is `[{id, reason}]` with `reason` set to
|
|
170
|
+
`disabled by component <id>` — while `tower_doctor` repeats that reason and adds the English remedy
|
|
171
|
+
(`enable the reported row in the plugin page and restart DSH` for the `components` check; a
|
|
172
|
+
capability-specific check names the row directly, e.g. `enable the tower-tests row and restart DSH`).
|
|
173
|
+
Neither interpolates a translated label.
|
package/docs/guide/release.md
CHANGED
|
@@ -97,29 +97,70 @@ git for-each-ref refs/tags/v2.1.0-beta.1 --format='%(objecttype)|%(subject)'
|
|
|
97
97
|
The pushed tag must be exactly `v<version>`. The tag push re-runs `release:check` against
|
|
98
98
|
`<tag>^`; this is why tagging a commit whose `package.json` did not move fails.
|
|
99
99
|
|
|
100
|
-
## 6. GitHub Release
|
|
100
|
+
## 6. GitHub Release with cumulative notes
|
|
101
|
+
|
|
102
|
+
A Release body is not just its own CHANGELOG section: it carries **every** section after the
|
|
103
|
+
previous GitHub Release (see [Cumulative Release notes](#cumulative-release-notes--累积发布说明)
|
|
104
|
+
below). `scripts/release-notes.mjs` is the authority for that body — do not hand-extract a single
|
|
105
|
+
section. The checklist step is `npm run release:notes -- --version <v> --since <prev-release> --output notes.md`, then pass that file to `gh release create`/`gh release edit` with `--notes-file notes.md`.
|
|
101
106
|
|
|
102
107
|
```bash
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
# Stable release (the common case): no pre-release flags.
|
|
109
|
+
VERSION=2.3.0
|
|
110
|
+
SINCE=2.1.0 # the previous GitHub Release, not the previous tag
|
|
111
|
+
npm run release:notes -- --version "$VERSION" --since "$SINCE" --output notes.md
|
|
112
|
+
npm run release:notes -- --version "$VERSION" --since "$SINCE" --check notes.md
|
|
113
|
+
gh release create "v$VERSION" --title "dsh-tower $VERSION" --notes-file notes.md --verify-tag
|
|
114
|
+
# editing a Release that already exists (for example, to correct its window):
|
|
115
|
+
gh release edit "v$VERSION" --notes-file notes.md
|
|
109
116
|
gh release view "v$VERSION" --json tagName,isPrerelease,body
|
|
110
117
|
```
|
|
111
118
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
($lines[$start..$end] -join "`n").Trim() | Set-Content release-notes.md -Encoding utf8
|
|
119
|
+
```bash
|
|
120
|
+
# Pre-release variant (a beta, for example 2.5.0-beta.1): mark the Release and keep "latest"
|
|
121
|
+
# on the previous stable Release.
|
|
122
|
+
VERSION=2.5.0-beta.1
|
|
123
|
+
SINCE=2.4.2 # the previous GitHub Release; a pre-release counts as one too
|
|
124
|
+
npm run release:notes -- --version "$VERSION" --since "$SINCE" --output notes.md
|
|
125
|
+
gh release create "v$VERSION" --title "dsh-tower $VERSION" --notes-file notes.md \
|
|
126
|
+
--prerelease --verify-tag --latest=false
|
|
121
127
|
```
|
|
122
128
|
|
|
123
|
-
`--
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
`--check notes.md` exits 1 when the file differs from the generated text (it prints the first
|
|
130
|
+
differing line and both byte counts), so a correction is verifiable. The script exits 2 on a
|
|
131
|
+
missing/unknown flag, an invalid version, `since >= version`, or an empty window; it never emits
|
|
132
|
+
a Release body it cannot justify.
|
|
133
|
+
|
|
134
|
+
`--verify-tag` refuses to create the tag at the default branch when the pushed tag is missing.
|
|
135
|
+
A beta Release adds `--prerelease --latest=false`: GitHub marks it as a pre-release and keeps
|
|
136
|
+
the "latest" marker on the stable Release. Releases are what consumers and the plugin directory
|
|
137
|
+
can see — a tag alone is not a release.
|
|
138
|
+
|
|
139
|
+
## Cumulative Release notes / 累积发布说明
|
|
140
|
+
|
|
141
|
+
A GitHub Release is cumulative: it aggregates every CHANGELOG section **after the previous
|
|
142
|
+
Release**, up to and including the version being released.
|
|
143
|
+
|
|
144
|
+
- The **previous Release** is the latest published GitHub Release by version. Pre-releases
|
|
145
|
+
created with `--prerelease` count as Releases, so a `2.1.0-beta.1` Release is a boundary too.
|
|
146
|
+
- **Tag-only versions have no Release.** A version that was tagged or published to npm without
|
|
147
|
+
creating a GitHub Release — 2.1.1 and 2.1.2 were tag-only, and 2.2.0 too — would otherwise
|
|
148
|
+
lose its notes; its CHANGELOG section rolls into the next Release.
|
|
149
|
+
- Example: once 2.1.0 is the previous Release, the 2.3.0 Release includes 2.1.1, 2.1.2, 2.2.0
|
|
150
|
+
and 2.3.0:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npm run release:notes -- --version 2.3.0 --since 2.1.0 --output notes.md
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The window is `(2.1.0, 2.3.0]`; `2.1.0-beta.1` sorts before 2.1.0 and therefore stays out.
|
|
157
|
+
- `npm run release:notes` (`scripts/release-notes.mjs`) is the authoritative generator: it reads
|
|
158
|
+
`CHANGELOG.md`, filters the window, orders sections by SemVer (pre-release identifiers
|
|
159
|
+
included), and emits the `# dsh-tower <v>` title, a provenance quote naming the previous
|
|
160
|
+
Release and the included versions, then the sections separated by `---`.
|
|
161
|
+
- `--check <file>` re-verifies an existing notes file byte-for-byte (exit 1 on a mismatch), so
|
|
162
|
+
the body of a published Release can be audited without editing it. Use `--root <dir>` to check
|
|
163
|
+
another checkout.
|
|
164
|
+
|
|
165
|
+
Never hand-extract only the current version's section: that is what produced single-section
|
|
166
|
+
Releases whose tag-only predecessors were invisible.
|
|
@@ -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-10-23 · baseline 2026-10-23 · visible components:
|
|
6
|
+
Decision: [ADR 0005](../adr/0005-component-surface.md) · decision date: 2026-10-23 · 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
|
|
@@ -29,11 +29,14 @@ 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-
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
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. |
|
|
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. |
|
|
38
|
+
| `preset-tower-reviewer` | visible | row | Declared by dsh-tower/preset-reviewer: the reviewer cannot be dispatched. Supported while review.requireReview is false (merge is unaffected); with the gate on 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
|
|
|
@@ -71,9 +74,9 @@ All keys from `domain/config/schema.js`, classified on the component axis.
|
|
|
71
74
|
|---|---|---|
|
|
72
75
|
| `section` | `core` | Legacy alias for mode.promptSection: the Tower-mode prompt contract, not a capability. |
|
|
73
76
|
| `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. |
|
|
77
|
+
| `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)) |
|
|
78
|
+
| `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)) |
|
|
79
|
+
| `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
80
|
| `mode.promptSection` | `core` | Replaces the whole Tower-mode prompt text; contract text, not a capability. |
|
|
78
81
|
| `mode.sectionAppend` | `optional-config` | Appends team conventions to the prompt. |
|
|
79
82
|
| `mode.allowAgentControl` | `core` | Decides whether the model may toggle the mode (ADR 0003). A governance rule, never a component. |
|
|
@@ -104,28 +107,37 @@ All keys from `domain/config/schema.js`, classified on the component axis.
|
|
|
104
107
|
| `spawn.maxLiveWorkers` | `core` | Resource-safety cap on live workers. |
|
|
105
108
|
| `spawn.staleMs` | `optional-config` | Roster staleness window before merge is blocked. |
|
|
106
109
|
| `spawn.maxWaitMs` | `optional-config` | Upper bound for a blocking spawn wait. |
|
|
107
|
-
| `spawn.requireLease` | `optional-config` | Advisory-lease gate before a worker starts. |
|
|
110
|
+
| `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
111
|
| `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. |
|
|
112
|
+
| `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)) |
|
|
113
|
+
| `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
114
|
| `tests.timeoutMs` | `optional-config` | Per-command discovery timeout. |
|
|
113
115
|
| `tests.maxBytes` | `optional-config` | Captured discovery output cap. |
|
|
114
|
-
| `tests.autoChecks` | `optional-config` | Attaches a tests check to completion when discovery runs. |
|
|
116
|
+
| `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
117
|
| `tests.allowPackageDiscovery` | `core` | Allows repository code to execute; a security contract, not a component. |
|
|
116
118
|
| `gates.requireChecks` | `forbidden-to-disable` | Requires completion evidence before merge; a merge gate. |
|
|
117
119
|
| `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
120
|
| `gates.allowReviewExemption` | `forbidden-to-disable` | The review-exemption path is part of the review and merge contract. |
|
|
119
121
|
| `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. |
|
|
122
|
+
| `docs.boardLocation` | `optional-config` | Chooses where the board is written; whether it is written is the tower-board component row (#133). |
|
|
123
|
+
| `archive.afterDays` | `optional-config` | Archive age threshold inside the optional tower-archive component (#135). |
|
|
124
124
|
| `archive.maxMissions` | `optional-config` | Hot terminal-mission cap. |
|
|
125
125
|
| `leases.ttlMs` | `optional-config` | Tuning inside the optional tower-leases component. |
|
|
126
126
|
| `dev.reloadDelayMs` | `optional-config` | Delay so a tool result is delivered before a scheduled reload inside the optional tower-hmr component. |
|
|
127
127
|
| `dev.reloadRequiredIds` | `optional-config` | Loader ids passed to reconcileProfile as required by the tower-hmr component. |
|
|
128
128
|
|
|
129
|
+
## Behaviour knobs
|
|
130
|
+
|
|
131
|
+
Non-boolean keys that gate behaviour are reviewed on the same axis as booleans
|
|
132
|
+
(#138): each must be `core`, `optional-component`, `forbidden-to-disable`, or an
|
|
133
|
+
`optional-config` key with a dated tuning-only exemption. A string or union that
|
|
134
|
+
hides a capability cannot use the tuning class without that record.
|
|
135
|
+
|
|
136
|
+
| Key |
|
|
137
|
+
|---|
|
|
138
|
+
| `spawn.requireLease` |
|
|
139
|
+
| `tests.admission` |
|
|
140
|
+
|
|
129
141
|
## Non-goals
|
|
130
142
|
|
|
131
143
|
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: 53 · 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. |
|
|
@@ -43,7 +43,6 @@
|
|
|
43
43
|
| `spawn.requireLease` | `` | Optional lease name tower_spawn must hold before starting a worker. |
|
|
44
44
|
| `send.maxPerMinute` | `120` | Per-repository tower_send rate limit per minute (0 disables). |
|
|
45
45
|
| `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
46
|
| `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
47
|
| `tests.timeoutMs` | `60000` | Per discovery command timeout. |
|
|
49
48
|
| `tests.maxBytes` | `16777216` | Maximum captured stdout bytes per command. |
|
|
@@ -53,10 +52,8 @@
|
|
|
53
52
|
| `gates.requireTests` | `false` | Require a passing test-discovery receipt for the reviewed worktree tip before merge. |
|
|
54
53
|
| `gates.allowReviewExemption` | `false` | Allow reviewPolicy:none missions with a non-empty reviewExemptionReason to skip review blockers. |
|
|
55
54
|
| `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). |
|
|
55
|
+
| `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). |
|
|
56
|
+
| `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
57
|
| `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
58
|
| `leases.ttlMs` | `900000` | Default advisory lease lifetime; expiry never auto-releases. |
|
|
62
59
|
| `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:
|
|
@@ -74,7 +79,6 @@ violates the declared schema (for example `merge.timeoutMs` below 1000) fail wit
|
|
|
74
79
|
| `spawn.requireLease` | `''` | Optional lease name that must be held before a worker can spawn. |
|
|
75
80
|
| `send.maxPerMinute` | `120` | Per-repository `tower_send` rate limit (0 disables); exceeding it returns `rate-limited`. |
|
|
76
81
|
| `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
82
|
| `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
83
|
| `tests.timeoutMs` | `60000` | Per discovery command timeout. |
|
|
80
84
|
| `tests.maxBytes` | `16777216` (16 MiB) | Captured stdout cap per command. |
|
|
@@ -84,10 +88,8 @@ violates the declared schema (for example `merge.timeoutMs` below 1000) fail wit
|
|
|
84
88
|
| `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
89
|
| `gates.allowReviewExemption` | `false` | With `review.requireReview:true`, allow `reviewPolicy:"none"` + a non-empty reason to skip review blockers. Off deliberately; irrelevant while the global review gate is off. |
|
|
86
90
|
| `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). |
|
|
91
|
+
| `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. |
|
|
92
|
+
| `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
93
|
| `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
94
|
| `leases.ttlMs` | `900000` | Default advisory lease lifetime; expiry never auto-releases. |
|
|
93
95
|
| `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`.
|
|
@@ -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,24 @@ 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 refuse a row config outside its
|
|
69
|
+
component's `allowedOverrides`.
|
|
49
70
|
|
|
50
71
|
## Config keys
|
|
51
72
|
|
|
@@ -89,7 +110,6 @@ not free growth: a later increase still needs its own decision.
|
|
|
89
110
|
| `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
111
|
| `send.maxPerMinute` | tuning | Message rate limit; zero disables it. |
|
|
91
112
|
| `send.interruptDefault` | tuning | Default for the interrupt-before-deliver flag. |
|
|
92
|
-
| `tests.enabled` | core | Whether declared discovery may run at all. |
|
|
93
113
|
| `tests.admission` | core | Chooses when repository code is executed. |
|
|
94
114
|
| `tests.timeoutMs` | tuning | Per-command discovery timeout. |
|
|
95
115
|
| `tests.maxBytes` | tuning | Captured discovery output cap. |
|
|
@@ -99,10 +119,8 @@ not free growth: a later increase still needs its own decision.
|
|
|
99
119
|
| `gates.requireTests` | core | Requires a test receipt before merge. |
|
|
100
120
|
| `gates.allowReviewExemption` | core | Allows a review exemption and its reason. |
|
|
101
121
|
| `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. |
|
|
122
|
+
| `docs.boardLocation` | tuning | Chooses where the board is written; whether it is written is the tower-board row. |
|
|
123
|
+
| `archive.afterDays` | tuning | Archive age threshold; archival itself is the tower-archive row. |
|
|
106
124
|
| `archive.maxMissions` | tuning | Hot terminal-mission cap. |
|
|
107
125
|
| `leases.ttlMs` | tuning | Default advisory lease lifetime. |
|
|
108
126
|
| `dev.reloadDelayMs` | tuning | Delay so a tool result is delivered before reload. |
|
|
@@ -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: 53 · 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
|
|---|---|
|