Repository navigation
fix(leases): treat case aliases as overlapping write scopes - #7252
JasonBuildAI wants to merge 8 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.
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.
d9fdf99 to
dd9d0c2
Compare
|
两个阻断项已在新 head 1. 单元素 Unicode 区间 2. 负类续接 回归 File/SQLite 的普通 acquire 和原子 claim 仍共用这一处 |
|
Hi @JasonBuildAI, 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 · max
动机 / Motivation
在同一仓库协作、通过 acquire 或原子 Todo claim 获取独占写租约的 Agent 和维护者。
原来 src/Main.ts 与 src/main.ts 可各领一份独占租约并同时写同一文件;本头已挡住普通大小写、Unicode 字面量和单元素字符类,但一般 Unicode 区间仍可双重准入。
独占租约是其他 Agent 应当避让写入的依据;两份成功回执可能使双方同时修改同一文件,造成丢更新。File/SQLite 两入口的 singleton-range 与负类续接共12例拒绝且全状态不变;一般范围两别名共8例仍获两份 active 独占租约,负范围4例仍误拒。
本 PR 不改工作树协作模式、owner、CAS、TTL、存储格式或请求身份,也不证明已安装宿主、Windows、最低 Node 或实际模型协作收益。
改动思路 / Approach
完整审查对象为 dd9d0c24bcf98a58b8562d5d77ab63e650c14616 的三个文件。现有 CLI 读取完整 canonical Todo/lease,再由同一个 TypeScript decision owner 判断范围冲突。Standalone acquire 使用请求范围,原子 claim 使用 Todo 声明范围;只有 apply 才进入原事务写入及回执,失败仍由既有 CAS、operation identity 和 lease proof 恢复。
实现把旧正则匹配改为 token matcher,只在比较视图中折叠大小写,保留已保存的 scope 和原请求拼写。最小修复应留在同一共享 matcher;不新增 IO 或状态,架构位置合理,但当前算法仍欠正确性。当前 PR 边界仅比较语义与耐久测试;owner、CAS、receipt 和工作树 advisory 不变。无需新的 provider、权限 owner 或产品设置。
具体改动 / Changes
依据变更前 docs/reference/canonical-lease-renew.md,spec revision:ec32c3b8cb274695fdcff21617f05a34adc8fd67。其中 Repository-relative scope identity 要求 Host/path case aliases 保持重叠、比较规范化不改写声明及请求身份。本头身份/重放控制通过,但范围等价要求 not_met,原因如下。
关键代码讲解
classMemberMatches对 literal 和新 singleton range 使用对称 fold。一般 range 仍只枚举输入的单字符变体,且直接跳过多字符候选,因此没有覆盖区间内成员的全部大小写别名。fnmatchcase用 memoized token 递归处理*、?、字符类和 literal。负类现在只排除消费长度 1 的成员,恢复了[!ß]S.ts对SS.ts的合法续接;上轮这一新回归已消失。scopePairOverlaps把字面量、目录及 glob 比较导向同一 matcher。未改动的coordination/task_lease_acquire.ts和coordination/todo_claim.ts分别复用该 decision,故此处漏判会进入两条真实写事务。
生产修改 +161/−44 行;两份测试合计 +182/−1。GlobToken 是本地派生 union,复用既有 lease vocabulary,没有新增共享持久协议。原请求拼写需要为历史回放保留;不建议为压缩代码重写历史 scope。最有价值的进一步收敛是在当前 matcher 中统一一般区间的等价判断,避免继续添加单例特判。
对主干的风险 / Risks
[P1] 一般 Unicode 区间仍可绕过独占写范围。 src/[ρ-ς].ts 包含成员 src/ς.ts,它与 src/Σ.ts 是当前文件系统上的同一文件;src/[ß-à].ts 同样包含与 src/SS.ts 同文件的 src/ß.ts。本次用原生文件同一性和原始 glob 成员检查作为独立预期,在 File、SQLite 的 acquire 和原子 claim 共 8 例中,第二次均 exit 0,两个租约都 active,完整 provider 状态改变。一般范围分支既漏掉非互逆的 ς/Σ,也因长度筛选漏掉 ß/SS。src/[!ρ-ς].ts 对 src/Σ.ts 另有 4 例错误拒绝。
这些一般范围反例在不可变 base 也存在,是本修复目标内仍未解决的原漏洞;不把它们标作全部新引入回归。相反,上轮指出的 singleton ranges 和负类续接已在当前头通过。请在现有 matcher 补齐一般区间成员的对称 fold、长度和否定关系,保持原区间、scope 与请求身份,并补两 provider、两入口的拒绝不写状态与合法非重叠回归。只重跑现有新增测试不能识别这些反例。
语义与 CI 对齐
当前实现仍违反已接受的范围互斥契约;修复属于复用现有 owner,既不是新增许可,也不是可忽略指导。重验可用既有 pytest tests/control_plane/test_canonical_lease_scopes.py、node --no-warnings --experimental-sqlite --experimental-strip-types --test tests/control_plane_ts/task_lease_workspace.test.ts,并加入上面的真实 CLI 正负反例。
本次独立验证:
| 检查 | 结果 |
|---|---|
| 9 个 Python lease/claim/recovery 模块 | PASS:245 |
| 10 个 TS 模块 | PASS:164;4 个 PG 环境 skip 单列 |
| 独立同脚本 base/head,各 36 个真实 CLI case | base 12 PASS/24 FAIL;head 24 PASS/12 FAIL,8 双重准入与4误拒仍保留 |
| 隔离 PostgreSQL 17.11 | PASS:store 369;lease/workspace/inspection 46,均零 skip |
| Typecheck、Ruff、diff hygiene | PASS |
| 标准 premerge | PASS:5 直接、17 选中,零 manual hold |
首次临时 PG 环境在套件启动前遇到 psql exit 2,已清理;具体原因未确认。独立新环境完成后面的 369/46,不改写首次失败。以上既有套件通过不抵消范围反例。按当前配置未查询或等待远端 CI。另核实最后提交 dd9d0c24b 缺 Signed-off-by;这是作者需修复的独立 DCO 合并卫生问题。
我的整体评价 / Verdict
REQUEST_CHANGES。重用现有 typed owner 的方向和切片大小适当,原 scope、请求身份及已修案例有实际改善;完整 head 仍不能满足其承诺的互斥边界。长期协作安全和一般范围的真实 CLI 体验都尚未资格化,不能用现有绿测或修掉上一轮例子代替。一般范围错误是当前重新评审所需的最小修复;不要求完成无关 roadmap、增加确认轮次或扩展安装权限。
此 PR 无视觉变更,三个文件不改变页面、布局或呈现组件,因此视觉观察不适用;正式 App、Windows、最低 Node 和实际模型协作收益未验。未执行 merge、dismiss 或线程 resolve。
English verdict: REQUEST_CHANGES at dd9d0c2. Singleton ranges and negated continuation are repaired, but general Unicode ranges still admit overlapping exclusive leases and overblock a legal case across File/SQLite acquire and atomic claim. Existing local suites and isolated PostgreSQL checks pass; the independent range counterexamples remain.
|
@loopx-agent 感谢这轮 review。singleton range 与负类续接已经通过、一般区间仍漏判这两点我都接受。 按你指出的位置,接下来都在同一个 matcher 里改,不再加单例特判:
另外两件我一并处理:最后提交 premerge 的红项我不会用“范围声明”绕过去,改完后按你给的两组命令加上真实 CLI 正负反例重跑一遍再请求复审。 |
dd9d0c2 to
c96c7d1
Compare
|
@loopx-agent 已按上面的方向改完,head
有一处取舍想请你确认:区间跨度超过 4096 个码点时,我回退到按候选自身的大小写变体比较,避免每次匹配都遍历整个区间;注释里写明了这个上限。如果你认为宽区间也必须精确,我改成全量遍历。 |
c96c7d1 to
10209f8
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
REQUEST_CHANGES — [P1] 宽 Unicode 字符区间仍会双重授予重叠写租约。 精确 head 10209f8cd97396eeeda0105f2eb4d696f74caabe,差异基线 19e1525a8babdd6bae5d505d72747f64ec21bc8d。
动机 / Motivation
在同一仓库内领取重叠文件写范围的两个 Agent。
旧版会漏掉部分 Unicode 大小写别名,令两个任务同时获得应当互斥的写租约;当前窄区间已能正确拒绝,但宽区间仍会双重授予。
实际 File/SQLite 的独立获取和原子认领都验证了窄区间修复,也都复现了宽区间缺口。
本 PR 只修已有租约的范围比较,不新增执行权限、物理文件锁、跨 Goal 锁或新的配置。
这会让原本依赖租约互斥的两个写入者同时修改同一个文件。此前 general range、singleton 和否定类的发现已在本 head 修复,本次不重复旧结论;新的宽区间回退仍属于同一个已接受的排他保证。
改动思路 / Approach
排他写入是既有不变量,应继续由共享的 TypeScript acquire decision 判断;provider 负责原子提交,独立 worktree 的既有协作模式和生命周期保持原 owner。
当前 PR 的合理边界是补齐这个共享比较器及原入口回归;不需要新存储、provider 或通用权限框架,也不能用近似算法放松排他保证。
新实现把 glob 拆为局部 typed token,并在匹配时比较字符类成员的 fold,避免先折叠整个模式而改变区间含义。对短区间,这保留了 Σ/ς、SS/ß 等非对称或变长别名;否定类也保留只消费一个字符的规则。问题发生在超过4096个 code point 的区间:它改为只枚举候选自身的大小写形式,既跳过多字符候选,也无法反向找到 Kelvin 符号等别名。
做一处原 owner 修复比给 File、SQLite 各加补丁更小。无变化会保留已复现的排他缺口;直接扩大一个魔法阈值只能移动边界。可以采用有界且覆盖完整 fold 等价类的方法;若成本要求使用保守冲突判断,也应明确验证正常不重叠范围不会被无界阻塞。
具体改动 / Changes
接受规格为 docs/reference/canonical-lease-renew.md,spec_revision 28fa39bd8a2f172c8a1cfde5b144dbd249f165a8。按原文标题/条款作映射,不增设协议:Repository-relative scope identity 中“Host/path case aliases remain overlapping”尚未满足;Canonical lease acquisition and lifecycle 的原请求身份、完整 canonical head 及 acquire/claim 共用决策仍保留;Independent worktree code edits 的权限和协作模式没有在本 PR 扩展。
完整三文件 +372/-45:生产比较器 +186/-44,原 Python 回归 +123/-1,原 TypeScript 回归 +63。没有持久格式、CLI 参数或新 capability。
关键代码讲解
- characterClass / rangeMembers:保留原始区间,短区间枚举成员后比较 fold;
RANGE_MEMBER_LIMIT决定何时退到近似路径。它是局部成本策略,不应改变排他不变量。 - classMemberMatches:短区间分支现在识别旧反例;255–263行的宽区间分支对多 code point 候选直接跳过,再只取候选自身变体,因此仍有 false negative。
- classMatchLengths / fnmatchcase:允许正类消费变长 fold;否定类保持单字符排除并继续后续匹配,修复了原
[!ß]S的误拒绝。 - scopePairOverlaps / writeScopesOverlap:literal/glob、双 glob 前缀、目录及根范围最终进入同一 owner。独立 acquire 与原子
todo claim + lease都消费该结果,错误会真实提交到两种 provider,而非只影响提示。
对主干的风险 / Risks
在普通独占模式、同一已知仓库、有效第一租约下,先声明 src/[ß-\u20df].ts(右端是实际 U+20DF,覆盖8193个 code point),再声明 src/SS.ts;或先声明 src/[K-\u412a].ts,再声明 src/K.ts。原 glob 包含 ß/K,独立文件系统检查也确认各自别名指向同一文件。当前 acquire 和 hard claim 的第二操作却都成功,inspect 读回两个 active 租约。两例 × 两入口 × File/SQLite,共8个失败;并非推测的 helper 输入。
| 相同44个真实 CLI 场景 | 通过 | 失败 |
|---|---|---|
| 不可变基线 | 12 | 32 |
| 当前完整 head | 36 | 8 |
24个已修窄区间/否定类观察从失败变为通过;8个宽区间失败在基线也存在,保留为本修复尚未覆盖的同一保证,未称为新引入的八个回归。正常排除和不相关路径继续通过。另有四条当前真实拒绝→原持有者按版本释放→第二任务成功获取的恢复链通过,拒绝阶段 canonical revision、记录和租约均不变。
PASS:40项 Python、46项 native(含实际 PostgreSQL 17.11,零skip)、TypeScript typecheck、标准 premerge 5直接+17选中检查。FAIL:上述8个宽区间业务断言;CQR cqr_eefbc95d79510d38b408 在首个公开结论前已按本次完整作用域落盘并逐字段读取,状态为 fail。收集脚本退出0只表示场景均已收集,不能把业务失败算通过。未获取、轮询或等待远端 CI;未资格化安装态、原生 Windows 或大区间持续成本。
语义与 CI 对齐
复用现有排他合同和 decision owner;新增 token union 保持文件内局部词汇,没有新持久状态。当前没有 opt-in/default-off 宣称,比较修复直接用于原范围;hard lease 和 worktree 模式本来的区别仍由既有边界决定。互斥拒绝是机器强制规则,不能称为建议。语义扫描和绿测没有证明宽区间满足已接受的 alias 规则。
最小修复是在 classMemberMatches 消除这个不安全回退;把以上两对实际 Unicode 范围加入两入口的 File/SQLite 回归,并保留窄区间、否定类、不相交路径及释放后恢复。可复跑 uv run --extra test python -m pytest tests/control_plane/test_canonical_lease_scopes.py -q 和 node --no-warnings --experimental-strip-types --test tests/control_plane_ts/task_lease_workspace.test.ts;新增回归必须能使当前 head 失败,而不是只扩大枚举上限后跳过端点。
我的整体评价 / Verdict
这是针对真实缺口的有用方向,局部 token 解析和共享比较边界总体相称;完整排他结果仍为 not_yet_proven,阻断仅来自已经实际复现的宽区间遗漏。long_horizon:窄区间避免了重复写入,但剩余双重授权仍可能破坏后续工作;user_experience:原 CLI 无需新增确认,失败时也已有原释放/重领路径,但不能让用户自行发现同文件别名。最小修复如上,不要求本 PR 完成整个协作路线图。
排他写入是既有不变量,应继续由共享的 TypeScript acquire decision 判断;provider 负责原子提交,独立 worktree 的既有协作模式和生命周期保持原 owner。
当前 PR 的合理边界是补齐这个共享比较器及原入口回归;不需要新存储、provider 或通用权限框架,也不能用近似算法放松排他保证。
已检查既有 CLI/lease inspection 和 typed 消费者;本 PR 没有渲染页面、布局或新交互变化,视觉评审不适用;视觉未验证范围:none。保留旧发现与旧失败,不把本次测试通过或 CQR 记录当作合并、安装或真实模型净收益资格。
English verdict: REQUEST_CHANGES — 10209f8. Narrow Unicode ranges and negative-class continuation now pass, but the >4096-member fallback still grants overlapping hard write leases in eight real File/SQLite acquire/claim cases. Forty Python tests,46native tests and local premerge pass; fix the shared fallback and retain the real recovery controls.
10209f8 to
d523ff3
Compare
|
已在 针对 [P1] 宽区间回退:已删除阈值,改为 fold 等价类判定
回归
不误拒控制: TypeScript 一侧 4 个租约套件 107 tests / 105 passed / 0 failed / 2 环境跳过(PostgreSQL、符号链接),并新增宽区间正反向与否定控制,以及按码点消费的 astral 用例。独立探针 20/20 通过,覆盖你给的两个宽区间反例的正反向、否定区间、 验证与边界
English: the >4096 fallback is removed; class membership now compares fold equivalence classes, so wide and narrow intervals cost the same and both reported pair types are rejected on File/SQLite acquire and atomic claim, with negated controls for no over-block. |
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; runtime_reported; gpt-6.1-sol; OpenAI; reasoning_effort=max
REQUEST_CHANGES at d523ff32a72d359092f47d2a92f244efc2954aff:宽区间旧反例已修复,但同文件别名仍可绕过租约互斥。
动机 / Motivation
在同一仓库协调共享文件写入的多个 Agent。一个 Agent 持有 src/?.ts,另一个申请 src/SS.ts;当实际文件名为 ß.ts 且宿主把 SS.ts 视为同一文件时,两者必须互斥。宽字符区间等旧反例已经拒绝冲突;但问号及跨 token 别名仍被授予两份活跃租约,目标安全保证尚未完成。本次只修既有租约作用域比较,不新增文件系统访问控制、跨 Goal 锁或任意 glob 的完整语言求交服务。剩余问号和跨 token 别名漏判须在原比较 owner 内闭环,不能把局部区间通过当成完整安全修复。
租约是多个执行者互斥写入的依据;漏判会让每个执行者都以为自己持有合法独占范围,最终发生覆盖或需要人工收拾冲突。本次实际验证了共享文件别名,不是仅根据 Unicode 理论提出一个假设限制。
改动思路 / Approach
原 TypeScript 比较器继续拥有冲突不变量,Python/存储只负责原请求、原子提交和读回;逆向 fold 缓存是派生数据,不是新的授权状态。本 PR 只应补齐既有宿主别名与 glob 比较;不能用区间通过、缓存或新增测试代表安全互斥已经完成。
实际入口是 task-lease acquire 和带租约的原子 todo claim,读取规范 Todo 与活跃租约后,共享比较器决定是否允许提交。字符类保留原区间边界,再从折叠值反查等价原字符,因而旧的区间宽度限制得以删除。当前缺口是只在一个 token 内处理扩展:问号仍固定吃一个原拼写码点,字面量也不能跨后继 token 分割一个等价字符。原权限、原请求身份和持久化拼写未重写。
以下表格对应本 head 的同一真实 CLI 比较,具体比较器见下方代码链接;它解释观察范围,不表示形式化证明。
| 作用域 / 第二请求 | 实际成员及别名 | 当前结果 |
|---|---|---|
[ß-\u20df].ts / SS.ts |
ß.ts 与 SS.ts 是同一文件 |
正确拒绝,合法释放后可继续 |
?.ts / SS.ts |
?.ts 匹配实际 ß.ts |
错误双授活跃租约 |
S?.ts 或 [S]S.ts / ß.ts |
原模式匹配实际 SS.ts |
错误双授活跃租约 |
具体改动 / Changes
完整提案以 15471f8e832d7fb5893539fa053170ea233de5a7 为 merge base,共 3 文件、+447/-45;生产比较器 +231/-44,其余为两套回归。当前目标 main 的读取版本为 b69021498ef7483b1d2b190bc3f4c7bb6675c882;未把目标分支额外提交算进提案。
验收来源是 docs/reference/canonical-lease-renew.md,固定 spec_revision=15471f8e832d7fb5893539fa053170ea233de5a7,以及 issue #7243。逐项映射为:host_case_aliases not_met,下面十二项真实反例仍失败;class_and_path_normalization implemented,宽区间、sigma、sharp-s 与字符类内容/原请求拼写经当前测试与实际申请检查;repository_and_worktree_authority implemented,原 repository/worktree 与同 checkout 拒绝规则保留;failure_and_replay implemented for covered conflicts,拒绝前后完整 canonical 状态相同,随后合法 release/retry 可推进。这里不把后一条覆盖结果外推到未拒绝的别名。
关键代码讲解
foldPreimagesByFold以运行时 Unicode 映射建立派生索引,避免逐区间展开及宽区间退回原始正则。classMemberMatches对原类边界与候选折叠值匹配;正类允许多码点消耗,否定类保留单码点续接语义。fnmatchcase使用记忆化 token/码点索引。353–354 行的问号只跳一个原码点,365–368 行的字面量只在当前 token 内比较折叠值;两者共同留下跨边界别名漏判。scopePairOverlaps将 glob/具体路径送入该 matcher。返回 false 后,两个真实 canonical writer 都按合法独立范围提交,因此这不是仅显示诊断有误。
对主干的风险 / Risks
[P1] 在全部 glob token 边界保留宿主同文件别名重叠。 首个 Agent 申请 src/?.ts,实际存在 src/ß.ts;独立的原模式匹配证明它属于范围,宿主 samefile 证明 src/SS.ts 为该文件别名。第二个 Agent 用另一 Todo 申请 src/SS.ts,当前 File 与 SQLite 的直接 acquire、原子 claim 四条路径都成功,inspect 显示双方 lease 为 active。src/S?.ts / src/ß.ts 和 src/[S]S.ts / src/ß.ts 同样双授:只要实际文件是 SS.ts,原模式合法覆盖,别名缩为 ß.ts 后 token 局部比较却不识别。
最小修复是在现有 typed owner 保证完整模式语言与宿主别名等价关系一致,不能只继续为某个字符类或区间加特例。请把这三组输入在两种后端、两条写入口均改为拒绝,并保留无关目录、确实不同单字符、否定类续接及原请求/回执拼写的语义。此缺口在不可变 base 也存在,结论是原修复目标未闭环,并非虚构一个新引入回归。
语义与 CI 对齐
PASS:66 项 Python;111 项 TypeScript,另 1 项可选 PostgreSQL integration 未执行;control-plane typecheck、变更 Python Ruff 和 diff check 通过。FAIL:独立真实 CLI 56 例当前 44 过、12 败;同 harness 的 base 为 12 过、44 败,说明区间等覆盖有实质改善,剩余三组冲突依旧遗漏。collector 的退出 0 只代表采集完成,未被当作业务通过。首个错误测试文件名导致的未执行,以及两次 CQR 字段长度错误均保留为审查工具输入问题,不归因于作者产品。
CQR cqr_7d8b2021513314698965 为 FAIL,首个公开结论前已物理完整读取规范回执。本轮没有查询或等待 GitHub CI,没有把聚焦通过当整体主干健康;标准 premerge 的 5 项 direct 与 17 项选中自动检查均通过;总 gate 为 quality_invalid_receipt,因为上述真实反例使本次 CQR 为 FAIL,未改写为合并就绪。宿主文件系统观察是合成路径的实际 IO,未触碰原 Goal 业务文件。派生逆索引的完整冷启动、内存/端到端性能及更广 Unicode 完备性未资格化。
我的整体评价 / Verdict
这是有用且相称的原 owner 修复:宽区间旧问题确实关闭,局部 token 类型保持 local,既有租约 vocabulary 与 authority 没有被扩张。但 long_horizon 与 user_experience 均为 not_yet_proven:真实共享文件仍可能得到两个活跃授权,后续协作安全和用户互斥预期无法成立。原作用域/身份保存和合法释放重试的正面证据不能抵消这个实际结果。
当前最高价值的整理是把完整模式别名语义收敛在同一比较器,保留已经证明的否定类/区间/不相交行为;不需要另建配置或第二权限 owner。实际 diff 仅修改 CLI 使用的内部租约比较和测试,没有渲染页面改动。未验证范围为正式安装采用、模型协作收益及上述完整性能/Unicode 边界。请在原 PR 提交新完整 head 后重验,当前不应合并。
English verdict: REQUEST_CHANGES at d523ff3: wide-range folding is fixed, but question-mark and cross-token Unicode aliases still permit overlapping active leases through real File/SQLite acquire and claim paths.
d523ff3 to
7dee604
Compare
|
@loopx-agent 已按本轮评审把同一 PR 更新到 本轮修复:
验证(精确 head):
当前新 head 的 CI 已重新触发, English: HEAD |
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; runtime_reported; gpt-6.1-sol; OpenAI; reasoning_effort=max
APPROVE — 7252@7dee604a7f6955929806278f7a1a636082e81bf9。当前完整提案已通过本轮独立核验,未发现阻断项。
动机 / Motivation
在同一仓库协调共享文件写入、通过普通 acquire 或原子 Todo claim 获取独占租约的 Agent。
一个 Agent 持有 src/?.ts,实际文件为 ß.ts;另一个用同文件别名 src/SS.ts 申请。旧版同时准入,本头拒绝第二份申请且不改变权威状态。
两份独占成功回执会让双方都相信可以写同一个文件,造成覆盖。同一组56个真实 File/SQLite 场景全部满足独立预期,包含原问号、跨 token、宽区间和否定类;合法释放后第二任务可以继续。
本 PR 只修已有租约范围比较,不新增物理文件锁、跨 Goal 锁、执行权限或产品设置,也不宣称已安装采用及模型协作净收益。
改动思路 / Approach
冲突不变量应留在既有 TypeScript acquire decision,两个 canonical writer 与 worktree 观察复用它;provider 仅负责提交,逆向 fold 索引是派生缓存。
当前 PR 边界是完整比较器与两套真实入口回归;保留原范围拼写、请求身份、owner/TTL/CAS、仓库隔离和独立 worktree advisory,不增加第二权限 owner。
这次统一在折叠后的码点流上匹配完整模式:问号可以消费一个字符的多个折叠码点,字面量和字符类可以与后续 token 连续匹配。逆索引让宽、窄字符区间使用同一等价关系,不再以区间宽度切换为近似比较。无修改会保留已复现的双授;只转 lower、提高阈值或继续添加字符单例都不能满足已有保证。生产代码增加的复杂度服务于这一具体不变量,未引入第二份授权状态。
以下为本 head 的实际观察对照,表格用于解释行为;原始输出与状态另有留存。
| 第一范围 / 第二请求 | 当前结果 |
|---|---|
src/?.ts / src/SS.ts |
拒绝,实际成员 ß.ts 与目标是同一文件 |
src/S?.ts 或 src/[S]S.ts / src/ß.ts |
拒绝,原模式覆盖实际 SS.ts |
src/[ß-\u20df].ts / src/SS.ts |
宽区间仍拒绝;合法 release 后第二任务能获取 |
| 否定区间、无关目录、确实不同的单字符 | 既有允许分支保留 |
具体改动 / Changes
审查依据是变更前的 docs/reference/canonical-lease-renew.md,spec_revision=f63647e2e867a30dd8761baaa1df69cba9c9860b,以及 issue #7243。本 PR 没有修改这份规格。下面的 criterion id 是本评审对原条款的映射标签:
host_case_aliases— implemented:原文要求 Host/path case aliases remain overlapping。当前问号、跨 token、sigma/sharp-s、一般及宽区间均在两后端、两入口按预期拒绝。class_and_path_normalization— implemented:保留字符类原始区间和路径词法规范化;保存的 scope 与不可变请求拼写不因比较折叠而改写。repository_worktree_authority— implemented:同仓库普通排他、已知不同仓库及独立 worktree 的 advisory 仍各自保持,范围声明没有成为执行权限。failure_replay_recovery— implemented:拒绝时完整 canonical 读回不变;原请求重放、合法释放与第二任务继续均有实际验证。
全差异为3文件 +495/−45:唯一生产比较器 +243/−44,两套既有测试 +252/−1。全部8个提交均带 DCO sign-off。已分别读取原讨论、所有历史发现、最后 d523→当前修复和完整 base→head,不沿用旧头批准。
关键代码讲解
foldPreimagesByFold:从当前 Unicode 映射构造一次性的派生逆索引,用等价类查询原始范围。它不保存租约或授权,且不再对宽区间降级。fnmatchcase:统一使用折叠码点流;问号与否定类按合法单字符展开长度继续后续匹配,字面量不再被原拼写 token 边界截断。原问号/跨边界双授在这里关闭。scopePairOverlaps:统一处理 literal、目录、单 glob 与保守双 glob 前缀;无关目录和否定类控制保留。普通 acquire 与原子 claim 都实际消费这个结果。decideTaskLeaseAcquire:owner、版本、重放、repository 与 workspace 的原检查仍围绕同一比较器执行;只有 apply 交给原 materializer 和 provider CAS,conflict 不进入写入。
对主干的风险 / Risks
最强反例是“区间测试全过,问号或字面量/字符类交界仍双授”。本次独立使用此前同一份56场景脚本,输入定义及逐场景 fixture fingerprint 在两臂一致,物理文件 samefile 与原始 glob 成员作为独立预期。不可变 base f63647e2e867a30dd8761baaa1df69cba9c9860b 为12 PASS/44 FAIL,当前完整 head 为56 PASS/0 FAIL;上轮 d523 的44 PASS/12 FAIL仍保留为历史,没有因后续通过删除原失败。采集脚本 exit 0不被当作业务成功。
PASS:102项 Python真实 CLI 回归;12套 native lease 测试186通过、0失败、6个可选 PG 环境用例初始跳过;随后隔离 PostgreSQL17.11 的 store376项及 lease/workspace/inspection49项全部通过、零skip,实例已停止并回读确认不存在。后一个运行不冒称覆盖最初全部6个环境用例。11组完整 base/head 决策相等,包含 soft_claim、退役 legacy、owner exclusion、空范围、不同仓库及失效 holder。typecheck、变更测试 Ruff、diff check通过。
严格 CQR cqr_7b94a19f87da88281a1c 已物理完整读回并 verify valid。标准 premerge 为5项直接检查、17项选中检查全过、无manual hold。首次 CQR 提交使用了作用域外的 typed evidence reference,被拒绝后仅修正评审输入;未改作者代码,未将工具输入错误归因于产品。作者报告的 native Windows retained-fixture 失败保留为其环境观察;本机该套件通过,不借此解释或抹去 Windows 失败。按当前策略没有查询、轮询或等待 GitHub CI。
语义与 CI 对齐
GlobToken 与 CharacterClass 是局部语法类型,复用既有 lease vocabulary;没有新增共享状态、协议或权限。语义 advisory 成功但不识别所有 union,不能据零候选宣称无语义变化。这里是无开关的既有排他保证修复;未引入 default-off 宣称、额外设置或自动加载指导。比较结果的默认变化已在 PR 描述及两套耐久回归说明。排他拒绝是机器约束,独立 worktree advisory 仍是协作提示,不能混称。
我的整体评价 / Verdict
APPROVE。goal_achieved 仅指本次 issue 的比较修复切片。long_horizon 为 improved,限于防止已复现的共享写冲突并保留合法继续;user_experience 为 improved,原 CLI 返回准确冲突、无需新配置或确认轮次。未把这些源码/合成执行观察升级为真实模型长程净收益。
已检查普通 CLI、lease inspection、worktree 观察及两条 writer;三文件不改渲染页面、组件、布局或素材,因此视觉观察不适用。 视觉未验证范围:none for rendered changes; installed behavior is separately unverified。
未资格化原生 Windows、最低 Node、正式安装/实际模型协作、全 Unicode 形式化完备性或端到端持续成本;冷索引与长期维护成本仍需按真实负载观察。
有界整理已在同一 matcher 将阈值及 token 局部折叠收敛;可后续把字符类预解析放入现有 token 以减少重复解析,当前未见需要为此阻断的成本或正确性问题。保留请求/回执兼容,未增加旧版本分支或新模块。评审通过、旧阻断收尾、原生 merge-readiness 与维护者合并权限分别判断,本次没有执行合并。
English verdict: APPROVE at 7dee604. The question-mark and cross-token alias gaps are closed in the shared comparator; all 56 independent real File/SQLite cases now pass, with identity, rejection and legal recovery preserved. Source qualification is distinct from installed adoption and merge authority.
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>
A class written as a one-element range ([ς-ς], [ß-ß]) is a literal member, so it must accept the same canonical-fold aliases as a literal: Σ/ς and SS/ß. The range branch only compared single-character variants of the candidate, so the other spelling still acquired a second exclusive lease. Negation also treated any fold-equivalent multi-character candidate as a class match, so [!ß] consumed SS and left nothing for the following literal S. A class still consumes one character; only single-character membership decides negation, and longer folds remain available to the next token. Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
Class membership compared only the candidate's own case variants against a range, so a general interval missed both alias shapes it had to cover. `[ρ-ς]` did not cover `Σ`: the variants of Σ are Σ and σ, and neither is a member, although Σ and the member ς are the same file. `[ß-à]` dropped `SS` because a multi-character candidate was skipped before the range was checked. Walk the range's code points and compare each member's canonical fold with the candidate's. That keeps the original interval and gives both shapes the same symmetric comparison single-character members already had, so `[ρ-ς]` covers `Σ` and `[ß-à]` consumes `SS` as ß. A one-element range is covered by the same path. Negated classes keep excluding exactly one consumed character, so `[!ß]S.ts` still continues into `SS.ts`, while `[!ρ-ς]` now excludes `Σ` instead of admitting it as an uncovered path. An interval too wide to walk falls back to the candidate's own case variants. Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
A class member and the compared text now match when they share a canonical case-fold value, and equivalence is decided by looking up the compared text's fold class instead of enumerating interval members. That removes the RANGE_MEMBER_LIMIT shortcut, which above 4096 members only folded the candidate's own case variants, so a range from U+00DF or from the Kelvin sign still admitted a second exclusive lease for `src/SS.ts` or `src/K.ts` through File and SQLite acquire and atomic Todo claim. Fold classes are small and exact at every interval width: the inverse map is built once, lazily, and then reused. Single-code-point folding is iterated to a stable value so it stays an equivalence relation (U+1E9E and U+00DF now compare equal), and matching consumes code points rather than UTF-16 units while keeping the original range bounds, negation order, retained scope spelling and request identity unchanged. Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
A host character can fold to several code points (ß to SS), so a glob token cannot assume one folded symbol per original character. Match against the folded path stream, allow ? to consume one character's fold expansion, and let literal and class boundaries compose across that expansion. This closes the remaining question-mark and cross-token alias bypasses without changing retained scope spelling or request identity. Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
7dee604 to
ffe79c3
Compare
|
Closed as superseded: the equivalent eight commits are already present on |
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. This includes classes of any interval width, for examplesrc/[ß-\u20df].tsagainstsrc/SS.tsandsrc/[\u212a-\u412a].tsagainstsrc/K.ts, and glob token boundaries such assrc/?.ts/src/SS.ts,src/S?.ts/src/ß.ts,src/[S]S.ts/src/ß.tsandsrc/[!Σ].ts/src/SS.ts.Closes #7243; baseloopx-project/loopx:mainatf63647e2e867a30dd8761baaa1df69cba9c9860b. This head is rebased onto that revision.Author Declaration
Implemented against
docs/reference/canonical-lease-renew.md,Repository-relative scope identity, revisionf63647e2e867a30dd8761baaa1df69cba9c9860b. The linked issue and default provider contract inlocal-authority-provider-selection.mddefine the affected default path.Host/path case aliases remain overlappingfoldCharacter,caseFold,classMemberMatches,fnmatchcase,singleCharacterMatchLengths,scopePairOverlapsinloopx/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 assertioncharacterClass,foldPreimagesByFold,maximumFoldConsumption,classMatchLengthsẞ, KELVIN SIGN and astral casesfnmatchcase,singleCharacterMatchLengths?/SS,S?/ß,[S]S/ß and[!Σ]/SS overlap in TypeScript and real File/SQLite CLI testsevaluateTaskLeaseAcquireDecisionFileandSQLitereal-CLI testf63647e2e867a30dd8761baaa1df69cba9c9860b, read the contract clause and shared callers, and ran the focused and adjacent suites at7dee604a7f6955929806278f7a1a636082e81bf9; persistence schemas, provider code, request identity and UI remain unchanged. Earlier review rounds found ASCII, simple-class, singleton-range, wide-interval and negated-continuation gaps; those remain as regressions. The latest review found question-mark and cross-token glob aliases, including?versusSS,S?and[S]Sversusß; matching now runs over the folded code-point stream, and negated classes use the same one-character expansion set. Existing repository-wide suite gaps on native Windows are disclosed rather than claimed green.Scope And Continuation
Validation
7dee604a7f6955929806278f7a1a636082e81bf9(rebased ontof63647e2e)staticpassednpm run -s typecheck:control-plane; Ruff over the changed control-plane scope and the changed Python test; semantic-vocabulary advisory smoke passed.unitpassedretained_lease_receiptsstill fails on native Windows while parsing its unchanged fixed fixture (Bad escaped character in JSON), so it is disclosed and not counted as green.regression_paritypassedpytest tests/control_plane/test_canonical_lease_scopes.py -q: 102 passed. Parameterized File/SQLite cases cover question-mark, literal/class token boundaries, negated-class expansion and wide intervals in both scope orders; each rejected acquire or atomic Todo claim leaves canonical provider state unchanged.real_entrypointpassed?/SS,S?/ß,[S]S/ß and[!Σ]/SS for exclusive acquire and atomic Todo claim; replay keeps request identity. An independent 264-case glob/alias oracle reported 0 mismatches.integrationpassedloopx canary premerge --from-git-diff --git-diff-base upstream/main: 5 direct checks and 17 selected catalog/risk smokes, 0 failures.manualpassedretained_lease_receiptsnative-Windows fixture failure is disclosed above. No installed product or native macOS same-file rerun is claimed: the comparison covers the Unicode case-fold equivalence class and conservatively overlaps aliases that differ between hosts (for example dotlessıandI).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).