Skip to content

fix(leases): treat case aliases as overlapping write scopes - #7252

Open
JasonBuildAI wants to merge 4 commits into
loopx-project:mainfrom
JasonBuildAI:codex/agent-z6k2
Open

JasonBuildAI wants to merge 4 commits into
loopx-project:mainfrom
JasonBuildAI:codex/agent-z6k2

Conversation

@JasonBuildAI

@JasonBuildAI JasonBuildAI commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

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.ts and src/main.ts, or Unicode-equivalent names such as src/Σ.ts and src/ς.ts, which name one physical file.

  • Outcome basis / optional anchor: #7243, reproduced through the real CLI with both File and SQLite canonical authority.
  • Goal/source and gap: The accepted lease contract requires host/path case aliases to remain overlapping, but the shared acquire decision compared literals, path prefixes and globs case-sensitively. The adjacent lexical-alias fix in fix(leases): canonicalize equivalent write scopes #7204 left this case-alias member of the same bypass.
  • Observable before -> after, with the validation row that proves it: Before the change, a second exclusive acquire for a case-only alias returned status: "applied", acquired: true, and mutated the provider; after the change it exits 1 with write_scope_conflict and leaves provider state unchanged.
  • Issue/task and intended base: Closes #7243; base loopx-project/loopx:main at e1c42d1605669e6c03004585b070c70b5cb6a57e.

Author Declaration

  • Written by: model_agent, OpenAI Codex
  • AI disclosure: this change was drafted with AI assistance at my request, under my direction and review.

Implemented against

Criterion (spec clause) Disposition Symbol / path Test or command
Host/path case aliases remain overlapping implemented scopePairOverlaps in loopx/control_plane/work_items/task_lease_acquire_decision.ts tests/control_plane_ts/task_lease_workspace.test.ts; tests/control_plane/test_canonical_lease_scopes.py
Case folding is limited to conflict comparison; retained and request spelling stays unchanged implemented comparisonWriteScopes, normalizeWriteScopes scope comparison folds case aliases without rewriting retained or request spelling; operation-identity replay assertion
Glob character-class and Unicode literal behavior is not weakened implemented caseFold, classMatch, fnmatchcase Σ/ς and ß/SS literal, directory-prefix and glob cases; case-folded [A-Z], negated classes and preserved original-range coverage for [A-z]
The shared decision serves both File and SQLite hard-lease authority implemented evaluateTaskLeaseAcquireDecision Parameterized File and SQLite real-CLI test
  • Self-check before submission: Rebased onto e1c42d1605669e6c03004585b070c70b5cb6a57e, read the contract clause and shared callers, reran the focused suites at 76c62434024cdbe895c457d5b147f610195580c3, 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 missed C/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

  • Completed scope and remaining work: Complete within this scope. The fix covers Unicode case-folded literal, directory-prefix, glob and character-class comparisons at the shared decision owner; no configuration, storage or provider migration is needed.
  • Slice boundary / successor: N/A. This closes the remaining case-alias member of the exclusive-scope bypass addressed by fix(leases): canonicalize equivalent write scopes #7204; no successor is required for this defect.

Validation

  • Tested revision: 76c62434024cdbe895c457d5b147f610195580c3 (rebased onto e1c42d1605669e6c03004585b070c70b5cb6a57e)
  • Run state: finished
  • Input classes: synthetic
