Repository navigation
fix(leases): accept scoped native Windows worktree paths - #7817
huangruiteng merged 2 commits into
Conversation
Signed-off-by: Lihua <1017343802@qq.com>
Signed-off-by: Lihua <1017343802@qq.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
动机 / Motivation
受影响的是在 Windows 独立 Git 工作树内申请代码编辑租约的用户或 Agent。例如申请 docs/** 时,目录和普通文件都在已验证的工作树内,旧版仍因反斜杠与硬编码斜杠不匹配而拒绝;修复后同一任务可以取得租约并继续编辑。本次验证看到范围内申请成功、持久化回读和重放保留同一租约,同时越界链接与已知同目录冲突继续被拒绝。本 PR 不改变机器身份来源、租约权限、共享状态隔离、scope 语法或合并授权,也不交付已安装 Windows 产品的完整资格证明。
我先读取了 PR 的完整说明、现有讨论和关联 issue,再对照不可变基线 789694fa42972db17acd47aa2604b1907060028d 与当前 head 94253740d1fe82c4d04d9fe291674f3921bd0184。依据是 issue #7805,spec_revision 为 sha256:99a47b946bd3cb78c9073436ec466f4ba661e567257b021876dde70469ac8eac,读取时的 updated_at 为 2026-11-01T09:43:46Z。下面的 W7805-1、W7805-2、W7805-3 是便于逐项核对而给出的评审局部标签,原 issue 没有这些编号。
W7805-1 要求真实范围内的普通目录、文件可以通过观察;W7805-2 要求越界路径及不支持的重定向仍被拒绝;W7805-3 要求路径修复继续使用同一个无本地路径的工作树身份和租约规则。三项均有下述对应执行证据。本次交付解决的是这一个已复现的阻断,不以测试数量代替结果,也不把更上层长期协作验收写成已经完成。
改动思路 / Approach
保留在现有 TypeScript 文件系统观察 owner 中:这是所有已启用工作树租约都必须满足的正确性不变量,无需增加配置、状态或第二个判定服务。当前边界是修复独立工作树的路径归属判断,并把实际扫描普通文件和目录的既有测试纳入原生 Windows lane。
旧路径在 realpath(full) 后拼接 ${root}/ 做字符串前缀判断。Windows 的 realpath 返回反斜杠,所以即使正常子项也会被拒绝。新路径使用 Node 的 path.relative(root, physicalPath),再拒绝空关系、父目录、以父目录开头的关系和绝对关系。空关系不能作为普通子项通行,跨盘关系也不能借此变成合法子目录;名称本身以两个点开头的合法文件仍应允许。
该做法复用了同文件 requireInternalFileLink 已使用的相对路径边界表达。两处分别处理普通物理子项和直接文件链接,后者还有逐段检查、目标 scope、禁止目录链接等额外义务;把它们强行合成通用策略会扩大本次修改。局部变量从 relative 改为 scopePath,也消除了对导入函数的遮蔽。这个有界整理已经随修复完成,未发现值得增加框架或新配置的剩余重构。
| 触发 | 基线 | 当前 head |
|---|---|---|
| Windows 范围内目录与普通文件 | 错报 redirected path | 申请成功并持久化 |
| 已知越界链接、同一物理 checkout 的冲突 | 拒绝 | 继续拒绝 |
| 未提供 write_worktree 的旧租约路径 | 既有规则 | 完整响应、回读和历史保持一致 |
这是源代码与下述执行结果的比较表,没有把模拟运行当成原生 Windows 截图或安装证据。
具体改动 / Changes
生产逻辑仅修改 observeLeaseWorktree 所属的 task_lease_workspace.ts:先按原有 scope matcher 选择实际目录项,再用相对路径确认其物理归属。目录仍递归扫描,ignored 文件仍检查;只有原有 ENOENT 并发删除分支被忽略,其他错误继续上抛。Git 管理目录、独立 worktree 根目录、origin codec 和 MachineGuid/inode 摘要的逻辑没有变化。
调用链已追到 executeTaskLeaseAcquire:仅当请求显式携带 write_worktree 时,它要求 canonical authority 和非空 scopes,调用观察器,将既有 LeaseWorkspace 交给 canonical acquire owner。失败走现有 typed envelope,申请被拒绝时实际 authority 与 journal 均保持不变;通过观察也不会绕过 Todo 所有权、仓库身份或同物理 checkout 的冲突检查。sameLeaseCheckout 和 leaseWorkspace 继续消费原先四个字段,没有新权限或新持久化格式。
既有 observed Git origins use the Todo repository identity contract 测试现在创建 docs/guide.md 并通过实际 docs/** scope matcher 扫描目录和文件,替换了原来永远返回 false 的 callback。原 Windows 分支此前没有经过出错位置,这是本次最有价值的回归覆盖。.github/workflows/python-tests.yml 把此套件加入原生 Windows 的现有命令,test_windows_lane_coverage.py 同步精确清单;未修改 runner、激活条件、非阻断选项或 Node 版本。四条路径全部读取,最终 diff 为 +16/-8,生产代码 +9/-6。
我独立执行的验证如下,均以当前 head 为准,作者提供的计数没有混入:
- PASS:
npm run typecheck:control-plane;6 个相关 TypeScript 套件共 520 tests,通过一次性的真实 PostgreSQL 服务运行,零 skip。包含工作树观察、native CLI、租约生命周期及 PostgreSQL authority-store integration;服务随后关闭,未访问活动 Goal 数据。 - PASS:
uv run --extra test python -m pytest -q tests/test_task_lease_worktree.py tests/control_plane/test_task_lease_cli.py tests/control_plane/test_task_lease_argument_recovery.py tests/test_cli_entrypoint.py,163 passed。 - PASS:
scripts/ci/test_windows_lane_coverage.py、scripts/ci/test_impact_plan.py、tests/test_python_ci_workflow.py,556 passed、33 subtests passed;检查了真实 YAML 中单一命令和精确清单的绑定。 - PASS:同一实际 Git/worktree fixture、固定时钟,在基线和 head 经 native JSON CLI 运行 File、SQLite。macOS 每后端每版本各 9 次调用,比较完整 response、authority 和 history;仅归一化独立 runtime 路径及 provider revision 的随机部分,保留 generation、状态、诊断、ownership、scope、工作树摘要和完整 receipt。两后端全部一致,归一化摘要分别为
ac3a53a7e0b732e3c813e4cd2c61b0dc4f4c4411072a83572e6d98dcd3613cea和ff1a3e663cbfb71251b0d52b9b5861d56b6fa0dab2886413c49fca19aa2e7c9e。 - PASS:同一 native JSON CLI、真实 File/SQLite store 以及真实 Git/文件系统,通过未改动的生产观察器源码投影到 Windows 命名空间;使用真实
path.win32,仅替换观察器的路径/平台 IO 适配与合成 MachineGuid。每后端基线 5 次、head 9 次,旧版正常 scoped acquire 确实返回 redirected path,head 则 applied;拒绝外部链接后修复,可以继续申请,重放不新增事务,同 checkout 的新申请被拒绝,独立 checkout 保留 integration advisory。对未覆盖邻项允许的窄 scope,在 scope 内新增文件后,head 重放仍保留原 receipt,基线暴露同一误拒绝。关闭 worktree 模式的完整响应及状态历史也在 Windows 投影中一致。 - PASS:另外 17 个 Windows 路径关系用例分别在基线和 head 执行真实生产观察器,含大小写别名、空格与 Unicode、UNC、父目录、跨盘、跨共享、同名前缀目录和未来文件的重定向祖先。6 个预期误拒绝得到修复,其余 11 个保持预期结果。独立的“应允许范围内申请”oracle 又通过 native CLI 在旧版实际失败(exit 1),修复后的相同调用通过;并非只比较新 helper 的返回值。
- PASS:
loopx canary premerge --from-git-diff --git-diff-base 789694fa42972db17acd47aa2604b1907060028d,5 个 direct checks、19 个 selected checks;包括 RFC bilingual、changed Python compile、现有 canary 和 public boundary。另跑 Ruff、diff-scoped semantic advisory、DCO 与 4 路径公共边界扫描。两个提交均有 sign-off,advisory 没发现受支持的新词汇载体;这不等于动态语义不存在。
不可变 fixture 摘要为 3c5d8ff1f3c45c8701bf4efbde2d04af97d8dc2a2e1aeffe0f949d46be96782d。Mac CLI 和 Windows 命名空间 CLI 共 64 次调用另加一次预期失败的敏感性运行;上面的 17 例是独立的观察器边界探针。初次本地验证中存在错误测试路径、测试 harness 缺少 VM flag/source receipt 及模拟绑定错误;这些运行没有覆盖对应义务,保留为失败记录,修正的是评审脚本。最终执行使用正确入口和同一 fixture/oracle,没有改变产品代码或放宽断言。
对主干的风险 / Risks
最需要防止的是修复 Windows 误拒绝时把真正的越界误判为内部目录。新判断同时拒绝父路径、根自身及跨盘绝对结果;基线/head 的路径探针覆盖这些相反方向。真实文件系统测试另覆盖 ignored 外链、未创建叶项的祖先重定向、链接目标的 scope 和回退后重试。现有链接检查独立保留,因此通过正常子项不会降低链接约束。同一 checkout 的冲突、不同 checkout 的 advisory、repository mismatch 与 retained receipt 也由真实 provider 测试覆盖。
这不是新增的 default-off 功能。worktree 模式依旧由显式参数启用,纯 legacy acquire 没有调用观察器;测试证明该入口在两个 provider 上的完整可观察语义相同。对于已启用模式,路径归属检查是强制安全条件,不是“建议”。PR 说明已披露旧、新默认行为的差异,错误文本继续是通用工作树诊断,没有按 PR 标题或 prose 子串推导权限。已有 CLI 足以完成原任务,用户无需增加配置、复制已知信息或额外确认;非法链接修复后可直接重试。
这里没有原生 Windows OS 实跑,因此没有证明 Windows ACL、junction/设备路径、原生注册表访问或平台安装的全部组合。Windows 命名空间执行和 macOS 真文件系统分别给出有用但不同的证据,不能互换。PR 已补进现有原生 Windows lane;平台发布资格仍由该 lane 及维护者处理。我遵守当前 wait_for_ci=false,没有获取、轮询或等待 CI。作者提供的较早 CI 清单失败和后续待确认信息保留为 diagnostic、required=false、原因 unresolved;上述独立本地验证只证明当前代码与清单,不追认早先远端失败的原因。公共扫描的唯一提示来自空的临时 runtime 尚未创建,不是源码泄漏。
我的整体评价 / Verdict
APPROVE,未发现需要阻断此 head 的具体缺陷。修复将 Windows 的正常代码编辑任务从误拒绝带到可取得、回读、重放的租约,并保留 scoped refusal 与所有权边界;这比仅添加一个序列化字段或静态测试有实际使用价值。代价限制在现有 owner 的路径判断与相应测试,没有新服务、新状态或迁移负担。复用现有 scope/identity/lease owner 的判断符合仓库边界,本次有界重构已完成,扩大为通用路径抽象没有证据收益。
未改变渲染界面;检查的是 task-lease acquire 的 CLI、TypeScript 观察器和已有 File/SQLite 后端,结果通过现有命令直接回读。原生 Windows 操作系统实跑及已安装 App 未验证;Windows 命名空间执行保留了模拟边界。长期执行方面,本次仅证明合法工作能继续、拒绝后可修复重试、重放不重复写入;未测长程 soak、安装采用或长期成本收益,也不据此宣布上层目标完成。关联经验中“使用既有 owner、让真实消费路径完成有用工作”的指导已用到上述 acquire/持久化/重放对照,经验本身不作为运行通过的证据。
本结论只覆盖 94253740d1fe82c4d04d9fe291674f3921bd0184。APPROVE 与后续 merge readiness、原生平台资格和维护者合并授权分别判断;没有执行合并或 bypass。
English verdict: APPROVE — head 9425374; the Windows scoped-path false rejection is fixed while containment, lease identity, refusal and replay behavior remain covered. Local checks and real-provider tests passed; native Windows OS execution and installed-product qualification remain unverified. No merge performed.
Goal And Delivered Outcome
Native Windows
--write-worktreeobservation rejected existing in-scope entries becauserealpath()returns native separators while the containment test appended a literal slash. Use the existing Node path owner to compare the physical child's relative location, preserving rejection of the root, parent escapes and absolute cross-root relations.Closes #7805. Target:
loopx-project/loopx:main, based on789694fa42972db17acd47aa2604b1907060028d.Author Declaration
Scope And Continuation
The TypeScript filesystem observation owner keeps its policy and path-free result. The local
relativestring was renamed toscopePathso the existing imported path operation can be used. No stored scopes, repository identities, lease comparison rules or authority-store semantics change.Compared the maintained MIT-licensed is-path-inside design and Node path documentation. The fix reuses the relative-path boundary convention already present in this owner.
Bounded refactoring: remove the local name shadow and reuse native path relations within the existing observation loop. The Windows workflow retains its jobs, required gates and timeout budgets; it gains this suite.
Validation
Current head:
94253740d1fe82c4d04d9fe291674f3921bd0184.Runtime/CLI validation below remains pinned to
54f4f258f8530abc2d0d516d0495fdda8c8fbd91; the follow-up changes only the existing CI qualification manifest.Every commit is DCO signed. No private state, raw logs, credentials or generated artifacts are included.
CI manifest follow-up
The first hosted
changesjob failed its exact Windows-suite contract because the workflow addition was not yet included inscripts/ci/test_windows_lane_coverage.py. The same failure was reproduced locally (one failure in the two-case module). Commit94253740d1fe82c4d04d9fe291674f3921bd0184extends that existing manifest; its exact-list and fail-closed assertions remain intact.All 21 CI preflight unit tests pass after the fix. Current-head standard premerge passes four direct checks, changed-Python compilation and all 19 selected checks (ten catalog canaries, eight risk smokes and the public-boundary scan). Ruff, bilingual diff and the focused public scan pass. Native Windows CI has since passed at this new head; full hosted CI remains held by a separate Dashboard check. The earlier manifest failure is retained as historical evidence.
Remote Windows qualification
At unchanged head
94253740d1fe82c4d04d9fe291674f3921bd0184, the native Windows job completed successfully, including the unconditionally executed host-qualified control-plane step containingtask_lease_workspace.test.ts. This resolves the native Windows qualification hold for the scoped observation path.The separate Dashboard shard failed the frozen loaded-lens switch budget: one measured switch was approximately 1,258 ms against the unchanged 1,000 ms ceiling. That failure and the failed aggregate remain open; this update does not establish its cause or pristine-base parity, and it does not claim full CI readiness or installed App acceptance.