返回博客列表
工程效率2026年8月27日13 分钟阅读

我为禅道写了一个 Codex Skill:把任务单变成可审计的代码提交

我尝试把禅道任务交给 Codex 自动实现,但真正困难的不是调用模型,而是建立项目匹配、Git 隔离、测试构建、独立审查和人工兜底组成的安全边界。

#Codex#AI Agent#禅道

团队每天都在重复一条熟悉的链路:打开禅道、阅读任务、找到项目、创建分支、修改代码、运行测试、提交代码,再回到禅道更新处理结果。

当 Codex 已经能够理解代码库并完成不少开发工作时,一个很自然的问题是:能不能让它直接从禅道领取任务?

我最开始以为,这件事只是“读取任务描述,然后调用一次 Codex”。真正动手后才发现,模型调用反而是最简单的一环。真正困难的是如何回答下面这些问题:

  • 这条任务究竟属于哪个代码仓库?

  • AI 修改代码时,如何不污染开发者当前工作区?

  • Worker 说自己完成了,是否真的完成了?

  • 测试通过是否代表改动安全?

  • 什么情况下允许提交、推送或修改禅道状态?

  • 执行中断后,如何知道任务进行到了哪一步?

因此,我写的最终不只是一个禅道 Skill,而是一套由 Skill、编排器、验证器、Reviewer 和状态机 共同组成的 Zentao Codex Agent。

我希望它解决什么问题

这个 Agent 的目标不是代替开发者做所有判断,也不是看到任务就自动上线。它更像一个有严格权限边界的初级开发者:领取明确授权的工作项,在隔离环境中实现需求,完成检查并留下完整记录;遇到模糊或危险的情况时,主动停下来交给人处理。

完整流程如下:

禅道任务 / Bug
    ↓
任务筛选与项目解析
    ↓
创建独立 Git worktree
    ↓
Codex Worker 实现需求
    ↓
测试、构建、Diff 与安全检查
    ↓
独立 Codex Reviewer 审查
    ↓
Fixer 有限次数修复并重新验证
    ↓
本地 Commit
    ↓
可选 Push / 可选禅道完成

这里最重要的设计原则是:任何一步没有明确通过,后续的高风险动作就不会发生。

Skill 负责约束“怎么写代码”

项目中的 zentao-worker Skill 很短,但它承担了最关键的行为约束。它要求 Codex 只实现当前禅道事项,并把标题、描述和验收标准视为范围边界。

在修改代码前,Worker 必须先检查:

  • 项目结构与相关模块;

  • 类似功能的现有实现;

  • 必要的调用关系和数据结构;

  • 相关测试与项目自身的开发规范。

在完成修改后,它还必须检查:

git status --short
git diff HEAD
git diff HEAD --check

这里使用 git diff HEAD,是为了同时覆盖 staged 和 unstaged 变更。Skill 还明确禁止 Worker 自己提交、推送、回写禅道、修改生产配置,或者通过删除和弱化测试来掩盖失败。

Skill 解决的是“模型应该如何工作”,但它不能单独解决执行权限、流程恢复和安全门禁。所以我把真正的外部动作留给 Python 编排层统一控制。

为什么使用 Git worktree

如果让 Agent 直接在开发者正在使用的仓库里修改代码,会产生很多不可控问题:当前分支可能有未提交内容,Agent 可能覆盖本地修改,多条任务之间也可能互相干扰。

因此,每条禅道任务都会拥有独立分支和独立 worktree:

branch:   zentao/task-123
worktree: /tmp/zentao-agent/task-123

Bug 则使用类似 zentao/bug-123 的分支名称。Worker、测试命令和 Reviewer 都只在这个 worktree 中运行。

这带来了几个直接收益:

  1. 不污染主仓库当前工作区;

  2. 每个工作项都有清晰的分支边界;

  3. 失败时可以保留现场,方便人工检查;

  4. Reviewer 看到的 Diff 与最终提交内容一致。

Agent 不会执行 git reset --hard、git clean -fd 或 force push。即使清理 worktree,也会先确认目录没有未提交修改。

项目匹配宁可失败,也不猜测

一套禅道往往对应多个代码仓库。任务描述可能写得很清楚,也可能只出现“挂号”“订单”或“会员”这样的业务词。

我在配置中为每个项目声明禅道项目名称和关键词:

