Skip to content

基于用户反馈的 Skills 改进

由 Markdown 原样翻译并转换为 Astro Starlight MDX 格式。

日期: 2025-11-28 状态: 草稿 来源: 两个 Claude 实例在真实开发场景中使用 superpowers


两个 Claude 实例提供了来自实际开发会话的详细反馈。他们的反馈揭示了当前技能中的系统性缺口,这些缺口使得本可预防的 bug 即使在遵循技能的情况下仍然被交付。

关键洞察: 这些是问题报告,而不仅仅是解决方案提议。问题是真实存在的;解决方案需要仔细评估。

关键主题:

  1. 验证缺口 - 我们验证操作成功,但没有验证它们是否实现了预期结果
  2. 流程卫生 - 后台进程会累积,并在子代理之间相互干扰
  3. 上下文优化 - 子代理获得了太多无关信息
  4. 缺少自我反思 - 在交接前没有提示批判自己的工作
  5. Mock 安全 - Mock 可能在未被检测到的情况下偏离接口
  6. 技能激活 - 技能存在,但没有被读取/使用

发生了什么:

  • 子代理测试“OpenAI 集成”
  • 设置了 OPENAI_API_KEY 环境变量
  • 收到状态码 200 响应
  • 报告“OpenAI 集成正常工作”
  • 但是 响应包含 "model": "claude-sonnet-4-20250514" - 实际上正在使用 Anthropic

根本原因: verification-before-completion 检查操作成功,但没有检查结果是否反映了预期的配置变更。

影响: 高 - 对集成测试产生错误信心,bug 被发布到生产环境

示例失败模式:

  • 切换 LLM 提供商 → 验证状态码 200,但不检查模型名称
  • 启用功能标志 → 验证没有错误,但不检查功能是否已激活
  • 更改环境 → 验证部署成功,但不检查环境变量

发生了什么:

  • 会话期间分派了多个子代理
  • 每个子代理都启动了后台服务器进程
  • 进程累积(4+ 个服务器在运行)
  • 陈旧进程仍然绑定到端口
  • 后续 E2E 测试命中了配置错误的陈旧服务器
  • 测试结果令人困惑/不正确

根本原因: 子代理是无状态的 - 不知道先前子代理启动的进程。没有清理协议。

影响: 中高 - 测试命中错误服务器,产生误报/误失败,调试混乱


问题 3:子代理提示中的上下文膨胀

Section titled “问题 3:子代理提示中的上下文膨胀”

发生了什么:

  • 标准做法:给子代理完整计划文件阅读
  • 实验做法:只给任务 + 模式 + 文件 + 验证命令
  • 结果:更快、更聚焦,更常见一次尝试完成

根本原因: 子代理在无关的计划章节上浪费 token 和注意力。

影响: 中 - 执行更慢,失败尝试更多

有效做法:

你正在向 packnplay 的测试套件添加一个单独的 E2E 测试。
**你的任务:** 将 `TestE2E_FeaturePrivilegedMode` 添加到 `pkg/runner/e2e_test.go`
**要测试什么:** 一个在其元数据中请求 `"privileged": true`
的本地 devcontainer feature 应该导致容器以 `--privileged` 标志运行。
**严格遵循 TestE2E_FeatureOptionValidation 的模式**(在文件末尾)
**编写后运行:** `go test -v ./pkg/runner -run TestE2E_FeaturePrivilegedMode -timeout 5m`

发生了什么:

  • 添加了自我反思提示:“用新鲜的眼光看你的工作 - 有什么可以做得更好?”
  • 任务 5 的实现者识别出失败测试是由实现 bug 导致的,而不是测试 bug
  • 追踪到第 99 行:strings.Join(metadata.Entrypoint, " ") 创建了无效的 Docker 语法
  • 如果没有自我反思,本来只会报告“测试失败”,而没有根本原因

根本原因: 实现者不会自然地退后一步,在报告完成前批判自己的工作。

影响: 中 - 本可由实现者发现的 bug 被交接给审查者


发生了什么:

