Skip to content

feat: add issue code review loop#119

Merged
keting merged 11 commits into
keting:mainfrom
XiaoCooder:main
May 21, 2026
Merged

feat: add issue code review loop#119
keting merged 11 commits into
keting:mainfrom
XiaoCooder:main

Conversation

@XiaoCooder
Copy link
Copy Markdown
Collaborator

@XiaoCooder XiaoCooder commented May 18, 2026

摘要

新增 Issue 编码与双 Agent 评审循环内置流程模板,支持从 issue 初始化、编码推送、双评审、评审决策到 PR 提交的固定 5 Task 协作流程。该改动让项目可以基于协作仓库中的 flow-state.json 和当前轮次评审产物派生业务状态,而不是只依赖数据库中的静态前序任务状态。

同时修复 TASK-005 完成后仍被两份已提交 review 重新派生为 可派发 的问题;现在决策任务已经进入 completedapprovedabandoned 时会保留终态。

Closes #108

相关 Discussion:

范围

  • 本 PR 保持小而聚焦。
  • 本 PR 没有把无关重构、纯格式化改动或翻译工作混入功能改动。
  • 公开 API、数据模型、权限、安全边界和部署行为没有变化;如有变化,已
    在下方说明影响。

影响说明:新增了 issue review loop 相关后端服务、流程模板字段识别、任务业务状态接口响应字段消费,以及前端展示/派发逻辑对该流程业务态的支持;没有新增环境变量。

截图或录屏

迁移 / 配置影响

无新增环境变量或部署步骤。应用启动时会确保内置 Issue 编码与双 Agent 评审循环模板存在,并刷新旧版本内置模板配置。

数据模型影响:为项目默认最大评审轮次和任务结果/时间字段补齐了兼容字段初始化逻辑;现有 SQLite 启动迁移路径会自动补齐缺失列。

验证

  • cd src/backend && uv run python -m pytest tests/ -v
  • cd src/frontend && npm test
  • cd src/frontend && npm run build

检查清单

  • 改动范围清晰且聚焦。
  • 已按需关联相关 Issue。
  • 已按需关联相关 Discussion。
  • 如果行为、API 形状或配置发生变化,已同步更新文档。
  • 没有引入密钥、私有 URL 或个人本机路径。

@XiaoCooder XiaoCooder requested a review from keting as a code owner May 18, 2026 15:00
Copy link
Copy Markdown
Owner

@keting keting left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

【请核对这几个问题】

  1. 已经派发过的任务,还能被再次派发

举个例子:

用户点击了 TASK-003 评审 A 的“复制 Prompt 并派发”,这个任务已经进入 running 了。
但这时候 Agent 还没来得及写回结果,flow-state.json 里它还是 unlocked。

现在系统只看 flow-state.json,看到 TASK-003 = unlocked,就还允许用户再点一次派发。

这会造成同一个评审任务被重复发给 Agent。
正确行为应该是:任务已经 running 了,就不能再普通派发;除非用户明确点“重新派发”。

  1. TASK-005 判断“需要修复”后,又会被系统重新点亮

举个例子:

  • TASK-002 写完代码;
  • TASK-003 和 TASK-004 两个评审都完成;
  • TASK-005 看完评审,决定:“还有问题,先回去修代码”;
  • 它把流程改成:TASK-002 = needs_fix,TASK-005 = frozen。

按理说,下一步用户应该只看到:去派发 TASK-002 修代码。

但现在因为两份旧的 review.json 还在,后端又会把 TASK-005 算成 unlocked。
用户页面上可能同时看到:

  • TASK-002:需修复
  • TASK-005:可派发

这就很 confusing。用户可能会问:“到底是先修代码,还是再跑一次决策?”
如果他又点了 TASK-005,系统就会拿同一份评审结果重复做一次相同决策。

正确行为应该是:进入 needs_fix 后,TASK-005 本轮就结束了,不应该再亮起来。

  1. TASK-005 做完“需要修复”的决策后,会一直显示运行中,最后变成超时

举个例子:

