@twinsearth/w2m-dsh-plugin 1.3.1 → 1.4.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 (57) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +709 -0
  4. package/bin/w2m-grant.mjs +98 -0
  5. package/bin/w2m-maintenance.mjs +112 -0
  6. package/codex/mcp-servers.toml +1 -1
  7. package/deploy/maintenance/bootstrap.ps1 +30 -0
  8. package/deploy/maintenance/bootstrap.sh +61 -0
  9. package/deploy/server/w2m-maintenance.path +10 -0
  10. package/deploy/server/w2m-maintenance.service +20 -0
  11. package/deploy/windows/w2m-maintenance.xml +31 -0
  12. package/docs/COLLAB/COLLABORATION-MODEL.md +240 -0
  13. package/docs/COLLAB/EXECUTION-AUTH-MODEL.md +361 -0
  14. package/docs/COLLAB/M1-ACCEPTANCE-RUNBOOK.md +70 -0
  15. package/docs/COLLAB/M1-MAINTENANCE.md +313 -0
  16. package/docs/COLLAB/M2-TASK-GRANT.md +231 -0
  17. package/docs/COLLAB/M3-GIT-NATIVE-TASKS.md +291 -0
  18. package/docs/COLLAB/M4-PROJECT-ROOM.md +260 -0
  19. package/docs/COLLAB/M5-MULTI-CLIENT-PRIMITIVES.md +263 -0
  20. package/docs/COLLAB/M6-CROSS-NETWORK-RELIABILITY.md +231 -0
  21. package/docs/COLLAB/M7-METRICS-ACCEPTANCE.md +263 -0
  22. package/docs/COLLAB/TASK-MODEL.md +281 -0
  23. package/docs/MULTI-MACHINE-COLLABORATION-PLAN.md +186 -0
  24. package/docs/RELEASE-STABLE.md +193 -0
  25. package/docs/acceptance/1.3.10-beta.10/M1-LIVE-DIAG.json +27 -0
  26. package/docs/acceptance/1.3.10-beta.10/M1-LIVE-POSTUPDATE-DIAG.json +18 -0
  27. package/docs/acceptance/1.3.10-beta.10/M1-LIVE-RESTART.json +24 -0
  28. package/docs/acceptance/1.3.10-beta.10/M1-LIVE-UPDATE.json +24 -0
  29. package/docs/acceptance/1.3.11/M1-LIVE-DIAG.json +26 -0
  30. package/docs/acceptance/1.3.12-beta.1/M1-LIVE-DIAG.json +26 -0
  31. package/docs/acceptance/1.4.0/M1-LIVE-MAINTENANCE.json +20 -0
  32. package/docs/acceptance/1.4.0/REGRESSION-1.4.0.json +81 -0
  33. package/package.json +26 -5
  34. package/plugin.json +1 -1
  35. package/src/acceptance/evidence.mjs +79 -0
  36. package/src/agent/agent.mjs +141 -3
  37. package/src/agent/grant-gate.mjs +89 -0
  38. package/src/agent/maintenance-ferry.mjs +111 -0
  39. package/src/agent/outbox.mjs +138 -0
  40. package/src/agent/patch-artifact.mjs +53 -0
  41. package/src/agent/worktree.mjs +118 -0
  42. package/src/maintenance/actions.mjs +203 -0
  43. package/src/maintenance/grant.mjs +139 -0
  44. package/src/maintenance/operator-key.mjs +151 -0
  45. package/src/maintenance/protocol.mjs +204 -0
  46. package/src/maintenance/runner.mjs +189 -0
  47. package/src/plugin/tools.mjs +1 -1
  48. package/src/relay/blob-sessions.mjs +148 -0
  49. package/src/relay/merge-queue.mjs +137 -0
  50. package/src/relay/project-locks.mjs +179 -0
  51. package/src/relay/project-notifications.mjs +184 -0
  52. package/src/relay/project-refs.mjs +110 -0
  53. package/src/relay/project-reviews.mjs +161 -0
  54. package/src/relay/project-room.mjs +163 -0
  55. package/src/relay/reliability-metrics.mjs +180 -0
  56. package/src/relay/server.mjs +382 -1
  57. package/src/relay/state.mjs +308 -1