// 接口定义 close()
interface PlatformAdapter {
close(): Promise<void>;
}
// 代码(有 BUG)调用 cleanup()
await adapter.cleanup();
// Mock(匹配 BUG)定义 cleanup()
vi.mock('web-adapter', () => ({
WebAdapter: vi.fn().mockImplementation(() => ({
cleanup: vi.fn().mockResolvedValue(undefined), // 错误!
})),
}));
  • 测试通过
  • 运行时崩溃:“adapter.cleanup is not a function”

根本原因: Mock 来源于有 bug 的代码调用内容,而不是接口定义。TypeScript 无法捕获方法名错误的内联 mock。

影响: 高 - 测试给出错误信心,运行时崩溃

为什么 testing-anti-patterns 没有阻止这一点: 该技能涵盖了测试 mock 行为和在不理解的情况下进行 mock,但没有涵盖“从接口派生 mock,而不是从实现派生”的具体模式。


发生了什么:

  • 分派了代码审查者子代理
  • 无法找到测试文件:“该文件似乎不存在于仓库中”
  • 文件实际上存在
  • 审查者不知道要先显式读取它

根本原因: 审查者提示中没有包含显式的文件读取指令。

影响: 低中 - 审查失败或不完整


发生了什么:

  • 实现者在自我反思期间识别出 bug
  • 实现者知道修复方法
  • 当前工作流:报告 → 我分派修复者 → 修复者修复 → 我验证
  • 额外往返增加了延迟,却没有增加价值

根本原因: 当实现者已经诊断出问题时,实现者和修复者角色之间仍然存在僵硬分离。

影响: 低 - 有延迟,但没有正确性问题


发生了什么:

  • testing-anti-patterns 技能存在
  • 人类和子代理在编写测试前都没有读取它
  • 本可以防止一些问题(虽然不是全部 - 见问题 5)

根本原因: 没有强制要求子代理读取相关技能。没有提示包含技能读取。

影响: 中 - 如果不使用,技能投资就被浪费


1. verification-before-completion:添加配置变更验证

Section titled “1. verification-before-completion:添加配置变更验证”

添加新章节:

## 验证配置变更
在测试配置、提供商、功能标志或环境的变更时:
**不要只验证操作成功。要验证输出反映了预期变更。**
### 常见失败模式
操作成功是因为存在*某个*有效配置,但它不是你想要测试的配置。
### 示例
| 变更 | 不充分 | 必需 |
|--------|-------------|----------|
| 切换 LLM 提供商 | 状态码 200 | 响应包含预期模型名称 |
| 启用功能标志 | 没有错误 | 功能行为实际处于激活状态 |
| 更改环境 | 部署成功 | 日志/变量引用新环境 |
| 设置凭据 | 认证成功 | 已认证用户/上下文正确 |
### 门控函数

在声明配置变更有效之前:

  1. 识别:这次变更后应该有什么不同?
  2. 定位:在哪里可以观察到该差异?
  • 响应字段(模型名称、用户 ID)
  • 日志行(环境、提供商)
  • 行为(功能激活/未激活)
  1. 运行:显示该可观察差异的命令
  2. 验证:输出包含预期差异
  3. 只有这时:声明配置变更有效

危险信号:

  • “请求成功”,但没有检查内容
  • 检查状态码但不检查响应体
  • 验证没有错误但没有正向确认
