dsh-tower 2.5.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +114 -0
- package/COMPATIBILITY.md +2 -0
- package/CONTRIBUTING.md +1 -1
- package/GLOSSARY.md +6 -0
- package/PRINCIPLES.md +15 -7
- package/README.en.md +18 -16
- package/README.md +18 -11
- package/adapters/leases.js +20 -6
- package/adapters/legacy.js +149 -37
- package/adapters/stateFile.js +11 -1
- package/app/archive.js +30 -5
- package/app/briefing.js +95 -12
- package/app/checkCompletion.js +107 -18
- package/app/enrichedMissions.js +1 -1
- package/app/freshness.js +77 -0
- package/app/gate.js +26 -12
- package/app/merge.js +7 -4
- package/app/mission.js +24 -1
- package/app/missionContext.js +2 -1
- package/app/plan.js +64 -1
- package/app/provision.js +133 -22
- package/app/read.js +38 -5
- package/app/rebase.js +10 -8
- package/app/review.js +104 -61
- package/app/reviewCheckout.js +133 -0
- package/app/sandbox.js +121 -0
- package/app/sharedCheckout.js +90 -0
- package/app/spawn-prechecks.js +31 -20
- package/app/spawn-support.js +59 -8
- package/app/spawn.js +3 -0
- package/app/teardown.js +119 -2
- package/app/tower.js +34 -6
- package/client.js +321 -0
- package/cordis.patch.yml +55 -3
- package/docs/adr/0005-component-surface.md +58 -13
- package/docs/adr/0006-mission-shapes.md +306 -0
- package/docs/architecture/overview.md +18 -10
- package/docs/examples/first-mission.md +3 -2
- package/docs/faq.md +9 -7
- package/docs/guide/install-upgrade-migration.md +58 -7
- package/docs/guide/quickstart.md +20 -10
- package/docs/guide/scenarios-and-philosophy.md +35 -28
- package/docs/operations/runbook.md +1 -1
- package/docs/operations/security.md +11 -10
- package/docs/reference/blockers.md +20 -11
- package/docs/reference/component-surface.md +20 -8
- package/docs/reference/config.generated.md +8 -5
- package/docs/reference/config.md +30 -5
- package/docs/reference/doctor.md +7 -7
- package/docs/reference/errors.generated.md +1 -0
- package/docs/reference/errors.md +2 -1
- package/docs/reference/operations.md +1 -1
- package/docs/reference/shapes.md +123 -0
- package/docs/reference/surface-budget.md +53 -4
- package/docs/reference/testing.md +5 -3
- package/docs/reference/tools.generated.md +7 -7
- package/docs/reference/tools.md +5 -5
- package/docs/zh/faq.md +7 -5
- package/docs/zh/guide/install-upgrade-migration.md +25 -6
- package/docs/zh/guide/quickstart.md +17 -10
- package/docs/zh/guide/scenarios-and-philosophy.md +23 -15
- package/docs/zh/operations/security.md +9 -8
- package/docs/zh/reference/blockers.md +13 -9
- package/docs/zh/reference/config.md +22 -5
- package/docs/zh/reference/errors.md +2 -1
- package/docs/zh/reference/testing.md +5 -4
- package/docs/zh/reference/tools.md +3 -3
- package/domain/batch.js +75 -105
- package/domain/collab/batch.js +30 -10
- package/domain/collab/spawn.js +53 -16
- package/domain/components.js +12 -4
- package/domain/config/schema.js +87 -22
- package/domain/contracts/outputs.js +2 -1
- package/domain/contracts/tool-inputs/missions.js +10 -2
- package/domain/errors.js +6 -0
- package/domain/evidence.js +283 -0
- package/domain/gate.js +17 -34
- package/domain/integration/gate.js +17 -43
- package/domain/integration/init.js +3 -2
- package/domain/integration/provision.js +51 -0
- package/domain/integration/rebase.js +16 -1
- package/domain/integration/review.js +203 -9
- package/domain/mission.js +16 -33
- package/domain/missions/completion.js +46 -8
- package/domain/missions/findings.js +2 -1
- package/domain/missions/lifecycle.js +131 -35
- package/domain/missions/plan.js +30 -6
- package/domain/missions/review.js +2 -18
- package/domain/planning.js +5 -5
- package/domain/read/missionContext.js +17 -3
- package/domain/read/views.js +5 -1
- package/domain/roles.js +222 -0
- package/domain/roster.js +9 -3
- package/domain/scope.js +51 -0
- package/domain/shapes/build.js +254 -0
- package/domain/shapes/index.js +171 -0
- package/domain/shapes/survey.js +445 -0
- package/domain/state/archive.js +28 -4
- package/domain/state/migrations.js +111 -4
- package/domain/state/schema.js +69 -7
- package/domain/state/semantics.js +93 -13
- package/domain/state.js +5 -1
- package/domain/summary.js +5 -11
- package/domain/vocabulary.js +15 -33
- package/host/capability-registry.js +25 -9
- package/host/config.js +40 -3
- package/host/doctor.js +20 -8
- package/host/preset-survey-sandbox.js +19 -0
- package/host/preset-survey.js +18 -0
- package/host/presets.js +17 -11
- package/host/tool-registry.js +23 -13
- package/locale/en.json +16 -1
- package/locale/preset-survey/en.json +6 -0
- package/locale/preset-survey/zh.json +6 -0
- package/locale/preset-survey-sandbox/en.json +6 -0
- package/locale/preset-survey-sandbox/zh.json +6 -0
- package/locale/zh.json +16 -1
- package/package.json +19 -2
- package/ports/state.js +8 -1
- package/scripts/architecture-waivers.json +3 -3
- package/scripts/check-components.mjs +120 -6
- package/scripts/check-docs.mjs +1 -1
- package/scripts/check-pack.mjs +5 -0
- package/scripts/component-surface.json +83 -29
- package/scripts/generate-capability-locale.mjs +5 -0
- package/scripts/generate-shapes-reference.mjs +171 -0
- package/scripts/mount-smoke/surface.mjs +3 -0
- package/scripts/pack-manifest.txt +22 -0
- package/scripts/shape-audit.mjs +361 -0
- package/scripts/surface-baseline.json +75 -4
- package/scripts/types-coverage.mjs +7 -0
- package/survey-sandbox.js +48 -0
- package/survey.js +40 -0
- package/tools/complete.js +5 -5
- package/tools/diff.js +5 -3
- package/tools/plan.js +1 -1
- package/tools/preflight.js +3 -2
- package/tools/review.js +1 -1
- package/tools/spawn.js +48 -50
- package/tools/status.js +8 -1
- package/tools/test.js +2 -1
- package/types/index.d.ts +76 -5
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,120 @@
|
|
|
4
4
|
|
|
5
5
|
## 未发布(Unreleased)
|
|
6
6
|
|
|
7
|
+
## 2.7.0(2026-11-10)
|
|
8
|
+
|
|
9
|
+
### 新增
|
|
10
|
+
|
|
11
|
+
- **#148 检查任务(survey)成为独立的只读任务形状**:`tower_plan` 接受 `kind: "survey"`,
|
|
12
|
+
记录 `readCommit` 与 `readDirty`,不创建分支、工作树或提交;完成时要求 HEAD 与记录的提交一致、
|
|
13
|
+
受版本控制的文件状态不变,并接受可选的结构化结果 `{paths, symbols, notes, plan}`。
|
|
14
|
+
`tower-survey` 是独立的可选组件行,随包提供只读 preset 与可选的 detached sandbox preset;
|
|
15
|
+
survey 不占用执行代理并发预算,也不会被要求合并或 rebase。完成的 survey 现在是终态,可归档、
|
|
16
|
+
满足依赖、不再阻塞 `tower_init --reanchor`,也不再被规划为“未完成工作”。
|
|
17
|
+
- **#148 可写的 detached survey sandbox**:`survey.sandbox: "detached"` 为需要写文件的检查任务
|
|
18
|
+
提供隔离工作树(无分支,HEAD 固定在 `readCommit`),完成后清理;只读检查仍使用共享检出。
|
|
19
|
+
- **#148 依赖的新鲜度与结果传递**:`survey-stale` 警告在依赖读取落后于当前基准时出现;
|
|
20
|
+
`gates.requireFreshSurvey: true` 可将它提升为合并阻塞。依赖 briefing 会带上 survey 的
|
|
21
|
+
`readCommit` 与有界结果(路径/符号/计划/备注)。
|
|
22
|
+
- **#149 形状注册表与维度审计机制**:`domain/shapes/{index,build,survey}.js` 成为任务生命周期的
|
|
23
|
+
唯一来源;生成式 `docs/reference/shapes.md` 列出 16 个状态维度的裁决、拥有模块与机械规则;
|
|
24
|
+
`scripts/shape-audit.mjs` 的 12 条规则随 `npm run check` 运行,阻止新的 kind/role 特例与
|
|
25
|
+
第二套状态词汇。
|
|
26
|
+
- **#149 单一证据读取器**:`domain/evidence.js` 统一读取完成检查、测试发现回执与调用方检查,
|
|
27
|
+
并保留来源标签;`tower_complete` 不再复制合成 check,`tower_test` 只返回回执。
|
|
28
|
+
- **#149 旧版导入的严格适配器**:`adapters/legacy.js` 声明一种载荷对应一种格式,拒绝混合
|
|
29
|
+
mission id 命名空间,把 `unresolved` / `dropped` / `remapped` 如实写入摘要与快照记录;导入
|
|
30
|
+
仍然只读,绝不激活或恢复任务。
|
|
31
|
+
- **#149 形状拥有路径权限**:`shape.reach` 声明 `ownsPaths` 与唯一围栏,`domain/scope.js` 的
|
|
32
|
+
`scopeFenceBlocker` 成为所有门禁、规划与工作树检查的共同来源。
|
|
33
|
+
- **#149 角色与形状正交**:`domain/roles.js` 的 `ROLE_CONTRACT` 统一解析 spawn、preset、roster、
|
|
34
|
+
briefing 与预算,不再出现“角色 ∧ 形状”条件;`reviewer` 仍只绑定完成的 build 任务。
|
|
35
|
+
- **#149 审查、批次、租约与阻塞状态的契约化**:reviewer 在记录 commit 的 detached checkout 上
|
|
36
|
+
审查,裁决绑定该提交;batch 有显式的 `open → closed|abandoned` 生命周期;过期租约需要显式
|
|
37
|
+
`force` 才能被他人恢复;`blocked` 只由 blockers 派生,不能直接写入。
|
|
38
|
+
|
|
39
|
+
### 变更
|
|
40
|
+
|
|
41
|
+
- **状态 schema 从 v3 直接迁移到 v5**:`state.json` 在首次写入时自动执行 v3 → v4 → v5 迁移,
|
|
42
|
+
保留预迁移备份并只推进一次 `revision`。v4 为 survey 增加 `readCommit`/`readDirty` 字段、
|
|
43
|
+
移除 worktree/branch/baseCommit/merge 字段;v5 从 blockers 派生 `blocked` 并规范化 batch 状态。
|
|
44
|
+
只读加载仍在内存中迁移,不回写文件;计划但从未启动的旧 survey 会用 `state.base` 锚定。
|
|
45
|
+
- **配置面 57 → 56 键**:移除 `tests.autoChecks`。旧配置会收到带迁移说明的
|
|
46
|
+
`config-unknown-key`,指向 `tower_test` 的 `mission.testRuns` 回执与 `domain/evidence.js`;
|
|
47
|
+
没有替代开关。可见组件仍为 12 个(内部 4 个),工具仍为 29 个,preset 增至 4 个。
|
|
48
|
+
- **直接写 `blocked` 状态被拒绝**:`tower_mission` 不再接受 `status: "blocked"`;使用
|
|
49
|
+
`blocker` 报告阻塞,使用 `clear_blockers` 解除。`paused` 与 `blocked` 不再重叠。
|
|
50
|
+
- **survey 的合并/变基/预检/差异动作被明确拒绝**:`tower_merge`、`tower_rebase`、
|
|
51
|
+
`tower_preflight` 与 `tower_diff` 对 survey 返回可操作的 `invalid-transition`,而不是
|
|
52
|
+
合成 `tip-moved`/`no-review` 等 build 语义。
|
|
53
|
+
- **PR 模板新增形状/角色问题**,并要求说明是否新增了应属于 shape 的条件;生成的形状参考
|
|
54
|
+
与 ADR 0006 同步更新。
|
|
55
|
+
|
|
56
|
+
- **#148/#149 形状注册表 W4:证据、旧版导入、scope 与角色的一次性契约。** `domain/evidence.js`
|
|
57
|
+
成为测试证据的唯一读取者:测试发现回执记录在 `mission.testRuns` 中并按分支 tip 绑定,
|
|
58
|
+
`tower_complete` 不再把同一证据复制成合成的 `tests` check,门禁、`tower_batch` 与 completion
|
|
59
|
+
校验都走同一个读取器。删除配置键 `tests.autoChecks`(写入旧键会收到带迁移说明的
|
|
60
|
+
`config-unknown-key`),配置键 57 → 56。`adapters/legacy.js` 改为严格适配器契约:声明格式与
|
|
61
|
+
状态重映射(Tower Codex `retired` → `abandoned`)、拒绝混合 mission id 命名空间、把
|
|
62
|
+
`unresolved` / `dropped` / `remapped` 如实写入摘要与快照记录,并只向消费方暴露规范的
|
|
63
|
+
`pausedMissionIds`。shape 以 `reach` 拥有路径权限,`domain/scope.js` 的 `scopeFenceBlocker`
|
|
64
|
+
是唯一围栏;角色与 shape 正交,`domain/roles.js` 的 `ROLE_CONTRACT` 让 spawn 预检、preset、
|
|
65
|
+
roster 与 gate 各自解析,不再出现"角色 ∧ shape"条件。`shape:audit` 新增四条规则
|
|
66
|
+
`evidence-single-read`、`legacy-vocabulary`、`scope-reach`、`role-shape-conflation`。
|
|
67
|
+
- **#149 生成的形状参考改为维度清单。** `docs/reference/shapes.md` 现在按 16 个维度列出裁决
|
|
68
|
+
(shape-owned / correctly generic / redesigned)、拥有模块与机械规则;W1–W4 的裁决同时记录在
|
|
69
|
+
ADR 0006 的 W4 附录中。
|
|
70
|
+
|
|
71
|
+
### 修复
|
|
72
|
+
|
|
73
|
+
- **完成的 survey 不再死锁管理动作**:`tower_init --reanchor` 不再因一个完成的 survey 被拒绝;
|
|
74
|
+
survey 可归档并计入 `archive.maxMissions`;规划不再把它算作未完成工作。
|
|
75
|
+
- **survey 完成可正常确认收件箱**:只读与 sandbox preset 现在包含 `tower_inbox`,避免存在未读
|
|
76
|
+
消息时完成被 `completion.js` 拒绝。
|
|
77
|
+
- **旧版计划中的 survey 可迁移**:当 v2.6.0 的 survey 只有 `kind`、没有 baseCommit/worktree 时,
|
|
78
|
+
迁移用 `state.base` 与真实 tracked fingerprint 锚定,不再让每次加载都以
|
|
79
|
+
`state-migration-failed` 失败。
|
|
80
|
+
- **detached sandbox 的 briefing 使用 sandbox 路径**:不再把 `workdir` 指向主检出,并把空 scope
|
|
81
|
+
说明改为“可写 sandbox”而非“只读检查”。
|
|
82
|
+
- **归档 survey 的依赖证据不再丢失**:归档索引保留有界的 `readCommit` 与结果;依赖 briefing 与
|
|
83
|
+
`requireFreshSurvey` 在归档后仍能判断新鲜度,不会因缺少证据而失败开放。
|
|
84
|
+
- **已记录的 sandbox 决定完成路径**:`mission.sandbox` 优先于当前配置,避免运行中把 detached 改回
|
|
85
|
+
`none` 时错误删除 sandbox 或切换完成规则。
|
|
86
|
+
- **review checkout 的陈旧路径可恢复**,teardown 会清理残留的审查检出;survey 不占 build 执行
|
|
87
|
+
代理预算。
|
|
88
|
+
|
|
89
|
+
## 2.6.0(2026-11-01)
|
|
90
|
+
|
|
91
|
+
### 新增
|
|
92
|
+
|
|
93
|
+
- **#M39/#M40 审查策略成为单一三态控件**:`review.mode` 提供 `off` / `optional`(默认)/
|
|
94
|
+
`required`;插件页 `dsh-tower` 配置区只有一个三态单选组,不是组件行,也不是三个互斥行。
|
|
95
|
+
`off` 是受支持的模式:不注册 `tower_review`、拒绝 reviewer spawn、合并门禁忽略审查;
|
|
96
|
+
`optional` 记录审查但从不阻塞合并;`required` 恢复"干净且绑定分支 tip 的审查"合并门禁。
|
|
97
|
+
- **#M42 双语迁移说明**:安装、升级与迁移文档新增 2.5.0 → 2.6.0 一节,说明三种模式、控件位置、
|
|
98
|
+
旧布尔别名的映射与单版本兼容窗口、从 `off` 启动需要重启的限制,以及 `required` 对
|
|
99
|
+
`preset-tower-reviewer` 行的依赖。
|
|
100
|
+
|
|
101
|
+
### 变更
|
|
102
|
+
|
|
103
|
+
- **#M42 审查策略的组件表面重新分类(2026-11-01 决策)**:`review.mode` 是一个三态配置控件;
|
|
104
|
+
`review.requireReview` 从 `forbidden-to-disable` 改为 `core`(已弃用的单版本别名)。`required`
|
|
105
|
+
模式下的合并门禁、审查证据与豁免路径仍不可被行/布尔关闭;`preset-tower-reviewer` 仍是可关闭的
|
|
106
|
+
能力行。PRINCIPLES 原则 11 与 ADR 0005 增加 wave-5 说明;组件门禁新增"审查策略不是行、没有互斥行"
|
|
107
|
+
的检查。数量不变:54 个配置键、11 个可见组件。
|
|
108
|
+
- **#M42 文档全面改用 `review.mode`**:README、quickstart、FAQ、场景、架构、安全、测试参考与
|
|
109
|
+
reviewer 行注释不再把旧布尔写成当前用法;历史迁移记录保留。
|
|
110
|
+
- `GLOSSARY.md` 增补 review policy / 审查策略 术语与"一个三态控件,不是三个行"的文案规则。
|
|
111
|
+
|
|
112
|
+
### 修复
|
|
113
|
+
|
|
114
|
+
- **F25 reviewer 行注释过时**:`cordis.patch.yml` 不再声称"仅当旧布尔为 false 时才能关闭",
|
|
115
|
+
改为按 `review.mode` 的三态语义描述(`off`/`optional` 可关闭,`required` 时拒绝该组合)。
|
|
116
|
+
- **F26/F28 文档不再把审查策略称为"永不可关闭"**:生成的组件参考与架构总览现在只把宿主、
|
|
117
|
+
健康契约、恢复出口与合并门禁列为永不可关闭;`required` 模式下的审查门禁(证据、tip 绑定、
|
|
118
|
+
豁免路径)仍不可关闭,而策略本身是一个三态控件。
|
|
119
|
+
- **F27 测试计数同步**:CONTRIBUTING 与 COMPATIBILITY 的 2.6.0 行更新为 729 个离线测试。
|
|
120
|
+
|
|
7
121
|
## 2.5.0(2026-11-01)
|
|
8
122
|
|
|
9
123
|
### 新增
|
package/COMPATIBILITY.md
CHANGED
|
@@ -5,6 +5,8 @@ is shipped in the npm package; doctor reads it locally and never contacts a regi
|
|
|
5
5
|
|
|
6
6
|
| dsh version | tag / commit | verified on | what was run |
|
|
7
7
|
|---|---|---|---|
|
|
8
|
+
| 0.2.1-rc.2 | v2.7.0 | 2026-11-10 | mount smoke (28/28 default tools, 3 skills, 4 presets with distinct labels, 1 bundle row), `npm run test:property` (111), full static gate chain, focused shape/survey/evidence/scope/role/review/batch/leases tests, CI `test (22)`, `test (24)`, `test (22, windows)`, `mount`, `mount-fixtures` |
|
|
9
|
+
| 0.2.1-rc.2 | v2.6.0 | 2026-11-01 | mount smoke (28/28 default tools, 3 skills, 2 presets with distinct labels, 1 bundle row), `npm test` (729), `npm run test:property` (111), `npm run check` (docs + generated + component + surface + pack 294 + types + architecture + domain), `npm run smoke:mount` |
|
|
8
10
|
| 0.2.1-rc.2 | v2.5.0 | 2026-11-01 | mount smoke (28/28 default tools, 3 skills, 2 presets with distinct labels, 1 bundle row), `npm test` (696), `npm run test:property` (111), `npm run check` (docs + generated + component + surface + pack 293 + types + architecture + domain), `npm run release:check -- --mode tag --tag v2.5.0` |
|
|
9
11
|
| 0.2.1-rc.2 | v2.4.2 | 2026-10-24 | mount smoke (28/28 default tools, 3 skills, 2 presets with distinct labels, 1 bundle row), `npm test` (631), docs + seven generated checks, types coverage 148/148, components + philosophy + surface gates |
|
|
10
12
|
| 0.2.1-rc.2 | v2.4.1 | 2026-10-24 | mount smoke (28/28 default tools, 3 skills, 2 presets, 1 bundle row), `npm test` (620), `types:coverage` 146/146, docs + seven generated checks incl capability-row locales, components + philosophy + surface gates |
|
package/CONTRIBUTING.md
CHANGED
|
@@ -126,7 +126,7 @@ the push gates; CI schedules it nightly.
|
|
|
126
126
|
|
|
127
127
|
## Pull request checklist
|
|
128
128
|
|
|
129
|
-
1. `npm test` passes locally (
|
|
129
|
+
1. `npm test` passes locally (786 tests today, 785 run + 1 platform skip, including protocol end-to-end against a real temporary Git repository and mock spawn coverage), `npm run test:property` passes (111 generated property cases; also part of `npm run check`), and `npm run docs:check` passes.
|
|
130
130
|
2. New behavior has a test. Bug fixes should add a test that fails before the fix.
|
|
131
131
|
3. No secrets, tokens, personal paths, session logs, or generated artifacts are added. `.gitignore` covers local acceptance output; do not force-add it.
|
|
132
132
|
4. Keep the public API and state-file formats compatible; if a change is breaking, call it out in `CHANGELOG.md` and bump the version.
|
package/GLOSSARY.md
CHANGED
|
@@ -28,6 +28,7 @@ off-state sentence.
|
|
|
28
28
|
| preset | preset | 预设 | A shipped role definition. Never leave the English word in Chinese copy. |
|
|
29
29
|
| worktree | worktree | 工作树 | The isolated checkout a worker runs in. |
|
|
30
30
|
| review | review | 审查 | The review contract and the act of reviewing. Do not use 评审 in plugin-page copy. |
|
|
31
|
+
| review policy | review policy | 审查策略 | The one tri-state control on the plugin-page configuration form: off, optional (default) or required. It is not a component row and never three mutually exclusive rows. |
|
|
31
32
|
| lease | lease | 租约 | Advisory resource coordination. Never 咨询式租约: the lease coordinates, it does not consult. |
|
|
32
33
|
| worker | worker | 执行代理 | The role that executes a mission. "Tower Worker" is a product name and stays as-is. |
|
|
33
34
|
| reviewer | reviewer | 审查代理 | The role that independently reviews a mission. "Tower Reviewer" is a product name and stays as-is. |
|
|
@@ -65,3 +66,8 @@ is migrated separately under the same glossary.
|
|
|
65
66
|
working: a reviewer preset needed for merging is a precondition, not an
|
|
66
67
|
optional extra, and test evidence that cannot be recorded keeps merges blocked
|
|
67
68
|
until the requirement is cleared.
|
|
69
|
+
7. Describe the review policy as one tri-state control (off / optional /
|
|
70
|
+
required), never as three rows and never as a capability that is permanently
|
|
71
|
+
on. In required mode it is a merge precondition; in off mode review is
|
|
72
|
+
intentionally unavailable. The deprecated boolean alias maps true → required,
|
|
73
|
+
false → optional, and unset → optional.
|
package/PRINCIPLES.md
CHANGED
|
@@ -140,18 +140,26 @@ Enforced by: `docs/operations/incident.md`, `scripts/regression-audit.mjs`, and
|
|
|
140
140
|
A component may exist only for a capability whose off state leaves the rest of the
|
|
141
141
|
product intact. Every capability that qualifies gets a component, so the user can
|
|
142
142
|
control it. Every capability that does not — the host, the health contract, the
|
|
143
|
-
recovery escape, the
|
|
144
|
-
|
|
143
|
+
recovery escape, the merge gates — is configuration or code, never a switch: not
|
|
144
|
+
as a row, and not as a boolean that removes it.
|
|
145
|
+
|
|
146
|
+
The review policy is the deliberate middle case: it is one tri-state configuration
|
|
147
|
+
control (`off`, `optional`, `required`) on the plugin page, not a component row and
|
|
148
|
+
never one row per mode. Its off mode is documented and supported, while the review
|
|
149
|
+
gate in required mode — the clean, tip-bound review evidence and its exemption
|
|
150
|
+
path — remains non-disableable. A row may not own the policy, and the reviewer row
|
|
151
|
+
is not mutually exclusive with it.
|
|
145
152
|
|
|
146
153
|
Refused because of this: a single all-or-nothing switch offered as "simpler"; a
|
|
147
154
|
disableable `tower_doctor`/`tower_recover`; a core feature exposed as a toggle; a
|
|
148
|
-
component whose off state is worse than uninstalling the plugin
|
|
155
|
+
component whose off state is worse than uninstalling the plugin; a review policy
|
|
156
|
+
exposed as a row or as three mutually exclusive rows.
|
|
149
157
|
|
|
150
158
|
Enforced by: `scripts/check-components.mjs` (locale coverage, the
|
|
151
|
-
forbidden-to-disable denylist and the generated
|
|
152
|
-
`scripts/surface-baseline.mjs` (the component count is a
|
|
153
|
-
`docs/adr/0005-component-surface.md`, and
|
|
154
|
-
`npm run docs:check:components`.
|
|
159
|
+
forbidden-to-disable denylist, the review-policy control and the generated
|
|
160
|
+
component reference), `scripts/surface-baseline.mjs` (the component count is a
|
|
161
|
+
baselined surface), `docs/adr/0005-component-surface.md`, and
|
|
162
|
+
`tests/components.test.mjs`; gate: `npm run docs:check:components`.
|
|
155
163
|
|
|
156
164
|
## Non-goals
|
|
157
165
|
|
package/README.en.md
CHANGED
|
@@ -16,7 +16,8 @@ The repository root **is** the DSH bundle: `package.json` declares `dsh.bundle.p
|
|
|
16
16
|
|
|
17
17
|
- **Configurable Tower mode.** `/tower` toggles a session-level stance that narrows the tool surface and injects the Tower protocol. `/tower on|off <instruction>` also forwards your instruction to the model in the same step. By default the model may also enter/leave with `tower_control` and a concrete reason; set `mode.allowAgentControl:false` for human-only control.
|
|
18
18
|
- **Missions with real isolation.** Every mission gets its own branch and Git worktree; workers never touch your checkout.
|
|
19
|
-
- **
|
|
19
|
+
- **Two mission shapes, one registry.** Every mission is a `build` or a `survey`, and the shape owns its statuses, completion evidence, provisioning, path reach and spawn contract instead of scattering `if (kind === …)` branches. Surveys are read-only: they pin a `readCommit` (by default in a detached sandbox) and complete without commits, a branch or a merge. `npm run shape:audit` fails a second vocabulary, a per-kind conditional outside `domain/shapes/`, an evidence read outside `domain/evidence.js`, and a role check combined with a shape check.
|
|
20
|
+
- **Review-gated merges.** The review policy is one tri-state control on the plugin page: `off`, `optional` (the default) or `required` (`review.mode` in config). With `required`, a reviewer bound to the current branch tip must submit a `clean` / `merge` verdict before merge; the merge gate re-checks scope, staleness, task completion, review history, and worktree ownership before a `--no-ff` merge. Independent review is optional by default; use `reviewPolicy:"none"` + reason plus `gates.allowReviewExemption:true` for a single mission when the policy is required. Multi-route review is recorded per round: while a non-clean round keeps the mission `active`, a later reviewer may still submit its verdict for the same reviewed tip and it appends to that round. A completed `survey` dependency satisfies the dependency gate (a build dependency still must be merged). Upgrade note: the old boolean review toggle is a one-release alias; the mapping is in [install, upgrade and migration](docs/guide/install-upgrade-migration.md).
|
|
20
21
|
- **Durable coordination.** Roster, inbox, findings, reviews, and mission state are persisted under `.git/dsh-tower/`, so a restart does not lose the plan.
|
|
21
22
|
- **Bounded state growth.** Terminal missions whose worktree is gone move to `.git/dsh-tower/archive/` once `archive.afterDays`/`archive.maxMissions` says so, keeping their tasks, reviews, findings and completion report; mutations then rewrite only active work. `tower_status includeArchived` lists compact archived summaries, and `tower_doctor` reports archive size, count and policy.
|
|
22
23
|
- **Safe by default.** While Tower mode is active, `create_goal`, `update_goal(resume)`, `todo_write`, the Team task board, and `spawn_teammate` are refused; a paused goal is never resumed automatically.
|
|
@@ -100,10 +101,10 @@ tower_init repoRoot=D:\work\repo # records the base (current bra
|
|
|
100
101
|
tower_plan repoRoot=D:\work\repo missions=[{"title":"Feature A","scope":["src/a/**"],"tasks":["implement","commit"],"kind":"build"}]
|
|
101
102
|
tower_spawn repoRoot=D:\work\repo missionId=M1 role=worker
|
|
102
103
|
# the worker implements and commits in its worktree, calls tower_complete; its final report returns here
|
|
103
|
-
# Optional:
|
|
104
|
+
# Optional: set review.mode:required first if you need mandatory independent review
|
|
104
105
|
tower_spawn repoRoot=D:\work\repo missionId=M1 role=reviewer
|
|
105
106
|
# the reviewer inspects read-only and calls tower_review (clean+merge, or p1-Nitems/p2-Nitems)
|
|
106
|
-
tower_preflight repoRoot=D:\work\repo missionId=M1 # review optional by default; with review.
|
|
107
|
+
tower_preflight repoRoot=D:\work\repo missionId=M1 # review optional by default (review.mode:optional); with review.mode:required expect no-review before review and ok:true after
|
|
107
108
|
tower_merge repoRoot=D:\work\repo missionId=M1
|
|
108
109
|
tower_teardown repoRoot=D:\work\repo
|
|
109
110
|
|
|
@@ -138,18 +139,19 @@ planning tools, `tower_status`, and the remaining tools keep working.
|
|
|
138
139
|
|
|
139
140
|
Mode entry and `tower_plan` validate the required presets before work starts. The
|
|
140
141
|
worker preset is mandatory; the reviewer preset is required only when
|
|
141
|
-
`review.
|
|
142
|
-
optional and is locked with an explanatory `preset-missing` error when
|
|
143
|
-
is
|
|
144
|
-
`tower_doctor` and `tower_recover` are not components and
|
|
145
|
-
are registered for every configuration (#122).
|
|
142
|
+
`review.mode` is `required`, so the reviewer row may be off while the policy is
|
|
143
|
+
`off` or `optional` and is locked with an explanatory `preset-missing` error when
|
|
144
|
+
the policy is `required`. `tower-tests` off with `gates.requireTests:true` is
|
|
145
|
+
refused the same way. `tower_doctor` and `tower_recover` are not components and
|
|
146
|
+
have no config key: they are registered for every configuration (#122).
|
|
146
147
|
|
|
147
148
|
[Principle 11](PRINCIPLES.md) and
|
|
148
149
|
[ADR 0005](docs/adr/0005-component-surface.md) say a component may exist only for
|
|
149
|
-
a capability whose off state leaves the rest intact; the host, health, recovery
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
a capability whose off state leaves the rest intact; the host, health, recovery
|
|
151
|
+
and merge-gate capabilities are never switches. The review policy is the one
|
|
152
|
+
tri-state configuration control (off/optional/required), not a row. The per-key
|
|
153
|
+
answer is the generated [component surface](docs/reference/component-surface.md),
|
|
154
|
+
enforced by `npm run docs:check:components`.
|
|
153
155
|
|
|
154
156
|
## Configuration
|
|
155
157
|
|
|
@@ -164,7 +166,7 @@ generated [component surface](docs/reference/component-surface.md), enforced by
|
|
|
164
166
|
autoPauseGoal: true
|
|
165
167
|
denyTools: [create_goal, todo_write, team_task_create, team_task_update, team_task_reopen, spawn_teammate]
|
|
166
168
|
review:
|
|
167
|
-
|
|
169
|
+
mode: optional # one tri-state control: off, optional (default) or required
|
|
168
170
|
maxRoundsPerReviewer: 5
|
|
169
171
|
merge:
|
|
170
172
|
timeoutMs: 900000
|
|
@@ -179,7 +181,7 @@ generated [component surface](docs/reference/component-surface.md), enforced by
|
|
|
179
181
|
- `mode.allowAgentControl` defaults to `true` in code; the bundled `cordis.patch.yml` also sets it explicitly to `true` so the model may enter/leave when the user intent or task state warrants it, always stating a reason. Set it to `false` for human-only control. The bundled patch also enables `mode.workspaceLock` (single-active tower), `send.interruptDefault`, `init.autoGitInit`, and sets `spawn.staleMs` to 15s.
|
|
180
182
|
- `/tower base <branch>` records a session-level init base preference; `/tower on` clears an active plan mode (`mode.planModePolicy:"refuse"` instead refuses entry), keeping plan and Tower mutually exclusive.
|
|
181
183
|
- Repository-level config: put the same JSON shape in `<git-common-dir>/dsh-tower/config.json` (usually `.git/dsh-tower/config.json`) to override `review`/`merge`/`worktrees`/`spawn`. Precedence is code defaults < profile config < repository config < tool arguments. `mode.*` is session-scoped and only the profile layer controls it.
|
|
182
|
-
- Every key and default is in [docs/reference/config.md](docs/reference/config.md); unknown keys are rejected (top-level and inside a group) so typos cannot silently disable security-relevant settings. Tool error codes are in [docs/reference/errors.md](docs/reference/errors.md).
|
|
184
|
+
- Every key and default is in [docs/reference/config.md](docs/reference/config.md); unknown keys are rejected (top-level and inside a group) so typos cannot silently disable security-relevant settings. The three review modes, where the plugin-page control lives, and the one-release alias migration are in [install, upgrade and migration](docs/guide/install-upgrade-migration.md). Tool error codes are in [docs/reference/errors.md](docs/reference/errors.md).
|
|
183
185
|
|
|
184
186
|
## Tools (29: 22 core + 7 optional)
|
|
185
187
|
|
|
@@ -190,12 +192,12 @@ The list above is the 22 core tools plus 5 of the component-owned optional tools
|
|
|
190
192
|
## Safety and guardrails
|
|
191
193
|
|
|
192
194
|
- **No merge without a green gate.** `tower_preflight` is read-only and reports stable blocker codes; `tower_merge` refuses when any blocker remains.
|
|
193
|
-
- **Review is bound to a commit when
|
|
195
|
+
- **Review is bound to a commit when required.** With `review.mode:required`, a moved tip reports `tip-moved` and requires a fresh review; reviewers use a read-only preset, and a branch change during review is rejected. `off` is a documented mode that removes `tower_review`; `optional` records reviews but never blocks a merge.
|
|
194
196
|
- **Scope enforcement.** A mission may only change files matched by its declared scope globs; `out-of-scope` blocks the merge.
|
|
195
197
|
- **Worktree ownership.** Dirty worktrees, a running worker, or a branch owned by a closed mission block teardown/merge.
|
|
196
198
|
- **Goal isolation.** Entering Tower mode pauses the active goal and records it; leaving does not resume it. Use `/tower off` and resume the goal yourself.
|
|
197
199
|
|
|
198
|
-
|
|
200
|
+
31 stable blocker/failure codes (including gate guards, `no-check-evidence`, `worker-stale`, test gates, and merge/rebase operation failures) — the complete list with triggers and fixes is in [docs/reference/blockers.md](docs/reference/blockers.md). Tool-level error codes are in [docs/reference/errors.md](docs/reference/errors.md); events are in [docs/reference/events.md](docs/reference/events.md).
|
|
199
201
|
|
|
200
202
|
## How it works
|
|
201
203
|
|
package/README.md
CHANGED
|
@@ -16,7 +16,12 @@ Tower 模式与 mission/审查工作流的灵感来自 Moonshot AI 的 **Kimi Co
|
|
|
16
16
|
|
|
17
17
|
- **可配置的 Tower 模式。** `/tower` 切换会话级模式:收窄工具面并注入 Tower 协议;`/tower on|off <指令>` 还能把指令作为下一步用户消息转发给模型。默认也允许 Agent 通过 `tower_control` 带理由进出;设 `mode.allowAgentControl:false` 可改为仅人类控制。
|
|
18
18
|
- **任务级隔离。** 每个 mission 使用独立分支和 Git worktree,worker 不会碰到你的主检出。
|
|
19
|
-
-
|
|
19
|
+
- **两种 mission 形状,一个注册表。** 每个 mission 是 `build` 或 `survey`,其状态、完成证据、
|
|
20
|
+
供给方式、路径权限与 spawn 契约都由 shape 拥有,不再散落 `if (kind === …)` 分支。survey 只读:
|
|
21
|
+
固定一个 `readCommit`(默认在 detached sandbox 中)即可完成,不需要提交、分支或合并。
|
|
22
|
+
`npm run shape:audit` 会拒绝第二套词汇、`domain/shapes/` 之外的按 kind 条件、
|
|
23
|
+
`domain/evidence.js` 之外的证据读取,以及角色检查与 shape 检查混合的写法。
|
|
24
|
+
- **审查门禁合并。** 审查策略是插件页上的一个三态控件:`off`、`optional`(默认)或 `required`(配置键 `review.mode`)。设为 `required` 时,审查者针对当前分支 tip 给出 `clean` / `merge` 结论;合并门禁重新校验 scope、分支新鲜度、任务完成度、审查记录与 worktree 归属,通过后才执行 `--no-ff` 合并。独立审查默认可选;策略为 `required` 时,单个 mission 可用 `reviewPolicy:"none"` + 理由并开启 `gates.allowReviewExemption:true` 做更窄豁免。升级提示:旧的布尔审查开关是保留一个版本的别名;映射关系见[安装、升级与迁移](docs/zh/guide/install-upgrade-migration.md)。
|
|
20
25
|
- **可恢复的协作状态。** roster、inbox、findings、reviews 与 mission 状态持久化在 `.git/dsh-tower/` 下,重启不会丢计划。
|
|
21
26
|
- **受控的状态增长。** worktree 已移除的终态任务在达到 `archive.afterDays`/`archive.maxMissions` 后移入 `.git/dsh-tower/archive/`,保留 tasks、reviews、findings 与完成报告;此后 mutation 只重写活跃工作。`tower_status includeArchived` 列出压缩后的归档摘要,`tower_doctor` 报告归档大小、数量与策略。
|
|
22
27
|
- **默认安全。** Tower 模式开启期间拒绝 `create_goal`、`update_goal(resume)`、`todo_write`、Team 任务板与 `spawn_teammate`;被暂停的 goal 不会被自动恢复。
|
|
@@ -99,10 +104,10 @@ tower_init repoRoot=D:\work\repo # 首次初始化,记录 base
|
|
|
99
104
|
tower_plan repoRoot=D:\work\repo missions=[{"title":"Feature A","scope":["src/a/**"],"tasks":["implement","commit"],"kind":"build"}]
|
|
100
105
|
tower_spawn repoRoot=D:\work\repo missionId=M1 role=worker
|
|
101
106
|
# worker 在 worktree 内实现并提交,调用 tower_complete;最终报告会返回本会话
|
|
102
|
-
#
|
|
107
|
+
# 可选:需要强制独立审查时,先设 review.mode:required
|
|
103
108
|
tower_spawn repoRoot=D:\work\repo missionId=M1 role=reviewer
|
|
104
109
|
# reviewer 只读审查并调用 tower_review(clean+merge 或 p1-Nitems/p2-Nitems)
|
|
105
|
-
tower_preflight repoRoot=D:\work\repo missionId=M1 #
|
|
110
|
+
tower_preflight repoRoot=D:\work\repo missionId=M1 # 默认审查可选(review.mode:optional);设为 review.mode:required 时审查前预期 no-review,审查后 ok:true
|
|
106
111
|
tower_merge repoRoot=D:\work\repo missionId=M1
|
|
107
112
|
tower_teardown repoRoot=D:\work\repo
|
|
108
113
|
|
|
@@ -140,13 +145,15 @@ tower_teardown repoRoot=D:\work\repo
|
|
|
140
145
|
与其余工具继续工作。
|
|
141
146
|
|
|
142
147
|
进入 Tower 模式与 `tower_plan` 前会校验必需的 preset。worker preset 是强制依赖;reviewer
|
|
143
|
-
preset 只在 `review.
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
preset 只在 `review.mode` 为 `required` 时必需,因此策略为 `off` 或 `optional` 时允许关闭
|
|
149
|
+
reviewer 行,策略为 `required` 时会以带解释的 `preset-missing` 锁定。`tower-tests` 关闭且
|
|
150
|
+
`gates.requireTests:true` 时同样拒绝。`tower_doctor` 与 `tower_recover` 不是组件、也没有配置键:
|
|
151
|
+
任何配置都会注册它们(#122)。
|
|
146
152
|
|
|
147
153
|
原则 11([PRINCIPLES.md](PRINCIPLES.md))与
|
|
148
154
|
[ADR 0005](docs/adr/0005-component-surface.md) 要求:只有关闭后仍不破坏其余能力的 capability
|
|
149
|
-
才能成为组件;host
|
|
155
|
+
才能成为组件;host、健康检查、恢复通道与合并门禁永远不是开关。审查策略是唯一的
|
|
156
|
+
三态配置控件(off/optional/required),不是组件行。逐键答案见生成的
|
|
150
157
|
[组件表面](docs/reference/component-surface.md),由 `npm run docs:check:components` 强制。
|
|
151
158
|
|
|
152
159
|
## 配置
|
|
@@ -162,7 +169,7 @@ preset 只在 `review.requireReview` 为 true 时必需,因此审查可选时
|
|
|
162
169
|
autoPauseGoal: true
|
|
163
170
|
denyTools: [create_goal, todo_write, team_task_create, team_task_update, team_task_reopen, spawn_teammate]
|
|
164
171
|
review:
|
|
165
|
-
|
|
172
|
+
mode: optional # 一个三态控件:off、optional(默认)或 required
|
|
166
173
|
maxRoundsPerReviewer: 5
|
|
167
174
|
merge:
|
|
168
175
|
timeoutMs: 900000
|
|
@@ -177,7 +184,7 @@ preset 只在 `review.requireReview` 为 true 时必需,因此审查可选时
|
|
|
177
184
|
- 代码中 `mode.allowAgentControl` 默认 `true`;随包 `cordis.patch.yml` 也显式设为 `true`:模型可在用户意图/任务状态明确时自行进出,但必须给出理由;设为 `false` 时只有人类能切换。随包 patch 还默认开启 `mode.workspaceLock`(单活塔)、`send.interruptDefault`、`init.autoGitInit`,并把 `spawn.staleMs` 设为 15s。
|
|
178
185
|
- `/tower base <branch>` 可设置会话级 init base 偏好;`/tower on` 会清除活跃 plan mode(`mode.planModePolicy:"refuse"` 则拒绝进入),保证 plan/tower 互斥。
|
|
179
186
|
- 仓库级配置:在 `<git-common-dir>/dsh-tower/config.json`(通常是 `.git/dsh-tower/config.json`)放入同样的 JSON 结构,可覆盖 `review`/`merge`/`worktrees`/`spawn`;优先级为代码默认 < profile < 仓库配置 < 工具参数。`mode.*` 属于会话级,只在 profile 生效。
|
|
180
|
-
- 所有键与默认值见 [docs/reference/config.md](docs/reference/config.md)
|
|
187
|
+
- 所有键与默认值见 [docs/reference/config.md](docs/reference/config.md);未知键(顶层与分组内)都会被拒绝,防止拼写错误静默失效。三种审查模式、插件页控件位置与单版本别名迁移见[安装、升级与迁移](docs/zh/guide/install-upgrade-migration.md)。工具错误码见 [docs/reference/errors.md](docs/reference/errors.md)。
|
|
181
188
|
|
|
182
189
|
## 工具(29 个:22 个核心 + 7 个可选)
|
|
183
190
|
|
|
@@ -188,12 +195,12 @@ preset 只在 `review.requireReview` 为 true 时必需,因此审查可选时
|
|
|
188
195
|
## 安全与约束
|
|
189
196
|
|
|
190
197
|
- **门禁不全绿不合并。** `tower_preflight` 是只读检查,返回稳定的 blocker code;存在 blocker 时 `tower_merge` 拒绝合并。
|
|
191
|
-
- **审查绑定提交。**
|
|
198
|
+
- **审查绑定提交。** 策略为 `review.mode:required` 时,审查后分支 tip 变化会报 `tip-moved`,必须重新审查;reviewer 使用只读 preset,若在审查期间改动分支,提交审查会被拒绝。`off` 是受支持的模式,会移除 `tower_review`;`optional` 记录审查但从不阻塞合并。非 clean 的一轮审查会让 mission 回到 active,此后其他 route 仍可为同一 reviewed tip 提交结论并追加到同一轮;依赖为 `completed` 的 survey 视为已满足(build 依赖仍需 merged)。
|
|
192
199
|
- **scope 强制。** mission 只能修改其 scope glob 匹配的文件,越界会以 `out-of-scope` 阻止合并。
|
|
193
200
|
- **worktree 归属。** 脏工作树、运行中的 worker、已关闭 mission 的分支都会阻止合并或 teardown。
|
|
194
201
|
- **goal 隔离。** 进入 Tower 模式会暂停活跃 goal 并记录;退出不会自动恢复,需要你自行恢复。
|
|
195
202
|
|
|
196
|
-
|
|
203
|
+
31 个稳定 blocker/failure code(含 gate 守卫、`no-check-evidence`、worker-stale、tests 门禁与 merge/rebase 操作失败码)——完整清单、触发条件与修复动作见 [docs/reference/blockers.md](docs/reference/blockers.md)。工具本身抛出的错误码见 [docs/reference/errors.md](docs/reference/errors.md),事件见 [docs/reference/events.md](docs/reference/events.md)。
|
|
197
204
|
|
|
198
205
|
## 工作原理
|
|
199
206
|
|
package/adapters/leases.js
CHANGED
|
@@ -247,7 +247,7 @@ export async function acquireLease(commonDir, options = {}) {
|
|
|
247
247
|
'lease-busy',
|
|
248
248
|
`lease "${name}" is held by ${existing.holder} (pid ${existing.pid}) until ${expiresAt}` +
|
|
249
249
|
(expired
|
|
250
|
-
? `; the lease expired at ${expiresAt} but is not taken over automatically —
|
|
250
|
+
? `; the lease expired at ${expiresAt} but is not taken over automatically — an explicit tower_resource release with force: true recovers it`
|
|
251
251
|
: ''),
|
|
252
252
|
)
|
|
253
253
|
}
|
|
@@ -269,9 +269,12 @@ export async function acquireLease(commonDir, options = {}) {
|
|
|
269
269
|
}
|
|
270
270
|
|
|
271
271
|
/**
|
|
272
|
-
* Release a lease.
|
|
273
|
-
*
|
|
274
|
-
* `
|
|
272
|
+
* Release a lease. A live lease can only be released by the holder that owns
|
|
273
|
+
* its token. An expired lease is recoverable: the holder may still release it
|
|
274
|
+
* with the original token, and `force: true` lets the tower take over an
|
|
275
|
+
* EXPIRED lease whose holder crashed — that is explicit recovery, never silent
|
|
276
|
+
* takeover, and a live lease always refuses a foreign token. Returns `true` when
|
|
277
|
+
* removed and `false` when no such lease exists.
|
|
275
278
|
*/
|
|
276
279
|
export async function releaseLease(commonDir, options = {}) {
|
|
277
280
|
const name = options?.name
|
|
@@ -295,10 +298,21 @@ export async function releaseLease(commonDir, options = {}) {
|
|
|
295
298
|
const { doc } = await readLeaseDocument(commonDir)
|
|
296
299
|
const existing = doc.leases[name]
|
|
297
300
|
if (!existing) return false
|
|
301
|
+
const expired = now >= existing.expiresAt
|
|
298
302
|
if (existing.token !== token) {
|
|
299
|
-
|
|
303
|
+
if (expired && force) {
|
|
304
|
+
// Explicit recovery of a crashed holder: only an expired lease and only
|
|
305
|
+
// with `force`. Live leases below still refuse a foreign token.
|
|
306
|
+
} else {
|
|
307
|
+
fail(
|
|
308
|
+
'lease-token',
|
|
309
|
+
expired
|
|
310
|
+
? `lease "${name}" is held by ${existing.holder} (pid ${existing.pid}) and expired at ${new Date(existing.expiresAt).toISOString()}; the token does not match — pass force: true to take over the expired lease`
|
|
311
|
+
: `lease "${name}" is held by ${existing.holder} (pid ${existing.pid}) until ${new Date(existing.expiresAt).toISOString()}; a live lease can only be released by its holder`,
|
|
312
|
+
)
|
|
313
|
+
}
|
|
300
314
|
}
|
|
301
|
-
if (!force &&
|
|
315
|
+
if (!force && expired) {
|
|
302
316
|
fail(
|
|
303
317
|
'lease-busy',
|
|
304
318
|
`lease "${name}" expired at ${new Date(existing.expiresAt).toISOString()}; release it explicitly with force: true`,
|