Repository navigation
Conversation
|
This pull request has merge conflicts with Choose the remote for the base repository, not an out-of-date fork. git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEADFor a same-repository clone whose Keep the DCO |
loopx-agent
left a comment
There was a problem hiding this comment.
[P2] 取整后仍须检查 proposal 上限大于零。 plan_adaptive_replay_points 第185行先由 _positive 接受 0.5,再 int 成零,随后把本来可选的结果截成空元组。相同有效候选/观测在不可变基线会抛 ValueError。这是对非法整数上限的校验回归,不是合法整数输入故障,也没有证据表明实际 Host 已饥饿。请在取整后验证 >0,补 0.5/0.999 用例,或移除额外 replay 提交。
动机 / Motivation
对代码仓库执行变更质量检查的操作者,以及调用 replay 规划 Python API 的开发者。旧质量检查遇到卡住的 Git 会一直等待;当前会拒绝并清理,但额外 replay 改动又让小于一的正数上限静默变为零。真实 CLI 的 Git 超时、取消和恢复已通过;完整 PR 仍因 replay 参数回归而阻塞。本次不运行模型、benchmark 或安装态 Host,不把单条 Git 查询预算当成整个命令的总时限,也不执行合并。
额外 replay 提交仍存在小于一的 proposal 上限校验回归。
改动思路 / Approach
实际范围是四个文件、两次提交,+334/-23:Git scope生产65/-8、replay生产29/-15,以及191行Git测试和49行数值测试。PR正文称只有Git与新测试两文件,不能据此忽略父提交 b8fd8c9 的 replay 变化。_git 以独立 POSIX session 启动真实 Git,单次 communicate 使用10秒;超时调用既有 OS 树回收、关闭管道而不等待子进程持有的管道,再抛兼容 ValueError;取消先清理再继续传播。这个常量为每次调用重新使用,不是整个 prepare 的共享总 deadline,审查没有凭标题制造10秒端到端 SLO。文本/字节模式和 scope/hash顺序保持原路径。另一个数值 owner 的 finite helper 拒绝 NaN、inf等输入,但整数化位置产生了上述新反例。
评审先读基线规范 loopx/capabilities/change_quality/README.md,不可变版本 98c4ba315d98c7e79b16f1ae184c5dd120d54a20。按原章节标识映射:Protocol:implemented;Provider Boundary:implemented。Git 生命周期放在 change_quality 的 IO 边界并复用 process_runtime,位置合适;额外 replay 提交属于另一个 owner,须修复本次发现的取整校验回归或从本 PR 移除。当前可交付边界是既有质量检查的 Git 读取、错误、取消与恢复;没有新开关、CLI 参数或运行模型的授权。次要 replay 数值边界未找到单独书面规范,相关回归判断使用先前公共 API 的明确 guard、同输入对照和当前 main;没有发明 RFC 条款。
具体改动 / Changes
-
_git: Bound one actual Git read。Unbounded run becomes Popen/10s communicate;Text/bytes preserved; timeout ValueError, cancellation re-raised。调用者:resolve_git_root/build_change_quality_scope。 -
_retire_owned_probe: Retire only owned group and pipe ends。New cleanup reuses shared OS owner;Close inherited pipes unread; bounded final reap。调用者:_git timeout/cancel branches。 -
build_change_quality_scope: Bind exact scope and hash。Selection/hash input order unchanged;Failed required Git proof is refusal, not empty scope。调用者:prepare/record/verify receipts。 -
plan_adaptive_replay_points: Plan finite scored replay candidates。New positive-float validation precedes integer cast;Normalized proposal_limit must remain >0; current0.5 case violates it。调用者:Existing public Python API and replay tests; no external production caller discovered。
对主干的风险 / Risks
五份完整 head 原生模块140PASS、一个 Windows junction skip;四份共同 base 模块117PASS、同一个skip。独立使用真实 source CLI 和同一个隔离仓库做 off/enabled/新变更与untracked/非法base/非Git/恢复六组完整 JSON 对照,共12次调用,未删除指纹或错误字段。再通过真实 Git 的 fsmonitor hook 启动辅助进程:当前超时11.191秒、exit1、0个记录中的后代存活、心跳795→795;终端取消3.198秒、exit-2、0个后代存活、30→30,保留 KeyboardInterrupt。基线超时由外层安全上限收止,不能算产品成功;基线取消返回观察期间心跳仍增长,也没有伪称其所有PID持续存活。四条恢复命令均成功。关闭质量功能时 prepare 仍先读取Git,这是既有顺序,完整关闭输出相同,不能宣称 off路径零Git。
上述 Git 修复证据充分,但同样有效候选/观测的15组数值 API 对照中,0.5在基线报错、当前静默空结果;独立“normalized integer必须为正”后置条件在base通过、head失败。现有140个测试遗漏此例。当前main已有其他 replay有限证据处理并保留取整后检查,未改的累加溢出问题没有被误算成此次新回归;仍需作者在解决当前冲突后提交新head重新评审。必要源检查:Ruff、17roots mypy、严格TS、raw maintainability、全树语义词汇及diff advisory、编译、diff、公共边界和两次DCO均通过。当前 CQR cqr_b8c5c0c3fe37d7db869e 持久记录两个相关blocker,verify/premerge因此拒绝;没有把检测到回归的测试标成PASS。
本次检查 change_quality/scope.py、CLI 错误返回与 replay Python API,没有渲染页面变更。原生 Windows、安装态 Host 和真实模型采用未验证;本次没有页面截图资格声明。本轮没有获取、轮询或等待远端 CI;未知 CI 原因保持未知。已验证边界是源码检出与真实本地依赖,不能替代安装态 Host、真实 provider/模型采用、长期完整成本或父任务验收。当前队列召回的 outcome-first 经验用于组织这次真实调用证据,不作为测试或模型采用证明。审查结论、未解决线程、GitHub readiness 和维护者合并授权分开,审查本身不执行合并。
我的整体评价 / Verdict
本次检查 change_quality/scope.py、CLI 错误返回与 replay Python API,没有渲染页面变更。原生 Windows、安装态 Host 和真实模型采用未验证;本次没有页面截图资格声明。额外 replay 提交仍存在小于一的 proposal 上限校验回归。
REQUEST_CHANGES:先修复取整后的 proposal_limit 正值检查,或移除额外 replay 提交,并补回归用例。Git生命周期修复可保留,但不能据此批准完整四文件head。
Reviewer: model_agent | gpt-6.1-sol | OpenAI | runtime_reported | reasoning_effort=max
English verdict: REQUEST_CHANGES — Git timeout and cancellation cleanup are verified, but the extra replay change silently accepts a proposal limit that truncates to zero; repair the normalized-integer guard or remove that commit.
| if int(max_proposals) <= 0: | ||
| raise ValueError("max_proposals must be positive") | ||
| threshold = float(minimum_score_minutes) | ||
| proposal_limit = int(_positive(max_proposals, "max_proposals")) |
There was a problem hiding this comment.
[P2] 取整后仍须验证 proposal_limit 大于零。_positive(0.5) 会通过,随后 int 变成0,最终把有效候选截成空元组;相同候选/观测在基线会抛 ValueError。这是对非法整数上限的校验回归,现有新增测试没有覆盖。请在取整后拒绝 <=0,补0.5/0.999用例并保留正常整数结果,或移除额外 replay 提交。
There was a problem hiding this comment.
这条成立:_positive 接受 (0, 1) 的数,int() 之后变成 0,proposal_limit 为 0 时切片把本来有效的候选截成空元组;基线里那句 int(max_proposals) <= 0 的正值检查正是在这个提交里被替换掉的。
按第二个选项处理:b8fd8c924 属于另一个 owner(replay 数值校验),已经从本分支摘掉。fix/change-quality-git-budget 现在只剩 fix(change-quality): hold every Git read of the scope to one budget 一个提交、两个文件——loopx/capabilities/change_quality/scope.py 与 tests/test_change_quality_git_budget.py;adaptive_replay_planner.py 和 tests/test_explore_replay_runtime.py 不再出现在本 PR 里。取整后 <= 0 的拒绝与 0.5 / 0.999 用例会放在 fix/adaptive-replay-numeric-inputs 上单独补。
本 PR 的 Git 部分没有改动:_git 超时先回收自己那个会话再返回 ValueError,取消先回收再继续传播,真实 CLI 的超时、取消与恢复对照与上一轮相同。
新 head:20b7a40fe。
`_git` ran `subprocess.run` with no timeout, so the six reads behind `build_change_quality_scope` and `resolve_git_root` waited as long as Git did. A checkout blocked on a stale index lock, a network mount or an oversized history held the receipt open with no answer at all; reproduced with a `git` on PATH that only sleeps -- `resolve_git_root` was still waiting five seconds in. Every read now runs in a session of its own under `GIT_PROBE_TIMEOUT_SECONDS`, and an exhausted budget retires that session through the existing `terminate_process_tree` before dropping the pipes unread: Git starts fsmonitor hooks and pagers inside the session it runs in, and an inherited pipe outlives the leader, so a drain would wait on work this call no longer owns. The refusal keeps the existing `ValueError` exit that `resolve_git_root`, `_required_git_text`, `_name_only` and `_hash_command_output` already used, so consumers keep their current failure handling. The same wait also now retires the session when the receipt run is cancelled (a terminal Ctrl-C reaches this process's group, not the probe's) and then re-raises the original interruption. Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>
293fd1e to
20b7a40
Compare
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
[P2] 首个 POSIX shell fixture 缺少平台限制。 新增测试在 tests/test_change_quality_git_budget.py:50-62 将无扩展名的 git 文件写成 #!/bin/sh,随后要求它触发 did not answer 超时。原生 Windows 的 shell-free Popen 无法把这个文件作为 POSIX 脚本执行:可能找到真正的 Git,或得到启动错误,都不能验证预期超时。相邻的两个测试已用 os.name != "posix" 跳过。请给首个测试采用相同条件,或提供真正可移植的 fixture;再运行整个文件及 Windows 对应收集/执行。这是准确源码的静态发现,本机没有执行原生 Windows。
动机 / Motivation
对仓库执行变更质量检查的开发者和自动化调用者。 真实 Git 的 fsmonitor hook 卡住时,基线需由外层安全上限终止;新 head 约10.9秒返回错误并停止辅助进程。 正常、关闭及开启质量策略的完整 prepare JSON 与基线相同,错误和恢复路径已独立执行;当前仍有首个 POSIX 测试未隔离平台的问题。 本次不改变质量策略、receipt 协议或 merge 权限,也不承诺整个命令只耗时10秒。 首个 shell fixture 需要平台限定或可移植实现;原生 Windows 执行仍未验证。
改动思路 / Approach
预算与清理属于既有 Git provider IO 边界,复用共享 OS 树回收,不增加 TypeScript 决策 owner 或持久状态。 当前可交付边界为 scope 的 Git 读取、错误、取消及恢复;额外 replay 改动已经移除,平台测试仍需修复。
实际路径是原 CLI 调用 build_change_quality_prepare_packet,再由 build_change_quality_scope 读取路径和内容;质量策略、类型化验证与 OS 清理各自保留原 owner。单纯给旧 subprocess.run 加 timeout 不能证明继承管道和辅助进程已停止,因而隔离进程组与不排空管道有实际用途。这里的10秒是每次 Git 调用重新使用的预算值,不能据标题推导整个 prepare 的共享 deadline。既有关闭策略仍先读 scope;正常关闭/开启输出均做了完整对照,并非声称关闭时零 Git。
具体改动 / Changes
当前准确 head 20b7a40fedb888f3468894723b3360f110d9c408,PR 目标 main 当时为 62e234c939996ad42f2b1168451cc9a656dce11d,实际 merge base 为 98c4ba315d98c7e79b16f1ae184c5dd120d54a20。完整提案是两文件 +256/-8:生产 +65/-8、新增测试191行,没有新持久状态、协议字段、配置或自动执行器。
规范 loopx/capabilities/change_quality/README.md,不可变 revision 98c4ba315d98c7e79b16f1ae184c5dd120d54a20:Protocol — implemented,四种变更来源、哈希及拒绝语义保持;Provider Boundary — implemented,IO 仍在 Python 能力边界,复用共享进程树 owner。规范没有把10秒定义为整个命令时限。
关键代码讲解
_git保留 UTF-8 text 与原始 bytes 两类调用,改用独立 session 的Popen;正常结果仍为CompletedProcess,超时抛兼容ValueError的异常,取消先清理再传播。_retire_owned_probe复用terminate_process_tree,关闭管道而不排空,再有界等待 leader。它只拥有本次 Git 的组,不能授予对其他执行器的处置权;原生 Windows 清理未测。build_change_quality_scope继续收集 committed、staged、unstaged、untracked,再按原顺序计算二进制内容指纹;所需 Git 读取失败不会变成成功空范围。
与上一轮 293fd1e0d0bd5ef350f64cff139007dcc6bb8b5f 对比,Git 生产与测试 blob 完全相同;额外 replay 文件及提交已移除,因此原 proposal_limit 发现不再属于当前提案。这只关闭该旧发现,不继承旧结论,也不替代本轮整审。
对主干的风险 / Risks
PASS:本轮独立执行47项相关测试,公共不可变基线相同三模块43项通过;仓库指定 lint roots、无 import 遮蔽的冷 mypy17个源根、diff hygiene、编译、RFC配对与贡献提交 DCO 均通过。真实 Git 的同一合成 fixture 做了16次 CLI 调用:关闭/开启策略与三类合法拒绝的完整 JSON/exit/diagnostic 均一致。超时 head10.876秒、exit1、辅助进程消失且心跳290→290;基线由外层安全上限16.557秒终止,不能记作产品成功。取消 head1.373秒、exit-2、心跳15→15;基线虽 exit-2,辅助进程仍活着且15→30。故障移除后两版本原命令都恢复成功。
UNVERIFIED:原生 Windows 没有执行,新增首个测试的平台问题仍为上述 P2。cqr_448c1813e4dcd2e344f6 如实记录 fail,verify 为 invalid_receipt;不能把47项 POSIX 通过当成平台隔离已成立。源码 quick premerge 在当前 head 与完全未改的不可变基线均 exit1,完整 traceback 仅替换检出路径后字节一致,错误来自未改的机器配置读取路径。这一旧 checkout 与当前配置的兼容性观察独立归因,仍保持失败;改变的 Git 不变量已有47项测试及真实 CLI 证据,不把它算作新的 PR 缺陷或 CQR 通过。没有获取、轮询或等待远端 CI。
语义与 CI 对齐
本变更属于 local_only 的 Git IO 预算与类型化异常;没有增加业务状态词汇、字符串状态分类、工作义务或领域专用 quota 文案。正常 prepare 指令全文保持,超时诊断及取消清理是公开披露并实际验证的有意行为变化。预算并不自动启用质量能力;现有状态与 merge authority 均保持原边界。
我的整体评价 / Verdict
检查 scope.py、receipt/context 调用者和新增测试:本 PR 没有渲染页面变更。 原生 Windows、安装态 Host 与实际模型采用未验证;没有页面截图资格声明。
Git 读取到错误再到恢复的源码路径有正向证据,long_horizon 和 user_experience 的改善仅限已测边界;未证明安装态采用、长期效果或费用节省。实现体量与所有权合适,额外 replay 已拆走。当前完整 head 仍需修复新增首个测试的平台适用条件,因此 REQUEST_CHANGES;不将静态 Windows 发现伪装成已执行的 Windows 失败,也不据公开评审执行合并。
English verdict: REQUEST_CHANGES — 20b7a40 verifies bounded Git timeout/cancellation and recovery, but the first POSIX shell test needs an explicit platform guard or a portable fixture; native Windows remains untested.
| tmp_path: Path, monkeypatch: pytest.MonkeyPatch | ||
| ) -> None: | ||
| repo = _worktree(tmp_path) | ||
| _fake_git(tmp_path, "#!/bin/sh\nsleep 30\n", monkeypatch) |
There was a problem hiding this comment.
[P2] 给首个 POSIX shell fixture 加平台限制。这里写出的 git 是无扩展名的 #!/bin/sh 文件;原生 Windows 的 shell-free Popen 无法执行它来产生所断言的 did-not-answer 超时,可能调用真正 Git 或得到启动错误。相邻两个测试已经有 os.name != posix 的 skip。请沿用该平台条件或提供可移植 fixture;本次是准确源码的静态发现,没有声称执行过原生 Windows。
There was a problem hiding this comment.
这条成立:第一个用例写出的是无扩展名的 #!/bin/sh 脚本,Windows 上无 shell 的 Popen 既不会按 PATH 找到它,也不能保证执行它。那样要么落到真正的 Git 上立刻作答,要么抛启动错误,两条路都碰不到「预算用尽」这个断言,等于什么都没验证。
按你给的第一个选项改了:test_a_git_that_never_answers_stops_inside_the_budget 现在带上与旁边两条用例相同的 os.name != "posix" 条件。这个文件里的三条 Git 进程用例在非 POSIX 平台上统一跳过。
代价我写清楚:非 POSIX 平台上这个文件就不再覆盖 Git 子进程,预算超时、会话回收与 Ctrl-C 回收都只在 POSIX 验证,原生 Windows 的进程树回收仍未执行。我这边没有 Windows 环境,不会把静态结论说成已执行的 Windows 结果;要让 Windows 也覆盖这三条,需要把固定装置换成 git.cmd 这类可移植形态,那是单独一件事。
本 head:27cc0d382。这个文件仍是 4 passed,CI 选择集的 lint 全绿。
Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>
The budget test writes an extension-less `#!/bin/sh` script and asserts a "did not answer" timeout. A Windows `Popen` without a shell cannot execute that file, so on that platform the test either reached the real Git or failed to start, and proved nothing either way. It now carries the same `os.name != "posix"` condition as the session retirement and cancellation tests beside it. 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
[P2] Windows 上 Git 查询超时后,收尾仍可无限等待。 _retire_owned_probe 先进入共享的 graceful tree cleanup。Windows 分支可能卡在无时限的 taskkill,也可能在两次终止都失败后无限等待原 Git 进程。后面的 1 秒 wait 无法约束这段代码。请让共享 runtime 约束收尾和管道生命周期,并验证终止失败时原调用仍能收到超时或取消。
动机 / Motivation
开发者和自动化调用者通过 change-quality CLI 检查仓库范围与质量回执。 旧实现用无超时的 subprocess.run。卡住的 Git 使质量检查持续等待,取消也可能遗留 helper。 真实 fsmonitor hook 卡住时,基线须由外层 watchdog 终止。当前 macOS CLI 约 11.5 秒返回错误并回收 helper。 健康输入、关闭和开启质量策略时的完整 prepare 输出与基线一致,macOS 超时和取消后可用原命令恢复。 评审以单次 Git 查询及清理为范围。整个 prepare 的耗时仍取决于多次查询和文件哈希。 Windows 清理分支遇到卡住或拒绝终止的 taskkill 时,查询已超时,调用仍可持续等待。
改动思路 / Approach
Git 读取归 change-quality 的 scope owner,进程树归共享 process runtime。保留现有分工,把超时后的等待与管道收尾纳入同一有界生命周期。 本 PR 可独立交付 Git scope 的超时、取消和恢复,Windows 清理仍需完成有界回收。
现有 CLI 通过 receipt 层读取 Git 根目录、引用、路径和内容指纹。communicate(timeout=10.0) 限制单条 Git 查询。查询超时或被取消后,_retire_owned_probe 才接手,调用共享 runtime,然后关闭流并等待原进程。复用已有 owner 是合适的,当前选中的 graceful helper 在 Windows 上没有完整的收尾上限。已有 force_terminate_process 提供有界的强制回收路径,可以复用其等待约束,并处理 reader 尚未退出时的管道释放。
具体改动 / Changes
当前 head 27cc0d3828d6b8d41bf8a075aced1f42e2a030fe,不可变比较基线 8e86f173f08bd55eb833d8cdf42360f93bf989fe。完整 diff 为两文件 +257/-8,生产 +65/-8,测试 +192。第一轮的 replay 数值文件已退出完整 diff。上一轮的 shell fixture 已加 POSIX skip,与相邻两项进程测试一致。两条旧发现已解决。
按基线 loopx/capabilities/change_quality/README.md 的 Protocol 和 Provider Boundary 评审,四种来源和精确指纹仍由原 scope 生成,质量策略及验证由原 owner 执行。现有 prepare/record/verify 调用都使用这条 scope 路径。
关键代码讲解
_git保留 UTF-8 文本和原始 bytes 两种模式,并从Popen还原CompletedProcess。正常的路径列表和二进制哈希输入已做同仓库对照。 scope.py 第 48 行_retire_owned_probe在 POSIX 上回收本次查询拥有的进程组。Windows 分支先运行taskkill /T,必要时运行/T /F。两次调用无超时,最后的 leader wait 也无上限。若 descendant 仍持有输出管道,还需证明 reader 和 close 能够有界退出。 scope.py 第 35 行 · process_runtime.py 第 219 行build_change_quality_scope依次收集 committed、staged、unstaged 和 untracked 内容。现有 CLI 将ValueError映射为错误 payload,前提是清理返回控制。 scope.py 第 162 行
对主干的风险 / Risks
通过。 当前 37 个聚焦测试通过,基线相同共享集 33 个通过,两边配置的 17 个 mypy 文件通过。16 次真实源码 CLI 调用验证了正常启停策略的完整输出、三类拒绝和解除故障后的恢复。macOS hook 持续等待时,基线在 16.352 秒由 watchdog 终止,当前在 11.452 秒退出 1,helper 消失且心跳停住。终端 SIGINT 后,当前以 -2 退出,helper 消失。基线 helper 仍在写入。quick premerge 的 6 个直接检查和 3 个选中检查通过。
阻塞。 macOS 上把实际 runtime dispatch 指向 Windows 清理分支,用真实子进程注入 taskkill 卡住、非零退出两种故障。0.25 秒查询预算到期后,两者都等到 3 秒 watchdog,未返回类型化错误。对照只换成共享有界回收 helper,约 0.57 秒返回错误。该对照用于定位等待位置,原生 Windows 和 descendant 持有管道的回收仍须验证。独立的必需 Ruff 检查在基线和当前都只报同一处 F841,文件 tests/control_plane/test_goal_create_completion.py:184 的 blob 和完整诊断一致。严格回执 cqr_d0909c20f52d9260f1b4 为 fail,verify 为 invalid_receipt。
语义与 CI 对齐
固定预算和兼容 ValueError 的诊断属于现有 Git IO。完整 off/on 对照覆盖健康调用的原结果,超时和取消的变化在真实 macOS Git 路径上可观察。贡献提交的 DCO trailer 和 GitHub 集成提交签名均已核对。远端 CI 状态未查询。
我的整体评价 / Verdict
改动入口是 change-quality CLI 和 Git 读取,评审覆盖了原调用路径及测试。 原生 Windows、安装态 Host 与长期效益未验证。
macOS 的 CLI 在故障后能返回控制,并且原命令能恢复。Windows 的共享清理路径仍有可复现的无期限等待,需要在本 PR 的 Git 生命周期边界内修复。已有有界 process runtime 能承接清理,作者还需验证实际 Windows 的 pipe reader 和失败退路。严格 Ruff 的基线故障独立保留。
English verdict: REQUEST_CHANGES — 27cc0d3 fixes the earlier POSIX test eligibility issue and improves macOS timeout and cancellation behavior. The Windows cleanup path can still block before returning the Git error. Bound cleanup and verify failure and pipe ownership. The unrelated required Ruff failure remains a separate merge hold.
| """ | ||
|
|
||
| try: | ||
| terminate_process_tree(process, grace_seconds=0.2) |
There was a problem hiding this comment.
[P2] 给 Windows 的 Git 超时收尾设定上限。这里先调用 terminate_process_tree。Windows 分支的两次 taskkill 都没有 timeout,强制终止失败后还会无期限 process.wait()。因此后面的 wait(timeout=1.0) 来不及生效,prepare 等命令可能收不到超时错误或取消。在 macOS 故障注入中,卡住和返回非零的 taskkill 都让 0.25 秒 Git 预算的调用等到 3 秒 watchdog。请复用共享 runtime 的有界回收路径,把管道收尾也纳入上限,并覆盖终止失败及 descendant 持有管道的场景。原生 Windows 尚未执行。
There was a problem hiding this comment.
这条 P2 成立,已经按共享 runtime 的方向修了。
scope.py的_retire_owned_probe改走新增的retire_owned_process,不再直接调用 Windows 上无界的 gracefulterminate_process_tree。process_runtime.py的retire_owned_process复用force_terminate_process的单个 cleanup deadline;Windows 的两次taskkill都只拿到 deadline 的一部分,taskkill.wait的超时被转换成已确认失败的RuntimeError,随后仍在同一 deadline 内 reap 原 Git 进程。- 管道收尾也放进同一 deadline:
stdin/stdout/stderr的close()放在 daemon helper 里,调用线程只按剩余预算 join。reader 被 descendant 卡住时,close 不完成也不会延长原调用的等待。 _retire_owned_probe只吞掉清理异常;原TimeoutExpired/KeyboardInterrupt继续走GitProbeUnavailable或取消路径。
验证:在 POSIX 上把 runtime dispatch 指向 Windows 分支,用真实子进程注入 taskkill 卡住和返回非零两种故障;超时与取消各两组用例,均在 3 秒内返回。tests/test_change_quality_git_budget.py 8 passed。原生 Windows 未执行;leader 已退出时 descendant cleanup 仍可能 unconfirmed,但调用不会再被它拖住。
新 head:efe5b5fc7。tests/control_plane/test_goal_create_completion.py:184 的 F841 是基线独立问题,没有塞进本 PR。
The change-quality scope retired a timed-out or cancelled Git read through the graceful Windows process-tree cleanup, whose two taskkill calls and leader wait had no deadline. A stuck or refused taskkill could therefore delay the read's own timeout or cancellation indefinitely. Add a shared retire_owned_process path that reuses force_terminate_process under one cleanup deadline. Convert taskkill timeouts into the existing unconfirmed-cleanup failure, and close pipe ends on daemon helpers joined only until the same deadline, so a reader blocked by a descendant cannot extend the caller's wait. Cleanup failures no longer replace the read's own timeout or cancellation. 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
本轮没有阻塞发现。[P3] retire_owned_process(timeout=0.02) 接受 20 ms,却给内部强制回收 50 ms。现有 Git 调用只使用默认 1 秒。后续可让内部预算取外层剩余时间,补一个小于 50 ms 的单测,保住这个共享接口对短预算的承诺。 process_runtime.py 第 411 行
动机 / Motivation
change-quality prepare 依赖 Git 读取仓库范围和指纹。开发者和自动化在读 Git diff 和验证质量回执时都会遇到这个入口。Git 卡住时,调用者需要收到可诊断的错误,才能解除阻塞后用原命令重试。上一轮对 27cc0d38 的 P2 发现,即使 Git 已超时,Windows 的 taskkill、进程等待和仍被 reader 持有的管道也可能继续阻塞。 上一轮 Windows 发现
单次 Git 查询有 10 秒通信预算及独立的清理预算,整条命令的时间仍取决于多次读取和哈希。 旧 head 五种情况均触发 4 秒 watchdog。新 head 四次返回 Git 超时错误,一次传播 SIGINT,均回收 Git 主进程。 实际 taskkill 和 Job Object 路径仍待实测。
评审依据为 loopx/capabilities/change_quality/README.md,修订 8e86f173f08bd55eb833d8cdf42360f93bf989fe。Protocol 要求 committed、staged、unstaged、untracked 四类内容参与原有精确指纹。 Provider Boundary 要求仓库仍决定验证方式。这两条分别用完整输出对照和回执生命周期 smoke 核验。 基线契约
改动思路 / Approach
Git scope 负责每次查询的等待预算和错误出口。共享 process runtime 负责终止原进程和回收管道。只给 communicate 加超时会把等待转移到清理阶段。当前方案复用共享强制回收,并把管道关闭放在受 deadline 约束的等待里。当前交付边界是原 CLI 的有界拒绝、取消和重试,保留既有质量回执协议。
具体改动 / Changes
关键代码讲解
_git通过Popen执行每次 scope 查询,保留 UTF-8 文本及原始 bytes 两条路径。超时进入GitProbeUnavailable,取消在清理后继续向上传播。 scope.py 第 41 行_force_terminate_windows_process_tree给taskkill /T /F和其自身 reap 分配剩余时间,最后仍尝试回收 Git 主进程。主进程已退出时保留进程树清理未确认的状态。 process_runtime.py 第 298 行retire_owned_process分配清理预算。daemon worker 关闭管道,调用线程只等待到 deadline。测试的 Windows 故障分支覆盖了卡住和非零退出的taskkill,另用真实管道补验了仍有 reader 的情况。 process_runtime.py 第 355 行 · 测试第 217 行build_change_quality_scope继续生成相同的四类变更和精确指纹。旧 head 和新 head 在同一真实 Git 仓库上的正常与禁用输出完全相同。早期混入的 replay 改动已退出本 PR 的完整 diff。 scope.py 第 155 行
对主干的风险 / Risks
| 验证 | 当前结果 |
|---|---|
| 本地源码 CLI 与真实 Git | PASS。14 次对照覆盖启用、禁用、无效输入、fsmonitor 超时、SIGINT、重试。POSIX 子进程在返回前停止。 |
| Windows 清理故障注入 | PASS。macOS 上复用 CPython 的 Windows reader-thread 实现和真实管道。 |
| 原生 Windows | UNVERIFIED。注入中持有管道的后代可以继续存活,返回时仍可观察到等待释放管道的关闭线程。 |
| 本地检查 | 66 项测试通过、1 项跳过,配置内 mypy 和变更文件 Ruff 通过。全树语义 smoke 在 quick 组合中超时,单独以相同 120 秒上限重跑通过。 |
语义与 CI 对齐
质量策略默认仍关闭。健康输入与错误结构保持兼容,Git I/O 超时也适用于原有禁用路径。语义差异扫描没有发现新共享词表定义,Git 异常仍进入原 ValueError 边界。仓库要求的 Ruff 在旧、新 head 都复现相同 F841,触发文件在 PR diff 外,当前 main 的该文件已修复。该分支的必需 lint 和严格回执资格保留阻塞。远端 CI 未查询。 失败位置
我的整体评价 / Verdict
支持合入这一变更的代码设计。上一轮的 Windows 收尾阻塞已通过同入口的匹配故障注入复验。现有 scope 和共享 runtime 的分工足以交付有界错误与重试,主进程及后代进程的确认边界也清楚。合并前仍需分支同步、必需验证和 GitHub 审批收尾。
English verdict: APPROVE for efe5b5fc74771e1cb318c731294ffd01c5c4b0be. Bounded error and cancellation were reproduced on the same CLI boundary. Native Windows and merge qualification remain open.
retire_owned_process floored the native-termination share at a 50 ms grace, so a caller asking for 20 ms received more time than its own deadline. Derive the inner budget from what is left of the caller's deadline instead, and cover the sub-grace case so the shared entry point keeps that promise. Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>
|
回应 review 5812516250 里的 P3( 这条 P3 成立,已经收掉。 原来 terminate_timeout = min(timeout * 0.8, deadline - time.monotonic())不再有 50 ms 下限,20 ms 就是 20 ms;80/20 的切分保留,剩下的时间仍然留给 补了一条小于 50 ms 的用例 验证: 新 head: |
7241849 to
dd05ea8
Compare
|
本分支做了一次纯身份重建,代码内容没有任何变化。
这次重建只清理身份字段,不涉及功能取舍;原生 Windows 与合并资格仍是原有状态。 |
|
Hi @gcl-coder, the DCO If the log confirms a missing |
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
动机 / Motivation
运行 change-quality prepare 的开发者和自动化要读取目标仓库的 Git 差异,才能确认质量回执覆盖了当前改动。当真实 Git 的 fsmonitor 钩子卡住时,旧命令一直等待,收到 Ctrl-C 后还可能留下继续运行的子进程。
当前提交在同一入口报告 Git 读取超时,取消继续传播,POSIX 子进程停止后可以重试得到同一范围。本次给每次 Git 查询设定通信与清理预算,整条命令的耗时还取决于查询数量和文件读取。原生 Windows 的进程树回收尚待验证。
改动思路 / Approach
Git scope 保留路径、引用和二进制差异的读取职责。共享 process runtime 负责它启动的进程和管道。仅给 communicate 加时限会把等待转移到终止或关闭管道。当前实现复用共享强制终止,并在清理时限内等待管道关闭。交付边界是现有命令的有界失败、取消和重试。回执资格仍由原有策略和校验决定。
具体改动 / Changes
关键代码讲解
-
_git为每次 Git 通信设置 10 秒上限,保留 UTF-8 文本和原始字节两条路径。启动或等待失败走现有 CLI 错误结构,取消清理后继续传播。 scope.py 第 41 行 -
_force_terminate_windows_process_tree在共享时限内等待并尝试回收taskkill和原进程。原进程先退出时,回收状态保留为未确认。 process_runtime.py 第 298 行 -
retire_owned_process给终止操作分配至多八成的清理预算,管道关闭只等待剩余时间。上一轮指出的 50 ms 下限已经移除。20 ms 输入在旧代码中传出 50 ms,这一提交传出 16 ms。真实 POSIX 子进程在两次对照中均被回收。 process_runtime.py 第 389 行 新增预算用例 -
build_change_quality_scope仍按已提交、暂存、未暂存和未跟踪四类生成范围与指纹。逐个暂停实际prepare触发的 11 次 Git 读取时,每次都返回带命令名的超时错误,恢复后完整输出相同。 scope.py 第 155 行
依据 loopx/capabilities/change_quality/README.md 的修订 8e86f173f08bd55eb833d8cdf42360f93bf989fe。Protocol 已满足,同一混合 Git 工作区启用和关闭资格时,基线与当前提交的完整输出分别相同,回执 smoke 也通过了精确范围和陈旧范围检查。Provider Boundary 已满足,仓库继续决定验证方式,Git 进程预算由现有 I/O owner 处理。 变更质量契约
对主干的风险 / Risks
| 本地验证 | 结果 |
|---|---|
| 真实 Git、超时和取消 | PASS。基线命中 watchdog,取消后子进程继续写入。当前提交返回超时或传播 SIGINT,子进程停止,清除阻塞后重试恢复同一输出。 |
| Windows 清理故障注入 | PASS。macOS 上用真实子进程、管道和 CPython 的 Windows reader 实现复验。五种情况均返回,Git 主进程均已回收。三种持有管道的情形仍观察到存活后代。 |
| 本地聚焦验证 | PASS。67 项预算与进程测试通过,1 项跳过。另有 37 项质量回执测试、26 项传输测试通过,改动文件 Ruff、mypy 和精确回执 smoke 通过。 |
| 原生 Windows | UNVERIFIED。实际 taskkill 和 Job Object 回收仍待验证。 |
语义与 CI 对齐
质量资格仍沿用原有配置。启用和关闭资格的正常输出以及无效引用、非仓库输入的拒绝结果都通过了完整对照。共享 Git 读取时限也适用于关闭资格的 prepare 路径。局部 GitProbeUnavailable 沿用既有 ValueError 出口,差异扫描没有发现新增共享词表,全树语义 smoke 通过。
必需的工作流 Ruff 在分支基线和当前提交均复现同一条 F841。出错文件的 blob 相同,位于 PR 改动范围外,当前 main 对该文件的 Ruff 检查已通过。这个结果单独限制严格回执和合并资格。远端 CI 未查询。 精确提交上的诊断位置
我的整体评价 / Verdict
支持合入当前设计。Git 卡住时,现有命令能给出可诊断的错误,调用者可以清除阻塞后继续工作。正常的质量回执流程通过了对照。上一轮的短预算问题已有独立反例和修复证据。分支仍需同步并通过必需检查,原生 Windows 回收范围保留为待验证。
English verdict: APPROVE for dd05ea87163f3f779d158344846d75f991ce1e6d. The per-query timeout, cancellation, retry, and short cleanup budget were verified. Required lint on this head and native Windows verification remain open.
dd05ea8 to
c98f3fe
Compare
|
补一条 DCO 说明。
处理:保留合并提交的树与两个父提交( 代价还是那一条:head 再次变化,请按新 head 重看。功能、测试与正文内容未改。 |
Goal And Delivered Outcome
Outcome basis / optional anchor: 延续 fix(registry): retire the git probe process tree on timeout #8115(registry 探针)与 fix(contract): bound the Git ownership probes in a public-boundary scan #8152 / fix(doctor): hold every Git read to one budget #8153 / fix(lark): hold the collector's Git reads to one budget #8154 给各自 owner 立的有界读取,本 PR 处理
loopx/capabilities/change_quality/scope.py这个 owner,并把超时/取消后的 Windows 收尾交给共享 process runtime。Goal/source and gap:
_git用裸subprocess.run读 Git,没有预算。build_change_quality_scope与resolve_git_root一共六处读取(rev-parse --show-toplevel、两次rev-parse --verify、四次diff --name-only、一次ls-files --others、三次diff --binary)都会跟着 Git 一起等下去。仓库卡在陈旧index.lock、网络盘或超大历史时,change-quality receipt 既不返回也不报错。Windows 上即使communicate已超时,_retire_owned_probe仍可能卡在无时限的taskkill或原进程wait。Observable before → after, with the validation row that proves it:
sleep 30的git调resolve_git_root--show-toplevel的包装 git,它带着写心跳的子进程taskkill卡住或返回非零main.Author Declaration
Implemented against
loopx/registry.py(fix(registry): retire the git probe process tree on timeout #8115)确立的进程组回收方式、loopx/capabilities/change_quality/现有的错误出口,以及loopx/extensions/process_runtime.py现有的force_terminate_process有界回收路径,是依据。GIT_PROBE_TIMEOUT_SECONDS、_gitpytest -q tests/test_change_quality_git_budget.py_retire_owned_probe走共享retire_owned_processtaskkill卡住/非零仍返回原 timeout_force_terminate_windows_process_tree超时转RuntimeError,_retire_owned_probe只吞清理异常_close_owned_streams_before_deadline的 daemon close helper_git的except BaseException分支GitProbeUnavailable(ValueError)resolve_git_root、_required_git_text、_name_only、_hash_command_output的既有ValueError处理不变build_change_quality_scopereceipt.py、context.py两个消费者,确认它们只依赖既有ValueError出口与未变的返回结构;用只sleep 30的假 git 复现改前的无界等待;用带心跳子进程的包装 git 验证超时与取消两条路径都停住;在 POSIX 上把 runtime dispatch 指向 Windows 分支,用真实子进程注入卡住和返回非零的taskkill,超时与取消均能返回。聚焦测试8 passed;tests/extensions/test_process_runtime.py共享集 69 passed,3 个本机基线失败是psobservation(未改动 main 上同样失败),与本 diff 无关;ruff --select E4,E7,E9,F全绿。没有在原生 Windows 上执行进程树回收。Scope And Continuation
改动落在
loopx/capabilities/change_quality/scope.py的 Git 读取与收尾、loopx/extensions/process_runtime.py的共享收尾 helper,以及tests/test_change_quality_git_budget.py的故障注入。loopx/capabilities/repository_change_window/repository.py有 16 处同类未设预算的调用,属于另一个 owner,留作独立后续。早前本分支上还带着一个 replay 数值提交(
b8fd8c924,改adaptive_replay_planner.py与tests/test_explore_replay_runtime.py)。它属于另一个 owner,已拆回fix/adaptive-replay-numeric-inputs,本 PR 不再包含;原先正文与实际改动文件数不一致的问题随之消失。测试固定装置的平台条件(
c98f3fe27)评审指出的 P2 成立:
test_a_git_that_never_answers_stops_inside_the_budget写出的是无扩展名的#!/bin/sh脚本,Windows 上无 shell 的Popen既不会按 PATH 找到它,也不能保证执行它——那样要么落到真正的 Git 上立刻作答,要么抛启动错误,两条路都摸不到「预算用尽」这个断言。按评审给的第一个选项处理:给这条用例加上与旁边会话回收、Ctrl-C 回收两条用例相同的
os.name != "posix"条件。现在这个文件的三条 Git 进程用例在非 POSIX 平台上统一跳过,不会出现「看着覆盖、实际没测」的情况。代价写在这里:非 POSIX 平台上这个文件不再具备 Git 子进程覆盖,预算超时、会话回收与取消收回收只在 POSIX 验证,原生 Windows 的进程树回收仍未执行。Windows 分支通过
os.nameproxy + 真实子进程 + 假taskkill的 POSIX 注入验证了有界返回,但这不能替代原生 Windows 上的taskkill/Job Object 行为验证。本轮新修复(
c98f3fe27)_retire_owned_probe现在调用共享retire_owned_process:它在单个 cleanup deadline 内复用force_terminate_process,把 Windowstaskkill超时转成已确认失败的RuntimeError,并在同一 deadline 内用 daemon helper 关闭stdin/stdout/stderr。因此taskkill卡住、返回非零,或 descendant 仍持有管道时,原 Git 查询的TimeoutExpired/取消不会被清理路径替换或无限延长。原生 Windows 和 Job Object 路径仍未执行;leader 退出后的 descendant cleanup 仍是 unconfirmed,这一点与force_terminate_process的既有语义一致。retire_owned_process把这条 deadline 再切成两段:原生终止拿剩余时间的 80%,剩下的留给管道收尾。切片直接取调用方 deadline 的剩余量,不再套 50 ms 下限,所以传 20 ms 进去就是 20 ms,不会被抬成 50 ms 再返回;tests/extensions/test_process_runtime.py补了一条小于 50 ms 的用例守住这个约定。身份重建与 DCO 补齐(c98f3fe27)
本 head 的提交做过一次纯身份重建:author/committer 统一为
ChengLong GU <88359731+gcl-coder@users.noreply.github.com>,起因是首个提交沿用了本机全局配置里的私人邮箱。重建后合并提交失去 GitHub 生成合并的 DCO 豁免,因此给它补了Signed-off-bytrailer 并重建其后的提交。重建后新 head 的树与重建前相同(9dbf0b51a1),4 个文件字节未变。