projects:
  saas:
    name: SAAS
    repo: /home/code/saas
    base_branch: develop
    zentao_projects: [SAAS]
    task_keywords: [商户, 订单, 商品, 会员]

解析器会优先使用禅道项目名称进行精确匹配,再使用关键词计算候选结果。只有出现唯一的正向匹配时才继续执行。

如果没有项目匹配,或者多个项目得分相同,Agent 不会“挑一个看起来差不多的仓库”,而是进入 NEEDS_HUMAN_REVIEW。

对于自动修改代码而言,拒绝猜测比提高任务命中率更重要。

Worker、Verifier 和 Reviewer 相互独立

我没有把“写代码”和“判断代码是否正确”交给同一次模型调用。

Worker:完成实现

Worker 根据任务标题、描述、验收标准和项目上下文修改代码,并按固定格式输出:

STATUS: SUCCESS
SUMMARY:
...
FILES_CHANGED:
...
TEST_RECOMMENDATIONS:
...
COMMIT_MESSAGE:
...
ZENTAO_COMMENT:
...

只有明确输出 STATUS: SUCCESS,编排器才会进入下一阶段。自然语言中模糊的“应该完成了”不会被当作成功。

Verifier:执行确定性检查

Verifier 不依赖模型自我评价,而是运行项目配置的测试和构建命令,并检查真实 Git 状态。

除了测试与构建,它还会检查:

  • 是否真的产生了代码变更;

  • 修改文件数量是否超限;

  • Diff 行数是否超限;

  • 是否出现疑似 Token、密码或私钥;

  • 是否修改了 .env、生产配置、CI/CD 或数据库 migration;

  • git diff --check 是否发现空白和格式问题。

默认上限是 30 个文件和 2000 行 Diff。危险文件不是绝对无法修改,但必须在配置中按精确相对路径显式放行。

Reviewer:重新阅读任务并独立审查

通过确定性检查后,另一个独立的 Codex Reviewer 会重新阅读任务与当前 Diff。Reviewer 不修改代码,只检查需求覆盖、异常路径、兼容性、安全、测试充分性、API 和数据库风险。

Reviewer 的输出解析采用严格策略:只有单独且唯一的 APPROVED 才算通过。出现 REJECTED、多个结论或模糊文本,都不会被误判成批准。

如果 Reviewer 拒绝,Fixer 会根据具体原因修复,然后重新进入测试、构建和 Review。重试次数默认最多三次,耗尽后转人工处理,而不是无限循环。

用状态机让自动化过程可恢复、可审计

长时间运行的 Agent 不能只依赖日志判断任务进度。我使用 SQLite 保存任务状态和状态历史,主要状态包括:

FETCHED → PREPARING → CODING → VERIFYING → REVIEWING
        → FIXING → READY_TO_COMMIT → COMMITTED
        → PUSHED → ZENTAO_UPDATED → COMPLETED

异常任务会进入:

FAILED
NEEDS_HUMAN_REVIEW

状态机的价值不只是展示进度。它还负责防止非法跳转、跳过已完成任务,并在进程中断后留下可追踪的执行历史。

已经完成的任务默认不会重复运行。确实需要重跑时,必须显式使用 --force。

禅道版本差异如何处理

禅道不同版本、不同部署方式之间的 API 路径和请求体可能并不完全一致。Task 和 Bug 的字段也有所差异。

我没有把 HTTP 请求散落在编排逻辑中,而是集中封装在 ZentaoClient。API Endpoint、鉴权 Header 和 Scheme 都可以通过 YAML 配置:

zentao:
  base_url: https://zentao.example.com
  auth_header: Authorization
  auth_scheme: Bearer
  work_item_type: task
  endpoints:
    my_tasks: /api.php/v2/tasks
    task_detail: /api.php/v2/tasks/{task_id}
    start_task: /api.php/v2/tasks/{task_id}/start
    finish_task: /api.php/v2/tasks/{task_id}/finish

Task 和 Bug 从 API 返回后,都会被转换成统一的内部模型:标题、描述、验收标准、所属项目、标签和负责人。这样上层流程不需要了解每个禅道版本的字段命名。

Bug 模式还可以读取 keywords 作为标签,并使用 status=assigntome 筛选当前 Token 用户负责的问题。

默认不 Push,也不自动完成禅道任务

自动化最容易犯的错误,是把“技术上能做”理解成“默认就应该做”。

