dsh-tower 2.0.4 → 2.1.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.
Files changed (184) hide show
  1. package/CHANGELOG.md +152 -0
  2. package/COMPATIBILITY.md +39 -0
  3. package/CONTRIBUTING.md +157 -2
  4. package/README.en.md +46 -3
  5. package/README.md +44 -3
  6. package/SECURITY.md +27 -0
  7. package/adapters/default.js +12 -0
  8. package/adapters/store-fs.js +6 -0
  9. package/adapters/vcs-git.js +4 -0
  10. package/app/archive.js +190 -0
  11. package/app/batch.js +79 -0
  12. package/app/board.js +27 -0
  13. package/app/checkCompletion.js +89 -0
  14. package/app/enrichedMissions.js +26 -0
  15. package/app/findings.js +78 -0
  16. package/app/gate.js +126 -0
  17. package/app/init.js +291 -0
  18. package/app/merge.js +573 -0
  19. package/app/messages.js +66 -0
  20. package/app/mission.js +37 -0
  21. package/app/missionContext.js +14 -0
  22. package/app/plan.js +50 -0
  23. package/app/provision.js +193 -0
  24. package/app/read.js +113 -0
  25. package/app/rebase.js +302 -0
  26. package/app/recording.js +72 -0
  27. package/app/review.js +147 -0
  28. package/app/roster.js +123 -0
  29. package/app/spawn.js +125 -0
  30. package/app/teardown.js +205 -0
  31. package/app/tower.js +552 -0
  32. package/cordis.patch.yml +61 -53
  33. package/docs/README.md +3 -1
  34. package/docs/agents/domain.md +38 -0
  35. package/docs/agents/issue-tracker.md +46 -0
  36. package/docs/agents/triage-labels.md +15 -0
  37. package/docs/architecture/overview.md +114 -13
  38. package/docs/board.md +72 -0
  39. package/docs/guide/hot-reload.md +1 -1
  40. package/docs/guide/install-upgrade-migration.md +25 -1
  41. package/docs/guide/release.md +125 -0
  42. package/docs/operations/security.md +47 -2
  43. package/docs/reference/blockers.md +1 -1
  44. package/docs/reference/config.generated.md +70 -0
  45. package/docs/reference/config.md +8 -1
  46. package/docs/reference/doctor.md +58 -8
  47. package/docs/reference/errors.generated.md +41 -0
  48. package/docs/reference/errors.md +7 -3
  49. package/docs/reference/tools.generated.md +33 -0
  50. package/docs/reference/tools.md +7 -6
  51. package/docs/zh/README.md +3 -2
  52. package/docs/zh/guide/hot-reload.md +1 -1
  53. package/docs/zh/guide/install-upgrade-migration.md +18 -1
  54. package/docs/zh/operations/security.md +30 -1
  55. package/docs/zh/reference/blockers.md +1 -1
  56. package/docs/zh/reference/config.md +8 -1
  57. package/docs/zh/reference/doctor.md +35 -2
  58. package/docs/zh/reference/errors.md +6 -2
  59. package/docs/zh/reference/tools.md +3 -3
  60. package/domain/collab/batch.js +112 -0
  61. package/domain/collab/messages.js +127 -0
  62. package/domain/collab/roster.js +158 -0
  63. package/domain/collab/spawn.js +188 -0
  64. package/domain/config/schema.js +427 -0
  65. package/domain/contracts/outputs.js +305 -0
  66. package/domain/contracts/tool-inputs/collaboration.js +58 -0
  67. package/domain/contracts/tool-inputs/findings.js +29 -0
  68. package/domain/contracts/tool-inputs/index.js +94 -0
  69. package/domain/contracts/tool-inputs/integration.js +37 -0
  70. package/domain/contracts/tool-inputs/missions.js +95 -0
  71. package/domain/contracts/tool-inputs/ops.js +75 -0
  72. package/domain/contracts/tool-inputs/shared.js +25 -0
  73. package/domain/errors.js +98 -0
  74. package/domain/gate.js +125 -0
  75. package/domain/integration/gate.js +190 -0
  76. package/domain/integration/init.js +261 -0
  77. package/domain/integration/liveness.js +31 -0
  78. package/domain/integration/merge.js +377 -0
  79. package/domain/integration/provision.js +124 -0
  80. package/domain/integration/rebase.js +236 -0
  81. package/domain/integration/review.js +52 -0
  82. package/domain/integration/teardown.js +118 -0
  83. package/domain/mission.js +68 -0
  84. package/domain/missions/completion.js +167 -0
  85. package/domain/missions/findings.js +201 -0
  86. package/domain/missions/lifecycle.js +186 -0
  87. package/domain/missions/plan.js +149 -0
  88. package/domain/missions/review.js +179 -0
  89. package/domain/planning.js +121 -0
  90. package/domain/read/enrichedMissions.js +58 -0
  91. package/domain/read/missionContext.js +53 -0
  92. package/domain/read/views.js +141 -0
  93. package/domain/report.js +67 -0
  94. package/domain/roster.js +40 -0
  95. package/domain/scope.js +177 -0
  96. package/domain/state/archive.js +257 -0
  97. package/domain/state/migrations.js +145 -0
  98. package/domain/state/schema.js +239 -0
  99. package/domain/state/semantics.js +117 -0
  100. package/domain/state.js +183 -0
  101. package/domain/summary.js +116 -0
  102. package/domain/vocabulary.js +179 -0
  103. package/index.js +63 -20
  104. package/lib/archive.js +279 -0
  105. package/lib/briefing.js +176 -0
  106. package/lib/caseSensitivity.js +66 -0
  107. package/lib/compatibility.js +108 -0
  108. package/lib/config.js +33 -375
  109. package/lib/doctor.js +275 -4
  110. package/lib/git.js +60 -16
  111. package/lib/hmr.js +4 -3
  112. package/lib/leases.js +17 -7
  113. package/lib/legacy.js +39 -14
  114. package/lib/modeState.js +39 -0
  115. package/lib/modeStore.js +11 -43
  116. package/lib/persistence.js +356 -0
  117. package/lib/presets.js +35 -0
  118. package/lib/spawn-prechecks.js +168 -0
  119. package/lib/spawn-support.js +79 -0
  120. package/lib/stateFile.js +99 -0
  121. package/lib/store.js +67 -135
  122. package/lib/tests.js +17 -12
  123. package/lib/tool-registry.js +544 -0
  124. package/lib/workspaceLock.js +2 -2
  125. package/lib/worktreeOwnership.js +22 -10
  126. package/locale/en.json +23 -1
  127. package/locale/zh.json +22 -0
  128. package/package.json +32 -4
  129. package/ports/clock.js +6 -0
  130. package/ports/ids.js +7 -0
  131. package/ports/index.js +2 -0
  132. package/ports/store.js +11 -0
  133. package/ports/vcs.js +11 -0
  134. package/scripts/architecture.mjs +94 -0
  135. package/scripts/check-docs.mjs +9 -3
  136. package/scripts/check-pack.mjs +49 -0
  137. package/scripts/domain-metrics.mjs +48 -0
  138. package/scripts/generate-config-reference.mjs +46 -0
  139. package/scripts/generate-error-reference.mjs +29 -0
  140. package/scripts/generate-tools-reference.mjs +56 -0
  141. package/scripts/issue-audit.mjs +259 -0
  142. package/scripts/lib/cli.mjs +76 -0
  143. package/scripts/lib/tmp.mjs +20 -0
  144. package/scripts/mount-smoke/fixtures.mjs +162 -0
  145. package/scripts/mount-smoke/host.json +4 -0
  146. package/scripts/mount-smoke/probe.mjs +125 -0
  147. package/scripts/mount-smoke/surface.mjs +141 -0
  148. package/scripts/mount-smoke.mjs +270 -0
  149. package/scripts/pack-manifest.txt +212 -0
  150. package/scripts/release-check.mjs +303 -0
  151. package/tools/adopt.js +72 -0
  152. package/tools/batch.js +78 -0
  153. package/tools/complete.js +53 -0
  154. package/tools/diff.js +39 -0
  155. package/tools/doctor.js +50 -0
  156. package/tools/finding.js +40 -0
  157. package/tools/findings.js +39 -0
  158. package/tools/import-legacy.js +42 -0
  159. package/tools/inbox.js +39 -0
  160. package/tools/index.js +39 -0
  161. package/tools/init.js +63 -0
  162. package/tools/interrupt.js +42 -0
  163. package/tools/legacy-map.js +44 -0
  164. package/tools/merge.js +39 -0
  165. package/tools/mission.js +41 -0
  166. package/tools/plan.js +39 -0
  167. package/tools/preflight.js +45 -0
  168. package/tools/rebase.js +39 -0
  169. package/tools/recover.js +45 -0
  170. package/tools/reload.js +52 -0
  171. package/tools/resource.js +54 -0
  172. package/tools/review.js +45 -0
  173. package/tools/roster.js +60 -0
  174. package/tools/send.js +147 -0
  175. package/tools/spawn.js +196 -0
  176. package/tools/status.js +102 -0
  177. package/tools/teardown.js +42 -0
  178. package/tools/test.js +61 -0
  179. package/types/cordis.d.ts +6 -0
  180. package/types/index.d.ts +373 -0
  181. package/lib/errors.js +0 -123
  182. package/lib/tools.js +0 -2460
  183. package/lib/tower.js +0 -2912
  184. /package/{lib → domain}/batch.js +0 -0
