关于 never-twice
Never Twice 是一项用于分析故障并选择防护措施的技能,目标是避免同类故障再次发生。它优先考虑结构性修复,其次是 CI 检查;更强方案不可行时才采用书面规则。
主要功能
- 01归纳反复出现的故障模式
- 02按持久性排列修复方案
- 03要求测试夹具复现实际故障
- 04在故障发生之处设置防护
- 05审查被删除的规则和检查
适用场景
- 01用结构性改动取代重复修补
- 02为可复现故障添加 CI 检查
- 03判断事故是否值得设置防护
- 04审查现有防护措施的删除
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ vivswan/ skills --skill never-twice安装后在 Claude Code 中输入/never-twice确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSE.mdREADME.md.codex-plugin/plugin.jsonagents/openai.yaml详细介绍
永不重复
通过改进架构或选择更合适的数据结构,从根本上消除问题,让这类故障无法再次发生。这胜过逐个修复问题实例。
所有通过下方门槛的故障(包括 Agent 的大多数纠正),都应采取能够达到的最持久应对措施:
| 阶梯 | 应对措施 | 这类问题仍可能造成什么 | 偏好 |
|---|---|---|---|
| 1 | 改进架构或数据结构 | 无法再次发生 | 目标 |
| 2 | lint 规则或 CI 测试 | 会再次发生,但无法合入 | 可接受 |
| 3 | 技能或书面规则 | 会再次发生,但下一个 Agent 会知道 | 退而求其次 |
| 4 | 人工警惕 | 什么都可能发生 | 避免 |
这条阶梯不是供人挑选的菜单,偏好会随着级别下降而呈指数级减弱。第 1 级是目标,第 2 级是明显较弱但可接受的关卡。第 3 级和第 4 级各自都又下降了一大步,只有在所有更高级别都确实无法实现时才采用。
落在第 3 级或第 4 级属于债务,而非解决方案。一旦可以达到更高级别,就应沿阶梯向上,将规则或人工警惕转化为更高一级的应对措施。
**阶梯的门槛:**是否值得为某个故障设置防护,必须针对具体情境权衡判断,不能靠计数,也不能查表决定。判断时要考虑四个问题:能否复现故障,是否具体了解原因,正常运行中触发条件出现的频率有多高,以及防护的成本和部署位置。
Agent 的纠正本身就能回答这些问题:默认行为就是具体原因,每条提示词都会触发该条件,因此单次纠正通常就能通过门槛。可能意味着正常运行中经常会触发该条件。服务中断(forge 宕机、依赖项服务宕机)是典型的低概率触发条件。
可以随时复现,不等于能在本地复现。如果某个条件的原因已被具体查明,但只会在 CI 中出现,或需要无法在本地搭建的环境,并且实际运行中持续发生,那么判断结果也一样:原因已知,触发频繁。
所有情境都必须遵守两个约束。夹具必须复现观察到的故障,因此没有复现就绝不构建夹具;也绝不伪造本地夹具来代替仅存在于其他环境中的条件。防护应部署在条件出现的环境中。未复现且原因不明的事件只记录,不设置防护。
记录(事件发生的位置、观察到的现象)应写在记录该事件的 PR 正文、报告或 issue 中,绝不写进规则。“发生过两次”是判断的依据,不是单独的触发条件。如果判断不明确,就询问用户;绝不悄悄构建,也绝不默默放弃。
以下是判断示例,并非详尽列表:
- 发布步骤依赖有人先手动编辑版本文件。跳过编辑即可复现故障,每次发布都会运行该步骤,而自动生成版本只需一行代码,因此让该步骤自动生成版本。
- 通过代码已了解合并竞争的原因,两个推送在一分钟内到达时就会出现竞争窗口。在本地无法复现,但正常运行中该窗口经常出现,因此在合并步骤中(即竞争发生的位置)添加几行代码实现锁定,使其串行执行,且无需伪造夹具。
- 检查只会在 CI 运行器的映像每晚重建后失败,本地从不失败。原因具体明确,映像每晚重建,而且在流水线中添加一个单步探测的成本很低,因此防护应部署在条件出现的位置:CI 端的故障触发器。
- forge 宕机了十分钟,导致一次推送失败。正常运行中很少发生服务中断,因此重试推送并记录事件;如果修复看起来仍然合理,再询问用户。
- 某次运行过程中,审查启动器的临时目录消失了。既无法复现,原因也不明,因此重新启动该次运行并记录事件,不构建防护。
工作流
- 为类别命名,用一句话说明:“任何新的 Event 成员都可以发布,无需在名册中登记。”如果做不到,这还不是一个类别:修复具体实例,然后继续。
- 找到底层机制,找出允许新成员加入的机制:按惯例同步的两个工件、在应使用封闭集合的地方使用字符串、以散文形式保存的状态、手动输入的惯例。
- 尽可能攀升到最接近第 1 级的位置:在任务范围和权限允许的范围内,实现你能做到的最持久级别。
- **发布。**只有在报告中指出与更持久级别之间的差距,较低级别的后备方案才能发布。在你有权创建任务的地方,将这一差距作为独立任务跟踪。绝不要把它留作备注:备注是以散文形式保存的状态,而这正是允许该类别存在的底层机制。
测试所有发布的内容:
如果明天出现这个类别的新成员,修复还有效吗?还是会像以前一样悄无声息地漏过?
有三种答案:
- 它悄无声息地漏过:这个类别依然存在。
- 它失败得很明显,但为时已晚(发布后,在运行时):这是实例保护机制,这个类别依然存在。
- 在能够阻止它的级别上,它无法再次出现:无法构建(1)、在 CI 中拦截(2)、由已加载的规则捕获(3)。
第 1 级:这个类别无法再次出现
事件枚举不断扩展,超出对其进行分派的名册所能覆盖的范围:
// Fix 1 shipped Event.Deleted's missing entry. Fix 2 shipped
// Event.Archived's, plus a guard. Both read as complete:
handlers[Event.Archived] = onArchived;
assert(Object.keys(handlers).length === EVENT_COUNT); // fires at runtime, after the hole ships
// Rung 1: the roster is total, so the hole cannot be built:
const handlers: Record<Event, Handler> = {
[Event.Created]: onCreated,
[Event.Updated]: onUpdated,
[Event.Deleted]: onDeleted,
[Event.Archived]: onArchived,
};
// A new member fails compilation until the roster covers it.
类型级机制(封闭联合类型、品牌类型、Typestate)属于 /no-invalid-states 技能的范畴。
第 2 级:问题会重现,但无法合并
同样的清理工作总是靠手动提交:
$ git log --oneline -- docs/
9e8f7a6 fix: replace curly quotes with ASCII
a1b2c3d fix: replace curly quotes with ASCII (again)
# Rung 2: a CI check, marked required in branch protection.
# Fail-closed: hits and grep errors both fail; only "no matches" passes.
- name: check-typography
run: |
status=0
grep -rnP '[\x{2018}-\x{201D}\x{2014}]' docs/ || status=$?
test "$status" -eq 1
第 3 级:问题会重现,但下一个 Agent 会知道
同样的纠正意见总是在不同会话中被反复提出:
"Timestamps in logs are ISO 8601, not epoch." (Tuesday)
"ISO 8601 in logs, please." (Thursday, new session)
# Rung 3: the correction becomes a rule in a file every session loads.
$ tail -1 AGENTS.md
Log timestamps are ISO 8601 (2026-08-26T14:03:00Z), never epoch.
如何编写规则(规则存放的位置、触发条件、质量标准)由 /craft-skills-and-memories 技能负责。本级决定应由已加载的规则来响应,然后将工作交给该技能。
删除保护机制(切斯特顿栅栏)
这套阶梯会设立保护机制,而本规则负责规范如何移除它们。若不能说明设立规则、保护机制或检查项的原因,就绝不要移除它们。每次变更中的此类删除都必须归入以下两种结果之一,否则不得合并:
- 由覆盖同一类别的替代项接替:体现该规则的脚本、更高一级的保护机制,或保留规则的改写
- 明确命名为有意删减,并在变更中记录原因
示例:一次文档重写删除了 90 行沿用已久的规则。逐行审查并归类为替代项/改写/明确命名的删减后,只发现一处未映射的实质性损失,并在合并前将其恢复。
审查就是这一机制。本规则将其设为每次删除的默认步骤,而不是有人注意到问题后才采取的补救措施。
关键在于二者的配合:激进重写仍被允许,正是因为删除会经过审查。正是这项审查让大胆删除变得安全,而不是成为阻碍。
审查标准
安装此技能后,执行代码审查的技能(例如 /rubber-duck-review)会将本节扩展到其审查提示词中。请要求审查者标出:
- 重复修复同一故障类别的先前修复。请引用证据(先前的提交、文档行或更正),不要凭猜测
- 对于每项,指出该修复所在的阶梯级别,提出一个更持久的具体级别,并说明在本次更改的范围内为何能够(或不能)达到该级别
- 将这项测试应用于所有即将发布的内容:如果明天出现该类别的新成员,是不是无法构建、会在 CI 中被拦截、会被已加载的规则捕获,还是会悄无声息地漏过?
- 删除规则、保护机制或检查,却既没有对应能覆盖其类别的后继措施,也没有在本次更改中记录有意删除的理由(见上文“删除保护机制”)
- 底线的判断不支持的保护机制(见上文“底线”):对未复现且原因未知的事件设置保护;未经用户确认就对未复现的罕见触发条件或含糊不清的决策设置保护;无法复现已观察到故障的测试样例
- 有具体原因、触发条件常由正常操作产生且保护机制值得其代价的情况下,只修复实例却未设置保护机制(底线判断支持设置保护机制):此类修复应沿阶梯向上推进
按照上文的工作流对审查所得的问题进行分流处理。