OpenHuman 的 ship-and-babysit 工作流:从本地提交到 PR 全绿的端到端发布流程
本文基于 OpenHuman 仓库中的 Agent 定义文件 .agents/agents/ship-and-babysit.md,完整拆解其“提交 → 推送 → 开 PR → 盯 CI 与 CodeRabbit 反馈”的四阶段端到端发布(ship)流程。该文档是 OpenHuman 团队为编码 Agent(Claude Code / Codex 等)编写的行为规范,定义了 fork 模型下的分支/远端规则、功能级 E2E 覆盖门禁、PR 模板合规要求,以及一个可重复执行的 CI 看护循环。读完本文,你将掌握如何在开源 fork 贡献场景中,用脚本化的 gh + git 命令链把一个功能分支安全地驱动到“CI 全绿 + 评审清零”的状态,并了解 OpenHuman 用 mock 后端支撑 Rust / Playwright 双栈 E2E 的配套机制。
一、流程定位:为什么 OpenHuman 需要 ship-and-babysit
在 .agents/agents/ 目录下,OpenHuman 定义了三个协作 Agent,各司其职:
| Agent 文件 | 职责 | 触发场景 |
|---|---|---|
| ship-and-babysit.md | 从本地改动一路负责到 PR 变绿:提交、推送、开 PR、盯 CI 与评审反馈 | 用户要求“端到端把功能发出去”,而非只实现功能 |
| pr-manager.md | 接手一个已有 PR:检出、分流评论、应用所有可执行修改、推送回 PR 分支 | 用户给出 PR URL/编号,要求处理评论、清理、推进到可合并状态 |
| pr-manager-lite.md | 在调用方已准备好工作区(分支已检出、base 已合入、upstream 已设置)时,直接跳到“收评论 → 修 → 推” | 本地工作区已由 preem 等 shell 助手预先准备好的场景 |
ship-and-babysit 的 frontmatter 明确了它的定位:
name: ship-and-babysit
description: Commit local changes, push the branch to the user's fork, open or reuse a PR against tinyhumansai/openhuman:main, then babysit CI and CodeRabbit feedback until the PR is green and clean. Use when the user wants an end-to-end ship flow, not just implementation.
model: inherit
即:它不是“实现功能”的 Agent,而是“把已经写完的东西安全送出去并盯到收工”的流程执行器。该文档的完整流程分为四个 Phase:Commit(提交)→ Push(推送)→ Open PR(开 PR)→ Babysit loop(看护循环),并辅以一组硬性 Guardrails(护栏)。下文逐段展开,并结合仓库中真实存在的脚本与测试目录佐证每一环节的落地方式。
二、仓库事实与 fork 模型:流程的前置约定
文档开列了四条“Repo facts”,它们是后续所有命令的坐标系:
- Upstream 是
tinyhumansai/openhuman,PR 一律以main为基线; - 分支推送到
origin(用户自己的 fork),upstream只用于 fetch,绝不向其推送; - PR 通过
--head <fork-owner>:<branch>指向tinyhumansai/openhuman:main创建; - PR 描述遵循模板 .github/PULL_REQUEST_TEMPLATE.md;
- 功能类改动在 ship 之前必须有匹配的 E2E 覆盖。
为在整个流程中复用,文档要求在开始时一次性解析出 fork 所属者:
FORK_OWNER=$(git remote get-url origin | sed -E 's#.*://[^/]+(\.git)?$#\1#')
这条命令从 origin 的 remote URL(兼容 owner/repo.git 与 owner:repo.git 两种写法)中用正则提取出 fork owner 段。安全约束同样前置:如果 origin 解析出来就是 tinyhumansai(即没有 fork、直指上游),Agent 必须停下并要求用户先添加 fork remote——永远不把分支推送到上游仓库。这是典型的 fork 贡献模型防护:upstream fetch-only,origin 可读写,从机制上杜绝误推。
三、Phase 1 — Commit:只提交相关内容,不绕过 hook
提交阶段有四步纪律:
- 检查
git status、已暂存与未暂存的 diff,以及近期提交信息(用于对齐提交风格); - 如果没有任何改动、且分支已推送、且已有 PR,直接跳到 Phase 4——这是看护循环可以独立复用的关键设计,意味着该文档既可以驱动“从零到绿”,也可以接管“只差盯 CI”的半成品状态;
- 如有本地改动,只暂存相关文件,并使用 Conventional Commits 前缀:
feat:、fix:、refactor:、chore:、docs:、test:; - 对自己的改动不得绕过 commit hooks(不得用
--no-verify)。
与 pr-manager.md 相比,ship-and-babysit 的提交纪律更严格:后者在 rebase 解决冲突后允许 --force-with-lease,而本文档对 hook 的态度是“自己的改动必须过 hook”,唯一例外出现在 Phase 2 的 pre-push 场景(后述),且要求把例外显式记录进 PR 描述。
四、Feature E2E 规则:ship 前的覆盖门禁
这是文档中最具工程含金量的一段——它把“什么改动需要什么测试”固化成了可执行的判定规则:
- Rust 侧:涉及 core、domain、persistence、CLI、JSON-RPC 的功能改动,需要在
tests/*_e2e.rs中有 Rust E2E 覆盖;新增或变更的 RPC 面通常应落在 tests/json_rpc_e2e.rs。 - 前端侧:用户流程需要 Playwright E2E 覆盖,落在
app/test/e2e/specs/*.spec.ts。仓库中该目录实际包含大量 spec(如chat-tool-call-flow.spec.ts、auth-access-control.spec.ts、card-payment-flow.spec.ts等),与该规则的目录约定一致。 - mock 贯穿全程:E2E 必须通过 scripts/mock-api-server.mjs、
scripts/mock-api/*或 app/test/e2e/mock-server.ts 把后端调用打满 mock,禁止在 E2E 中触碰真实后端或第三方 API。 - 优先使用聚焦命令,避免全量跑:
pnpm test:rust:e2e -- --suite <suite>
pnpm --filter openhuman-app test:e2e:web:build
bash app/scripts/e2e-web-session.sh test/e2e/specs/<spec>.spec.ts
- 单元测试仍然有价值(针对窄逻辑),但不能替代新功能的 E2E 覆盖。
这几条规则在仓库中都有真实实现支撑,可以直接验证:
pnpm test:rust:e2e在根 package.json 中定义为bash scripts/test-rust-e2e.sh。脚本 scripts/test-rust-e2e.sh 的实际行为与文档完全对应:它先在固定端口(默认MOCK_API_PORT=18505)启动node scripts/mock-api-server.mjs,轮询__admin/health直至就绪,然后串行执行tests/*_e2e.rs中列出的全部 suite;支持--suite <name>多次过滤(对应文档里的“聚焦命令”),--之后的参数转发给cargo test(例如-- --ignored可启用带#[ignore]的用例)。脚本还显式设置BACKEND_URL/VITE_BACKEND_URL指向 mock 地址,并把RUST_MIN_STACK提到 16 MiB 以承载 agent-harness E2E 的深 future 栈——细节与“mock 打满、不碰真实服务”的规则一一对应。该脚本同时被.github/workflows/e2e.yml的rust-e2e-linuxjob 调用,保证本地命令与 CI 行为同源。pnpm --filter openhuman-app test:e2e:web:build在 app/package.json 中映射到bash ./scripts/e2e-web-build.sh,是 Playwright web 端 E2E 的构建入口。bash app/scripts/e2e-web-session.sh test/e2e/specs/<spec>.spec.ts对应的 app/scripts/e2e-web-session.sh 会拉起三件套:mock 后端(默认端口 18473)、openhuman-core(端口 17788,/rpc端点)和 web 服务(端口 4173),并用临时OPENHUMAN_WORKSPACE隔离状态——这正是文档要求的“整条调用链走 mock”的具象化。
从源码结构看,这套规则的意图是:Rust 核心与 Tauri 前端各有独立 E2E 通道,但共享同一个 mock 后端(scripts/mock-api-server.mjs),使本地、Docker、CI 三处跑的是同一条链路,从而让“Feature E2E rule”不只是一句口号,而是有脚本、端口、健康检查兜底的可验证门禁。
五、Phase 2 — Push:先确认不在 main 上
推送阶段三步:
- 确认当前分支不是
main; - 推送到
origin,若缺少 upstream tracking 则加-u; - 对 pre-push hook 失败做了区分处理:
- 若 hook 失败源于无关的既有破损(pre-existing breakage),允许
git push --no-verify,但必须在 PR 描述中显式记录这一例外; - 若 hook 失败源于自己的改动,必须修复后重新推送。
- 若 hook 失败源于无关的既有破损(pre-existing breakage),允许
这个“区分归因、例外留痕”的做法,比一刀切地禁止或允许 --no-verify 更贴近真实 fork 贡献场景:上游仓库的 hook 失败不应阻塞一个与本地改动无关的 PR,但任何绕过都必须进入 PR 叙事,供维护者审计。
六、Phase 3 — Open PR:查重 + 模板合规
开 PR 阶段的命令与要求:
- 验证
upstream确实指向tinyhumansai/openhuman; - 先查该分支是否已有 PR,避免重复开 PR:
gh pr list --repo tinyhumansai/openhuman --head <fork-owner>:<branch> --state open --json number,url
- 若不存在 PR,先检查
git log main..HEAD与git diff main...HEAD,再撰写标题与描述,且描述必须严格遵循 .github/PULL_REQUEST_TEMPLATE.md。其中 checklist 的每一项都必须勾选;不适用项要写成- [x] N/A: <reason>的形式,这样 scripts/check-pr-checklist.mjs(由根package.json的pr:checklist脚本暴露)才能通过。
这一点与真实模板内容吻合:.github/PULL_REQUEST_TEMPLATE.md 的 “Submission Checklist” 明确要求“If a section does not apply to this change, mark the item as N/A with a one-line reason. Do not delete items.”,清单条目覆盖测试新增(happy path + 至少一个 failure/edge case)、变更行 diff 覆盖率 ≥ 80%(由 pnpm test:coverage 与 pnpm test:rust 本地预演,门禁在 .github/workflows/ci-lite.yml)、docs/TEST-COVERAGE-MATRIX.md 的功能 ID 矩阵更新、无新增外部网络依赖(mock 策略)、以及 ## Related 中用 Closes #NNN 关闭关联 issue 等。模板还专门设有一段 “AI Authored PR Metadata”——要求 AI 发起的 PR 记录 Linear issue、分支、commit SHA、验证命令(pnpm --filter openhuman-app format:check、pnpm typecheck、聚焦测试、Rust/Tauri fmt/check)以及被阻断的验证命令与影响面。这与 ship-and-babysit 文档本身作为 Agent 规范的身份形成闭环:Agent 开的 PR,描述结构由模板强约束,且要能解释自己跑了哪些验证。
- 以
main为基线创建 PR; - 记录 PR 编号与 URL,供后续看护循环使用。
七、Phase 4 — Babysit loop:看护循环
这是文档同名“babysit”的核心:一个明确终止条件的循环,重复执行直到 PR 变“clean”:
- 查 CI:
gh pr checks <PR#> --repo tinyhumansai/openhuman --json name,state,link,description
- 修 Actions 失败:若是 Actions 支撑的 check 失败,用
gh run view <run-id> --log-failed --repo tinyhumansai/openhuman拉取失败日志,修复、提交、再推送; - 查 CodeRabbit 反馈:同时拉取 PR 行内评论与 issue 评论:
gh api repos/tinyhumansai/openhuman/pulls/<PR#>/comments --paginate
gh api repos/tinyhumansai/openhuman/issues/<PR#>/comments --paginate
- 逐条处置建议:正确且在范围内的建议直接采纳;错误或超范围的建议,先在讨论串里回复一个简短的驳回理由,再处理;
- 通过 GitHub GraphQL API 关闭已解决的评审线程(resolve review thread);
- 退出条件(三个同时满足):必需 check 全部成功、没有未解决的 CodeRabbit 线程、没有新的 CodeRabbit issue 评论在要求改动。
循环的退出条件被写成合取式(AND),这避免了“CI 绿了但评审没清”或“评审清了但 CI 还在跑”就提前收工的常见 Agent 失败模式。
八、Guardrails:四条不可逾越的护栏
文档末尾列出的护栏值得单独强调,它们定义了该流程的“权力边界”:
- 绝不推送到
upstream——上游仓库对贡献者永远是 fetch-only; - 绝不 force-push
main; - 绝不未处理就 resolve 评审线程——要么修掉问题,要么回复有理有据的驳回理由;
- 不合并 PR。流程在“CI 全绿 + 评审干净”处停止,合并权保留给维护者。
最后一条尤其关键:它把 ship-and-babysit 定义为“推进到可合并”而非“替人合并”,与 pr-manager.md 中“finish the PR, not produce a triage report”(必须动手修,而不是只输出分流报告)的哲学互补——前者负责把 PR 做完,后者(连同 Guardrails)确保它停在维护者决策的边界之前。
九、配套机制速查
把文档中出现的关键命令/文件与仓库实体对照,读者可以按路径自行深入:
| 文档中的要素 | 仓库中的对应实现 |
|---|---|
pnpm test:rust:e2e -- --suite <suite> |
package.json 中 test:rust:e2e → scripts/test-rust-e2e.sh(mock 后端 + tests/*_e2e.rs 串行执行,--suite 可多次过滤) |
Rust E2E 覆盖落点 tests/*_e2e.rs |
tests/json_rpc_e2e.rs 等,与脚本内 ALL_E2E_SUITES 列表一致 |
前端 E2E 落点 app/test/e2e/specs/*.spec.ts |
app/test/e2e/specs/ 下大量 Playwright spec(chat-*、auth-*、agent-* 等) |
| mock 后端 | scripts/mock-api-server.mjs、scripts/mock-api/、app/test/e2e/mock-server.ts |
| web 端 E2E 会话脚本 | app/scripts/e2e-web-session.sh(mock + core + web 三进程拉起,端口隔离) |
| PR 模板 | .github/PULL_REQUEST_TEMPLATE.md(checklist / diff 覆盖率 ≥ 80% / AI Authored PR Metadata) |
| checklist 校验 | pnpm pr:checklist → scripts/check-pr-checklist.mjs |
| 同族 Agent | pr-manager.md、pr-manager-lite.md |
十、小结:一份可移植的 Agent 发布规范
ship-and-babysit 的技术价值不在于任何单条命令,而在于它把“fork 贡献 + AI Agent 值守”这一真实协作场景中的每一个风险点都转化成了显式规则:fork owner 一次性解析且永不推上游、提交只圈相关文件、hook 例外的双归因处理、开 PR 前查重、checklist 的 N/A: <reason> 合规写法、CI 与 CodeRabbit 双通道轮询、线程必须先处置再 resolve、以及“到绿为止但不合并”的权限边界。
从仓库结构看,这套规范并非孤立文本:--suite 过滤、mock 健康检查、e2e-web-session.sh 的三进程编排、pr:checklist 脚本都已经在仓库中实现并被 CI 复用。对维护多语言(Rust + React/Tauri)单仓的团队而言,把这类“发布纪律”沉淀为 Agent 定义文件(.agents/agents/ 下三个 md 文件互为印证),并让规范中的每条命令都能在仓库里找到同名脚本承接,是保证 AI 参与贡献时行为可预期、可审计的关键做法。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00