@@ -0,0 +1,263 @@
1
+ # M7 指标与验收设计规格
2
+
3
+ > 状态:**设计稿**。只定义指标口径、验收体系与门禁,不含实现。
4
+ > 版本:`1.3.8-beta.*`(GitHub prerelease,不入 npm)。
5
+ > 上游:`docs/MULTI-MACHINE-COLLABORATION-PLAN.md` M7;覆盖 M1–M6 与三个模型(v1.4.x)的验收。
6
+
7
+ ---
8
+
9
+ ## 1. 目标与非目标
10
+
11
+ ### 目标
12
+
13
+ 1. **每个能力都必须能被真实任务触发**:不允许"单测过了就算完成";验收记录必须能追溯到具体 task_id、机器、时间、ledger 区间。
14
+ 2. **负向测试是必选项**:每个模块的错误码都必须有一条自动化用例证明"该拒绝时确实拒绝,且没有副作用"。
15
+ 3. **指标统一口径**:任务成功率、重试/重派、分块断点、outbox、事件延迟、锁冲突、审查时效、合并冲突、P2P 成功率等,全部由 relay 真实数据计算,不靠客户端自报。
16
+ 4. **门禁可执行**:preflight 增加"验收证据存在且可校验"的检查;beta 与 stable 的验收门槛不同,写死为门禁而不是口头约定。
17
+ 5. **证据可留存、可复现**:验收产物(evidence)进仓库固定目录,含命令、结果、sha、ledger 区间,后续版本可回放核对。
18
+
19
+ ### 非目标
20
+
21
+ - 不做外部 APM/监控平台接入(本版本只暴露指标 + 本地快照)。
22
+ - 不做性能压测基线(M7 只做正确性与混沌验收;吞吐性能另立)。
23
+ - 不追求 100% 自动化真实机器测试(允许"人工执行 + 证据上传",但证据格式与校验必须统一)。
24
+
25
+ ---
26
+
27
+ ## 2. "真实任务可触发"的定义(验收硬标准)
28
+
29
+ 一条能力算通过,必须同时满足:
30
+
31
+ ```
32
+ 1. 存在一个真实 task_id(relay 可查、结果可复现)
33
+ 2. 涉及 ≥1 台真实机器(不是进程内 mock;单机能力可 1 台)
34
+ 3. 结果通过 relay 聚合可验证(outcome/status/字段断言)
35
+ 4. sanitized 证据入仓库:docs/acceptance/<version>/<case>.json + 关联 artifact
36
+ 5. 负向用例证明"拒绝路径正确且无副作用"(head/任务/文件未变)
37
+ 6. 能在另一台机器/另一时间重跑(命令、版本、前置条件齐全)
38
+ ```
39
+
40
+ 证据文件格式(统一):
41
+
42
+ ```json
43
+ {
44
+ "version": "1.3.2-beta.1",
45
+ "case": "M1-UPDATE-EMPTY-ALLOWLIST",
46
+ "kind": "positive|negative|chaos",
47
+ "requirement": "空 allow-list 机器远程升级",
48
+ "machines": ["1c14d7d2-…", "6c92b251-…"],
49
+ "commands": [["node", "bin/w2m-maintenance.mjs", "--only-check"]],
50
+ "task_id": "01M6…",
51
+ "ledger_seq": [100, 104],
52
+ "result": { "outcome": "ok", "assertions": { "version": "1.3.2-beta.1" } },
53
+ "artifacts": [{ "sha256": "…", "bytes": 1234 }],
54
+ "executed_at": "2026-10-21T14:00:00Z",
55
+ "executor": "lead|human",
56
+ "reproduce": "docs/acceptance/<version>/RUNBOOK.md#M1-UPDATE-EMPTY-ALLOWLIST"
57
+ }
58
+ ```
59
+
60
+ ---
61
+
62
+ ## 3. 指标口径(统一由 relay 计算)
63
+
64
+ ### 3.1 任务与调度
65
+
66
+ ```
67
+ wm_tasks_total{status, mode}
68
+ wm_task_duration_ms{status, mode} p50/p95/p99(直方图)
69
+ wm_task_requeued_total 重派发次数(reconnect/lease 过期)
70
+ wm_lease_expired_total{reason}
71
+ wm_result_duplicate_total 重复结果被去重的次数(应为 0 效果、>0 收到)
72
+ wm_dependency_released_total
73
+ ```
74
+
75
+ ### 3.2 授权(M1/M2)
76
+
77
+ ```
78
+ wm_grant_issued_total{machine_scope}
79
+ wm_grant_denied_total{code} 各 GRANT_* 拒绝码计数
80
+ wm_grant_expired_total
81
+ wm_maintenance_requests_total{action,outcome}
82
+ wm_maintenance_rollback_total
83
+ ```
84
+
85
+ ### 3.3 数据面(M3/M6)
86
+
87
+ ```
88
+ wm_blob_upload_sessions_total{state}
89
+ wm_blob_chunks_total{retried}
90
+ wm_blob_resume_total
91
+ wm_blob_bytes_total{mode}
92
+ wm_blob_sha_mismatch_total
93
+ wm_agent_outbox_depth
94
+ wm_agent_outbox_dropped_total
95
+ wm_worktree_active / wm_worktree_cleaned_total
96
+ wm_merge_queue_depth{project,branch}
97
+ wm_merge_conflict_total
98
+ wm_merge_merged_total
99
+ ```
100
+
101
+ ### 3.4 协作(M4/M5)
102
+
103
+ ```
104
+ wm_project_events_total{type}
105
+ wm_event_lag_ms{client} 客户端应用时间 - 事件时间(p95)
106
+ wm_sse_reconnects_total
107
+ wm_lock_acquired_total{kind} / wm_lock_conflict_total / wm_lock_force_released_total
108
+ wm_review_pending / wm_review_decision_total{decision}
109
+ wm_review_latency_ms 创建→首个结论(p95)
110
+ wm_notification_sent_total{kind} / wm_notification_digest_total / wm_notification_unread
111
+ ```
112
+
113
+ ### 3.5 传输(M6)
114
+
115
+ ```
116
+ wm_p2p_push_attempts_total / wm_p2p_push_success_total / wm_p2p_fallback_total
117
+ wm_p2p_crypto_refusals_total
118
+ wm_relay_restarts_total(relay 启动时从持久化文件推断或写入)
119
+ ```
120
+
121
+ ### 3.6 暴露方式
122
+
123
+ - `GET /metrics`(Prometheus 文本格式,已有路由形态可复用)或 `w2m_stats` 聚合视图。
124
+ - 指标只存计数器/直方图聚合(内存 + 定期快照到 state),不存原始事件。
125
+ - 标签基数限制:machine_id 可入标签,task_id 不入(避免爆炸)。
126
+
127
+ ---
128
+
129
+ ## 4. 验收体系分层
130
+
131
+ | 层 | 名称 | 运行环境 | 强制程度 |
132
+ |---|---|---|---|
133
+ | L0 | 单元/纯函数 | 任意 | beta 必过(已有 node --test) |
134
+ | L1 | 组件集成 | 本地 fake relay + 真 agent 进程 | beta 必过 |
135
+ | L2 | 真实任务 | 编队真机(jp-shared / TwinsEarth / macmini / ToFuture) | beta 子集、stable 全量 |
136
+ | L3 | 混沌 | 本地/真机断网、杀进程、重启、配额 | beta 抽测、stable 必过 |
137
+ | N | 负向矩阵 | 随每模块交付 | 全部版本必过 |
138
+
139
+ ---
140
+
141
+ ## 5. 验收矩阵(按版本)
142
+
143
+ | 版本 | 模块 | L2 正向(真实任务) | N 负向(必须自动化) | L3 混沌 |
144
+ |---|---|---|---|---|
145
+ | 1.3.2-beta | M1 | 空 allow-list 机器远程 update/restart/diag;relay 升级 | 未签/错签/过期/换机/重放/越界 action/sha 不符/包内版本不符/篡改 | 升级中断回滚;relay 重启后恢复 |
146
+ | 1.3.3-beta | M2 | 空 allow-list 执行 node -e;多机 grant;撤销生效 | GRANT_* 全矩阵;重放;超 max_uses;旧 agent | 撤销后立即拒绝;重启后计数保留 |
147
+ | 1.3.4-beta | M3 | 两机并行改不同文件 → 合并;同文件 → MERGE_CONFLICT | 篡改 patch/写 .git/非合并器/force push/中断恢复 | 合并中断后锁恢复;重放幂等 |
148
+ | 1.3.5-beta | M4 | 双客户端同任务板/制品/决策;断线补齐 | 非成员/observer 越权/seq 缺口/重复事件 | relay 重启 seq 连续 |
149
+ | 1.3.6-beta | M5 | 锁冲突/过期/强制释放;审查通过/拒绝;离线通知补齐 | 并发抢锁/非 holder/自审/重复结论/通知风暴 | 锁过期重入;通知限流不丢 |
150
+ | 1.3.7-beta | M6 | 40MiB 分块上传下载;断点续传;outbox 恢复 | BLOB_* 全矩阵;outbox 满;P2P 明文回落 | relay/agent 重启;断网 30/60s |
151
+ | 1.3.8-beta | M7 | 验收证据可执行、指标可读;跑一遍前六项 | 证据缺失/伪造/过期 → preflight 拒绝 | 指标在混沌下保持单调/无倒挂 |
152
+
153
+ ---
154
+
155
+ ## 6. 测试与混沌基础设施
156
+
157
+ ### 6.1 本地
158
+
159
+ - **fake relay**(已有 `startFakeRabbit`):L1 用;扩展支持 blob-sessions/项目房间路由。
160
+ - **双机 harness**(已有 `test/agent.test.mjs` 与部署脚本):L1 两 agent + 一个 relay。
161
+ - **chaos 工具**(新增,纯 JS 优先,避免外部依赖):
162
+ - `pause(agent)` / `kill(agent)` / `kill relay`(进程级)
163
+ - 网络中断:本地代理(node net server 转发,可主动断开)模拟断网/丢包/慢速
164
+ - 时钟偏移:注入 `now()`(部分模块已支持)
165
+ - 配额耗尽:直接填满 blob/outbox/锁上限
166
+ - 所有 chaos 用例必须有确定性断言(轮询超时而非 sleep 固定值)。
167
+
168
+ ### 6.2 真机(L2)
169
+
170
+ - 机器池:`jp-shared`(Linux)、`TwinsEarth`(Windows)、`macmini`(macOS)、`ToFuture`(Windows)。
171
+ - 前置:M1/M2 完成后通过 grant 授权执行;不能在验收中放宽 allow-list(验收本身要证明"不放宽也能做")。
172
+ - 数据安全:M3 验收使用**临时测试仓库**(本地 bare 或专用 demo repo),不碰本插件发布仓库的 main。
173
+ - 清理:验收后停服务/删临时 worktree/撤销 grant,脚本化。
174
+
175
+ ---
176
+
177
+ ## 7. 门禁集成(preflight 扩展)
178
+
179
+ 新增检查项(注册进 preflight 的 15 项清单,替换/扩展现有"declared suite"):
180
+
181
+ ```
182
+ 1. acceptance-evidence:docs/acceptance/<current_version>/ 存在;每个 case JSON 通过 schema 校验;
183
+ task_id 能在当前 ledger/relay 中查到(或标记 archived 并带哈希);artifact sha 可下载且匹配。
184
+ 2. negative-matrix:每个已声明错误码在证据中有至少一条 negative case;缺一个即 RED。
185
+ 3. metrics-smoke:/metrics(或 stats)包含全部声明指标名;无重复/无非法标签。
186
+ 4. chaos-lite(stable 门禁):本地 chaos 用例全过;beta 只需抽测并记录。
187
+ 5. docs-claims:与现有 claims 检查合并,新增文档必须被索引(§9)。
188
+ ```
189
+
190
+ beta / stable 门槛:
191
+
192
+ ```
193
+ beta :L0+L1 全过 + 本版本 L2 正向至少 1 条 + 对应 N 负向全过 + 证据入库
194
+ stable:beta 全部 + L2 全量 + L3 本版本相关用例全过 + 上版本回归
195
+ ```
196
+
197
+ ---
198
+
199
+ ## 8. 验收证据的保存与索引
200
+
201
+ ```
202
+ docs/acceptance/README.md 索引(版本 → case 列表 → 状态)
203
+ docs/acceptance/<version>/RUNBOOK.md 可复现步骤(命令/前置/期望)
204
+ docs/acceptance/<version>/<case>.json 机器可读证据
205
+ docs/acceptance/<version>/artifacts/ 可选小产物(大产物只存 sha,走 blob/ release)
206
+ ```
207
+
208
+ 规则:
209
+
210
+ - 证据必须由 `scripts/acceptance.mjs`(新增)生成骨架,人工填结果后校验,避免手写非法 JSON。
211
+ - 每次发布(beta/stable)时,CHANGELOG 对应条目必须引用该版本的 acceptance 目录。
212
+ - 证据随版本冻结;后续修改以新 case 追加,不改历史。
213
+
214
+ ---
215
+
216
+ ## 9. 文档与声明的一致性
217
+
218
+ - 新文档(含本系列设计稿)必须登记进 `docs/acceptance/README.md` 或 `docs/INDEX`(实现时按现有 claims 机制选择),否则门禁拒绝。
219
+ - **能力级别口径修正**(M7 一并落地):
220
+ - "多机派发/聚合"(已实现)
221
+ - "多机协同开发"(M1–M6 完成后才算)
222
+ - "多客户端共享项目"(M4–M5 完成后才算)
223
+ - README/CHANGELOG 不再用"多机协同"泛称覆盖未完成能力。
224
+
225
+ ---
226
+
227
+ ## 10. 风险与对策
228
+
229
+ | 风险 | 对策 |
230
+ |---|---|
231
+ | 真机验收不可重复(机器掉线/环境漂移) | 证据带机器快照(版本/OS/caps);允许同 case 重跑覆盖(保留历史);关键 case 至少两台机器 |
232
+ | 测试变慢导致门禁不可用 | 分层:beta 只跑子集;stable 全量;chaos 用确定性轮询而非 sleep |
233
+ | 指标本身不可信 | 指标由 relay 计算;对关键指标加自检(如 task 计数 = ledger 事件计数校验) |
234
+ | 证据造假/过期 | task_id + ledger 区间 + artifact sha 三重校验;过期(>90 天)标记 stale 并在门禁提示 |
235
+ | 测试污染生产数据 | 验收专用项目/临时仓库;grant 限时;验收后清理脚本 |
236
+ | 时长失控(六模块验收) | 每个模块 ≥1 条正向 + 全负向矩阵;完整矩阵按版本 incremental 累加 |
237
+
238
+ ---
239
+
240
+ ## 11. 交付物清单(实现阶段)
241
+
242
+ ```
243
+ scripts/acceptance.mjs 证据骨架生成/校验/汇总
244
+ scripts/chaos.mjs 进程/网络/配额混沌工具(本地)
245
+ scripts/metrics-check.mjs 指标名与自检
246
+ src/relay/metrics.mjs 新增 M1–M6 计数器/直方图
247
+ src/relay/server.mjs /metrics 暴露(或复用现有路由)
248
+ scripts/preflight.mjs 新增三项检查(evidence/negative/metrics)
249
+ docs/acceptance/README.md 索引
250
+ docs/acceptance/<version>/… 每个 beta 的证据
251
+ test/acceptance-schema.test.mjs 证据 schema 校验(无证据即失败)
252
+ test/metrics.test.mjs 指标名/单调/自检
253
+ docs/COLLAB/M7-METRICS-ACCEPTANCE.md 本文
254
+ ```
255
+
256
+ ## 12. 开放问题(实现前确认)
257
+
258
+ 1. **真机验收执行方式**:人工执行 + 证据上传,还是配置 self-hosted runner 自动跑?前者快、后者可复现。
259
+ 2. **SLO 阈值**:任务成功率/事件延迟/上传断点成功率的硬阈值定多少(本稿暂不写数值)?
260
+ 3. **证据保留**:90 天过期提示是否合适?历史版本证据是否永久保留?
261
+ 4. **混沌工具**:本版本只做本地代理模拟断网,还是也要真机断网(更真实但操作成本高)?
262
+ 5. **stable 门槛**:L3 混沌是否强制全过才能 stable(推荐),还是允许豁免清单?
263
+ 6. **文档索引机制**:新增 `docs/acceptance/README.md` 作为唯一索引,还是并入现有 claims 的 shipped-documents 列表?
@@ -0,0 +1,281 @@
1
+ # 任务模型(Task Model)设计规格 — v1.4.1-beta
2
+
3
+ > 状态:**模型级设计稿**。把 M3(worktree/patch/合并队列/冲突)与既有任务/数据面能力归纳为统一任务模型,冻结任务种类、工作区、制品、分支与合并语义。
4
+ > 版本:`1.4.1-beta.*`;实现按 M3(1.3.4)与 M5 的审查/锁(1.3.6)分步交付,模型一致性验收在 1.4.1-beta 完成。
5
+ > 相关:`docs/COLLAB/M3-GIT-NATIVE-TASKS.md`、`docs/COLLAB/M5-MULTI-CLIENT-PRIMITIVES.md`、`docs/COLLAB/EXECUTION-AUTH-MODEL.md`。
6
+
7
+ ---
8
+
9
+ ## 1. 问题陈述
10
+
11
+ 现状的任务模型只有三个要素:
12
+
13
+ ```
14
+ 任务 = argv + 每台机器自己的项目副本 + 事后锚点一致性检查
15
+ ```
16
+
17
+ 它隐含三个假设,全部与现实不符:
18
+
19
+ 1. **假设只读**:任务不会改动代码;但真实开发必须改。
20
+ 2. **假设幂等**:重试安全;但写文件/提交不幂等,重试会产生两份改动。
21
+ 3. **假设结果只是文本**:stdout/stderr;但代码改动需要**可合并的制品**(commit/tree/patch)与**冲突语义**。
22
+
23
+ 后果:多台机器只能"各自跑命令",无法共同完成一个改动;没有分支/合并/冲突,就没有"联合开发"。
24
+
25
+ ---
26
+
27
+ ## 2. 任务模型总览
28
+
29
+ ```
30
+ Task = 意图 + 输入 + 工作区 + 义务 + 结果 + 证据
31
+
32
+ 意图 kind(exec/worktree/merge/review/maintenance)
33
+ 输入 argv、argv 约束、base_commit、project/branch、依赖、授权能力
34
+ 工作区 none / copy(旧行为)/ worktree(隔离)
35
+ 义务 gates:测试门禁、审查门禁、锁、锚点
36
+ 结果 结构化信封:status/outcome + artifacts + workspace 证据 + tests
37
+ 证据 ledger 事件 + 内容寻址制品 + 签名/授权来源(provenance)
38
+ ```
39
+
40
+ 任务模型与另外两个模型的分工:
41
+
42
+ | 模型 | 回答的问题 |
43
+ |---|---|
44
+ | 执行授权模型(v1.4.0) | 这台机器**能否执行**这条命令/这次维护 |
45
+ | **任务模型(本文)** | **执行什么、在哪个工作区、产出什么、如何合并** |
46
+ | 协作模型(v1.4.2) | **谁在哪个项目/房间**与谁协作、权限与通知 |
47
+
48
+ ---
49
+
50
+ ## 3. 任务种类(kind)与副作用分类
51
+
52
+ | kind | 工作区 | 副作用 | 执行者 | 重试语义 |
53
+ |---|---|---|---|---|
54
+ | `exec` | none/copy | 只读(红线保证) | agent | 可自由重试(幂等由只读保证) |
55
+ | `worktree` | 隔离 worktree | 改动代码(制品化) | agent | 新尝试 = 新 worktree(丢弃半成品) |
56
+ | `merge` | 合并专用 worktree | 推进项目 ref | 指定合并器(M2 grant) | 幂等(patch_sha+head 去重,已合并即 no-op) |
57
+ | `review` | 无 | 无代码副作用(结论) | 人/客户端 | 幂等(重复结论拒绝) |
58
+ | `maintenance` | 无 | root 系统动作 | root runner | 幂等(request_id) |
59
+
60
+ **副作用分类是模型的根**:任务的幂等/重试/隔离策略由副作用类型决定,而不是由调用方口头声明。
61
+
62
+ ---
63
+
64
+ ## 4. 工作区模型
65
+
66
+ ### 4.1 三种工作区
67
+
68
+ | workspace.kind | 语义 | 隔离 | 用于 |
69
+ |---|---|---|---|
70
+ | `none` | 不提供项目副本(纯命令) | 与机器其他任务共用 | 诊断类 exec |
71
+ | `copy`(兼容默认) | 机器上的现有项目副本 | 无(v1.3.x 行为) | 既有调用方 |
72
+ | `worktree` | 每任务独立 git worktree,起点=base_commit | 强隔离 | 所有会产生代码改动的任务 |
73
+
74
+ ### 4.2 worktree 不变量
75
+
76
+ 1. 起始点严格等于 `base_commit`(不存在则 `BASE_COMMIT_UNKNOWN`)。
77
+ 2. 写权限**仅限 worktree 内**;`.git` 管理目录由执行器接管(hooks/config 禁用)。
78
+ 3. 任务结束:成功 → 制品化(commit/patch)→ 清理;失败/超时 → 丢弃 worktree。
79
+ 4. 并发上限(默认 4/机器);超限 `WORKTREE_LIMIT`。
80
+ 5. 执行前后取 `git status` 快照,作为证据的一部分。
81
+ 6. 同一 base 上的两个任务互不可见;合并时才相遇。
82
+
83
+ ### 4.3 为什么不是"各自副本"
84
+
85
+ `copy` 工作区无法回答"这台机器的改动是相对哪个版本",也无法隔离并发任务。`worktree` 让改动**总是相对一个明确 base**,从而可传输、可合并、可审计。
86
+
87
+ ---
88
+
89
+ ## 5. 制品模型(Artifact)
90
+
91
+ 制品是任务结果的**一等公民**,内容寻址且不可变:
92
+
93
+ ```json
94
+ {
95
+ "sha256": "…",
96
+ "kind": "patch|conflict|diagnostic|report|log|binary",
97
+ "bytes": 12345,
98
+ "output_mode": "inline|blob",
99
+ "label": "patch for 01M6…",
100
+ "produced_by": { "task_id": "…", "machine_id": "…", "attempt": 1 },
101
+ "basis": { "base_commit": "…", "head_commit": "…", "tree": "…" },
102
+ "created_at": "…"
103
+ }
104
+ ```
105
+
106
+ 规则:
107
+
108
+ - **上传完成前不可引用**;引用后内容不可变(sha 就是身份)。
109
+ - `patch` 制品必须携带 `base_commit` 与头部提交信息;合并器只接受"从声明的 base 生成"的 patch,并重新计算 sha 校验。
110
+ - `conflict` 制品是冲突现场(含 conflict markers 的 diff),任何项目成员可下载。
111
+ - 制品可超过内联阈值 → M6 分块/blob 通道;大 patch 不阻塞任务上报。
112
+ - 制品生命周期与项目绑定(M4);默认保留 30 天,可选永久(发布附件)。
113
+
114
+ ---
115
+
116
+ ## 6. 分支与合并模型
117
+
118
+ ### 6.1 分支
119
+
120
+ - 项目(M4)拥有一个或多个分支 ref;任务声明 `target_branch`。
121
+ - `base_commit` 必须属于目标分支历史(合并器校验)。
122
+ - 分支 head **只由合并任务推进**;agent 绝不直接写项目 ref。
123
+ - 红线:禁止 force push/改写历史(`FORCE_PUSH_REFUSED`);head 单调前进。
124
+
125
+ ### 6.2 合并队列
126
+
127
+ ```
128
+ (queue per project+branch)
129
+ item = {queue_id, patch_sha, base_commit, source_task_id, submitted_by, state}
130
+ state: queued → claimed → applying → merged | conflict | failed
131
+ ```
132
+
133
+ - 同一队列同一时刻一个合并器(merge-slot lease,M5 的锁是用户级协调,二者互补)。
134
+ - 合并步骤固定:取队首 → 校验 patch_sha/base → `git apply --3way`/`git am` → 门禁 → 推进 head → 事件 → 释放。
135
+ - 合并策略(项目配置):
136
+ - `strict`(默认):base 必须等于当前 head,否则 `BASE_MISMATCH`
137
+ - `replay`:能干净应用到新 head 则重放,否则冲突
138
+ - `queue-base`:按队列顺序依次应用(顺序保证,不等价最新 head)
139
+ - 门禁(可组合):测试门禁(同 patch_sha 测试任务通过)、审查门禁(同 patch_sha 有 approved 审查)、锁门禁(enforced 分支/文件锁)。
140
+ - 幂等:同一 `(patch_sha, target_head)` 已合并 → `already_merged`(no-op);重放/重试安全。
141
+
142
+ ### 6.3 冲突是一等结果
143
+
144
+ ```json
145
+ {
146
+ "status": "conflict",
147
+ "reason_code": "MERGE_CONFLICT",
148
+ "merge": {
149
+ "patch_sha": "…", "target_head": "…", "base_commit": "…",
150
+ "files": ["src/a.mjs"],
151
+ "hunks": [{"file": "src/a.mjs", "start": 10, "lines": 4, "side": "both"}],
152
+ "artifact_sha256": "冲突现场(blob)",
153
+ "next": "由人选择解决者:重新提交到新 base / 手动解决后作为新 patch 入队"
154
+ }
155
+ }
156
+ ```
157
+
158
+ - 冲突时**绝不推进 head、绝不覆盖任何人的改动**。
159
+ - 不自动重试(防抖动);解决方式是新 patch 入队。
160
+ - 冲突现场可被审查任务、项目房间、通知引用(M4/M5)。
161
+
162
+ ---
163
+
164
+ ## 7. 结果信封与证据
165
+
166
+ 任务结果统一为结构化信封(向后兼容既有字段):
167
+
168
+ ```json
169
+ {
170
+ "task_id": "…", "attempt": 1, "kind": "worktree",
171
+ "status": "ok|failed|refused|conflict|timeout|expired",
172
+ "outcome": "ok|no_change|merged|already_merged|…",
173
+ "reason_code": null,
174
+ "authorization": { "provenance": "grant|allow-list|maintenance", "capability_id": "…" },
175
+ "workspace": { "kind": "worktree", "base_commit": "…", "head_commit": "…", "tree": "…", "cleaned": true },
176
+ "artifacts": [{ "sha256": "…", "kind": "patch", "bytes": 1234, "output_mode": "blob" }],
177
+ "tests": { "ran": true, "exit_code": 0, "summary": "…" },
178
+ "evidence": { "status_before": "…", "status_after": "…", "diffstat": {…} },
179
+ "timings": { "started_at": "…", "ended_at": "…", "duration_ms": 1234 }
180
+ }
181
+ ```
182
+
183
+ 规则:
184
+
185
+ - 结果必须可**仅凭信封**回答:执行了什么(授权来源)、在什么基础上、产出了什么(制品 sha)、有没有门禁。
186
+ - 不引用不存在的制品;上传失败 → `BLOB_UPLOAD_FAILED`(任务失败而不是静默降级)。
187
+ - 结果进 ledger,可重放;`provenance` 用于审计"哪次显式授权"。
188
+
189
+ ---
190
+
191
+ ## 8. 状态机与重试
192
+
193
+ ```
194
+ created → queued(waiting: deps/locks/gates) → offered → running → reporting → terminal
195
+ terminal = done | failed | refused | conflict | timeout | expired | cancelled
196
+
197
+ merge 任务:queued → claimed → applying → merged | conflict | failed
198
+ retry 规则(按副作用):
199
+ exec:可直接重试(只读),attempt += 1
200
+ worktree:重试 = 新 worktree(从同一 base),旧产物丢弃并记录
201
+ merge:重试 = 重新入队(幂等);已合并则 no-op
202
+ maintenance:重试 = 同 request_id(幂等),已执行返回既有结果
203
+ 任何重试都必须保留 attempt 与去重键,防止双执行
204
+ ```
205
+
206
+ 关键不变量:
207
+
208
+ 1. **at-most-once 副作用**:同一 `(task_id, attempt, dedupe_key)` 最多执行一次。
209
+ 2. **结果幂等**:relay 对重复结果去重;agent outbox(M6)保证最终投递。
210
+ 3. **不悬挂**:租约过期后任务可重派发;策略(halt)决定是否由其他机器接手。
211
+
212
+ ---
213
+
214
+ ## 9. 失败分类(模型级)
215
+
216
+ ```
217
+ 输入类:BASE_COMMIT_UNKNOWN、BASE_MISMATCH、PATCH_APPLY_FAILED、ARTIFACT_UNKNOWN
218
+ 资源类:WORKTREE_LIMIT、BLOB_QUOTA、OUTBOX_FULL
219
+ 门禁类:TESTS_REQUIRED、REVIEW_REQUIRED、LOCK_WAIT_TIMEOUT、PATH_LOCK_CONFLICT
220
+ 合并类:MERGE_CONFLICT、FORCE_PUSH_REFUSED、NOT_MERGE_EXECUTOR、MERGE_ABORTED
221
+ 授权类:RED_LINE_VIOLATION、NOT_AUTHORIZED、GRANT_*、MAINTENANCE_*
222
+ 传输类:BLOB_INCOMPLETE、BLOB_SHA_MISMATCH、SESSION_NOT_FOUND
223
+ ```
224
+
225
+ 每类都必须有负向测试(M7 的 negative matrix)。
226
+
227
+ ---
228
+
229
+ ## 10. 兼容与迁移
230
+
231
+ - 新字段全部可选;`kind` 缺省 = `exec`,`workspace.kind` 缺省 = `copy` → 既有调用方行为不变。
232
+ - 旧 relay 忽略新字段;旧 agent 按旧逻辑执行(可能拒绝或只读)。
233
+ - 旧任务(无 project_id)惰性归入 default 项目(M4);不重写 ledger。
234
+ - 迁移路径:`exec/copy` → 逐步把写任务切到 `worktree`;合并队列按项目启用(默认不启用,显式开启)。
235
+
236
+ ---
237
+
238
+ ## 11. 验收(模型一致性)
239
+
240
+ ### 正向(真实任务证据)
241
+
242
+ 1. **联合改动**:两机各自 worktree 任务(不同文件)→ 两个 patch 入队 → 合并器依次合并 → 项目 head 含两者改动,测试通过,两条 ref.updated 事件。
243
+ 2. **冲突**:两机改同一处 → 第二个 item 返回结构化 `MERGE_CONFLICT`(文件/hunk/现场 blob),head 不变。
244
+ 3. **base 前进**:strict 下返回 `BASE_MISMATCH`;replay 下干净应用则自动合并。
245
+ 4. **门禁**:测试/审查/锁门禁分别挡住合并,满足后续行。
246
+ 5. **幂等**:同 patch 重复入队 → `already_merged`;worktree 任务重试不产生两份改动。
247
+ 6. **结果证据**:每个任务信封能独立回答"授权来源/base/制品/门禁"。
248
+
249
+ ### 负向(自动化矩阵)
250
+
251
+ ```
252
+ 非 worktree 产出的 patch / 篡改 sha / 写 .git / 直接改项目 ref / force push /
253
+ merge 中断后锁恢复 / worktree 超限 / 冲突被误判为成功 / 缺门禁仍合并 /
254
+ 重试双执行 / 结果引用不存在的 blob → 全部对应错误码且 ref 不变
255
+ ```
256
+
257
+ ---
258
+
259
+ ## 12. 与版本/模块的映射
260
+
261
+ ```
262
+ 1.3.4-beta M3 实现:worktree/patch/合并队列/冲突(本章 §4–§7 的落地)
263
+ 1.3.6-beta M5 实现:锁与审查任务(本章门禁部分)
264
+ 1.4.1-beta 模型一致性验收:统一 kind/结果信封/幂等语义/失败分类
265
+ 共享模块:
266
+ src/relay/task-model.mjs 任务种类与状态机、副作用分类
267
+ src/relay/merge-queue.mjs 队列/merge-slot/门禁/幂等
268
+ src/relay/project-ref.mjs 分支 head 与单调推进
269
+ src/agent/worktree.mjs 工作区生命周期(M3)
270
+ src/agent/patch-artifact.mjs 制品生成/上传/证据
271
+ ```
272
+
273
+ ## 13. 开放问题(实现前确认)
274
+
275
+ 1. **写任务的默认 kind**:显式 `worktree` 才允许写(推荐),还是 copy 也允许写(兼容但危险)?
276
+ 2. **仓库来源**:项目 origin 指向 GitHub 私仓还是 relay 本机 bare?合并在哪一侧执行?
277
+ 3. **合并策略默认**:strict(推荐)还是 replay?
278
+ 4. **门禁默认**:测试/审查默认关闭、按项目开启(推荐)?
279
+ 5. **非 git 项目**:是否支持"无 git 的 worktree"(文件级快照/补丁)还是只支持 git?
280
+ 6. **Windows**:大小写不敏感与 symlink 限制如何表达(限制 patch 内容/仅告警)?
281
+ 7. **制品保留**:30 天默认够不够?发布附件是否永久保留?