这个 Agent 的默认配置是:

agent:
  auto_commit: true
  auto_push: false
  auto_finish_zentao: false
  process_only_tagged_tasks: true
  required_tag: ai-auto

也就是说,任务必须明确带有 ai-auto 标签,并指派给当前账号才会被处理。通过所有门禁后可以生成本地提交,但默认不会推送远程,也不会自动把禅道事项标记为完成。

我认为这是一个更合理的起点:先让 Agent 证明它能稳定地产出可审查的提交,再逐步开放 Push、PR/MR 或禅道状态变更。

凭据不能进入 Codex 和项目命令

禅道用户名与 Token 只提供给 HTTP 客户端。调用 Codex、Git、测试和构建命令前,Agent 会从子进程环境中移除对应变量。

这样做可以降低两类风险:

  1. 任务代码或构建脚本意外读取禅道凭据;

  2. 模型执行环境把凭据写入日志、代码或测试数据。

Verifier 还会扫描新增 Diff 中常见的 Access Key、私钥头部以及密码、Token、Secret 等赋值模式。正则检查不能替代专业 Secret Scanner,但它是一道成本很低、值得保留的前置门禁。

先 Dry Run,再允许真实执行

首次接入一个禅道环境时,我会先运行:

python main.py --dry-run
python main.py --task 123 --dry-run

Dry Run 只查询禅道、规范化任务并解析项目,不创建 worktree、不调用 Codex、不提交代码,也不回写禅道。

确认任务筛选、负责人、标签、项目与目标仓库都正确后,再执行指定任务:

python main.py --task 123

Agent 既可以单次扫描退出,也可以作为 daemon 常驻轮询。当前 MVP 强制单任务串行执行,max_parallel_tasks 必须为 1。这是有意保留的限制:并行任务会进一步引入仓库锁、资源竞争、重复领取和状态一致性问题,不适合在第一版里草率加入。

测试不是装饰,而是自动化的准入条件

这个项目目前为配置校验、项目解析歧义、Reviewer 输出解析、安全扫描、危险文件、SQLite 状态、Git 集成,以及 Mock 后的禅道和 Codex 客户端编写了自动化测试。

本次整理文章时,我重新执行了完整测试:

16 passed

这些测试不能证明 Agent 已经适配所有禅道版本,也不能证明模型不会写错代码;它们保证的是流程边界不会因为普通重构而悄悄失效。例如,Reviewer 的一句模糊回答不能被误判为批准,项目匹配出现歧义时不能继续执行,危险配置被修改时不能进入提交阶段。

我从这个项目里得到的几个结论

1. Agent 的可靠性主要来自外围系统

Prompt 很重要,但不能承担全部安全职责。权限隔离、状态机、确定性命令、Diff 检查和动作门禁,都应该由普通程序控制。

2. “不知道”必须是一种合法结果

无法确定仓库、没有代码变更、Reviewer 结论模糊、涉及生产配置——这些情况不应该被强行转换成成功或失败。NEEDS_HUMAN_REVIEW 是自动化流程中非常重要的终态。

3. 自动开发不等于自动发布

生成代码、提交代码、推送远端、合并分支、部署生产和关闭禅道任务,是风险完全不同的动作。它们应该逐级授权,而不是打包成一个“全自动”开关。

4. Skill 应该短而明确

Skill 更适合描述稳定的工作原则和禁止事项。具体的 API 路径、项目仓库、测试命令、重试次数与自动化权限则放在配置和编排器中。这样 Skill 可以复用,环境差异也更容易维护。

下一步

当前版本仍然是一个偏保守的单机 MVP。接下来可以继续扩展:

  • 接入 GitHub/GitLab,自动创建 PR 或 MR,而不是直接 Push 后结束;

  • 用 Webhook 替代固定间隔轮询;

  • 读取 CI 结果,只有远端流水线通过后才回写禅道;

  • 增加企业微信、飞书或邮件通知;

  • 为并行任务增加项目级锁和领取机制;

  • 记录模型、Token 消耗、执行耗时和任务成功率;

  • 为不同项目配置不同的 Worker Skill 和审查策略。

我真正想做的,不是让 AI 无人监管地修改更多代码,而是让它在清晰、可回滚、可审计的边界内承担更多重复工作。

当一套 Agent 知道什么时候可以继续、什么时候必须停止时,它才开始具备进入真实研发流程的资格。