dsh-tower 2.1.0-beta.1 → 2.3.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 +154 -0
- package/COMPATIBILITY.md +6 -1
- package/CONTRIBUTING.md +71 -5
- package/PRINCIPLES.md +145 -0
- package/README.en.md +14 -5
- package/README.md +14 -5
- package/SECURITY.md +6 -0
- package/adapters/archive.js +279 -0
- package/adapters/caseSensitivity.js +66 -0
- package/adapters/default.js +51 -8
- package/{lib → adapters}/git.js +1 -1
- package/{lib → adapters}/leases.js +15 -6
- package/{lib → adapters}/legacy.js +20 -6
- package/adapters/persistence.js +543 -0
- package/adapters/stateFile.js +99 -0
- package/adapters/store-fs.js +28 -1
- package/adapters/store.js +204 -0
- package/{lib → adapters}/tests.js +15 -7
- package/adapters/vcs-git.js +15 -1
- package/{lib → adapters}/workspaceLock.js +1 -1
- package/{lib → adapters}/worktreeOwnership.js +18 -7
- package/app/archive.js +188 -0
- package/app/batch.js +9 -10
- package/app/board.js +4 -4
- package/app/checkCompletion.js +23 -11
- package/app/enrichedMissions.js +2 -4
- package/app/findings.js +6 -7
- package/app/gate.js +32 -15
- package/app/init.js +59 -22
- package/app/merge.js +64 -51
- package/app/messages.js +7 -8
- package/app/mission.js +7 -5
- package/app/plan.js +20 -9
- package/app/provision.js +12 -14
- package/app/read.js +65 -6
- package/app/rebase.js +14 -16
- package/app/recording.js +7 -8
- package/app/review.js +17 -8
- package/app/roster.js +14 -15
- package/{lib → app}/spawn-prechecks.js +4 -3
- package/app/spawn.js +12 -13
- package/app/teardown.js +28 -8
- package/app/tower.js +121 -39
- package/docs/README.md +6 -2
- package/docs/adr/0001-ports-adapters-as-data.md +49 -0
- package/docs/adr/0002-single-state-json.md +50 -0
- package/docs/adr/0003-allow-agent-control-default-true.md +48 -0
- package/docs/adr/0004-surface-budget.md +50 -0
- package/docs/adr/README.md +51 -0
- package/docs/agents/domain.md +38 -0
- package/docs/agents/issue-tracker.md +46 -0
- package/docs/agents/triage-labels.md +15 -0
- package/docs/agents/workflow.md +51 -0
- package/docs/architecture/overview.md +170 -37
- package/docs/guide/hot-reload.md +1 -1
- package/docs/guide/install-upgrade-migration.md +18 -1
- package/docs/guide/release.md +1 -1
- package/docs/operations/incident.md +82 -0
- package/docs/operations/runbook.md +12 -3
- package/docs/reference/blockers.md +3 -3
- package/docs/reference/config.generated.md +5 -0
- package/docs/reference/config.md +4 -0
- package/docs/reference/doctor.md +104 -8
- package/docs/reference/errors.generated.md +41 -35
- package/docs/reference/errors.md +54 -40
- package/docs/reference/events.md +38 -6
- package/docs/reference/surface-budget.md +128 -0
- package/docs/reference/tools.generated.md +9 -8
- package/docs/reference/tools.md +9 -8
- package/docs/zh/guide/hot-reload.md +1 -1
- package/docs/zh/guide/install-upgrade-migration.md +13 -1
- package/docs/zh/reference/blockers.md +3 -3
- package/docs/zh/reference/config.md +4 -0
- package/docs/zh/reference/doctor.md +63 -2
- package/docs/zh/reference/errors.md +4 -2
- package/docs/zh/reference/tools.md +6 -6
- package/domain/batch.js +2 -2
- package/domain/config/schema.js +51 -4
- package/domain/contracts/outputs.js +33 -3
- package/domain/contracts/tool-inputs/missions.js +2 -1
- package/domain/contracts/tool-inputs/ops.js +1 -0
- package/domain/errors.js +297 -40
- package/domain/events.js +207 -0
- package/domain/integration/gate.js +41 -12
- package/domain/integration/init.js +40 -4
- package/domain/integration/merge.js +35 -0
- package/domain/lockBudget.js +30 -0
- package/domain/mission.js +1 -1
- package/domain/missions/completion.js +72 -5
- package/domain/missions/lifecycle.js +11 -4
- package/domain/missions/plan.js +14 -0
- package/domain/missions/review.js +90 -7
- package/domain/planning.js +6 -3
- package/domain/read/views.js +17 -1
- package/domain/report.js +9 -1
- package/domain/scope.js +83 -11
- package/domain/state/archive.js +257 -0
- package/domain/state/migrations.js +145 -0
- package/domain/state/schema.js +6 -0
- package/domain/state.js +154 -4
- package/domain/summary.js +2 -0
- package/domain/surface.js +248 -0
- package/host/doctor.js +555 -0
- package/{lib → host}/modeStore.js +1 -1
- package/{lib → host}/tool-registry.js +106 -31
- package/host/tower.js +32 -0
- package/index.js +12 -9
- package/package.json +10 -3
- package/ports/archive.js +18 -0
- package/ports/caseSensitivity.js +15 -0
- package/ports/clock.js +10 -2
- package/ports/config.js +19 -0
- package/ports/contract.js +38 -0
- package/ports/events.js +24 -0
- package/ports/ids.js +10 -2
- package/ports/index.js +54 -2
- package/ports/ownership.js +17 -0
- package/ports/state.js +16 -0
- package/ports/store.js +52 -3
- package/ports/vcs.js +25 -4
- package/scripts/architecture-waivers.json +19 -0
- package/scripts/architecture.mjs +634 -23
- package/scripts/check-docs.mjs +8 -35
- package/scripts/check-pack.mjs +68 -39
- package/scripts/check-philosophy.mjs +266 -0
- package/scripts/domain-metrics.mjs +13 -4
- package/scripts/generate-config-reference.mjs +14 -1
- package/scripts/generate-error-reference.mjs +68 -14
- package/scripts/generate-events-reference.mjs +83 -0
- package/scripts/generate-tools-reference.mjs +11 -0
- package/scripts/issue-audit.mjs +2 -2
- package/scripts/lib/surface.mjs +87 -0
- package/scripts/pack-manifest.txt +64 -19
- package/scripts/regression-audit.mjs +164 -0
- package/scripts/release-check.mjs +1 -1
- package/scripts/surface-baseline.json +21 -0
- package/scripts/surface-baseline.mjs +120 -0
- package/tools/adopt.js +2 -3
- package/tools/batch.js +2 -3
- package/tools/complete.js +3 -4
- package/tools/diff.js +3 -3
- package/tools/doctor.js +7 -10
- package/tools/finding.js +2 -3
- package/tools/findings.js +3 -3
- package/tools/import-legacy.js +3 -5
- package/tools/inbox.js +3 -3
- package/tools/index.js +7 -7
- package/tools/init.js +4 -7
- package/tools/interrupt.js +2 -3
- package/tools/legacy-map.js +3 -5
- package/tools/merge.js +3 -3
- package/tools/mission.js +2 -3
- package/tools/plan.js +7 -6
- package/tools/preflight.js +3 -3
- package/tools/rebase.js +3 -3
- package/tools/recover.js +38 -13
- package/tools/reload.js +3 -6
- package/tools/resource.js +3 -5
- package/tools/review.js +3 -4
- package/tools/roster.js +2 -3
- package/tools/send.js +2 -2
- package/tools/spawn.js +8 -5
- package/tools/status.js +16 -8
- package/tools/teardown.js +3 -4
- package/tools/test.js +2 -2
- package/types/index.d.ts +35 -1
- package/ui-bundle/README.md +37 -3
- package/ui-bundle/client.cards.js +356 -0
- package/ui-bundle/client.denied.js +142 -0
- package/ui-bundle/client.dock.js +333 -0
- package/ui-bundle/client.helpers.js +271 -0
- package/ui-bundle/client.js +79 -1747
- package/ui-bundle/client.locales.js +247 -0
- package/ui-bundle/client.mode.js +206 -0
- package/ui-bundle/client.overlay.js +64 -0
- package/ui-bundle/client.styles.js +151 -0
- package/ui-bundle/client.toolview.js +82 -0
- package/ui-bundle/package.json +1 -0
- package/lib/doctor.js +0 -270
- package/lib/persistence.js +0 -349
- package/lib/store.js +0 -118
- /package/{lib → app}/briefing.js +0 -0
- /package/{lib → app}/spawn-support.js +0 -0
- /package/{lib → host}/compatibility.js +0 -0
- /package/{lib → host}/config.js +0 -0
- /package/{lib → host}/hmr.js +0 -0
- /package/{lib → host}/modeState.js +0 -0
- /package/{lib → host}/presets.js +0 -0
package/docs/reference/doctor.md
CHANGED
|
@@ -8,26 +8,122 @@ It returns:
|
|
|
8
8
|
"ok": true,
|
|
9
9
|
"checks": [
|
|
10
10
|
{ "id": "git", "status": "pass", "detail": "git version 2.55.0" },
|
|
11
|
-
{ "id": "
|
|
11
|
+
{ "id": "surface", "status": "pass", "detail": "config keys: 64 · sections: 15 · tools: 27 (baseline 2026-10-23)" },
|
|
12
|
+
{ "id": "state", "status": "pass", "detail": ".../state.json revision 7" },
|
|
13
|
+
{ "id": "state-schema", "status": "pass", "detail": "schemaVersion 3 is current" },
|
|
14
|
+
{ "id": "case-mode", "status": "pass", "detail": "caseInsensitive: true; scope matching uses case-insensitive comparison" }
|
|
12
15
|
],
|
|
16
|
+
"surface": {
|
|
17
|
+
"configKeys": 64,
|
|
18
|
+
"sections": 15,
|
|
19
|
+
"tools": 27,
|
|
20
|
+
"baselineDate": "2026-10-23",
|
|
21
|
+
"line": "config keys: 64 · sections: 15 · tools: 27"
|
|
22
|
+
},
|
|
23
|
+
"stateLock": {
|
|
24
|
+
"held": true,
|
|
25
|
+
"pid": 4242,
|
|
26
|
+
"startedAt": 1761280000000,
|
|
27
|
+
"at": 1761280120000,
|
|
28
|
+
"alive": true,
|
|
29
|
+
"ageMs": 121000,
|
|
30
|
+
"heartbeatAgeMs": 1000,
|
|
31
|
+
"staleAfterMs": 3000,
|
|
32
|
+
"stale": false
|
|
33
|
+
},
|
|
34
|
+
"gitLock": {
|
|
35
|
+
"held": true,
|
|
36
|
+
"pid": 4242,
|
|
37
|
+
"phase": "merge",
|
|
38
|
+
"heldMs": 92000
|
|
39
|
+
},
|
|
40
|
+
"stateSchema": {
|
|
41
|
+
"currentVersion": 3,
|
|
42
|
+
"supportedVersion": 3,
|
|
43
|
+
"migrations": [],
|
|
44
|
+
"status": "current"
|
|
45
|
+
},
|
|
46
|
+
"archive": {
|
|
47
|
+
"missions": 3,
|
|
48
|
+
"archivedMissions": 12,
|
|
49
|
+
"stateBytes": 48211,
|
|
50
|
+
"archiveBytes": 190244,
|
|
51
|
+
"years": [2026],
|
|
52
|
+
"files": 2,
|
|
53
|
+
"unreadable": [],
|
|
54
|
+
"policy": { "enabled": true, "afterDays": 30, "maxMissions": 100 }
|
|
55
|
+
},
|
|
13
56
|
"environmentContract": {
|
|
14
57
|
"platform": "win32",
|
|
15
58
|
"gitVersion": "git version 2.55.0",
|
|
16
59
|
"repoRoot": "D:/repo",
|
|
17
60
|
"commonDir": "D:/repo/.git",
|
|
18
|
-
"stateDir": "D:/repo/.git/dsh-tower"
|
|
61
|
+
"stateDir": "D:/repo/.git/dsh-tower",
|
|
62
|
+
"caseInsensitive": true,
|
|
63
|
+
"comparisonMode": "case-insensitive"
|
|
19
64
|
}
|
|
20
65
|
}
|
|
21
66
|
```
|
|
22
67
|
|
|
68
|
+
`surface` (and the `surface` check) reports the concept budget in the same format
|
|
69
|
+
as the generated references: `config keys: N · sections: N · tools: N`, plus the
|
|
70
|
+
committed baseline date from `scripts/surface-baseline.json`. The counts are live
|
|
71
|
+
(the config schema and the defined tool surface, independent of the operational
|
|
72
|
+
switches); `npm run surface:check` is what fails when a count grew without an ADR
|
|
73
|
+
or a matching deletion. See
|
|
74
|
+
[surface-budget.md](surface-budget.md) for the classification table.
|
|
75
|
+
|
|
76
|
+
`stateSchema` describes the file **on disk**, not the in-memory document: `currentVersion` is the
|
|
77
|
+
`schemaVersion` doctor read, `supportedVersion` is the build's `SCHEMA_VERSION`, and `migrations` is
|
|
78
|
+
the ordered v→v path a write would apply (`[]` when current). `status` is one of `current`,
|
|
79
|
+
`migratable`, `newer`, `unmigratable`, `invalid`. Doctor never migrates, so a `migratable` file stays
|
|
80
|
+
byte-identical and the `state-schema` check is a `warn` (the next locked write applies the migration
|
|
81
|
+
and keeps a `state.json.v<N>.bak` backup). A file written by a newer plugin reports `newer` and a
|
|
82
|
+
`fail`: this build refuses to downgrade it. A state-load failure is reported as a failed check rather
|
|
83
|
+
than thrown so doctor remains useful on a broken repository.
|
|
84
|
+
|
|
85
|
+
`stateLock` (and the `state-lock` check) reports the held lock's age (`ageMs`, from the record's
|
|
86
|
+
`since`) and heartbeat freshness (`heartbeatAgeMs`, from its last `at`) against the stale window
|
|
87
|
+
(`staleAfterMs = 3 × 1000 ms`). A live PID is not proof of a live writer: a holder that stopped
|
|
88
|
+
heartbeating is reported as `stale: true` and is reclaimable by the next mutation (or by
|
|
89
|
+
`tower_recover clear-stale-lock force:true`), while a fresh heartbeat is reported as held and is never
|
|
90
|
+
stolen. This is what makes a recycled PID diagnosable without reading `state.lock` by hand.
|
|
91
|
+
|
|
92
|
+
`gitLock` (and the `git-lock` check) reports the repo-scoped git mutex: `{held, pid, phase, heldMs}`
|
|
93
|
+
while a merge/rebase/init/teardown/provision holds it, and `{held: false}` when free. A merge may hold
|
|
94
|
+
it for the configured merge timeout (15 minutes by default), so a held lock is a `warn` rather than a
|
|
95
|
+
failure; the holder's phase and age make a stalled tower diagnosable. `tower_status` returns the same
|
|
96
|
+
`gitLock` object.
|
|
97
|
+
|
|
98
|
+
`archive` reports the terminal-mission archive: active `missions`, `archivedMissions` and `stateBytes`,
|
|
99
|
+
the archive directory's `archiveBytes`/`years`/`files`, every `unreadable` diagnostic, and the configured
|
|
100
|
+
`policy` (`enabled`, `afterDays`, `maxMissions`). A missing archive directory is a healthy empty archive
|
|
101
|
+
(`archive` check `pass`); a truncated index or year file is a `warn` with the diagnostic, and archiving is
|
|
102
|
+
paused until it is repaired rather than overwriting it. Nothing is lost while the archive is unreadable:
|
|
103
|
+
the eligible missions stay in `state.json`.
|
|
104
|
+
|
|
23
105
|
Checks cover: git availability, repository identity, state schema/load, repo config presence,
|
|
24
106
|
worker/reviewer preset names, subagents and HMR services, workspace lock holder/staleness, live roster,
|
|
25
|
-
worktree existence/ownership/dirtiness, test-discovery policy,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
107
|
+
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.
|
|
108
|
+
|
|
109
|
+
`caseInsensitive` is the mode `tower_init` detected once on the checkout's filesystem; `null` means
|
|
110
|
+
"not detected yet" and the `case-mode` check is a `warn`. `comparisonMode` is the comparison scope
|
|
111
|
+
matching actually uses: `case-insensitive` folds both sides, `case-sensitive` stays byte-exact. Doctor
|
|
112
|
+
never probes — a probe creates a temporary file, and a read-only call must not write. Run
|
|
113
|
+
`tower_init` on a writable checkout to record the mode; until then the safe `case-sensitive` default
|
|
114
|
+
applies.
|
|
115
|
+
|
|
116
|
+
Checks cover: git availability, repository identity, state schema/load, the detected case mode, repo
|
|
117
|
+
config presence, worker/reviewer preset names, subagents and HMR services, state lock age/heartbeat,
|
|
118
|
+
workspace lock holder/staleness, live roster, worktree existence/ownership/dirtiness, test-discovery policy, legacy
|
|
119
|
+
snapshots, batches and leases. Missions intentionally cleaned up by `tower_teardown` are skipped (the
|
|
120
|
+
check notes the removed count) instead of being reported as missing.
|
|
121
|
+
`warn` is advisory; `fail` means the repository needs attention before spawning/merging.
|
|
122
|
+
|
|
123
|
+
The `reports` check names the largest completion report in the repository (`<bytes> B on <mission>`,
|
|
124
|
+
against the `gates.maxReportBytes` cap); the same numbers are returned as
|
|
125
|
+
`reports: { count, largest: { missionId, bytes } | null, capBytes }`. Reports written before the cap
|
|
126
|
+
only produce a `warn`; doctor never rewrites them.
|
|
31
127
|
|
|
32
128
|
The environment contract is informational; it is not an identity or authorization proof. `tower_init`
|
|
33
129
|
still requires the tower role, and `tower_spawn` still checks the live subagent catalog.
|
|
@@ -2,38 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
> Generated by `npm run docs:generate:errors` from `domain/errors.js`. Do not edit by hand.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
|
10
|
-
|
|
11
|
-
| `
|
|
12
|
-
| `invalid
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
17
|
-
| `
|
|
18
|
-
| `
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
5
|
+
The catalog is the source of truth for the stable codes and the recovery metadata the tool boundary
|
|
6
|
+
renders: `retryable` marks a failure that can succeed later unchanged, and `nextAction` is the
|
|
7
|
+
single recovery step appended to the message as ` — next: …` (with ` (retryable)` when retryable).
|
|
8
|
+
|
|
9
|
+
| Code | Meaning | Retryable | Next action |
|
|
10
|
+
|---|---|---|---|
|
|
11
|
+
| `invalid-argument` | A tool argument is missing, malformed, or not allowed. | no | — |
|
|
12
|
+
| `repo-invalid` | repoRoot is missing, relative, or not inside a Git repository. | no | pass an absolute path to a directory inside a Git repository |
|
|
13
|
+
| `not-initialized` | Tower is not initialized for this repository; run tower_init. | no | run tower_init for this repository |
|
|
14
|
+
| `mission-not-found` | No mission with that id exists. | no | list missions with tower_status |
|
|
15
|
+
| `finding-not-found` | No finding with that id exists. | no | list findings with tower_findings |
|
|
16
|
+
| `invalid-transition` | The requested state transition is not allowed. | no | — |
|
|
17
|
+
| `stale-base` | The repository state changed while the operation was being prepared; retry. | yes | retry the tool; if it keeps failing, run tower_preflight and inspect tower_status |
|
|
18
|
+
| `state-locked` | Another Tower writer holds the state lock. | yes | wait for the other Tower writer to finish, then retry |
|
|
19
|
+
| `state-corrupt` | The Tower state file is unreadable or invalid. | no | restore state.json from a backup, or re-initialize after filing the corruption |
|
|
20
|
+
| `state-version-unsupported` | The Tower state file uses a schema version this build cannot read or downgrade. | no | upgrade dsh-tower to a release that supports this state schemaVersion |
|
|
21
|
+
| `state-migration-failed` | A Tower state migration failed; the original state file was not modified. | no | restore the pre-migration backup state.json.v<N>.bak and report the migration bug |
|
|
22
|
+
| `config-invalid` | Tower configuration is invalid. | no | fix the repository config file, then retry |
|
|
23
|
+
| `config-unknown-key` | Tower configuration has an unknown top-level key. | no | remove the unknown key from the repository config file, then retry |
|
|
24
|
+
| `git-failed` | A git command failed. | no | — |
|
|
25
|
+
| `git-timeout` | A git command timed out or exceeded its output buffer. | yes | retry; if it persists, raise git.timeoutMs or merge.timeoutMs |
|
|
26
|
+
| `worktree-dirty` | The mission worktree has uncommitted changes and cannot complete. | no | commit or remove the worktree changes, then retry tower_complete |
|
|
27
|
+
| `spawn-unavailable` | The subagents service is unavailable. | no | enable the DSH subagents service, then retry |
|
|
28
|
+
| `provider-missing` | No subagent provider is registered. | no | register a subagent provider in the DSH profile, then retry |
|
|
29
|
+
| `provider-cooldown` | The provider is throttled; spawn is in a short cooldown. | yes | wait for the cooldown to expire, then retry |
|
|
30
|
+
| `preset-missing` | The configured agent preset is not registered. | no | install the tower-worker and tower-reviewer agent presets and restart DSH |
|
|
31
|
+
| `reviewer-prerequisite` | The reviewer spawn prerequisite is not met. | no | complete the mission first, then spawn the reviewer |
|
|
32
|
+
| `not-authorized` | The calling agent role may not perform this action. | no | call the tool as the tower, or as the worker/reviewer it is assigned to |
|
|
33
|
+
| `workspace-busy` | Another Tower session owns this repository workspace lock. | yes | run /tower off in the other session or wait for its lock to become stale |
|
|
34
|
+
| `worker-limit` | The repository reached spawn.maxLiveWorkers live workers. | no | wait for a live worker to finish or raise spawn.maxLiveWorkers |
|
|
35
|
+
| `rate-limited` | The tower_send per-minute rate limit was reached. | yes | wait for the next per-minute window or raise send.maxPerMinute |
|
|
36
|
+
| `takeover-refused` | The mission already has a live worker that was not stopped first. | no | run tower_interrupt for the live worker, then retry the takeover |
|
|
37
|
+
| `reload-unavailable` | The DSH HMR service is not available in this profile. | no | restart DSH instead of reloading |
|
|
38
|
+
| `reload-failed` | The HMR reload failed; check the DSH log and restart if needed. | no | check the DSH log and restart DSH |
|
|
39
|
+
| `tests-failed` | Declared test discovery failed or did not meet its admission requirements. | no | fix the failing tests, then retry tower_complete |
|
|
40
|
+
| `tests-timeout` | Test discovery exceeded its time or output budget. | no | raise tests.timeoutMs or tests.maxBytes (or speed up the suite), then retry |
|
|
41
|
+
| `lease-busy` | Another holder owns the requested advisory lease. | no | release the lease or wait for it to expire, then retry |
|
|
42
|
+
| `lease-token` | The lease release token does not match the current holder. | no | use the token returned when the lease was acquired |
|
|
43
|
+
| `batch-invalid` | The batch request or its close evidence is invalid. | no | fix the batch request or the receipt hashes, then retry |
|
|
44
|
+
| `legacy-invalid` | The legacy snapshot or mapping is invalid. | no | check the legacy snapshot path, schema and mapping, then retry |
|
|
45
|
+
| `tool-internal` | An unexpected failure; read the message and the activity log. | no | — |
|
package/docs/reference/errors.md
CHANGED
|
@@ -2,54 +2,68 @@
|
|
|
2
2
|
|
|
3
3
|
Tool failures are thrown errors, not `{ok:false}` results (gate outcomes are the exception: see
|
|
4
4
|
[blockers.md](blockers.md)). Every thrown error carries a stable code as a message prefix. This
|
|
5
|
-
release defines
|
|
5
|
+
release defines 35 tool error codes.
|
|
6
6
|
|
|
7
|
-
Every `TowerToolError`
|
|
8
|
-
(structured context or `null`) for programmatic callers
|
|
9
|
-
|
|
7
|
+
Every `TowerToolError` carries `retryable` (boolean), `nextAction` (string or `null`) and `details`
|
|
8
|
+
(structured context or `null`) for programmatic callers. The model-visible surface is the message:
|
|
9
|
+
when the catalog supplies a recovery step, exactly one trailing ` — next: …` segment is rendered;
|
|
10
|
+
when the code is also retryable, that same segment ends with ` (retryable)`. Codes without a
|
|
11
|
+
`nextAction` keep the historical `[tower:<code>] <message>` form byte-for-byte, and a message that
|
|
12
|
+
already contains its action is not suffixed twice. The table below is generated from
|
|
13
|
+
`domain/errors.js`, the same catalog the runtime uses.
|
|
10
14
|
|
|
11
15
|
```
|
|
12
|
-
[tower:mission-not-found] unknown mission "M9"
|
|
16
|
+
[tower:mission-not-found] unknown mission "M9" — next: list missions with tower_status
|
|
13
17
|
```
|
|
14
18
|
|
|
15
19
|
Callers can match the prefix instead of the prose. Codes are reserved for the conditions below; a
|
|
16
20
|
plain `Error` without a structured code is reported as `tool-internal`.
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
| `
|
|
28
|
-
| `state-locked` | Another Tower writer holds the state lock. | concurrent mutation after the lock timeout |
|
|
29
|
-
| `state-corrupt` | The Tower state file is unreadable or invalid. | hand-edited `state.json` |
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
42
|
-
| `
|
|
43
|
-
| `
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
51
|
-
| `
|
|
52
|
-
| `
|
|
22
|
+
<!-- generated:error-table:start -->
|
|
23
|
+
| Code | Meaning | Retryable | Next action | Typical trigger |
|
|
24
|
+
|---|---|---|---|---|
|
|
25
|
+
| `invalid-argument` | A tool argument is missing, malformed, or not allowed. | no | — | `missionId`/`id` omitted; unknown status; empty report; bad scope glob; `timeoutMs < 1000`; `status: "all"` on an update. |
|
|
26
|
+
| `repo-invalid` | repoRoot is missing, relative, or not inside a Git repository. | no | pass an absolute path to a directory inside a Git repository | passing a path in a directory that is not a repo |
|
|
27
|
+
| `not-initialized` | Tower is not initialized for this repository; run tower_init. | no | run tower_init for this repository | a read tool before `tower_init` |
|
|
28
|
+
| `mission-not-found` | No mission with that id exists. | no | list missions with tower_status | a stale or mistyped `M9` |
|
|
29
|
+
| `finding-not-found` | No finding with that id exists. | no | list findings with tower_findings | `tower_findings({id:"F99"})` |
|
|
30
|
+
| `invalid-transition` | The requested state transition is not allowed. | no | — | 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. |
|
|
31
|
+
| `stale-base` | The repository state changed while the operation was being prepared; retry. | yes | retry the tool; if it keeps failing, run tower_preflight and inspect tower_status | two-phase mutation revalidation |
|
|
32
|
+
| `state-locked` | Another Tower writer holds the state lock. | yes | wait for the other Tower writer to finish, then retry | concurrent mutation after the lock timeout |
|
|
33
|
+
| `state-corrupt` | The Tower state file is unreadable or invalid. | no | restore state.json from a backup, or re-initialize after filing the corruption | hand-edited `state.json`; invalid JSON; no usable `schemaVersion` |
|
|
34
|
+
| `state-version-unsupported` | The Tower state file uses a schema version this build cannot read or downgrade. | no | upgrade dsh-tower to a release that supports this state schemaVersion | opening a repository written by a newer plugin; an unknown older version; this build refuses to downgrade |
|
|
35
|
+
| `state-migration-failed` | A Tower state migration failed; the original state file was not modified. | no | restore the pre-migration backup state.json.v<N>.bak and report the migration bug | a bug in a migration step; see the pre-migration `state.json.v<N>.bak` |
|
|
36
|
+
| `config-invalid` | Tower configuration is invalid. | no | fix the repository config file, then retry | malformed repo `config.json`, a value that violates the schema |
|
|
37
|
+
| `config-unknown-key` | Tower configuration has an unknown top-level key. | no | remove the unknown key from the repository config file, then retry | a typo such as `allowAgentToggle` |
|
|
38
|
+
| `git-failed` | A git command failed. | no | — | a rejected merge command, invalid ref |
|
|
39
|
+
| `git-timeout` | A git command timed out or exceeded its output buffer. | yes | retry; if it persists, raise git.timeoutMs or merge.timeoutMs | slow pre-merge hook, huge diff |
|
|
40
|
+
| `worktree-dirty` | The mission worktree has uncommitted changes and cannot complete. | no | commit or remove the worktree changes, then retry tower_complete | staged, modified, or untracked files left behind before `tower_complete` |
|
|
41
|
+
| `spawn-unavailable` | The subagents service is unavailable. | no | enable the DSH subagents service, then retry | a Host without `subagents` |
|
|
42
|
+
| `provider-missing` | No subagent provider is registered. | no | register a subagent provider in the DSH profile, then retry | an empty provider list |
|
|
43
|
+
| `provider-cooldown` | The provider is throttled; spawn is in a short cooldown. | yes | wait for the cooldown to expire, then retry | repeated retryable provider failures; wait for `blockedUntil` |
|
|
44
|
+
| `preset-missing` | The configured agent preset is not registered. | no | install the tower-worker and tower-reviewer agent presets and restart DSH | missing `tower-worker`/`tower-reviewer` preset rows, DSH not restarted |
|
|
45
|
+
| `reviewer-prerequisite` | The reviewer spawn prerequisite is not met. | no | complete the mission first, then spawn the reviewer | reviewer spawned before the mission is `completed`, or before a worktree exists |
|
|
46
|
+
| `not-authorized` | The calling agent role may not perform this action. | no | call the tool as the tower, or as the worker/reviewer it is assigned to | a worker calling a tower-only tool or changing mission status/scope; a reviewer mutating findings |
|
|
47
|
+
| `workspace-busy` | Another Tower session owns this repository workspace lock. | yes | run /tower off in the other session or wait for its lock to become stale | `mode.workspaceLock:true` and a second session calls a repo-scoped tool |
|
|
48
|
+
| `worker-limit` | The repository reached spawn.maxLiveWorkers live workers. | no | wait for a live worker to finish or raise spawn.maxLiveWorkers | spawning a fifth worker with the default limit |
|
|
49
|
+
| `rate-limited` | The tower_send per-minute rate limit was reached. | yes | wait for the next per-minute window or raise send.maxPerMinute | `send.maxPerMinute` exceeded |
|
|
50
|
+
| `takeover-refused` | The mission already has a live worker that was not stopped first. | no | run tower_interrupt for the live worker, then retry the takeover | `tower_spawn({takeover:true})` before `tower_interrupt` |
|
|
51
|
+
| `reload-unavailable` | The DSH HMR service is not available in this profile. | no | restart DSH instead of reloading | `tower_reload` / `/tower reload` on a profile without HMR |
|
|
52
|
+
| `reload-failed` | The HMR reload failed; check the DSH log and restart if needed. | no | check the DSH log and restart DSH | `reconcileProfile` threw |
|
|
53
|
+
| `tests-failed` | Declared test discovery failed or did not meet its admission requirements. | no | fix the failing tests, then retry tower_complete | nonzero command, malformed output, minCount/required shortfall |
|
|
54
|
+
| `tests-timeout` | Test discovery exceeded its time or output budget. | no | raise tests.timeoutMs or tests.maxBytes (or speed up the suite), then retry | `tests.timeoutMs`, or stdout over `tests.maxBytes` |
|
|
55
|
+
| `lease-busy` | Another holder owns the requested advisory lease. | no | release the lease or wait for it to expire, then retry | `tower_resource acquire` while `heavy-ci` is held |
|
|
56
|
+
| `lease-token` | The lease release token does not match the current holder. | no | use the token returned when the lease was acquired | releasing with another session's token |
|
|
57
|
+
| `batch-invalid` | The batch request or its close evidence is invalid. | no | fix the batch request or the receipt hashes, then retry | unknown member, duplicate id, unsafe receipt path, missing receipt hash |
|
|
58
|
+
| `legacy-invalid` | The legacy snapshot or mapping is invalid. | no | check the legacy snapshot path, schema and mapping, then retry | unknown schema, source inside the Tower state dir, mapping rewrite |
|
|
59
|
+
| `tool-internal` | An unexpected failure; read the message and the activity log. | no | — | a bug; read the message and the activity log |
|
|
60
|
+
<!-- generated:error-table:end -->
|
|
61
|
+
|
|
62
|
+
A git-mutex timeout is a `state-locked` error that names the holder. Its `details` is
|
|
63
|
+
`{ holderPid, holderPhase, heldMs, waitedMs }`, and the message renders the same facts as
|
|
64
|
+
`held by pid 1234 (merge) for 92 s; waited 60 s`. `holderPhase` is the operation that holds the lock
|
|
65
|
+
(`merge`, `rebase`, `init`, `teardown`, `provision`); `tower_status` and `tower_doctor` expose the same
|
|
66
|
+
holder as `gitLock: { held, pid, phase, heldMs }` while it is held.
|
|
53
67
|
|
|
54
68
|
## Why thrown?
|
|
55
69
|
|
package/docs/reference/events.md
CHANGED
|
@@ -1,15 +1,42 @@
|
|
|
1
1
|
# Tower events
|
|
2
2
|
|
|
3
|
+
> Generated by `npm run docs:generate:events` from `domain/events.js`. Do not edit by hand.
|
|
4
|
+
|
|
3
5
|
Tower emits process-local Cordis events after successful tool calls (mutations and read-only tools), so
|
|
4
|
-
other plugins can observe a repository without polling. This release
|
|
5
|
-
|
|
6
|
+
other plugins can observe a repository without polling. This release defines 34 process-local events:
|
|
7
|
+
5 stable domain events and 29 deprecated tool-shaped
|
|
8
|
+
aliases. Events are **not** sent over the network and carry no telemetry.
|
|
9
|
+
|
|
10
|
+
## Domain events (stable)
|
|
11
|
+
|
|
12
|
+
Domain events describe what changed, not which tool ran. They are emitted only after the state write
|
|
13
|
+
succeeds, so `revision` is the revision a reader can load back from `state.json`; a failed tool emits
|
|
14
|
+
no domain event. `at` is the transition timestamp in epoch milliseconds. `state.json` stays
|
|
15
|
+
authoritative: these events are an observation channel, not an event-sourced log, and are never
|
|
16
|
+
replayed.
|
|
17
|
+
|
|
18
|
+
| Event | Payload | Emitted after |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| `tower.mission.planned` | `{ missionId, revision, at }` | tower_plan persisted one or more new missions |
|
|
21
|
+
| `tower.mission.completed` | `{ missionId, revision, at }` | tower_complete recorded a completion report (or tower_mission set status "completed") |
|
|
22
|
+
| `tower.mission.reviewed` | `{ missionId, revision, at }` | tower_review recorded a verdict for the mission branch |
|
|
23
|
+
| `tower.mission.merged` | `{ missionId, revision, at, commit }` | tower_merge recorded the merge commit that landed the mission branch |
|
|
24
|
+
| `tower.mission.abandoned` | `{ missionId, revision, at }` | a mission transitioned to "abandoned" |
|
|
6
25
|
|
|
7
26
|
```js
|
|
8
|
-
ctx.on('tower
|
|
9
|
-
//
|
|
27
|
+
ctx.on('tower.mission.merged', ({ missionId, revision, at, commit }) => {
|
|
28
|
+
// the merge is already durable; commit is the merge commit that landed
|
|
10
29
|
})
|
|
11
30
|
```
|
|
12
31
|
|
|
32
|
+
## Tool-shaped aliases (deprecated)
|
|
33
|
+
|
|
34
|
+
The older `tower/<tool>` events are kept for one minor release as a **deprecated** alias for the
|
|
35
|
+
tool-shaped observation channel; prefer the domain events above and expect the alias to be removed in
|
|
36
|
+
the next minor. They carry `{ tool, repoRoot, value }`, fire after a successful tool call, and never
|
|
37
|
+
fail a tool when a listener throws. For `tower_merge` the alias also fires for a gate failure whose
|
|
38
|
+
`value.ok` is false.
|
|
39
|
+
|
|
13
40
|
| Event | Emitted after |
|
|
14
41
|
|---|---|
|
|
15
42
|
| `tower/init` | `tower_init` created or re-anchored state |
|
|
@@ -38,11 +65,16 @@ ctx.on('tower/merge', (payload) => {
|
|
|
38
65
|
| `tower/resource` | `tower_resource` acquired/released/inspected leases |
|
|
39
66
|
| `tower/import_legacy` | `tower_import_legacy` stored a legacy snapshot |
|
|
40
67
|
| `tower/legacy_map` | `tower_legacy_map` mapped or listed legacy ids |
|
|
41
|
-
| `tower/mode` | `tower_mode` read
|
|
68
|
+
| `tower/mode` | `tower_mode` read the session stance (`value.mode`, `allowAgentControl`, `deniedWhileActive`) |
|
|
42
69
|
| `tower/control` | `tower_control` entered/left the mode (`value.mode`, actor/reason in `value`) |
|
|
43
70
|
| `tower/reload` | `tower_reload` returned a reload schedule/result |
|
|
44
71
|
|
|
45
|
-
|
|
72
|
+
```js
|
|
73
|
+
ctx.on('tower/merge', (payload) => {
|
|
74
|
+
// deprecated: prefer tower.mission.merged
|
|
75
|
+
// payload.tool, payload.repoRoot, payload.value (the tool's JSON result)
|
|
76
|
+
})
|
|
77
|
+
```
|
|
46
78
|
|
|
47
79
|
For a durable audit trail use `<git-common-dir>/dsh-tower/activity.jsonl` and `MISSIONS.md` instead;
|
|
48
80
|
events are process-local and are not replayed.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Surface budget
|
|
2
|
+
|
|
3
|
+
The plugin measures its user-facing surface in three numbers: **config leaf keys**,
|
|
4
|
+
**config sections**, and **registered tools**. The committed baseline is
|
|
5
|
+
[`scripts/surface-baseline.json`](../../scripts/surface-baseline.json); the brake
|
|
6
|
+
is `npm run surface:check`; the decision record is
|
|
7
|
+
[ADR 0004](../adr/0004-surface-budget.md).
|
|
8
|
+
|
|
9
|
+
Decision date: 2026-10-23
|
|
10
|
+
|
|
11
|
+
Every key and tool is classified here so "why is this here" has an answer that a
|
|
12
|
+
check can read. `core` is a behavioural or safety contract, `tuning` is a safe
|
|
13
|
+
default a user may want to change, and `niche` was added for one workflow and
|
|
14
|
+
must carry a removal note for the next minor. A live key or tool missing from
|
|
15
|
+
these tables fails `npm run surface:check`.
|
|
16
|
+
|
|
17
|
+
## Config keys
|
|
18
|
+
|
|
19
|
+
| Key | Class | Note |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| `section` | core | Legacy alias for `mode.promptSection`; changes the Tower-mode prompt contract. |
|
|
22
|
+
| `allowAgentControl` | core | Legacy alias for `mode.allowAgentControl`; decides whether the model may toggle the mode. |
|
|
23
|
+
| `init.autoGitInit` | tuning | Repository bootstrap convenience; safe default is off. |
|
|
24
|
+
| `init.snapshotDirty` | tuning | Baseline snapshot convenience; tracks dirty state explicitly. |
|
|
25
|
+
| `init.autoExclude` | tuning | Keeps generated artifacts out of user dirt without touching `.gitignore`. |
|
|
26
|
+
| `mode.promptSection` | core | Replaces the whole Tower-mode prompt text. |
|
|
27
|
+
| `mode.sectionAppend` | tuning | Appends team conventions to the prompt. |
|
|
28
|
+
| `mode.allowAgentControl` | core | The human-owned-mode toggle described by ADR 0003. |
|
|
29
|
+
| `mode.autoPauseGoal` | core | Entering the mode pauses an active goal; exit never resumes it. |
|
|
30
|
+
| `mode.requireResumeAuthorization` | core | Guards resuming a user-paused mission. |
|
|
31
|
+
| `mode.requireHumanAckOnFirstEntry` | core | First entry can require an explicit human ack. |
|
|
32
|
+
| `mode.workspaceLock` | core | One Tower session per repository is a safety contract. |
|
|
33
|
+
| `mode.workspaceLockScope` | tuning | Chooses whether read-only tools skip the workspace lock. |
|
|
34
|
+
| `mode.planModePolicy` | core | Entering Tower mode either clears plan mode or refuses. |
|
|
35
|
+
| `mode.workspaceStaleMs` | tuning | Stale window for the advisory workspace lock. |
|
|
36
|
+
| `mode.heartbeatMs` | tuning | Heartbeat interval while the mode is on. |
|
|
37
|
+
| `mode.denyTools` | core | The denied-tool list is part of the mode guard. |
|
|
38
|
+
| `mode.storePath` | niche | Custom mode-store location added for one deployment workflow. Removal note: derive the path from `$DSH_HOME` in the next minor; no in-tree caller sets this key. |
|
|
39
|
+
| `mode.maxEntries` | tuning | Bounds the persisted mode store. |
|
|
40
|
+
| `mode.lockTimeoutMs` | tuning | Bounded wait for the cross-process mode lock. |
|
|
41
|
+
| `review.requireReview` | core | Turns the review gate into a merge blocker. |
|
|
42
|
+
| `review.maxRoundsPerReviewer` | core | Caps review rounds; changes the review lifecycle. |
|
|
43
|
+
| `merge.timeoutMs` | tuning | Default merge timeout; a per-call value wins. |
|
|
44
|
+
| `merge.maxBufferBytes` | tuning | Captured merge output buffer. |
|
|
45
|
+
| `git.timeoutMs` | tuning | Default Git command timeout outside merge. |
|
|
46
|
+
| `worktrees.root` | tuning | Default worktree placement. |
|
|
47
|
+
| `spawn.workerPreset` | core | Chooses the tool surface a worker receives. |
|
|
48
|
+
| `spawn.reviewerPreset` | core | Chooses the read-only reviewer surface. |
|
|
49
|
+
| `spawn.maxDepth` | tuning | Subagent depth requested at spawn. |
|
|
50
|
+
| `spawn.provider` | tuning | Provider route for spawned agents. |
|
|
51
|
+
| `spawn.workerModel` | tuning | Optional model route for workers. |
|
|
52
|
+
| `spawn.reviewerModel` | tuning | Optional independent model route for reviewers. |
|
|
53
|
+
| `spawn.maxLiveWorkers` | core | Resource-safety cap on live workers. |
|
|
54
|
+
| `spawn.staleMs` | tuning | Roster staleness window before merge is blocked. |
|
|
55
|
+
| `spawn.maxWaitMs` | tuning | Upper bound for a blocking spawn wait. |
|
|
56
|
+
| `spawn.requireLease` | niche | Advisory-lease gate added for one multi-agent workflow. Removal note: fold into `tower_resource` policy in the next minor unless a second workflow uses it. |
|
|
57
|
+
| `send.maxPerMinute` | tuning | Message rate limit; zero disables it. |
|
|
58
|
+
| `send.interruptDefault` | tuning | Default for the interrupt-before-deliver flag. |
|
|
59
|
+
| `tests.enabled` | core | Whether declared discovery may run at all. |
|
|
60
|
+
| `tests.admission` | core | Chooses when repository code is executed. |
|
|
61
|
+
| `tests.timeoutMs` | tuning | Per-command discovery timeout. |
|
|
62
|
+
| `tests.maxBytes` | tuning | Captured discovery output cap. |
|
|
63
|
+
| `tests.autoChecks` | tuning | Attaches a tests check to completion. |
|
|
64
|
+
| `tests.allowPackageDiscovery` | core | Allows repository code to execute; a security contract. |
|
|
65
|
+
| `gates.requireChecks` | core | Requires completion evidence before merge. |
|
|
66
|
+
| `gates.requireTests` | core | Requires a test receipt before merge. |
|
|
67
|
+
| `gates.allowReviewExemption` | core | Allows a review exemption and its reason. |
|
|
68
|
+
| `gates.maxReportBytes` | tuning | Report size cap; the refusal itself is not configurable. |
|
|
69
|
+
| `docs.board` | tuning | Writes the human-readable board. |
|
|
70
|
+
| `docs.boardLocation` | tuning | Chooses the board location. |
|
|
71
|
+
| `archive.enabled` | core | Controls whether terminal missions leave the hot state. |
|
|
72
|
+
| `archive.afterDays` | tuning | Archive age threshold. |
|
|
73
|
+
| `archive.maxMissions` | tuning | Hot terminal-mission cap. |
|
|
74
|
+
| `tools.exposeOperational` | core | Master switch for the operational tool surface. |
|
|
75
|
+
| `tools.exposeBatch` | core | Registers the evidence-batch tool. |
|
|
76
|
+
| `tools.exposeImport` | core | Registers the legacy import tools. |
|
|
77
|
+
| `tools.exposeLeases` | core | Registers advisory leases. |
|
|
78
|
+
| `tools.exposeTests` | core | Registers test discovery; required when the test gate is on. |
|
|
79
|
+
| `tools.exposeRecovery` | core | Registers the stale-lock escape hatch. |
|
|
80
|
+
| `tools.exposeDoctor` | core | Registers the read-only health contract. |
|
|
81
|
+
| `leases.ttlMs` | tuning | Default advisory lease lifetime. |
|
|
82
|
+
| `dev.hotReload` | tuning | Registers the dev reload tool when HMR exists. |
|
|
83
|
+
| `dev.reloadDelayMs` | tuning | Delay so a tool result is delivered before reload. |
|
|
84
|
+
| `dev.reloadRequiredIds` | niche | Loader ids added for one linked-profile development setup. Removal note: derive the required ids from the bundle manifest in the next minor. |
|
|
85
|
+
|
|
86
|
+
## Tools
|
|
87
|
+
|
|
88
|
+
Tools are `core` when the product does not work without them and `optional` when
|
|
89
|
+
an operational switch (`tools.expose*`) or the dev mode can legitimately remove
|
|
90
|
+
them from the surface.
|
|
91
|
+
|
|
92
|
+
| Tool | Class | Note |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `tower_adopt` | core | Reconciles a restored roster with the durable catalog. |
|
|
95
|
+
| `tower_batch` | optional | Evidence-batch workflow; `tools.exposeBatch`. |
|
|
96
|
+
| `tower_complete` | core | The only completion path for a worker. |
|
|
97
|
+
| `tower_diff` | core | The read-only review view. |
|
|
98
|
+
| `tower_doctor` | optional | Read-only health contract; `tools.exposeDoctor`. |
|
|
99
|
+
| `tower_finding` | core | Out-of-scope issue recording. |
|
|
100
|
+
| `tower_findings` | core | Finding lifecycle. |
|
|
101
|
+
| `tower_import_legacy` | optional | Legacy migration; `tools.exposeImport`. |
|
|
102
|
+
| `tower_inbox` | core | Message delivery and acknowledgement. |
|
|
103
|
+
| `tower_init` | core | Creates the repository state. |
|
|
104
|
+
| `tower_interrupt` | core | Stops a runaway worker. |
|
|
105
|
+
| `tower_legacy_map` | optional | Legacy audit mapping; `tools.exposeImport`. |
|
|
106
|
+
| `tower_merge` | core | The only merge path. |
|
|
107
|
+
| `tower_mission` | core | Mission progress and blockers. |
|
|
108
|
+
| `tower_plan` | core | Mission planning. |
|
|
109
|
+
| `tower_preflight` | core | The read-only merge gate. |
|
|
110
|
+
| `tower_rebase` | core | Keeps a mission branch current. |
|
|
111
|
+
| `tower_recover` | optional | Stale-lock recovery; `tools.exposeRecovery`. |
|
|
112
|
+
| `tower_reload` | optional | Dev-only reload; registered while the HMR/dev mode is on. |
|
|
113
|
+
| `tower_resource` | optional | Advisory leases; `tools.exposeLeases`. |
|
|
114
|
+
| `tower_review` | core | Independent review verdicts. |
|
|
115
|
+
| `tower_roster` | core | Roster and live activity. |
|
|
116
|
+
| `tower_send` | core | Durable messages to agents. |
|
|
117
|
+
| `tower_spawn` | core | Starts workers and reviewers. |
|
|
118
|
+
| `tower_status` | core | The dashboard. |
|
|
119
|
+
| `tower_teardown` | core | Worktree cleanup and archiving. |
|
|
120
|
+
| `tower_test` | optional | Explicit discovery; `tools.exposeTests`. |
|
|
121
|
+
|
|
122
|
+
## Non-goals
|
|
123
|
+
|
|
124
|
+
This is a measurement and a brake, not a cleanup epic. The numbers report the
|
|
125
|
+
surface; they do not decide which key is bad.
|
|
126
|
+
|
|
127
|
+
Enforced by: `scripts/surface-baseline.mjs` and `domain/surface.js`; tests in
|
|
128
|
+
`tests/surface.test.mjs`; gate: `npm run surface:check`.
|