@@ -0,0 +1,70 @@
1
+ # Config reference (generated)
2
+
3
+ > Generated by `npm run docs:generate:config` from `domain/config/schema.js`. Values are defaults before repo config.
4
+
5
+ | Key | Default | Description |
6
+ |---|---|---|
7
+ | `section` | `""` | Legacy alias for mode.promptSection: replaces the whole Tower-mode system prompt text. |
8
+ | `allowAgentControl` | `` | Legacy alias for mode.allowAgentControl: register tower_control so the model may toggle the mode. |
9
+ | `init.autoGitInit` | `false` | tower_init may run git init and create an empty initial commit when the directory is not a repository. |
10
+ | `init.snapshotDirty` | `false` | tower_init may snapshot uncommitted tracked changes into a dangling commit used as the mission baseline. |
11
+ | `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. |
12
+ | `mode.promptSection` | `` | Replacement Tower-mode system prompt text. Empty keeps the built-in reminder. |
13
+ | `mode.sectionAppend` | `` | Extra text appended to the Tower-mode system prompt section (team conventions). |
14
+ | `mode.allowAgentControl` | `true` | Register tower_control so the model may enter/leave the mode with a reason. |
15
+ | `mode.autoPauseGoal` | `true` | Pause an active goal when entering Tower mode. |
16
+ | `mode.requireResumeAuthorization` | `false` | Require an authorization reference when resuming a mission paused with pauseCause=user. |
17
+ | `mode.requireHumanAckOnFirstEntry` | `false` | Require a human /tower ack before the model may enter Tower mode for the first time in a session. |
18
+ | `mode.workspaceLock` | `false` | Allow only one Tower session per repository (advisory <git-common-dir>/dsh-tower/tower.lock). |
19
+ | `mode.workspaceLockScope` | `"all"` | all = every repo-scoped tool acquires the workspace lock; mutations = only tools that write state/worktrees do. |
20
+ | `mode.planModePolicy` | `"clear"` | On Tower entry: clear an active plan mode, or refuse to enter until the plan is exited. |
21
+ | `mode.workspaceStaleMs` | `300000` | A workspace lock older than this is stale and can be taken over. |
22
+ | `mode.heartbeatMs` | `10000` | Workspace-lock heartbeat interval while Tower mode is on. |
23
+ | `mode.denyTools` | `["create_goal","todo_write","team_task_create","team_task_update","team_task_reopen","team_task_list","spawn_teammate","wait_agent","workflow","ralph","subagent","subagent_fork"]` | Tools refused while Tower mode is active. |
24
+ | `mode.storePath` | `` | Directory for modes.json; defaults to $DSH_HOME/tower-mode. |
25
+ | `mode.maxEntries` | `200` | Maximum persisted mode entries. |
26
+ | `mode.lockTimeoutMs` | `5000` | How long saveModes waits for the cross-process modes.lock before giving up. |
27
+ | `review.requireReview` | `false` | Require an independent clean review before merge. Default false: review is optional; set true to enforce the review gate globally. |
28
+ | `review.maxRoundsPerReviewer` | `5` | Review-round cap per reviewer per mission branch. |
29
+ | `merge.timeoutMs` | `900000` | Default tower_merge timeout, including pre-merge hooks. |
30
+ | `merge.maxBufferBytes` | `67108864` | Captured merge output buffer. |
31
+ | `git.timeoutMs` | `15000` | Default timeout for Git commands other than merge; merge uses merge.timeoutMs. |
32
+ | `worktrees.root` | `` | Default parent directory for mission worktrees; empty derives <main-checkout-parent>/.dsh-tower-worktrees/<hash>. |
33
+ | `spawn.workerPreset` | `"tower-worker"` | Agent preset for worker missions. |
34
+ | `spawn.reviewerPreset` | `"tower-reviewer"` | Agent preset for reviewers. |
35
+ | `spawn.maxDepth` | `1` | Subagent depth requested when spawning tower agents. |
36
+ | `spawn.provider` | `` | Subagent provider id; empty picks spawn, then the first registered provider. |
37
+ | `spawn.workerModel` | `{}` | Model route for workers; omitted inherits the parent session. |
38
+ | `spawn.reviewerModel` | `{}` | Model route for reviewers; omitted inherits the parent session (recommended). |
39
+ | `spawn.maxLiveWorkers` | `4` | Maximum live worker entries allowed per repository at once. |
40
+ | `spawn.staleMs` | `15000` | Roster entries with no catalog activity after this age are marked stale and block merge (0 disables). |
41
+ | `spawn.maxWaitMs` | `90000` | Upper bound for tower_spawn waitMs (default 90s, Kimi-compatible). |
42
+ | `spawn.requireLease` | `` | Optional lease name tower_spawn must hold before starting a worker. |
43
+ | `send.maxPerMinute` | `120` | Per-repository tower_send rate limit per minute (0 disables). |
44
+ | `send.interruptDefault` | `false` | Default value of tower_send.interrupt when the caller omits it. |
45
+ | `tests.enabled` | `true` | Run declared test discovery when a mission configures it. |
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). |
47
+ | `tests.timeoutMs` | `60000` | Per discovery command timeout. |
48
+ | `tests.maxBytes` | `16777216` | Maximum captured stdout bytes per command. |
49
+ | `tests.autoChecks` | `true` | Attach a tests check to the completion report when discovery runs. |
50
+ | `tests.allowPackageDiscovery` | `true` | Allow package.json tower.testDiscovery commands from the mission worktree (executes repository code; see security docs). |
51
+ | `gates.requireChecks` | `false` | Require at least one completion check (tower_complete.checks) before a mission can merge. |
52
+ | `gates.requireTests` | `false` | Require a passing test-discovery receipt for the reviewed worktree tip before merge. |
53
+ | `gates.allowReviewExemption` | `false` | Allow reviewPolicy:none missions with a non-empty reviewExemptionReason to skip review blockers. |
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. |
55
+ | `docs.board` | `true` | Write a human-readable MISSIONS.md after mission changes. |
56
+ | `docs.boardLocation` | `"common"` | common = <git-common-dir>/dsh-tower/MISSIONS.md; repo = <repoRoot>/MISSIONS.md. |
57
+ | `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. |
58
+ | `archive.afterDays` | `30` | Terminal missions untouched for at least this many days are archived on the next mutation (0 archives them immediately). |
59
+ | `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). |
60
+ | `tools.exposeOperational` | `true` | Master switch for batch/resource/import/legacy/recover/test/doctor tools. |
61
+ | `tools.exposeBatch` | `true` | Register tower_batch. |
62
+ | `tools.exposeImport` | `true` | Register tower_import_legacy/tower_legacy_map. |
63
+ | `tools.exposeLeases` | `true` | Register tower_resource advisory leases. |
64
+ | `tools.exposeTests` | `true` | Register tower_test; gates.requireTests requires it. |
65
+ | `tools.exposeRecovery` | `true` | Register tower_recover (stale state-lock escape). |
66
+ | `tools.exposeDoctor` | `true` | Register tower_doctor (read-only health contract). |
67
+ | `leases.ttlMs` | `900000` | Default advisory lease lifetime; expiry never auto-releases. |
68
+ | `dev.hotReload` | `true` | Register tower_reload and /tower reload when the DSH HMR service is available. |
69
+ | `dev.reloadDelayMs` | `50` | Delay before a scheduled reload, so the tool result is delivered first. |
70
+ | `dev.reloadRequiredIds` | `["tower-bundle","tower","tower-skills","preset-tower-worker","preset-tower-reviewer"]` | Loader entry ids passed to reconcileProfile as required. |
@@ -41,11 +41,13 @@ violates the declared schema (for example `merge.timeoutMs` below 1000) fail wit
41
41
  | `allowAgentControl` | `true` (alias of `mode.allowAgentControl`) | Legacy alias: register `tower_control` so the model may toggle the mode. |
42
42
  | `init.autoGitInit` | `false` (bundled patch: `true`) | `tower_init` may run `git init` + an empty initial commit when `repoRoot` is an absolute, existing directory that is not a repository. |
43
43
  | `init.snapshotDirty` | `false` | `tower_init` may snapshot uncommitted **tracked** changes into a dangling commit used as the mission baseline (untracked files are not included; the commit is kept alive under `refs/tower/snapshot/*`). |
44
+ | `init.autoExclude` | `true` | Append in-repo artifacts (`MISSIONS.md`, in-repo worktree roots) to `.git/info/exclude`; never touches `.gitignore`. |
44
45
  | `mode.promptSection` | `''` | Replacement prompt text (takes precedence over `section` when both set). |
45
46
  | `mode.sectionAppend` | `''` | Extra text appended after the reminder (team conventions). |
46
47
  | `mode.allowAgentControl` | `true` | Register `tower_control`; the model may enter/leave with a reason. Set `false` for human-only mode control. |
47
48
  | `mode.autoPauseGoal` | `true` | Pause an active goal on entry; the goal is never auto-resumed on exit. |
48
49
  | `mode.requireResumeAuthorization` | `false` | Resuming a mission paused with `pauseCause:user` requires an `authorization` reference. |
50
+ | `mode.requireHumanAckOnFirstEntry` | `false` | Require a human `/tower ack` before the model may make the first Tower-mode entry in a session. |
49
51
  | `mode.workspaceLock` | `false` (bundled patch: `true`) | Allow only one Tower session per repository (`tower.lock`); others get `workspace-busy`. |
50
52
  | `mode.workspaceLockScope` | `all` | `all` locks every repo tool; `mutations` skips the workspace lock for read-only tools. |
51
53
  | `mode.planModePolicy` | `clear` | Tower entry clears an active plan mode (or `refuse` until the plan is exited); a pre-step also clears a plan activated later. |
@@ -81,12 +83,17 @@ violates the declared schema (for example `merge.timeoutMs` below 1000) fail wit
81
83
  | `gates.requireChecks` | `false` | Require at least one `tower_complete.checks` entry before merge (`no-check-evidence`). |
82
84
  | `gates.requireTests` | `false` | Require a passing discovery receipt for the current mission branch tip (`tests-missing`); independent of the review gate. |
83
85
  | `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
+ | `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. |
84
87
  | `docs.board` | `true` | Write `MISSIONS.md` after mutations. |
85
88
  | `docs.boardLocation` | `common` | `common` = `<git-common-dir>/dsh-tower/MISSIONS.md`; `repo` = `<repoRoot>/MISSIONS.md`. |
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
+ | `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
+ | `tools.exposeOperational` | `true` | Register batch/resource/import/legacy/recover/test/doctor tools; `false` shrinks the fixed tool-schema context. |
86
93
  | `leases.ttlMs` | `900000` | Default advisory lease lifetime; expiry never auto-releases. |
87
94
  | `dev.hotReload` | `true` | Register `tower_reload`; `/tower reload` is disabled when off. It returns `reload-unavailable` when the profile has no HMR service. |
88
95
  | `dev.reloadDelayMs` | `50` | Delay before a scheduled reload so the tool result is delivered first. |
89
- | `dev.reloadRequiredIds` | four Tower loader ids | Entry ids passed to `reconcileProfile` as required. |
96
+ | `dev.reloadRequiredIds` | five Tower loader ids | Entry ids passed to `reconcileProfile` as required. |
90
97
 
91
98
  ## Example
92
99
 
@@ -8,26 +8,76 @@ It returns:
8
8
  "ok": true,
9
9
  "checks": [
10
10
  { "id": "git", "status": "pass", "detail": "git version 2.55.0" },
11
- { "id": "state", "status": "pass", "detail": ".../state.json revision 7" }
11
+ { "id": "state", "status": "pass", "detail": ".../state.json revision 7" },
12
+ { "id": "state-schema", "status": "pass", "detail": "schemaVersion 3 is current" },
13
+ { "id": "case-mode", "status": "pass", "detail": "caseInsensitive: true; scope matching uses case-insensitive comparison" }
12
14
  ],
15
+ "stateSchema": {
16
+ "currentVersion": 3,
17
+ "supportedVersion": 3,
18
+ "migrations": [],
19
+ "status": "current"
20
+ },
21
+ "archive": {
22
+ "missions": 3,
23
+ "archivedMissions": 12,
24
+ "stateBytes": 48211,
25
+ "archiveBytes": 190244,
26
+ "years": [2026],
27
+ "files": 2,
28
+ "unreadable": [],
29
+ "policy": { "enabled": true, "afterDays": 30, "maxMissions": 100 }
30
+ },
13
31
  "environmentContract": {
14
32
  "platform": "win32",
15
33
  "gitVersion": "git version 2.55.0",
16
34
  "repoRoot": "D:/repo",
17
35
  "commonDir": "D:/repo/.git",
18
- "stateDir": "D:/repo/.git/dsh-tower"
36
+ "stateDir": "D:/repo/.git/dsh-tower",
37
+ "caseInsensitive": true,
38
+ "comparisonMode": "case-insensitive"
19
39
  }
20
40
  }
21
41
  ```
22
42
 
43
+ `stateSchema` describes the file **on disk**, not the in-memory document: `currentVersion` is the
44
+ `schemaVersion` doctor read, `supportedVersion` is the build's `SCHEMA_VERSION`, and `migrations` is
45
+ the ordered v→v path a write would apply (`[]` when current). `status` is one of `current`,
46
+ `migratable`, `newer`, `unmigratable`, `invalid`. Doctor never migrates, so a `migratable` file stays
47
+ byte-identical and the `state-schema` check is a `warn` (the next locked write applies the migration
48
+ and keeps a `state.json.v<N>.bak` backup). A file written by a newer plugin reports `newer` and a
49
+ `fail`: this build refuses to downgrade it. A state-load failure is reported as a failed check rather
50
+ than thrown so doctor remains useful on a broken repository.
51
+
52
+ `archive` reports the terminal-mission archive: active `missions`, `archivedMissions` and `stateBytes`,
53
+ the archive directory's `archiveBytes`/`years`/`files`, every `unreadable` diagnostic, and the configured
54
+ `policy` (`enabled`, `afterDays`, `maxMissions`). A missing archive directory is a healthy empty archive
55
+ (`archive` check `pass`); a truncated index or year file is a `warn` with the diagnostic, and archiving is
56
+ paused until it is repaired rather than overwriting it. Nothing is lost while the archive is unreadable:
57
+ the eligible missions stay in `state.json`.
58
+
23
59
  Checks cover: git availability, repository identity, state schema/load, repo config presence,
24
60
  worker/reviewer preset names, subagents and HMR services, workspace lock holder/staleness, live roster,
25
- worktree existence/ownership/dirtiness, test-discovery policy, legacy snapshots, batches and leases.
26
- Missions intentionally cleaned up by `tower_teardown` are skipped (the check notes the removed count)
27
- instead of being reported as missing.
28
- `warn` is advisory; `fail` means the repository needs attention before spawning/merging. A state-load
29
- failure is reported as a failed check rather than thrown so doctor remains useful on a broken
30
- repository.
61
+ worktree existence/ownership/dirtiness, test-discovery policy, completion-report sizes, archive health, legacy`nsnapshots, batches and leases. Missions intentionally cleaned up by `tower_teardown` are skipped (the check`nnotes the removed count) instead of being reported as missing.
62
+
63
+ `caseInsensitive` is the mode `tower_init` detected once on the checkout's filesystem; `null` means
64
+ "not detected yet" and the `case-mode` check is a `warn`. `comparisonMode` is the comparison scope
65
+ matching actually uses: `case-insensitive` folds both sides, `case-sensitive` stays byte-exact. Doctor
66
+ never probes — a probe creates a temporary file, and a read-only call must not write. Run
67
+ `tower_init` on a writable checkout to record the mode; until then the safe `case-sensitive` default
68
+ applies.
69
+
70
+ Checks cover: git availability, repository identity, state schema/load, the detected case mode, repo
71
+ config presence, worker/reviewer preset names, subagents and HMR services, workspace lock
72
+ holder/staleness, live roster, worktree existence/ownership/dirtiness, test-discovery policy, legacy
73
+ snapshots, batches and leases. Missions intentionally cleaned up by `tower_teardown` are skipped (the
74
+ check notes the removed count) instead of being reported as missing.
75
+ `warn` is advisory; `fail` means the repository needs attention before spawning/merging.
76
+
77
+ The `reports` check names the largest completion report in the repository (`<bytes> B on <mission>`,
78
+ against the `gates.maxReportBytes` cap); the same numbers are returned as
79
+ `reports: { count, largest: { missionId, bytes } | null, capBytes }`. Reports written before the cap
80
+ only produce a `warn`; doctor never rewrites them.
31
81
 
32
82
  The environment contract is informational; it is not an identity or authorization proof. `tower_init`
33
83
  still requires the tower role, and `tower_spawn` still checks the live subagent catalog.
@@ -0,0 +1,41 @@
1
+ # Error code catalog (generated)
2
+
3
+ > Generated by `npm run docs:generate:errors` from `domain/errors.js`. Do not edit by hand.
4
+
5
+ | Code | Description |
6
+ |---|---|
7
+ | `invalid-argument` | A tool argument is missing, malformed, or not allowed. |
8
+ | `repo-invalid` | repoRoot is missing, relative, or not inside a Git repository. |
9
+ | `not-initialized` | Tower is not initialized for this repository; run tower_init. |
10
+ | `mission-not-found` | No mission with that id exists. |
11
+ | `finding-not-found` | No finding with that id exists. |
12
+ | `invalid-transition` | The requested state transition is not allowed. |
13
+ | `stale-base` | The repository state changed while the operation was being prepared; retry. |
14
+ | `state-locked` | Another Tower writer holds the state lock. |
15
+ | `state-corrupt` | The Tower state file is unreadable or invalid. |
16
+ | `state-version-unsupported` | The Tower state file uses a schema version this build cannot read or downgrade. |
17
+ | `state-migration-failed` | A Tower state migration failed; the original state file was not modified. |
18
+ | `config-invalid` | Tower configuration is invalid. |
19
+ | `config-unknown-key` | Tower configuration has an unknown top-level key. |
20
+ | `git-failed` | A git command failed. |
21
+ | `git-timeout` | A git command timed out or exceeded its output buffer. |
22
+ | `worktree-dirty` | The mission worktree has uncommitted changes and cannot complete. |
23
+ | `spawn-unavailable` | The subagents service is unavailable. |
24
+ | `provider-missing` | No subagent provider is registered. |
25
+ | `provider-cooldown` | The provider is throttled; spawn is in a short cooldown. |
26
+ | `preset-missing` | The configured agent preset is not registered. |
27
+ | `reviewer-prerequisite` | The reviewer spawn prerequisite is not met. |
28
+ | `not-authorized` | The calling agent role may not perform this action. |
29
+ | `workspace-busy` | Another Tower session owns this repository workspace lock. |
30
+ | `worker-limit` | The repository reached spawn.maxLiveWorkers live workers. |
31
+ | `rate-limited` | The tower_send per-minute rate limit was reached. |
32
+ | `takeover-refused` | The mission already has a live worker that was not stopped first. |
33
+ | `reload-unavailable` | The DSH HMR service is not available in this profile. |
34
+ | `reload-failed` | The HMR reload failed; check the DSH log and restart if needed. |
35
+ | `tests-failed` | Declared test discovery failed or did not meet its admission requirements. |
36
+ | `tests-timeout` | Test discovery exceeded its time or output budget. |
37
+ | `lease-busy` | Another holder owns the requested advisory lease. |
38
+ | `lease-token` | The lease release token does not match the current holder. |
39
+ | `batch-invalid` | The batch request or its close evidence is invalid. |
40
+ | `legacy-invalid` | The legacy snapshot or mapping is invalid. |
41
+ | `tool-internal` | An unexpected failure; read the message and the activity log. |
@@ -2,7 +2,7 @@
2
2
 
3
3
  Tool failures are thrown errors, not `{ok:false}` results (gate outcomes are the exception: see
4
4
  [blockers.md](blockers.md)). Every thrown error carries a stable code as a message prefix. This
5
- release defines 31 tool error codes.
5
+ release defines 35 tool error codes.
6
6
 
7
7
  Every `TowerToolError` also carries `retryable` (boolean), `nextAction` (string or `null`) and `details`
8
8
  (structured context or `null`) for programmatic callers; the message prefix remains the stable contract
@@ -13,7 +13,7 @@ for text-only clients.
13
13
  ```
14
14
 
15
15
  Callers can match the prefix instead of the prose. Codes are reserved for the conditions below; a
16
- message without a more specific match is reported as `tool-internal`.
16
+ plain `Error` without a structured code is reported as `tool-internal`.
17
17
 
18
18
  | Code | Meaning | Typical trigger |
19
19
  |---|---|---|
@@ -23,15 +23,19 @@ message without a more specific match is reported as `tool-internal`.
23
23
  | `mission-not-found` | No mission with that id exists. | a stale or mistyped `M9` |
24
24
  | `finding-not-found` | No finding with that id exists. | `tower_findings({id:"F99"})` |
25
25
  | `invalid-transition` | The requested state transition is not allowed. | a blocked→merged attempt; a finding transition not in the graph; a reviewer mutating findings; completing a mission with open tasks, blockers, no commits, or unread inbox messages. |
26
+ | `stale-base` | The repository state changed while the operation was being prepared; retry. | two-phase mutation revalidation |
26
27
  | `worktree-dirty` | The mission worktree has uncommitted changes and cannot complete. | staged, modified, or untracked files left behind before `tower_complete` |
27
28
  | `state-locked` | Another Tower writer holds the state lock. | concurrent mutation after the lock timeout |
28
- | `state-corrupt` | The Tower state file is unreadable or invalid. | hand-edited `state.json` |
29
+ | `state-corrupt` | The Tower state file is unreadable or invalid. | hand-edited `state.json`; invalid JSON; no usable `schemaVersion` |
30
+ | `state-version-unsupported` | The state file's schema version is newer than this build supports, or cannot be migrated. | opening a repository written by a newer plugin; an unknown older version; this build refuses to downgrade |
31
+ | `state-migration-failed` | A state migration threw; the original file was not modified. | a bug in a migration step; see the pre-migration `state.json.v<N>.bak` |
29
32
  | `config-invalid` | Tower configuration is invalid. | malformed repo `config.json`, a value that violates the schema |
30
33
  | `config-unknown-key` | Tower configuration has an unknown top-level key. | a typo such as `allowAgentToggle` |
31
34
  | `git-failed` | A git command failed for a reason other than the delegated cases. | a rejected merge command, invalid ref |
32
35
  | `git-timeout` | A git command timed out or exceeded its output buffer. | slow pre-merge hook, huge diff |
33
36
  | `spawn-unavailable` | The subagents service is unavailable. | a Host without `subagents` |
34
37
  | `provider-missing` | No subagent provider is registered. | an empty provider list |
38
+ | `provider-cooldown` | spawn is in a short cooldown after repeated retryable provider failures. | wait for `blockedUntil`, then retry |
35
39
  | `preset-missing` | The configured agent preset is not registered. | missing `tower-worker`/`tower-reviewer` preset rows, DSH not restarted |
36
40
  | `reviewer-prerequisite` | The reviewer spawn prerequisite is not met. | reviewer spawned before the mission is `completed`, or before a worktree exists |
37
41
  | `not-authorized` | The calling agent role may not perform this action. | a worker calling a tower-only tool or changing mission status/scope; a reviewer mutating findings |
@@ -0,0 +1,33 @@
1
+ # Tools overview (generated)
2
+
3
+ > Generated by `npm run docs:generate:tools` from the registered tool definitions. Do not edit by hand.
4
+
5
+ | Tool | Description |
6
+ |---|---|
7
+ | `tower_adopt` | Reconcile the roster with the durable subagent catalog after a restart or new session. Agents that are provably gone are marked retired (records are kept) or missing (retireMissing:false, so the tower can inspect/take over); agents whose parent catalog cannot be read stay unknown and are never changed. Only the tower may adopt. |
8
+ | `tower_batch` | Manage finite evidence batches: create freezes missionIds/requiredChecks/receiptPaths, status summarizes members, close verifies every required check and receipt hash, abandon retires a stale open batch. Membership and exclusions are immutable after create; close/abandon never remove missions. |
9
+ | `tower_complete` | Worker-only: report mission completion. Enforced by the store: every task done or dropped, no blockers, the current owner, the mission branch has at least one commit since its spawn base, and no unread inbox messages. checks records verifiable evidence (name/command/exitCode/evidence); gates.requireChecks can require at least one before merge. Stores the full report, capped at gates.maxReportBytes (default 32768 UTF-8 bytes); a larger report is rejected with invalid-argument and an actionable nextAction, never truncated. Persists reportBytes on the stored report. completion means ready for preflight and optional review, not merged. |
10
+ | `tower_diff` | Read-only view of a mission branch for review: merge base, changed files, commits, and a capped patch against the recorded base. Safe for reviewers; it never modifies the repository. |
11
+ | `tower_doctor` | Read-only environment and repository health check: git, state schema, case-sensitivity detection (caseInsensitive and the active scope comparison mode), config, presets/subagents/HMR, workspace lock, roster staleness, worktree ownership, test discovery, leases, and the terminal-mission archive (count, bytes, years, unreadable diagnostics and the configured afterDays/maxMissions policy). Returns checks with pass/warn/fail and an environment contract. Never writes state and never probes the filesystem. |
12
+ | `tower_finding` | File a structured finding (bug / improve / vuln / idea) for the tower to route. Use this for anything notable OUTSIDE your mission scope instead of editing out-of-scope files. Severity is one of low|medium|high|critical. Use tower_findings to list, read, or close findings later. |
13
+ | `tower_findings` | List, read, or update findings for a repository. Without id it lists findings newest-first with counts by status (filters: status, type, severity, missionId, open, limit). With id it returns that finding; with id plus status and/or note it records a lifecycle transition. Statuses are open -> triaged -> fixed|wontfix, and fixed/wontfix can be reopened to open. Only the tower or a worker may change a finding; reviewers may only list and read. Read-only when no id is given. |
14
+ | `tower_import_legacy` | Read-only import of a Tower Codex (schemaVersion 1) or DSH legacy state file. Stores the full payload under <common>/dsh-tower/legacy/<sha256>.json and records only a summary in state.legacySnapshots; never activates, adopts, or resumes paused missions. |
15
+ | `tower_inbox` | Read your tower inbox: messages addressed to you plus broadcasts, newest first. Only returned messages are acknowledged; the result reports unreadRemaining, so keep reading until it reaches 0. Set include_read for history. Unread messages block tower_complete. |
16
+ | `tower_init` | Initialize Tower mission state for a repository (absolute repoRoot). Creates <git-common-dir>/dsh-tower/state.json and an activity log; idempotent on an existing workspace. Probes whether the checkout filesystem folds case and records caseInsensitive once, so scope matching follows the real filesystem (a skipped probe records null and keeps the safe case-sensitive comparison). Options: autoGitInit (git init + empty initial commit when the directory is not a repository), snapshotDirty (snapshot uncommitted work into a dangling commit used as the mission baseline), reanchor (change base when every mission is merged/abandoned). Only the tower may initialize. |
17
+ | `tower_interrupt` | Interrupt one running tower roster agent (worker or reviewer) and record it as interrupted in the roster. Only the tower may interrupt. Idempotent: an agent that is not running returns interrupted:false. Use it before takeover after a runaway worker, or to stop work that is no longer wanted. |
18
+ | `tower_legacy_map` | Audit-only mapping between a legacy snapshot id and a current mission. map records sourceHash/oldId/newMissionId; status lists mappings. Mapping never adopts or resumes a legacy mission. |
19
+ | `tower_merge` | Merge a mission branch into the recorded base branch with --no-ff, after the hard gate passes. Verifies the two merge parents and records the mission as merged. If the merge commit already landed (manual merge, crashed or slow pre-merge hook), it detects the merge commit and records it as reconciled instead of merging again. A failed gate returns stable blocker codes without merging; a hook failure aborts cleanly and returns "merge-hook-failed" while a real content conflict returns "merge-conflict". |
20
+ | `tower_mission` | Update one mission: lifecycle status, decision note, blocker, clear blockers, mark a task done, drop a task with a reason, or widen the scope (tower only). "merged" is never set here; only a future tower_merge records it. Merged and abandoned are terminal. |
21
+ | `tower_plan` | Plan one or more missions in an initialized repository. Each mission has a printable-Unicode title (NFC-normalized, at most 120 characters), relative scope globs, optional task checklist, optional mission-id dependencies, and kind build|survey. Assigns M1.. ids and auto-adds a dependency when build scopes overlap (serializes merges); rejects dependency cycles. |
22
+ | `tower_preflight` | Read-only merge gate for one mission: returns ok plus stable blocker codes (branch-missing, deps-unmerged, read-only-survey, stale-base, out-of-scope, base-dirty, branch-owned-by-closed-missions, and mission/worker guards). Review blockers (no-review, review-hold, not-clean, review-not-approved, tip-moved, review-mission-mismatch) apply only when review.requireReview is on. Never changes anything. |
23
+ | `tower_rebase` | Rebase a mission branch onto the current base tip inside its own worktree. Refuses while its worker is running or the worktree is dirty. A clean conflict-free rebase waives re-review for the old review; a conflict is aborted and the mission is marked blocked. If the branch is already contained in the base, it returns alreadyMerged and leaves the branch untouched so tower_merge can record the landed merge. |
24
+ | `tower_recover` | Read-only recovery diagnostics and an explicit stale-state-lock repair. status reports the state lock owner; clear-stale-lock requires confirm:"CLEAR_STALE_LOCK" and only moves a lock whose PID is provably dead to a backup file. It never resets Tower state or worktrees. |
25
+ | `tower_reload` | Dev-only: ask DSH HMR to reconcile this profile and reload the Tower Host plugin from disk. The reload is scheduled a few milliseconds after this call returns (so the result reaches the model first). Use after editing the plugin source in a linked profile; plugin_manager install/update already triggers reconciliation by itself. Registered while dev.hotReload is on; fails with reload-unavailable when the HMR service is absent. |
26
+ | `tower_resource` | Advisory resource leases per repository (coordination only; no OS CPU/memory hard cap). acquire/release require a token and are tower-only; status is read-only. Expired leases are never taken over silently. |
27
+ | `tower_review` | Submit an independent review verdict for a completed mission, bound to the current branch tip. status is "clean", "p1-Nitems", or "p2-Nitems"; merge is "merge", "fix-then-merge", or "hold" (only clean + merge is later approval). Non-clean returns the mission to active. Reviewers are capped at 5 rounds per branch; a tower-submitted review is break-glass and requires breakGlassReason. |
28
+ | `tower_roster` | List tower roster agents with authoritative live activity from the subagent catalog (running/inactive/missing/unknown). Read-only; never resumes children. Use before completion or merge decisions. |
29
+ | `tower_send` | Send a durable inbox message to a tower participant: a roster agent name, "tower", or "all" (broadcast). The message is persisted first; when the recipient is a currently running roster agent the tower also steers it into that child's current turn. Returns delivery results. |
30
+ | `tower_spawn` | Spawn a worker or reviewer for a mission. Workers use the tower-worker preset; reviewers require a completed mission and use the read-only tower-reviewer preset. Provisions (or reuses) the mission git worktree and branch, generates a self-contained briefing, and starts a background subagent in that worktree. The child final report is delivered back to this session; tower_roster shows authoritative activity. Records the agent in the roster; reviewer spawns record the branch tip so a reviewer that modifies the branch cannot submit a review. Returns immediately unless wait=true. |
31
+ | `tower_status` | Read the Tower dashboard for a repository: state path, base branch, revision, mission status counts, paged mission summaries (scope/deps/tasks/worktree/owner), roster size, findings, archive count, config path and the activity tail. Pass missionId for one full mission (tasks, notes, reviews, findings, completion report; archived missions are loaded from their year file); offset/limit page the mission list (default 20, max 100); includeGate adds preflight gate digests for the page; includeArchived appends compact archived summaries after the active page; format:"markdown" returns the human-readable mission board (active missions plus an archived count). Read-only. |
32
+ | `tower_teardown` | Tear down eligible mission worktrees after their missions are merged or abandoned. A worktree whose roster agent is running is always kept (force cannot override); dirty worktrees need force; exclude keeps named missions. dry_run previews the remove/keep decisions without changing anything. Terminal missions whose worktree is gone and whose configured archive policy is due (archive.afterDays / archive.maxMissions) are moved to <git-common-dir>/dsh-tower/archive/ so state.json stops rewriting them; nothing is deleted. |
33
+ | `tower_test` | Run the mission worktree's declared tower.testDiscovery (package.json) and record the receipt. Returns ok:false with diagnostics when discovery fails; does not change mission status. Use tower_complete for automatic admission checks. |
@@ -11,7 +11,8 @@ The build exposes up to 29 tools: 19 core repository tools below (including `tow
11
11
  `task_drop_reason`/`taskDropReason`, `clear_blockers`/`clearBlockers`,
12
12
  `include_read`/`includeRead`, `dry_run`/`dryRun`, `timeoutMs`/`timeout_ms`,
13
13
  `waitMs`/`wait_ms`, `mission_id`/`missionId` on findings, `agent_id`/`agentId` on interrupt,
14
- `include_gate`/`includeGate` on status, `retire_missing`/`retireMissing` on adopt. Outputs use
14
+ `include_gate`/`includeGate`, `include_archived`/`includeArchived` on status,
15
+ `retire_missing`/`retireMissing` on adopt. Outputs use
15
16
  canonical camelCase names.
16
17
 
17
18
  ## Roles
@@ -31,16 +32,16 @@ findings but not mutate them. Violations throw `[tower:not-authorized]` or
31
32
  | Tool | Parameters | Purpose / notes |
32
33
  |---|---|---|
33
34
  | `tower_init` | `base?`, `autoGitInit?`, `snapshotDirty?`, `reanchor?` | Create `<git-common-dir>/dsh-tower/state.json`. `autoGitInit` runs `git init` + empty initial commit; `snapshotDirty` records a dangling snapshot commit of uncommitted tracked changes used as the mission baseline; `reanchor` changes base only when no non-terminal mission exists. Returns `dirty`, `snapshotCommit`, `reanchored`. |
34
- | `tower_plan` | `missions[]` (`title`, `scope[]`, `tasks[]?`, `deps[]?`, `kind?`, `context?`, `references[]?` ≤20, `reviewPolicy?`, `reviewExemptionReason?`) | Create missions `M1..`; overlapping build scopes auto-add a dependency; rejects unknown deps and cycles. `reviewPolicy:"none"` requires a non-empty reason; when the review gate is on, preflight also requires `gates.allowReviewExemption`. |
35
+ | `tower_plan` | `missions[]` (`title` — printable Unicode, NFC-normalized, ≤120 characters, `scope[]`, `tasks[]?`, `deps[]?`, `kind?`, `context?`, `references[]?` ≤20, `reviewPolicy?`, `reviewExemptionReason?`) | Create missions `M1..`; overlapping build scopes auto-add a dependency; rejects unknown deps and cycles. Mission ids stay ASCII (`M1`, `M2`); invisible characters and over-long titles are rejected with `invalid-argument`. `reviewPolicy:"none"` requires a non-empty reason; when the review gate is on, preflight also requires `gates.allowReviewExemption`. |
35
36
  | `tower_mission` | `id` (alias `missionId`), `status?`, `task_done?`, `task_drop?`+`task_drop_reason?`, `blocker?`, `clear_blockers?`, `note?`, `scope?`, `pause_cause?`, `authorization?` | Update one mission. Worker-safe fields are task/blocker/note; `merged` cannot be set here. Paused missions record `pause_cause` (user/failure/resource/dependency); resuming a user-paused mission honors `mode.requireResumeAuthorization`. |
36
- | `tower_status` | `missionId?`, `offset?`, `limit?`, `includeGate?`, `format?` | Dashboard by default. `missionId` returns full detail (tasks/notes/reviews/findings/completionReport). `offset`/`limit` page missions (default 20, max 100), `includeGate` adds gate digests, `format:"markdown"` returns the board. |
37
+ \|\ `tower_status`\ \|\ `missionId\?`,\ `offset\?`,\ `limit\?`,\ `includeGate\?`,\ `includeArchived\?`,\ `format\?`\ \|\ Dashboard\ by\ default\.\ Paged\ missions\ carry\ `reportBytes`\ only;\ `missionId`\ returns\ full\ detail\ \(tasks/notes/reviews/findings/completionReport\ body\),\ and\ an\ archived\ mission\ is\ loaded\ from\ its\ year\ file\.\ `offset`/`limit`\ page\ active\ missions\ \(default\ 20,\ max\ 100\),\ `includeGate`\ adds\ gate\ digests,\ `includeArchived`\ appends\ compact\ archived\ summaries\ \(flagged\ `archived:true`,\ no\ report\ body\)\ after\ the\ active\ page,\ and\ `format:"markdown"`\ returns\ the\ board\ \(active\ rows\ plus\ an\ archived\ count\)\.\ Includes\ `mode`\ with\ `lastChange`\ and\ recent\ `history`\ when\ the\ host\ mode\ hook\ is\ available\.\ `archive`\ reports\ `\{count,\ years,\ unreadable,\ message}`;\ a\ corrupt\ archive\ degrades\ to\ `unreadable:true`\ instead\ of\ failing\ the\ dashboard\.\ \|
37
38
  | `tower_spawn` | `missionId` (alias `id`), `role` (`worker`/`reviewer`), `name?`, `instructions?`, `worktreeRoot?`, `wait?`, `waitMs?`, `takeover?`, `includeDirtyBase?` | Provision/reuse the worktree and start a subagent. Worker uses `tower-worker`; reviewer requires a `completed` mission and uses the read-only `tower-reviewer`. `takeover:true` replaces a live worker entry that is no longer running (increments ownerGeneration). Only one live worker per mission; `spawn.maxLiveWorkers` caps the repository. |
38
39
  | `tower_roster` | — | Roster with live activity (`running`/`inactive`/`missing`/`unknown`) and `stale` flags from the subagent catalog. Read-only. |
39
40
  | `tower_adopt` | `retireMissing?` | Reconcile roster entries with the durable subagent catalog. Provably gone agents are retired (default) or marked `missing` (`retireMissing:false`) so they can be inspected/taken over. |
40
41
  | `tower_diff` | `missionId` (alias `id`), `maxBytes?` | Read-only merge-base diff: files, commits, capped patch. Safe for reviewers. |
41
42
  | `tower_send` | `to`, `subject`, `body`, `scope?`, `interrupt?`, `consentRef?` | Durable message to a roster name, `tower`, or `all`; delivers live to a running agent. `interrupt:true` interrupts the running turn first (Kimi-style), then delivers; `consentRef` is recorded for audit. Rate-limited by `send.maxPerMinute`. |
42
43
  | `tower_inbox` | `limit?`, `include_read?` | Read/ack messages addressed to the caller; unread messages block `tower_complete`. |
43
- | `tower_complete` | `missionId` (alias `id`), `report`, `checks?` | Worker reports completion (mission becomes `completed`, ready for preflight and optional review). Requires current owner, no open tasks/blockers, ≥1 commit, no unread inbox. `checks` records `{name, command?, exitCode?, evidence?}` evidence; `gates.requireChecks` can demand it. When configured, test discovery runs automatically and adds a `tests` check; failure throws `[tower:tests-failed]`. |
44
+ | `tower_complete` | `missionId` (alias `id`), `report`, `checks?` | Worker reports completion (mission becomes `completed`, ready for preflight and optional review). Requires current owner, no open tasks/blockers, ≥1 commit, no unread inbox. `report` is capped at `gates.maxReportBytes` (default 32768 UTF-8 bytes): a larger report is rejected with `invalid-argument` and a `nextAction` to file the full text in the mission worktree, never truncated; the stored report records `reportBytes`. `checks` records `{name, command?, exitCode?, evidence?}` evidence; `gates.requireChecks` can demand it. When configured, test discovery runs automatically and adds a `tests` check; failure throws `[tower:tests-failed]`. |
44
45
  | `tower_interrupt` | `to?` (roster name), `agentId?`, `reason?` | Interrupt one running roster agent via `subagents.interrupt`, record `state:"interrupted"`. Idempotent (not running → `interrupted:false`). |
45
46
  | `tower_finding` | `type`, `title`, `summary`, `details`, `suggested_fix`, `severity?`, `missionId?`, `location?` | Record an out-of-scope issue; reviewers use this instead of editing. Finds start as `open`. |
46
47
  | `tower_findings` | `id?`, `status?`, `note?`, filters (`type?`, `severity?`, `missionId?`, `open?`, `limit?`) | Lifecycle tool: list/read/transition. Statuses `open → triaged → fixed`/`wontfix`, reopen allowed. Only the tower or a worker may mutate; a reviewer can list/read. |
@@ -48,11 +49,11 @@ findings but not mutate them. Violations throw `[tower:not-authorized]` or
48
49
  | `tower_preflight` | `missionId` (alias `id`) | Read-only gate: returns `{ok, blockers[], branchTip, baseTip, changedFiles, review}`. |
49
50
  | `tower_merge` | `missionId` (alias `id`), `timeoutMs?` (alias `timeout_ms`) | Merge after a green gate. Records already-landed merges (`reconciled`), recovers a failed call whose commit exists (`recoveredFromFailure`), and separates `merge-hook-failed` from `merge-conflict`. |
50
51
  | `tower_rebase` | `missionId` (alias `id`) | Rebase the branch onto the base in its worktree. Refuses while the worker runs or the worktree is dirty. Already-contained branches return `alreadyMerged` untouched. |
51
- | `tower_teardown` | `force?`, `exclude?`, `dry_run?` (alias `dryRun`) | Remove eligible merged/abandoned worktrees and branches; running agents are always kept. |
52
+ | `tower_teardown` | `force?`, `exclude?`, `dry_run?` (alias `dryRun`) | Remove eligible merged/abandoned worktrees and branches; running agents are always kept. The final pass applies the `archive` policy: terminal missions whose worktree is gone are moved to `<git-common-dir>/dsh-tower/archive/` once `archive.afterDays`/`archive.maxMissions` says so, and `archived` lists their ids. Nothing is deleted; `archive.afterDays:0` archives a just-torn-down mission immediately. |
52
53
  | `tower_mode` | — | Report mode, last change, paused goal, denied tools, store path, plugin version, HMR availability, workspace-lock holder. |
53
54
  | `tower_control` | `action` (`enter`/`exit`), `reason` | Model-initiated mode change; registered by default; disable with `mode.allowAgentControl:false`; reason is mandatory. |
54
55
  | `tower_reload` | `repoRoot?` | Dev-only: ask DSH HMR to reconcile the profile and reload the Host plugin from disk. Registered while `dev.hotReload` is on; scheduled after the result (default 50 ms); fails with `reload-unavailable` without HMR. When `repoRoot` is supplied the caller must be the tower. |
55
- | `tower_doctor` | — (takes `repoRoot`) | Read-only health contract: git, state schema, config, presets/subagents/HMR, workspace lock, roster, worktree realpath/ownership, test config, leases. Returns `{ok, checks[{id,status,detail}], environmentContract}`. Never writes. |
56
+ | `tower_doctor` | — (takes `repoRoot`) | Read-only health contract: git, state schema, config, presets/subagents/HMR, workspace lock, roster, worktree realpath/ownership, test config, leases and the terminal-mission archive. Returns `{ok, checks[{id,status,detail}], stateSchema, archive, environmentContract}`; `archive` reports `{missions, archivedMissions, stateBytes, archiveBytes, years, files, unreadable, policy{enabled,afterDays,maxMissions}}`. A missing archive directory is healthy; a corrupt file is a `warn` with its diagnostic. Never writes. |
56
57
  | `tower_recover` | `action` (`status`/`clear-stale-lock`), `confirm?` | Recovery diagnostics plus an explicit stale state-lock repair. `clear-stale-lock` requires `confirm:"CLEAR_STALE_LOCK"` and only moves a lock whose PID is provably dead to a backup; it never resets state/worktrees. |
57
58
  | `tower_test` | `missionId` (alias `id`) | Run the worktree's `package.json` `tower.testDiscovery` and record a receipt; `ok:false` on failure. Use `tower_complete` for automatic admission checks. |
58
59
  | `tower_batch` | `action` (`create`/`status`/`close`/`abandon`), `missionIds?`, `exclusions?`, `requiredChecks?`, `receiptPaths?`, `batchId?`, `checks?`, `treeTip?`, `receiptHashes?`, `reason?` | Finite evidence batch. Create freezes members/checks/receipts; close verifies every required check and receipt hash; merged members are eligible; abandon retires a stale open batch without deleting missions. Membership is immutable. |
package/docs/zh/README.md CHANGED
@@ -29,7 +29,7 @@
29
29
  | 新用户 | [什么时候用 Tower](guide/scenarios-and-philosophy.md) → [快速开始](guide/quickstart.md) → [工具参考](reference/tools.md) → [第一个 mission 示例(英文)](../examples/first-mission.md) |
30
30
  | 正在选择工作流 | [Tower 与 Agent Teams、Ralph、Workflow、普通并行子代理的区别](guide/scenarios-and-philosophy.md) |
31
31
  | 运维 | [运维 runbook(英文)](../operations/runbook.md) → [blocker 与失败码](reference/blockers.md) → [错误码](reference/errors.md) → [事件](reference/events.md) → [Doctor](reference/doctor.md) → [测试](reference/testing.md) → [批次/租约/旧版](reference/operations.md) → [安全模型](operations/security.md) |
32
- | 维护者 | [架构与状态机(英文)](../architecture/overview.md) → [配置参考](reference/config.md) → [热重载](guide/hot-reload.md) → [贡献指南](../../CONTRIBUTING.md) |
32
+ | 维护者 | [架构与状态机(英文)](../architecture/overview.md) → [配置参考](reference/config.md) → [热重载](guide/hot-reload.md) → [发布检查清单(英文)](../guide/release.md) → [看板查询(英文)](../board.md) → [贡献指南](../../CONTRIBUTING.md) |
33
33
 
34
34
  参考页是当前发行版的权威说明;CI 中的 `npm run docs:check` 会校验文档与实现一致(包括中文页)。
35
35
 
@@ -51,7 +51,8 @@
51
51
  - [安全模型](operations/security.md)
52
52
 
53
53
  以下页面目前只有英文版:[架构与状态机](../architecture/overview.md)、
54
- [运维 runbook](../operations/runbook.md)、[示例:第一个 mission](../examples/first-mission.md)。
54
+ [运维 runbook](../operations/runbook.md)、[示例:第一个 mission](../examples/first-mission.md)、
55
+ [发布检查清单](../guide/release.md)、[看板查询](../board.md)。
55
56
 
56
57
  ## 语言 / Language
57
58
 
@@ -97,7 +97,7 @@ Windows 上通常是 `C:\Users\<you>\.dsh`):
97
97
  |---|---|
98
98
  | `dev.hotReload` | 注册 `tower_reload`;关闭时 `/tower reload` 也被禁用(默认 `true`)。无 HMR 服务时工具仍注册,以便返回 `reload-unavailable`。 |
99
99
  | `dev.reloadDelayMs` | 计划中的对账运行前的延迟,单位毫秒(默认 `50`),让工具/命令结果先送达模型,再替换插件。 |
100
- | `dev.reloadRequiredIds` | profile 必须保持活跃的 Loader 条目 id(默认 `tower`、`tower-skills`、`preset-tower-worker`、`preset-tower-reviewer`)。即使某个 required 条目早已处于非活跃状态,也会被报告为重载失败。 |
100
+ | `dev.reloadRequiredIds` | profile 必须保持活跃的 Loader 条目 id(默认 `tower-bundle`、`tower`、`tower-skills`、`preset-tower-worker`、`preset-tower-reviewer`)。即使某个 required 条目早已处于非活跃状态,也会被报告为重载失败。 |
101
101
 
102
102
  重载是**调度而非等待**:`/tower reload` 与 `tower_reload` 立即返回 `scheduled`、`delayMs`、
103
103
  `requiredIds`(以及存在待处理错误时的 `lastError`),随后 HMR 在延迟后对账 profile。这是有意为之——
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## 环境要求
6
6
 
7
- - DSH `0.2.1-rc.2` 或更新版本
7
+ - DSH `0.2.1-rc.2`;每个版本实际验证过的 host 构建见 [COMPATIBILITY.md](../../../COMPATIBILITY.md)
8
8
  - Node.js 22+
9
9
  - Git(本地 worktree;不需要网络访问)
10
10
 
@@ -31,6 +31,23 @@ Host 插件在 DSH 启动时加载:**安装或更新后需要重启 DSH**。
31
31
 
32
32
  ## 迁移说明
33
33
 
34
+ - **state schema v1 → v2(未发布)**:`state.json` 会自动迁移。升级后的第一次**写入**会保留
35
+ `state.json.v1.bak` 备份、`revision` 只 +1,并向 `activity.jsonl` 追加 `state.migrated {from,to}`;
36
+ 只读工具仍可读取 v1 文件且不改写它。`tower_doctor` 报告磁盘上的版本与待执行的迁移。由更新插件
37
+ 写入的文件会以 `[tower:state-version-unsupported]` 拒绝,绝不会被重写或降级;迁移抛错会报
38
+ `[tower:state-migration-failed]`,原文件保持字节不变。
39
+ - **state schema v2 → v3(未发布,#90)**:scope 匹配跟随检出目录所在文件系统的行为。迁移把
40
+ `caseInsensitive` 回填为 `null`(“尚未探测”);迁移不会读取文件系统,因此升级后的第一次
41
+ `tower_init` 会探测一次并记录 `true`/`false`。在此之前使用安全的 `case-sensitive` 比较,
42
+ `tower_doctor` 会把未知模式报告为 `warn`(`case-mode`)。在大小写不敏感的检出上,
43
+ `src/Api/**` 与 `src/api/**` 从此视为重叠,仅大小写不同的改动不再被 out-of-scope 门禁拒绝。
44
+ 存储的 scope 文本永远不会被改写:看板仍显示操作者输入的原样。
45
+ - **2.0.4 → 2.1.0-beta.1**:不需要迁移 config;state 会按上述说明自动迁移(v1/v2 → v3)。
46
+ 这是发布到 `next` dist-tag 的**预发布**,
47
+ `latest` 仍指向 2.0.4——请用 `dsh plugin --profile web add dsh-tower@next`(或精确版本号)安装。
48
+ 运行时行为未变;本版本承载的是交付闸门(CI 中的 mount smoke、`npm run release:check`、证据约定
49
+ 与追踪策略),而不是工具变化。本文档先于人工发布步骤合入:只有在[发布清单](release.md)执行
50
+ `npm publish --tag next` 之后,`next` 才会解析到 2.1.0-beta.1;`latest` 在稳定版发布前保持 2.0.4。
34
51
  - **2.0.3 → 2.0.4**:不需要迁移 state/config。本版本是安全加固与完整性修复(授权、merge SHA 绑定、
35
52
  dirty worktree 完成拒绝、stale lock 恢复、限制 test/git 进程环境、`tower_recover`、UI 0.2.2)。
36
53
  升级后请按威胁模型重新检查 `review.requireReview`、`tests.admission` 与 worker 工具权限。
@@ -26,6 +26,35 @@ Tower 提供的是**协作层面的保证**,不是沙箱。需要理解哪些
26
26
  - **建议性 workspace 锁。** `mode.workspaceLock` 防止两个 Tower 会话意外驱动同一个仓库;它是同用户
27
27
  之间的协调锁,不是用户间的认证机制。
28
28
 
29
+ ## 仓库保护
30
+
31
+ 本节描述的是**本仓库**,不是 Tower 所操作的仓库。服务端分支保护不是插件功能——Tower 只写入本地基线分支,无法配置远端——但 `dsh-tower` 为自己的 `main` 开启了保护,因为门禁代码本身也是攻击者最想修改的代码:
32
+
33
+ - `main` 的所有变更都必须经过 pull request,且**对 admin 同样生效**,因此维护者也无法直接 push。
34
+ - 必需检查为 `test (22)`、`test (24)` 与 `mount`。mount 任务会打包压缩包并在一次性 profile 中启动,所以“测试通过”不能把一个安装后不出现的 bundle 合入。
35
+ - 禁止 force-push 与删除分支,已发布 tag 的历史不能被改写。
36
+ - 不要求强制审批:只有一个维护者时,自我审批只是橡皮图章。替代它的是 two-key 路径的四行 PR 正文(failure mode、change、test、blast radius),见 [CONTRIBUTING.md](../../../CONTRIBUTING.md#landing-changes)。
37
+
38
+ 它带来的保证:未经验证的变更无法进入 `main`,必需检查变红或缺失时连所有者也不能绕过,历史只追加。它**不**提供:第二个审阅者、对泄露的维护者 token 的防护,或对 Tower 所操作仓库的任何保护——那些仍是部署关注点。设置、理由与校验命令记录在 [SECURITY.md](../../../SECURITY.md#repository-protection-on-main)。
39
+
40
+ ## 模型主动进入模式的威胁模型
41
+
42
+ `mode.allowAgentControl:true` 意味着仓库文件、issue 文本或抓取页面中的提示注入可以让模型“进入
43
+ Tower 模式并修复它”。Tower **不认证**这句话背后的人类意图。它提供的是:
44
+
45
+ - 进入会记录操作者、动作、理由与时间;`tower_status.mode.history` 直接展示最近的模式切换,
46
+ 不必手工 grep 活动日志;
47
+ - 模型不能绕过 preflight 门禁、review policy、scope、测试与 clean-base 检查;自己写 plan 也不会
48
+ 自动获得合并权;
49
+ - worker 的工具面受限并被要求留在 worktree,但主检出保护来自 scope/merge 检查与 preset,
50
+ **不是** OS 沙箱;
51
+ - `mode.workspaceLock` 防止第二个 Tower 会话驱动同一仓库。
52
+
53
+ 它**不**防御:完全通过所有门禁但被恶意指令操纵的模型;仓库自己声明的 hooks/测试命令;或无视
54
+ 指令、通过 shell 访问 worktree 之外文件的 worker。更高保证的团队应设
55
+ `mode.allowAgentControl:false`、保持 `review.requireReview:true`、在运行 Tower 前审查仓库,并用
56
+ OS 级隔离运行 host。
57
+
29
58
  ## 不由插件强制执行的约束(部署关注点)
30
59
 
31
60
  - **Worktree 隔离是协议约定,不是 OS 沙箱。** worker 会被分配独立 worktree/分支并被要求留在其中,但
@@ -45,7 +74,7 @@ Tower 提供的是**协作层面的保证**,不是沙箱。需要理解哪些
45
74
  插件。只为你信任的检出配置 HMR 监视 root;重载会执行这些文件当前的代码。
46
75
  - **提示注入。** reviewer 会读取不受信任的分支内容(diff/评论)。只读 preset 限制了影响面,但模型侧
47
76
  没有绝对防护。
48
- - **基线分支保护。** Tower 会写入本地基线分支;服务端的 branch protection/push 规则不属于本插件。
77
+ - **基线分支保护。** Tower 会写入本地基线分支;服务端 branch protection/push 规则不属于本插件。本仓库自身 `main` 的保护见[仓库保护](#仓库保护);为你运行 Tower 的仓库开启同等保护仍属于部署关注点。
49
78
 
50
79
  ## 报告问题
51
80
 
@@ -17,7 +17,7 @@
17
17
  | `branch-missing` | 还没有 worktree/分支:先 spawn worker。 |
18
18
  | `stale-base` | 分支不包含基线 tip:执行 `tower_rebase`。 |
19
19
  | `read-only-survey` | survey mission 修改了文件;survey mission 必须只读。 |
20
- | `out-of-scope` | 改动文件超出 mission scope;有意为之就扩大 scope,否则回退。 |
20
+ | `out-of-scope` | 改动文件超出 mission scope;有意为之就扩大 scope,否则回退。在大小写敏感的检出上,仅大小写不同的路径仍会被拒绝,detail 会指出折叠大小写后本可匹配的 scope 条目。 |
21
21
  | `base-dirty` | 主检出存在未提交改动,合并会覆盖它们。 |
22
22
  | `deps-unmerged` | 依赖 mission 尚未 merged/abandoned。 |
23
23
  | `no-review` | `review.requireReview:true` 且分支上没有审查:spawn reviewer(或关闭审查门禁)。 |
@@ -40,11 +40,13 @@ merge、worktree 与 spawn 策略。它使用与 profile 配置相同的 JSON
40
40
  | `allowAgentControl` | `true`(`mode.allowAgentControl` 的别名) | 兼容别名:注册 `tower_control`,允许模型切换模式。 |
41
41
  | `init.autoGitInit` | `false`(随包 patch:`true`) | 当 `repoRoot` 是绝对、已存在但不是仓库的目录时,`tower_init` 可以执行 `git init` 并创建一个空的初始提交。 |
42
42
  | `init.snapshotDirty` | `false` | `tower_init` 可以把未提交的**已跟踪**改动快照为游离提交作为 mission 基线(不含未跟踪文件;该提交通过 `refs/tower/snapshot/*` 保持存活)。 |
43
+ | `init.autoExclude` | `true` | 把仓库内产物(`MISSIONS.md`、仓库内 worktree root)追加到 `.git/info/exclude`;绝不修改 `.gitignore`。 |
43
44
  | `mode.promptSection` | `''` | 替换用的提示文案(与 `section` 同时设置时优先)。 |
44
45
  | `mode.sectionAppend` | `''` | 追加在提醒之后的额外文本(团队约定)。 |
45
46
  | `mode.allowAgentControl` | `true` | 注册 `tower_control`;模型可带理由进入/退出。设为 `false` 则仅人类可控制模式。 |
46
47
  | `mode.autoPauseGoal` | `true` | 进入模式时暂停活跃 goal;退出时不会自动恢复。 |
47
48
  | `mode.requireResumeAuthorization` | `false` | 恢复因 `pauseCause:user` 暂停的 mission 时,必须提供 `authorization` 引用。 |
49
+ | `mode.requireHumanAckOnFirstEntry` | `false` | 要求人类先执行 `/tower ack`,模型才能在当前会话首次进入 Tower 模式。 |
48
50
  | `mode.workspaceLock` | `false`(随包 patch:`true`) | 每个仓库只允许一个 Tower 会话(`tower.lock`);其他会话得到 `workspace-busy`。 |
49
51
  | `mode.workspaceLockScope` | `all` | `all` 锁定所有仓库工具;`mutations` 对只读工具跳过 workspace 锁。 |
50
52
  | `mode.planModePolicy` | `clear` | 进入 Tower 模式会清除活跃的 plan mode(或 `refuse` 直到退出计划);前置步骤也会清除稍后激活的计划。 |
@@ -80,12 +82,17 @@ merge、worktree 与 spawn 策略。它使用与 profile 配置相同的 JSON
80
82
  | `gates.requireChecks` | `false` | 合并前要求至少一条 `tower_complete.checks` 证据(`no-check-evidence`)。 |
81
83
  | `gates.requireTests` | `false` | 要求针对当前 mission 分支 tip 的通过测试回执(`tests-missing`);与审查门禁独立。 |
82
84
  | `gates.allowReviewExemption` | `false` | 在 `review.requireReview:true` 时,允许用 `reviewPolicy:"none"` + 非空理由跳过审查 blocker;默认故意关闭;全局审查关闭时无影响。 |
85
+ | `gates.maxReportBytes` | `32768`(32 KiB) | `tower_complete.report` 的 UTF-8 字节硬上限。超出时以 `invalid-argument` 拒绝并给出可执行的 `nextAction`(把全文写进 mission worktree 的文件并引用路径),绝不截断。已存在的超限报告仍可读取。 |
83
86
  | `docs.board` | `true` | 变更后写入 `MISSIONS.md`。 |
84
87
  | `docs.boardLocation` | `common` | `common` = `<git-common-dir>/dsh-tower/MISSIONS.md`;`repo` = `<repoRoot>/MISSIONS.md`。 |
88
+ | `archive.enabled` | `true` | 在变更边界把 worktree 已移除的终态(`merged`/`abandoned`)任务移入 `<git-common-dir>/dsh-tower/archive/`。归档任务保留 tasks、reviews、findings 与完成报告;这是移动,不是删除。 |
89
+ | `archive.afterDays` | `30` | 年龄触发:终态任务超过这么多天未更新,会在下次变更时归档(`0` 表示立即归档)。 |
90
+ | `archive.maxMissions` | `100` | 数量触发:热状态中的终态任务超过此数时,从最旧的开始归档直到满足上限(`0` 表示归档所有符合条件的终态任务)。worktree 仍存在的任务永不归档。 |
91
+ | `tools.exposeOperational` | `true` | 注册 batch/resource/import/legacy/recover/test/doctor 工具;`false` 可缩小固定工具 schema 上下文。 |
85
92
  | `leases.ttlMs` | `900000` | 默认咨询式租约寿命;过期不会自动释放。 |
86
93
  | `dev.hotReload` | `true` | 注册 `tower_reload`;关闭时 `/tower reload` 也被禁用。无 HMR 服务时调用返回 `reload-unavailable`。 |
87
94
  | `dev.reloadDelayMs` | `50` | 计划中的 reload 延迟,先让工具结果送达。 |
88
- | `dev.reloadRequiredIds` | 四个 Tower loader id | 作为 required 传给 `reconcileProfile` 的条目 id。 |
95
+ | `dev.reloadRequiredIds` | 五个 Tower loader id | 作为 required 传给 `reconcileProfile` 的条目 id。 |
89
96
 
90
97
  ## 示例
91
98