**为什么这有效:**
强制验证意图,而不仅仅是操作成功。
---
### 2. subagent-driven-development:为 E2E 测试添加进程卫生
**添加新章节:**
```markdown
## E2E 测试的进程卫生
当分派会启动服务(服务器、数据库、消息队列)的子代理时:
### 问题
子代理是无状态的 - 它们不知道先前子代理启动的进程。后台进程会持续存在,并可能干扰后续测试。
### 解决方案
**在分派 E2E 测试子代理前,在提示中包含清理:**

在启动任何服务之前:

  1. 杀掉现有进程:pkill -f "" 2>/dev/null || true
  2. 等待清理:sleep 1
  3. 验证端口空闲:lsof -i : && echo “ERROR: Port still in use” || echo “Port free”

测试完成之后:

  1. 杀掉你启动的进程
  2. 验证清理:pgrep -f "" || echo “Cleanup successful”
### 示例

任务:运行 API 服务器的 E2E 测试

提示包含: “在启动服务器之前:

  • 杀掉任何现有服务器:pkill -f ‘node.*server.js’ 2>/dev/null || true
  • 验证端口 3001 空闲:lsof -i :3001 && exit 1 || echo ‘Port available’

测试后:

  • 杀掉你启动的服务器
  • 验证:pgrep -f ‘node.*server.js’ || echo ‘Cleanup verified’”
### 为什么这很重要
- 陈旧进程使用错误配置提供请求
- 端口冲突导致静默失败
- 进程累积拖慢系统
- 测试结果令人困惑(命中错误服务器)

权衡分析:

  • 给提示增加样板内容
  • 但能防止非常令人困惑的调试
  • 对 E2E 测试子代理来说值得

3. subagent-driven-development:添加精简上下文选项

Section titled “3. subagent-driven-development:添加精简上下文选项”

修改步骤 2:用子代理执行任务

之前:

仔细阅读 [plan-file] 中的那个任务。

之后:

## 上下文方法
**完整计划(默认):**
当任务复杂或有依赖时使用:

仔细阅读 [plan-file] 中的任务 N。

**精简上下文(用于独立任务):**
当任务是独立且基于模式时使用:

你正在实现:[1-2 句话的任务描述]

要修改的文件:[确切路径] 要遵循的模式:[对现有函数/测试的引用] 要实现的内容:[具体要求] 验证:[要运行的确切命令]

[不要包含完整计划文件]

**在以下情况使用精简上下文:**
- 任务遵循现有模式(添加类似测试,实现类似功能)
- 任务是自包含的(不需要其他任务的上下文)
- 模式引用足够(例如,“遵循 TestE2E_FeatureOptionValidation”)
**在以下情况使用完整计划:**
- 任务依赖其他任务
- 需要理解整体架构
- 复杂逻辑需要上下文

示例:

精简上下文提示:
"你正在为 devcontainer features 中的 privileged mode 添加测试。
文件:pkg/runner/e2e_test.go
模式:遵循 TestE2E_FeatureOptionValidation(在文件末尾)
测试:元数据中带有 `"privileged": true` 的 Feature 会产生 `--privileged` 标志
验证:go test -v ./pkg/runner -run TestE2E_FeaturePrivilegedMode -timeout 5m
报告:实现、测试结果、任何问题。"

为什么这有效: 减少 token 使用,提高专注度,在适用时更快完成。


4. subagent-driven-development:添加自我反思步骤

Section titled “4. subagent-driven-development:添加自我反思步骤”

修改步骤 2:用子代理执行任务

添加到提示模板:

完成后,在报告之前:
退后一步,用新鲜的眼光审查你的工作。
问自己:
- 这是否真的按规范解决了任务?
- 是否有我没有考虑的边界情况?
- 我是否正确遵循了模式?
- 如果测试失败,根本原因是什么(实现 bug 还是测试 bug)?
- 这个实现有什么可以做得更好?
如果你在反思期间识别出问题,现在就修复它们。
然后报告:
- 你实现了什么
- 自我反思发现(如果有)
- 测试结果
- 变更的文件

为什么这有效: 在交接前捕获实现者自己可以发现的 bug。已有案例记录:通过自我反思识别出 entrypoint bug。

权衡: 每个任务增加约 30 秒,但能在审查前捕获问题。


5. requesting-code-review:添加显式文件读取

Section titled “5. requesting-code-review:添加显式文件读取”

修改 code-reviewer 模板:

在开头添加:

## 要审查的文件
在分析之前,读取这些文件:
1. [列出 diff 中变更的具体文件]
2. [被变更引用但未修改的文件]
使用 Read 工具加载每个文件。
如果你找不到某个文件:
- 检查 diff 中的确切路径
- 尝试替代位置
- 报告:"Cannot locate [path] - please verify file exists"
在你读取实际代码之前,不要继续审查。

为什么这有效: 显式指令防止“文件未找到”问题。


6. testing-anti-patterns:添加 Mock-接口漂移反模式

Section titled “6. testing-anti-patterns:添加 Mock-接口漂移反模式”

添加新的反模式 6:

## 反模式 6:从实现派生的 Mock
**违规:**
```typescript
// 代码(有 BUG)调用 cleanup()
await adapter.cleanup();
// Mock(匹配 BUG)有 cleanup()
const mock = {
cleanup: vi.fn().mockResolvedValue(undefined)
};
// 接口(正确)定义 close()
interface PlatformAdapter {
close(): Promise<void>;
}

