mcpField
E
execute

execute

github.com/chrislacey89开发465 次安装今天更新
代码仓库

关于 execute

用于完成范围明确的实现任务的流程技能,涵盖计划、构建、验证结果和提交。它设有分支隔离、任务上下文和 TDD 标记检查,并分别说明受监督与 AFK 执行方式。

主要功能

  1. 01规划、构建、验证并提交
  2. 02检查分支与工作树隔离
  3. 03以任务上下文作为执行门槛
  4. 04支持受监督与 AFK 模式
  5. 05执行环境检查与 TDD 标记

适用场景

  1. 01实现已明确范围的 Issue 切片
  2. 02基于完整上下文运行无人值守迭代
  3. 03在主机提供的隔离工作区中开发
  4. 04提交前验证实际结果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/chrislacey89/skills --skill execute

安装后在 Claude Code 中输入/execute确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
mutation-at-consumption.md
next-step-menu.md
restated-claims.md
sidecar.sh
state-backend.md
trivial-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 只依据显式设置或主机环境变量作出判断。

  1. 检查当前分支:git branch --show-current
  2. 如果当前分支是基础分支(例如 main、prod、master),请为此任务创建一个新的功能分支。
  3. 如果当前分支是另一个功能分支(既非基础分支,也不是以此任务命名的分支),说明你位于之前工作的过时分支上。不要在此处提交新工作。**例外:**如果当前分支是在此任务的 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-start hooks,以准备 .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 条目:

  1. 如果提到文件路径,请检查文件是否存在。
  2. 如果提到函数、类型或导出符号,请搜索其导出定义。
  3. 如果提到某种结构(例如“Effect Layer”“Zod schema”“React component”“Context provider”),请确认结构本身符合要求,而不只是名称相符。纯辅助函数不能满足“Effect Layer”的要求。普通对象不能满足“Zod schema”的要求。
  4. 如果 Consumes 条目提到来自同级切片的类型化符号(参见 /prd-to-issues 的 Boundary Map 指南),请确认消费者代码通过 import type(或相应语言的等效方式)从生产者派生其输入类型,而不是重新声明该结构。本地重新声明即使恰好与生产者一致,也违反 DRY 原则;生产者演进时,它会悄然产生偏差。类型化导入是从结构上杜绝此类偏差的修复方式。

如果任何 Consumes 符号缺失或结构不符,请停止。上游边界映射已过时。请选择以下一种方案:

  1. 在此切片中扩大范围,补上缺失内容。在第一条提交的消息和 PR 描述中注明范围扩展,并在上游已关闭的问题中发布事后更正评论,避免后续切片依赖过时声明。
  2. 通过 /correct-course 回退,更新上游边界映射并调整受影响的切片。
  3. 为缺失工作新建切片,并将此切片设为依赖该切片。

不要悄悄补上缺失内容——请为下一个切片留下线索。

对于一次性任务、仍在规划中的同级切片,或没有上游 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 或已交付代码,它就符合条件。如果不能——比如设计上的思考、一个“我们或许需要”的想法,或作者只是边想边说、却没有明确调用方的方案——那它就不符合条件,不能将其提升;提升它会凭空扩大范围并臆造依赖。如有疑问,就视为不符合条件。