TASK-005 已经完成判断了:这轮不能合并,需要回到 TASK-002 修代码。
它也写了 decision.json,更新了 flow-state.json。

但问题是,HALF 后端现在不把这个当成 TASK-005 完成。
因为它既没有看到 TASK-005/result.json,也没有特殊逻辑去认 decision.json。

所以页面上会出现这种情况:

  • 用户明明看到流程已经进入 needs_fix;
  • 但 TASK-005 节点还一直是 running;
  • 等 10 或 20 分钟后,它突然变成 needs_attention。

这对用户来说很奇怪:任务明明正常做完了决策,为什么系统说它超时失败?

正确行为应该是二选一:

  • 要么让 TASK-005 在 needs_fix 时也写 result.json;
  • 要么后端看到合法的 decision.json 后,就把 TASK-005 标成完成。
  1. TASK-002 完成时,系统记录了一个实际不存在的文件路径

举个例子:

中间轮次里,prompt 明确告诉 Agent:
TASK-002 不要生成 result.json,只要写 branch.json、测试报告、实现摘要这些中间产物。

但后端现在把 TASK-002 的完成路径记成:

TASK-002/result.json

这个文件其实不存在。

以后用户或开发者排查问题时,看到数据库里写着这个路径,就会去找 result.json,结果找不到。
这不是致命问题,但会让排查变麻烦。

正确做法是:记录真实存在的文件,比如当前轮次的 TASK-002/rounds/round-001/branch.json。

  1. 内置模板每次服务启动都会显示“被更新过”

举个例子:

HALF 服务重启一次,内置模板没有任何内容变化。
但系统还是把这个模板的 updated_at 改成当前时间,updated_by 改成 admin。

这样会带来两个小问题:

  • 模板列表里,这个模板可能永远排在最上面;
  • 以后想查“这个模板到底什么时候真的被改过”,查不到真实时间,因为每次启动都会刷新。

正确行为应该是:只有模板内容真的变化了,才更新 updated_at。

  1. 文档说的输入项和实际页面不一致

举个例子:

文档里还写着这个模板需要这些输入:

  • base_branch
  • work_branch_name
  • pr_target_branch

但实际代码里已经删掉了这些输入,而且 prompt 里固定写死用 main 分支。

用户看文档会以为自己可以配置分支,结果页面上根本没有这些输入项。
维护者看文档也可能误以为代码漏实现了。

正确做法是:要么更新文档,明确当前版本固定用 main;要么代码恢复这些输入。总之文档和实际行为要一致。

  1. 默认最大评审轮次 3 写在太多地方

这个不是必须改,但建议整理。

举个例子:

现在默认最大评审轮次是 3。这个 3 写在后端、前端、模板应用逻辑等多个地方。
如果以后产品决定默认改成 5,就要到处找这些 3,漏掉一个地方就会出现前后端不一致。

建议抽一个常量,比如 DEFAULT_MAX_REVIEW_ROUNDS = 3。
这样以后只改一个地方,比较不容易出错。

建议优先级:

必须先改:

  • 已 running 的任务还能重复派发;
  • TASK-005 在 needs_fix 后又被点亮;
  • TASK-005 needs_fix 后不收敛,最后变成超时。

建议顺手改:

  • TASK-002 记录不存在的 result.json 路径;
  • 模板每次启动都刷新更新时间;
  • 文档和实际输入项不一致;
  • 默认值 3 抽成常量。

This update fixes several edge cases in the built-in Issue 编码与双 Agent 评审循环 workflow where task state could be derived from stale or incomplete loop artifacts.

The decision task now validates TASK-005 decision.json against the current flow-state round, round_id, branch, and commit before treating the decision as submitted. This prevents old review or decision files from unlocking or completing the current round incorrectly, especially after a needs_fix transition.

Polling now completes loop tasks from their real business artifacts instead of relying only on result.json: TASK-002 records branch.json, TASK-003 and TASK-004 record review.json, and TASK-005 records decision.json once the decision reaches a terminal loop phase. Running loop tasks also require redispatch instead of a duplicate dispatch, matching the frontend controls.

