Install
$ agentstack add skill-lion-1209-lion-skills-verify-and-fix ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Verify and Fix
概述
把"声称完成"变成"经验证完成"。核心:代码写完 ≠ 测试过 ≠ 类型对 ≠ 真的没问题——这三个"≠"是大多数返工和线上事故的源头。本 skill 的纪律是:交付前用工具实际跑一遍验证,修 bug 时找根因而非压症状,绝不为"通过"而弱化检查。
> 灵感来自 multi-agent "loop engineering"(写代码的 agent 与检查的 agent 分离、循环到全绿)。但本 skill 提炼的是纪律本身——无论单 agent 迭代还是多 agent loop,验证-修复的纪律不变。loop 是一种强制实现,纪律才是本质。
何时使用
- 刚写完代码,准备声称"完成"/"修好了"
- 修 bug,决定怎么修
- 测试/类型检查报错,要处理
- 来回改同一处反复出问题(怀疑在原地打转)
不该用:纯探索/原型阶段(本来就不要求正确,验证是负担);明确无失败可能的确定性逻辑(过度验证也是负担)。
与相邻 skill 的衔接:verify-and-fix 在 task-breakdown 的下游——每个任务的"完成定义"就是它的验证目标。task 拆出"做完 X 后能验证 Y",verify-and-fix 负责"实际去验证 Y、不通过就修"。它把 task-breakdown 里的"完成定义"从纸面标准变成实际跑过的证据。
核心内容
第一原则:未经运行的代码不算完成
"我看了一遍,应该没问题"——这是最危险的完成声明。代码审查(肉眼看)不能替代运行验证。肉眼能发现风格、明显逻辑错,但发现不了:实际运行时的类型不匹配、边界数据触发的分支、依赖交互、并发时序。
完成的标准必须是实际跑过的证据:
- 有测试 → 跑测试,全绿才算过
- 有类型系统(TS/带 type hint 的 Python/Rust)→ 跑类型检查
- 能跑 → 实际跑一遍目标场景
- 三者都要,不是任选其一(测试过不代表类型对,类型对不代表运行时分支都对)
重构场景尤其要防回归:把 for 循环换成 find、换库、提函数、改异步为同步——这类"等价改写"最易悄悄丢失原代码处理的边界(原循环在没找到时可能返回 null/抛业务错,find 改写后这个分支没了)。验证重构时,问自己:原代码处理过哪些情况?新写法每一条都覆盖了吗? 尤其是边界——空集合、找不到、异常输入。重构的验证标准比新写更高:新写只验"能跑",重构还要验"行为没变"。
> 反例:用户说"我把 for 循环改成 find,应该没问题",你附和"嗯看着对"——但 find 可能返回 undefined,.name 就抛错。这种 bug 肉眼看不出来,跑一下立刻暴露。
修病因,不修症状
修 bug 最常见的错法:在报错末端兜底,把错误压住,而不查它为什么报错。
- 报错
Cannot read 'map' of undefined→ 加可选链data?.map(...)压住。错误消失了,但data为什么是 undefined 的病因还在——换个场景又会炸。 - 测试报
result is 4, expected 5→ 把断言改成toBe(4)。测试变绿了,但"为什么是 4 而不是 5"的问题被掩盖。
区分"合理兜底"和"掩盖 bug":
- 合理兜底:值合法地可能为空(如可选字段、外部数据可能缺失),兜底是设计的一部分。例:
user.nickname ?? user.name——昵称本来就可不填。 - 掩盖 bug:值本不该为空却空了——说明上游有 bug(数据源问题、初始化遗漏、路径没覆盖)。这时兜底只是把火盖住,火还在烧。
判断尺子:问"这个 undefined/null 在什么情况下出现?"——能说出一个合理的业务场景("用户没填昵称")→ 合理兜底;说不清、或答"反正运行时出现了"→ 是 bug,查源头。
修病因的路径:沿错误向上游追溯——报错点 ← 数据从哪来 ← 谁产生/传递的 ← 什么条件下变成这样。修在最上游的"产生"处,而不是最下游的"消费"处。
断言失败时的灰度——先确认期望值的权威性。测试报"实际 4,期望 5"时,别本能地认定"代码错了、断言对"。先问:期望值 5 从哪来? 是规格文档/业务约定的硬需求 → 代码错了,修代码;还是开发者随手写的猜测("我觉得应该是 5")→ 可能断言本身错了。区分两种"改断言":
- 错的改断言:期望值有权威来源(规格/约定),为了通过把它改成实际值——这是弱化检查。
- 对的改断言:发现期望值本身就是错的(规格已改、当初写错了),改成正确的期望值——这是修正错误期望,不是放水。
判断尺子:改完的断言,描述的是"应该怎样"还是"现在怎样"? 前者(哪怕是改出来的)可能正确;后者(让断言迁就现状)一定错。
红线:绝不为"通过"弱化检查
这是不可越界的底线。当测试/类型检查挡路时,正确的反应是修代码让它真正通过,错误的是改检查让它放行:
- ❌ 把断言从期望值改成实际值(
toBe(5)→toBe(4))——测试退化成"描述现状",失去验证意义 - ❌ 删掉报错的测试
- ❌
@ts-ignore/any压住类型错(除非有明确临时理由 + 留 TODO) - ❌ 用
try/catch吞掉异常让流程"通过" - ❌
|| []/|| {}给本该有值的东西兜个默认值让它"不报错"
为什么这条是红线:弱化检查制造的是欺骗性通过——绿灯看起来像"完成",实际是"绕过了验证"。它比直接失败更危险,因为失败会逼你修,欺骗性通过会让你带着 bug 交付。检查挡路是信号,不是障碍——它在告诉你"这里有真实问题"。
唯一例外:临时调试想跑通流程时可以临时禁用某项检查,但必须留显式标记(TODO + 理由),且不进主分支。
保留原始失败信息
修 bug 时,失败报告(报错、堆栈、行号、复现步骤)是定位根因的唯一线索。别在传递过程中"总结"或"过滤"它:
- 差:"测试挂了,有个 bug"——builder/你自己无从下手,只能瞎猜。
- 好:完整保留报错原文、行号、堆栈、触发场景。"
TypeError: Cannot read 'name' of undefined at user.js:12,调用栈:getProfile←render,复现:用户列表为空时触发"。
为什么:根因往往藏在行号、调用栈、触发条件里。"有个 bug"这种总结把这些全丢了,逼修的人从零重新发现。哪怕你觉得某些信息"无关",也原样保留——是否相关由修的人判断,不由你过滤。
> 在多 agent loop 里这条尤其关键:checker 的失败报告必须原样转发给 builder,编排器不能自作主张总结("checker 说有点问题"会把行号丢了)。单 agent 迭代同理——别把上一轮的报错"消化"成模糊印象,留着原文对照。
修复后防回归:跑全部相关,不只跑改的
修完一个 bug,只跑出问题的那个测试是不够的——你的修复可能让别处悄悄坏了。修复后的验证范围应该是全部相关测试,不是只跑改动点:
- 改了函数 X → 跑所有调用 X 的测试,不只跑 X 自己的
- 改了共享逻辑 → 跑全量测试
- 改了配置/类型定义 → 跑受影响的所有模块
为什么:修复本质是改动,改动就有副作用。只验"原本报错的地方不报了"会漏掉"修复让别处报错了"。这就是回归——表面上修好了 A,实际引入了 B。养成习惯:修完默认跑全量相关测试,把"只跑改的那个"当作偷懒的危险信号。
验证范围要匹配改动的影响面,不是无脑跑全量(那是另一种浪费):
- 改了叶子函数(没人调用或只一处调用)→ 跑该函数 + 唯一调用方
- 改了共享工具/被多处依赖的逻辑(utils、配置、类型定义)→ 跑所有调用方的测试
- 改了核心数据流/公共组件 → 跑全量,甚至手动冒烟关键路径
- 改动只影响自己(如函数内部重构、行为不变)→ 跑该函数测试即可
判断尺子:这次改动可能波及哪些代码? 影响面 = 验证范围。低估影响面(把共享改动当叶子改)是回归的常见源头;高估影响面(叶子改动跑全量)只是慢,不危险——拿不准时往大了跑。
循环要有刹车
修了 A 引入 B、修 B 又引入 A——这种循环不罕见。没有刹车的循环会无限转下去,越改越烂。预设停止条件:
- 全绿:所有验证通过 → 成功,停。
- 超最大轮次(如 5 轮)→ 停,承认当前方案走不通,退回去重新想(不是硬撑)。
- 同一失败连续两轮:说明上次修复没生效或方向错 → 停,换思路。
- 修复引入回归:新改动的代价大于收益 → 停,回退这次改动。
- 无进展:几轮都在原地打转 → 停,把问题升级(求助/换方案/拆小)。
刹车不是放弃,是防止把"修不好"变成"越改越糟"。到达刹车条件时,正确动作是退回重新设计或求助,而不是继续在同一处硬磨。
常见错误
| 问题 | 修法 | |------|------| | 没跑就声称完成("看着应该没问题") | 跑测试/类型检查/实际运行,用证据替代肉眼判断 | | 修症状(末端兜底压住报错) | 向上游追溯根因,在最上游产生处修 | | 为通过弱化检查(改断言/删测试/any/吞异常) | 红线不可越——修代码真正通过,而非改检查放行 | | 把失败报告总结成"有个 bug" | 原样保留报错/行号/堆栈/复现步骤 | | 修复无限循环、越改越乱 | 预设刹车(轮次/重复失败/回归/无进展),到点停并退回 | | 把合理兜底(可选字段)当 bug 修,或反过来 | 问"空值何时出现"——有合理业务场景=兜底,说不清=bug | | 重构只验"能跑",不验"行为没变" | 重构要覆盖原代码处理过的所有边界,标准比新写更高 | | 修复后只跑出问题的测试 | 跑全部相关测试(调用方/共享逻辑/全量),防回归 |
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Lion-1209
- Source: Lion-1209/Lion-Skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.