关于 execute
用于完成范围明确的实现任务的流程技能,涵盖计划、构建、验证结果和提交。它设有分支隔离、任务上下文和 TDD 标记检查,并分别说明受监督与 AFK 执行方式。
主要功能
- 01规划、构建、验证并提交
- 02检查分支与工作树隔离
- 03以任务上下文作为执行门槛
- 04支持受监督与 AFK 模式
- 05执行环境检查与 TDD 标记
适用场景
- 01实现已明确范围的 Issue 切片
- 02基于完整上下文运行无人值守迭代
- 03在主机提供的隔离工作区中开发
- 04提交前验证实际结果
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ chrislacey89/ skills --skill execute安装后在 Claude Code 中输入/execute确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/mutation-at-consumption.mdnext-step-menu.mdrestated-claims.mdsidecar.shstate-backend.mdtrivial-task-rule.md详细介绍
原文在翻译后有更新,译文可能不是最新。
执行
执行一个完整的工作单元:规划、构建、验证实际结果并提交。
调用时机
这是一个主要的流水线技能,适用于 /prd-to-issues 产出具体任务切片后,或用户已明确界定实现任务时。
当工作已准备好进行构建、验证和提交时,使用 /execute。
如果任务切片在实现期间仍需要用户主动判断、监督或验收决策,请使用 HITL /execute。只有当下一个任务切片已在 GitHub 中妥善记录、没有阻碍,且其问题、边界图以及任何关联的研究资料(归档文件或 spike 问题)或 docs/solutions/ 上下文都足够清晰时,才使用 AFK /execute。
对于毫无上下文的读者也清晰明了——这正是 AFK 迭代的要求。/prd-to-issues §4 的上下文完整性检查是此条件背后的关卡:它要求每个 AFK 任务切片在提交问题前都包含 ### Context 区块,或明确声明为空。没有此类区块却标记为 AFK 的任务切片,从未经过此条件的检查,只是被假定符合条件。应将此视为改用 HITL 执行该任务切片,或将其退回 /prd-to-issues 的理由,而不是可以敷衍了事的形式要求。
强制执行 Ralph 自动检测和 TDD 标记关卡,请参阅下文的步骤 0:前置条件。
如果问题或方案尚未明确,不要用它替代 /shape、/research 或 /write-a-prd。实现完成并准备好评审后,不要用它替代 /pre-merge。
工作流
0. 前置条件
分支隔离关卡。 在开始任何实现工作之前,确保你正在一个为此特定任务创建的干净分支上工作,而不是沿用之前工作遗留的功能分支。
隔离已由环境提供——停止操作(先检查此项,再执行编号规则)。 某些环境会在运行 /execute 之前,为会话提供专用的隔离工作树和分支——Conductor 工作区、GitHub Codespaces、devcontainer 及类似主机都会配置“一个工作区 = 一个分支,自动从基分支派生”,并已运行设置脚本。在这种情况下,流水线不负责配置环境:在这里创建工作树会嵌套在主机工作树中,而主机分支(以工作区而非任务命名)也不是“陈旧”的。读取环境信号,并将工作树交由已负责管理它的一方(Norman——读取环境的信号,而不是在脑海中预设流水线总会负责配置;Meadows——一个库存只由一个参与者管理,因此流水线应让步,而不是再添加一个竞争的管理者)。
通过 .claude/settings.json 中的 worktree.provisioning 确定配置模式——可选值为 "host" | "pipeline" | "auto";若未设置此键,则默认为 "auto"(与现有的 research.storage 先例一致):
host——隔离由主机负责。无条件停止操作。pipeline——由流水线配置。跳过此停止操作,运行下方的编号关卡。auto(默认)——满足以下任一信号时停止操作:- 主机环境变量描述了当前工作树——下方代码块输出
host_owned=yes。这是成本最低、优先级最高的判别方式:流水线唯一的检测机制是 Bash,而这些变量在 Agent 的 shell 中可见。仅有变量并不足够:CONDUCTOR_WORKSPACE_PATH会被 Conductor 下启动的任何 shell 继承,并且可能指向其他仓库的工作区,因此只有当它包含当前顶层目录时才算匹配。(不要通过当前工作目录中的.conductor目录来检测——Conductor 将其保存在$CONDUCTOR_ROOT_PATH下,而非工作区中。) - 当前工作树不是仓库的主工作树——
git rev-parse --show-toplevel与git worktree list --porcelain中的第一个路径不同。
- 主机环境变量描述了当前工作树——下方代码块输出
# host-signal: does a host env var describe THIS tree, not just this shell?
host_owned=no
top=$(cd "$(git rev-parse --show-toplevel)" && pwd -P)
if [ -n "${CONDUCTOR_WORKSPACE_PATH:-}" ]; then
ws=$(cd "$CONDUCTOR_WORKSPACE_PATH" 2>/dev/null && pwd -P) || ws=
if [ -n "$ws" ]; then
case "$top/" in "$ws"/*) host_owned=yes ;; esac
fi
fi
if [ -n "${CODESPACES:-}" ] || [ -n "${REMOTE_CONTAINERS:-}" ]; then host_owned=yes; fi
echo "host_owned=$host_owned"
CODESPACES 和 REMOTE_CONTAINERS 不包含可供比较的路径,因此只检查它们是否存在。它们的作用范围是容器级别,所以能读取到它们的 shell 就位于它们所描述的仓库所在容器中。同一段代码也出现在 /closeout、/lfg 和 /init-pipeline 中,内容逐字节一致,并由该扩展包自身仓库中的 scripts/test-host-signal-containment.sh 固定验证。
决定不创建工作树时:跳过工作树创建和 EnterWorktree,在当前分支上就地工作。 下方编号规则已满足——尤其是规则 3 不适用(由主机预配的分支既不是基础分支,也不是以任务命名的分支,但它并非过时分支;不要嵌套创建工作树,也不要停止)。主机已预先配置好被 Git 忽略的配置文件和依赖项,因此“工作树设置检查清单”中的大部分内容仅供参考——如果命令失败,可以抽查 .env.local 和依赖项,但不要重新配置。其中的 Git 钩子项是例外,仍然适用。 主机预配的是已跟踪文件和依赖项;Git 钩子位于 .git/hooks 中,该目录属于每个工作树,且不受版本控制,因此主机预配的工作区通常没有 Git 钩子。即使决定不创建工作树,也要检查此项。继续进行问题类型检查。
此处决定不创建工作树的逻辑与 /closeout 的拆除检查有意不对称。流入时只需回答*“我是否已经处于隔离状态?”——通用检测(toplevel ≠ primary)和环境变量提示任一项都足以作出判断。流出时的问题——“谁负责拆除?”*——要求更严格,不能仅依赖通用启发式判断,因为由流水线创建的工作树也满足 toplevel ≠ primary;/closeout 只依据显式设置或主机环境变量作出判断。
- 检查当前分支:
git branch --show-current - 如果当前分支是基础分支(例如
main、prod、master),请为此任务创建一个新的功能分支。 - 如果当前分支是另一个功能分支(既非基础分支,也不是以此任务命名的分支),说明你位于之前工作的过时分支上。不要在此处提交新工作。**例外:**如果当前分支是在此任务的
Consumes from #N声明中列出的同级切片分支,说明你正要有意从该分支分叉,以创建堆叠 PR 切片——请继续。**例外(主机预配):**如果上面的决定不创建工作树的检查已触发,则此分支是主机隔离工作区分支——它并非过时分支;请就地工作。
要创建隔离分支,请使用以下其中一种方法(按优先级排序):
- Worktrunk(如果有
wt):wt switch --create <branch-name>— 从适当的基础创建新的工作树和分支,并切换到该分支,从而实现完整的文件系统隔离。请参阅/worktrunk技能指南。 - 普通 Git:
git checkout <base> && git checkout -b <branch-name>— 在当前工作目录中从适当的基础创建新分支。
默认情况下,适当的基础是仓库自身的基础分支——即仓库声明的分支(git symbolic-ref refs/remotes/origin/HEAD --short | sed 's@^origin/@@')。不要假设它是 main。对于依赖于尚未合并的 Consumes from #N、且该依赖会提供此切片所导入符号的切片,应从该同级切片的分支创建新分支,以便堆叠 PR 可以以该同级切片的 PR 为目标(Hammant 的 Trunk-Based Development 第 13 章:每个故事可以有多个 PR;同级切片的 PR 仍须在 2 天内合并到仓库的基础分支)。
根据任务推导分支名称:例如 issue-5-landing-page、landing-page 或问题的 slug。不要重复使用之前工作的分支名称。
将工作树作为会话进入(当你创建了工作树时)。 创建工作树并不等同于让会话在其中运行——wt switch --create 会在每条命令执行后将 shell 的 cwd 重置回项目根目录,因此如果不再执行一步,会话仍会锚定在原始检出目录中,报告的 cwd 也会谎报实际工作的目录。创建工作树后,使用 harness 的 EnterWorktree 工具进入其中,让会话的工作目录确实就是该工作树:
- 调用
EnterWorktree { path: <absolute-worktree-path> }。该路径必须已出现在此仓库的git worktree list中(刚创建工作树后就会出现)。此操作会持久地将会话切换到工作树。 - 创建步骤保持完全不变——
wt switch --create/git worktree add会运行 worktrunk 的pre-starthooks,以准备.env.local和依赖项。EnterWorktree只会切换 cwd,不会运行这些 hooks。先创建(以准备环境变量和依赖项),再进入。 - 这样就把信息放在实际环境中,而不是依赖记忆:cwd 会如实说明编辑和提交将落在哪里(监视中的操作人员可以看到),也无需记住在每条命令前加
cd——从而避免因漏掉前缀而将命令写入错误工作树的失误。 - Plain git 选项(
git checkout -b,不使用工作树)会在当前检出目录中创建分支——没有工作树可进入,因此跳过EnterWorktree,直接在当前目录操作。
AFK / 无头模式回退方案。 如果 EnterWorktree 不可用(无头运行、AFK Ralph、cron),则改用 cwd 前缀规范:每条 Bash 命令执行后,shell cwd 都会重置到项目根目录,因此请在本次会话的每次 Bash 调用前加上 cd <absolute-worktree-path> &&。仅当 harness 工具确实不可用时才使用此方案——这是为不具备原生机制的环境保留的旧式变通方法。
此工作树的拆除工作由流水线末尾的 /closeout 负责——PR 合并后,/closeout 会将 shell 重新锚定到基础检出目录、移除工作树,并清理已合并的分支。/execute 是工作树生命周期的入口,/closeout 是出口。下面第 6 步的清理仅移除 .tdd-* 标记——有意不移除工作树。
创建工作树后,请进行设置。 新工作树会继承已跟踪文件,但不会继承被 Git 忽略的文件(.env.local、每个工作树专属的依赖项、构建缓存)。有两种方式:
首选——通过 worktrunk hooks 配置一次(项目中的 .config/wt.toml):
[pre-start]
copy = "wt step copy-ignored"
install = "pnpm install"
pre-start hooks 会阻塞后续操作——完成前不会报告工作树已就绪。两项设置都使用 pre-start(而非 post-start),因为 post-start 会在后台运行,后续需要 .env.local 或 node_modules 的命令可能会与 hook 发生竞争。完整配置方法请参阅 /worktrunk。每个项目只需配置一次。
回退方案——使用普通的 git worktree add 手动设置(不使用 worktrunk):
cp <source-repo>/.env.local <worktree>/.env.local(以及项目使用的其他任何被 Git 忽略的配置)。- 在工作树中运行项目的安装命令(
pnpm install、npm ci、pip install -r requirements.txt等)。
工作树设置检查清单(DO-CONFIRM——逐项执行每个步骤,然后验证后再继续)。 无论以何种方式创建工作树,均适用:
- 已复制 Git 忽略的配置 —
.env.local(以及其他任何.env.*、*.local或项目专属的忽略配置)存在于工作区中 - 已安装依赖 — 安装命令(
pnpm install、npm ci等)已在工作区中运行且未报错 - 会话位于工作区内 —
pwd显示工作区路径,因为你是通过EnterWorktree { path }进入该目录的(而不是项目根目录)。仅在 AFK/无头回退场景中,此项表示每次 Bash 调用都使用cd <absolute-worktree-path> &&前缀 -
$CLAUDE_PROJECT_DIR作用域正确 — 如果项目脚本引用了此环境变量,请确认它解析为工作区路径,而不是主仓库路径 - 已安装本地 Git 钩子,且其管理器位于
PATH中 —ls "$(git rev-parse --git-dir)/hooks/" | grep -v '\.sample$'能列出内容,且仓库声明使用的管理器(lefthook、husky、pre-commit等)能够正常解析。钩子位于.git/hooks中,此目录按工作区隔离且不受跟踪,因此全新的工作区不会继承任何钩子——而最糟糕的是,故障不会有任何提示:每次提交都会成功,仓库文档声明的所有提交时保障措施却都没有运行。如果缺少管理器,请安装它(lefthook install),或者在第 6 步的审查备注中记录此分支的本地检查未启用,避免有人误把本地运行通过当作合并门禁 - 不存在分类标记 — 工作区中不存在
.claude/.tdd-active、.claude/.tdd-skipped和.claude/.lfg-active(初始状态为空;第 3 步会创建其中一个) - 不存在审查后锁定标记 —
.claude/.review-stamped和.claude/.fix-findings-active均不存在。两者都属于上一个分支的审查:/pre-merge第 4 阶段会写入第一个,/fix-findings会写入第二个;在该分支结束时,/closeout和/fix-findings分别会将其移除。遗留的.review-stamped会在此切片开始前拒绝所有实现写入;遗留的.fix-findings-active则会让整个切片都处于锁定状态,这种情况不会发出任何提示。发现任意一个都请删除——你现在正处于一个尚未经过审查的分支起点,因此它们都不可能描述当前分支
问题形态检测门禁。 如果任务是 GitHub issue,请确认它是一个切片(可直接实现),而不是尚未拆分的 PRD。运行 gh issue view <n> --comments,并检查是否有匹配 ^Decomposed into: #\d+ 的评论。
- 如果存在此类评论:继续。PRD 已拆分;操作者大概正在处理其中一个子切片(并且应该提供该切片的编号,而不是 PRD 的编号)。
- 如果不存在此类评论,且 issue 正文包含结构化提案标记(名为
Appetite、Rabbit Holes、No-gos、User Stories或Implementation Decisions的章节):停止。这是一个尚未拆分的 PRD。调用/prd-to-issues <this-issue-number>,生成可直接实现的切片,然后针对其中一个子切片 issue 重新启动/execute。 - 如果存在多条
Decomposed into:评论,请阅读最新的一条;/prd-to-issues负责确保只有一条评论具有权威性。
对于与 GitHub issue 无关的单次任务,请跳过此门禁。
阻塞切片门禁。 仍在 issue 页面时,请先确认该切片确实可以着手处理,再开始实现。直接读取依赖关系边,而不要依赖摘要计数,因为变更后计数可能会暂时过时:
gh issue view <n> --json blockedBy \
--jq '{open_blockers: [.blockedBy.nodes[] | select(.state == "OPEN") | {number, title}],
ever_wired: .blockedBy.totalCount}'
open_blockers 为空表示可以着手处理——继续。只要存在未解决的阻塞项,就停止:向用户说明阻塞 issue,并让他们决定是先处理阻塞项,还是因为依赖关系已过时或与此切片无关而覆盖该阻塞。不要开始实现后,才在中途发现缺口。
在相信空列表之前,先查看 ever_wired。 空列表有两种原因:切片中确实没有未解决的阻塞项,或者仓库早于 /prd-to-issues §7 中的边关联机制,完全没有任何边。totalCount 会统计所有状态的阻塞项,因此可以区分这两种情况——ever_wired > 0 表示这个切片曾建立关联,其中的阻塞项后来已经关闭,所以空列表确实准确。ever_wired == 0 表示从未写入过边,空列表不能证明任何事情;如果 issue 正文中有纯文本形式的 Blocked by #N 行,请相信这段文本,并手动检查这些阻塞项的状态。
注意,这里的 state 是 GraphQL 枚举值——大写的 OPEN,不是 REST 端点使用的小写 open。
对于与 GitHub issue 无关的一次性任务,请跳过此关卡。
Ralph 自动检测关卡。 评估以下三个条件:
- 任务来自 GitHub issue(不是一次性的口头请求)
- issue 的范围涉及多个切片(PRD、大批量任务,或多个用户故事)
- 仓库根目录中不存在
ralph-once.sh或ralph.sh
如果三个条件都满足,请立即调用 /setup-ralph-loop。在 Ralph 设置完成或条件不满足之前,不要继续执行步骤 1。
流水线钩子关卡。 钩子文件存在,不等于钩子包含审查后的编辑锁;本关卡会检查这两者。在审查后的编辑锁发布之前运行过 /init-pipeline 的项目,其钩子不会读取 .claude/.review-stamped:该锁在此处不起作用,而且持有 .claude/.fix-findings-active 的 /fix-findings 修复器也会被拒绝——这是由分类子句导致的,拒绝消息提到 /tdd,却没有提到修复器当前所在的路由。这两部分都会静默失败,因此仅检查文件是否存在并不够:从外部看,一个不起作用的锁,与一个审查后无人编辑过的分支毫无区别。
HOOK="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel)}/.claude/hooks/enforce-classification.sh"
# Every path the hook's own clause 1 tests. A path missing from this list is a
# boundary this gate cannot see, so an install predating it reports current
# forever. scripts/test-lfg-marker.sh section 7 pins this list against that
# clause; add a path here in the commit that adds it there, or the suite fails.
GATE_TERMS=".claude/.review-stamped .claude/.tdd-active .claude/.tdd-skipped .claude/.lfg-active"
if [ ! -f "$HOOK" ]; then
echo "hooks-absent"
else
missing=""
for term in $GATE_TERMS; do
grep -qF -- "$term" "$HOOK" || missing="$missing $term"
done
if [ -n "$missing" ]; then
echo "hooks-stale"
echo "missing:$missing" >&2
else
echo "hooks-lock-present"
fi
fi
hooks-absent 和 hooks-stale 都表示现在就调用 /init-pipeline,并且在它报告完成之前,不要继续执行步骤 1。前者会为尚无强制执行钩子的项目创建这些钩子。后者会用 § 2 的钩子正文重新创建现有安装,并重新运行 § 6 中的 .gitignore 追加操作;安装时没有为锁的两个标志执行这一步——如果不追加它们,这两个标志都会成为可提交的未跟踪文件,并在该仓库后续的每个分支中持续保持锁关闭或开启。重新创建时,/init-pipeline § 2 会保留现有的 IMPL_PATTERNS 行,而不是重新询问,因此升级不会重置项目自定义的触发范围。旁路文件属于 /init-pipeline § 8 的处理范围,是否创建由该处的关卡决定。若为 hooks-lock-present,则继续执行。
此关卡无法检测的内容;否则它的第三种判定就会被误解为它并未作出的声明。 它会搜索钩子自身第 1 条子句检查的所有路径,因此能识别每条路径上的一个版本边界,并报告安装处于边界的哪一侧。这种判断只在一个方向上可靠。钩子缺少其中任何一个术语,都能证明它早于该版本,因此只要触发 hooks-stale,判定就是正确的;但项目一旦升级,这些术语就会一直存在,所以之后若 /init-pipeline § 2 的钩子正文发生变化,却没有增加新路径——例如调整顺序、改写拒绝消息,或修复某条子句的逻辑——此关卡仍会报告 hooks-lock-present,也不会分发这些更改。因此,第三种判定的名称指的是它找到的术语,而非版本是否最新;此检查无法确定后者。
GATE_TERMS 是该声明的全部内容,scripts/test-lfg-marker.sh 第 7 节会将其与钩子的第 1 条款绑定起来,因此,在钩子中添加标记却未将其加入此列表,会导致添加该标记的提交无法通过测试套件。这取代了本段过去用文字表述的规则——引入边界时,在同一提交中为其指定独立术语——chrislacey89/skills#370 读过这条规则却没有遵循,将 .claude/.lfg-active 引入项目,却没有为其添加分发术语,导致它在此前安装的每个项目中都不起作用(chrislacey89/skills#385)。即使分发边界不是一条新路径,仍然没有对应机制,而且仍需独立分发。
TDD 分类门禁。 第 3 步要求在编写任何代码前先对工作进行分类。加载 /tdd 时,harness 预处理会自动创建 .claude/.tdd-active(不依赖 LLM);视觉前端会创建 .claude/.tdd-skipped;/lfg 会创建 .claude/.lfg-active。PreToolUse 钩子会阻止所有 .ts 文件写入,除非存在上述某个标记。第 6 步会在提交后移除所有这些标记。
简单任务例外。 对于与当前功能工作无关的单次提交清理任务,可以直接创建 .claude/.tdd-skipped,跳过分类。必须同时满足的四个条件,以及任一条件不满足时应采取的做法,都只在 references/trivial-task-rule.md 中说明。
假设验证门禁。 如果任务对应的 GitHub issue 包含“Assumptions from Parent PRD”部分,请花 60 秒根据当前实际情况检查其中列出的每项假设,再继续处理。对于每项假设:
- 外部服务是否仍可用,且 API 和价格层级符合预期?
- 根据目前掌握的信息,父级 PRD 中的方法是否仍然成立?
- 此切片所依赖的包是否仍使用兼容的版本和入口点?对于多运行时包,替换子路径(例如
pkg→pkg/http,或任何pkg/<sub>→pkg/<other-sub>)是伪装成纯类型差异的运行时变更——应将其视为假设变化,而不是可免检的类型等效编辑。
如果所有假设仍然成立,请继续执行第 1 步。如果任何假设已发生变化,请停止并告知用户——此切片需要先进行有针对性的 /research + mini-PRD 流程,再开始执行,而不是在实现期间打补丁。不要基于过时的假设继续操作,并试图在执行过程中绕过问题。
对于没有“Assumptions from Parent PRD”部分的一次性任务,请完全跳过此门禁。
尚未排除的可行性检查(建议)。 如果任务的研究产物(归档文件或 spike issue)仍包含 Uncertain 或 Speculative 假设,且可以用 10 行临时代码轻松验证其结论——例如,库是否确实提供此功能、流式处理路径是否会输出部分标签、这种格式能否在所需位置渲染——请现在提出这些问题,并建议在正式实现前进行 /prototype FEASIBILITY 验证。这只是建议,不会阻止工作(遵循 XP 的松弛原则);用户可能有理由继续推进,并在实现过程中发现答案。但成本差异确实存在(XP 的缺陷成本递增):现在做 spike 只需几分钟,而在实现过程中才发现同一假设有误,则可能需要数小时来调整方向。请明确列出尚未排除的假设,让用户决定。对于没有研究产物的一次性任务,或所有假设都已标记为 Verified / Refuted 的情况,请跳过此检查。
依赖验证门禁。 仅适用于基于 issue 的切片工作。如果任务来自 /prd-to-issues 创建的 GitHub issue,且其 ## Boundary Map / ### Consumes 部分引用了已关闭的上游切片,请花 60 秒验证当前代码树中是否存在列出的每个符号,且其路径与声明一致。这可以在实现开始前发现上游边界映射的偏差。
对于每个 Consumes 条目:
- 如果提到文件路径,请检查文件是否存在。
- 如果提到函数、类型或导出符号,请搜索其导出定义。
- 如果提到某种结构(例如“Effect Layer”“Zod schema”“React component”“Context provider”),请确认结构本身符合要求,而不只是名称相符。纯辅助函数不能满足“Effect Layer”的要求。普通对象不能满足“Zod schema”的要求。
- 如果
Consumes条目提到来自同级切片的类型化符号(参见/prd-to-issues的 Boundary Map 指南),请确认消费者代码通过import type(或相应语言的等效方式)从生产者派生其输入类型,而不是重新声明该结构。本地重新声明即使恰好与生产者一致,也违反 DRY 原则;生产者演进时,它会悄然产生偏差。类型化导入是从结构上杜绝此类偏差的修复方式。
如果任何 Consumes 符号缺失或结构不符,请停止。上游边界映射已过时。请选择以下一种方案:
- 在此切片中扩大范围,补上缺失内容。在第一条提交的消息和 PR 描述中注明范围扩展,并在上游已关闭的问题中发布事后更正评论,避免后续切片依赖过时声明。
- 通过
/correct-course回退,更新上游边界映射并调整受影响的切片。 - 为缺失工作新建切片,并将此切片设为依赖该切片。
不要悄悄补上缺失内容——请为下一个切片留下线索。
对于一次性任务、仍在规划中的同级切片,或没有上游 Consumes 条目的问题,请跳过此检查。
此检查仅针对仓库内部符号(路径、导出项、结构)。对外部可解析声明(包名、公共 API 符号,以及相对于研究快照固定的版本)的对应检查,会在 /pre-merge 的 Boundary Map Contracts 维度下的“Spec-reality check”中进行。步骤 0 检查切片开始时的注册信息;/pre-merge 在合并时检查注册信息。两次检查时机都有意如此;请勿扩大此检查范围,以免与审查时的检查重复。
1. 了解任务
阅读所有引用的计划、PRD 或 GitHub 问题。探索代码库,了解相关文件、模式和惯例。如果任务含义不明确,请先向用户询问,澄清范围再继续。
阅读问题的评论串(仅限基于问题的工作)。 如果任务来自 GitHub 问题,请在实施前阅读其评论串——不要只看问题正文。步骤 0 的问题结构检测关卡已运行 gh issue view <n> --comments,因此评论串已在上下文中;请直接使用,无需重新获取。评论中包含工作流的后续状态:前一轮迭代的交接说明(已完成的工作、剩余工作,以及前一上下文窗口遇到的确切错误输出)、指出哪些内容未取得进展的停滞停止说明、上游边界映射发生偏差时针对本问题发布的事后更正评论,以及问题创建后添加的明确人工范围变更。在 AFK Ralph 循环中,这一点至关重要——第 N+1 轮的设计是接续第 N 轮写入评论串的内容继续工作,因此跳过评论串会导致重复推导或重做前一轮已经说明的工作。
优先级规则。 问题正文仍是持久有效的约定。评论是补充,不会悄然推翻正文。只有以下评论才具有权威补充效力:(a) 工作流发布的后续说明或更正评论,或 (b) 明确的人工范围变更。自由讨论只能作为上下文,不能覆盖正文。如果评论似乎与正文中的范围相矛盾,且并非明确由工作流发布的更正或人工范围变更,请向用户指出冲突,不要擅自依据评论行事。
处置规则。 对评论进行分类并非全部工作——还要决定如何处理正文未涵盖的线程内容。当此切片的 PR 将关闭该 issue(Closes #N)时,这一点尤为重要。正文是此流程的基准:验收标准位于正文中,/prd-to-issues 会拆解正文,Closes #N 会按正文所述关闭 issue。留在评论中的能力不属于上述任何机制,因此关闭时会悄然将其丢弃——而且这种损失不会发出任何信号,因为 issue 会以绿色状态关闭,PR 也会干净利落地合并。
判断标准只有一个,而且不是“这个想法好不好”:你能说出它的使用者吗? 如果你能指出会用到该能力的 issue、切片、PRD 或已交付代码,它就符合条件。如果不能——比如设计上的思考、一个“我们或许需要”的想法,或作者只是边想边说、却没有明确调用方的方案——那它就不符合条件,不能将其提升;提升它会凭空扩大范围并臆造依赖。如有疑问,就视为不符合条件。
对于符合条件的评论,在完成切片前将其呈现给用户,并提供两种处置方式:
- 将其提升到正文中——将其添加到此 issue 的验收标准中,并在此切片中实现;前提是它确实属于同一个接口边界,且工作量在可接受范围内。
- 为其单独创建 issue,并添加
Blocks #N链接,指明你已确定的使用者——适用于确实需要做、但应归属其他地方的工作。
无论哪种方式,都能为该能力指定一个在 issue 关闭后仍然有效的负责人。不要悄然将其纳入范围,不要擅自扩大范围,也不要仅仅因为注意到了它就着手实现——由用户决定。AFK 运行时无法询问用户,因此默认创建后续 issue 并添加 Blocks #N 链接;将内容提升到正文会在未经批准的情况下扩大切片范围。
对于未关联 GitHub issue 的一次性任务,跳过此项检查及其处置规则(与 Step 0 使用的范围保护规则相同)。
先解决 sidecar 问题。 研究归档和项目经验应通过此技能捆绑的解析器 references/sidecar.sh,在其绝对安装路径下查找;切勿自行计算 sidecar 路径。
SC="<this skill's directory>/references/sidecar.sh"
bash "$SC" resolve
退出码为 0 时继续。若退出码为 1 且处于 AFK 迭代中,请停止:按原样输出解析器的 stderr——遇到 SIDECAR MISS 时,其中是 init 命令——并留给人工处理,因为创建 sidecar 是人工操作,而且只读取部分上下文的迭代会被误认为已完成。若处于 HITL 运行中,将同一行显示给用户,并以只读方式继续:下方的解决方案 grep 会回退到 docs/solutions/,PRD 引用的研究路径仍会按原样检查。
阅读此功能的研究材料。 PRD 的“Research Reference”部分会注明材料所在位置,具体取决于项目的 research.storage 模式,位置有两种:
-
Spike issue 模式——PRD 引用一个已关闭且带有
research标签的 GitHub issue(Refs #<spike-issue-number>)。使用以下命令读取:gh issue view <spike-issue-number>此方法适用于任何机器——全新克隆、CI 沙箱、恢复后的笔记本电脑或贡献者环境。
-
归档模式(默认)——PRD 引用研究条目的绝对路径。读取前先检查:
SC="<this skill's directory>/references/sidecar.sh" bash "$SC" require "<path the PRD cites>" --from "PRD #<n>"未找到时,解析器会输出
MISSING: <path> (referenced by PRD #<n>)并以退出码 1 退出——通常是因为当前机器与生成研究材料的机器不同。若处于 AFK 迭代中,请停止。若处于 HITL 运行中,将该行显示给用户,并重新运行/research,或在用户明确确认缺少上下文后继续。
一些旧版 PRD 可能仍引用仓库根目录或 plans/ 下的 research.md——如果该文件存在,就读取它。无论材料位于何处,其中都包含可供当前方案参考的缓存技术研究。不要重新研究已经确定的事项。
在以下 grep 之前,先阅读切片中的 ### Context 块(仅限基于 issue 的工作)。 如果切片 issue 的 ## Boundary Map 中有 ### Context 子章节,请先阅读其中的锚点、注意事项和研究指针。/prd-to-issues 编写该块,就是为了让本次会话不必重新推导这些信息:锚点指出要阅读或仿照的现有文件,研究指针则可直接带你找到对应资料,无须翻阅上级 PRD 来查找。必须阅读该块——写好后却不优先阅读,这并非无关紧要的额外开销,而会让读者逐渐学会跳过该章节,徒增 Boundary Map 的负担。
将其视为指针,而非事实。锚点是在任务拆分时编写的,之后兄弟 issue 可能已经合并;如有冲突,应以下文的优先级规则为准,以代码和研究资料为准。锚点过时,应在 issue 中更正,而不是跳过该块。
开始实现前,请查阅 docs/solutions/ 和 sidecar 中相关的过往解决方案。该块会搜索 sidecar 的 solutions/ 目录和项目的 solutions.in_tree_path(值为 none 时跳过);如果没有 sidecar,则只搜索 docs/solutions/。将 KW 设置为要查找的关键词:
SC="<this skill's directory>/references/sidecar.sh"
KW="relevant-keyword"
SIDE="$(bash "$SC" path solutions 2>/dev/null)" || SIDE=""
ITP="$(bash "$SC" config get solutions.in_tree_path 2>/dev/null)" || ITP=""
[ -n "$SIDE" ] || ITP="docs/solutions" # SIDECAR MISS: fall back to the default store
for DIR in "$SIDE" "${ITP:-docs/solutions}"; do
case "$DIR" in ""|none) continue ;; esac
[ -d "$DIR" ] && grep -rl -- "$KW" "$DIR"
done
true
列出两个位置中的匹配项;两处互不覆盖。若匹配项位于 docs/solutions 以外的 in_tree_path,那就是该仓库自己的惯例:将其作为普通文档阅读。如果你阅读的条目引用了其他路径(如 Related: 指针、研究条目或档案),在依赖该路径之前,使用 SC="<this skill's directory>/references/sidecar.sh"; bash "$SC" require "<cited path>" --from "<the citing entry's file path>" 检查它(新 shell 中没有 SC),并打开命令输出的路径,而不是自行从引用条目推导路径;如果输出 MISSING:,在 AFK 迭代中应停止,在 HITL 运行中则应告知用户。
如果该问题领域存在过往解决方案,请吸取其中的经验教训,并避免其中记录的陷阱。
资料优先级: 研究资料与解决方案条目中的指导发生冲突时,遵循研究资料——它已根据当前安装的版本进行验证。存储位置不影响可信度:spike issue 和归档条目具有同等效力。如果冲突足以让你无法确定,请在继续之前告知用户。有选择地加载解决方案条目:先用 grep 搜索相关关键词,然后只阅读匹配的文件。
2. 规划实现(可选)
如果尚未规划任务,请为其制定计划。如果 GitHub issue 包含边界图(Produces/Consumes 章节),请利用它们理解需要实现或对接的接口。
特定技术栈的参考资料
开始实现前,请检查项目的技术栈并加载相关最佳实践。
- Next.js / React 项目:如果
package.json中包含next或react,请在编写代码前加载以下技能:/vercel-react-best-practices— 性能优化和 React Server Component 指南/vercel-composition-patterns— 可扩展且避免 prop 膨胀的组件组合模式/next-best-practices— 文件约定、数据模式、元数据和错误处理/next-cache-components— 缓存组件、use cache、cacheLife和cacheTag
3. 实现
停下——编写任何代码前,先进行分类:
- 后端代码 → 立即调用
/tdd(创建.claude/.tdd-active) - 行为逻辑繁重的前端(reducer、状态机、验证、无障碍功能、交互回归)→ 立即调用
/tdd(创建.claude/.tdd-active) - 视觉效果/布局/样式/文案前端 → 运行:
mkdir -p .claude && touch .claude/.tdd-skipped
PreToolUse 钩子会阻止所有 .ts 文件写入,除非存在上述标记之一。完成工作分类前,不要编写实现代码。
如果无法使用 /tdd,请遵循以下最低要求:
- 为最小的行为垂直切片编写一个失败的测试
- 运行测试——确认它失败(红)
- 编写让测试通过所需的最少代码(绿)
- 从步骤 1 开始,重复处理下一个行为切片
- 如有需要,进行重构,同时确保测试保持通过
不要一次性写完所有测试——先写一个并让它通过,再继续下一个。
[TypeScript 项目] 库回调的返回值。 当一个逻辑单元实现了库要求应用提供的回调(Agent 钩子、中间件、代理、工具处理程序、渲染属性、生命周期方法)时,使用 satisfies LibraryReturnType、新建对象字面量或派生类型(ReturnType<typeof …>),将返回值锚定到库声明的返回类型上。不要返回带类型的局部变量——TypeScript 不会对这类返回值执行多余属性检查,因此库签名未声明的字段会在运行时被静默丢弃。完整理由请参阅 /tdd 的重构步骤;如果研究产物(归档文件或 spike issue)中包含 Library Callback Contracts 快照(/research 第 1.25 阶段),请将其已接受字段列表用作固定依据。
注释代码,而非事故。 你在此处编写的注释,应该说明当前的原因——这段代码做什么,以及为何采用这种写法。导致你来到这里的事故,应写入提交消息和 PR 正文:此前的行为是什么、当前实现取代了哪个早期尝试、过程中否决了什么。提交一个逻辑单元前,重新阅读你为它添加的注释,并将任何解释历史而非代码的句子移走。注释是写给下一位编辑此文件的人看的,绝不是写给此差异的审查者看的。 如果一句话是在回应审查可能提出的异议——为什么不用适配器而用这个、哪个组件才是真正交付的组件、并行路径是有意为之——那它就是写给审查者看的,应与其他面向审查的说明一起放在 PR 正文中。判断依据在于强调语气:大写的 REAL、SAME 或 DELIBERATE,说明这条注释是在与并非此文件下一位编辑的人争辩。每件事只说一次:如果一条注释重复了文件中已有的句子,或旁边声明中已有的句子,就应删除这条重复内容。
每个逻辑单元完成后提交
不要把所有更改累积到一个提交中。每完成一个自包含的进展单元,就提交一次。逻辑单元是指能让代码库保持可运行状态的最小更改——类型检查通过、测试通过,且没有未完成的接线工作。例如:
- 一个红—绿—重构 TDD 循环(针对一种行为的测试 + 实现)
- 一个附带测试的新模块、类型或架构
- 一项接线更改(路由注册、依赖注入、配置)
- 一项在不改变行为的前提下改善结构的重构
- 一个迁移文件或种子文件
- 一项跨文件的类型或接口重构,若按文件逐步实施会导致类型检查失败——整个连锁更改算作一个逻辑单元
每完成一个逻辑单元后:
- 运行
pnpm run typecheck和pnpm run test(或项目对应的命令)。提交前修复所有失败项。 - 只暂存该单元涉及的文件——不要暂存无关更改。
- 提交说明应写明该单元完成了什么,不要写“WIP”或“progress”。如果该单元修正了先前的行为,就把上文所述的事件经过写入引导到此处的段落中——并且也要放在 PR 正文里,而不能只写在提交说明中。压缩合并会把这些分单元提交说明合并成一条,而
/closeout将压缩合并视为惯例,因此提交说明并不是持久可靠的载体。PR 正文中的副本会原样保留,供几个月后运行git blame的读者查看。
如果一个单元同时涉及测试和实现,两者应放在同一个提交中。如果某项重构由该单元引发,但在概念上相互独立,则应单独提交该重构。
4. 验证
“所有步骤都已完成”并不等于完成验证。请检查实际结果。
到这一步,每个逻辑单元都已在类型检查和测试通过后提交。第 4 步是对完整功能切片进行验证——确认整个功能端到端正常运行,而不仅仅是各个单元分别通过。
最后再运行一遍完整的反馈循环:
pnpm run typecheck
pnpm run test
修复所有问题。如需修复,请单独提交(例如,“修复 X 与 Y 之间的集成”)。
然后按验证阶梯进行验证——选择你能达到的最高级别:
第 1 级:静态验证
- 应存在的文件确实存在
- 导出均已就绪(不只是声明了,而且确实已导出)
- 模块间的导入已正确连接(没有从无法解析的路径导入)
- 实现内容充实(不是存根、不是
console.log占位符,也不是本该有真实代码却只写了 TODO 注释)
删除完整性(当此切片的 diff 删除或重命名模块时)。 根据你已经拿到的 diff 判断触发条件,而不是依据流水线中没有任何步骤写入的章节标题:
BASE_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD --short 2>/dev/null | sed 's@^origin/@@')
if [ -z "$BASE_BRANCH" ]; then
for candidate in main master prod develop trunk; do
if git rev-parse --verify "$candidate" >/dev/null 2>&1; then BASE_BRANCH=$candidate; break; fi
done
fi
# A name is not a ref. $BASE_BRANCH names the branch (for `--base`, `git switch`);
# $BASE_REF points at it, and is the only thing safe as a range endpoint.
if git rev-parse --verify "origin/$BASE_BRANCH" >/dev/null 2>&1; then
BASE_REF="origin/$BASE_BRANCH"
else
BASE_REF="$BASE_BRANCH"
echo "note: origin/$BASE_BRANCH does not resolve — measuring against the local branch, which may be stale" >&2
fi
git diff --diff-filter=DR --name-status "$BASE_REF...HEAD"
残余问题: $BASE_REF 的新鲜度取决于上一次 git fetch;在三角分支 fork(或远程仓库不叫 origin)中,origin/$BASE_BRANCH 可能不存在,也可能跟踪的是你的 fork 而非上游仓库——此时 else 分支会回退到本地分支,这正是此防护要避免的过时引用行为。它会将问题写入 stderr,而不是静默回退,因为正是这种看似合理、却没有任何信号提示的错误答案,让这个缺陷存在了五个月。如果计数看起来不对,请运行 git fetch 后重试,或手动设置 BASE_REF。
删除的路径会以 D<TAB><path> 的形式输出,重命名的路径会以 R<score><TAB><old><TAB><new> 的形式输出。只要有任何输出,就会触发此检查;输出为空则跳过。根据输出判断是否触发,而不是看退出状态——无论哪种情况,命令的退出状态都是 0。切片正文中的 ### Deletes 章节是可选提示,用于指明需要扫描的表面;它不是触发条件。
对于每个被删除或重命名的模块,列举其外部使用者依赖的表面——调用方曾被教导要发送以供该模块使用的符号名称,除其导出内容以外。典型表面包括:
- 模块读取的 DOM 数据属性(
data-*) - 模块应用或查询的 CSS 类名和选择器
- 全局或自定义事件名称(
addEventListener('foo-bar')、dispatchEvent(new CustomEvent('foo-bar'))) window、localStorage或sessionStorage的键- 模块拥有的路由名称、配置键或功能标志名称
根据模块删除前的内容推断其表面(git show <base>:<path>,或切片正文恰好包含的 Deletes 提示说明)。在合并后的代码树中,针对每个表面,搜索项目使用的所有源文本文件类型——模板、源代码、样式、配置和文档。不要限定为固定的扩展名列表;相关表面取决于技术栈(导入可能涉及 .py/.rb/.go/.rs,模板可能涉及 .vue/.svelte/.astro/.tsx,样式可能涉及 .css/.scss/.sass/.less/.styl,配置可能涉及 .yml/.toml/.json,随项目发布的文档可能涉及 .md/.mdx)。通过检查必须满足零匹配。非零匹配时:恢复模块、迁移使用方,或将其声明为有意保留但已失效,并在后续切片中跟踪清理工作。仅检查导入是对“使用者”最狭义的定义;相关表面可能更广。
上游形态扫描。 完成上述使用者表面扫描后,列出此切片中共享模块上所有被触及的导出(上下文字段、构建器返回类型字段、接口或类型成员、架构字段、导出的映射或记录条目)。对每个此类导出,在删除后的代码树中搜索非自身引用的读取。通过检查必须满足零匹配。非零匹配时:确认读取方仍有效且用途合理。零匹配时:在同一个 PR 中删除该导出——旧使用方一经删除,迁移窗口便随之关闭;保留未被读取的导出会扩大导入契约,使后续清理从静默删除变成破坏性变更。死导出检查工具(knip、ts-prune、TypeScript noUnusedLocals)可以覆盖其中许多情况,但无法检测导出的上下文、接口或架构字段——这些字段会被类型自身读取,因此在工具看来仍然有效;有针对性的删除后 grep 正是在完成检查工具无法完成的工作。
Tier 2:命令验证
- 测试通过(不只是“没有测试失败”——要确认测试确实存在并已运行)
- 测试的实际耗时没有意外增加。如果之前运行很快的测试突然增加了几秒,尤其是在添加重试、休眠、退避或间隔代码之后,就说明引入了未通过注入方式提供的实时原语。请参阅
/tdd§ 与时间耦合的原语。应通过注入解决,而不是调高testTimeout。 - 构建成功
- Lint 检查无误
- diff 中记录的任何命令——功能提供的 CLI,或要求读者复制并运行的命令块——都已实际执行,而不是凭记忆写出
Tier 2.5:运行时启动验证
当此切片涉及 schema、迁移、环境配置、服务初始化或新增路由时,必须执行。仅当对现有模块所做的更改纯属逻辑变更,且不会影响应用启动时,才可跳过。
- 数据库已就绪:运行待执行的迁移或
db:push——不要因为测试通过就假定开发数据库已使用最新 schema(测试通常使用会自行运行迁移的内存数据库) - 开发服务器可从冷启动无错误地启动:运行
pnpm run dev(或等效命令),确认服务器能够启动并响应,而不只是构建成功 - 新增或变更的路由加载时没有 500 错误:
curl -s -o /dev/null -w '%{http_code}' http://localhost:<port>/返回 200 - 必需的环境变量均已设置且有效(检查
.env.local或等效文件) - 启动期间服务器控制台输出中没有未处理的错误
如果无法启动开发服务器(例如缺少外部服务),请在 Step 5 检查清单和 Step 6 评审备注中都注明跳过了哪些检查及原因。检查清单会展示给用户,评审备注会展示给评审者。只在检查清单中说明意味着,在没有展示检查清单的运行中,这条说明就会丢失——而所有 AFK 运行都不会展示检查清单。无法在评审中看到被跳过的验证,正是此层级要弥补的问题。
Tier 2.6:非 Dry-Run 路径健全性检查(仅适用于 CLI + 编排切片)
当此切片提供带有 dry-run 或预览模式的 CLI、定时任务、cron Worker 或编排入口点时,必须执行。
Dry-Run 成功不代表实际运行也会成功。Dry-Run 可能会在存储或副作用发生前提前结束,从而掩盖接入生产路径的占位函数。
对于生产代码路径中作为默认项接入的每个函数(层构建、DI 容器、配置对象、CLI 标志处理器),请检查:
- 该函数是否被命名、记录或注释为占位内容、存根、TODO 或后续工作?
- 如果是,它是否满足以下任一条件:(a) 受快速失败检查保护,在非 Dry-Run 模式下会抛出错误;或 (b) 仅绑定到 Dry-Run 代码路径?
如果有任何占位函数在没有快速失败保护的情况下,作为默认项接入非 Dry-Run 路径,请立即标记。可选方案:
- 添加运行时保护:
if (!process.env.ALLOW_PLACEHOLDER) throw new Error(...)或等效代码 - 仅在
dryRun === true时绑定存根,并要求非 Dry-Run 路径使用实际实现 - 将此切片的交付设为依赖实际实现(范围更大,但可彻底消除静默降级窗口)
这是一次静默降级检查:如果操作人员运行时没有使用 --dry-run,输出会是真实结果,还是会有占位数据流经生产路径?
第 2.7 级:生产运行时一致性(仅适用于部署运行时 ≠ 测试运行时的切片)
有条件适用——绝非通用。仅当满足以下至少一项时才启用此级别,否则完全跳过。测试运行时与部署运行时相同的切片(大多数 Node 到 Node 和 Node 到 Vercel 的工作)不会涉及此级别。
- 切片的部署运行时与测试运行时不同,例如 workerd 与 miniflare/Node、真实浏览器与 jsdom、边缘/Lambda 运行时与本地开发服务器。
- 切片会发布或提供静态资源,其路径在部署时解析——绝对路径的
<link>/<script>/<img>引用、发布/复制步骤、CDN 或 Worker 公共根目录。 - 平台施加了测试运行时不会强制执行的限制——加密迭代次数上限、请求/响应大小、CPU/时间预算、包大小,或测试运行时存在但生产环境中不存在的 API。
此级别存在的原因——以及它为何是反馈循环,而非检查层。 测试通过,只能证明测试所模拟环境中的行为,而不是代码实际部署环境中的行为。当测试运行时比部署运行时更宽容,或者某个接缝被模拟掉、没有使用真实产物时,“测试通过”并不能证明“在生产环境中可用”。这就是开发/生产环境一致性差距(Twelve-Factor 第 X 条:沿着工具维度缩小开发↔生产环境差距),也是缺失的自动化反馈循环;持续交付通过针对类生产环境的冒烟测试来弥补这一点。这里的任务不是增加形式主义,而是让“有人运行了已部署的东西并确认正常”这一步成为结构性环节,使安全网依托于流水线,而非个人的尽责程度。
针对真实产物运行,而不是测试构建产物:
- 在(或针对)代码实际部署到的运行时中构建并运行已发布的产物——使用
wrangler dev/ 使用生产运行时的平台模拟器、预览部署,或以实际服务方式提供生产构建产物——而不只是运行pnpm run dev。 - 端到端地验证故障接缝所在的路径:提交登录表单,在浏览器中加载已发布页面,并确认其链接的子资源(
/_astro/*、/assets/*、字体、favicon)都返回 200;调用测试中被模拟的平台 API。如果 HTML 返回 200,但其子资源返回 404,仍然不够。 - 如果测试仅因测试运行时比生产环境更宽容而通过(某个限制未被强制执行,或存在生产环境中没有的 API),就应将其视为盲测,而不是通过的测试——在断言中固定生产环境限制,并在真实运行时中重新验证。
更深层的修复通常位于上游的 /tdd:为了保持测试通过而不得不模拟的接缝(平台加密 API、文件系统、部署复制步骤),正是 GOOS 指出不应模拟的接缝——将不归你所有的外部类型包装在薄适配器中,并对适配器进行集成测试。此级别用于发现一致性差距;自有适配器加上忠实的测试运行时,则能避免问题再次发生。
审查节奏说明。 此级别基于多项趋同依据而添加(持续交付中针对类生产环境的冒烟测试、Twelve-Factor 第 X 条、《Release It!》中的“面向部署设计”、GOOS 的行走骨架),并由一次触发事件促成(某个下游 Cloudflare Workers 仓库的 audit-publish 切片,2026-06-25——两个切片都通过了测试套件,却都只在 workerd/已部署运行时中出错)。如果在合理数量的切片样本中,此级别在没有其他问题的差异中触发率低于 10%,就应将其降为建议项或移除,而不是让它沦为形式主义。
第 3 级:行为验证
- API 端点返回预期响应(使用 curl 或 httpie 验证)
- 浏览器流程端到端运行正常(如适用且可验证)
- 数据从输入到存储再到输出的流转正确
第 4 级:人工验证
- 仅在你确实无法自行验证时才询问用户
- 明确说明需要他们检查什么:“你能否在第二个浏览器标签页中打开第 3 课,并确认状态指示器显示了你的姓名?”
- 绝不要用人工验证替代第 1 至第 3 级
如果验证发现缺口,请修复并将修复作为单独的提交提交。不要修改之前的提交——历史记录应显示构建了什么,以及后来修正了什么。
对集合声明进行范围检查。 在写出任何涉及某个集合的句子之前,先说明该集合、生成该集合的枚举过程,以及未覆盖的成员。如果找到的每个成员都使用相同措辞或属于同一种文件,说明枚举过早结束——请参阅 references/restated-claims.md。
然后在消费点进行变异(仅当此切片包含一个声称某项属性成立的测试时)。 如果此切片不包含此类测试,则跳过——文档、配置和样式工作没有可供其作用的对象。该阶梯、绿色结果的两种解读,以及变异编辑必须取自语料而非临时编写的规则,都在 references/mutation-at-consumption.md 中统一说明。
Bug 修复验证(任务是修复而非新增功能时)
如果此工作单元是在修复 Bug,请在提交前执行以下额外检查:
- 对修复进行分类:这是纠正(消除缺陷——即导致问题的代码错误),还是变通方案(在缺陷仍存在时抑制故障)?如果务实的选择是采用变通方案,请在提交说明中注明,并保持问题未关闭或创建后续任务来进行纠正。
- 搜索结构相同的代码:在代码库中搜索导致缺陷的相同模式。如果在其他位置发现,请修复所有实例或为它们创建问题。在一个位置修复缺陷、却在另外三个位置仍然存在,意味着还有 75% 未修复。
- 确认两个条件:确认 (a) 不再产生损坏状态,且 (b) 原始故障不再发生。如果只是抑制了故障,但底层状态仍然错误,那么这就是变通方案,而非纠正。
5. 手动验证清单
在移交给 /pre-merge 之前,请向用户提供一份验证清单,以便他们确认工作已准备就绪。
**准备工作:**总结已构建的内容——列出所做的提交和更改的关键文件。如果任务源自带有验收标准的 GitHub 问题,请将这些标准纳入清单,免得用户需要来回查阅。
请提供以下清单:
行为审查
- 手动操作时(浏览器、终端、API),功能表现符合预期
- 边缘情况和错误状态处理正确
- 相邻功能没有回归问题
代码质量
- 已审查差异——没有遗留调试代码、console.logs 或已注释掉的代码块
- 没有合并前应解决的 TODO
- 没有本应配置化或设为环境变量的硬编码值
验收标准
如果任务源自 GitHub issue 或包含验收标准的 PRD,请阅读这些标准,并为每一项生成一个具体的验证步骤。每个步骤都应明确告知用户要做什么以及预期结果,而不只是复述标准。
如果切片 issue 中有 User Stories Addressed 部分,并且其中引用了父 PRD,请阅读切片声称涵盖的父 PRD 用户故事。衍生矩阵条目(来自 /prd-to-issues 第 5 步)会在此作为验证目标浮现——每项映射的承诺都需要一个用户可以检查的具体步骤。切片中未映射的承诺不属于此清单的范围;它们属于其他切片,或由合并后的 /pre-merge 对账处理。
示例——如果验收标准是“用户可以通过电子邮件重置密码”:
- 前往 /login → 点击“忘记密码” → 输入测试邮箱 → 确认收到重置邮件 → 点击链接 → 设置新密码 → 使用新密码登录
生成步骤后,始终包含:
- 范围符合请求——没有擅自添加内容,也没有遗漏(这是执行过程中的自检;
/pre-merge的 Surgical Scope 维度是在差异检查时对合并后的代码块进行的检查)
将已验证状态写回 issue(强制执行)。 切片 issue 的 ## Acceptance Criteria 清单由 /prd-to-issues 编写,是“此切片合并前必须满足的条件”的持久契约。每当确认某项标准已通过验证(通过你的第 4 步验证,或由用户在此确认),就将该勾选状态写回 issue,让跟踪记录反映实际情况,无需任何人阅读聊天记录。这不是一个单独的“记得去做”步骤:它伴随你已完成的验证进行,因此无需做出新的判断。
安全编辑——只切换你实际验证过的复选框行,绝不重新生成正文。gh issue edit --body* 会替换整个正文,因此请先读取当前正文,只将已确认的 - [ ] 行改为 - [x],然后通过 --body-file 写回:
BODY_FILE=$(mktemp) # unique per run — two parallel worktrees / Ralph iterations must not share a temp path
# Guard the read half. A 5xx makes `gh` fail, and the edit below would then
# replace the whole issue body with whatever this file happens to hold.
if ! gh issue view <slice-issue-number> --json body -q .body > "$BODY_FILE"; then
echo "writeback aborted: issue body fetch failed" >&2; rm -f "$BODY_FILE"; exit 1
fi
[ -s "$BODY_FILE" ] || { echo "writeback aborted: issue body came back empty" >&2; rm -f "$BODY_FILE"; exit 1; }
ORIG_BYTES=$(wc -c < "$BODY_FILE")
# In "$BODY_FILE", change ONLY the verified criterion lines under
# "## Acceptance Criteria" from "- [ ]" to "- [x]". Leave every other line untouched.
# Guard the write half. Flipping "- [ ]" to "- [x]" preserves length exactly,
# so the edited body is never shorter. Shorter means the edit dropped content.
[ "$(wc -c < "$BODY_FILE")" -ge "$ORIG_BYTES" ] || { echo "writeback aborted: edited body is shorter than the body read" >&2; rm -f "$BODY_FILE"; exit 1; }
gh issue edit <slice-issue-number> --body-file "$BODY_FILE"
rm -f "$BODY_FILE"
不要勾选你未实际验证的项目,也不要触碰已确认标准以外的行——不慎编辑可能会损坏 issue 内容。/execute 是这些复选框的唯一写入者;/pre-merge 只读取它们,不会写入,因此不会有第二个编辑者与之冲突。
这些防护措施不可省略,第 6 步中的 AFK 回写也会复用它们。 这是针对负载较高时会返回 5xx 的 API 执行读取-修改-写入操作,而且 gh issue edit --body* 会替换整个正文——因此此处读取失败不会丢失一个标记,而是会丢失由 /prd-to-issues 编写、并由 /pre-merge 第 4 阶段对账的验收标准。出于相同原因,/pre-merge 的标记也会采用这两项防护措施。读取失败后重试一次;如果第二次尝试也中止,就不要勾选复选框,并说明原因,而不是写回。
准备好进行 PR 审查
- 是否准备好现在创建 PR 并进行架构审查(将直接进入
/pre-merge)?
等待用户审查并确认。如果他们指出需要修复的事项,请处理这些修复、提交更改,然后重新展示检查清单。只有在用户确认后,才能继续执行第 6 步。
审查前的可选理解环节。 如果将要合并此分支的人并非作者,或者希望在批准前逐个提交地熟悉内容,可以在 /pre-merge 之前选择 /walk-commits——这是一个按提交交互式讲解的流程(意图、风险最高的代码行、有意为之的特殊之处、刻意未包含的内容,以及逐个提交确认)。这是可选项,绝不会自动调用;应将其作为一种选择告知用户,不要自动运行。
“准备好进行 PR 审查”事项如何决定交接: 如果用户确认这一最后事项,第 6 步将执行清理,然后自动调用 /pre-merge,并传入 PRD issue 编号(如果任务源自某个 PRD issue)。如果用户确认了行为、代码质量和验收标准事项,但对 PR 审查事项回答“否”——因为他们想将更多工作合并处理、正在等待外部输入,或计划暂时保留此分支——第 6 步将执行清理,随后 /execute 正常退出。准备好后,用户再手动调用 /pre-merge。
6. 清理与交接
此时所有提交都应已完成。本步骤负责实施后的清理工作,以及向 /pre-merge 的过渡。它只移除分类标记——有意不拆除工作树或分支。这是流水线末尾 /closeout 的职责,在 PR 合并后执行;在此处移除工作树会导致分支在审查和合并前被销毁。
移除分类标记:
rm -f "$CLAUDE_PROJECT_DIR/.claude/.tdd-active" "$CLAUDE_PROJECT_DIR/.claude/.tdd-skipped" "$CLAUDE_PROJECT_DIR/.claude/.lfg-active"
AFK 运行也会持久化已验证的 AC。 AFK Ralph 迭代会跳过第 5 步的用户检查清单,因此依赖该清单执行的回写也不会发生。在 AFK 迭代退出前,将第 4 步中验证过的任何验收标准回写到对应的切片 issue,使用与第 5 步相同的 gh issue edit --body-file 切换方式——并包含其中的两项保护措施:检查获取操作的退出状态;如果编辑后的正文比读取到的内容更短,则拒绝写入。AFK 模式下无人监督写入,因此它最需要这两项保护措施。AFK 模式尤其需要一目了然的清晰状态——即使工作已经验证,issue 仍全部未勾选,而这正是此操作要弥补的缺口。
将审查备注写入 PR 正文。 /execute 结束时掌握了一些没有任何产物记录的信息:运行了哪些验证层级、跳过了哪些以及原因;第 0 步的 Consumes 门控吸收了哪些范围;哪些假设发生了变化;实现中哪些部分最薄弱。作者准备程度与较低缺陷密度的相关性,在实证中最强(Cohen);结构化的阅读计划也能显著提高缺陷发现率(Dunsmore)——但前提是审查者能看到这些信息。如果这些内容只留在第 5 步的检查清单中,信息传达给用户后就止步于此;而 AFK 运行不会展示任何检查清单,因此信息谁也传达不到。
应将这些信息交给审查者,作为 PR 正文中的 ## Review Notes 区块。它原生集成于 GitHub,可在新会话中保留,且无需新增文件系统状态。由 /execute 整理备注;由 /pre-merge 写入备注——无论是作者模式还是循环模式的交接,都应将该区块传递给 /pre-merge,由它在第 2 阶段与 PR 模板其余部分一同写入正文。创建 PR 由 /pre-merge 负责;为了传递一个下一步本就要打开正文写入的区块,/execute 不应开始编写 PR 正文。
备注必须可核查,而不能盲信。 审阅者对这项更改的全部了解都来自差异内容和这些备注——而备注由正在接受审阅的 Agent 编写。全新的上下文能避免合理化,却无法防止误报(Leveson:任何控制系统的表现都不会优于其测量通道)。因此,请记录运行过哪些验证命令及其退出状态,并采用审阅者可以重新运行的形式。绝不要断言必须让审阅者凭信任接受的结论:“Tier 2.5 verified”正是无法核查的说法。
## Review Notes
**Verification commands run**
| Command | Exit | Tier |
|---|---|---|
| `pnpm run typecheck` | 0 | 2 |
| `pnpm run test` | 0 | 2 |
| `curl -s -o /dev/null -w '%{http_code}' localhost:3000/reports` → `200` | 0 | 2.5 |
| `shellcheck --version` → `0.9.0` (matches `.shellcheck-version`); `shellcheck scripts/*.sh` | 0 | 2 |
**Name the version whenever the repo pins the tool, and say whether local gates ran.** A bare "lint clean" is a claim about *whichever* binary happened to be on `PATH`, and linters disagree across releases in both directions — so the reviewer cannot tell whether it describes the merge gate or a different instrument. Two lines close it: give the version beside any pinned tool's row, and state once whether the repo's own pre-commit/pre-push hooks were active for this branch. A branch whose hooks never ran is not disqualified; it is *differently evidenced*, and the reviewer needs to know which they are reading.
**Verification skipped, and why**
- Tier 2.7 — not applicable: test runtime and deploy runtime are both Node 22.
- Tier 2.5 dev-server boot — skipped, no Postgres available in this environment.
**Scope absorbed under the Consumes gate**
- #41 declared `ReportSchema` as a Zod schema; the tree had a plain object. Added the schema here; correction comment filed on #41.
**Assumptions that shifted**
- None.
**Known-weak spots**
- The retry path in `lib/fetch.ts` is covered only for the 2-attempt case.
自动调用 /pre-merge。 采用哪种模式取决于 Step 5 的处理结果:
- Step 5 已运行,且用户确认“Ready for PR Review” → 立即以 author-mode 调用
/pre-merge,并传入审阅备注;如果任务源自 PRD issue,也传入 issue 编号,以便它收集切片沿袭信息并验证边界映射契约,而无需再次询问用户。 - AFK Ralph 迭代(由于没有用户可供询问,Step 5 在结构上无法运行)→ 进入
/pre-mergeloop-mode,而不是退出并等待手动调用。这会将 AFK 边界从“代码已存在”推进到“代码已审阅且每项发现都有负责人”。loop-mode 会在 PR 上的持久记录中记下发现,不会提交更改,也不会做出决定,因此操作人员早上接手时,拿到的是一条已审阅的分支和一份经过分流的问题清单,而不是一条在夜间被悄悄修改过的分支。 - 用户对 PR 审阅事项回答了“no”,或因琐碎任务例外而跳过了 Step 5 →
/execute在此处退出,用户准备好后再手动调用/pre-merge。
手动退出时的下一步菜单。 当 /execute 停止且未自动调用 /pre-merge 时——用户对 PR 审阅事项回答了“no”,或正在批量处理更多工作——不要只显示一个空白文本框。使用单个 AskUserQuestion 提供下一步选项(参见 references/next-step-menu.md),并将推荐步骤置于首位;每个选项都应说明结果,而不只是技能名称:→ 立即运行 /pre-merge(推荐 — 打开 PR 并进行审阅)、批量处理更多工作 — 退出但不打开 PR、先运行 /walk-commits(逐个阅读分支上的提交)。除流水线中的后续步骤外,还应添加 1–3 个从本次运行中提取的后续事项,例如“在运行中的应用里验证验收标准 N”“展示风险最高的更改差异”,与 /walk-commits 针对特定提交的深入检查保持一致。平台提供的自由文本“Other”选项就是退出途径——不要再添加一个。若 Step 5 已确认 PR 审阅(由自动调用处理),或处于 AFK Ralph 迭代中(没有用户可供询问),则不适用此菜单。
打印运行时交接行。 无论是自动调用 /pre-merge,还是用户退出后稍晚再手动调用,都要打印此行,以便新会话可以通过复制粘贴启动:
**Next session:** /pre-merge
**Input:** the verified commits on branch <branch-name>
将 <branch-name> 替换为提交所在的实际分支(git branch --show-current)。
如果无法在此上下文窗口中完成任务,请在 GitHub issue 上留言,说明:
- 已完成的工作
- 尚待完成的工作
- 下一轮迭代需要注意的陷阱或棘手之处
- 如果失败由错误导致(构建失败、测试失败或 API 行为异常),请附上完整的错误输出——下一轮迭代需要看到真实错误,而不是摘要
如果错误表明研究产物或 PRD 中的方案有误,请在留言中说明——这意味着应该回退,而不是继续用同一方案重试。
AFK 进度与停滞检测。 在 Ralph 下运行时,进度指的是认知状态的推进,而非活动本身。只有以下至少一项从未解决转为已解决,迭代才算取得进展:
- 当前切片中一项尚未满足的验收标准
- 一项失败的检查(类型检查、测试或验证关卡)变为通过
- 研究产物或 PRD 中一项明确指出的未知问题或难题得到解决 没有此类进展的代码变更只是原地踏步。危险信号:同一部分在多轮迭代中持续处于活动状态;测试仍然失败,但“失败方式不同”;错误类型反复出现,代码却只做表面改写;没有任何验收复选框或关卡取得进展。
平台期停止规则。 如果同一部分连续两轮迭代都原地踏步,请停止,并发表评论 issue(格式与上文的重复失败评论相同),指出哪些内容没有取得进展。不要开始第三轮迭代。滞后机制:只要有一轮迭代有所恢复——至少实现了上述一项进展——就将原地踏步计数器重置。此规则是对现有重复失败规则的补充,并非替代规则。
此规则仅适用于 AFK Ralph 迭代。HITL /execute 运行由用户判断节奏,无需采用此启发式规则。
交接
- 预期输入: 具体任务、issue 或范围足够明确、可安全实施的任务切片;如果以 AFK 方式运行,还需提供持久化的上游产物
- 产出: 经验证的代码变更,按逻辑单元拆分为多个提交(每个单元一个提交),以及供下一位审核者或下一轮迭代使用的实施上下文
- 可能调用: 后端工作和行为逻辑复杂的前端工作会调用
/tdd;项目技术栈需要时,还会调用特定技术栈的参考技能 - 自动调用: 缺少强制执行钩子时调用
/init-pipeline;如果已安装的钩子早于 Step 0 的GATE_TERMS中任何术语——锁的标记标志或分类标记——也会调用该技能;Step 0 的hooks-absent和hooks-stale判定都会路由至此。当任务来自包含多个切片的 GitHub issue,且仓库中没有 Ralph 脚本时,会调用/setup-ralph-loop;在 Step 6 结束时调用/pre-merge——在作者模式下,Step 5 已运行且用户确认了“准备好进行 PR 审核”清单项后调用;在循环模式下,于 AFK Ralph 迭代中调用 - 默认的后续步骤:
/pre-merge——HITL 模式下,在 Step 5 用户确认后于作者模式自动调用;AFK Ralph 迭代中于循环模式自动进入;如果用户对 PR 审核清单项回答“否”,或因任务简单而跳过 Step 5,则由用户手动调用