@liangjie559567/ultrapower 5.0.21 → 5.0.23

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 (49) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/commands/ax-analyze-error.md +0 -1
  3. package/commands/ax-context.md +0 -1
  4. package/commands/ax-decompose.md +0 -1
  5. package/commands/ax-draft.md +0 -1
  6. package/commands/ax-evolution.md +0 -1
  7. package/commands/ax-evolve.md +0 -1
  8. package/commands/ax-export.md +0 -1
  9. package/commands/ax-implement.md +0 -1
  10. package/commands/ax-knowledge.md +0 -1
  11. package/commands/ax-reflect.md +0 -1
  12. package/commands/ax-review.md +0 -1
  13. package/commands/ax-rollback.md +0 -1
  14. package/commands/ax-status.md +0 -1
  15. package/commands/ax-suspend.md +0 -1
  16. package/commands/brainstorm.md +0 -1
  17. package/commands/execute-plan.md +0 -1
  18. package/commands/write-plan.md +0 -1
  19. package/dist/__tests__/validateMode.test.d.ts +2 -0
  20. package/dist/__tests__/validateMode.test.d.ts.map +1 -0
  21. package/dist/__tests__/validateMode.test.js +100 -0
  22. package/dist/__tests__/validateMode.test.js.map +1 -0
  23. package/dist/lib/validateMode.d.ts +49 -0
  24. package/dist/lib/validateMode.d.ts.map +1 -0
  25. package/dist/lib/validateMode.js +68 -0
  26. package/dist/lib/validateMode.js.map +1 -0
  27. package/docs/CLAUDE.md +1 -1
  28. package/docs/prd/ultrapower-standards-draft.md +191 -0
  29. package/docs/prd/ultrapower-standards-rough.md +560 -0
  30. package/docs/reviews/ultrapower-standards/review_critic.md +230 -0
  31. package/docs/reviews/ultrapower-standards/review_domain.md +243 -0
  32. package/docs/reviews/ultrapower-standards/review_product.md +102 -0
  33. package/docs/reviews/ultrapower-standards/review_tech.md +142 -0
  34. package/docs/reviews/ultrapower-standards/review_ux.md +110 -0
  35. package/docs/reviews/ultrapower-standards/summary.md +85 -0
  36. package/docs/standards/README.md +85 -0
  37. package/docs/standards/agent-lifecycle.md +445 -0
  38. package/docs/standards/anti-patterns.md +365 -0
  39. package/docs/standards/audit-report.md +388 -0
  40. package/docs/standards/contribution-guide.md +208 -0
  41. package/docs/standards/hook-execution-order.md +320 -0
  42. package/docs/standards/runtime-protection.md +336 -0
  43. package/docs/standards/state-machine.md +316 -0
  44. package/docs/standards/templates/agent-template.md +63 -0
  45. package/docs/standards/templates/hook-template.md +74 -0
  46. package/docs/standards/templates/skill-template.md +48 -0
  47. package/docs/standards/user-guide.md +290 -0
  48. package/docs/tasks/ultrapower-standards/manifest.md +441 -0
  49. package/package.json +1 -1
