规范:建立 Codex 多人协作与自助合并流程
This commit is contained in:
parent
a6d13f20b2
commit
9db8ac36e3
58
.gitea/PULL_REQUEST_TEMPLATE.md
Normal file
58
.gitea/PULL_REQUEST_TEMPLATE.md
Normal file
@ -0,0 +1,58 @@
|
||||
> 开工时标题使用 `WIP: <任务名称>` 并先填写“任务占用与边界”。Codex 完成独立合并前自审后补齐其余内容并移除 `WIP:`。
|
||||
|
||||
## 任务占用与边界
|
||||
|
||||
- 任务名称:
|
||||
- 实际开发者:
|
||||
- 任务来源/团队分工:
|
||||
- 目标结果:
|
||||
- 预计修改路径:
|
||||
- 预计页面或路由:
|
||||
- 预计 Controller / Repository:
|
||||
- 数据源、表/视图和字段:
|
||||
- 上游 `source_key` / `scope`:
|
||||
- 权限名、菜单键或共享组件:
|
||||
- 明确不修改的范围:
|
||||
|
||||
## 开工碰撞检查
|
||||
|
||||
- [ ] Codex 已执行 `scripts/codex-task-preflight.ps1`
|
||||
- [ ] 已检查最新 `main` 和所有远程任务分支
|
||||
- [ ] 已搜索主线既有页面、接口、Repository 和数据查询
|
||||
- [ ] 未发现重叠;或已在下方写明相关分支和协调结论
|
||||
|
||||
相关分支、重叠项与协调结论:
|
||||
|
||||
## 实际改动
|
||||
|
||||
- 完成内容:
|
||||
- Codex 实际修改的文件:
|
||||
- 新增或变更的公共契约:
|
||||
- 与原计划的差异:
|
||||
|
||||
## 验证证据
|
||||
|
||||
- 正常流程:
|
||||
- 空数据/错误输入/无权限场景:
|
||||
- 受影响旧功能:
|
||||
- 自动测试及结果:
|
||||
- 实际页面、接口或只读业务查询结果:
|
||||
|
||||
## 风险检查
|
||||
|
||||
- [ ] 不涉及金额、权限、审批、数据库迁移、生产配置或数据修复
|
||||
- [ ] 如涉及上述高风险内容,已在下方写明影响、回滚和所需授权
|
||||
- [ ] 未提交 `.env`、密码、Token、私钥、真实财务数据、生产日志或构建产物
|
||||
- [ ] 前端改动符合 `DESIGN.md`;无前端改动则写“不适用”
|
||||
|
||||
高风险影响、回滚和授权状态:
|
||||
|
||||
## 合并前复查
|
||||
|
||||
- [ ] 已重新拉取远程引用并用实际修改范围再次运行碰撞检查
|
||||
- [ ] PR 不再是 `WIP:`,所有测试结果已经补齐
|
||||
- [ ] Codex 已重新读取原始需求和验收标准,并逐文件审核 `origin/main...任务分支` 的完整 diff
|
||||
- [ ] Codex 自审确认无越界修改、重复实现、敏感内容、未处理冲突和未验证结果
|
||||
- [ ] 合并顺序不会覆盖其他未合并任务
|
||||
- [ ] 已授权当前任务的 Codex 执行 Squash merge、验证 `main` 并清理任务分支
|
||||
- [ ] 如涉及生产迁移、数据修复、删除、覆盖配置或部署,已另行取得对应高风险授权
|
||||
42
AGENTS.md
42
AGENTS.md
@ -20,7 +20,7 @@
|
||||
|
||||
## 2. 分支与提交
|
||||
|
||||
- `main` 是稳定主线,默认禁止直接推送、禁止强推、禁止直接修改。项目负责人 `WuYongJie`,以及得到其明确授权的 Codex,可以直接合并 PR 或直接推送 `main`;任何身份仍禁止强推。
|
||||
- `main` 是稳定主线,禁止直接在其上开发、禁止强推。团队成员的任务必须先经过独立分支和 PR;任务 Codex 完成独立合并前自审及实际验证后,即被授权执行 Squash merge、推送 `main`、验证合并结果和清理任务分支,不需要等待其他开发者或项目负责人操作。
|
||||
- 每次开工必须先核对当前目录、远程仓库、Git 身份和当前分支:项目目录必须是 `CaiWuXiTong`,`origin` 必须指向 `WuYongJie/CaiWuXiTong`,分支用户名必须属于当前任务的实际开发者。发现其他项目名、其他项目开发者名或无法解释的旧分支时立即停止,不得继续提交。
|
||||
- 每项任务都必须从最新 `main` 新建分支;不得长期共用或复用旧任务分支。
|
||||
- 分支命名:新功能用 `feature/用户名-任务名`,修复用 `fix/用户名-问题名`,紧急修复用 `hotfix/用户名-问题名`。
|
||||
@ -30,17 +30,28 @@
|
||||
- 提交前必须查看 `git status` 和 `git diff`,确认没有把别人的改动、日志、构建产物、`.env` 或敏感文件带进去。
|
||||
- 禁止使用 `git push --force`、`git reset --hard`、`git clean -fd`,除非负责人明确给出目标和操作步骤。
|
||||
|
||||
### 2.1 多人任务可见性与碰撞检查
|
||||
|
||||
- 团队按既有分工自行认领任务。每位开发者把已确定的任务交给自己的 Codex 后,Codex 必须先划定预计修改路径,以及路由、Controller、Repository、数据表/视图、上游 `source_key`、`scope`、权限名、菜单键等公共契约;范围不明确时不得开始修改。
|
||||
- 开工前由 Codex 执行 `scripts/codex-task-preflight.ps1`,检查最新 `main`、所有远程任务分支和主线既有实现。检查参数必须包含任务名、实际开发者、预计修改路径和精确公共契约关键词;不得用“页面”“接口”“数据库”等宽泛词代替。
|
||||
- 预检通过后立即创建并推送独立任务分支,并在 Gitea 创建标题以 `WIP:` 开头的 PR。WIP PR 必须在写大量代码前公开任务目标、模块边界和公共契约,不能等功能完成后才首次推送。
|
||||
- 任何 Codex 发现相同页面、路由、Controller、Repository、表/视图、`source_key`、`scope`、权限名、菜单键或公共组件正在另一远程分支开发时,必须停止写入并把重叠项报告给相关开发者;不得自行再造另一套实现,也不得通过覆盖或回退对方代码解决。
|
||||
- 远程不可见的本机改动无法被其他 Codex 检查。任务分支和 WIP PR 不及时推送造成的重复开发,不能以“Codex 没发现”为理由继续合并。
|
||||
- 并行 Codex/Subagent 优先用于只读探索、测试、日志分析和代码审查;多个代理不得同时写同一批文件。需要并行写入时必须使用互不重叠的 worktree 和文件边界,由主任务代理统一复核。
|
||||
- 普通代码重叠和同步方案由相关分支提交人及其 Codex 处理。只有产品方向、重大业务规则或生产高风险操作需要升级确认。
|
||||
|
||||
## 3. 新人使用 AI 的固定流程
|
||||
|
||||
新人本地接入的唯一手工动作是启动 Docker Desktop、在网页中登录,以及输入一次负责人提供的系统数据库密码。拉取代码、构建、初始化、本机引导库迁移、缓存清理、服务启动、页面打开、系统数据库切换后的两套上游验证和数据字典同步均由 Codex 完成;不得把命令、迁移、连接参数、TLS、证书、`APP_KEY` 或上游配置交给新人处理。
|
||||
|
||||
1. 先确认任务目标、验收方式、影响范围和不能触碰的模块。
|
||||
1. 先确认任务目标和验收方式,由 Codex 根据现有代码、远程分支和团队既有分工划定影响范围与不能触碰的模块。
|
||||
2. 进入正确项目和分支,先执行 `git status`;看到陌生改动,先停止并说明情况。
|
||||
3. 向 AI 提供:任务目标、当前分支、报错或现象、相关文件、不可修改的范围。
|
||||
4. 要求 AI 先说明改动计划、涉及文件、风险和验证步骤;确认后再修改。
|
||||
5. 修改后查看 diff,并要求 AI 用普通语言解释“改了什么、为什么、怎样测试”。
|
||||
6. 在本地或测试环境完成最小有效验证,记录实际结果。
|
||||
7. 推送任务分支并发起 PR;新人不得自行绕过 PR 合并到 `main`。项目负责人和其明确授权的 Codex 按上面的例外执行。
|
||||
3. 向 AI 提供:任务目标、当前分支、报错或现象和验收结果;技术范围由 AI 检查后提出,不能反向要求新人猜文件、命令或数据链路。
|
||||
4. 要求 AI 运行任务预检,说明改动计划、涉及文件、公共契约、冲突风险和验证步骤;预检阻断时先由相关开发者协调,不能继续修改。
|
||||
5. 预检通过后立即推送任务分支并创建 WIP PR,再进入正式开发。
|
||||
6. 修改后查看 diff,并要求 AI 用普通语言解释“改了什么、为什么、怎样测试”。
|
||||
7. 在本地或测试环境完成最小有效验证,记录实际结果。
|
||||
8. 更新 WIP PR 后,由当前任务的 Codex 切换到“合并前自审”阶段,重新从最新 `main` 审核完整 diff、公共契约、冲突、测试和风险;全部通过后安全合并到 `main`、验证主线并清理分支。新人不手工执行合并命令,也不得绕过 PR。
|
||||
|
||||
推荐向 AI 提问的模板:
|
||||
|
||||
@ -49,23 +60,24 @@
|
||||
任务:<要实现的功能或要修复的问题>。
|
||||
当前分支:<分支名>。
|
||||
现场:<报错、复现步骤、相关文件>。
|
||||
边界:不要修改 <模块/表/配置>;不要执行删除、部署、生产数据库操作。
|
||||
要求:先列出改动计划、风险和验证步骤,等我确认后再改。
|
||||
验收:<页面、接口或业务结果>。
|
||||
要求:先检查最新 main、全部远程任务分支和现有实现,运行 scripts/codex-task-preflight.ps1,自行划定技术边界;有重叠就停止并报告。预检通过后立即推送任务分支并创建 WIP PR,再开始修改。不要执行删除、部署或生产数据库操作。
|
||||
```
|
||||
|
||||
## 4. PR 与合并
|
||||
|
||||
- 新人和普通协作者的任务分支必须通过 PR 合并到 `main`;项目负责人和其明确授权的 Codex 可以作为例外直接合并或推送。
|
||||
- 所有成员的任务分支必须通过 PR 合并到 `main`。PR 满足检查和验证条件后,任务开发者的 Codex 可以直接执行 Squash merge,不需要项目负责人代为合并;禁止绕过 PR 把开发提交直接推入 `main`。
|
||||
- PR 作者必须填写:做了什么、Codex 改了哪些文件、怎样测试、是否涉及金额/权限/数据库/配置。
|
||||
- 新人提交的 PR 至少由另一位成员检查后才能批准;作者不得把“AI 已完成”当作测试结果。项目负责人和其明确授权的 Codex 不受这条审核人数限制,但仍必须完成实际验证并如实记录结果。
|
||||
- 涉及金额计算、权限、导入、数据库迁移、账户或系统配置时,必须在 PR 中明确标注,并由负责人最终确认。
|
||||
- 使用仓库默认的 Squash merge(压缩合并)。合并由负责人或指定合并人执行。
|
||||
- PR 合并后分支自动删除;不要把已合并分支继续用于新任务。
|
||||
- WIP PR 是任务占用和公共契约的远程可见记录。标题使用 `WIP: <任务>`;描述必须使用仓库的 `.gitea/PULL_REQUEST_TEMPLATE.md`,其中预计范围在开工时填写,实际范围和测试结果在 Codex 合并前自审时补齐。
|
||||
- PR 不要求另一位开发者审核。任务 Codex 必须在实现结束后另做一次合并前自审:重新读取需求和验收标准,以 `origin/main...任务分支` 的完整 diff 为准逐文件检查,不得直接沿用开发阶段的“已经完成”结论。
|
||||
- 涉及金额计算、权限、导入、数据库迁移、账户或系统配置时,必须在 PR 中明确标注影响与回滚;代码仍可在检查和测试通过后由 Codex 合并,但生产迁移、数据修复、删除、覆盖配置和部署继续执行独立高风险授权。
|
||||
- 使用仓库默认的 Squash merge(压缩合并)。执行合并的 Codex 必须先重新拉取远程引用,确认工作区干净、PR 不再是 WIP、需求和验收逐项满足、完整 diff 无越界或敏感内容、测试通过、目标为最新 `main` 且没有未协调重叠,再执行合并。
|
||||
- 合并后 Codex 必须核对远程 `main` 已包含目标改动,报告合并提交和验证结果,并删除已合并的本地、远程任务分支;下一项任务必须重新从最新 `main` 创建分支。
|
||||
|
||||
## 5. 测试与问题处理
|
||||
|
||||
- 修改完成不等于可用。至少验证正常流程、错误输入/无权限场景和本次改动影响的旧功能。
|
||||
- 出现冲突、测试失败或 PR 显示落后 `main` 时,不要自行猜命令。先让 AI 只分析同步方案,禁止强推和 rebase;必要时找负责人处理。
|
||||
- 出现冲突、测试失败或 PR 显示落后 `main` 时,不要自行猜命令。先让 AI 只分析同步方案,禁止强推和 rebase;普通代码冲突交由相关分支提交人及其 Codex 处理,未解决前不得合并。
|
||||
- 提交问题时必须写清:环境、账号角色、复现步骤、实际结果、期望结果和报错截图/日志(脱敏后)。
|
||||
|
||||
## 6. 数据与安全红线
|
||||
|
||||
@ -1,92 +1,137 @@
|
||||
# 新人团队协作 SOP
|
||||
|
||||
这份说明适用于 DoDo、sumu、shanshi、wangshu、chenxi、renshen 等使用 Codex 或其他 AI 编程工具参与本项目的成员。
|
||||
这份说明适用于 DoDo、sumu、shanshi、wangshu、chenxi、renshen 等使用 Codex 参与本项目的成员。团队按照已经确定的业务分工自行协作;开发者只需要把自己的任务目标、现场和验收结果告诉 Codex,仓库检查、技术范围、分支、命令、测试和 PR 由 Codex 完成。
|
||||
|
||||
## 一条必须遵守的规则
|
||||
|
||||
任务不能只留在个人电脑里。Codex 完成开工检查后,必须立即推送任务分支并创建 `WIP:` PR,先让其他开发者和 Codex 看见任务范围,再开始大量写代码。
|
||||
|
||||
没有推送的本机代码,其他人的 Codex 无法发现。等功能写完才推送,是重复页面、重复接口和重复数据库逻辑的主要来源。
|
||||
|
||||
## 开始一项任务
|
||||
|
||||
先确认自己正在正确的项目、仓库和身份下工作:
|
||||
开发者把下面这段交给自己的 Codex:
|
||||
|
||||
```text
|
||||
项目:CaiWuXiTong。
|
||||
任务:<已经分工给我的功能或修复>。
|
||||
现场:<报错、截图、现有页面或业务说明>。
|
||||
验收:<最终页面、接口或业务结果>。
|
||||
|
||||
请先核对仓库、Git 身份、最新 main 和全部远程任务分支,搜索现有页面、路由、Controller、Repository、表/视图、source_key、scope、权限名、菜单键和共享组件。自行列出预计修改路径与精确公共契约关键词,并运行 scripts/codex-task-preflight.ps1。
|
||||
|
||||
发现其他远程分支正在处理相同功能或公共契约时立即停止,只报告重叠项,不能继续写代码或另造一套实现。预检通过后,从最新 main 创建本人独立任务分支,立即推送并创建 WIP PR,填写任务范围,再开始修改。
|
||||
```
|
||||
|
||||
Codex 必须先核对:
|
||||
|
||||
```bash
|
||||
git rev-parse --show-toplevel
|
||||
git status --short --branch
|
||||
git remote get-url origin
|
||||
git config user.name
|
||||
git fetch --prune origin
|
||||
```
|
||||
|
||||
检查结果必须满足:
|
||||
|
||||
- 当前项目是 `CaiWuXiTong`。
|
||||
- `origin` 指向 `WuYongJie/CaiWuXiTong`。
|
||||
- 当前分支没有使用其他项目或其他开发者的名字。
|
||||
- 现有分支确实属于本人尚未完成的当前任务;已合并分支不得复用。
|
||||
- 当前项目是 `CaiWuXiTong`;
|
||||
- `origin` 指向 `WuYongJie/CaiWuXiTong`;
|
||||
- Git 身份和分支用户名属于当前实际开发者;
|
||||
- 没有无法解释的受控文件改动;
|
||||
- 当前任务没有复用已合并旧分支;
|
||||
- 已列出具体预计路径和公共契约,不能只写“财务页面”“数据库接口”等宽泛范围。
|
||||
|
||||
任何一项不符合时先停止,把上述输出发给负责人或 Codex 核对。确认无误后,每次都从最新主线创建新任务分支:
|
||||
## Codex 开工碰撞检查
|
||||
|
||||
由 Codex 执行,开发者不手工输入命令:
|
||||
|
||||
```powershell
|
||||
.\scripts\codex-task-preflight.ps1 `
|
||||
-Task '<任务名称>' `
|
||||
-Developer '<实际开发者用户名>' `
|
||||
-PlannedPath @('resources/views/目标模块', 'modules/EcommerceFinance/Repositories/目标文件.php') `
|
||||
-Contract @('具体路由片段', 'Repository类名', 'source.scope', '真实表或视图名')
|
||||
```
|
||||
|
||||
脚本会:
|
||||
|
||||
1. 拉取最新远程引用;
|
||||
2. 检查当前仓库、远程地址、Git 身份和工作区;
|
||||
3. 检查所有尚未包含于 `main` 的远程任务分支;
|
||||
4. 比较预计路径是否重叠;
|
||||
5. 在远程 diff 中检查路由、Repository、表/视图、`scope` 等公共契约;
|
||||
6. 搜索 `main` 是否已经存在同名功能;
|
||||
7. 给出“可以开始”或“停止并协调”的明确结果。
|
||||
|
||||
脚本返回阻断时,Codex 不得修改代码。相关开发者根据报告中的分支、文件和公共契约自行确认保留哪套实现,确认后重新运行预检。
|
||||
|
||||
## 立即建立远程可见任务
|
||||
|
||||
预检通过后,Codex 从最新 `main` 创建新分支:
|
||||
|
||||
```bash
|
||||
git switch main
|
||||
git pull origin main
|
||||
git pull --ff-only origin main
|
||||
git switch -c feature/用户名-任务名
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
git switch -c feature/dodo-login
|
||||
```
|
||||
|
||||
不要直接在 `main` 修改,也不要在上一个已完成任务的分支继续做新功能。分支名中的用户名必须是当前任务实际开发者,不能复制其他项目的分支名。
|
||||
|
||||
## 开发时
|
||||
|
||||
1. 先把任务和边界告诉 Codex,要求先分析后修改。
|
||||
2. 每次修改后检查变更:
|
||||
|
||||
```bash
|
||||
git status
|
||||
git diff
|
||||
```
|
||||
|
||||
3. 做完最小测试:目标页面/接口能否使用,错误输入是否有合理提示。
|
||||
4. 不要提交 `.env`、密码、Token、真实财务数据、日志或构建产物。
|
||||
|
||||
## 提交与推送
|
||||
|
||||
```bash
|
||||
git add <本次任务相关文件>
|
||||
git commit -m "feat: 简短说明本次功能"
|
||||
git push -u origin feature/用户名-任务名
|
||||
```
|
||||
|
||||
提交信息只描述本次任务,例如:
|
||||
修复使用 `fix/用户名-问题名`,紧急修复使用 `hotfix/用户名-问题名`。一个分支只做一个任务,已合并分支不得复用。
|
||||
|
||||
随后在 Gitea 立即创建 PR:
|
||||
|
||||
- 标题:`WIP: <任务名称>`;
|
||||
- 目标分支:`main`;
|
||||
- 描述:完整填写 `.gitea/PULL_REQUEST_TEMPLATE.md` 中的“开工范围”;
|
||||
- 尚未完成的测试明确写“待完成”,不能伪造结果。
|
||||
|
||||
WIP PR 用于公开任务占用,不代表功能已经完成,也不能被合并。
|
||||
|
||||
## 开发过程
|
||||
|
||||
1. 页面先复用现有共享组件和业务 Repository;
|
||||
2. Controller 和页面不得直接连接数据库;
|
||||
3. 新上游查询必须同时具备 Repository、稳定 `scope`、缓存调用、刷新器映射和测试;
|
||||
4. 公共路由、权限、侧边栏、数据库配置、共享组件和部署脚本有改动时,必须在 WIP PR 中单独列出;
|
||||
5. 多个 Codex/Subagent 可以并行做只读排查、测试和审查,但不能同时写同一批文件;
|
||||
6. 每次准备扩大修改范围时,先更新 WIP PR,再用新增路径和契约重新运行预检;
|
||||
7. 修改后由 Codex 检查 `git status`、`git diff`,不得提交 `.env`、密码、Token、真实财务数据、日志或构建产物。
|
||||
|
||||
## 申请合并
|
||||
|
||||
Codex 在合并前必须进入一次独立自审,不得直接沿用开发阶段的完成结论:
|
||||
|
||||
1. 再次 `git fetch --prune origin`;
|
||||
2. 使用实际修改路径和公共契约重新运行预检;
|
||||
3. 对比最新 `main`,确认没有新的重叠任务;
|
||||
4. 完成目标页面/接口、错误输入和受影响旧功能的最小验证;
|
||||
5. 更新 PR 中的实际文件、公共契约、测试证据和风险;
|
||||
6. 重新读取原始需求和验收标准,逐文件审核 `origin/main...任务分支` 的完整 diff;
|
||||
7. 确认没有越界修改、重复实现、敏感内容、未处理冲突和未验证结果;
|
||||
8. 自审通过后将标题从 `WIP:` 改成正式标题。
|
||||
|
||||
开发者把下面这段交给自己的 Codex:
|
||||
|
||||
```text
|
||||
feat: 增加店铺列表筛选
|
||||
fix: 修正订单金额汇总
|
||||
请进入独立的合并前自审,不要直接沿用开发阶段的完成结论。重新 fetch、读取原始需求和验收标准,并以 origin/main...当前任务分支的完整 diff 为准逐文件审核。检查工作区、WIP 状态、修改边界、公共契约、远程支线重叠、敏感内容、测试证据和目标 main。条件全部满足后,使用 Squash merge 合并到 main,核对远程 main 已包含目标改动,再验证实际页面或接口并删除已合并任务分支。禁止强推;如有冲突、测试失败、自审不通过或涉及尚未授权的生产迁移/数据操作,立即停止并报告。
|
||||
```
|
||||
|
||||
不要使用 `update`、`test`、`修改一下` 这类无法理解的提交说明。
|
||||
日常 PR 自审、合并、主线验证和分支清理由任务开发者的 Codex 完成,不需要另一位开发者或项目负责人参与。开发者不手工执行 Git 合并命令,也不能绕过 PR。涉及生产迁移、数据修复、删除、覆盖配置或部署时,继续执行仓库现有高风险授权规则。
|
||||
|
||||
## 发起 PR
|
||||
## PR 落后或发生冲突
|
||||
|
||||
在 Gitea 中发起从任务分支到 `main` 的 PR,并填写:
|
||||
开发者把下面这段交给 Codex:
|
||||
|
||||
```text
|
||||
做了什么:
|
||||
Codex 改了哪些文件:
|
||||
我怎么测试的:
|
||||
是否涉及金额、权限、数据库或配置:
|
||||
当前任务分支的 PR 落后 main 或发生冲突。请先 fetch,并只读分析最新 main、当前分支和其他活跃远程分支的差异。禁止 force push 和 rebase。请列出冲突文件、双方业务逻辑、推荐保留方案和测试范围。普通代码冲突由相关分支提交人及其 Codex 处理;没有完成协调前不得修改、推送或合并。
|
||||
```
|
||||
|
||||
另一位成员检查后批准。负责人使用 Squash merge 合并;合并后删除本地和远程任务分支,下一项任务重新从最新 `main` 创建新分支。
|
||||
不能用删除对方代码、恢复旧版本、复制另一项目实现或扩大公共权限的方式消除冲突。
|
||||
|
||||
## PR 显示“落后 main”或发生冲突
|
||||
## 任务完成
|
||||
|
||||
不要使用强推,也不要自己猜 rebase 命令。把下面这段交给 Codex:
|
||||
|
||||
```text
|
||||
我在 <当前任务分支>,PR 显示已落后 main/发生冲突。
|
||||
请先只分析如何安全同步,禁止 force push 和 rebase。
|
||||
请给出最小操作步骤,并说明每一步的作用和可能风险。
|
||||
```
|
||||
|
||||
若仍不确定,停止操作并联系负责人。
|
||||
- PR 合并后删除本地和远程任务分支;
|
||||
- 下一项任务重新从最新 `main` 开始;
|
||||
- WIP PR、远程分支和实际代码是团队 Codex 判断任务占用的依据;
|
||||
- 产品方向或重大业务规则无法由现有需求判断时,再提交清晰的选项、影响和证据进行确认。
|
||||
|
||||
@ -2,12 +2,24 @@
|
||||
|
||||
## 1. 这套流程解决什么问题
|
||||
|
||||
本项目由 6 位新人使用 Codex 协作开发。项目负责人只确认方向、验收结果和高风险动作,不负责日常命令操作。
|
||||
本项目由 6 位新人使用 Codex 协作开发。团队按照已经确定的业务分工自行认领和协调任务;Codex 负责仓库检查、技术范围、分支、实现、测试和 PR,日常命令不能转交给新人。产品方向、验收结果和高风险操作仍按仓库规则确认。
|
||||
|
||||
开发协作固定链路如下,详细要求见 `docs/team-collaboration.md`:
|
||||
|
||||
```text
|
||||
团队既有分工 -> Codex 检查 main、远程支线和既有实现
|
||||
-> 运行 scripts/codex-task-preflight.ps1
|
||||
-> 立即推送独立任务分支并创建 WIP PR
|
||||
-> 开发、测试、再次碰撞检查
|
||||
-> 任务 Codex 独立自审 -> 合并、验证 main 并清理分支
|
||||
```
|
||||
|
||||
任务没有远程分支和 WIP PR 时,其他人的 Codex 无法看见,不得开始大量开发。发现相同页面、路由、Repository、表/视图、上游 `scope`、权限名、菜单键或公共组件时,相关 Codex 必须停止写入并报告重叠项,由相关开发者先完成协调。
|
||||
|
||||
固定链路如下:
|
||||
|
||||
```text
|
||||
任务分支开发 -> 本机验证 -> PR -> 合并 main
|
||||
任务分支开发 -> 本机验证 -> PR -> 任务 Codex 独立自审 -> 合并并验证 main
|
||||
-> 开发机复用缓存构建镜像
|
||||
-> 经 SSH 隧道只上传变化的镜像层
|
||||
-> 2GB 服务器只拉取本机 Registry 并切换镜像
|
||||
|
||||
205
scripts/codex-task-preflight.ps1
Normal file
205
scripts/codex-task-preflight.ps1
Normal file
@ -0,0 +1,205 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$Task,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidatePattern('^[A-Za-z0-9_-]+$')]
|
||||
[string]$Developer,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string[]]$PlannedPath,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string[]]$Contract,
|
||||
|
||||
[string]$Remote = 'origin',
|
||||
[string]$BaseRef = 'origin/main'
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Invoke-Git {
|
||||
param([Parameter(ValueFromRemainingArguments = $true)][string[]]$Arguments)
|
||||
|
||||
$output = & git @Arguments 2>&1
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Git 命令执行失败:git $($Arguments -join ' ')`n$($output -join "`n")"
|
||||
}
|
||||
|
||||
return @($output)
|
||||
}
|
||||
|
||||
function Normalize-RepoPath {
|
||||
param([string]$Path)
|
||||
|
||||
return ($Path.Trim() -replace '\\', '/').TrimStart('.', '/').TrimEnd('/')
|
||||
}
|
||||
|
||||
function Test-PathOverlap {
|
||||
param(
|
||||
[string]$Planned,
|
||||
[string]$Changed
|
||||
)
|
||||
|
||||
return $Planned -eq $Changed `
|
||||
-or $Changed.StartsWith($Planned + '/', [StringComparison]::OrdinalIgnoreCase) `
|
||||
-or $Planned.StartsWith($Changed + '/', [StringComparison]::OrdinalIgnoreCase)
|
||||
}
|
||||
|
||||
$repoRoot = (Invoke-Git rev-parse --show-toplevel | Select-Object -First 1).Trim()
|
||||
$repoName = Split-Path -Leaf $repoRoot
|
||||
$originUrl = (Invoke-Git remote get-url $Remote | Select-Object -First 1).Trim()
|
||||
$gitUser = (Invoke-Git config user.name | Select-Object -First 1).Trim()
|
||||
|
||||
if ($repoName -ne 'CaiWuXiTong') {
|
||||
throw "当前目录不是 CaiWuXiTong:$repoRoot"
|
||||
}
|
||||
|
||||
if ($originUrl -notmatch 'WuYongJie/CaiWuXiTong(?:\.git)?$') {
|
||||
throw "远程仓库不是 WuYongJie/CaiWuXiTong:$originUrl"
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($gitUser)) {
|
||||
throw 'Git user.name 未配置。'
|
||||
}
|
||||
|
||||
$plannedPaths = @($PlannedPath | ForEach-Object { Normalize-RepoPath $_ } | Where-Object { $_ } | Select-Object -Unique)
|
||||
$contracts = @($Contract | ForEach-Object { $_.Trim() } | Where-Object { $_ } | Select-Object -Unique)
|
||||
|
||||
if ($plannedPaths.Count -eq 0 -or $contracts.Count -eq 0) {
|
||||
throw '必须同时提供预计修改路径和公共契约关键词,不能在范围不明确时开始开发。'
|
||||
}
|
||||
|
||||
Invoke-Git fetch --prune $Remote | Out-Null
|
||||
Invoke-Git rev-parse --verify $BaseRef | Out-Null
|
||||
|
||||
$status = @(Invoke-Git status --porcelain=v1)
|
||||
$trackedChanges = @($status | Where-Object { $_ -and -not $_.StartsWith('?? ') })
|
||||
$untrackedChanges = @($status | Where-Object { $_ -and $_.StartsWith('?? ') })
|
||||
|
||||
if ($trackedChanges.Count -gt 0) {
|
||||
Write-Host '阻断:工作区已有受控文件改动,不能开始新任务。' -ForegroundColor Red
|
||||
$trackedChanges | ForEach-Object { Write-Host " $_" }
|
||||
exit 2
|
||||
}
|
||||
|
||||
$currentBranch = (Invoke-Git branch --show-current | Select-Object -First 1).Trim()
|
||||
$remoteBranches = @(
|
||||
Invoke-Git for-each-ref '--format=%(refname:short)' "refs/remotes/$Remote" |
|
||||
Where-Object {
|
||||
$_ -and
|
||||
$_ -ne $Remote -and
|
||||
$_ -ne "$Remote/HEAD" -and
|
||||
$_ -ne $BaseRef -and
|
||||
$_ -ne "$Remote/$currentBranch"
|
||||
}
|
||||
)
|
||||
|
||||
$branchFindings = @()
|
||||
|
||||
foreach ($branch in $remoteBranches) {
|
||||
$counts = ((Invoke-Git rev-list --left-right --count "$BaseRef...$branch") | Select-Object -First 1).Trim() -split '\s+'
|
||||
$branchOnly = [int]$counts[1]
|
||||
|
||||
if ($branchOnly -eq 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
& git merge-base --is-ancestor $branch $BaseRef
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
$changedFiles = @(Invoke-Git diff --name-only "$BaseRef...$branch" | ForEach-Object { Normalize-RepoPath $_ })
|
||||
$pathHits = @()
|
||||
|
||||
foreach ($planned in $plannedPaths) {
|
||||
foreach ($changed in $changedFiles) {
|
||||
if (Test-PathOverlap -Planned $planned -Changed $changed) {
|
||||
$pathHits += $changed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$subject = (Invoke-Git log -1 '--format=%s' $branch | Select-Object -First 1).Trim()
|
||||
$diffText = (Invoke-Git diff --unified=0 "$BaseRef...$branch") -join "`n"
|
||||
$contractHits = @(
|
||||
$contracts | Where-Object {
|
||||
$branch -match [Regex]::Escape($_) `
|
||||
-or $subject -match [Regex]::Escape($_) `
|
||||
-or $diffText -match [Regex]::Escape($_)
|
||||
}
|
||||
)
|
||||
|
||||
if ($pathHits.Count -gt 0 -or $contractHits.Count -gt 0) {
|
||||
$branchFindings += [pscustomobject]@{
|
||||
Branch = $branch
|
||||
Subject = $subject
|
||||
PathHits = @($pathHits | Select-Object -Unique)
|
||||
ContractHits = @($contractHits | Select-Object -Unique)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$existingFindings = @()
|
||||
$searchRoots = @('app', 'config', 'database', 'modules', 'resources', 'routes', 'tests')
|
||||
|
||||
foreach ($keyword in $contracts) {
|
||||
$matches = @(& git grep -n -I -F -e $keyword $BaseRef -- @searchRoots 2>$null)
|
||||
|
||||
if ($LASTEXITCODE -notin @(0, 1)) {
|
||||
throw "检查主线公共契约失败:$keyword"
|
||||
}
|
||||
|
||||
if ($matches.Count -gt 0) {
|
||||
$existingFindings += [pscustomobject]@{
|
||||
Keyword = $keyword
|
||||
Matches = @($matches | Select-Object -First 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Codex 任务开工检查:$Task" -ForegroundColor Cyan
|
||||
Write-Host "开发者:$Developer;Git 身份:$gitUser;基线:$BaseRef"
|
||||
Write-Host "预计路径:$($plannedPaths -join ', ')"
|
||||
Write-Host "公共契约:$($contracts -join ', ')"
|
||||
|
||||
if ($untrackedChanges.Count -gt 0) {
|
||||
Write-Host '提醒:存在未跟踪文件,Codex 必须确认归属且不得顺带提交:' -ForegroundColor Yellow
|
||||
$untrackedChanges | ForEach-Object { Write-Host " $_" }
|
||||
}
|
||||
|
||||
if ($existingFindings.Count -gt 0) {
|
||||
Write-Host '主线已存在相同关键词,请先复用或说明差异:' -ForegroundColor Yellow
|
||||
|
||||
foreach ($finding in $existingFindings) {
|
||||
Write-Host " [$($finding.Keyword)]"
|
||||
$finding.Matches | ForEach-Object { Write-Host " $_" }
|
||||
}
|
||||
}
|
||||
|
||||
if ($branchFindings.Count -gt 0) {
|
||||
Write-Host '阻断:以下远程任务分支与当前计划重叠。停止修改,由相关开发者先完成协调:' -ForegroundColor Red
|
||||
|
||||
foreach ($finding in $branchFindings) {
|
||||
Write-Host " $($finding.Branch) — $($finding.Subject)"
|
||||
|
||||
if ($finding.PathHits.Count -gt 0) {
|
||||
Write-Host " 重叠路径:$($finding.PathHits -join ', ')"
|
||||
}
|
||||
|
||||
if ($finding.ContractHits.Count -gt 0) {
|
||||
Write-Host " 重叠契约:$($finding.ContractHits -join ', ')"
|
||||
}
|
||||
}
|
||||
|
||||
exit 2
|
||||
}
|
||||
|
||||
Write-Host '通过:未发现远程任务分支与当前计划重叠。可以创建或继续独立任务分支。' -ForegroundColor Green
|
||||
exit 0
|
||||
Loading…
x
Reference in New Issue
Block a user