上一篇写了用 GitHub Projects + Orca 把多 Agent 编程工作流跑通的第一天——那天只是「能跑」。这一篇是五天后的续集:它已经长成一个完整的体系。先放数字:122 次提交、111 个 PR 合并、127 张 issue,side project(panta-log,本地优先的语音复盘工具)V0.1 MVP 六个工作包全部收官。

这篇不讲过程,把这套工作流现在的功能点按层拆开。每一层都是踩坑踩出来的,坑也照写。

看板层:从「能记任务」到「自动流转的任务系统」

上一篇的看板只能算记事本,这一轮它长出了四处自动化。地基是一条约定:一个 issue = 一个 worktree = 一个 PR,一张卡从开工到合并走完整闭环,下面的自动化都围着它转。

主卡 + 子卡(官方 sub-issue)。一个专项一张主卡,拆出的子卡用 GitHub 官方 sub-issue API 挂接,父卡页面自动显示进度条,子卡关闭自动勾选。之前手写 - [ ] #N tasklist 的做法废弃——它没有任何联动,最后一个子卡关了父卡还傻开着(这个坑真踩过:两个专项主卡都手动收的尾)。后来干脆写了个 30 行的 Action:最后一个子卡关闭时自动关闭父卡,专项生命周期零手动维护。

主卡子卡进度条

这个 Action 上线前还撞了一堵测试永远测不出的墙:issues 事件的 workflow,GitHub 只认默认分支上的版本。PR 分支里写得再对、本地 dry-run 全过,事件就是不派发——静默的,连报错都没有。「关父卡」这个动作因此没法在 PR 里真实验证,只能合进 master,等第一张收官的专项卡来检验。改 CI 配置类的东西,验证闭环天生缺一半。

建卡脚本。手工建 issue 的坑:14 张新卡全部漏挂看板、漏设字段——看板过滤直接失灵。现在建卡一律走脚本:

scripts/new-issue.sh --title "xxx" --body-file card.md \
  --type bug --phase 1 --area backend --priority P1 --labels area/hotkey

一条命令完成:issue 创建(含类型标签 + @me 指派)→ 挂板 → Status=Todo → Phase/Area/Priority/Milestone。建卡即合规,不靠记性。

建卡脚本效果:Todo 视图 9 张卡,状态/标签/优先级全部有值

三条自动流转:PR squash 合并 → issue 自动关 → 卡片自动进 Done;master CI 变红 → 自动开一张「master CI 红」跟卡,恢复绿自动关;最后一个子卡关闭 → 父专项卡自动关闭。加上 master 红时其他 PR 的停线门禁(快速失败,修复类 PR 打 area/ci 标签放行),看板从「任务的墓志铭」变成了「活的调度器」——这句是合并当天写的,现在看依然成立。

status_board 三列:Todo 14 / In Progress 1 / Done 89

认领协议(防撞车):开工前必须先把卡片置 In Progress 占坑 + 扫一眼有没有同卡的 open PR。这条是被撞出来的——两个会话同时开工一张卡,第二个的分支自动加了 -2 后缀:

同卡撞车,分支自动 -2 后缀

执行层:多实例隔离,测试与正式互不打架

上一篇没解决的一个问题:正式实例在自测,另一个会话要起实例验证代码——单实例锁、端口、数据目录全绑死一份,只能互相杀。

解法是给实例一个身份:PANTA_DATA_DIR 环境变量把数据目录(锁、数据库、日志)整体搬走,PANTA_PORT 换端口:

scripts/dev.sh                                                    # 正式实例
PANTA_DATA_DIR=~/.pantalog-dev PANTA_PORT=9787 scripts/dev.sh     # 测试实例

两个实例并行互不知道对方存在。不设环境变量时行为与原来完全一致,单实例防护不削弱。测试实例默认不注册全局热键(双实例抢系统热键没有赢家)。

CI 层:把「review 跟不上」交给机器

这是整个体系里最实的一层。先把丑话说在前面:多 agent 并行交付,PR 产生的速度远超我读 diff 的速度,很多 PR 我是扫一眼描述就合并的,并不确定测试是否存在、断言是否有效。解法不是更认真地 review(不可扩展),是把 review 里能机械化的部分全机械化。

还有一个前提得先交代:仓库在 GitHub 免费套餐上,分支保护配不了,API 实测 403。原生 required checks 用不了,「不让红的进 master」就没法交给平台,只能自己造两层:机器能硬卡的做进 CI(下面这些门禁全是);机器管不了的,比如「合并前到底看没看 checks」,写成红线协议塞进 CONTRIBUTING.md,当每个会话的交通规则。

覆盖率三件套

  1. PR 自动评论覆盖率变化——数字可见本身就有约束力:

PR 覆盖率自动评论

  1. 基线棘轮--cov-fail-under 把全量覆盖率封底,只许升不许降(71.5 一路涨到 79,因为还债和带测试的修复在推高它)
  2. diff-cover 增量门禁:PR 改动/新增的行强制测试触达 ≥80%,否则红、无法合并——存量零覆盖不追缴,新代码没测试就物理上进不来

覆盖率只回答「有没有测试」,回答不了「测试测的是什么」。最疼的一次来自旧库升级:测试套件每个用例都从 create_all 建新库开始,全绿;用户手里的旧库升级路径零覆盖,一启动就崩——新库永远测不出迁移 bug。此后立了铁律:动 schema 必须附「旧库就地升级」测试,用旧 schema 建库、跑迁移、断言结果,新库验证不算数。