为什么这是错误的:

  • Mock 将 bug 编码进测试
  • TypeScript 无法捕获方法名错误的内联 mock
  • 测试通过,因为代码和 mock 都是错的
  • 使用真实对象时运行时崩溃

修复:

// ✅ 好:从接口派生 mock
// 步骤 1:打开接口定义(PlatformAdapter)
// 步骤 2:列出其中定义的方法(close、initialize 等)
// 步骤 3:精确 mock 这些方法
const mock = {
initialize: vi.fn().mockResolvedValue(undefined),
close: vi.fn().mockResolvedValue(undefined), // 来自接口!
};
// 现在测试会失败,因为代码调用了不存在的 cleanup()
// 该失败会在运行时之前暴露 bug
在编写任何 mock 之前:
1. 停止 - 先不要查看被测代码
2. 查找:依赖项的接口/类型定义
3. 读取:接口文件
4. 列出:接口中定义的方法
5. MOCK:只 mock 这些方法,并且名称完全一致
6. 不要:查看你的代码调用了什么
如果你的测试因为代码调用了 mock 中不存在的内容而失败:
✅ 好 - 测试发现了代码中的 bug
修复代码,使其调用正确的接口方法
而不是修复 mock
危险信号:
- “我会 mock 代码调用的内容”
- 从实现中复制方法名
- 没有读取接口就编写 mock
- “测试失败了,所以我会把这个方法加到 mock 里”

检测:

当你看到运行时错误 “X is not a function” 而测试通过时:

  1. 检查 X 是否被 mock
  2. 将 mock 方法与接口方法进行比较
  3. 查找方法名不匹配