The default max review round value is centralized across backend and frontend, template application preserves the same default, and the built-in template no longer updates updated_at/updated_by when its content is unchanged. The PRD is aligned with the current fixed-main-branch behavior, and AGENTS.md documents the repository testing rule.

Tests: docker compose run --rm backend uv run --with pytest pytest tests/test_issue_review_loop.py tests/test_polling_service.py; docker build --target build -t half-frontend-test ./frontend; docker run --rm half-frontend-test npm test -- ProjectNewPage
@XiaoCooder
Copy link
Copy Markdown
Collaborator Author

已核对,这几个问题本次更新已经覆盖并修复。

  1. 已 running 的任务还能重复派发

已修复。现在后端会区分普通派发和重新派发:

  • 普通 dispatch 遇到 issue review loop 中已 running 的任务会直接拒绝;
  • 用户如果确实要再次发送,只能走 redispatch;
  • 前端也同步调整:running 状态下不再显示普通“复制 Prompt 并派发”,只允许明确点击“重新派发”。
  1. TASK-005 在 needs_fix 后又被旧 review.json 重新点亮

已修复。现在后端只有在满足以下条件时才会派生 TASK-005 = unlocked:

  • 两份 review 都已提交;
  • 当前 phase 仍处于评审/等待决策阶段;
  • TASK-002 仍是 waiting_review;
  • TASK-005 还没有进入 completed / approved / abandoned / running 等终止或处理中状态。

因此流程进入 needs_fix 后,旧的 review.json 不会再把 TASK-005 重新点亮。页面上只会引导用户回到 TASK-002 修复代码。

  1. TASK-005 做出 needs_fix 决策后一直 running,最后超时

已修复。轮询逻辑现在会识别合法的:

TASK-005/decisions/round-xxx/decision.json

只要 decision 与当前 flow-state.json 的 round、round_id、分支和 commit 匹配,并且流程进入 needs_fix / approved / completed /
needs_attention 等收敛状态,后端就会把 TASK-005 标记为 completed,不再等待不存在的 TASK-005/result.json。

  1. TASK-002 完成时记录不存在的 result.json 路径

已修复。对于 issue review loop 的 TASK-002,轮询器现在优先记录真实存在的:

TASK-002/rounds/round-xxx/branch.json

如果找不到 branch.json,才会退回到可用的 flow-state.json,不会默认记录不存在的 TASK-002/result.json。

  1. 内置模板每次启动都会刷新 updated_at

已修复。内置模板初始化逻辑现在会先比较模板内容,只有内容实际变化时才更新 updated_at / updated_by。服务重启但模板内容不变时,不
会再刷新更新时间。

  1. 文档输入项和实际页面不一致

已修复。PRD 已同步当前实现:当前版本不再暴露 base_branch、work_branch_name、pr_target_branch 输入项,流程固定以 main 作为基准
分支和 PR 目标分支,工作分支由 Agent 自动生成。

  1. 默认最大评审轮次 3 写在太多地方

已整理。后端和前端分别抽出了 DEFAULT_MAX_REVIEW_ROUNDS = 3,项目创建、模板应用、页面默认值和测试都改为引用常量,避免后续改默认
值时出现前后端不一致。

另外已补充/更新测试覆盖这些场景:

  • running 的 loop task 不能普通 dispatch,只能 redispatch;
  • needs_fix 后旧评审不会重新解锁 TASK-005;
  • TASK-005 可以通过合法 decision.json 完成;
  • TASK-002 完成时记录真实的 branch.json 路径;
  • 内置模板内容未变化时不刷新更新时间;
  • 默认最大评审轮次使用统一常量。

验证结果:

  • 后端相关测试:38 passed
  • 前端 ProjectNewPage 测试:80 passed
  • 前端 Docker build 通过

提交已推送:7f974a3 fix: harden issue review loop completion。

@keting keting merged commit 4b4cfd8 into keting:main May 21, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

实现自动识别 Issue、编码、多 Agent 评审并提交 PR 的流程

2 participants