PR 工作流不是“让模型写一段总结”。它是一条交付协议:先把任务隔离到 branch 或 worktree,再收集真实 diff 和测试证据,然后生成 commit 与 PR body,最后在危险动作前停下来让用户确认。
Pi 源码快照 61babc2 没有内置完整 PR agent。它提供的是更底层的能力:bash/edit/write 工具、extension 生命周期、session 记录、git checkpoint 示例和可扩展命令。课程要做的是在这些能力外层加一个 PR-ready harness,让 agent 的输出变成 reviewer 能放心打开的变更包。
上一阶段你已经能让 agent 修改代码、运行命令、生成 trace。现在问题变成:
- agent 修完 bug 后,怎么保证它不是直接污染主分支?
- PR 描述里的测试证据从哪里来,能不能被复核?
- commit、push、open PR 这些动作,哪些可以自动做,哪些必须让用户确认?
如果没有这套边界,agent 可能“看起来完成了任务”,但 reviewer 拿到的是一个缺测试、缺风险说明、甚至混入无关修改的 diff。
把 PR 工作流拆成六个控制点:
- branch 控制点:任务开始前创建隔离分支或 worktree,记录 base branch 和任务 id。
- change 控制点:agent 只能在授权路径内修改,并在结束时读取
git diff。 - evidence 控制点:每条测试命令都保存 command、exit code、摘要和关键输出。
- commit 控制点:commit message 来自任务目标和 diff 摘要,但 staged scope 必须先检查。
- PR body 控制点:PR body 不是流水账,而是 summary、verification、risks、review checklist。
- user confirmation 控制点:push、创建 PR、合并、危险 rewrite 都是显式确认点。
这六个点串起来后,PR 工作流才是工程系统,而不是 prompt 模板。
1. 从 branch 开始,而不是从 prompt 开始
Section titled “1. 从 branch 开始,而不是从 prompt 开始”真实 coding agent 不应该先写代码再想怎么交付。任务创建时就要记录:
type PrTask = { id: string; baseBranch: string; workBranch: string; prompt: string; cwd: string;};baseBranch 用来计算 diff,workBranch 用来承载变更,cwd 用来约束命令执行位置。后续所有证据都挂到同一个 id 上。
2. 把 diff 当成事实来源
Section titled “2. 把 diff 当成事实来源”PR body 的 summary 可以由模型草拟,但事实必须来自本地仓库:
git status --shortgit diff --stat <baseBranch>...HEADgit diff <baseBranch>...HEAD教学时要强调变量流:baseBranch 进入 diff 命令,diff 输出进入摘要器,摘要器只能解释已经发生的文件变化,不能凭任务描述补不存在的实现。
3. 收集测试证据
Section titled “3. 收集测试证据”测试证据至少包含四个字段:
type Evidence = { command: string; exitCode: number; summary: string; outputPath?: string;};exitCode 是机器判断,summary 是给 reviewer 快速阅读,outputPath 保存长日志。PR body 里可以只放摘要,但后台应该保留完整日志。
4. 生成 PR body
Section titled “4. 生成 PR body”课程 lab 的入口是 /Users/ryanchen/codespace/pi-agent-course-lab/src/13-pr-workflow.ts。这个 starter 已经把 PR body 拆成 Summary、Verification、Risks 三段。
课堂上要把它扩展成更接近真实交付的结构:
## Summary- 做了什么
## Verification- `npm test -- login` -> exit 0: login tests passed
## Risks- 可能影响哪些路径
## Review Checklist- [ ] diff 只包含本任务相关文件- [ ] 测试证据可以复跑- [ ] 没有 secrets、token、私有日志5. 设计用户确认点
Section titled “5. 设计用户确认点”PR 工作流里有两类动作:
- 可自动动作:创建临时分支、读取 diff、运行测试、生成本地 commit message 草稿。
- 需要确认的动作:stage 大范围文件、commit、push、创建 PR、merge、force push。
确认点不是为了降低自动化能力,而是为了让责任边界清楚:agent 可以准备材料,用户确认后才把材料推向共享仓库。
6. 关联 Pi 的 extension 思路
Section titled “6. 关联 Pi 的 extension 思路”Pi 的 auto-commit-on-exit.ts 示例说明 extension 可以在 session_shutdown 时检查 git status 并运行 git add / git commit。这不是课程推荐的最终 PR 策略,因为自动 git add -A 风险较高;但它证明了 Pi extension 能在生命周期事件里挂接 git 工作流。
更稳妥的 PR agent 应该把这个动作拆开:先收集 status 和 diff,再生成可审查计划,最后在用户确认后才 commit 和 push。
本课实践是把 starter 的 createPrBody() 扩成一个小型 PR packager。入口文件:
/Users/ryanchen/codespace/pi-agent-course-lab/src/13-pr-workflow.ts
第一步不要接 GitHub API,只处理本地材料:
const task = { title: "Fix flaky login test", baseBranch: "main", workBranch: "agent/fix-login-test",};
const diffSummary = ["Stabilized async wait", "Kept change scoped to test harness"];const tests = [ { command: "npm test -- login", exitCode: 0, summary: "login tests passed" },];const risks = ["Could hide a real timing issue if app behavior changes"];
const body = createPrBody({ title: task.title, summary: diffSummary, tests, risks });变量流要讲清楚:
task是用户输入和分支状态的结合,不来自模型自由发挥。diffSummary应由真实 diff 生成或人工确认。tests必须来自实际命令执行结果。risks可以由模型辅助枚举,但要绑定到 diff 中真实变更的文件和行为。
课堂代码推演
Section titled “课堂代码推演”课堂上把 PR workflow 压缩成一段可讨论的伪代码:
async function preparePr(task: PrTask) { await exec("git", ["switch", "-c", task.workBranch]);
const result = await runAgent(task.prompt); const status = await execText("git", ["status", "--short"]); const diff = await execText("git", ["diff", `${task.baseBranch}...HEAD`]);
const tests = await runEvidenceCommands(["npm run typecheck", "npm test"]);
const prBody = createPrBody({ title: task.prompt, summary: summarizeDiff(diff), tests, risks: classifyRisks(diff), });
return { status, diff, tests, prBody, needsUserConfirmation: true };}这段代码里有三个关键控制点:
第一,task.workBranch 在 agent 修改代码之前就存在,所以后续 diff 有边界。
第二,tests 不是 PR body 里的普通文本,而是由 runEvidenceCommands() 返回的结构化证据。命令失败时也要进入证据列表,因为失败本身是 reviewer 需要知道的信息。
第三,函数返回 needsUserConfirmation: true,表示它只准备 PR-ready 材料,不直接 push。课堂会让学生讨论:如果把 push 放进这个函数,应该增加哪些审批参数和失败回滚策略?
/Users/ryanchen/codespace/pi-agent-course-lab/src/13-pr-workflow.ts:本课 lab starter,包含Evidence类型和createPrBody()。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/examples/extensions/auto-commit-on-exit.tsat61babc2:展示 extension 能在session_shutdown中读取 git 状态并提交。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/examples/extensions/git-checkpoint.tsat61babc2:展示基于 session 事件创建 git checkpoint,适合理解“代码状态”和“会话状态”要同步。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/examples/extensions/github-issue-autocomplete.tsat61babc2:展示通过gh issue list读取 GitHub issue,并把外部研发对象接入输入体验。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/core/extensions/types.tsat61babc2:定义ExtensionAPI、session_shutdown、tool_call等 extension 边界。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/core/agent-session.tsat61babc2:AgentSession负责事件订阅、session 持久化、工具注册和 active tools 管理,是 PR harness 接入执行证据的宿主。/Users/ryanchen/codespace/external-sources/pi/packages/coding-agent/src/core/session-manager.tsat61babc2:session JSONL 记录、custom entry 和 fork 信息可用于把 PR 证据归档到任务历史。
学完本课后,你应该能做到:
- 解释 branch、diff、test evidence、commit、PR body 和用户确认点之间的顺序。
- 设计一个 PR body 数据结构,并说明每个字段从哪里来。
- 区分“模型生成摘要”和“命令产生证据”。
- 指出
git add -A、push、merge、force push 为什么必须经过额外确认。 - 能基于
/Users/ryanchen/codespace/pi-agent-course-lab/src/13-pr-workflow.ts生成一个可审查的 PR body。
- 实践题:扩展
/Users/ryanchen/codespace/pi-agent-course-lab/src/13-pr-workflow.ts,给createPrBody()增加Review Checklist,并把失败的测试命令也保留在Verification中。 - 思考题:如果 agent 自动 commit,但用户后来发现 diff 混入了无关文件,你会把防线放在 branch 创建、staging、PR body 生成,还是 push 前确认?说明原因。