Check kind Result Public-safe evidence / limitation
static passed npm run -s typecheck:control-plane; Ruff over tests and the changed control-plane scope; semantic-vocabulary advisory found no supported new vocabulary carriers.
unit passed node --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_parity passed pytest 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 with status: "applied" and mutated the provider; the head paths return write_scope_conflict without changing provider state, or correctly permit a negated-class non-overlap.
real_entrypoint passed Parameterized real-CLI parity with File and SQLite canonical authority: ASCII and Unicode case aliases are rejected for exclusive acquire and atomic Todo claim; replay keeps request identity, and each rejected write leaves provider state unchanged.
integration passed At prior head 7815a91: adjacent lease acquire/renew/lifecycle/CLI suites, 47 passed and 2 skipped. The current revision changes only the shared comparison and focused regressions.
manual passed At prior head 7815a91: loopx canary premerge --from-git-diff --git-diff-base upstream/main --timeout-seconds 300 selected 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.
  • Coverage and gaps: Focused TypeScript, Python and real-CLI File/SQLite paths pass at the current revision, including rejected-write state equality. PostgreSQL was not run because this change is in the shared provider-independent decision and does not alter schema, storage or provider behavior. The repository-wide TypeScript suite remains host-blocked on this native Windows checkout by unrelated CRLF fixture parsing and process-cleanup failures; full pytest collection remains blocked by POSIX-only fcntl imports. Those host failures were not treated as passing evidence.

Frontend / Visual Evidence

  • UI impact: none
  • Before: N/A
  • After: N/A
  • First viewport and preview: N/A
  • States and viewports shown: N/A
  • Source data: none
  • Attention review: N/A; no user-visible surface changed.
  • Craft review: N/A; no user-visible surface changed.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring (no functional changes)
  • Documentation update
  • Test update

LoopX Area

  • Control plane (goals, todos, quota, scheduler, registry, runtime)
  • Benchmark boundary (adapters, runners, verifiers, scoring, evidence)
  • Capability or extension (providers, adapters, skills)
  • Public docs or presentation surface (README, protocols, dashboard)
  • Build, packaging, installer, or CI
  • Host or runtime integration

Technical Direction

  • Direction / acceptance reference, when applicable: Core control-plane hardening; canonical task-lease write-scope authority.

Shared-authority RFC fixture impact

  • Production-scale fixture schema: N/A; this PR does not claim shared-authority RFC promotion progress.
  • Semantic dimensions changed, or reviewed no-impact rationale: N/A; only conflict comparison changes, not persisted identity.
  • Provider conformance arms run: File and SQLite through the canonical CLI; PostgreSQL is covered by the same shared decision but was not run locally.
  • Read-only legacy/file/PostgreSQL three-arm rehearsal (required for promotion, runtime-routing, or compatibility-projection changes): N/A; no promotion, routing or compatibility projection changes.

Boundary Checklist

  • Neither the diff nor this PR body/comments/attachments disclose private state, credentials, raw traces or verifier output, internal links, or local machine paths (including .loopx/, .codex/goals/, and live ACTIVE_GOAL_STATE.md).
  • I did not duplicate maintainer-owned benchmark work unless a maintainer split out a public issue for it.
  • I kept the change scoped to the linked issue/task.
  • Existing English/Chinese RFC pairs (including ledger entries) both have a diff, and the local premerge check python3 scripts/check_rfc_bilingual.py --base origin/main --head HEAD passes, or no paired RFC changed.
  • I completed the visual evidence section for UI changes, or marked UI impact none.
  • Every commit includes a DCO Signed-off-by trailer (git commit -s).

JasonBuildAI added a commit to JasonBuildAI/loopx that referenced this pull request Oct 24, 2026
Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
@JasonBuildAI

Copy link
Copy Markdown
Contributor Author

Independent AI review

This is an independent AI review of the exact head 7815a91fdd1b850fbcdebdf839ecac159b6d729f. It is not a human approval or a formal GitHub approval.

Verdict: APPROVE WITH NOTES

