@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.
- package/.claude-plugin/plugin.json +1 -1
- package/commands/ax-analyze-error.md +0 -1
- package/commands/ax-context.md +0 -1
- package/commands/ax-decompose.md +0 -1
- package/commands/ax-draft.md +0 -1
- package/commands/ax-evolution.md +0 -1
- package/commands/ax-evolve.md +0 -1
- package/commands/ax-export.md +0 -1
- package/commands/ax-implement.md +0 -1
- package/commands/ax-knowledge.md +0 -1
- package/commands/ax-reflect.md +0 -1
- package/commands/ax-review.md +0 -1
- package/commands/ax-rollback.md +0 -1
- package/commands/ax-status.md +0 -1
- package/commands/ax-suspend.md +0 -1
- package/commands/brainstorm.md +0 -1
- package/commands/execute-plan.md +0 -1
- package/commands/write-plan.md +0 -1
- package/dist/__tests__/validateMode.test.d.ts +2 -0
- package/dist/__tests__/validateMode.test.d.ts.map +1 -0
- package/dist/__tests__/validateMode.test.js +100 -0
- package/dist/__tests__/validateMode.test.js.map +1 -0
- package/dist/lib/validateMode.d.ts +49 -0
- package/dist/lib/validateMode.d.ts.map +1 -0
- package/dist/lib/validateMode.js +68 -0
- package/dist/lib/validateMode.js.map +1 -0
- package/docs/CLAUDE.md +1 -1
- package/docs/prd/ultrapower-standards-draft.md +191 -0
- package/docs/prd/ultrapower-standards-rough.md +560 -0
- package/docs/reviews/ultrapower-standards/review_critic.md +230 -0
- package/docs/reviews/ultrapower-standards/review_domain.md +243 -0
- package/docs/reviews/ultrapower-standards/review_product.md +102 -0
- package/docs/reviews/ultrapower-standards/review_tech.md +142 -0
- package/docs/reviews/ultrapower-standards/review_ux.md +110 -0
- package/docs/reviews/ultrapower-standards/summary.md +85 -0
- package/docs/standards/README.md +85 -0
- package/docs/standards/agent-lifecycle.md +445 -0
- package/docs/standards/anti-patterns.md +365 -0
- package/docs/standards/audit-report.md +388 -0
- package/docs/standards/contribution-guide.md +208 -0
- package/docs/standards/hook-execution-order.md +320 -0
- package/docs/standards/runtime-protection.md +336 -0
- package/docs/standards/state-machine.md +316 -0
- package/docs/standards/templates/agent-template.md +63 -0
- package/docs/standards/templates/hook-template.md +74 -0
- package/docs/standards/templates/skill-template.md +48 -0
- package/docs/standards/user-guide.md +290 -0
- package/docs/tasks/ultrapower-standards/manifest.md +441 -0
- package/package.json +1 -1
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
# 反模式清单
|
|
2
|
+
|
|
3
|
+
> **ultrapower-version**: 5.0.21
|
|
4
|
+
> **优先级**: P0(必须遵守)
|
|
5
|
+
> **真理之源**: `docs/standards/audit-report.md`
|
|
6
|
+
> **覆盖范围**: T-09(已知反模式 + 正确替代方案)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 目录
|
|
11
|
+
|
|
12
|
+
1. [安全反模式](#1-安全反模式)
|
|
13
|
+
2. [状态管理反模式](#2-状态管理反模式)
|
|
14
|
+
3. [Agent 生命周期反模式](#3-agent-生命周期反模式)
|
|
15
|
+
4. [并发反模式](#4-并发反模式)
|
|
16
|
+
5. [模式路由反模式](#5-模式路由反模式)
|
|
17
|
+
6. [测试反模式](#6-测试反模式)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. 安全反模式
|
|
22
|
+
|
|
23
|
+
### AP-S01:未校验 mode 参数直接拼接路径
|
|
24
|
+
|
|
25
|
+
**来源**:`docs/standards/runtime-protection.md` §2.4
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// ❌ 反模式:路径遍历风险
|
|
29
|
+
const stateFilePath = `.omc/state/${mode}-state.json`;
|
|
30
|
+
// 攻击者可传入 mode = "../../etc/passwd" 读取任意文件
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
// ✅ 正确:先校验再拼接
|
|
35
|
+
import { assertValidMode } from '../lib/validateMode';
|
|
36
|
+
const validMode = assertValidMode(mode);
|
|
37
|
+
const stateFilePath = `.omc/state/${validMode}-state.json`;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**规则**:所有使用 mode 参数构建文件路径的代码,必须先调用 `assertValidMode()` 或 `validateMode()`。
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
### AP-S02:直接读取 SubagentStopInput.success
|
|
45
|
+
|
|
46
|
+
**来源**:`docs/standards/agent-lifecycle.md` §2
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// ❌ 反模式:SDK 不提供此字段,始终为 undefined
|
|
50
|
+
const succeeded = input.success; // undefined → falsy → 错误地标记为失败
|
|
51
|
+
const succeeded = Boolean(input.success); // undefined → false → 同上
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
// ✅ 正确:使用推断机制
|
|
56
|
+
// SDK does not provide success field; treat undefined as success
|
|
57
|
+
const succeeded = input.success !== false;
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**规则**:`SubagentStopInput.success` 已标记为 `@deprecated`,禁止直接读取。
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### AP-S03:在状态文件中存储敏感信息
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// ❌ 反模式:状态文件可能被其他进程读取
|
|
68
|
+
state_write(mode="autopilot", {
|
|
69
|
+
api_key: "sk-...",
|
|
70
|
+
user_token: "Bearer ...",
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// ✅ 正确:状态文件只存储执行状态,不存储凭证
|
|
76
|
+
state_write(mode="autopilot", {
|
|
77
|
+
current_phase: "executing",
|
|
78
|
+
task_id: "task-123",
|
|
79
|
+
active: true,
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 2. 状态管理反模式
|
|
86
|
+
|
|
87
|
+
### AP-ST01:混淆两种 stale 阈值
|
|
88
|
+
|
|
89
|
+
**来源**:`docs/standards/state-machine.md` §4(差异点 D-09)
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// ❌ 反模式:将 agent stale 阈值用于 mode stale 检测
|
|
93
|
+
const STALE_THRESHOLD_MS = 5 * 60 * 1000; // 5 分钟
|
|
94
|
+
if (Date.now() - modeState.last_updated > STALE_THRESHOLD_MS) {
|
|
95
|
+
// 错误:mode stale 阈值应为 1 小时,不是 5 分钟
|
|
96
|
+
cleanupModeState();
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
// ✅ 正确:区分两种阈值
|
|
102
|
+
// Agent stale(subagent-tracker):5 分钟
|
|
103
|
+
const STALE_THRESHOLD_MS = 5 * 60 * 1000;
|
|
104
|
+
|
|
105
|
+
// Mode stale marker(mode-registry):1 小时
|
|
106
|
+
const STALE_MARKER_THRESHOLD = 60 * 60 * 1000;
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**规则**:两种 stale 阈值含义不同,不得混用。详见 `docs/standards/state-machine.md` §4。
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### AP-ST02:跨会话误清理状态文件
|
|
114
|
+
|
|
115
|
+
**来源**:`docs/standards/agent-lifecycle.md` §3.3(修复 issue #573)
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// ❌ 反模式:无条件清理所有活跃状态
|
|
119
|
+
for (const stateFile of activeStateFiles) {
|
|
120
|
+
fs.unlinkSync(stateFile); // 可能删除其他并发会话的状态
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// ✅ 正确:检查 session_id 匹配
|
|
126
|
+
const stateSessionId = state.session_id as string | undefined;
|
|
127
|
+
if (!sessionId || !stateSessionId || stateSessionId === sessionId) {
|
|
128
|
+
fs.unlinkSync(localPath); // 只清理匹配当前会话的状态
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
### AP-ST03:在 `~/.claude/` 中存储 OMC 状态
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
// ❌ 反模式:状态存储在全局目录
|
|
138
|
+
~/.claude/state/autopilot-state.json
|
|
139
|
+
|
|
140
|
+
// ✅ 正确:状态存储在 worktree 根目录
|
|
141
|
+
{worktree}/.omc/state/autopilot-state.json
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**规则**:所有 OMC 状态必须存储在 git worktree 根目录的 `.omc/state/` 下,不在 `~/.claude/`。
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 3. Agent 生命周期反模式
|
|
149
|
+
|
|
150
|
+
### AP-AL01:向孤儿 Agent 发送 SHUTDOWN 信号
|
|
151
|
+
|
|
152
|
+
**来源**:`docs/standards/agent-lifecycle.md` §3.2
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// ❌ 反模式:逐个向孤儿 agent 发送信号(session-end hook 不这样做)
|
|
156
|
+
for (const orphanAgent of orphanAgents) {
|
|
157
|
+
sendShutdownSignal(orphanAgent.agent_id); // 错误:批量清除,非逐个信号
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// ✅ 正确:批量清除 tracking 文件
|
|
163
|
+
function cleanupTransientState(directory: string): number {
|
|
164
|
+
const trackingPath = path.join(directory, '.omc', 'state', 'subagent-tracking.json');
|
|
165
|
+
if (fs.existsSync(trackingPath)) {
|
|
166
|
+
fs.unlinkSync(trackingPath); // 批量清除所有 agent 记录
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
### AP-AL02:混淆超时阈值(5 分钟 vs 10 分钟)
|
|
174
|
+
|
|
175
|
+
**来源**:`docs/standards/agent-lifecycle.md` §1.1(差异点 D-08)
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// ❌ 反模式:将 stale 检测阈值用于自动终止
|
|
179
|
+
if (elapsed > 5) { // 5 分钟
|
|
180
|
+
autoKillAgent(); // 错误:5 分钟只触发警告,不自动终止
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// ✅ 正确:区分两个阈值
|
|
186
|
+
if (elapsed > 5) {
|
|
187
|
+
// 触发警告(stale 检测)
|
|
188
|
+
interventions.push({ type: "timeout", auto_execute: false });
|
|
189
|
+
}
|
|
190
|
+
if (elapsed > 10) {
|
|
191
|
+
// 自动终止(10 分钟阈值)
|
|
192
|
+
interventions.push({ type: "timeout", auto_execute: true });
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
### AP-AL03:实现死锁检测但忽略 DEADLOCK_CHECK_THRESHOLD
|
|
199
|
+
|
|
200
|
+
**来源**:`docs/standards/agent-lifecycle.md` §1.4(差异点 D-10)
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// ❌ 反模式:硬编码死锁阈值
|
|
204
|
+
if (mutuallyWaitingAgents.length >= 2) { // 错误:应使用常量
|
|
205
|
+
triggerDeadlockIntervention();
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
// ✅ 正确:使用已定义的常量
|
|
211
|
+
export const DEADLOCK_CHECK_THRESHOLD = 3;
|
|
212
|
+
if (mutuallyWaitingAgents.length >= DEADLOCK_CHECK_THRESHOLD) {
|
|
213
|
+
triggerDeadlockIntervention();
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 4. 并发反模式
|
|
220
|
+
|
|
221
|
+
### AP-C01:绕过原子写入保护
|
|
222
|
+
|
|
223
|
+
**来源**:`docs/standards/agent-lifecycle.md` §6.3(差异点 D-07,已知技术债务 TD-4)
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
// ❌ 反模式:直接写入,无原子保护(当前 writeTrackingStateImmediate 的已知问题)
|
|
227
|
+
writeFileSync(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
228
|
+
// 风险:并发写入可能导致文件损坏
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// ✅ 正确(v2 目标):使用原子写入
|
|
233
|
+
atomicWriteJsonSync(statePath, state);
|
|
234
|
+
// 通过临时文件 + rename 保证原子性
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**注意**:`writeTrackingStateImmediate()` 当前使用直接写入(TD-4),v2 目标统一为 `atomicWriteJsonSync`。
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### AP-C02:不使用防抖直接写入高频状态
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// ❌ 反模式:每次工具调用都立即写入
|
|
245
|
+
onToolCall(() => {
|
|
246
|
+
writeTrackingState(directory, state); // 高频写入,性能问题
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
// ✅ 正确:使用防抖合并写入
|
|
252
|
+
const WRITE_DEBOUNCE_MS = 100;
|
|
253
|
+
const debouncedWrite = debounce(writeTrackingState, WRITE_DEBOUNCE_MS);
|
|
254
|
+
onToolCall(() => {
|
|
255
|
+
debouncedWrite(directory, state);
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## 5. 模式路由反模式
|
|
262
|
+
|
|
263
|
+
### AP-MR01:将 autopilot 与 ultrapilot 同时激活
|
|
264
|
+
|
|
265
|
+
**来源**:`docs/standards/state-machine.md` §5(差异点 D-04)
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
// ❌ 反模式:同时激活互斥模式
|
|
269
|
+
activateMode('autopilot');
|
|
270
|
+
activateMode('ultrapilot'); // 错误:两者互斥
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
// ✅ 正确:检查互斥冲突
|
|
275
|
+
const EXCLUSIVE_MODES = ['autopilot', 'ultrapilot', 'swarm', 'pipeline'];
|
|
276
|
+
if (checkExclusiveConflict(newMode)) {
|
|
277
|
+
throw new Error(`Mode ${newMode} conflicts with active exclusive mode`);
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**互斥模式完整列表**:`autopilot`、`ultrapilot`、`swarm`、`pipeline`(共 4 个,非 PRD 原描述的 2 个)。
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
### AP-MR02:使用不在白名单中的 mode 字符串
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
// ❌ 反模式:使用未定义的 mode
|
|
289
|
+
state_write(mode="custom-mode", { ... }); // 错误:不在 VALID_MODES 中
|
|
290
|
+
|
|
291
|
+
// ❌ 反模式:大小写错误
|
|
292
|
+
state_write(mode="AUTOPILOT", { ... }); // 错误:大小写敏感
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
// ✅ 正确:只使用 VALID_MODES 中的 8 个值
|
|
297
|
+
// 'autopilot' | 'ultrapilot' | 'team' | 'pipeline' |
|
|
298
|
+
// 'ralph' | 'ultrawork' | 'ultraqa' | 'swarm'
|
|
299
|
+
state_write(mode="autopilot", { ... });
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## 6. 测试反模式
|
|
305
|
+
|
|
306
|
+
### AP-T01:测试文件放在错误目录
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
// ❌ 反模式:测试文件与源文件混放
|
|
310
|
+
src/lib/validateMode.ts
|
|
311
|
+
src/lib/validateMode.test.ts // 错误:应在 __tests__ 目录
|
|
312
|
+
|
|
313
|
+
// ✅ 正确:测试文件统一放在 src/__tests__/
|
|
314
|
+
src/lib/validateMode.ts
|
|
315
|
+
src/__tests__/validateMode.test.ts
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
### AP-T02:测试中使用错误的导入路径
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
// ❌ 反模式:路径层级错误
|
|
324
|
+
// 文件位于 src/__tests__/validateMode.test.ts
|
|
325
|
+
import { validateMode } from '../../lib/validateMode'; // 错误:多了一层 ../
|
|
326
|
+
|
|
327
|
+
// ✅ 正确:从 src/__tests__/ 到 src/lib/ 只需一层
|
|
328
|
+
import { validateMode } from '../lib/validateMode';
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
### AP-T03:不测试非字符串类型输入
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
// ❌ 反模式:只测试字符串输入
|
|
337
|
+
it('should validate mode', () => {
|
|
338
|
+
expect(validateMode('autopilot')).toBe(true);
|
|
339
|
+
expect(validateMode('unknown')).toBe(false);
|
|
340
|
+
// 缺少非字符串类型测试
|
|
341
|
+
});
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// ✅ 正确:测试所有边界情况
|
|
346
|
+
it('should return false for non-string types', () => {
|
|
347
|
+
expect(validateMode(null)).toBe(false);
|
|
348
|
+
expect(validateMode(undefined)).toBe(false);
|
|
349
|
+
expect(validateMode(42)).toBe(false);
|
|
350
|
+
expect(validateMode(undefined)).toBe(false);
|
|
351
|
+
expect(validateMode([])).toBe(false);
|
|
352
|
+
});
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## 差异点索引
|
|
358
|
+
|
|
359
|
+
| 差异点 | 相关反模式 | 详细说明 |
|
|
360
|
+
|--------|-----------|---------|
|
|
361
|
+
| D-04 | AP-MR01 | 互斥模式为 4 个,非 PRD 原描述的 2 个 |
|
|
362
|
+
| D-07 | AP-C01 | writeTrackingStateImmediate 绕过原子保护(TD-4) |
|
|
363
|
+
| D-08 | AP-AL02 | 超时阈值:5 分钟(stale)vs 10 分钟(自动终止) |
|
|
364
|
+
| D-09 | AP-ST01 | stale 阈值:agent(5 分钟)vs mode(1 小时) |
|
|
365
|
+
| D-10 | AP-AL03 | DEADLOCK_CHECK_THRESHOLD 已定义但未使用 |
|