前端三道棘轮(思路同源:存量封顶、增量达标):禁新增 ts-ignore、单文件行数封顶、i18n 双语 key 一致性。加上 oxlint+oxfmt 基线,前端从零约束追平了后端 Ruff 的治理水平。

运维细节:每个 job 硬超时(默认 360 分钟,挂死一次烧穿私库按分钟计费的额度)、concurrency 取消旧 run、runner 只用 ubuntu(私库按分钟计费,macOS 十倍费率用不起)、master 红自动跟卡:

master CI 红自动跟卡

红线协议:测试永远测不出的那部分。门禁管代码,管不住流程。五天里真正造成损失的坑,没有一个能被测试拦住:

  • 带红合并:master 变红过两次,都是 PR 还红着就合造成的。其中一次特别迷惑——本地全绿、CI 红,最后查清是 pytest 撞了 UTC 边界,北京早 8 点前后才触发。此后第一条红线:gh pr checks 全绿才许合并;日期相关的测试失败,先怀疑时区再怀疑代码。
  • 重复修:master 红了一次,两个会话同时开工修同一个问题,先落地的合了,后落地的整个作废重做。此后修前必看板 + 最新 run,确认没有别人在修同一张卡。
  • 继承错误逐卡修:master 红时切出去的新分支,错误跟着进每个 worktree。正解是修完 master 后各自 rebase,继承的错误随 rebase 消失;在多张卡里各自修同一个 master 问题,等于把一个 bug 修 N 遍。
  • 网页绕行:PR 冲突必须在本地 rebase 解决、跑绿、--force-with-lease 推回。在 GitHub 网页上手改文件「绕过」冲突,等于跳过本地测试直接进 master。

五条规则(还有一条「master 红就停线:不合并、不开新卡」)全写在 CONTRIBUTING.md 的「CI 红线规则」里,每个会话开工前必读。

基建层:崩溃教出来的基础设施

日志落盘。起因是某天早上的一场三连崩:启动时 16 条积压任务批量补投,每条各起线程调本地转写模型,MLX 的 GPU stream 线程绑定、并发直接 Metal 层 C++ 异常 → 主进程当场死亡 → 不走任何清理逻辑 → 子进程 UI 窗口成孤儿还开着。排查时发现日志只在终端,崩了连现场都没留下。修完之后日志体系是三层:后端轮转落盘、前端错误上报落盘、测试进程隔离(不污染用户日志)。

三连崩日志现场

日志第一次派上用场就值回票价:排查「转写卡很久」——从日志实锤三个叠加原因(批量串行排队、同日期聚合被重复触发 N 次、GLM 偶发返回不合规 JSON),顺手各修各的卡。

转写 worker 子进程。转写是 GPU 满载的重活,还持有 Python 全局锁把 API 线程饿死。挪进独立子进程后主进程永远响应,子进程退出连显存缓存一起释放。配套硬约束:父死子退(getppid 看门狗)——强退程序就是全停,转写中的任务重启后自动补投,不丢数据。

导入内容校验。测试残留的 23 个假 WAV(内容是 2048 个字母 x)被当真导入,直到转写才报一坨错。现在文件头魔数 + ffprobe 可解码探测,垃圾在门口被拦。配套还有时长上限:超长录音入库标 too_long 待手动转写,不再占用队列。

最有价值的一课:AI 实现跑偏了产品意图

五天里最值得写的发现不是技术问题。复盘时我意识到:每条录音处理完都会调一次 LLM 生成「AI 回顾草稿」(评分、心情、下一步),还会触发一次当日汇总——而我的产品文档写的是:录音级只做转写和结构化提取;每日回顾是「当天多条语音的合并」,在复盘页看。文档白纸黑字,实现凭空多出一层。

代价清单:LLM 调用量翻倍(当天额度打爆)、时间线多了没人要的草稿块、状态机多了误导性的「转写中」、队列被拉长。它存在了好几天,写它的 agent 没发现,验收它的我也没发现。

教训:agent 写完不会自己拿产品文档对照,这个验收动作只能人做。修正卡当天开、当天关:录音级只留转写 + 提取(事实/计划/想法/问题,承诺闭环的数据源),AI 回顾回归日级聚合(带防抖),prompt 集中管理与评测联动。

反思:AI 时代的质量观

五天下来,对「AI 写代码,人干什么」有了具体得多的答案。「review 跟不上」的正确解法不是更认真地 review,认真不可扩展。能机械化的部分全机械化:覆盖率棘轮挡住没有测试的代码,diff-cover 挡住改了没测的行,lint 和 guards 挡住风格漂移。下限交给机器,人只管机器管不了的那部分——这个功能该不该有,做得好不好,是不是我想要的。

多 agent 并行同理:协调成本是真实的。五个会话同时开工时,撞车、抢号、互相污染工作区都会发生——规范和脚本不是官僚主义,是把协调成本从人肉记忆挪进脚本。

下一步:V0.1 的退出条件是连续 14 天真机自测(录音 → 次日勾选闭环)。护栏立完了,接下来该真用了。


数据截至 2026-09-16 定稿:122 commits / 111 merged PRs / 127 issues;覆盖率 71.5% → 79.6% 且基线棘轮持续生效;全部工作由多个 Claude Code agent 在 Orca worktree 中并行完成。