Repository navigation
Conversation
Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent | gpt-6.1-sol | OpenAI | runtime_reported | reasoning_effort=max
REQUEST_CHANGES:1 个 P2。默认预算可打断已经登记的临时 worktree,但异常路径没有撤销登记;成功重试后仍留下失效记录。
动机 / Motivation
维护多条已评审分支的开发者需要整合当前 source heads,同时在 Git helper 卡住时得到明确失败。旧同步可能无限等待;新实现让单次 Git 操作超时退出,正常情况下仍生成候选、发布本地整合分支并读回回执。可验证的目标是有界失败后仍能安全继续;本 PR 不改变 plan、分支顺序、source refs 或远端/PR 权限,临时 worktree 清理仍是这个目标的一部分。目前尚缺超时后撤销本次 candidate 登记的恢复路径。
改动思路 / Approach
唯一 Git 入口 _git 保持既有文本和错误处理,并将 communicate 等待限制到每次 10 秒;共享 process_runtime.retire_owned_process 负责已拥有进程组和管道的有界回收。这是既有 Python Git adapter 的 IO 边界,不是新增的 Core 决策能力。与 change_quality.scope、并行 #8362 相比,进程回收 owner 已复用,文本/bytes 与能力异常不必强行合成新框架。
但这里的 _git 同时执行 fetch、merge、worktree add/remove、reset 和 update-ref,并不都是读取。当前切片必须把新超时接入已有写操作的失败清理;仅杀进程不等于撤销 Git 已完成的登记。无需扩大成新事务系统:在既有 _build_candidate 里补本次 candidate 的有界清理并保留原异常,即可完成这个切片。不修改 plan schema、source refs、远端或 PR 权限。
具体改动 / Changes
全 PR 为 2 文件,+153/-6;生产文件 +62/-6,新增测试 91 行。规范依据 loopx/capabilities/integration_branch/README.md,不可变版本 c63a8f9cfc023ff22f39db5cb8b734da7653e83d,原文未被本 PR 改写。configure 的 preview-first 和 ignored plan、boundary 的不 push/不改 source branch 均通过真实 CLI;preview-and-sync 的临时 worktree 清理在下面反例中未满足,需修复后重验。
关键代码讲解
_gitL47 将每次 Git 操作放入独立 POSIX session,正常返回维持 UTF-8 replace 和原非零诊断。L73–79 的新 TimeoutExpired 分支回收进程后立即抛 IntegrationBranchError;它也可能中断已有写操作,不能只按读取失败理解。_retire_owned_readL30 复用共享回收函数,不把清理异常替换成成功结果。这个边界能限制等待,但并不知道 Git 是否已在 common dir 中登记 worktree。_build_candidateL605 先执行 worktree add,直到其返回后才把 added 设为 True。真实 post-checkout hook 是登记完成之后运行的;此时被新预算中断,finally 看到 added=False,跳过 worktree remove,却删除临时目录,留下 .git/worktrees 中指向不存在目录的条目。
对主干的风险 / Risks
[P2] 超时后清理本次 worktree 登记
独立真实 Git/公开 CLI 反例:建立 main、feature-a 和 ignored plan,先同步;推进 feature-a,再安装内容为 #!/bin/sh 加 sleep 11.5 的 .git/hooks/post-checkout,执行 loopx --format json integration-branch sync --repo-path <fixture> --execute,保留默认 10 秒。不可变 base 约 13.57 秒成功、0 个失效 worktree;本 head 约 11.54 秒返回 rc=2/invalid_state 和 worktree add 超时,git worktree list --porcelain 多出 1 个 prunable gitdir file points to non-existent location 条目。移除 hook 后重试同步成功,该条目依然存在。周期同步每次遭遇同样超时都会累积本能力自己的失效登记,不能由“已退出”或“重试成功”证明资源已收回。
最小修复:让 _build_candidate 在 worktree add 已产生部分效果而抛错时,也撤销本次 candidate 登记;清理即使出错也要执行目录收尾并保留原超时。只处理本次拥有的路径,不全局 prune 其他工作。新增真实 Git 的慢 post-checkout 回归,断言超时后 worktree 清单、临时目录、integration branch 和 last_sync 均符合预期,再验证解除故障后的成功与重复执行。
PASS:77 tests,1 skipped;新 3 条预算测试和原 integration、共享 process runtime 测试均通过。独立 CLI 的 configure preview/execute/replay、drift status、sync preview/execute/replay、in-sync status 和越界 plan 拒绝,共 9 项的完整 JSON/退出码在 base/head 相同;上面的写中断反例 FAIL,现有 fake-git 测试没有覆盖登记后 hook 阻塞。
本地 standard premerge 的 6 项直接检查和 2 项 catalog 检查通过。另行执行的 maintainability ratchet 仍 FAIL:Lark private_conversations.py 的 module_metric_budget;同一命令在不可变 base 与 head 的完整 450 字节输出相同,相关规则、配置及模块未改,原阈值保留。它是独立告警,不能掩盖本次反例。未查询或等待远端 CI。
原生 Windows 未运行,不能把 POSIX 或模拟分支当作 Windows 资格。10 秒按每次调用重新分配,另有有界清理时间;不是整条 sync 的总 deadline。默认仍不刷新 remote、preview 不发布 integration ref,stdin 从继承改为 DEVNULL 的行为也应作为自动化非交互边界明确保留。
我的整体评价 / Verdict
long_horizon:regression,重复超时可累积失效 worktree 登记;user_experience:regression,返回错误和成功重试都没有揭示或清掉遗留资源。当前必要修复是既有 candidate 生命周期内的清理,不是强制改写到 TypeScript 或增加全局状态。此处未改 rendered presentation,已检查 CLI/plan/Git 生命周期,visual_review 为 not_applicable,未验证范围是原生 Windows、生产采用与长期完整成本。共享词汇、actor 与权限未扩张,架构和代码量基本适配;正确方向及绿色单测仍不能覆盖已复现的生命周期缺口。修复后需在新精确 head 重跑完整 PR 及上述真实反例。
English verdict: REQUEST_CHANGES — timed-out worktree creation leaves a stale registration after recovery.
| try: | ||
| stdout, stderr = process.communicate(timeout=GIT_READ_TIMEOUT_SECONDS) | ||
| except subprocess.TimeoutExpired: | ||
| _retire_owned_read(process) |
There was a problem hiding this comment.
[P2] 超时后仍需撤销已产生的 worktree 登记。真实 post-checkout hook 等待11.5秒时,worktree add 已在 common dir 登记,随后被默认10秒预算中断;_build_candidate 尚未执行 added=True,finally 跳过 worktree remove,却删除临时目录。本 head 返回 rc2 后遗留一个 prunable 登记,移除 hook 并成功重试后仍存在;相同 base 成功且没有遗留。请让既有 candidate owner 在 add 部分完成后失败时也清理本次登记,保持原超时并在清理失败时仍收尾目录,避免全局 prune 无关工作。请补真实 Git 回归覆盖超时后的清单、目录、branch/receipt 不变及恢复。
动机 / Motivation
repository-change-window与change-quality的 Git 读取已经有预算(#8362、#8169),但整合分支这条路径还没有:loopx/capabilities/integration_branch/core.py的_git()是所有读取的唯一入口,configure_integration_branch、integration_branch_status、sync_integration_branch都经由它执行rev-parse、check-ignore、worktree list、fetch、merge、update-ref等命令,底层是一个没有上限的subprocess.run。仓库卡在陈旧
index.lock、网络挂载或卡住的 fsmonitor helper 上时,这条调用链既不返回也不报错;放在 hook 或自动化里就会把 Git 操作本身一起挂住。改动思路 / Approach
_git()仍是唯一入口,只在这一处加上预算:Popen,用communicate(timeout=GIT_READ_TIMEOUT_SECONDS)等待,预算 10 秒。retire_owned_process,在同一 deadline 内回收这次读取拥有的进程组、关闭未排空的管道;随后抛IntegrationBranchError。check=True时非零退出仍按stderr给出原有的错误细节。与 #8169、#8362 一样,这里只复用既有 owner:Git 读取的接口属于本能力,进程与管道的回收属于共享 runtime。
具体改动 / Changes
_git():从裸subprocess.run改为独立 session 的Popen+ 有界communicate;超时与取消都先回收再抛出/继续传播。_retire_owned_read():只回收本次读取拥有的会话,清理异常不替换本次读取的 timeout 或取消结果。tests/capabilities/test_integration_branch_git_budget.py:新增三条用例——正常读取保留文本与退出码、永不作答的假 git 在预算内拒绝且心跳停止、Git 无法启动时仍抛OSError。对主干的风险 / Risks
tests/capabilities/test_integration_branch.py加新增三条共 18 项全部通过(18 passed)。taskkill与 Job Object 路径由共享 runtime 承担,其行为已在 fix(change-quality): hold every Git read of the scope to one budget #8169 的注入用例中验证,本 PR 沿用同一入口。fetch);这是有意的 fail-closed 边界,调用方会拿到明确错误,而不是无限等待。status/sync的输出结构、CLI 参数或合并权限。我的整体评价 / Verdict
范围限定在一处 Git 入口,复用已验证的共享回收路径,正常结果与错误细节都保持不变。原生 Windows 与最慢读取的取舍仍待评审确认。