**为什么这有效:**
直接解决反馈中的失败模式。
---
### 7. subagent-driven-development:要求测试子代理读取技能
**当任务涉及测试时,添加到提示模板:**
```markdown
在编写任何测试之前:
1. 读取 testing-anti-patterns 技能:
使用 Skill 工具:superpowers:testing-anti-patterns
2. 在以下情况应用该技能中的门控函数:
- 编写 mock
- 向生产类添加方法
- mock 依赖项
这不是可选项。违反反模式的测试会在审查中被拒绝。

为什么这有效: 确保技能被实际使用,而不是仅仅存在。

权衡: 为每个任务增加时间,但防止整类 bug。


8. subagent-driven-development:允许实现者修复自我识别的问题

Section titled “8. subagent-driven-development:允许实现者修复自我识别的问题”

修改步骤 2:

当前:

子代理报告工作总结。

提议:

子代理执行自我反思,然后:
如果自我反思识别出可修复的问题:
1. 修复这些问题
2. 重新运行验证
3. 报告:“初始实现 + 自我反思修复”
否则:
报告:“实现完成”
在报告中包含:
- 自我反思发现
- 是否应用了修复
- 最终验证结果

为什么这有效: 当实现者已经知道修复方式时,减少延迟。已有案例记录:本可以为 entrypoint bug 节省一次往返。

权衡: 提示稍微更复杂,但端到端更快。


阶段 1:高影响、低风险(先做)

Section titled “阶段 1:高影响、低风险(先做)”
  1. verification-before-completion:配置变更验证
  • 清晰的新增内容,不改变现有内容
  • 解决高影响问题(对测试产生错误信心)
  • 文件:skills/verification-before-completion/SKILL.md
  1. testing-anti-patterns:Mock-接口漂移
  • 添加新的反模式,不修改现有内容
  • 解决高影响问题(运行时崩溃)
  • 文件:skills/testing-anti-patterns/SKILL.md
  1. requesting-code-review:显式文件读取
  • 对模板的简单补充
  • 修复具体问题(审查者找不到文件)
  • 文件:skills/requesting-code-review/SKILL.md
  1. subagent-driven-development:进程卫生
  • 添加新章节,不改变工作流
  • 解决中高影响问题(测试可靠性)
  • 文件:skills/subagent-driven-development/SKILL.md
  1. subagent-driven-development:自我反思
  • 更改提示模板(风险更高)
  • 但已有记录表明可捕获 bug
  • 文件:skills/subagent-driven-development/SKILL.md
  1. subagent-driven-development:技能读取要求
  • 增加提示开销
  • 但确保技能被实际使用
  • 文件:skills/subagent-driven-development/SKILL.md
  1. subagent-driven-development:精简上下文选项
  • 增加复杂性(两种方法)
  • 需要验证它不会造成混乱
  • 文件:skills/subagent-driven-development/SKILL.md
  1. subagent-driven-development:允许实现者修复
  • 更改工作流(风险更高)
  • 优化,而不是 bug 修复
  • 文件:skills/subagent-driven-development/SKILL.md

  1. 精简上下文方法:
  • 我们是否应该将其作为基于模式任务的默认方式?
  • 我们如何决定使用哪种方法?
  • 是否存在过于精简而遗漏重要上下文的风险?
  1. 自我反思:
  • 这会显著拖慢简单任务吗?
  • 它是否应该只适用于复杂任务?
  • 我们如何防止“反思疲劳”,即它变成机械流程?
  1. 进程卫生:
  • 它应该放在 subagent-driven-development 中,还是作为单独技能?
  • 它是否适用于 E2E 测试之外的其他工作流?
  • 我们如何处理进程应该持续存在的情况(开发服务器)?
  1. 技能读取强制:
  • 我们是否应该要求所有子代理读取相关技能?
  • 我们如何防止提示变得过长?
  • 是否存在过度文档化而失去焦点的风险?

我们如何知道这些改进有效?

  1. 配置验证:
  • “测试通过但使用了错误配置”的情况为零
  • Jesse 不会说“那实际上并没有测试你以为自己在测试的东西”
  1. 进程卫生:
  • “测试命中错误服务器”的情况为零
  • E2E 测试运行期间没有端口冲突错误
  1. Mock-接口漂移:
  • “测试通过但运行时因缺失方法崩溃”的情况为零
  • mock 与接口之间没有方法名不匹配
  1. 自我反思:
  • 可衡量:实现者报告是否包含自我反思发现?
  • 定性:进入代码审查的 bug 是否更少?
  1. 技能读取:
  • 子代理报告引用技能门控函数
  • 代码审查中的反模式违规更少

问题: 添加所有这些要求会让提示变得难以承受 缓解:

  • 分阶段实施(不要一次添加所有内容)
  • 让一些新增项成为条件性内容(E2E 卫生只用于 E2E 测试)
  • 考虑为不同任务类型创建模板

问题: 过多反思/验证会拖慢执行 缓解:

  • 保持门控函数快速(几秒,而不是几分钟)
  • 初始阶段让精简上下文作为可选项
  • 监控任务完成时间

问题: 遵循清单并不能保证正确性 缓解:

  • 强调门控函数是最低要求,而不是最高要求
  • 在技能中保留“使用判断”的语言
  • 说明技能能捕获常见失败,而不是所有失败

问题: 不同技能给出相互冲突的建议 缓解:

  • 跨所有技能审查变更以保持一致性
  • 记录技能如何交互(集成章节)
  • 部署前用真实场景测试

立即推进阶段 1:

  • verification-before-completion:配置变更验证
  • testing-anti-patterns:Mock-接口漂移
  • requesting-code-review:显式文件读取

在最终确定前与 Jesse 测试阶段 2:

  • 获取关于自我反思影响的反馈
  • 验证进程卫生方法
  • 确认技能读取要求值得其开销

暂缓阶段 3,等待验证:

  • 精简上下文需要真实世界测试
  • 实现者修复工作流变更需要仔细评估

这些变更解决了用户记录的真实问题,同时最大限度降低让技能变得更糟的风险。

-
0:000:00