@huajiwuyan/hello 3.0.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/README.md +68 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +448 -0
- package/package.json +38 -0
- package/templates/claude/commands/hello.md +760 -0
- package/templates/claude/skills/SKILL.md +90 -0
- package/templates/claude/skills/SKILL.toml +7 -0
- package/templates/claude/skills/assets/icon-large.svg +12 -0
- package/templates/claude/skills/assets/icon-small-400px.svg +12 -0
- package/templates/claude/skills/assets/templates/CHANGELOG.md +24 -0
- package/templates/claude/skills/assets/templates/CHANGELOG_{YYYY}.md +25 -0
- package/templates/claude/skills/assets/templates/INDEX.md +36 -0
- package/templates/claude/skills/assets/templates/archive/_index.md +22 -0
- package/templates/claude/skills/assets/templates/context.md +82 -0
- package/templates/claude/skills/assets/templates/modules/_index.md +22 -0
- package/templates/claude/skills/assets/templates/modules/module.md +35 -0
- package/templates/claude/skills/assets/templates/plan/proposal.md +104 -0
- package/templates/claude/skills/assets/templates/plan/tasks.md +49 -0
- package/templates/claude/skills/references/functions/auto.md +217 -0
- package/templates/claude/skills/references/functions/clean.md +167 -0
- package/templates/claude/skills/references/functions/commit.md +374 -0
- package/templates/claude/skills/references/functions/exec.md +178 -0
- package/templates/claude/skills/references/functions/help.md +105 -0
- package/templates/claude/skills/references/functions/init.md +228 -0
- package/templates/claude/skills/references/functions/plan.md +219 -0
- package/templates/claude/skills/references/functions/review.md +146 -0
- package/templates/claude/skills/references/functions/rollback.md +208 -0
- package/templates/claude/skills/references/functions/test.md +153 -0
- package/templates/claude/skills/references/functions/upgrade.md +371 -0
- package/templates/claude/skills/references/functions/validate.md +147 -0
- package/templates/claude/skills/references/rules/package.md +212 -0
- package/templates/claude/skills/references/rules/scaling.md +150 -0
- package/templates/claude/skills/references/rules/state.md +318 -0
- package/templates/claude/skills/references/rules/tools.md +371 -0
- package/templates/claude/skills/references/services/knowledge.md +408 -0
- package/templates/claude/skills/references/services/templates.md +344 -0
- package/templates/claude/skills/references/stages/analyze.md +201 -0
- package/templates/claude/skills/references/stages/design.md +379 -0
- package/templates/claude/skills/references/stages/develop.md +497 -0
- package/templates/claude/skills/references/stages/evaluate.md +286 -0
- package/templates/claude/skills/references/stages/tweak.md +244 -0
- package/templates/claude/skills/scripts/create_package.py +260 -0
- package/templates/claude/skills/scripts/list_packages.py +145 -0
- package/templates/claude/skills/scripts/migrate_package.py +399 -0
- package/templates/claude/skills/scripts/project_stats.py +438 -0
- package/templates/claude/skills/scripts/upgradewiki.py +321 -0
- package/templates/claude/skills/scripts/utils.py +596 -0
- package/templates/claude/skills/scripts/validate_package.py +309 -0
- package/templates/codex/prompts/hello.md +757 -0
- package/templates/codex/skills/SKILL.md +74 -0
- package/templates/codex/skills/SKILL.toml +7 -0
- package/templates/codex/skills/assets/icon-large.svg +12 -0
- package/templates/codex/skills/assets/icon-small-400px.svg +12 -0
- package/templates/codex/skills/assets/templates/CHANGELOG.md +24 -0
- package/templates/codex/skills/assets/templates/CHANGELOG_{YYYY}.md +25 -0
- package/templates/codex/skills/assets/templates/INDEX.md +36 -0
- package/templates/codex/skills/assets/templates/archive/_index.md +22 -0
- package/templates/codex/skills/assets/templates/context.md +82 -0
- package/templates/codex/skills/assets/templates/modules/_index.md +22 -0
- package/templates/codex/skills/assets/templates/modules/module.md +35 -0
- package/templates/codex/skills/assets/templates/plan/proposal.md +104 -0
- package/templates/codex/skills/assets/templates/plan/tasks.md +29 -0
- package/templates/codex/skills/references/functions/auto.md +181 -0
- package/templates/codex/skills/references/functions/brain.md +275 -0
- package/templates/codex/skills/references/functions/clean.md +154 -0
- package/templates/codex/skills/references/functions/commit.md +265 -0
- package/templates/codex/skills/references/functions/debug/condition-based-waiting.md +151 -0
- package/templates/codex/skills/references/functions/debug/defense-in-depth.md +147 -0
- package/templates/codex/skills/references/functions/debug/root-cause-tracing.md +168 -0
- package/templates/codex/skills/references/functions/debug.md +389 -0
- package/templates/codex/skills/references/functions/exec.md +153 -0
- package/templates/codex/skills/references/functions/help.md +101 -0
- package/templates/codex/skills/references/functions/init.md +221 -0
- package/templates/codex/skills/references/functions/plan.md +178 -0
- package/templates/codex/skills/references/functions/review.md +135 -0
- package/templates/codex/skills/references/functions/rlm.md +864 -0
- package/templates/codex/skills/references/functions/rollback.md +190 -0
- package/templates/codex/skills/references/functions/test.md +140 -0
- package/templates/codex/skills/references/functions/upgrade.md +363 -0
- package/templates/codex/skills/references/functions/validate.md +135 -0
- package/templates/codex/skills/references/rules/cache.md +136 -0
- package/templates/codex/skills/references/rules/scaling.md +124 -0
- package/templates/codex/skills/references/rules/state.md +201 -0
- package/templates/codex/skills/references/rules/tools.md +301 -0
- package/templates/codex/skills/references/services/attention.md +53 -0
- package/templates/codex/skills/references/services/knowledge.md +559 -0
- package/templates/codex/skills/references/services/package.md +383 -0
- package/templates/codex/skills/references/services/templates.md +390 -0
- package/templates/codex/skills/references/stages/analyze.md +191 -0
- package/templates/codex/skills/references/stages/design.md +355 -0
- package/templates/codex/skills/references/stages/develop.md +520 -0
- package/templates/codex/skills/references/stages/tweak.md +239 -0
- package/templates/codex/skills/rlm/__init__.py +39 -0
- package/templates/codex/skills/rlm/agent_orchestrator.py +422 -0
- package/templates/codex/skills/rlm/context_manager.py +366 -0
- package/templates/codex/skills/rlm/engine.py +915 -0
- package/templates/codex/skills/rlm/folding.py +391 -0
- package/templates/codex/skills/rlm/repl.py +452 -0
- package/templates/codex/skills/rlm/roles/analyzer.md +66 -0
- package/templates/codex/skills/rlm/roles/designer.md +94 -0
- package/templates/codex/skills/rlm/roles/explorer.md +43 -0
- package/templates/codex/skills/rlm/roles/implementer.md +62 -0
- package/templates/codex/skills/rlm/roles/kb_keeper.md +138 -0
- package/templates/codex/skills/rlm/roles/pkg_keeper.md +163 -0
- package/templates/codex/skills/rlm/roles/reviewer.md +74 -0
- package/templates/codex/skills/rlm/roles/synthesizer.md +90 -0
- package/templates/codex/skills/rlm/roles/tester.md +83 -0
- package/templates/codex/skills/rlm/schemas/agent_result.json +174 -0
- package/templates/codex/skills/rlm/session.py +376 -0
- package/templates/codex/skills/rlm/shared_tasks.py +370 -0
- package/templates/codex/skills/scripts/create_package.py +260 -0
- package/templates/codex/skills/scripts/list_packages.py +145 -0
- package/templates/codex/skills/scripts/migrate_package.py +399 -0
- package/templates/codex/skills/scripts/project_stats.py +438 -0
- package/templates/codex/skills/scripts/upgradewiki.py +321 -0
- package/templates/codex/skills/scripts/utils.py +596 -0
- package/templates/codex/skills/scripts/validate_package.py +309 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# 条件等待技术
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
不稳定的测试通常用任意延迟来猜测时序。这会产生竞态条件:测试在快速机器上通过,但在负载下或 CI 中失败。
|
|
6
|
+
|
|
7
|
+
**核心原则:** 等待你真正关心的条件,而不是猜测需要多长时间。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 适用场景
|
|
12
|
+
|
|
13
|
+
**使用时机:**
|
|
14
|
+
- 测试中有任意延迟(`setTimeout`、`sleep`、`time.sleep()`)
|
|
15
|
+
- 测试不稳定(有时通过,负载下失败)
|
|
16
|
+
- 并行运行时测试超时
|
|
17
|
+
- 等待异步操作完成
|
|
18
|
+
|
|
19
|
+
**不使用时机:**
|
|
20
|
+
- 测试实际的时序行为(防抖、节流间隔)
|
|
21
|
+
- 如果使用任意超时,必须注释说明原因
|
|
22
|
+
|
|
23
|
+
**决策流程:**
|
|
24
|
+
```
|
|
25
|
+
测试使用 setTimeout/sleep? → 测试时序行为? → 是 → 注释说明原因
|
|
26
|
+
↓ 否
|
|
27
|
+
使用条件等待
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 核心模式
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// ❌ 之前: 猜测时序
|
|
36
|
+
await new Promise(r => setTimeout(r, 50));
|
|
37
|
+
const result = getResult();
|
|
38
|
+
expect(result).toBeDefined();
|
|
39
|
+
|
|
40
|
+
// ✅ 之后: 等待条件
|
|
41
|
+
await waitFor(() => getResult() !== undefined);
|
|
42
|
+
const result = getResult();
|
|
43
|
+
expect(result).toBeDefined();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 快速参考
|
|
49
|
+
|
|
50
|
+
| 场景 | 模式 |
|
|
51
|
+
|------|------|
|
|
52
|
+
| 等待事件 | `waitFor(() => events.find(e => e.type === 'DONE'))` |
|
|
53
|
+
| 等待状态 | `waitFor(() => machine.state === 'ready')` |
|
|
54
|
+
| 等待数量 | `waitFor(() => items.length >= 5)` |
|
|
55
|
+
| 等待文件 | `waitFor(() => fs.existsSync(path))` |
|
|
56
|
+
| 复杂条件 | `waitFor(() => obj.ready && obj.value > 10)` |
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 实现
|
|
61
|
+
|
|
62
|
+
通用轮询函数:
|
|
63
|
+
```typescript
|
|
64
|
+
async function waitFor<T>(
|
|
65
|
+
condition: () => T | undefined | null | false,
|
|
66
|
+
description: string,
|
|
67
|
+
timeoutMs = 5000
|
|
68
|
+
): Promise<T> {
|
|
69
|
+
const startTime = Date.now();
|
|
70
|
+
|
|
71
|
+
while (true) {
|
|
72
|
+
const result = condition();
|
|
73
|
+
if (result) return result;
|
|
74
|
+
|
|
75
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
76
|
+
throw new Error(`等待 ${description} 超时,已等待 ${timeoutMs}ms`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
await new Promise(r => setTimeout(r, 10)); // 每 10ms 轮询一次
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Python 版本:**
|
|
85
|
+
```python
|
|
86
|
+
import time
|
|
87
|
+
|
|
88
|
+
def wait_for(condition, description, timeout_sec=5):
|
|
89
|
+
start_time = time.time()
|
|
90
|
+
|
|
91
|
+
while True:
|
|
92
|
+
result = condition()
|
|
93
|
+
if result:
|
|
94
|
+
return result
|
|
95
|
+
|
|
96
|
+
if time.time() - start_time > timeout_sec:
|
|
97
|
+
raise TimeoutError(f"等待 {description} 超时,已等待 {timeout_sec}s")
|
|
98
|
+
|
|
99
|
+
time.sleep(0.01) # 每 10ms 轮询一次
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 常见错误
|
|
105
|
+
|
|
106
|
+
**❌ 轮询太快:** `setTimeout(check, 1)` - 浪费 CPU
|
|
107
|
+
**✅ 修复:** 每 10ms 轮询一次
|
|
108
|
+
|
|
109
|
+
**❌ 无超时:** 条件不满足时永远循环
|
|
110
|
+
**✅ 修复:** 始终包含超时和清晰的错误信息
|
|
111
|
+
|
|
112
|
+
**❌ 陈旧数据:** 在循环前缓存状态
|
|
113
|
+
**✅ 修复:** 在循环内调用 getter 获取新鲜数据
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 何时使用任意超时是正确的
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// 工具每 100ms tick 一次 - 需要 2 次 tick 来验证部分输出
|
|
121
|
+
await waitForEvent(manager, 'TOOL_STARTED'); // 首先: 等待条件
|
|
122
|
+
await new Promise(r => setTimeout(r, 200)); // 然后: 等待定时行为
|
|
123
|
+
// 200ms = 100ms 间隔的 2 次 tick - 有文档说明和理由
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**要求:**
|
|
127
|
+
1. 首先等待触发条件
|
|
128
|
+
2. 基于已知时序(不是猜测)
|
|
129
|
+
3. 注释解释原因
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 实际影响
|
|
134
|
+
|
|
135
|
+
来自调试会话:
|
|
136
|
+
- 修复了 3 个文件中的 15 个不稳定测试
|
|
137
|
+
- 通过率: 60% → 100%
|
|
138
|
+
- 执行时间: 快 40%
|
|
139
|
+
- 不再有竞态条件
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 检查清单
|
|
144
|
+
|
|
145
|
+
重构异步测试时:
|
|
146
|
+
|
|
147
|
+
- [ ] 用 `waitFor()` 替换 `setTimeout/sleep`
|
|
148
|
+
- [ ] 条件检查在轮询中获取新鲜数据
|
|
149
|
+
- [ ] 超时有清晰的错误信息
|
|
150
|
+
- [ ] 如果保留任意延迟,注释说明原因
|
|
151
|
+
- [ ] 测试在负载下运行稳定
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# 纵深防御验证
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
当你修复一个由无效数据引起的 Bug 时,在一个地方添加验证似乎已经足够。但单一检查可能被不同的代码路径、重构或 Mock 绕过。
|
|
6
|
+
|
|
7
|
+
**核心原则:** 在数据经过的每一层都进行验证。让 Bug 在结构上不可能发生。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 为什么需要多层验证
|
|
12
|
+
|
|
13
|
+
单一验证: "我们修复了 Bug"
|
|
14
|
+
多层验证: "我们让 Bug 变得不可能"
|
|
15
|
+
|
|
16
|
+
不同层捕获不同情况:
|
|
17
|
+
- 入口验证捕获大多数 Bug
|
|
18
|
+
- 业务逻辑捕获边界情况
|
|
19
|
+
- 环境守卫防止特定上下文的危险
|
|
20
|
+
- 调试日志在其他层失效时帮助定位
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 四层防御
|
|
25
|
+
|
|
26
|
+
### 第1层: 入口点验证
|
|
27
|
+
|
|
28
|
+
**目的:** 在 API 边界拒绝明显无效的输入
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
function createProject(name: string, workingDirectory: string) {
|
|
32
|
+
if (!workingDirectory || workingDirectory.trim() === '') {
|
|
33
|
+
throw new Error('workingDirectory 不能为空');
|
|
34
|
+
}
|
|
35
|
+
if (!existsSync(workingDirectory)) {
|
|
36
|
+
throw new Error(`workingDirectory 不存在: ${workingDirectory}`);
|
|
37
|
+
}
|
|
38
|
+
if (!statSync(workingDirectory).isDirectory()) {
|
|
39
|
+
throw new Error(`workingDirectory 不是目录: ${workingDirectory}`);
|
|
40
|
+
}
|
|
41
|
+
// ... 继续处理
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 第2层: 业务逻辑验证
|
|
46
|
+
|
|
47
|
+
**目的:** 确保数据对当前操作有意义
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
function initializeWorkspace(projectDir: string, sessionId: string) {
|
|
51
|
+
if (!projectDir) {
|
|
52
|
+
throw new Error('工作空间初始化需要 projectDir');
|
|
53
|
+
}
|
|
54
|
+
// ... 继续处理
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 第3层: 环境守卫
|
|
59
|
+
|
|
60
|
+
**目的:** 在特定上下文中防止危险操作
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
async function gitInit(directory: string) {
|
|
64
|
+
// 在测试中,拒绝在临时目录外执行 git init
|
|
65
|
+
if (process.env.NODE_ENV === 'test') {
|
|
66
|
+
const normalized = normalize(resolve(directory));
|
|
67
|
+
const tmpDir = normalize(resolve(tmpdir()));
|
|
68
|
+
|
|
69
|
+
if (!normalized.startsWith(tmpDir)) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`测试期间拒绝在临时目录外执行 git init: ${directory}`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// ... 继续处理
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 第4层: 调试诊断
|
|
80
|
+
|
|
81
|
+
**目的:** 为取证捕获上下文
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
async function gitInit(directory: string) {
|
|
85
|
+
const stack = new Error().stack;
|
|
86
|
+
logger.debug('即将执行 git init', {
|
|
87
|
+
directory,
|
|
88
|
+
cwd: process.cwd(),
|
|
89
|
+
stack,
|
|
90
|
+
});
|
|
91
|
+
// ... 继续处理
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 应用模式
|
|
98
|
+
|
|
99
|
+
当你发现一个 Bug 时:
|
|
100
|
+
|
|
101
|
+
1. **追踪数据流** - 错误值从哪来?在哪使用?
|
|
102
|
+
2. **映射所有检查点** - 列出数据经过的每个点
|
|
103
|
+
3. **在每层添加验证** - 入口、业务、环境、调试
|
|
104
|
+
4. **测试每层** - 尝试绕过第1层,验证第2层能捕获
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 实际示例
|
|
109
|
+
|
|
110
|
+
**Bug:** 空 `projectDir` 导致 `git init` 在源代码中执行
|
|
111
|
+
|
|
112
|
+
**数据流:**
|
|
113
|
+
1. 测试设置 → 空字符串
|
|
114
|
+
2. `Project.create('name', '')`
|
|
115
|
+
3. `WorkspaceManager.createWorkspace('')`
|
|
116
|
+
4. `git init` 在 `process.cwd()` 中运行
|
|
117
|
+
|
|
118
|
+
**添加的四层防御:**
|
|
119
|
+
- 第1层: `Project.create()` 验证非空/存在/可写
|
|
120
|
+
- 第2层: `WorkspaceManager` 验证 projectDir 非空
|
|
121
|
+
- 第3层: `WorktreeManager` 测试中拒绝在 tmpdir 外执行 git init
|
|
122
|
+
- 第4层: git init 前的堆栈跟踪日志
|
|
123
|
+
|
|
124
|
+
**结果:** 全部 1847 个测试通过,Bug 无法复现
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 关键洞察
|
|
129
|
+
|
|
130
|
+
四层防御都是必要的。在测试过程中,每层都捕获了其他层遗漏的 Bug:
|
|
131
|
+
- 不同代码路径绕过了入口验证
|
|
132
|
+
- Mock 绕过了业务逻辑检查
|
|
133
|
+
- 不同平台的边界情况需要环境守卫
|
|
134
|
+
- 调试日志识别了结构性误用
|
|
135
|
+
|
|
136
|
+
**不要止步于一个验证点。** 在每层都添加检查。
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 检查清单
|
|
141
|
+
|
|
142
|
+
修复 Bug 后,确认以下各层:
|
|
143
|
+
|
|
144
|
+
- [ ] **第1层 - 入口验证:** 公共 API 是否验证了所有输入?
|
|
145
|
+
- [ ] **第2层 - 业务验证:** 内部函数是否检查了前置条件?
|
|
146
|
+
- [ ] **第3层 - 环境守卫:** 危险操作是否有上下文保护?
|
|
147
|
+
- [ ] **第4层 - 调试诊断:** 关键操作前是否有日志记录?
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# 根因追踪技术
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
Bug 通常在调用栈深处显现(git init 在错误目录执行、文件创建在错误位置、数据库使用错误路径打开)。你的直觉是在错误出现的地方修复,但那只是在治疗症状。
|
|
6
|
+
|
|
7
|
+
**核心原则:** 沿调用链反向追踪,直到找到原始触发点,然后在源头修复。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 适用场景
|
|
12
|
+
|
|
13
|
+
**使用时机:**
|
|
14
|
+
- 错误发生在执行深处(不在入口点)
|
|
15
|
+
- 堆栈跟踪显示长调用链
|
|
16
|
+
- 不清楚无效数据的来源
|
|
17
|
+
- 需要找到哪个测试/代码触发了问题
|
|
18
|
+
|
|
19
|
+
**决策流程:**
|
|
20
|
+
```
|
|
21
|
+
Bug在调用栈深处? → 能反向追踪? → 追踪到原始触发点 → 修复 + 添加纵深防御
|
|
22
|
+
↓ 否
|
|
23
|
+
在症状点修复(不推荐)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 追踪流程
|
|
29
|
+
|
|
30
|
+
### 1. 观察症状
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
错误: git init 在 /Users/jesse/project/packages/core 失败
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. 找到直接原因
|
|
37
|
+
|
|
38
|
+
**什么代码直接导致了这个问题?**
|
|
39
|
+
```typescript
|
|
40
|
+
await execFileAsync('git', ['init'], { cwd: projectDir });
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 3. 问: 谁调用了它?
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
WorktreeManager.createSessionWorktree(projectDir, sessionId)
|
|
47
|
+
→ 被 Session.initializeWorkspace() 调用
|
|
48
|
+
→ 被 Session.create() 调用
|
|
49
|
+
→ 被 Project.create() 的测试调用
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 4. 继续向上追踪
|
|
53
|
+
|
|
54
|
+
**传递了什么值?**
|
|
55
|
+
- `projectDir = ''`(空字符串!)
|
|
56
|
+
- 空字符串作为 `cwd` 会解析为 `process.cwd()`
|
|
57
|
+
- 那就是源代码目录!
|
|
58
|
+
|
|
59
|
+
### 5. 找到原始触发点
|
|
60
|
+
|
|
61
|
+
**空字符串从哪来?**
|
|
62
|
+
```typescript
|
|
63
|
+
const context = setupCoreTest(); // 返回 { tempDir: '' }
|
|
64
|
+
Project.create('name', context.tempDir); // 在 beforeEach 之前访问了!
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 添加堆栈跟踪
|
|
70
|
+
|
|
71
|
+
当无法手动追踪时,添加诊断代码:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// 在问题操作之前
|
|
75
|
+
async function gitInit(directory: string) {
|
|
76
|
+
const stack = new Error().stack;
|
|
77
|
+
console.error('DEBUG git init:', {
|
|
78
|
+
directory,
|
|
79
|
+
cwd: process.cwd(),
|
|
80
|
+
nodeEnv: process.env.NODE_ENV,
|
|
81
|
+
stack,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await execFileAsync('git', ['init'], { cwd: directory });
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**关键:** 在测试中使用 `console.error()`(不用 logger - 可能不显示)
|
|
89
|
+
|
|
90
|
+
**运行并捕获:**
|
|
91
|
+
```bash
|
|
92
|
+
npm test 2>&1 | grep 'DEBUG git init'
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**分析堆栈跟踪:**
|
|
96
|
+
- 查找测试文件名
|
|
97
|
+
- 找到触发调用的行号
|
|
98
|
+
- 识别模式(同一个测试?同一个参数?)
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 找出哪个测试造成污染
|
|
103
|
+
|
|
104
|
+
如果在测试期间出现问题但不知道是哪个测试:
|
|
105
|
+
|
|
106
|
+
**二分法查找:**
|
|
107
|
+
```bash
|
|
108
|
+
# 逐个运行测试,找到第一个污染者
|
|
109
|
+
npm test -- --testNamePattern="TestA" && check_pollution
|
|
110
|
+
npm test -- --testNamePattern="TestB" && check_pollution
|
|
111
|
+
# ...
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 实际示例: 空 projectDir
|
|
117
|
+
|
|
118
|
+
**症状:** `.git` 在 `packages/core/`(源代码)中创建
|
|
119
|
+
|
|
120
|
+
**追踪链:**
|
|
121
|
+
1. `git init` 在 `process.cwd()` 中运行 ← cwd 参数为空
|
|
122
|
+
2. WorktreeManager 被传入空 projectDir
|
|
123
|
+
3. Session.create() 传递了空字符串
|
|
124
|
+
4. 测试在 beforeEach 之前访问了 `context.tempDir`
|
|
125
|
+
5. setupCoreTest() 最初返回 `{ tempDir: '' }`
|
|
126
|
+
|
|
127
|
+
**根因:** 顶层变量初始化时访问了空值
|
|
128
|
+
|
|
129
|
+
**修复:** 将 tempDir 改为 getter,在 beforeEach 之前访问时抛出异常
|
|
130
|
+
|
|
131
|
+
**同时添加纵深防御:**
|
|
132
|
+
- 第1层: Project.create() 验证目录
|
|
133
|
+
- 第2层: WorkspaceManager 验证非空
|
|
134
|
+
- 第3层: NODE_ENV 守卫拒绝在测试中于 tmpdir 外执行 git init
|
|
135
|
+
- 第4层: git init 前的堆栈跟踪日志
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 核心原则
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
找到直接原因
|
|
143
|
+
↓
|
|
144
|
+
能向上追溯一层? → 是 → 反向追踪 → 是源头吗? → 是 → 在源头修复 → 每层添加验证 → Bug不可能发生
|
|
145
|
+
↓ 否 ↓ 否
|
|
146
|
+
绝不只修复症状! 继续追踪
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**绝不只在错误出现的地方修复。** 反向追踪找到原始触发点。
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 堆栈跟踪技巧
|
|
154
|
+
|
|
155
|
+
- **在测试中:** 使用 `console.error()` 而非 logger - logger 可能被抑制
|
|
156
|
+
- **操作之前:** 在危险操作前记录日志,而非失败后
|
|
157
|
+
- **包含上下文:** 目录、cwd、环境变量、时间戳
|
|
158
|
+
- **捕获堆栈:** `new Error().stack` 显示完整调用链
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 实际影响
|
|
163
|
+
|
|
164
|
+
来自调试会话:
|
|
165
|
+
- 通过5层追踪找到根因
|
|
166
|
+
- 在源头修复(getter 验证)
|
|
167
|
+
- 添加4层防御
|
|
168
|
+
- 1847个测试通过,零污染
|