开发工作流:本地功能分支 + 测试用例门禁(核心开发专用)
核心逻辑:main 必须永远绿,且 main 上的内容必须是用户验收过的。所有核心改动(新功能、bug 修复)走「测试方供用例 → 本地功能分支开发 → 全量测试(机器门禁)→ 构建测试包给用户安装验收(人工门禁)→ 合并回 main」。两道门禁各管一层:机器门禁防「改 A 坏 B」的客观回归,人工门禁防「用例全绿但不是用户想要的样子」的主观偏差——用例是需求的机器翻译,翻译本身可能有漏(尤其界面、体验、文案这类难以完全用例化的维度),人在合并前把关补上这层。
2026-09-07 起远端 GitHub PR / CI 流程取消(存量仓库的 ci.yml 保留不动、不主动删),裁决全部本地化。三权分立不变:用例定义权归测试 Agent(Hopper / TestEngineerAgent)——需求先转成验收用例(用例先行),开发 Agent 不写用例、不改用例;代码实现权归开发 Agent——只改实现代码去满足用例,永远不反向改用例迁就实现;结果裁决权归两道门禁 + Hopper 事后归档验收。
流程总览(各步细节见正文与 references):
- 第 0 步:定性——这次需求是不是核心开发
- 第 1 步:开工门禁——main 上必须有本次需求的未通过用例组
- 第 2 步:建 worktree + 功能分支
- 第 3 步:开发(用例目录只读、问题终止上报)
- 第 4 步:对齐 main + 检查用例变更
- 第 5 步:全量测试(机器门禁)
- 第 6 步:构建测试包 + 用户验收(人工门禁)
- 第 7 步:超集校验 + 合并回 main
- 第 8 步:收尾(Hopper 归档、清理)
第 0 步:定性——这次需求是不是核心开发
项目是软件项目 ≠ 这次需求就是核心开发。每次触发先判断:改动会不会触碰核心源代码的运行行为、应不应该由测试用例来覆盖——是则走本流程;纯文档、README、版本号、配置、格式调整是杂事,main 直改(git add → /commit)。拿不准时从严走本流程或直接问用户,宁重勿漏——把核心改动当杂事直改进 main,等于绕过两道门禁。
混合需求(核心功能 + 顺带文档)正常走本流程:琐碎文件修改不作为分支的开发目标,但功能开发必须同步的文档(README、CHANGELOG)在分支上顺带改是允许的,不算违规。
第 1 步:开工门禁——main 上必须有本次需求的未通过用例组
以 main 的最新提交为基准检查(git show main:test-cases/pending/ 之类,不能用脏工作区的状态冒充 main 状态):test-cases/ 目录结构齐全,且 pending/ 下存在本次需求对应的用例组(requirement.md + 用例文件)。
不满足则阻塞暂停,向用户汇报:请用户找测试 Agent(Hopper)提供最新的测试用例和需求文档并同步进项目。开发 Agent 不自己补用例——这是用例定义权的边界。过渡期(Hopper 供用例能力就绪前)用户手工提供用例也走同一结构,流程不区分用例来源。
目录结构、权限、同步机制全貌见 references/test-cases.md(开工前读一次)。
第 2 步:建 worktree + 功能分支
cd <主仓库目录> && git switch main && git pull
git worktree add ../<仓库名>-<功能名> -b <分支名> # 并行开发(推荐)
# 或不并行时:
git switch -c <分支名>
分支必须从最新 main 开出。分支命名收敛为 feat/<功能> / fix/<缺陷> + 小写短横线描述(如 feat/model-picker)——用例新增归测试方(开发 Agent 不再有 test/ 前缀)、杂事走 main 直改(不再有 chore/ 前缀)。一个分支只做一件事、只对应一个需求组。用户说「切分支 / 新建分支」但未说明用途时,默认创建名为 Test 的分支(用户 2026-09-06 立——快速试验场景不想每次起名,不追问用途;已存在未合并的 Test 分支时报告并让用户选择复用还是换名)。
worktree 的价值:多个功能(多个开发会话)并行时,同一目录里的改动、git status、测试运行会互相污染,分不清哪份改动属于哪个功能。每个功能独占一个 worktree 目录 + 分支,天然隔离。要点:
- worktree 目录放主仓库同级(
~/Developer/<仓库名>-<功能名>),不要嵌套在主仓库内(会被视为未跟踪内容,容易误提交); - 每个 worktree 首次进入装依赖(
bun install --frozen-lockfile/npm ci),node_modules 不共享; - 写全局共享目标的命令(如安装到系统位置的 sync 类脚本)不要在多个 worktree 并行跑;
- 一个分支同一时刻只能存在于一个 worktree。
第 3 步:开发中的纪律
- 用例目录只读 + 可运行,禁止增删改:
test-cases/下的一切(用例、requirement.md)只能读和执行,不能新建、修改、删除、移动。运行用例需要的伴生文件(fixture 等)也由测试方提供,缺了不自己补——阻塞上报。唯一的豁免是测试运行自产的缓存(__pycache__、.pytest_cache等),配合 .gitignore 兜底。本条已配套工具强制:跨端 hook(~/.claude/hooks/test-cases-guard.py,CC / ZCode / CodeBuddy / Trae 四端挂载)会直接拦截指向test-cases/的写操作(写入被 deny 时按提示走合规通道,不要绕过),见references/test-cases.md。 - 碰到用例或需求有问题 → 终止开发,向用户汇报:具体问题(用例跑不起来、用例疑似断言错误、需求文档含糊不清、用例之间互相矛盾)+ 建议,请用户与测试 Agent 对齐需求后解决。绝不通过改用例来绕过问题——这是 LLM 的著名失败模式(改不动代码就放宽断言),本流程从结构上禁掉它。
- 版本号不在功能分支 bump:多个并行分支各自 bump 必然冲突,版本号递增由发版流程统一处理(
/bump在 main 上改齐)。 - CHANGELOG 条目写在自己的段落:记录「为什么改 + 改了什么」;不同分支写的条目落在不同位置,git 能自动合并。避免和别的分支同时新建同一个版本标题。
第 4 步:对齐 main + 检查用例变更
功能开发完成后,把 main 合进当前分支(全仓库合并——包括 test-cases/ 目录,测试方的用例更新会自然带进来,这正是要全量合并的原因):
git merge main
merge 后必查一件事:diff 一下 pending/ 里本次需求组的用例和 requirement.md 有没有在开发期间被测试方更新。变了就先重新对齐需求(必要时重新评估开发内容和改动范围),再进下一步——开发期间需求悄悄变了而实现还照旧,是隐性返工的最大来源。有冲突照常解决,解决结果会被下一步的全量测试覆盖验证。
第 5 步:全量测试(机器门禁)
在分支上本地跑项目全量测试:项目原有单元测试 + test-cases/ 验收用例全部,加类型检查(如 bun test / npm test + tsc --noEmit,按项目技术栈)。
通过口径(三条,逐条判定):
passed/目录的用例必须全绿——这是回归防护,一条都不能红;pending/里本次需求组的用例必须全绿——这是增量目标;pending/里其它组的用例不阻塞本次合并——它们本来就处于未完成状态,红着是合理的(对应别的还没做完的工作;只要本次改动没把它们从「红」变「更红」,就不归本次管)。
未通过目录里只有本次一组用例时(串行开发的常见情形),这个口径就等价于「全部通过」。
红灯是分支上的正常工作状态(不是事故——main 红才是事故),在分支上修到绿再走下一步。
第 6 步:构建测试包 + 用户验收(人工门禁)
全量测试通过后、合并回 main 之前:在 dev 的 worktree 里按项目发布工艺构建测试版安装包(vsce package、npm pack 等,按项目来),交给用户安装实测,用户按 pending/<需求名>/requirement.md 逐条核对验收。
验收不通过是一个循环,不是一个终点:实现不符合需求 → dev 上改 → 重新跑全量测试(不是只跑改过的部分)→ 重新构建测试包 → 请用户复验;需求本身要变 → 走第 3 步第 2 条的通道(终止上报,用户找测试方改需求文档和用例、同步进来后重新对齐)。
构建与验收的完整规矩(测试版本标识、产物放 tmp/、不打 tag 不发 Release、与运行版隔离规则的关系)见 references/acceptance.md(进本步前读一次)。
第 7 步:超集校验 + 合并回 main
窗口纪律(合并前向用户提醒):从「main merge 进 dev」那一刻起,到「dev merge 回 main」那一刻止,main 不能有新提交、dev 也不能有新改动——merge 回 main 的必须是跑过全量测试且用户验收过的那份提交,main 或 dev 任何一边动了,这个等价关系就断了。全量测试一绿、用户验收一过就立刻合并,窗口期越短越安全;有别的分支处在窗口期时,测试方的用例同步也避开或事后通知返工。
合并前先跑硬校验:
git merge-base --is-ancestor main dev && echo "超集成立,可以合并"
这条命令检查「main 的所有提交都在 dev 里」(dev ⊇ main)。不成立说明 main 在窗口期动过(用户手动改、别的分支合回去、测试方同步用例都算)——必须回第 4 步重新对齐、重跑全量测试,绿了之后看 main 新进的是什么:只是用例或文档 → 用户验收结论延续,直接合并;进了别的功能代码 → 请用户快速复验再合并。纪律管「尽量不破坏」,校验管「破坏了必然被发现」,两条腿缺一不可。
成立则在 main 上合并(超集成立时这是快进合并,main 的指针直接移到 dev 最新提交,main 收到的树和测试过的树逐字节相同——这是整个保证的物理基础):
git switch main && git merge dev
超集纪律的完整论证(为什么窗口两边都不能动、并行分支怎么互相影响、「100% 没问题」的精确含义)见 references/merge-discipline.md(遇到窗口期被破坏、并行冲突时读)。
第 8 步:收尾
- 汇报请测试方归档:合并完成后向用户报告「已合并回 main,请让 Hopper 验收归档」。用例从
pending/挪到passed/由测试方做(开发 Agent 对用例目录无写权限),这同时是 Hopper 的独立验收环节——放弃 CI 后,这是「第三方裁决」的替代。归档与同步细节见references/test-cases.md。 - 清理 worktree:
cd <主仓库目录> && git worktree remove ../<仓库名>-<功能名>(测试包产物放 worktree 的 tmp/ 下,随 worktree 一起清)。 - 同步其它活着的 worktree:本次功能合并进 main 后,把 main 合回其它还活着的分支(进各 worktree 目录
git merge main)——冲突在几行规模时解决,比拖到几百行轻松;分支活得越短,冲突窗口越小。 - push:本流程不含 push——推远端走
/commit(用户自行git add后触发)或用户明确指示。
git 授权边界(与全局纪律衔接)
触发本 skill(用户要求开发核心功能)= 授权流程内的本地 git 操作:git worktree add / git switch -c / git merge main(对齐)/ git merge dev(合并回 main)/ git worktree remove,以及本地测试与构建。不含 push——推送远端仍只走 /commit(用户自行 git add 后触发)或用户明确指示。dev merge 回 main 产生的 merge commit 是本流程的固有产物,属流程内授权,不走「commit 授权 = 用户主动 /commit」的入口(那条管的是暂存区提交,本流程不触碰暂存区)。
references(按需加载)
references/test-cases.md— 测试用例体系全貌:目录结构、Hopper 权威源与单向同步、权限与 hook 强制、归档机制。第 1 步开工门禁前读。references/acceptance.md— 测试包构建与人工验收的完整规矩。第 6 步进验收前读。references/merge-discipline.md— 超集纪律完整论证:窗口期、硬校验、并行分支、main 窗口期变动的返工口径。第 7 步遇窗口期问题时读。