关于 work-with-pr
面向编码 Agent 的技能,覆盖从隔离 worktree 到实现、审核和合并的 PR 全流程。它强调拆分为小型独立 PR,并以证据记录手动 QA。
主要功能
- 01拆分为原子化 PR
- 02创建隔离分支 worktree
- 03要求手动 QA 留存证据
- 04循环检查 CI 与 Cubic
- 05创建并合并 PR
适用场景
- 01交付大型任务中的独立部分
- 02隔离并行进行的 PR 工作
- 03根据 CI 或审核意见修复变更
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ code-yeongyu/ oh-my-openagent --skill work-with-pr安装后在 Claude Code 中输入/work-with-pr确认已加载。
详细介绍
与 PR 协作 — 完整 PR 生命周期
你将执行完整的 PR 生命周期:从为任务新建专属工作树,到通过 ulw-loop 实现并完成有证据支撑的手动 QA,再到创建 PR,并持续验证,直到 PR 合并。此循环设有两个关卡——CI 和 Cubic;任何关卡未通过,都会让你回到该 PR 的工作树中修复并重新进行 QA。你需要持续循环,直到所有启用的关卡同时通过。
交付单元是能够编译、通过验证并独立成立的最小 PR,而不是“一项任务,一个 PR”。 一项任务通常会拆分成多个原子 PR;下文描述的是其中一个 PR 的生命周期,因此请对每个 PR 都执行此流程,并在 Phase 0 中并行构建彼此独立的 PR。
Phase 0: Setup → Split into atomic PRs, then branch + worktree per PR (parallel when independent)
Phase 1: Implement → Drive the work through the ulw-loop skill:
evidence-bound manual QA per success criterion, atomic commits
Phase 2: PR Creation → Push, create a reviewer-readable English PR targeting dev
Phase 3: Verify Loop → Unbounded iteration; a failing gate routes back to Phase 1:
├─ Gate A: CI → gh pr checks (bun test, typecheck, build)
└─ Gate B: Cubic → cubic-dev-ai[bot] "No issues found"
(SKIPPED, not failed, when Cubic's quota is exhausted)
Phase 4: Merge → Auto-merge by default; wait until actually merged, then worktree cleanup
Phase 0:设置
在开始实现之前,为每个 PR 新建一个隔离的工作树。用户的主工作目录是只读上下文——其中可能有未提交的工作,切换分支会破坏这些工作。隔离还能让并行处理变得简单:每个 PR 使用一个工作树,这样多个 PR 可以同时构建而不会相互冲突。
1. 确定 PR 的拆分方式
在创建任何内容之前,将任务拆分成尽可能小的原子 PR,确保每个 PR 都能编译、通过验证,并交付一个可供审查的部分。优先选择多个小 PR,而不是一个大 PR——200 行的 PR 会得到认真审查;2000 行的 PR 则只会被草草批准。按依赖关系排序:彼此独立的部分从基分支分支出来并行处理;有依赖关系的部分则依次堆叠,每个部分都从前一个部分分支出来。
建议默认并行构建多个彼此独立的 PR,而不是把它当作特殊选择:
- Subagents — 为每个 PR 分派一个后台子 Agent,由其独立负责自己的工作树、分支以及完整的 Phase 0→4 生命周期。
- 团队 — 对于更大规模的并行拆分,组建一个团队(
team_mode),并为每个 PR 指派一名成员。
当工作规模大到需要制定计划(ulw-plan)时,这种拆分不是可有可无的润色:计划必须将原子 PR、依赖顺序以及哪些 PR 可以并行执行,明确列为一等结构。
2. 确认仓库上下文
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
REPO_NAME=$(basename "$PWD")
BASE_BRANCH="dev" # CI blocks PRs to master
3. 创建分支
如果用户提供了分支名称,请使用该名称。否则,根据任务生成分支名称:
# Auto-generate: feature/short-description or fix/short-description
BRANCH_NAME="feature/$(echo "$TASK_SUMMARY" | tr '[:upper:] ' '[:lower:]-' | head -c 50)"
git fetch origin "$BASE_BRANCH"
git branch "$BRANCH_NAME" "origin/$BASE_BRANCH"
4. 创建工作树
将工作树放在仓库的同级目录中,而不是仓库内部。这样可以避免 Git 嵌套仓库问题,并保持工作树干净。
WORKTREE_PATH="../${REPO_NAME}-wt/${BRANCH_NAME}"
mkdir -p "$(dirname "$WORKTREE_PATH")"
git worktree add "$WORKTREE_PATH" "$BRANCH_NAME"
5. 设置工作上下文
后续所有工作都在工作树中进行。必要时安装依赖:
cd "$WORKTREE_PATH"
# If bun project:
[ -f "bun.lock" ] && bun install
Phase 1:实现
在工作树中通过 ulw-loop 技能(你的运行框架原生的 ultrawork 循环)完成所有实现工作。不要随意手动推进:ulw-loop 会将需求拆分为带有二元成功标准的目标,委派适当规模的子 Agent 进行代码编辑和 QA;而这里要求必须使用它的原因是,它会强制要求通过有证据支撑的真实环境手动 QA来证明每项成功标准,而不只是依赖全绿的测试套件。
手动 QA 是关卡,不是测试。 本仓库对此规定绝对:任何变更只要会影响 OpenCode 或 Codex,就必须先运行真实的运行框架(tmux / HTTP / 浏览器 / GUI——使用 ulw-loop 技能中的手动 QA 渠道表)并将证据写入磁盘,之后才算完成。没有证据文件就表示没有进行 QA;此时不得提交或推送。“类型检查通过”和“bun test 全绿”都不算 QA。
范围控制
每个 PR 都要保持精简:只交付它负责的那一部分,添加测试,证明变更有效,然后停止。不要重构周边代码、添加配置选项,也不要“改进”没有问题的内容——这些工作应放在独立的 PR 中;扩大范围会让问题更难定位。
提交策略
ulw-loop 通过 git-master 创建提交。保持每次提交原子化,以便 CI 在某项变更上失败时,可以单独定位并修复,而无需撤销所有内容:
3+ files changed → 2+ commits minimum
5+ files changed → 3+ commits minimum
10+ files changed → 5+ commits minimum
每次提交都应将实现与测试配对;只有在某项标准对应的 QA 证据已写入磁盘后,才能提交该项内容。
### 推送前本地验证推送前,运行 CI 将执行的相同检查——这是一道低成本的预筛选,可节省约 3-5 分钟的 CI 往返时间,不能替代上述手动 QA:
bun run typecheck
bun test
bun run build
推送前修复所有失败项;每项修复都应单独提交为原子提交。
阶段 2:创建 PR
<pr_creation>
推送并创建 PR
git push -u origin "$BRANCH_NAME"
PR 正文应使用英文,面向未跟进实现过程的人工审阅者。正文必须用通俗的语言说明工作内容,按与审阅者相关的领域归类变更,而不是罗列文件;并确保 QA 证据便于审查,让审阅者无需猜测每条日志能证明什么。引用经过脱敏的产物;不要将包含机密的原始日志、环境变量转储、令牌、身份验证标头或私人凭据粘贴到 PR 中。
如果 PR 正文需要截图或终端 PNG,请遵循 docs/reference/github-attachment-upload.md:通过已登录的网页会话上传到 GitHub 用户附件,只包含最终的 https://github.com/user-attachments/assets/<uuid> URL;切勿提交临时图片、使用发布资源或外部托管服务,也不要记录 Cookie 或令牌。
gh pr create \
--base "$BASE_BRANCH" \
--head "$BRANCH_NAME" \
--title "$PR_TITLE" \
--body "$(cat <<'EOF'
## Summary
[2-4 sentences in plain language: what changed, why it changed, and how observable behavior is different after this PR.]
## Changes
[Group bullets by reviewer-relevant area, not by file. Each bullet should say what changed and how a reviewer can map it to the diff.]
## QA & Evidence
For each automated command or manual QA action:
- **What was tested:** [command or surface driven, with the behavior it was meant to prove]
- **Observed result:** [actual result, including before/after when relevant]
- **Artifact:** [`path/to/sanitized-log-or-report`]
- **Why sufficient:** [which risk or success criterion this evidence covers]
## Risks & Residuals
[Map each meaningful risk to the evidence above and state the conclusion: mitigated, accepted, or blocked. Include unavailable gates here with the concrete reason.]
## Related Issues
[Link to issue if applicable]
EOF
)"
记录 PR 编号:
PR_NUMBER=$(gh pr view --json number -q .number)
</pr_creation>
阶段 3:验证循环
这是此技能的核心。所有正在执行的门禁都必须通过,PR 才算准备就绪。循环没有迭代次数上限——持续执行直到完成。门禁顺序是有意安排的:CI 成本最低、速度最快;Cubic 属于外部异步门禁。门禁 B(Cubic)是唯一可以跳过而非通过的门禁——仅限配额耗尽时;绝不能仅仅因为它发现问题就跳过。门禁失败不意味着修补后推送:应返回阶段 1,在那里按照相同的范围纪律修复;如果行为发生变化,则需重新获取手动 QA 证据,然后再重新进入循环。
<verify_loop>
while true:
1. Wait for CI → Gate A
2. If CI fails → back to Phase 1: read logs, fix + re-QA, commit, push, continue
3. Check Cubic → Gate B
4. If Cubic has issues → back to Phase 1: fix + re-QA, commit, push, continue
5. If Cubic quota out → record Gate B SKIPPED, stop waiting on it
6. All active gates pass → break
门禁 A:CI 检查
CI 是反馈最快的环节。通过 monitor 订阅其完成状态——绝不要用 gh pr checks --watch 阻塞一次模型往返。
# Subscribe to CI completion — the monitor event wakes the session when checks finish.
# Do NOT use `gh pr checks --watch` as a blocking tool call; it burns a full model
# round-trip (~29s) on every poll. Instead, register a monitor and end the turn:
monitor({
description: "CI completion for PR $PR_NUMBER",
command: "gh pr checks $PR_NUMBER --watch --fail-fast",
filter: "completed|fail|cancel"
})
# → end turn; the monitor's matching line arrives as an injected event.
# For a single midpoint status peek (at most once), use:
# gh pr checks "$PR_NUMBER" # one-shot, no --watch
失败时:获取失败运行的日志,以了解出错之处:
# Find the failed run
RUN_ID=$(gh run list --branch "$BRANCH_NAME" --status failure --json databaseId --jq '.[0].databaseId')
# Get failed job logs
gh run view "$RUN_ID" --log-failed
阅读日志,然后按照下方的迭代纪律进行修复。
门禁 B:Cubic 审批
Cubic(cubic-dev-ai[bot])是一个会在 PR 上发表评论的自动化审查机器人。它不使用 GitHub 的 APPROVED 审查状态,而是发布包含问题数量和置信度评分的评论。
审批信号:最新的 Cubic 评论包含 **No issues found**,且置信度为 **5/5**。
问题信号:评论列出了带有文件级详细信息的问题。
配额耗尽信号:Cubic 发布了使用量、配额或限制相关消息,而非审查结果;或者在下方规定的等待时间内没有出现 Cubic 审查。这是唯一可以跳过门禁 B 并继续的情况——在最终报告中将其记录为“已跳过”,绝不能默默跳过。发现问题绝不是跳过的理由。
# Get the latest Cubic review
CUBIC_REVIEW=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/reviews" \
--jq '[.[] | select(.user.login == "cubic-dev-ai[bot]")] | last | .body')
if echo "$CUBIC_REVIEW" | grep -q "No issues found"; then
echo "Cubic: APPROVED"
elif echo "$CUBIC_REVIEW" | grep -qiE "quota|usage limit|rate limit|out of (credits|reviews)|upgrade your plan"; then
echo "Cubic: SKIPPED (quota exhausted)" # Gate B satisfied-by-skip; do not loop on it
else
echo "Cubic: ISSUES FOUND"
echo "$CUBIC_REVIEW"
fi
发现问题时:Cubic 的审查正文包含结构化的问题描述。解析这些描述,判断哪些问题有效(其中一些可能是误报),并按照下方的迭代纪律修复有效问题。
PR 更新后会自动触发 Cubic 审查。推送修复后,订阅新审查的到达通知——绝不要运行会消耗模型往返的 for _ in $(seq 1 30) 轮询循环。
# Subscribe to a NEW Cubic review after push. The monitor exits when a review
# newer than PUSH_TIME appears, or times out (quota exhausted → Gate B SKIPPED).
PUSH_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)
monitor({
description: "Cubic review for PR $PR_NUMBER",
command: "LATEST=$(gh api repos/${REPO}/pulls/${PR_NUMBER}/reviews --jq '[.[] | select(.user.login == "cubic-dev-ai[bot]")] | last | .submitted_at // empty'); [ -n \"$LATEST\" ] && [ \"$LATEST\" > \"$PUSH_TIME\" ] && echo NEW_REVIEW || echo WAITING",
filter: "NEW_REVIEW",
timeout_ms: 600000 # 10 min bound — if no review arrives, Gate B is SKIPPED (quota exhausted)
})
# → end turn; if the monitor times out without NEW_REVIEW, treat Gate B as SKIPPED.
# For a single midpoint peek (at most once), use:
# gh api "repos/${REPO}/pulls/${PR_NUMBER}/reviews" --jq '[.[] | select(.user.login == "cubic-dev-ai[bot]")] | last | .submitted_at'
迭代纪律
循环中的每次迭代都应:
- 仅修复失败门禁指出的问题
- 如果修复改变了运行时行为,则获取新的手动 QA 证据(阶段 1)
- 以原子方式提交(每次提交仅包含一个逻辑修复)
- 推送
- 从门禁 A 重新开始(代码已更改 → 重新进行完整验证)
避免在修复迭代中“改进”无关代码。修复循环中的范围蔓延会让调试更困难,也可能引入新的失败。
</verify_loop>
阶段 4:合并与清理
所有正在执行的门禁都通过后(Cubic 可因配额耗尽而跳过):
<merge_cleanup>
合并 PR(默认自动合并)
默认启用自动合并——除非用户明确要求不要合并,否则就启用。自动合并会将合并交给 GitHub 处理;所有必需检查通过后,PR 就会立即合并,因此你无需一直守着检查结果。它不会绕过检查:如果某项检查失败,GitHub 就不会合并,届时你需要返回阶段 1 修复问题,并像处理其他失败的检查一样重新进行 QA。
# This repository requires merge commits. Never use --squash or --rebase.
# --auto arms auto-merge: GitHub merges as soon as all required checks pass.
gh pr merge "$PR_NUMBER" --merge --auto --delete-branch
# If the repo has not enabled the auto-merge feature, --auto errors; once the gates
# are green, fall back to a direct merge: gh pr merge "$PR_NUMBER" --merge --delete-branch
然后订阅合并完成通知——绝不要用 until [ ... MERGED ] 轮询循环阻塞模型往返:
# Subscribe to merge completion. The monitor exits when gh pr view returns MERGED.
monitor({
description: "Merge completion for PR $PR_NUMBER",
command: "[ \"$(gh pr view $PR_NUMBER --json state -q .state)\" = \"MERGED\" ] && echo MERGED || echo WAITING",
filter: "MERGED",
timeout_ms: 1800000 # 30 min bound for auto-merge to land after all gates pass
})
# → end turn; the MERGED event wakes the session for the cleanup step.
# If the monitor times out, check merge state once: gh pr view "$PR_NUMBER" --json state -q .state
如果用户选择不合并,则跳过合并,但仍要执行下面的清理操作:无论如何都会移除工作树。
将 .omo 状态同步回主仓库
移除工作树前,先将 .omo/ 状态复制回来。如果 .omo/ 被 Git 忽略,那么在工作树中运行期间写入其中的文件不会被提交或合并——移除工作树时这些文件就会丢失。
# Sync .omo state from worktree to main repo (preserves task state, plans, notepads)
if [ -d "$WORKTREE_PATH/.omo" ]; then
mkdir -p "$ORIGINAL_DIR/.omo"
cp -r "$WORKTREE_PATH/.omo/"* "$ORIGINAL_DIR/.omo/" 2>/dev/null || true
fi
清理工作树
工作树已完成使命——将其移除以避免占用过多磁盘空间:
cd "$ORIGINAL_DIR" # Return to original working directory
git worktree remove "$WORKTREE_PATH"
# Prune any stale worktree references
git worktree prune
报告完成情况
总结所发生的事情:
## PR Complete
- **PR**: #{PR_NUMBER} — {PR_TITLE}
- **Branch**: {BRANCH_NAME} → {BASE_BRANCH}
- **Iterations**: {N} verification loops
- **Gates**: CI pass | Cubic {pass | SKIPPED (quota exhausted)}
- **Merged**: {yes | no — left for you to merge, as requested}
- **Worktree**: cleaned up
</merge_cleanup>
故障恢复
<failure_recovery>
如果遇到无法恢复的错误(例如,与基分支发生合并冲突、基础设施故障):
- 不要删除工作树——用户可能想要检查或手动继续操作
- 报告发生了什么、尝试过哪些操作,以及当前进展
- 提供工作树路径,以便用户继续操作
对于合并冲突:
cd "$WORKTREE_PATH"
git fetch origin "$BASE_BRANCH"
git rebase "origin/$BASE_BRANCH"
# Resolve conflicts, then continue the loop
</failure_recovery>
反模式
| 违规行为 | 失败原因 | 严重程度 |
|---|---|---|
| 在主工作树而非隔离工作树中操作 | 污染用户的工作目录,可能破坏未提交的工作 | CRITICAL |
| 没有将手动 QA 证据保存到磁盘就提交或推送 | “测试通过”永远无法证明功能正常;对于涉及 OpenCode/Codex 的更改,仓库明确禁止这种做法 | CRITICAL |
| 直接推送到 dev/master | 完全绕过审查 | CRITICAL |
| 代码更改后跳过 CI 检查 | Cubic 可能基于过时的代码通过检查 | CRITICAL |
| 因 Cubic 发现问题而跳过它 | 只有配额耗尽才足以成为跳过的理由;必须修复实际问题并重新推送 | HIGH |
| 在验证循环中修复无关代码 | 范围蔓延会导致新的故障 | HIGH |
| 失败时删除工作树 | 用户将无法检查或继续操作 | HIGH |
| 没有理由就忽略 Cubic 的误报 | 应评估 Cubic 提出的问题,而不是盲目否定 | MEDIUM |
| 将互不相关的任务切片打包进一个大型 PR | 原子化审查不复存在——2000 行的 PR 会被草率批准,回归问题会被隐藏,一个有问题的切片还会阻塞其他所有切片 | HIGH |
| 一次提交大量更改 | 更难定位故障,且违反 git-master 原则 | MEDIUM |
| 推送前不运行本地检查 | 会把 CI 时间浪费在显而易见的故障上 | MEDIUM |