Repository navigation
fix(leases): treat case aliases as overlapping write scopes - #7252
JasonBuildAI wants to merge 4 commits into
Conversation
Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
Independent AI reviewThis is an independent AI review of the exact head Verdict: APPROVE WITH NOTES Earlier in this review, the implementation was found to miss mixed-case character-class ranges:
|
Signed-off-by: JasonBuildAI <jasonbuildai@gmail.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
动机
在大小写不敏感文件系统上并行修改同一仓库的开发者和 Agent。
例如两个执行者分别申请 src/Σ.ts 与 src/ς.ts;本机文件系统确认它们指向同一文件,但修复后仍会同时准入。ASCII 文件名与字符类反例已修复,Unicode 别名的核心风险仍在。
实际 File、SQLite 的普通 acquire 和原子 Todo claim 中仍有6个 Unicode 反例同时保有两份 active 租约;ASCII、混合大小写字符类及拒绝不写状态的检查通过。
本次只审查既有同 Goal/仓库的写范围互斥,不扩大为符号链接、跨 Goal 的物理锁,也不修改原范围拼写、请求身份、owner、TTL、CAS 或独立 worktree 的 advisory 模式。
改动思路
范围冲突应由既有 TypeScript acquire decision 统一决定,File、SQLite、共享服务与原子 claim 复用它。原样保留字符类后测试候选大小写变体,能修复 [A-b] 和 [!A-b];但 literal/prefix 仍以 toLowerCase 当作等价关系,不能覆盖已约定的实际主机大小写别名。
classMatch 保留原区间,对原字符及单字符大小写变体先求并集再取反;fnmatchcase、literal、目录前缀与双 glob 前缀增加大小写比较。比较副本改变,持久化及请求身份不转换。 保留唯一 decision owner,补全比较专用的大小写等价处理并覆盖 literal、目录和 glob 前缀;继续保留原区间、否定顺序和原始范围/请求拼写。仅把 lower 换成 upper 或在 CLI 再补一套判断均不足以建立一致契约。
本次只审查既有同 Goal/仓库的写范围互斥,不扩大为符号链接、跨 Goal 的物理锁,也不修改原范围拼写、请求身份、owner、TTL、CAS 或独立 worktree 的 advisory 模式。
具体改动
[P1] Unicode 大小写别名仍可取得两份互斥租约。 同一 macOS 文件的 src/Σ.ts/src/ς.ts(以及 src/ß.ts/src/SS.ts)在这里仍不相等,目录和 glob 前缀也用同一 lower 假设。独立实际 File/SQLite CLI 复现:普通 acquire 两种 provider 各有这2例,原子 Todo claim 各有 Σ/ς 1例,第二次都 exit 0、两份租约都 active、provider 状态改变;实际文件系统已确认两种写法对应同一文件。ASCII 和新字符类测试通过仍不能满足既有 “Host/path case aliases remain overlapping.” 契约。这是该修复仍遗漏的原缺陷。请在现有比较 owner 中统一覆盖 Unicode 大小写别名,保持原范围/请求身份和字符类区间/否定语义,并补齐两种 provider 的 acquire 与 atomic claim 回归。
不可变已接受规格:docs/reference/canonical-lease-renew.md,版本 28fa39bd8a2f172c8a1cfde5b144dbd249f165a8。先按原规格审查;下列是本审计映射标签,非伪造原编号:
host-case-alias-overlap(not_met):Host/path case aliases remain overlapping.preserve-scope-identity(implemented):Keep stored scope spelling and request identity unchanged.existing-canonical-lease-boundary(implemented):Use the shared canonical acquire path and retain owner, TTL, CAS, replay and independent-worktree advisory boundaries.
3 files +97/-21; production +34/-21。全差异、原调用者、完整讨论、DCO与公开边界已读。字符类之外的全部literal/目录/glob分支一起评估,不能只看最后一笔commit。
classMatch(loopx/control_plane/work_items/task_lease_acquire_decision.ts:143):对原区间逐项判断候选集合;否定发生于全部候选求并集之后,保留 [A-z] 的原标点和 [!A-b] 排除语义。
fnmatchcase(loopx/control_plane/work_items/task_lease_acquire_decision.ts:175):memoized glob 匹配把 foldCase 传给字符类;literal 分支只做逐字符 lower 比较,Σ/ς仍不等。
scopePairOverlaps(loopx/control_plane/work_items/task_lease_acquire_decision.ts:214):literal、目录、单 glob 和双 glob 路由到同一比较;lower 并不是实际主机全部大小写别名的等价关系。
decideTaskLeaseAcquire(loopx/control_plane/work_items/task_lease_acquire_decision.ts:410):原 owner/replay/版本检查先执行,完整其他有效租约通过范围比较决定 conflict 或 advisory,再由原执行层提交。
对主干的风险
独立 Python 28通过;native workspace 30通过/1项环境跳过;独立 PostgreSQL 16 三套隔离数据库集成合计375通过;标准 premerge 5项直接/编译检查和17项选中检查通过。16组不可变基线/当前实际 CLI 配对共160个进程,另外读取当前主机真实文件别名与纯 API 的 literal/目录/glob 分支。
macOS 实际同 inode 的 Σ/ς 与 ß/SS 路径都被当前 literal 比较漏掉;普通 acquire 在两种 provider 各漏2例,原子 claim 各漏1例。两份租约确实 active,provider 字节确实改变,不是只看纯函数。
Unicode 互斥缺口阻止批准。这是修复范围内仍未解决的原缺陷,不称为新引入回归。PostgreSQL集成通过不证明该主机别名反例已在 PostgreSQL 执行,native workspace 的1项 PostgreSQL 环境跳过仍单独披露;没有原生 Windows、已安装产品或 live 编辑资格。
实际CLI使用不可变基线产品源码;seed和物理路径在两臂间完全还原。原始完整退出/输出/receipt/字节摘要私有保留;公开只给合成反例,不发布本地路径、活跃Goal或原始运行日志。
CI按当前契约未查询、轮询或等待。未执行安装、模型调用或合并。
我的整体评价
REQUEST_CHANGES,对应 7252@7815a91fdd1b850fbcdebdf839ecac159b6d729f。核心host-case-alias验收尚未满足;请求在现有owner补齐,不能以部分ASCII修复宣称全部解决。
范围冲突应由既有 TypeScript acquire decision 统一决定,File、SQLite、共享服务与原子 claim 复用它。原样保留字符类后测试候选大小写变体,能修复 [A-b] 和 [!A-b];但 literal/prefix 仍以 toLowerCase 当作等价关系,不能覆盖已约定的实际主机大小写别名。
本次只审查既有同 Goal/仓库的写范围互斥,不扩大为符号链接、跨 Goal 的物理锁,也不修改原范围拼写、请求身份、owner、TTL、CAS 或独立 worktree 的 advisory 模式。
长期工作:preserved,保留原重放、拒绝与后续读回,未测多轮净收益;使用体验:not_yet_proven,实际 File、SQLite 的普通 acquire 和原子 Todo claim 中仍有6个 Unicode 反例同时保有两份 active 租约;ASCII、混合大小写字符类及拒绝不写状态的检查通过。
已检查既有 CLI 完整输出、错误/恢复正文及原调用者;没有页面、布局、组件或视觉素材变更,截图与构图检查不适用。
Unicode 互斥缺口阻止批准。这是修复范围内仍未解决的原缺陷,不称为新引入回归。PostgreSQL集成通过不证明该主机别名反例已在 PostgreSQL 执行,native workspace 的1项 PostgreSQL 环境跳过仍单独披露;没有原生 Windows、已安装产品或 live 编辑资格。
有界重构:保留唯一 decision owner,补全比较专用的大小写等价处理并覆盖 literal、目录和 glob 前缀;继续保留原区间、否定顺序和原始范围/请求拼写。仅把 lower 换成 upper 或在 CLI 再补一套判断均不足以建立一致契约。 审批、approval-closeout、merge-readiness和维护者合并权限各自核验;本评审不合并。
English verdict: REQUEST_CHANGES. REQUEST_CHANGES. The ASCII/class fixes work, but same-file Unicode case aliases still receive two active exclusive leases through real File/SQLite acquire and atomic claim. This is an unresolved defect in the stated repair scope, not a newly introduced regression. Complete the comparison-only equivalence without rewriting stored scopes or request identity.
7815a91 to
76c6243
Compare
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; model=gpt-6.1-sol; provider=OpenAI; runtime_reported; reasoning_effort=max.
动机
REQUEST_CHANGES:Unicode 字符类仍会为同一文件授予两份互斥租约。 结论仅针对 76c62434024cdbe895c457d5b147f610195580c3,已重新审查完整提案与上次评审之后的修复。
在同一仓库中以普通互斥租约并行修改文件的 Agent。
先持有 src/Σ.ts,再申请 src/ς.ts:基线允许双租约,当前头会拒绝;但先持有 src/[ς].ts 再申请 src/Σ.ts,当前头仍允许双租约。
普通 Unicode 字面量已恢复互斥;字符类中的 Unicode 别名仍未满足同一文件只能有一份互斥租约的要求。
本次不扩展为跨 Goal 或物理符号链接锁,不改存储拼写、请求身份、owner、TTL、CAS 或独立 worktree 的协作语义。
这是一条并发写入安全边界:第二个 Agent 收到成功回执后,会合理地认为自己能够独占修改文件。旧评审的普通 Σ/ς 和 ß/SS 字面量反例在当前头已修好;本次发现的是同一承诺在字符类分支仍有遗漏,不把它称为新引入的回归,也不把已有部分修复当作整个问题已解决。
改动思路
比较必须留在现有 TypeScript acquire decision,供全部 provider 与原子 claim 共用;本地 token 化可处理多字符折叠,但字符类仍只扩展输入字符,未建立对称等价。
当前边界:在同一比较 owner 补齐字符类等价、长度变化和否定语义,保留原范围及请求身份;不在 CLI 或 provider 再加一套规则。
原范围与请求拼写保持不变,冲突判断使用比较视图。为了处理 ß/SS 这样长度改变的字面量,局部 matcher 先拆 literal、star、question、class,再做带记忆的匹配。这仍属于原有 TypeScript owner,不增加第二套 provider 判断,整体成本与互斥失效的严重性相称。
下面是实际原生 CLI 观察,两个 provider 与两种申请入口结果一致:
| 第一份范围 / 第二份范围 | 不可变基线 | 本 head |
|---|---|---|
src/Σ.ts / src/ς.ts |
两份 active | 第二次拒绝,状态不变 |
src/[ς].ts / src/Σ.ts |
两份 active | 仍有两份 active |
src/[ß].ts / src/SS.ts |
两份 active | 仍有两份 active |
把整个 glob 先折叠也会破坏原字符区间和否定语义;修复应留在现有 matcher,比较字符类成员的等价关系,并处理等价名称所需的消费长度。
具体改动
[P1] 字符类只变换输入字符,遗漏反向 Unicode 等价与长度变化。 classMatch 对输入 Σ 构造 Σ/σ 候选,却不会得到字符类中的 ς;fnmatchcase 的 class 分支还固定消费一个单位,因此 [ß] 无法覆盖实际同文件别名 SS。对两种模式各执行 File、SQLite 的普通 acquire 与原子 Todo claim,共8例第二次退出0,读取到两份 active 租约,canonical 状态和 provider 字节都改变。另2例 [!ς]/Σ 发生相反的错误拒绝。当前主机实际文件读回确认 ς/Σ、ß/SS 分别指向同一个物理文件。
请在原比较 owner 中建立对称的字符类等价及所需的长度消费,保留原区间、否定顺序、存储拼写与不可变请求身份。至少增加上述两类正向、反向和否定回归,在两个 provider 的 acquire 与 atomic claim 中断言拒绝不写状态。仅将 lower 换成 upper 或再添一个单向候选不能完成这个边界。
评审前读取的接受依据是 https://github.com/loopx-project/loopx/blob/28fa39bd8a2f172c8a1cfde5b144dbd249f165a8/docs/reference/canonical-lease-renew.md,不可变版本 28fa39bd8a2f172c8a1cfde5b144dbd249f165a8;并读取 issue #7243、原评审与作者修复回复。以下 id 是本评审对原条款的映射标签:
host-case-alias-overlap— not_met:Host/path case aliases remain overlapping.preserve-scope-identity— implemented:Original request spellings remain immutable identity; retained scopes are not rewritten by comparison.canonical-lease-boundary— implemented:Atomic claim shares canonical acquire facts and decision; owner/TTL/CAS/replay and cooperative worktree boundaries stay distinct.
关键代码讲解
classMatch保留原区间,再测试输入的几个大小写候选。问题位于206–207行:候选变换既不对称,也不能表示多字符等价。fnmatchcase用 token 和 memo 处理变长 literal;232–235行的 class 分支仍固定消费一个单位,形成不同的等价语义。scopePairOverlaps统一路由 literal、目录、单 glob 和双 glob。普通字面量及目录/前缀的折叠修复保留在这一 owner。decideTaskLeaseAcquire沿用 owner、重放、版本和有效 holder 判断,再交原执行层提交;错误的 overlap 结果因此同时影响 standalone acquire 与 atomic claim。
完整差异为3文件,+234/-48;生产代码 +111/-47,其余为现有两份回归文件的扩展。没有新增持久化字段、配置、协议或 Python 决策 owner。新增 GlobToken 是本地闭合集合,语义 advisory 与标准检查已执行。
对主干的风险
独立26项 Python scope 回归通过,47项相邻 TypeScript 租约测试通过,最初1项 PostgreSQL 环境跳过单独保留。完整仓库 TypeScript 套件5,518通过、75项环境条件跳过;根 typecheck 和 scoped Ruff 通过。隔离 PostgreSQL16 的31项 workspace、369项 store、2项 service 测试全部通过,三个独立数据库及拥有的实例已停止。这些通用集成不能覆盖已经实证失败的字符类安全条件。
使用同一份独立合成 case 定义,在不可变基线 e1c42d1605669e6c03004585b070c70b5cb6a57e 与当前头各执行18组实际 CLI 案例:literal 修复、普通字符类、无关路径、正/否定 Unicode 字符类,以及两种 provider 的原子 claim。完整输出、退出、canonical 读回与字节摘要保留;跨不同 fixture 不冒称逐字相同。成功的 harness 退出仅说明收集完整,不把10个不满足独立断言的业务结果记为通过。
标准 premerge 的5项直接检查和17项选中检查通过;精确范围质量收据 cqr_664e0bbc21e3f555aca7 为 FAIL / invalid_receipt,包含本次 required 互斥验证失败。safe-fix 允许,但本评审未修改作者源代码。携带 Goal 绑定的原生 premerge 复验退出1、quality_invalid_receipt,所有自动子检查仍通过。基础检查绿色不能清除此收据或原互斥要求。没有查询、轮询或等待 CI,没有合并或处分旧评审。
语义与 CI 对齐
当前义务是既有的 “Host/path case aliases remain overlapping.”,触发条件是字符类范围覆盖真实同文件 Unicode 别名。最小修复及回归方向如上;复验可先运行 pytest tests/control_plane/test_canonical_lease_scopes.py -q 和现有 workspace TS 测试,再通过原生 CLI 补全两种 provider、两种申请方式的字符类反例。已有测试中 [Σ]/ς 的单向成功不能替代反向和长度变化。原 provider、CAS、默认配置与 worktree advisory 边界保持各自含义,失败拒绝是机器约束,不是文字建议。
我的整体评价
REQUEST_CHANGES,限 76c62434024cdbe895c457d5b147f610195580c3。 此 head 有真实部分价值,但 original case-alias-overlap 验收仍未满足。长期工作与使用体验维度均为 not_yet_proven:互斥回执仍可能隐藏同文件并发写入;不能以普通字面量已修复、更多测试或前次问题已解决来批准。
检查的是现有 CLI 和规范来源捕获路径,没有页面、布局、组件或图片变更;视觉构图检查不适用。
未独立验证原生 Windows、已安装 App、多轮协作收益或完整费用;PostgreSQL 通用集成通过不等于这些 Unicode CLI 反例已在 PostgreSQL 执行。
有界重构判断是保留唯一 shared decision 和本地 token matcher,在同一边界补齐等价语义,不增加第二套规则或状态。原请求、存储格式和回执兼容性值得保留,没有理由新建版本分支。采用的历史评审经验用于检查实际失败与恢复路径;它不认证此实现,也不证明记忆带来收益。修复后需要重新审查完整精确头及 required 质量验证。
English verdict: REQUEST_CHANGES at 76c6243. Literal Unicode aliases are fixed, but [ς]/Σ and [ß]/SS still admit two active exclusive leases through both real File/SQLite acquire and atomic claim. Eight double grants and two negated-class overblocks remain. Preserve original scope/request identity while repairing class equivalence; exact-scope quality remains FAIL despite passing repository checks.
| }; | ||
| const candidates = new Set([value]); | ||
| if (foldCase) { | ||
| for (const candidate of [value.toLowerCase(), value.toUpperCase(), caseFold(value)]) { |
There was a problem hiding this comment.
[P1] 字符类的等价关系仍不完整。输入 Σ 的 lower/upper/fold 候选不会包含类成员 ς,且 class 分支固定消费一个单位,故 [ß] 也漏掉同文件别名 SS。独立实际 File/SQLite CLI 中,src/[ς].ts→src/Σ.ts 和 [ß]→SS 在 acquire 与 atomic claim 共8例都允许第二份 active 互斥租约并改变 provider;当前主机已确认这些拼写对应同一物理文件。普通 Σ/ς 字面量修复通过不能满足字符类分支的原 case-alias 契约。请在现有 matcher 中补齐对称等价、长度消费和否定语义,保留原范围/请求身份,并给两种 provider、两种入口增加拒绝不写状态的回归。
There was a problem hiding this comment.
已在同一 matcher 内补齐字符类的对称等价、长度消费和否定语义,没有新增第二个比较 owner。
实现(loopx/control_plane/work_items/task_lease_acquire_decision.ts)
classMemberMatches:类成员与比较文本按同一个caseFold折叠后相等即匹配,不再只对输入构造单字符 lower/upper 候选。类成员ς因此能匹配Σ(反向亦然),成员ß的折叠ss能匹配输入SS。classMatchLengths:class 分支不再固定消费一个单位;折叠改变长度时按 1–3 个码元尝试消费,[ß]可以消费SS。- 否定:先求全部等价候选的并集再取反,
[!ς]因此正确排除Σ(不再出现反向误拒)。 - 原区间(
[A-b]、[!A-b]、[A-z])的区间与否定顺序、comparisonWriteScopes的持久化拼写、请求身份均未改动。
回归
- TS
tests/control_plane_ts/task_lease_workspace.test.ts:[ς]↔Σ、[ß]↔SS双向覆盖,[!Σ]/ς、[!ς]/Σ、[!ß]/SS的排除覆盖。 - Python
tests/control_plane/test_canonical_lease_scopes.py:在 File 与 SQLite 两种 provider 上,普通 acquire 与原子 Todo claim 都加了src/[ς].ts→src/Σ.ts、src/[ß].ts→src/SS.ts的拒绝且 provider 状态不变的断言;另加否定类允许(不冲突)的断言。
验证(head daeed87d7,已 rebase 到 c6462f902)
node --no-warnings --experimental-sqlite --experimental-strip-types --test tests/control_plane_ts/task_lease_workspace.test.ts:26 tests,24 passed,2 skipped(PostgreSQL 与符号链接环境门控)pytest tests/control_plane/test_canonical_lease_scopes.py -q:40 passed(原 26 例 + 新增 14 例)- 分支已同步
upstream/main(c6462f902),此前的 BEHIND 状态已消除。
字符类成员按定义仍是单个字符,所以 [SS] 不会被当作 ß 的等价写法;这一层语义我没有扩大。
There was a problem hiding this comment.
补充:main 在我推送后又前进了一次,所以这个分支又做了一次纯 rebase 同步。
- 最新 head:
d9fdf9906(仍是同样 4 个提交,内容未变) - 基于:
8cb50a2cd - 复跑结果不变:TS 24 passed / 2 skipped;
pytest tests/control_plane/test_canonical_lease_scopes.py -q40 passed
76c6243 to
daeed87
Compare
Hard-lease scope comparison was case-sensitive even though the contract requires host/path case aliases to remain overlapping. Two agents could therefore acquire active exclusive leases for one physical file on default case-insensitive Windows and macOS filesystems. Case-fold only admission matching, including literals, prefixes and glob character classes, while leaving retained scopes and request identity in their original spelling. Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
Folding mixed-case range endpoints could still miss case aliases such as C inside [A-b], so two exclusive leases could remain active. Test the original character and its single-code-unit case variants against the unmodified class instead. Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
Character-class membership compared the input against the class body one way only, so a class member that folds to the compared text (a class member ς against Σ) missed, and a member whose fold changes length ([ß] against SS) could never match because the class branch consumed exactly one unit. Membership is now a symmetric fold equivalence over both literals and single-code-point range variants, negation applies after that union, and a class may consume the number of units its fold needs. Original range intervals, negated semantics, stored spelling and request identity are unchanged. Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
daeed87 to
d9fdf99
Compare
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent · gpt-6.1-sol · OpenAI · declaration_source=runtime_reported · reasoning_effort=max.
详细中文评审
动机
在同一 Goal 和仓库中申请普通独占写范围的 Agent 与集成方。 旧头的普通字面量和字符类问题已有修复;当前头仍把单元素 Unicode 区间漏判,并把原本重叠的负类续接改成不重叠,真实 CLI 因此准入双租约。 File/SQLite 的 acquire 与原子 claim 共 12 个新反例仍同时持有两份 active 租约,4 个否定区间控制错误拒绝;36 组同案例两臂及物理文件别名已核。 只审既有 Goal 内的范围冲突和原身份,不扩成跨 Goal 物理锁,不改 owner、TTL、CAS、请求身份或独立 worktree 的协作权限。
REQUEST_CHANGES,精确头 d9fdf9906a262ded01a7852ce5b738c0f0496ec2。 原 [ς]、[ß] 问题确已修复,不沿用旧头结论;本次独立审计发现两个当前阻断项。先读的规格是 docs/reference/canonical-lease-renew.md@28fa39bd8a2f172c8a1cfde5b144dbd249f165a8 与 #7243。host-case-alias-overlap 尚未满足;preserve-scope-identity 和 existing-canonical-lease-boundary 的既有身份/事务边界保留。
改动思路
对范围建立比较副本,在既有 TypeScript acquire decision 内统一处理大小写折叠、字符类及变长匹配,保留原请求拼写和持久化身份。这个 owner 仍合适,File、SQLite、共享 provider 和原子 Todo claim 不应各自再判断一次。全局折叠 pattern 会改变字符类集合,简单正则 ignore-case 也不能完整覆盖 ß/SS;现有局部 parser/memo 方向合理,但区间分支仍只看输入候选,负类又把所有长度的候选压成一个布尔否定,导致下述真实错误。
具体改动
完整三文件 +330/-45,其中生产 +153/-44,仍在既有 624 行 decision 模块内;另两处为边界测试,无配置、provider 或 schema 迁移。两个独立发现如下,均已通过真实 CLI 与 provider 读回验证:
[P1] 单元素 Unicode 区间仍漏判同文件别名。 src/[ς-ς].ts→src/Σ.ts 和 src/[ß-ß].ts→src/SS.ts 在 File/SQLite 的普通 acquire、原子 claim 共 8 例都 exit 0、两份 lease 保持 active、canonical/provider 字节改变。本机实际文件分别确认为同一文件,原生 glob 也确认单元素区间覆盖原成员。因此 [ς] 已修好不能说明等价的 [ς-ς] 已满足同一约定。另 4 个 [!ς-ς] 控制错误拒绝。这是原修复范围余缺;请在当前 range 成员语义内补齐对称等价与长度消费,保持原区间、否定和请求身份。首次 lease 激活后再用实际 todo add 创建新 Todo,两种 provider 也都漏判,不能靠旧项目成员列表隔离它。
[P1] 负类的长候选压掉合法单字符续接,产生新回归。 src/[!ß]S.ts 本来匹配 src/SS.ts:首个 S 不是 ß,后一个 S 接续匹配。原生文件系统 glob 独立确认这一点;不可变基线 8cb50a2cd2f382378264545b7168f52967e2fe9e 的四条 File/SQLite acquire/claim 路线全部拒绝且不改 provider,当前头四条全部准入双租约。代码发现两字符 SS 可折叠成 ß 后,直接令整个负类失败,漏掉一个 S 的合法续接。请按可行消费长度和后续 token 处理,而不是用任意更长候选消灭所有单单位路径;同时保留 [!ß]x 的正确排除。
关键代码讲解
classMemberMatches@226:普通成员按对称 fold 判断,因此原简单类修复通过;但candidate超过一个码点便退出,range 只试输入的单字符变体,没有把区间中的 ς/ß 等价成员纳入。range 与同一成员的普通类于是分裂。fnmatchcase@256:memo 保留 token/输入位置,正类按多个消费长度递归;负类在 @279 仅以lengths.length === 0决定,尚未结合后续 token 就丢弃合法的一单位路径。第二个发现准确落在这个分支。decideTaskLeaseAcquire@506:原 mode/owner/version/replay 检查仍在范围比较之前,完整其他租约经同一 matcher 决定 conflict 或 advisory,再由原 provider 事务提交。两个错误真正进入了这个共享准入路径,不只是导出的纯函数输出不符。
独立同定义每臂 36 组实际 CLI,包含原修复的 12 个 literal/简单类路线通过、8 个 range 漏判、4 个新负类续接漏判、4 个否定 range 过阻,以及普通类/无关路径控制。原生物理别名和 glob 作为独立 oracle,所有完整 before/after、退出码、字节摘要保留。另验证正常路径先拒绝、原 owner 合法 release、原先被拒的一方再成功取得 lease,两种 provider 均能恢复推进,没有以弱化排他换通过。
对主干的风险
40 项 Python、native workspace 30 项通过/初始 1 项 PG 环境跳过、control-plane typecheck、scoped Ruff 通过。随后独立 PostgreSQL 16 三个隔离数据库完成 workspace31 + store369 + service2 共 402 项,均通过并正常停止;这不冒充上述 File/SQLite 具体反例已在 PostgreSQL 执行。6 个 soft_claim/legacy 完整 typed 决策与不可变基线相同;既有仓库/worktree owner 控制保留。
Goal 绑定标准 premerge 的 5 项直接和 17 项选择检查通过,但 aggregate 正确保留 quality_invalid_receipt,因为独立必需范围不变量仍失败。严格回执 cqr_36b4f5c123b7db7edcf2 为 FAIL,已物理全字段读回;没有通过测试数量或成功采集探针把业务失败洗成通过。没有改作者源码、降低断言、豁免质量门或查询/等待 hosted CI。未验原生 Windows、安装后的 App/Host、具体 PG 反例、全套 CI 和长期/完整费用因果收益。
我的整体评价
REQUEST_CHANGES,限 d9fdf9906a262ded01a7852ce5b738c0f0496ec2。 对核心排他目标仍为 not_yet_proven,且新增负类续接造成 regression;原简单类改善和正确的身份/事务边界单独保留。唯一 TypeScript acquire owner 与 provider 事务边界保持正确;应在现有 matcher 中修复区间等价和负类续接,不能在 CLI 或各 provider 再补一套规则。 本 PR 的默认 hard_lease 冲突比较尚不满足完整别名与既有 glob 行为;soft_claim/legacy、仓库边界和独立 worktree 权限不得借修复扩大。 现有比较副本复用已保留,较大的 parser 拆分可留到正确性恢复后;不增加第二个比较 owner。该 diff 只改内部 matcher 与测试,没有渲染展示变化,visual_review 为 not_applicable。修复后需要重跑同一范围反例及现有负类/恢复,保持拒绝不写状态。旧评论和当前 finding 不由本轮自动 dismiss/resolve,未执行合并。
英文简短结论
English verdict: REQUEST_CHANGES — d9fdf99. Simple Unicode classes are repaired, but singleton ranges still admit eight conflicting File/SQLite acquire/claim cases. Negative-class continuation adds four new double grants that the immutable baseline rejected; four negative-range controls also overblock. Real provider and OS glob evidence contradict the completed-scope claim. Focused/PG checks pass, while required independent scope validation, strict quality and aggregate premerge correctly remain failed. Repair the existing matcher without weakening identity or authority.
| for (const literal of literals) { | ||
| if (literal === candidate || (foldCase && caseFold(literal) === folded)) return true; | ||
| } | ||
| if ([...candidate].length !== 1) return false; |
There was a problem hiding this comment.
[P1] 单元素 Unicode 区间仍漏判同文件别名。src/[ς-ς].ts→src/Σ.ts、src/[ß-ß].ts→src/SS.ts 在 File/SQLite acquire 与原子 claim 共 8 例均准入两份 active 独占租约并修改 provider;原生 glob 与同文件检查独立确认覆盖。普通字符类虽已修复,range 仍只枚举输入单字符变体且拒绝变长候选。请在同一 matcher 补齐区间成员对称 fold 与消费长度,保留原 scope/请求身份;加入两 provider、两入口的拒绝不写状态及否定区间控制。
| // Negation applies after the union of every fold-equivalent candidate. | ||
| const lengths = classMatchLengths(token.body, value, valueIndex, foldCase); | ||
| result = token.negated | ||
| ? lengths.length === 0 && match(valueIndex + 1, tokenIndex + 1) |
There was a problem hiding this comment.
[P1] 任意长候选会压掉合法负类续接。src/[!ß]S.ts 原生 glob 实际匹配 src/SS.ts(首个 S 不等于 ß,再接一个 S)。不可变基线四条 File/SQLite acquire/claim 路线全部拒绝且不改状态;当前头四条全部准入双独占租约。这里发现两字符 SS 折叠为 ß 后,lengths.length === 0 使整个负类失败,丢掉一个 S 的合法路径。请按消费长度与后续 token 判断,并保留 [!ß]x 的正确排除、请求身份及拒绝不写状态。
Goal And Delivered Outcome
This prevents a silent exclusive-write bypass for default hard-lease multi-agent goals on case-insensitive Windows and macOS: two agents can otherwise hold active leases for
src/Main.tsandsrc/main.ts, or Unicode-equivalent names such assrc/Σ.tsandsrc/ς.ts, which name one physical file.status: "applied",acquired: true, and mutated the provider; after the change it exits 1 withwrite_scope_conflictand leaves provider state unchanged.Closes #7243; baseloopx-project/loopx:mainate1c42d1605669e6c03004585b070c70b5cb6a57e.Author Declaration
Implemented against
docs/reference/canonical-lease-renew.md,Repository-relative scope identity, revision28fa39bd8a2f172c8a1cfde5b144dbd249f165a8. The linked issue and default provider contract inlocal-authority-provider-selection.mddefine the affected default path.Host/path case aliases remain overlappingscopePairOverlapsinloopx/control_plane/work_items/task_lease_acquire_decision.tstests/control_plane_ts/task_lease_workspace.test.ts;tests/control_plane/test_canonical_lease_scopes.pycomparisonWriteScopes,normalizeWriteScopesscope comparison folds case aliases without rewriting retained or request spelling; operation-identity replay assertioncaseFold,classMatch,fnmatchcase[A-Z], negated classes and preserved original-range coverage for[A-z]evaluateTaskLeaseAcquireDecisionFileandSQLitereal-CLI teste1c42d1605669e6c03004585b070c70b5cb6a57e, read the contract clause and shared callers, reran the focused suites at76c62434024cdbe895c457d5b147f610195580c3, and intentionally left persistence schemas, provider code, request identity and UI unchanged. Independent review found that mixed-case character-class ranges such as[A-b]still missedC/c; the positive and negated cases were reproduced as failures and fixed at this head. The latest review then found that Unicode aliases such as Σ/ς and ß/SS still acquired separate leases; comparison-only Unicode folding and real File/SQLite acquire plus atomic-claim regressions are added at this head. Existing repository-wide suite gaps on native Windows are listed under Coverage and gaps rather than claimed as green.Scope And Continuation
Validation
76c62434024cdbe895c457d5b147f610195580c3(rebased ontoe1c42d1605669e6c03004585b070c70b5cb6a57e)staticpassednpm run -s typecheck:control-plane; Ruff overtestsand the changed control-plane scope; semantic-vocabulary advisory found no supported new vocabulary carriers.unitpassednode --no-warnings --experimental-sqlite --experimental-strip-types --test tests/control_plane_ts/task_lease_workspace.test.ts: 26 tests, 24 passed, 0 failed, 2 environment-gated skips.regression_paritypassedpytest tests/control_plane/test_canonical_lease_scopes.py -q: 26 passed at this head, including parametrized File/SQLite Unicode acquire and atomic-claim cases for Σ/ς and ß/SS. The failing paths previously acquired both exclusive leases withstatus: "applied"and mutated the provider; the head paths returnwrite_scope_conflictwithout changing provider state, or correctly permit a negated-class non-overlap.real_entrypointpassedintegrationpassed7815a91: adjacent lease acquire/renew/lifecycle/CLI suites, 47 passed and 2 skipped. The current revision changes only the shared comparison and focused regressions.manualpassed7815a91:loopx canary premerge --from-git-diff --git-diff-base upstream/main --timeout-seconds 300selected 17 checks, all passed. Public-boundary scan reported 0 errors and 43 pre-existing credential-reference warnings; premerge was not rerun after the focused Unicode change.fcntlimports. Those host failures were not treated as passing evidence.Frontend / Visual Evidence
Type of Change
LoopX Area
Technical Direction
Shared-authority RFC fixture impact
Boundary Checklist
.loopx/,.codex/goals/, and liveACTIVE_GOAL_STATE.md).python3 scripts/check_rfc_bilingual.py --base origin/main --head HEADpasses, or no paired RFC changed.none.Signed-off-bytrailer (git commit -s).