@@ -0,0 +1,445 @@
1
+ # Agent 生命周期规范
2
+
3
+ > **ultrapower-version**: 5.0.21
4
+ > **优先级**: P0(必须遵守)
5
+ > **真理之源**: `docs/standards/audit-report.md`
6
+ > **覆盖范围**: T-06(Agent 边界情况矩阵 + SubagentStopInput.success 废弃说明 + 孤儿 Agent 检测)
7
+
8
+ ---
9
+
10
+ ## 目录
11
+
12
+ 1. [Agent 边界情况矩阵](#1-agent-边界情况矩阵)
13
+ - 1.1 超时(Timeout)
14
+ - 1.2 孤儿状态(Orphan)
15
+ - 1.3 成本超限(Excessive Cost)
16
+ - 1.4 死锁检测(Deadlock)
17
+ - 1.5 文件冲突(File Conflict)
18
+ 2. [SubagentStopInput.success 废弃说明](#2-subagentstopinutsuccess-废弃说明)
19
+ - 2.1 废弃原因
20
+ - 2.2 推断机制替代方案
21
+ - 2.3 迁移指南
22
+ 3. [孤儿 Agent 检测与清理](#3-孤儿-agent-检测与清理)
23
+ - 3.1 孤儿 Agent 定义
24
+ - 3.2 session-end hook 清理机制
25
+ - 3.3 清理流程详解
26
+ 4. [关键常量汇总](#4-关键常量汇总)
27
+ 5. [AgentIntervention 接口规范](#5-agentintervention-接口规范)
28
+ 6. [并发保护与状态合并](#6-并发保护与状态合并)
29
+
30
+ ---
31
+
32
+ ## 1. Agent 边界情况矩阵
33
+
34
+ 来源:`src/hooks/subagent-tracker/index.ts`
35
+
36
+ ### 1.1 超时(Timeout)
37
+
38
+ | 参数 | 值 | 来源 |
39
+ |------|-----|------|
40
+ | 检测阈值 | `STALE_THRESHOLD_MS = 5 * 60 * 1000`(5 分钟) | `subagent-tracker/index.ts` |
41
+ | 警告触发 | agent 运行超过 5 分钟 | `getStaleAgents()` |
42
+ | 自动终止触发 | agent 运行超过 **10 分钟** | `suggestInterventions()` |
43
+ | 处理策略 | 生成 `AgentIntervention`,`suggested_action: "kill"` | `suggestInterventions()` |
44
+ | 自动执行 | `auto_execute: elapsed > 10`(超过 10 分钟自动执行) | `suggestInterventions()` |
45
+
46
+ > **注意(差异点 D-08)**:超时阈值存在两个不同的值:
47
+ > - `STALE_THRESHOLD_MS = 5 分钟`:用于 `getStaleAgents()` 检测 stale agent
48
+ > - `auto_execute: elapsed > 10`:实际自动终止阈值为 **10 分钟**
49
+ >
50
+ > 两者含义不同:5 分钟触发警告,10 分钟触发自动终止。实现者不得混淆。
51
+
52
+ **超时处理伪代码**:
53
+
54
+ ```typescript
55
+ // src/hooks/subagent-tracker/index.ts
56
+ function suggestInterventions(agents: SubagentInfo[]): AgentIntervention[] {
57
+ const interventions: AgentIntervention[] = [];
58
+
59
+ for (const agent of agents) {
60
+ const elapsed = (Date.now() - new Date(agent.started_at).getTime()) / 1000 / 60; // 分钟
61
+
62
+ if (elapsed > 5) { // STALE_THRESHOLD_MS / 60000
63
+ interventions.push({
64
+ type: "timeout",
65
+ agent_id: agent.agent_id,
66
+ agent_type: agent.agent_type,
67
+ reason: `Agent running for ${elapsed}m (threshold: 5m)`,
68
+ suggested_action: "kill",
69
+ auto_execute: elapsed > 10, // 超过 10 分钟自动终止
70
+ });
71
+ }
72
+ }
73
+
74
+ return interventions;
75
+ }
76
+ ```
77
+
78
+ ### 1.2 孤儿状态(Orphan)
79
+
80
+ | 参数 | 值 | 来源 |
81
+ |------|-----|------|
82
+ | 定义 | 父会话已结束但 agent 记录仍存在于 tracking 文件中 | session-end hook |
83
+ | 检测时机 | `SessionEnd` 事件触发时 | `session-end/index.ts` |
84
+ | 处理策略 | 删除整个 `subagent-tracking.json` 文件 | `cleanupTransientState()` |
85
+ | 是否逐个 SHUTDOWN | **否**(批量清除,非逐个信号) | `cleanupTransientState()` |
86
+
87
+ > **重要**:session-end hook **不向孤儿 agent 发送 SHUTDOWN 信号**。它通过删除 `subagent-tracking.json` 文件来批量清除所有 agent 记录。这意味着孤儿 agent 的状态在会话结束时被强制清除,而非优雅关闭。
88
+
89
+ ### 1.3 成本超限(Excessive Cost)
90
+
91
+ | 参数 | 值 | 来源 |
92
+ |------|-----|------|
93
+ | 常量名 | `COST_LIMIT_USD` | `subagent-tracker/index.ts` |
94
+ | 限制值 | `1.0`(美元) | 代码定义 |
95
+ | 检测字段 | `agent.token_usage.cost_usd` | `suggestInterventions()` |
96
+ | 处理策略 | 生成 `AgentIntervention`,`suggested_action: "warn"` | `suggestInterventions()` |
97
+ | 自动执行 | `auto_execute: false`(仅警告,不自动终止) | `suggestInterventions()` |
98
+
99
+ **成本检测伪代码**:
100
+
101
+ ```typescript
102
+ // src/hooks/subagent-tracker/index.ts
103
+ export const COST_LIMIT_USD = 1.0;
104
+
105
+ if (agent.token_usage && agent.token_usage.cost_usd > COST_LIMIT_USD) {
106
+ interventions.push({
107
+ type: "excessive_cost",
108
+ agent_id: agent.agent_id,
109
+ agent_type: agent.agent_type,
110
+ reason: `Agent cost $${agent.token_usage.cost_usd.toFixed(4)} exceeds limit $${COST_LIMIT_USD}`,
111
+ suggested_action: "warn",
112
+ auto_execute: false, // 仅警告,不自动终止
113
+ });
114
+ }
115
+ ```
116
+
117
+ ### 1.4 死锁检测(Deadlock)
118
+
119
+ | 参数 | 值 | 来源 |
120
+ |------|-----|------|
121
+ | 常量名 | `DEADLOCK_CHECK_THRESHOLD` | `subagent-tracker/index.ts` |
122
+ | 阈值值 | `3` | 代码定义 |
123
+ | 实现状态 | **常量已定义,检测逻辑未实现** | 差异点 D-10 |
124
+ | AgentIntervention 类型 | `"deadlock"` 已在类型定义中 | `AgentIntervention` 接口 |
125
+
126
+ > **注意(差异点 D-10)**:`DEADLOCK_CHECK_THRESHOLD = 3` 常量已在代码中定义,`AgentIntervention` 类型也包含 `"deadlock"` 类型,但 `suggestInterventions()` 函数中**未实现**死锁检测逻辑。当前 `suggestInterventions()` 仅检测 `timeout`、`excessive_cost` 和 `file_conflict`,不检测 `deadlock`。
127
+ >
128
+ > **规范要求(v2 目标)**:实现死锁检测逻辑,当检测到 N 个 agent(N >= `DEADLOCK_CHECK_THRESHOLD`)相互等待时触发干预。
129
+
130
+ **当前实现(差异点 D-10)**:
131
+
132
+ ```typescript
133
+ // src/hooks/subagent-tracker/index.ts
134
+ export const DEADLOCK_CHECK_THRESHOLD = 3; // 已定义但未使用
135
+
136
+ // suggestInterventions() 当前仅检测:
137
+ // ✅ timeout
138
+ // ✅ excessive_cost
139
+ // ✅ file_conflict
140
+ // ❌ deadlock(未实现)
141
+ ```
142
+
143
+ ### 1.5 文件冲突(File Conflict)
144
+
145
+ | 参数 | 值 | 来源 |
146
+ |------|-----|------|
147
+ | 检测函数 | `detectFileConflicts()` | `subagent-tracker/index.ts` |
148
+ | 检测条件 | 同一文件被多个 `RUNNING` 状态的 agent 修改 | `detectFileConflicts()` |
149
+ | 处理策略 | 生成 `AgentIntervention`,`suggested_action: "warn"` | `suggestInterventions()` |
150
+ | 自动执行 | `auto_execute: false`(仅警告) | `suggestInterventions()` |
151
+
152
+ ---
153
+
154
+ ## 2. SubagentStopInput.success 废弃说明
155
+
156
+ 来源:`src/hooks/subagent-tracker/index.ts`
157
+
158
+ ### 2.1 废弃原因
159
+
160
+ `SubagentStopInput.success` 字段已标记为 `@deprecated`,原因是 **Claude Code SDK 不提供此字段**。
161
+
162
+ ```typescript
163
+ // src/hooks/subagent-tracker/index.ts
164
+ export interface SubagentStopInput {
165
+ session_id: string;
166
+ transcript_path: string;
167
+ cwd: string;
168
+ permission_mode: string;
169
+ hook_event_name: "SubagentStop";
170
+ agent_id: string;
171
+ agent_type: string;
172
+ output?: string;
173
+ /** @deprecated The SDK does not provide a success field. Use inferred status instead. */
174
+ success?: boolean;
175
+ }
176
+ ```
177
+
178
+ **废弃背景**:
179
+ - SDK 在 `SubagentStop` 事件中不传递 `success` 字段
180
+ - 历史代码依赖此字段判断 agent 是否成功完成
181
+ - 直接读取 `input.success` 会导致始终得到 `undefined`,进而错误地将所有 agent 标记为失败
182
+
183
+ ### 2.2 推断机制替代方案
184
+
185
+ **当前实现(Bug #1 修复)**:
186
+
187
+ ```typescript
188
+ // src/hooks/subagent-tracker/index.ts
189
+ // SDK does not provide `success` field, so default to 'completed' when undefined (Bug #1 fix)
190
+ const succeeded = input.success !== false;
191
+ ```
192
+
193
+ **推断规则**:
194
+
195
+ | `input.success` 值 | `succeeded` 结果 | 说明 |
196
+ |---------------------|-----------------|------|
197
+ | `undefined`(SDK 默认) | `true` | SDK 不提供此字段,默认视为成功 |
198
+ | `true` | `true` | 显式成功(向后兼容) |
199
+ | `false` | `false` | 显式失败(向后兼容) |
200
+
201
+ **设计理由**:
202
+ - `input.success !== false` 等价于"除非明确为 false,否则视为成功"
203
+ - 这是对 SDK 行为的最保守假设:agent 完成即视为成功
204
+ - 保留向后兼容性:如果未来 SDK 提供此字段,`false` 值仍能正确处理
205
+
206
+ ### 2.3 迁移指南
207
+
208
+ **禁止模式**:
209
+
210
+ ```typescript
211
+ // ❌ 禁止:直接读取 success 字段
212
+ const succeeded = input.success; // 始终为 undefined,导致 falsy 判断错误
213
+
214
+ // ❌ 禁止:使用 Boolean 转换
215
+ const succeeded = Boolean(input.success); // undefined -> false,错误地标记为失败
216
+ ```
217
+
218
+ **正确模式**:
219
+
220
+ ```typescript
221
+ // ✅ 正确:使用推断机制
222
+ const succeeded = input.success !== false;
223
+
224
+ // ✅ 正确:或使用显式注释说明意图
225
+ // SDK does not provide success field; treat undefined as success
226
+ const succeeded = input.success !== false;
227
+ ```
228
+
229
+ ---
230
+
231
+ ## 3. 孤儿 Agent 检测与清理
232
+
233
+ 来源:`src/hooks/session-end/index.ts`
234
+
235
+ ### 3.1 孤儿 Agent 定义
236
+
237
+ **孤儿 Agent**:父会话(parent session)已通过 `SessionEnd` 事件结束,但 agent 的状态记录仍存在于 `subagent-tracking.json` 中的 agent。
238
+
239
+ **产生原因**:
240
+ - 会话异常终止(`reason: "other"`)
241
+ - 用户强制退出(`reason: "logout"`)
242
+ - 上下文清除(`reason: "clear"`)
243
+ - Agent 在会话结束时仍处于 `RUNNING` 或 `WAITING` 状态
244
+
245
+ ### 3.2 session-end hook 清理机制
246
+
247
+ `SessionEnd` 事件触发时,`processSessionEnd()` 按以下顺序执行:
248
+
249
+ ```
250
+ 1. recordSessionMetrics() — 记录会话指标(spawned/completed 计数)
251
+ 2. exportSessionSummary() — 导出到 .omc/sessions/{session_id}.json
252
+ 3. cleanupTransientState() — 删除 subagent-tracking.json(孤儿清理)
253
+ 4. cleanupModeStates() — 清理活跃模式状态文件
254
+ 5. extractPythonReplSessionIds() + cleanupBridgeSessions() — 清理 Python REPL
255
+ 6. triggerStopCallbacks() — 触发通知回调(文件/Telegram/Discord)
256
+ 7. notify('session-end', ...) — 触发新通知系统
257
+ ```
258
+
259
+ ### 3.3 清理流程详解
260
+
261
+ **`cleanupTransientState()` — 孤儿 Agent 批量清理**:
262
+
263
+ ```typescript
264
+ // src/hooks/session-end/index.ts
265
+ export function cleanupTransientState(directory: string): number {
266
+ // 删除整个 subagent-tracking.json 文件
267
+ const trackingPath = path.join(directory, '.omc', 'state', 'subagent-tracking.json');
268
+ if (fs.existsSync(trackingPath)) {
269
+ fs.unlinkSync(trackingPath); // 批量清除所有 agent 记录
270
+ filesRemoved++;
271
+ }
272
+
273
+ // 清理 24 小时前的 checkpoints
274
+ // 清理 .omc/ 下的所有 .tmp 文件
275
+ }
276
+ ```
277
+
278
+ **`cleanupModeStates()` — 模式状态清理**:
279
+
280
+ ```typescript
281
+ // src/hooks/session-end/index.ts
282
+ // 仅清理满足以下条件的状态文件:
283
+ // 1. state.active === true(活跃状态)
284
+ // 2. state.session_id 匹配当前 session_id(或无 session_id 的旧版文件)
285
+ export function cleanupModeStates(directory: string, sessionId?: string): {
286
+ filesRemoved: number;
287
+ modesCleaned: string[];
288
+ }
289
+ ```
290
+
291
+ **清理范围**:
292
+
293
+ | 文件 | 清理条件 |
294
+ |------|---------|
295
+ | `subagent-tracking.json` | 无条件删除(所有孤儿 agent 记录) |
296
+ | `autopilot-state.json` | `active=true` 且 session_id 匹配 |
297
+ | `ultrapilot-state.json` | `active=true` 且 session_id 匹配 |
298
+ | `ralph-state.json` | `active=true` 且 session_id 匹配 |
299
+ | `ultrawork-state.json` | `active=true` 且 session_id 匹配 |
300
+ | `ultraqa-state.json` | `active=true` 且 session_id 匹配 |
301
+ | `pipeline-state.json` | `active=true` 且 session_id 匹配 |
302
+ | `swarm-active.marker` | 无条件删除(marker 文件) |
303
+ | `swarm-summary.json` | 无条件删除(marker 文件) |
304
+ | `.omc/**/*.tmp` | 无条件删除(临时文件) |
305
+ | `.omc/checkpoints/*` | 超过 24 小时的文件 |
306
+
307
+ **session_id 匹配规则**(修复 issue #573):
308
+
309
+ ```typescript
310
+ // 防止跨会话误清理
311
+ const stateSessionId = state.session_id as string | undefined;
312
+ if (!sessionId || !stateSessionId || stateSessionId === sessionId) {
313
+ // 清理:无 sessionId 参数 OR 状态无 session_id(旧版)OR session_id 匹配
314
+ fs.unlinkSync(localPath);
315
+ }
316
+ // 否则:state.session_id 与当前 session 不匹配,跳过(防止清理其他并发会话的状态)
317
+ ```
318
+
319
+ ---
320
+
321
+ ## 4. 关键常量汇总
322
+
323
+ 来源:`src/hooks/subagent-tracker/index.ts`
324
+
325
+ | 常量名 | 值 | 用途 | 导出状态 |
326
+ |--------|-----|------|---------|
327
+ | `COST_LIMIT_USD` | `1.0` | 单 agent 成本上限(美元) | `export const` |
328
+ | `DEADLOCK_CHECK_THRESHOLD` | `3` | 死锁检测阈值(未实现) | `export const` |
329
+ | `STALE_THRESHOLD_MS` | `5 * 60 * 1000`(5 分钟) | Agent stale 检测阈值 | 模块内部 |
330
+ | `MAX_COMPLETED_AGENTS` | `100` | 已完成 agent 记录最大保留数 | 模块内部 |
331
+ | `LOCK_TIMEOUT_MS` | `5000`(5 秒) | 文件锁超时 | 模块内部 |
332
+ | `LOCK_RETRY_MS` | `50` | 文件锁重试间隔 | 模块内部 |
333
+ | `WRITE_DEBOUNCE_MS` | `100` | 状态写入防抖间隔 | 模块内部 |
334
+ | `MAX_FLUSH_RETRIES` | `3` | 最大刷新重试次数 | 模块内部 |
335
+ | `FLUSH_RETRY_BASE_MS` | `50` | 刷新重试指数退避基数 | 模块内部 |
336
+
337
+ **自动终止阈值**(非常量,硬编码在逻辑中):
338
+
339
+ ```typescript
340
+ // src/hooks/subagent-tracker/index.ts
341
+ auto_execute: elapsed > 10 // 10 分钟,硬编码(非常量)
342
+ ```
343
+
344
+ > **规范要求(v2 目标)**:将 `10` 提取为具名常量 `AUTO_KILL_THRESHOLD_MINUTES = 10`,提升可维护性。
345
+
346
+ ---
347
+
348
+ ## 5. AgentIntervention 接口规范
349
+
350
+ 来源:`src/hooks/subagent-tracker/index.ts`
351
+
352
+ ```typescript
353
+ export interface AgentIntervention {
354
+ type: "timeout" | "deadlock" | "excessive_cost" | "file_conflict";
355
+ agent_id: string;
356
+ agent_type: string;
357
+ reason: string;
358
+ suggested_action: "kill" | "restart" | "warn" | "skip";
359
+ auto_execute: boolean;
360
+ }
361
+ ```
362
+
363
+ **各类型的默认行为**:
364
+
365
+ | `type` | `suggested_action` | `auto_execute` | 实现状态 |
366
+ |--------|-------------------|----------------|---------|
367
+ | `timeout` | `"kill"` | `elapsed > 10`(10 分钟后为 true) | ✅ 已实现 |
368
+ | `excessive_cost` | `"warn"` | `false` | ✅ 已实现 |
369
+ | `file_conflict` | `"warn"` | `false` | ✅ 已实现 |
370
+ | `deadlock` | 未定义 | 未定义 | ❌ 未实现(差异点 D-10) |
371
+
372
+ **`auto_execute` 语义**:
373
+
374
+ - `true`:系统自动执行 `suggested_action`,无需人工确认
375
+ - `false`:仅生成建议,需人工或上层逻辑决定是否执行
376
+
377
+ ---
378
+
379
+ ## 6. 并发保护与状态合并
380
+
381
+ 来源:`src/hooks/subagent-tracker/index.ts`
382
+
383
+ ### 6.1 四层并发保护(subagent-tracking.json)
384
+
385
+ `subagent-tracking.json` 是并发写入最频繁的状态文件,采用四层保护:
386
+
387
+ ```
388
+ 第一层:debounce(WRITE_DEBOUNCE_MS = 100ms)
389
+ → 合并短时间内的多次写入请求
390
+
391
+ 第二层:flushInProgress Set
392
+ → 防止同一进程内的并发 flush
393
+
394
+ 第三层:mergeTrackerStates()
395
+ → 读取磁盘状态后合并,防止覆盖其他进程的写入
396
+
397
+ 第四层:文件锁(PID:timestamp 格式)
398
+ → 跨进程互斥,LOCK_TIMEOUT_MS = 5000ms
399
+ ```
400
+
401
+ ### 6.2 mergeTrackerStates 合并规则
402
+
403
+ ```typescript
404
+ // 计数器:取 Math.max,防止双重计数
405
+ const total_spawned = Math.max(diskState.total_spawned, pendingState.total_spawned);
406
+
407
+ // 同一 agent_id 的状态:newer timestamp wins
408
+ if (pendingTime >= existingTime) {
409
+ agentMap.set(agent.agent_id, agent);
410
+ }
411
+ ```
412
+
413
+ ### 6.3 writeTrackingStateImmediate 直接写入(差异点 D-07)
414
+
415
+ ```typescript
416
+ // src/hooks/subagent-tracker/index.ts(当前实现)
417
+ function writeTrackingStateImmediate(directory: string, state: SubagentTrackingState): void {
418
+ const statePath = getStateFilePath(directory);
419
+ state.last_updated = new Date().toISOString();
420
+ try {
421
+ writeFileSync(statePath, JSON.stringify(state, null, 2), "utf-8"); // 直接写入,无原子保护
422
+ } catch (error) {
423
+ console.error("[SubagentTracker] Error writing state:", error);
424
+ }
425
+ }
426
+ ```
427
+
428
+ > **注意(差异点 D-07)**:`writeTrackingStateImmediate()` 使用 `writeFileSync` 直接写入,绕过了 `atomicWriteJsonSync` 的原子保护。这是已知技术债务(TD-4),v2 目标是统一为 debounce + atomic 双层保护。
429
+
430
+ ### 6.4 MAX_COMPLETED_AGENTS 驱逐策略
431
+
432
+ ```typescript
433
+ // 已完成 agent 记录超过 MAX_COMPLETED_AGENTS = 100 时,驱逐最旧的记录
434
+ // 防止 subagent-tracking.json 无限增长
435
+ ```
436
+
437
+ ---
438
+
439
+ ## 差异点说明
440
+
441
+ | 差异点 | 描述 | 当前状态 | 规范要求 |
442
+ |--------|------|---------|---------|
443
+ | D-07 | subagent-tracker 内部写入 | `writeFileSync` 直接写入(无原子保护) | v2 统一为 atomicWriteJsonSync |
444
+ | D-08 | 超时阈值双重含义 | 5 分钟(stale 检测)vs 10 分钟(自动终止) | 必须区分,不得混淆 |
445
+ | D-10 | 死锁检测未实现 | `DEADLOCK_CHECK_THRESHOLD = 3` 已定义,逻辑未实现 | v2 实现死锁检测逻辑 |