Earlier in this review, the implementation was found to miss mixed-case character-class ranges: [A-b] folded only its endpoints and therefore missed C/c, allowing a second exclusive lease through the real CLI. The incremental commit at this head resolves that finding. Positive and negated range cases now behave correctly, and the provider state remains unchanged on the conflict path.

  1. Importance: Yes. This protects the exclusive-write concurrency boundary for default SQLite hard_lease and File-authority Goals on case-insensitive Windows/macOS hosts. A failure silently permits concurrent writes to one physical file under two apparently exclusive leases.
  2. Single problem: Yes. The cumulative diff stays within the shared lease decision and its two focused test files; the incremental commit only repairs character-class membership and adds boundary coverage.
  3. Logic and boundaries: No remaining implementation defect found. Mixed-case positive and negated classes behave correctly, including the original [A-z] coverage. The reviewer also checked all 1,378 ASCII letter ranges in positive and negated form without a mismatch.
  4. Abstraction: No unnecessary abstraction. The membership helper and candidate set remain local to the existing matcher; no new owner, module, configuration or persisted contract is introduced.
  5. Test sufficiency: The Python suite covers the mixed-range conflict through both File and SQLite, checks provider readback, and covers the negated-class non-overlap. The TypeScript test covers a case alias, mixed-case ranges, preserved spelling, directory prefixes, glob-versus-glob overlap and an unrelated-path negative case. The reviewer also reproduced the real-CLI conflict response and verified regular provider files, including SQLite sidecars, remained byte-identical.
  6. Checks: Focused Python 18 passed; TypeScript workspace 24 passed, 2 environment-gated skips; control-plane typecheck, Ruff and git diff --check passed; the real-server PostgreSQL authority CI check passed. The exact-head Release Artifacts build fails on the pre-existing browser assertion Goal drawer header is still pinned too close to the top edge in examples/personal-workspace-browser/typed-actions.mjs; the same assertion failed on merged PR fix(chat): withdraw misplaced Goal-level task selector #7248, and this diff changes no UI files. Broader CI jobs were still pending at the review snapshot.

JasonBuildAI added a commit to JasonBuildAI/loopx that referenced this pull request Oct 24, 2026
Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread loopx/control_plane/work_items/task_lease_acquire_decision.ts Outdated

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)]) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 字符类的等价关系仍不完整。输入 Σ 的 lower/upper/fold 候选不会包含类成员 ς,且 class 分支固定消费一个单位,故 [ß] 也漏掉同文件别名 SS。独立实际 File/SQLite CLI 中,src/[ς].ts→src/Σ.ts 和 [ß]→SS 在 acquire 与 atomic claim 共8例都允许第二份 active 互斥租约并改变 provider;当前主机已确认这些拼写对应同一物理文件。普通 Σ/ς 字面量修复通过不能满足字符类分支的原 case-alias 契约。请在现有 matcher 中补齐对称等价、长度消费和否定语义,保留原范围/请求身份,并给两种 provider、两种入口增加拒绝不写状态的回归。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在同一 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] 不会被当作 ß 的等价写法;这一层语义我没有扩大。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

补充:main 在我推送后又前进了一次,所以这个分支又做了一次纯 rebase 同步。

  • 最新 head:d9fdf9906(仍是同样 4 个提交,内容未变)
  • 基于:8cb50a2cd
  • 复跑结果不变:TS 24 passed / 2 skipped;pytest tests/control_plane/test_canonical_lease_scopes.py -q 40 passed

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>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 的正确排除。

关键代码讲解

  1. classMemberMatches@226:普通成员按对称 fold 判断,因此原简单类修复通过;但 candidate 超过一个码点便退出,range 只试输入的单字符变体,没有把区间中的 ς/ß 等价成员纳入。range 与同一成员的普通类于是分裂。
  2. fnmatchcase@256:memo 保留 token/输入位置,正类按多个消费长度递归;负类在 @279 仅以 lengths.length === 0 决定,尚未结合后续 token 就丢弃合法的一单位路径。第二个发现准确落在这个分支。
  3. 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;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 任意长候选会压掉合法负类续接。src/[!ß]S.ts 原生 glob 实际匹配 src/SS.ts(首个 S 不等于 ß,再接一个 S)。不可变基线四条 File/SQLite acquire/claim 路线全部拒绝且不改状态;当前头四条全部准入双独占租约。这里发现两字符 SS 折叠为 ß 后,lengths.length === 0 使整个负类失败,丢掉一个 S 的合法路径。请按消费长度与后续 token 判断,并保留 [!ß]x 的正确排除、请求身份及拒绝不写状态。

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants