dsh-tower 2.5.0 → 2.7.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 +114 -0
- package/COMPATIBILITY.md +2 -0
- package/CONTRIBUTING.md +1 -1
- package/GLOSSARY.md +6 -0
- package/PRINCIPLES.md +15 -7
- package/README.en.md +18 -16
- package/README.md +18 -11
- package/adapters/leases.js +20 -6
- package/adapters/legacy.js +149 -37
- package/adapters/stateFile.js +11 -1
- package/app/archive.js +30 -5
- package/app/briefing.js +95 -12
- package/app/checkCompletion.js +107 -18
- package/app/enrichedMissions.js +1 -1
- package/app/freshness.js +77 -0
- package/app/gate.js +26 -12
- package/app/merge.js +7 -4
- package/app/mission.js +24 -1
- package/app/missionContext.js +2 -1
- package/app/plan.js +64 -1
- package/app/provision.js +133 -22
- package/app/read.js +38 -5
- package/app/rebase.js +10 -8
- package/app/review.js +104 -61
- package/app/reviewCheckout.js +133 -0
- package/app/sandbox.js +121 -0
- package/app/sharedCheckout.js +90 -0
- package/app/spawn-prechecks.js +31 -20
- package/app/spawn-support.js +59 -8
- package/app/spawn.js +3 -0
- package/app/teardown.js +119 -2
- package/app/tower.js +34 -6
- package/client.js +321 -0
- package/cordis.patch.yml +55 -3
- package/docs/adr/0005-component-surface.md +58 -13
- package/docs/adr/0006-mission-shapes.md +306 -0
- package/docs/architecture/overview.md +18 -10
- package/docs/examples/first-mission.md +3 -2
- package/docs/faq.md +9 -7
- package/docs/guide/install-upgrade-migration.md +58 -7
- package/docs/guide/quickstart.md +20 -10
- package/docs/guide/scenarios-and-philosophy.md +35 -28
- package/docs/operations/runbook.md +1 -1
- package/docs/operations/security.md +11 -10
- package/docs/reference/blockers.md +20 -11
- package/docs/reference/component-surface.md +20 -8
- package/docs/reference/config.generated.md +8 -5
- package/docs/reference/config.md +30 -5
- package/docs/reference/doctor.md +7 -7
- package/docs/reference/errors.generated.md +1 -0
- package/docs/reference/errors.md +2 -1
- package/docs/reference/operations.md +1 -1
- package/docs/reference/shapes.md +123 -0
- package/docs/reference/surface-budget.md +53 -4
- package/docs/reference/testing.md +5 -3
- package/docs/reference/tools.generated.md +7 -7
- package/docs/reference/tools.md +5 -5
- package/docs/zh/faq.md +7 -5
- package/docs/zh/guide/install-upgrade-migration.md +25 -6
- 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 +13 -9
- package/docs/zh/reference/config.md +22 -5
- package/docs/zh/reference/errors.md +2 -1
- package/docs/zh/reference/testing.md +5 -4
- package/docs/zh/reference/tools.md +3 -3
- package/domain/batch.js +75 -105
- package/domain/collab/batch.js +30 -10
- package/domain/collab/spawn.js +53 -16
- package/domain/components.js +12 -4
- package/domain/config/schema.js +87 -22
- package/domain/contracts/outputs.js +2 -1
- package/domain/contracts/tool-inputs/missions.js +10 -2
- package/domain/errors.js +6 -0
- package/domain/evidence.js +283 -0
- package/domain/gate.js +17 -34
- package/domain/integration/gate.js +17 -43
- package/domain/integration/init.js +3 -2
- package/domain/integration/provision.js +51 -0
- package/domain/integration/rebase.js +16 -1
- package/domain/integration/review.js +203 -9
- package/domain/mission.js +16 -33
- package/domain/missions/completion.js +46 -8
- package/domain/missions/findings.js +2 -1
- package/domain/missions/lifecycle.js +131 -35
- package/domain/missions/plan.js +30 -6
- package/domain/missions/review.js +2 -18
- package/domain/planning.js +5 -5
- package/domain/read/missionContext.js +17 -3
- package/domain/read/views.js +5 -1
- package/domain/roles.js +222 -0
- package/domain/roster.js +9 -3
- package/domain/scope.js +51 -0
- package/domain/shapes/build.js +254 -0
- package/domain/shapes/index.js +171 -0
- package/domain/shapes/survey.js +445 -0
- package/domain/state/archive.js +28 -4
- package/domain/state/migrations.js +111 -4
- package/domain/state/schema.js +69 -7
- package/domain/state/semantics.js +93 -13
- package/domain/state.js +5 -1
- package/domain/summary.js +5 -11
- package/domain/vocabulary.js +15 -33
- package/host/capability-registry.js +25 -9
- package/host/config.js +40 -3
- package/host/doctor.js +20 -8
- package/host/preset-survey-sandbox.js +19 -0
- package/host/preset-survey.js +18 -0
- package/host/presets.js +17 -11
- package/host/tool-registry.js +23 -13
- package/locale/en.json +16 -1
- package/locale/preset-survey/en.json +6 -0
- package/locale/preset-survey/zh.json +6 -0
- package/locale/preset-survey-sandbox/en.json +6 -0
- package/locale/preset-survey-sandbox/zh.json +6 -0
- package/locale/zh.json +16 -1
- package/package.json +19 -2
- package/ports/state.js +8 -1
- package/scripts/architecture-waivers.json +3 -3
- package/scripts/check-components.mjs +120 -6
- package/scripts/check-docs.mjs +1 -1
- package/scripts/check-pack.mjs +5 -0
- package/scripts/component-surface.json +83 -29
- package/scripts/generate-capability-locale.mjs +5 -0
- package/scripts/generate-shapes-reference.mjs +171 -0
- package/scripts/mount-smoke/surface.mjs +3 -0
- package/scripts/pack-manifest.txt +22 -0
- package/scripts/shape-audit.mjs +361 -0
- package/scripts/surface-baseline.json +75 -4
- package/scripts/types-coverage.mjs +7 -0
- package/survey-sandbox.js +48 -0
- package/survey.js +40 -0
- package/tools/complete.js +5 -5
- package/tools/diff.js +5 -3
- package/tools/plan.js +1 -1
- package/tools/preflight.js +3 -2
- package/tools/review.js +1 -1
- package/tools/spawn.js +48 -50
- package/tools/status.js +8 -1
- package/tools/test.js +2 -1
- package/types/index.d.ts +76 -5
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# ADR 0006: Mission shapes
|
|
2
|
+
|
|
3
|
+
- Status: accepted
|
|
4
|
+
- Date: 2026-11-10
|
|
5
|
+
- Ref: #148, #149
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
`kind` was a column on one mission record, but it carried three contracts: a build
|
|
10
|
+
owns a branch and a worktree, is reviewed at its tip, and merges; a survey only
|
|
11
|
+
reads one commit. The state document could describe both shapes at once, so the
|
|
12
|
+
generic layers kept asking `kind === 'survey'` and the answer leaked into
|
|
13
|
+
completion (`commit >= 1`), the merge gate (`read-only-survey`), archive and
|
|
14
|
+
reanchor terminality, planning's open-work count, and provisioning. A survey
|
|
15
|
+
that completed could not be archived, blocked reanchor forever, counted as open
|
|
16
|
+
work, and paid for a full checkout while consuming the live-worker budget.
|
|
17
|
+
|
|
18
|
+
## Decision
|
|
19
|
+
|
|
20
|
+
`domain/shapes/` is the one registry. Each shape module owns its statuses,
|
|
21
|
+
transitions, terminality, merge/rebase permission, provisioning, plan fields,
|
|
22
|
+
completion invariants, dependency satisfaction, briefing and summary projection.
|
|
23
|
+
`domain/shapes/index.js` is the only way a generic layer resolves a shape:
|
|
24
|
+
`shapeFor(mission.kind)`, `isTerminal(kind, status)`, `canTransition(kind, from,
|
|
25
|
+
to)`, and `assertActionSupported(mission, action)` for merge/rebase/preflight/diff.
|
|
26
|
+
The global `TERMINAL_STATUSES`/`TRANSITIONS` vocabulary and the one-argument
|
|
27
|
+
`isTerminal`/`canTransition` entry points are deleted.
|
|
28
|
+
|
|
29
|
+
Schema version 4 is a TypeBox union: a build mission keeps its worktree/branch and
|
|
30
|
+
merge fields; a survey carries `readCommit` and `readDirty` and may not carry
|
|
31
|
+
`worktree`, `branch`, `baseCommit`, `mergedCommit` or `mergedAt`. `merged` is not
|
|
32
|
+
a survey status. Migration v3→v4 is pure, one-way and idempotent: it stamps every
|
|
33
|
+
mission with a shape, reads a legacy survey's commit from
|
|
34
|
+
`worktree.baseCommit ?? baseCommit`, drops its branch fields, and maps a legacy
|
|
35
|
+
`merged` survey to `completed`.
|
|
36
|
+
|
|
37
|
+
Read-only is structural, not a gate: `tower_plan({kind:'survey'})` refuses while
|
|
38
|
+
the `tower-survey` component row is off; a survey spawn uses the shared checkout
|
|
39
|
+
and the `tower-survey` preset allow-list; `tower_complete` checks that HEAD still
|
|
40
|
+
equals `readCommit`, that the tracked-status set matches the set captured at plan
|
|
41
|
+
time, and that zero commits were made.
|
|
42
|
+
|
|
43
|
+
`scripts/shape-audit.mjs` enforces the decision: no kind or role literal
|
|
44
|
+
comparison outside `domain/shapes/` and `domain/roles.js`, no global status
|
|
45
|
+
vocabulary, no one-argument entry points. Waivers are justified data in
|
|
46
|
+
`scripts/architecture-waivers.json` under `shapeWaivers`; an unjustified or stale
|
|
47
|
+
waiver fails. `docs/reference/shapes.md` is generated from the registry.
|
|
48
|
+
|
|
49
|
+
## Surface budget
|
|
50
|
+
|
|
51
|
+
The shape redesign grows the config surface by exactly one key, recorded in
|
|
52
|
+
`scripts/surface-baseline.json`:
|
|
53
|
+
|
|
54
|
+
- `spawn.surveyModel` (tuning): an optional independent model route, mirroring
|
|
55
|
+
`spawn.workerModel`/`spawn.reviewerModel`.
|
|
56
|
+
|
|
57
|
+
An earlier cut of this work also added `spawn.surveyPreset` (core) and
|
|
58
|
+
`survey.sandbox` (tuning) in a new `survey` section. Review contracted the
|
|
59
|
+
surface back before release (config 57 → 55, sections 15 → 14): a shape owns its
|
|
60
|
+
preset (`SHAPES.survey.spawn.defaultPreset`), so a `spawn.surveyPreset` key could
|
|
61
|
+
only disagree with the registry; and `survey.sandbox` advertised a detached
|
|
62
|
+
read-only checkout that W1 does not implement. Neither is a tuning knob a user
|
|
63
|
+
should see; the completion invariants (HEAD, tracked-status fingerprint, zero
|
|
64
|
+
commits) are the read-only guarantee, not a placement option.
|
|
65
|
+
|
|
66
|
+
The `tower-survey` row is a visible optional component (components 11 → 12): off
|
|
67
|
+
means surveys cannot be planned (an actionable code) and no survey agent can be
|
|
68
|
+
dispatched; build missions are unaffected. It is a declarative preset row: the
|
|
69
|
+
host resolves its availability through the async `agentPresets` probe, not the
|
|
70
|
+
process-local code registry.
|
|
71
|
+
|
|
72
|
+
## Rejected alternatives
|
|
73
|
+
|
|
74
|
+
- Keep `kind` on one record and guard each leak with another `kind === 'survey'`
|
|
75
|
+
check. Rejected: that is exactly the census in #149; the state document would
|
|
76
|
+
still be able to describe both shapes and every new layer would re-learn the
|
|
77
|
+
rule.
|
|
78
|
+
- Add a `shape: 'build'|'survey'` flag selecting an old and a new code path during
|
|
79
|
+
a transition period. Rejected: a dual-shape path lets a document carry an
|
|
80
|
+
impossible combination (a survey with a worktree, a merged survey) and makes the
|
|
81
|
+
migration untestable.
|
|
82
|
+
- Keep `commit >= 1` and make a survey commit an empty commit. Rejected: a survey
|
|
83
|
+
is a read, so the empty commit records nothing and hides that HEAD moved.
|
|
84
|
+
- Enforce read-only at merge time (`read-only-survey` in the gate). Rejected: the
|
|
85
|
+
write already happened by then; read-only must be structural (preset allow-list)
|
|
86
|
+
and checked as a completion invariant.
|
|
87
|
+
- Give a survey its own branch so the existing lifecycle "just works". Rejected:
|
|
88
|
+
it reintroduces provisioning cost, branch ownership, stale-base and merge
|
|
89
|
+
vocabulary for a kind that never merges.
|
|
90
|
+
|
|
91
|
+
## Consequences
|
|
92
|
+
|
|
93
|
+
- `kind` is now a closed vocabulary with one registry; adding a kind means adding
|
|
94
|
+
a shape module and regenerating `docs/reference/shapes.md`, not editing generic
|
|
95
|
+
layers.
|
|
96
|
+
- Older v3 documents migrate on load; a legacy survey's `worktree` is removed by
|
|
97
|
+
a bounded-retry teardown and its completion with commits is accepted as history.
|
|
98
|
+
- A completed survey is terminal, archivable, no longer open work, and no longer
|
|
99
|
+
blocks reanchor or the live-worker budget.
|
|
100
|
+
- Surveys can be planned only while the `tower-survey` row is on, and they cannot
|
|
101
|
+
be merged, rebased, preflighted or diffed: each refuses with `invalid-transition`
|
|
102
|
+
and an actionable next step.
|
|
103
|
+
- The shape audit is part of `npm run check`; a new kind or role comparison in a
|
|
104
|
+
generic layer fails CI unless a justified waiver is recorded.
|
|
105
|
+
|
|
106
|
+
## Addendum: W2 detached sandbox and dependency semantics (#148)
|
|
107
|
+
|
|
108
|
+
W1 implemented the shared-checkout survey only. W2 adds the placement it left
|
|
109
|
+
out, without changing the default or the shared path:
|
|
110
|
+
|
|
111
|
+
- `survey.sandbox` (`none`|`detached`, default `none`) is tuning, recorded in
|
|
112
|
+
`scripts/surface-baseline.json` (config keys 55 → 56, sections 14 → 15). The
|
|
113
|
+
shape resolves the placement (`resolveProvisioning`) and the preset
|
|
114
|
+
(`resolvePreset`); no generic layer reads the key or compares a kind.
|
|
115
|
+
`spawn.surveyPreset` stays removed because the preset follows the registry and
|
|
116
|
+
the resolved placement.
|
|
117
|
+
- `detached` provisions `git worktree add --detach <worktreeRoot>/<id>-sandbox
|
|
118
|
+
<readCommit>` with no branch and records `mission.sandbox`. It never records
|
|
119
|
+
`baseCommit`, never participates in stale-base, review or merge, and the
|
|
120
|
+
completion invariant is sandbox HEAD === `readCommit` plus zero commits.
|
|
121
|
+
Expected write/shell side effects in the disposable checkout are ignored;
|
|
122
|
+
the shared placement keeps the tracked-status fingerprint invariant.
|
|
123
|
+
- The sandbox preset (`tower-survey-sandbox`) allows read/write/edit/pwsh plus
|
|
124
|
+
the mission tools and still denies spawn/merge/rebase/review/diff. Both survey
|
|
125
|
+
presets are internal children of the one visible `tower-survey` group row, so
|
|
126
|
+
the component count does not move (12) and turning the capability off removes
|
|
127
|
+
both.
|
|
128
|
+
- Cleanup is advisory: completion, abandon and `tower_teardown` remove the
|
|
129
|
+
sandbox with a bounded retry. A failure keeps `mission.sandbox`, records
|
|
130
|
+
`sandboxCleanupError` and names the `git worktree remove --force <path>`
|
|
131
|
+
remedy in the activity log; the transition itself is not rolled back. Archival
|
|
132
|
+
defers a mission whose sandbox is still present instead of moving the only
|
|
133
|
+
record of the path out of hot state.
|
|
134
|
+
- `gates.requireFreshSurvey` (default `false`) controls dependency freshness. A
|
|
135
|
+
build depending on a survey whose `readCommit` is not ancestor-or-equal of the
|
|
136
|
+
build base gets a `survey-stale` warning in plan/preflight with
|
|
137
|
+
`{code,missionId,depId,readCommit,baseCommit,message}`; only when the gate is
|
|
138
|
+
`true` does the same code block the merge. The ancestor check is the existing
|
|
139
|
+
`isAncestor` (`git merge-base --is-ancestor`, which is true for equal commits).
|
|
140
|
+
- A build briefing carries each survey dependency's recorded `result`
|
|
141
|
+
(`paths`/`symbols`/`plan`/`notes`) with bounded formatting, so the dependent
|
|
142
|
+
worker sees the evidence rather than only the terminal status. `result`
|
|
143
|
+
remains survey-only at completion.
|
|
144
|
+
- Placement is decided at provisioning time, not at completion time: a recorded
|
|
145
|
+
`mission.sandbox` wins over current `survey.sandbox` config for provisioning,
|
|
146
|
+
preset and completion invariants. Flipping the key after a sandbox was created
|
|
147
|
+
therefore cannot skip the sandbox HEAD/zero-commit checks or strand the
|
|
148
|
+
disposable checkout, and a survey that never got a sandbox keeps the shared
|
|
149
|
+
invariants.
|
|
150
|
+
- The archive index keeps a survey's `readCommit` and bounded `result`, so an
|
|
151
|
+
archived survey dependency still carries its evidence into a build briefing
|
|
152
|
+
and `gates.requireFreshSurvey` cannot fail open after archiving (#73).
|
|
153
|
+
|
|
154
|
+
## Addendum: W3 review, batch, leases and paused-vs-blocked (#149)
|
|
155
|
+
|
|
156
|
+
W3 audited four state dimensions that are not mission kinds. None became a
|
|
157
|
+
shape: each has one owning module, and each verdict names a fitness rule that
|
|
158
|
+
`npm run shape:audit` enforces (generated into `docs/reference/shapes.md`).
|
|
159
|
+
|
|
160
|
+
- **Review — commit-stamped evidence (`app/review.js`, `app/reviewCheckout.js`,
|
|
161
|
+
`domain/missions/review.js`).** A verdict is evidence about a commit, not about
|
|
162
|
+
a mutable worktree. `tower_spawn` provisions a disposable detached checkout of
|
|
163
|
+
the mission branch tip for the reviewer and records `reviewCommit` /
|
|
164
|
+
`reviewCheckout` on the roster row. `tower_review` stamps `reviewedCommit` with
|
|
165
|
+
that recorded commit, refuses if the checkout HEAD moved, and the merge gate
|
|
166
|
+
compares the stamp to the current branch tip with the existing `tip-moved`
|
|
167
|
+
blocker. `tower_diff` shows a reviewer the same recorded commit. The old
|
|
168
|
+
after-the-fact "reviewer modified the branch" check (`assertReviewerSpawnTip`)
|
|
169
|
+
is deleted: a stale worker write can no longer affect what a review decided.
|
|
170
|
+
The checkout is removed when the verdict is recorded and retried by
|
|
171
|
+
`tower_teardown`. A tower break-glass verdict stamps the current branch tip
|
|
172
|
+
because there is no checkout. Fitness rule: `review-tip-read`.
|
|
173
|
+
- **Batch — correctly generic second lifecycle (`domain/batch.js`,
|
|
174
|
+
`domain/collab/batch.js`, `app/batch.js`).** A batch is finite evidence over
|
|
175
|
+
missions, not a mission kind, so it stays generic — but its second state
|
|
176
|
+
machine is now owned by one module: `BATCH_STATUS`, `BATCH_TRANSITIONS`
|
|
177
|
+
(`open → closed | abandoned`), `canTransitionBatch`, `isBatchTerminal` and
|
|
178
|
+
`normalizeBatchStatus` (legacy `created`/`active`/`pending` read as `open`).
|
|
179
|
+
`createBatch`/`closeBatch`/`abandonBatch` share one guarded transition and
|
|
180
|
+
state semantics validates the stored status, so an unknown or invented batch
|
|
181
|
+
status fails as state corruption. Receipt verification is unchanged. Fitness
|
|
182
|
+
rule: `batch-status-literal`.
|
|
183
|
+
- **Leases — advisory coordination, never authority (`adapters/leases.js`,
|
|
184
|
+
`app/spawn-prechecks.js`).** Lease acquisition is not OS-enforced exclusivity,
|
|
185
|
+
and the docs say so. There is exactly one enforcement point: the fail-closed
|
|
186
|
+
spawn admission when `spawn.requireLease` names a lease, which refuses a
|
|
187
|
+
missing lease and a lease held by another actor with an actionable remedy. The
|
|
188
|
+
prior contract deadlocked: an expired lease held by a crashed process could
|
|
189
|
+
never be released because a token mismatch was refused even with `force`. Now
|
|
190
|
+
`force: true` releases an **expired** lease (explicit crash recovery) while a
|
|
191
|
+
live lease still refuses a foreign token, and `acquire` never takes over
|
|
192
|
+
automatically. Fitness rule: `lease-ledger`.
|
|
193
|
+
- **Paused vs blocked — two distinct stopped semantics
|
|
194
|
+
(`domain/missions/lifecycle.js`, `domain/state/semantics.js`).** `blocked` is
|
|
195
|
+
derived machine state: it means "open blockers exist". `paused` is a deliberate
|
|
196
|
+
stop with a `pauseCause` (`user`|`failure`|`resource`|`dependency`). They never
|
|
197
|
+
coexist: a recorded blocker clears a pause and moves the mission to `blocked`;
|
|
198
|
+
`clear_blockers` clears them and resumes the mission (to `active`, or to
|
|
199
|
+
`completed` in the same patch); pausing or completing is refused while blockers
|
|
200
|
+
are open; `status: "blocked"` is refused and machine writers go through
|
|
201
|
+
`markBlocked(mission, text)` (merge/rebase conflicts use it). The state
|
|
202
|
+
invariant `status === "blocked" ⇔ open blockers exist` (for non-terminal
|
|
203
|
+
missions) is checked on every load/validation, and migration v4→v5 normalizes
|
|
204
|
+
recorded states: a blocked mission without a blocker gets an explicit legacy
|
|
205
|
+
blocker, blockers under another non-terminal status become `blocked`, and a
|
|
206
|
+
superseded `pauseCause` is removed. It is not a per-kind rule: both shapes
|
|
207
|
+
declare `blocked`/`paused` in their own transition tables. Fitness rule:
|
|
208
|
+
`blocked-status-write`.
|
|
209
|
+
- Because the stopped-status invariant is new, the persisted schema version is
|
|
210
|
+
now **5** (`SCHEMA_VERSION = 5` in `domain/mission.js`); the v4→v5 migration is
|
|
211
|
+
pure and idempotent like its predecessors.
|
|
212
|
+
- The audit grew four rules (`review-tip-read`, `batch-status-literal`,
|
|
213
|
+
`lease-ledger`, `blocked-status-write`) with mutation tests proving each rule
|
|
214
|
+
fails on a seeded violation. `scripts/shape-audit.mjs` exports `RULES`, and
|
|
215
|
+
`scripts/generate-shapes-reference.mjs` refuses to generate if a verdict names
|
|
216
|
+
a rule the audit does not run.
|
|
217
|
+
|
|
218
|
+
## Addendum: W4 evidence, legacy, scope and role-vs-kind (#149)
|
|
219
|
+
|
|
220
|
+
W4 audited the four remaining dimensions. Each had no single owner; each got a
|
|
221
|
+
written verdict, one owning module, and a fitness rule that `npm run shape:audit`
|
|
222
|
+
enforces (generated into `docs/reference/shapes.md`). No dimension became a
|
|
223
|
+
per-kind conditional and no second shape table was added.
|
|
224
|
+
|
|
225
|
+
- **Test receipts / evidence — redesigned to one reader (`domain/evidence.js`).**
|
|
226
|
+
The same question ("did tests run?") previously had four answers:
|
|
227
|
+
`mission.completionReport.checks`, a synthetic `tests` check that
|
|
228
|
+
`tower_complete` copied from discovery via `tests.autoChecks`,
|
|
229
|
+
`mission.testRuns`, and a separate `gates.requireTests` filter that re-read
|
|
230
|
+
receipts. `domain/evidence.js` now owns the normalized record
|
|
231
|
+
`{names, name, source, ok, command, exitCode, evidence, treeTip, entry}` with
|
|
232
|
+
sources `completionReport` | `testRuns` | `input`, aliases `tests`/`tower_test`
|
|
233
|
+
carried in `names`, and one ordering (completion checks first, then newest
|
|
234
|
+
receipts, then caller checks scoped to the mission) applied by `findEvidence` —
|
|
235
|
+
precedence and ordering are reader decisions, not fields on the record.
|
|
236
|
+
`evidenceEntries`, `findEvidence`, `passingDiscovery`,
|
|
237
|
+
`hasCheckEvidence` and `hasTestReceipts` are the only readers; the shapes
|
|
238
|
+
declare the contract (`build.evidence = {completionChecks: true, testReceipts:
|
|
239
|
+
"tip-bound"}`, `survey.evidence = {completionChecks: true, testReceipts:
|
|
240
|
+
false}`) because only a shape with a branch has a tip to bind a receipt to.
|
|
241
|
+
`gates.requireTests` uses `passingDiscovery`; completion blockers use
|
|
242
|
+
`hasCheckEvidence`; `tower_batch` validation/summary use `findEvidence`.
|
|
243
|
+
`tower_complete` no longer copies a `tests` check into the report and
|
|
244
|
+
`tower_test` returns the receipt only, so the config key `tests.autoChecks`
|
|
245
|
+
disappears (config surface 57 → 56, recorded in `scripts/surface-baseline.json`
|
|
246
|
+
and `docs/reference/surface-budget.md`). Fitness rule:
|
|
247
|
+
`evidence-single-read`, owned by `domain/evidence.js`; mutation tests seed a
|
|
248
|
+
completion-check read, a `mission.testRuns` read and prove the owner module is
|
|
249
|
+
exempt.
|
|
250
|
+
- **Legacy import — redesigned to a strict adapter contract
|
|
251
|
+
(`adapters/legacy.js`).** Import was approximate in four ways: the format was
|
|
252
|
+
inferred from the first mission id (mixed namespaces were only caught
|
|
253
|
+
incidentally), an absent `kind` was silently stamped `build`, a Tower Codex
|
|
254
|
+
`retired` status leaked into the mission summary as if it were a DSH status,
|
|
255
|
+
and batch exclusion read several hand-written snapshot shapes
|
|
256
|
+
(`missions`/`snapshot.missions`/`payload.missions`/`pausedIds`). The adapter now
|
|
257
|
+
exports `LEGACY_FORMATS`: one declared entry per accepted format (id pattern,
|
|
258
|
+
id hint, input statuses, `statusRemap`, `baseBranchField`,
|
|
259
|
+
`requiresBaseBranch`). Detection requires exactly one id namespace (a declared
|
|
260
|
+
`payload.format` is authoritative and validated against the ids); a Codex
|
|
261
|
+
`retired` status is remapped to `abandoned` and reported in `remapped`; an
|
|
262
|
+
absent kind is reported in `unresolved` instead of silently invented; mission
|
|
263
|
+
fields the summary does not carry are reported in `dropped` with the reason
|
|
264
|
+
that the exact bytes remain in the stored snapshot. The persisted snapshot
|
|
265
|
+
record gains `unresolved`, `droppedFields` and `remapped`, so the honesty
|
|
266
|
+
survives a restart. `domain/batch.js` reads only the canonical
|
|
267
|
+
`pausedMissionIds`. The import remains read-only, byte-exact,
|
|
268
|
+
hash-addressed and idempotent: it never writes `state.json`, never activates,
|
|
269
|
+
adopts or resumes a mission, and a paused mission stays paused. Fitness rule:
|
|
270
|
+
`legacy-vocabulary`, owned by `adapters/legacy.js`; mutation tests prove a
|
|
271
|
+
second `tower-codex`/`pausedIds` reader fails outside the adapter.
|
|
272
|
+
- **Scope ownership — redesigned to `shape.reach` (`domain/scope.js` +
|
|
273
|
+
shapes).** The shape flag `ownsScope` lived beside three enforcers (the
|
|
274
|
+
integration gate, the planner's auto-dependencies, and lifecycle scope
|
|
275
|
+
widening), each of which read `mission.scope` itself. The shape now declares
|
|
276
|
+
`reach = { ownsPaths, fence: "scope" | "read-only" }`: a build owns paths and
|
|
277
|
+
fences changed files against its scope; a survey reaches nothing and is fenced
|
|
278
|
+
read-only. `domain/scope.js` exports one `scopeFenceBlocker({ mission, reach,
|
|
279
|
+
changedFiles, caseInsensitive })`; `domain/integration/gate.js` derives the
|
|
280
|
+
reach from `shapeFor(mission.kind)` and re-exports the existing
|
|
281
|
+
`outOfScopeBlocker` shape; `domain/missions/plan.js`,
|
|
282
|
+
`domain/planning.js` and `domain/missions/lifecycle.js` derive
|
|
283
|
+
`reach.ownsPaths`. `ownsScope` is deleted. Fitness rule: `scope-reach`, owned
|
|
284
|
+
by `domain/scope.js`; mutation tests seed an `ownsScope` reader and a direct
|
|
285
|
+
`fileInScope` call.
|
|
286
|
+
- **Role vs kind — redesigned to an orthogonal role contract
|
|
287
|
+
(`domain/roles.js`).** Spawn resolved a reviewer by combining
|
|
288
|
+
`isReviewerRole(role)` with `shape.fields.readCommit`/`shape.canMerge`, and
|
|
289
|
+
presets, prechecks, placement, briefings and the roster each re-derived role
|
|
290
|
+
facts independently. `domain/roles.js` now owns `ROLE_CONTRACT`, one entry per
|
|
291
|
+
actor role with `preset`, `model`, `placement`, `briefing`, `tracksMission`,
|
|
292
|
+
`countsTowardBudget`, `spawnDiscovery` and `requires` (review, canMerge,
|
|
293
|
+
worktree, status set). `resolveRoleSpawn` returns the preset/model route,
|
|
294
|
+
`roleMissionBlockers` returns ordered prerequisites, and
|
|
295
|
+
`roleCountsTowardBudget`, `roleTracksMission`, `rolePlacement`,
|
|
296
|
+
`roleBriefingKind` and `rolePresetConfig` replace the scattered checks. The
|
|
297
|
+
shape supplies only facts (`canMerge`, `fields.worktree`, `spawn.model`);
|
|
298
|
+
`tools/spawn.js`, `app/spawn-prechecks.js`, `app/spawn-support.js`,
|
|
299
|
+
`domain/roster.js` and `host/presets.js` call the role API and never combine a
|
|
300
|
+
role predicate with a shape decision. Fitness rule: `role-shape-conflation`,
|
|
301
|
+
owned by `domain/roles.js`; mutation tests seed a role∧shape conditional and
|
|
302
|
+
prove the owner module is exempt.
|
|
303
|
+
- The audit now runs 12 rules. The generated reference's `Dimension inventory
|
|
304
|
+
(#149)` lists every audited dimension with its verdict, owner module and
|
|
305
|
+
fitness rule; `scripts/generate-shapes-reference.mjs` still refuses to emit a
|
|
306
|
+
rule id the audit does not run.
|
|
@@ -12,9 +12,14 @@ escape keep working.
|
|
|
12
12
|
|
|
13
13
|
[Principle 11](../../PRINCIPLES.md) and
|
|
14
14
|
[ADR 0005](../adr/0005-component-surface.md) say a component may exist only for
|
|
15
|
-
a capability whose off state leaves the rest intact, and that the host,
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
a capability whose off state leaves the rest intact, and that the host, the
|
|
16
|
+
health contract, the recovery escape and the merge gates are never switches.
|
|
17
|
+
The review policy is not one of them: `review.mode` is a single tri-state
|
|
18
|
+
control (`off`, `optional` or `required`) on the plugin-page configuration
|
|
19
|
+
form, not a component row and never one row per mode, and only the review gate
|
|
20
|
+
it turns on in `required` mode — the clean, tip-bound evidence and the
|
|
21
|
+
exemption path — is non-disableable. Which keys are allowed to be switches is a
|
|
22
|
+
table lookup in
|
|
18
23
|
[component-surface.md](../reference/component-surface.md), generated from
|
|
19
24
|
`scripts/component-surface.json`; the component count is baselined and checked by
|
|
20
25
|
`npm run docs:check:components`. That gate also refuses a `tools.expose*` route
|
|
@@ -221,12 +226,14 @@ planned ──spawn worker──▶ active ──tower_complete──▶ complet
|
|
|
221
226
|
│ │ ▲ │
|
|
222
227
|
│ │ └──── review round not clean ────┐
|
|
223
228
|
└── abandon ──▶ abandoned│ │
|
|
224
|
-
├── blocker ──▶ blocked ──
|
|
229
|
+
├── blocker ──▶ blocked ──clear_blockers──▶ active
|
|
225
230
|
└── pause ─────▶ paused ──active──▶ ...
|
|
226
231
|
```
|
|
227
232
|
|
|
228
|
-
`merged` and `abandoned` are terminal. `
|
|
229
|
-
set
|
|
233
|
+
`merged` and `abandoned` are terminal. `blocked` is derived: it holds exactly while the mission has an open
|
|
234
|
+
blocker, `clear_blockers` resumes it to `active`, and `tower_mission` refuses to set `status: "blocked"` directly.
|
|
235
|
+
`paused` is a deliberate stop (`pause_cause`); a blocker supersedes a pause. `merged` is only set by
|
|
236
|
+
`tower_merge`/reconciliation.
|
|
230
237
|
|
|
231
238
|
## Gate pipeline (`tower_preflight`)
|
|
232
239
|
|
|
@@ -236,9 +243,10 @@ set by `tower_merge`/reconciliation.
|
|
|
236
243
|
3. Survey missions must not change files; build missions must stay inside `scope`.
|
|
237
244
|
4. The main checkout must be clean for the files this merge would overwrite and on the base branch.
|
|
238
245
|
5. Dependency missions must be merged/abandoned, except a completed survey dependency which is its terminal success state (#107).
|
|
239
|
-
6. If `review.
|
|
240
|
-
clean, approve merge, and be bound to the tip (unless a clean
|
|
241
|
-
|
|
246
|
+
6. If the review policy is `required` (`review.mode:required`; the default is `optional`), a review
|
|
247
|
+
must exist, belong to this mission, be clean, approve merge, and be bound to the tip (unless a clean
|
|
248
|
+
rebase waived it). With `off`, review is intentionally unavailable and the gate ignores it. No
|
|
249
|
+
worker may be running.
|
|
242
250
|
|
|
243
251
|
## Merge
|
|
244
252
|
|
|
@@ -255,7 +263,7 @@ After the command, regardless of exit status, it inspects `HEAD`:
|
|
|
255
263
|
| Path | Contents |
|
|
256
264
|
|---|---|
|
|
257
265
|
| `<git-common-dir>/dsh-tower/state.json` | active missions, roster, reviews, findings, messages, reviews sequence |
|
|
258
|
-
| `<git-common-dir>/dsh-tower/archive/index.json` | compact terminal-mission index (ids, status, branch, timestamps) |
|
|
266
|
+
| `<git-common-dir>/dsh-tower/archive/index.json` | compact terminal-mission index (ids, status, branch, timestamps, survey `readCommit` and bounded `result`) |
|
|
259
267
|
| `<git-common-dir>/dsh-tower/archive/<year>.json` | full archived missions plus their linked reviews/findings |
|
|
260
268
|
| `<git-common-dir>/dsh-tower/activity.jsonl` | append-only activity |
|
|
261
269
|
| `<git-common-dir>/dsh-tower/state.lock` | single-writer state lock (heartbeat reclaim; a recycled PID cannot pin it) |
|
|
@@ -23,8 +23,9 @@ Wait for the worker's final report (delivered to this session) or check `tower_r
|
|
|
23
23
|
creates `src/greet.txt`, commits on `tower/m1-add-greeting`, marks both tasks done, and calls
|
|
24
24
|
`tower_complete`.
|
|
25
25
|
|
|
26
|
-
This example follows the strict workflow:
|
|
27
|
-
repository config
|
|
26
|
+
This example follows the strict workflow: set the review policy to `required` (`review.mode:required`
|
|
27
|
+
in the profile or repository config, or the one tri-state control on the Plugins page) before the
|
|
28
|
+
review step; the default is `optional`. Spawn the reviewer:
|
|
28
29
|
|
|
29
30
|
```json
|
|
30
31
|
{"repoRoot":"D:\\work\\demo","missionId":"M1","role":"reviewer"}
|
package/docs/faq.md
CHANGED
|
@@ -14,9 +14,9 @@ phase instead—use Workflow/Team to analyze and decide, then start Tower to exe
|
|
|
14
14
|
review) the repository change. See the [full guide](guide/scenarios-and-philosophy.md#4-how-tower-differs-from-other-dsh-workflow-patterns).
|
|
15
15
|
|
|
16
16
|
**Why did `tower_preflight` fail with `no-review` right after the worker finished?**
|
|
17
|
-
That happens only when `review.
|
|
18
|
-
independent review. Spawn a reviewer; after a clean review the gate turns green. With the
|
|
19
|
-
`review.
|
|
17
|
+
That happens only when the review policy is `required` (`review.mode:required`): a completed mission
|
|
18
|
+
awaits an independent review. Spawn a reviewer; after a clean review the gate turns green. With the
|
|
19
|
+
default `review.mode:optional`, review is optional and no `no-review` blocker is produced.
|
|
20
20
|
|
|
21
21
|
**Do I need to poll the worker?**
|
|
22
22
|
No. The worker's final report is delivered to this session; `tower_roster` shows live activity. Polling
|
|
@@ -40,10 +40,12 @@ Correct: the branch is already contained in the base. Call `tower_merge` to reco
|
|
|
40
40
|
instead of rebasing.
|
|
41
41
|
|
|
42
42
|
**Can I require independent review?**
|
|
43
|
-
Yes. Set
|
|
44
|
-
|
|
45
|
-
reason + `gates.allowReviewExemption:true` is the narrower
|
|
46
|
-
verdicts
|
|
43
|
+
Yes. Set the review policy to `required` — the one tri-state control on the Plugins page, or
|
|
44
|
+
`review.mode:required` in the profile or repository config (the default is `optional`). When required,
|
|
45
|
+
mission-level `reviewPolicy:"none"` + a reason + `gates.allowReviewExemption:true` is the narrower
|
|
46
|
+
exemption. `tower_review` works and records verdicts in `optional` mode too. The old boolean toggle is
|
|
47
|
+
a deprecated one-release alias; the mapping is in
|
|
48
|
+
[install, upgrade and migration](guide/install-upgrade-migration.md).
|
|
47
49
|
|
|
48
50
|
**Can a reviewer edit files?**
|
|
49
51
|
No. Reviewers use the read-only `tower-reviewer` preset (no write/edit/pwsh); a branch changed after the
|
|
@@ -29,6 +29,55 @@ The Host plugin loads at DSH startup: **restart DSH after install or update**.
|
|
|
29
29
|
|
|
30
30
|
## Migration notes
|
|
31
31
|
|
|
32
|
+
- **2.6.0 → 2.7.0 (#148/#149) — mission shapes, survey tasks, and state schema v5**: a completed
|
|
33
|
+
survey is a read of a commit, not a write of a branch. `tower_plan({kind:"survey"})` records
|
|
34
|
+
`readCommit` and `readDirty`, creates no branch or worktree, and completes only while HEAD and the
|
|
35
|
+
tracked-file status still match that read. The optional `survey.sandbox: "detached"` mode gives a
|
|
36
|
+
writable inspection a disposable worktree pinned to the same commit. `tower-survey` is an optional
|
|
37
|
+
component row; survey presets are read-only by default, and surveys do not count against
|
|
38
|
+
`spawn.maxLiveWorkers`.
|
|
39
|
+
- **State migration is automatic and one-way.** The first write after upgrading migrates
|
|
40
|
+
`state.json` from v3 to v5 in one step, writes `state.json.v3.bak` first, advances `revision`
|
|
41
|
+
once, and logs `state.migrated` with steps v3→v4 and v4→v5. Legacy survey rows gain
|
|
42
|
+
`readCommit`/`readDirty` (anchored at `state.base` when the old row never started), and build
|
|
43
|
+
rows are stamped `kind:"build"`. Read-only loads migrate in memory without rewriting the file.
|
|
44
|
+
A newer document is still refused with `state-version-unsupported` and never downgraded.
|
|
45
|
+
- **Config key count 57 → 56.** `tests.autoChecks` is gone; test evidence is now the
|
|
46
|
+
`mission.testRuns` receipt read by `domain/evidence.js`. A config that still writes the key is
|
|
47
|
+
rejected as `config-unknown-key` with this note; there is no replacement switch.
|
|
48
|
+
- **`blocked` is derived.** Write `status:"blocked"` through `tower_mission` is refused; use
|
|
49
|
+
`blocker` to report and `clear_blockers` to resolve. Batch statuses are canonical
|
|
50
|
+
`open → closed|abandoned`; an expired foreign lease requires explicit `force` to recover.
|
|
51
|
+
- **Survey actions are explicit.** `tower_merge`, `tower_rebase`, `tower_preflight`, and
|
|
52
|
+
`tower_diff` refuse surveys with `invalid-transition`; a completed survey is terminal for
|
|
53
|
+
planning, archive, dependency satisfaction, and reanchor. `tower_complete` accepts an optional
|
|
54
|
+
structured `result` (`paths`, `symbols`, `notes`, `plan`) for surveys only.
|
|
55
|
+
- Reviewers are now spawned into a detached checkout bound to the recorded commit; a verdict is
|
|
56
|
+
tied to that commit, and teardown cleans the checkout. Existing build workflows are unchanged.
|
|
57
|
+
- No npm dist-tag or review-policy change is required. Restart DSH after upgrading.
|
|
58
|
+
- **2.5.0 → 2.6.0 (#M39/#M40) — review modes replace the boolean**: the review policy is one
|
|
59
|
+
tri-state control on the Plugins page in the `dsh-tower` configuration section, and the config key
|
|
60
|
+
is `review.mode`: `off`, `optional` (the default) or `required`.
|
|
61
|
+
- `off` is supported and documented: `tower_review` is not registered, reviewer spawn is refused,
|
|
62
|
+
and the merge gate ignores review.
|
|
63
|
+
- `optional` keeps `tower_review` but never blocks a merge.
|
|
64
|
+
- `required` keeps the merge gate: a completed mission needs a clean review bound to the branch tip
|
|
65
|
+
(the existing per-mission exemption still applies).
|
|
66
|
+
`review.requireReview` is a deprecated one-release alias: `true` resolves to `required`, `false`
|
|
67
|
+
to `optional`, and leaving it unset means `optional`. A `true` next to an explicit non-`required`
|
|
68
|
+
mode is rejected with `config-invalid`; `false` never overrides an explicit mode. Saving from the
|
|
69
|
+
plugin page always writes `review.mode` and normalizes the alias to `false`. The policy is not a
|
|
70
|
+
component row and there are no mutually exclusive review rows: `preset-tower-reviewer` remains an
|
|
71
|
+
optional capability the policy may leave unused. The row may be off while `review.mode` is `off`
|
|
72
|
+
or `optional`; when `review.mode` is `required` and the reviewer row is off, planning and mode
|
|
73
|
+
entry refuse the combination with an explanatory `preset-missing` error.
|
|
74
|
+
A profile that starts with the policy `off` never registered `tower_review`; switching to
|
|
75
|
+
`optional` or `required` changes the merge gate immediately, but the tool appears only after a DSH
|
|
76
|
+
restart (or a profile reload), and the plugin page states that after such a save.
|
|
77
|
+
Upgrade: replace `review.requireReview: true|false` with `review.mode: required|optional`; keep the
|
|
78
|
+
alias only if the same config is shared with older 2.5 installations, and remove it after this
|
|
79
|
+
release. No state migration.
|
|
80
|
+
|
|
32
81
|
- **2.4.2 → 2.5.0 (#131/#133/#135/#136/#142)**: three component-shaped config
|
|
33
82
|
keys are gone — `tests.enabled`, `docs.board`, and `archive.enabled`. The
|
|
34
83
|
`tower-tests`, `tower-board`, and `tower-archive` component rows are the only
|
|
@@ -70,14 +119,14 @@ The Host plugin loads at DSH startup: **restart DSH after install or update**.
|
|
|
70
119
|
now ships the mandatory `tower` host anchor (the Host plugin and the worker preset are its internal
|
|
71
120
|
sections) plus individually switchable top-level rows: `tower-skills`, `tower-batch`, `tower-import`,
|
|
72
121
|
`tower-leases`, `tower-tests`, `tower-hmr` (shipped off) and `preset-tower-reviewer` (conditional on
|
|
73
|
-
`review.
|
|
74
|
-
still carries them is now rejected as an unknown key (`config-unknown-key`) and the host stops
|
|
122
|
+
the review policy, today `review.mode`). The `tools` config group and `dev.hotReload` were removed: a
|
|
123
|
+
profile that still carries them is now rejected as an unknown key (`config-unknown-key`) and the host stops
|
|
75
124
|
mounting until they are deleted, so no silent no-op switch remains. Move each capability to its row
|
|
76
125
|
in the plugin page instead; `tower-hmr` ships off, so `tower_reload` and `/tower reload` are reported
|
|
77
126
|
as "disabled by component tower-hmr" until you enable it for a linked dev profile. `tower_doctor` and
|
|
78
127
|
`tower_recover` are registered for every configuration and need no key. The reviewer row may be off
|
|
79
|
-
|
|
80
|
-
combination with an explanatory error. No state migration is needed.
|
|
128
|
+
while the policy is `off` or `optional`; with `review.mode:required`, planning and mode entry refuse
|
|
129
|
+
the combination with an explanatory error. No state migration is needed.
|
|
81
130
|
|
|
82
131
|
- **2.0.4 → 2.1.0-beta.1**: no config migration; the state file migrates automatically (v1/v2 → v3) as
|
|
83
132
|
described above. This is a **pre-release** published on the
|
|
@@ -89,10 +138,12 @@ The Host plugin loads at DSH startup: **restart DSH after install or update**.
|
|
|
89
138
|
runs `npm publish --tag next`, and `latest` stays on 2.0.4 until a stable release.
|
|
90
139
|
- **2.0.3 → 2.0.4**: no state/config migration. This is a security-hardening and integrity release
|
|
91
140
|
(authorization, merge SHA binding, dirty-worktree completion, stale-lock recovery, limited test/git
|
|
92
|
-
process environment, `tower_recover`, UI 0.2.2). Review `review.
|
|
93
|
-
worker tool permissions against your
|
|
141
|
+
process environment, `tower_recover`, UI 0.2.2). Review the review policy (`review.mode`, or the
|
|
142
|
+
deprecated `review.requireReview` alias), `tests.admission` and worker tool permissions against your
|
|
143
|
+
threat model after upgrading.
|
|
94
144
|
- **2.0.1 → 2.0.3**: no state migration. Independent review became **optional by default** in 2.0.3
|
|
95
|
-
(`review.
|
|
145
|
+
(today's `review.mode:optional`); set `review.mode:required` to keep the old review-gated merges.
|
|
146
|
+
2.0.1 added
|
|
96
147
|
`tower_batch` `abandon`, accepted merged members in batch close, made doctor skip torn-down worktrees,
|
|
97
148
|
and fixed terminal-mission spawn/preflight edge cases. The optional `ui-bundle` 0.2.1 adds the
|
|
98
149
|
persistent Tower-mode chip and mode-aware denied-tool rows.
|
package/docs/guide/quickstart.md
CHANGED
|
@@ -33,15 +33,25 @@ session. Use `tower_roster` to observe activity; do not poll in a loop.
|
|
|
33
33
|
|
|
34
34
|
## 4. Optional: review
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
The review policy is one tri-state control — `off`, `optional` (the default) or `required` — on the
|
|
37
|
+
Plugins page in the `dsh-tower` configuration section, or `review.mode` in the profile/repository
|
|
38
|
+
config. It is one control, not a component row and not three rows. Set it to `required` for the strict
|
|
39
|
+
workflow, then:
|
|
38
40
|
|
|
39
41
|
```
|
|
40
42
|
tower_spawn repoRoot=D:\work\repo missionId=M1 role=reviewer
|
|
41
43
|
```
|
|
42
44
|
|
|
43
45
|
Reviewers use the read-only `tower-reviewer` preset. They inspect `tower_diff` and submit
|
|
44
|
-
`tower_review`; with `review.
|
|
46
|
+
`tower_review`; with `review.mode:required`, a `clean` + `merge` verdict is required to merge.
|
|
47
|
+
`optional` records reviews but never blocks a merge, and `off` is a supported mode that does not
|
|
48
|
+
register `tower_review`.
|
|
49
|
+
|
|
50
|
+
The old boolean review toggle is a one-release alias (`true` → `required`, `false` → `optional`;
|
|
51
|
+
unset → `optional`). Prefer `review.mode`. A profile that started with
|
|
52
|
+
`off` only registers `tower_review` after a restart/reload, although the merge gate changes
|
|
53
|
+
immediately. See [install, upgrade and migration](install-upgrade-migration.md) for the full
|
|
54
|
+
migration.
|
|
45
55
|
|
|
46
56
|
## 5. Gate, merge, clean up
|
|
47
57
|
|
|
@@ -51,10 +61,10 @@ tower_merge repoRoot=D:\work\repo missionId=M1
|
|
|
51
61
|
tower_teardown repoRoot=D:\work\repo
|
|
52
62
|
```
|
|
53
63
|
|
|
54
|
-
With
|
|
55
|
-
review it should be `ok:true`. With the default (`
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
With `review.mode:required`, `tower_preflight` before review reports `no-review`, and after a clean
|
|
65
|
+
review it should be `ok:true`. With the default (`optional`), no review blocker is required.
|
|
66
|
+
`tower_merge` refuses while blockers remain. `tower_teardown` removes merged/abandoned worktrees and
|
|
67
|
+
branches.
|
|
58
68
|
|
|
59
69
|
```
|
|
60
70
|
/tower off
|
|
@@ -63,8 +73,8 @@ worktrees and branches.
|
|
|
63
73
|
## If something fails
|
|
64
74
|
|
|
65
75
|
Read the blocker code and use [the blocker reference](../reference/blockers.md). The most common cases:
|
|
66
|
-
`stale-base` (run `tower_rebase`), `no-review` (only with `review.
|
|
67
|
-
|
|
76
|
+
`stale-base` (run `tower_rebase`), `no-review` (only with `review.mode:required`; spawn a reviewer),
|
|
77
|
+
`merge-hook-failed` (retry, or raise `timeoutMs`), `tip-moved` (only when the policy is `required`;
|
|
68
78
|
the branch changed after review, review again).
|
|
69
79
|
|
|
70
80
|
Thrown tool failures carry a `[tower:<code>]` prefix; see [error codes](../reference/errors.md)
|
|
@@ -78,7 +88,7 @@ JSON file at `<git-common-dir>/dsh-tower/config.json` (usually `.git/dsh-tower/c
|
|
|
78
88
|
|
|
79
89
|
```json
|
|
80
90
|
{
|
|
81
|
-
"review": { "
|
|
91
|
+
"review": { "mode": "required", "maxRoundsPerReviewer": 3 },
|
|
82
92
|
"merge": { "timeoutMs": 1800000 },
|
|
83
93
|
"worktrees": { "root": "D:/tower-worktrees" }
|
|
84
94
|
}
|