对于符合条件的评论,在完成切片前将其呈现给用户,并提供两种处置方式:

  1. 将其提升到正文中——将其添加到此 issue 的验收标准中,并在此切片中实现;前提是它确实属于同一个接口边界,且工作量在可接受范围内。
  2. 为其单独创建 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 模式,位置有两种:

  1. Spike issue 模式——PRD 引用一个已关闭且带有 research 标签的 GitHub issue(Refs #<spike-issue-number>)。使用以下命令读取:

    gh issue view <spike-issue-number>
    

    此方法适用于任何机器——全新克隆、CI 沙箱、恢复后的笔记本电脑或贡献者环境。

  2. 归档模式(默认)——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. 为最小的行为垂直切片编写一个失败的测试
  2. 运行测试——确认它失败(红)
  3. 编写让测试通过所需的最少代码(绿)
  4. 从步骤 1 开始,重复处理下一个行为切片
  5. 如有需要,进行重构,同时确保测试保持通过

不要一次性写完所有测试——先写一个并让它通过,再继续下一个。

[TypeScript 项目] 库回调的返回值。 当一个逻辑单元实现了库要求应用提供的回调(Agent 钩子、中间件、代理、工具处理程序、渲染属性、生命周期方法)时,使用 satisfies LibraryReturnType、新建对象字面量或派生类型(ReturnType<typeof …>),将返回值锚定到库声明的返回类型上。不要返回带类型的局部变量——TypeScript 不会对这类返回值执行多余属性检查,因此库签名未声明的字段会在运行时被静默丢弃。完整理由请参阅 /tdd 的重构步骤;如果研究产物(归档文件或 spike issue)中包含 Library Callback Contracts 快照(/research 第 1.25 阶段),请将其已接受字段列表用作固定依据。

注释代码,而非事故。 你在此处编写的注释,应该说明当前的原因——这段代码做什么,以及为何采用这种写法。导致你来到这里的事故,应写入提交消息和 PR 正文:此前的行为是什么、当前实现取代了哪个早期尝试、过程中否决了什么。提交一个逻辑单元前,重新阅读你为它添加的注释,并将任何解释历史而非代码的句子移走。注释是写给下一位编辑此文件的人看的,绝不是写给此差异的审查者看的。 如果一句话是在回应审查可能提出的异议——为什么不用适配器而用这个、哪个组件才是真正交付的组件、并行路径是有意为之——那它就是写给审查者看的,应与其他面向审查的说明一起放在 PR 正文中。判断依据在于强调语气:大写的 REAL、SAME 或 DELIBERATE,说明这条注释是在与并非此文件下一位编辑的人争辩。每件事只说一次:如果一条注释重复了文件中已有的句子,或旁边声明中已有的句子,就应删除这条重复内容。

每个逻辑单元完成后提交

不要把所有更改累积到一个提交中。每完成一个自包含的进展单元,就提交一次。逻辑单元是指能让代码库保持可运行状态的最小更改——类型检查通过、测试通过,且没有未完成的接线工作。例如:

  • 一个红—绿—重构 TDD 循环(针对一种行为的测试 + 实现)
  • 一个附带测试的新模块、类型或架构
  • 一项接线更改(路由注册、依赖注入、配置)
  • 一项在不改变行为的前提下改善结构的重构
  • 一个迁移文件或种子文件
  • 一项跨文件的类型或接口重构,若按文件逐步实施会导致类型检查失败——整个连锁更改算作一个逻辑单元

每完成一个逻辑单元后:

  1. 运行 pnpm run typecheck 和 pnpm run test(或项目对应的命令)。提交前修复所有失败项。
  2. 只暂存该单元涉及的文件——不要暂存无关更改。
  3. 提交说明应写明该单元完成了什么,不要写“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 标志处理器),请检查:

  1. 该函数是否被命名、记录或注释为占位内容、存根、TODO 或后续工作?
  2. 如果是,它是否满足以下任一条件:(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,请在提交前执行以下额外检查:

  1. 对修复进行分类:这是纠正(消除缺陷——即导致问题的代码错误),还是变通方案(在缺陷仍存在时抑制故障)?如果务实的选择是采用变通方案,请在提交说明中注明,并保持问题未关闭或创建后续任务来进行纠正。
  2. 搜索结构相同的代码:在代码库中搜索导致缺陷的相同模式。如果在其他位置发现,请修复所有实例或为它们创建问题。在一个位置修复缺陷、却在另外三个位置仍然存在,意味着还有 75% 未修复。
  3. 确认两个条件:确认 (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-merge loop-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,则由用户手动调用

评论

支持 Markdown