Skip to content

fix(runtime): keep Windows lock records out of route discovery - #6658

Merged
huangruiteng merged 1 commit into
loopx-project:mainfrom
JasonBuildAI:codex/agent-t8m5
Oct 16, 2026
Merged

huangruiteng merged 1 commit into
loopx-project:mainfrom
JasonBuildAI:codex/agent-t8m5

Conversation

@JasonBuildAI

Copy link
Copy Markdown
Contributor

Goal

Close the remaining Windows case of the phantom two-root conflict that f955e8305 fixed for .lock/.ts-effect.lock: a released Windows lock-holder record (or an incident log) beside a root's own global registry must not be read back as machine state, so a Windows machine whose work has settled on one default runtime root stops refusing every implicit-default command (loopx status, update check, usage-ping status).

Closes #6657.

What changed

  • loopx/paths.py: _is_route_observation now recognizes the documented lock-metadata family beside the root's own global registry — registry.global.json.lock, registry.global.json.lock.holder, the legacy registry.global.json.lock.holder.json, registry.global.json.lock.incidents.jsonl, registry.global.json.ts-effect.lock — on every platform, so an archive restored on another host cannot re-create the conflict either. Redirected entries, unknown files and real registries keep their authority.
  • tests/test_local_state_migration.py: extends test_own_registry_locks_do_not_declare_a_second_authority and test_observations_and_consumer_locks_do_not_claim_a_second_runtime with the holder / legacy-holder / incident spellings, and adds test_a_real_lock_round_trip_keeps_the_default_route_fresh, which drives the real exclusive_file_lock producer — the writer behind Goal configuration — so the producer→consumer contract is pinned instead of hand-placed files.

Evidence

Before (pre-fix tree, sandbox with both default roots redirected to a disposable temp tree):

[OK]  lock only (control):        status='legacy'
[BUG] lock + released holder:     status='conflict'
[BUG] lock + legacy holder.json:  status='conflict'
[BUG] lock + incidents.jsonl:     status='conflict'
[OK]  lock + ts-effect.lock:      status='legacy'

After (this head):

[OK]  lock only (control):        status='legacy'
[OK]  lock + released holder:     status='legacy'
[OK]  lock + legacy holder.json:  status='legacy'
[OK]  lock + incidents.jsonl:     status='legacy'
[OK]  lock + ts-effect.lock:      status='legacy'

Negative case — the new guards against the unfixed paths.py (source fix stashed, tests kept):

FAILED tests/test_local_state_migration.py::test_observations_and_consumer_locks_do_not_claim_a_second_runtime
FAILED tests/test_local_state_migration.py::test_own_registry_locks_do_not_declare_a_second_authority[registry.global.json.lock.holder]
FAILED tests/test_local_state_migration.py::test_own_registry_locks_do_not_declare_a_second_authority[registry.global.json.lock.holder.json]
FAILED tests/test_local_state_migration.py::test_own_registry_locks_do_not_declare_a_second_authority[registry.global.json.lock.incidents.jsonl]
FAILED tests/test_local_state_migration.py::test_a_real_lock_round_trip_keeps_the_default_route_fresh

Real-host readback on the reporting machine (read-only, no state written): status: legacy, selected_runtime_root: %USERPROFILE%\.codex\loopx, and loopx status with implicit defaults completes with exit 0. Pre-fix, the same host refused with the two-root conflict message, and loopx usage-ping status died with an unhandled ValueError traceback (both in #6657).

Validation

At exact head 5ccf12f2f on Windows 11 (10.0.26100), Python 3.12.12:

  • uv run --extra test python -m pytest tests/test_local_state_migration.py tests/test_file_lock.py tests/test_doctor_installation_scope.py → 14 failed, 101 passed, 3 skipped. The 14 failures are host artifacts (symlink privilege WinError 1314, Windows path-quoting/separator rendering), not new: the pristine run of this file alone gave the identical 14 failed, 67 passed, and neither loopx/paths.py nor this test file changed between that baseline 488e4438e and the push base b2aca67d7 (git diff 488e4438e b2aca67d7 -- loopx/paths.py tests/test_local_state_migration.py is empty). The four new guards are among the passes (-k run: 7 passed; the same run against the unfixed source fails 5, see above).
  • uv run python -m ruff check loopx/paths.py tests/test_local_state_migration.py → clean
  • uv run python scripts/generate_semantic_inventory.py --changed-from HEAD (on the working diff) → changed sources: 1 paths, 1 snapshots; supported candidates: 0 — no new vocabulary carriers

Scope

Checklist

  • DCO sign-off on the commit (git commit -s)
  • Focused tests, negative case, honest before/after evidence
  • No private state, credentials, raw logs or local absolute paths in the diff

Drafted with AI assistance at my request, under my direction and review.

A Windows lock acquisition keeps a released holder record beside the
global registry (`registry.global.json.lock.holder`, legacy
`.lock.holder.json`) plus a timeout incident channel, and route discovery
only knew the `.lock` and `.ts-effect.lock` spellings. Goal configuration
locks every candidate runtime registry before writing, so on a Windows
machine whose other default root was still empty the untouched root held
only lock metadata -- and the route read that metadata back as machine
state, reported a phantom two-root conflict, and refused every
implicit-default command (`loopx status`, `update check`, `usage-ping
status`, the last one with an unhandled ValueError traceback). f955e83
fixed the same failure for the lock spellings it knew; the holder record
Windows keeps is the remaining gap.

Classify the documented lock-metadata family
(docs/reference/protocols/file-lock-acquisition-v0.md) as observations
on every platform, so an archive restored on another host cannot read a
sidecar back as a second authority either. Redirected entries, unknown
files and real registries stay authoritative for discovery.

Cover the regression with the spelled sidecar names, the observation
list, and a real `exclusive_file_lock` round trip that pins the producer
(`file_lock`) to the consumer (route discovery); the new guards fail on
the unfixed code and pass on POSIX and Windows.

Drafted with AI assistance at the maintainer's request, under their
direction and review.

Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
JasonBuildAI added a commit to JasonBuildAI/loopx that referenced this pull request Oct 16, 2026
…cation (issue loopx-project#6657, PR loopx-project#6658)

Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>

@JasonBuildAI JasonBuildAI left a comment

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.

Independent subagent review — exact head 5ccf12f2f

An independent review agent (fresh context, author summary not trusted) verified this head against the repository, the issue and the sandbox. Six-question verdicts:

# Question Verdict
1 Defect real and correctly root-caused PASS
2 Fix correctness, no masking, no missed producer spelling PASS
3 Tests catch the defect (negative case) PASS
4 Boundary and scope (files, DCO, base/head, Closes) PASS
5 Collisions (issues, open PRs, coordination registry) PASS
6 Honesty of the PR's evidence claims PASS

Key independent evidence:

  • Extracted the base tree (b2aca67d7) to scratch and reran the sandbox there: lock + released holder / legacy holder.json / incidents.jsonl → conflict on base, legacy on this head; the control case and .ts-effect.lock were unchanged. Both runs printed the source tree they imported.
  • Ran the real producer (exclusive_file_lock, exclusive_cross_runtime_file_lock, and a real timeout path) against a scratch registry and evaluated every resulting file through paths._is_route_observation: the Windows holder record and the incident channel are recognized; POSIX writes only the already-recognized lock file.
  • Sensitivity: the head tests against the unfixed source → 5 failed, 2 passed; an unknown file, an unknown subdir, a recognized name as a directory, and a real registry all still flip a metadata-only root to a conflict (no masking).
  • Focused suites: 7 passed (route guards); wider focused run 14 failed, 101 passed, 3 skipped, and the 14 failures reproduce on the unfixed base (host artifacts: WinError 1314 symlink privilege, Windows path-quoting/separator rendering).
  • Scope: b2aca67d7..5ccf12f2f is exactly one signed-off commit touching only loopx/paths.py and tests/test_local_state_migration.py; an open-PR scan found no other PR touching either file; the coordination-registry entry agent-t8m5 matches this head and distinguishes the earlier unrelated agent-t6m2 refusal claim.

Residual risks recorded by the reviewer (non-blocking):

  • Crash-only lock artifacts (.claim.* / .stale.* / .released.*, write-probe temps) can still resurrect the phantom conflict after a hard kill; the direction is fail-safe (the route refuses) and pre-existing.
  • The classifier ignores lock metadata even for a live holder, consistent with the pre-existing .lock handling where the kernel lock is authoritative.
  • POSIX behavior is code-evident (no os.name branch; plain-file tests) but was not executable on this Windows-only host; the real-host loopx status exit code was verified as "selection no longer raises" rather than rerunning the command against user state.

Review performed by an independent agent session at my request; verdicts above are its findings, sanitized of host-specific paths.

@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

动机

使用默认运行目录、曾在另一个默认目录留下 Windows 锁记录的 LoopX 使用者。用户已经把真正的配置放在一个默认目录,另一个目录仅有已释放的锁记录;原来后续 status 仍被误判为两个权威目录而拒绝。当前提案识别这组自有锁元数据,使原命令直接回到真正的配置;发现陌生文件或目录重定向时仍要求明确选择。

隔离目录中的真实隐式 status,在 base 因锁旁文件返回冲突;当前源码、普通 wheel 和 sdist 均选择原有 registry 并返回 0。添加陌生文件后仍拒绝,移除该文件后同一命令恢复,无需删除锁记录。

本次修复目录发现的错误分类,不清理磁盘文件,不改锁所有权,也不把尚未注册的空机器声称为健康;原生 Windows 内核锁与 junction 测试没有在本机执行。

评审对象为完整 head 5ccf12f2fd51744131ded56abb020fe2608c3e25,基准为实际 merge base b2aca67d76af45e554c620818a982a81f8d2d52b。先读原始问题与既有协议,再独立检查整个提案;作者已发布的自评只作为待核实的材料,不继承其结论。验收来源:#6657,版本 2026-10-16T14:23:32Z,依据原文的 Expected。

改动思路

这是现有运行目录发现器对既有锁生产者的分类遗漏,最小修复应留在该读边界;精确文件名集合与既有重定向检查组合即可,不需要删除记录或引入第二套路由状态。

当前 PR 交付锁旁记录、跨宿主恢复记录的识别及真实命令的拒绝和恢复验证;锁实现、目录迁移、异常崩溃清理与其他命令的报错格式保持各自原有维护边界。

判定依据是文件的既有生产者和路径类型,而不是记录内容是否“看起来无害”。名字恰好匹配还不够:重定向检查必须先发生,且对象须为普通文件。这样既能恢复正常默认路径,也能保留相反方向的安全边界:陌生状态不能因为旁边有合法锁记录就被隐藏。

协议基准为 docs/reference/protocols/file-lock-acquisition-v0.md 在 0ab7feb223f35b4a74175b5aee2909fd55aebd4a 的 holder 与 incident 约定:Windows 释放后保留 holder 是预期行为,内核锁才是所有权,读者仍需兼容旧 .holder.json。该 PR 修复读者分类,不能删除锁文件来“解决”冲突。

具体改动

_is_route_observation() 继续在最前面调用 _is_redirected_path(),然后只识别当前根自身 registry.global.json 的五种既有元数据文件:.lock、.ts-effect.lock、.lock.holder、.lock.holder.json、.lock.incidents.jsonl。只有 is_file() 为真才是观察记录。没有使用后缀通配、包含某段文字的黑名单或依赖 holder 内容的主观判断。_runtime_root_has_machine_state() 仍遍历所有直接项,default_runtime_route() 仍用同一 RuntimeRouteStatus 处理真实双根状态与无效根。

测试扩展了已有观察目录案例及自有锁参数化案例,并增加真实 exclusive_file_lock 生产者往返,覆盖新机器两个候选根只留下锁的实际起点。这里复用现有迁移测试文件与真实写入者;因 file_lock 反向导入 paths,局部列出稳定生产者文件名有明确原因,提取一个新注册中心反而会扩大这次范围。总共两个改动文件,没有新命令、开关、持久状态或新的一套 TypeScript 路由权威。

独立 base/head 宽测试分别为 102/106 通过、各 5 个同条件原生 Windows 跳过。把 head 的七个相关守卫测试原样交给 base 产品,得到 4 失败、3 通过,四处失败都对应新增元数据被当作第二个状态根;相同测试在 head 通过。macOS 上真实 POSIX 锁生产者仍使空根保持 fresh;真实 portable holder 写入函数加跨宿主归档样本则验证 Windows/旧 holder/incident 三种拼写的消费者规则。这里没有把 POSIX 内核锁执行声称为 Windows 内核锁执行。

更关键的是用户入口:在独立 home、真实 global registry 上,经正常隐式 loopx status 及原 registry codec,base 因旁文件报双根冲突,head 与普通 wheel/sdist 均回到原 legacy registry、退出 0。加入陌生文件后退出 1 并保留真实冲突说明,移除它后同一命令重新退出 0。命令没有额外传 registry/runtime 参数绕过缺陷,也没有清理锁记录。包内改动产品文件与源码、归档及安装逐字节相同。

还验证了陌生 .json、看似相近的 holder 后缀、合法名字却为目录、指向外部的 holder 符号链接,均继续声明真实状态或冲突;后加入的未知项同样不能逃过检查。外部目标内容未改。仅有锁的新机器虽然路由已正确回到 fresh,尚无注册表的 status 仍报告缺少 registry;这不是假冒健康,也没有被汇总成“所有命令都成功”。

对主干的风险

核心风险是把真正的第二个状态根过滤掉。精确对象名、普通文件限制及提前重定向检查共同收紧范围;真实第二 registry 的原测试、陌生目录与链接负例,以及添加真实状态后拒绝、删除该状态后恢复的实测,覆盖了修复误拒绝时容易漏掉的另一方向。已有文件内容、内核锁所有者及历史记录都不被本 PR 改写,跨平台归档识别也没有引入新的选择权限。

本机五个 Windows kernel/路径/junction 相关用例继续按原条件跳过,作者的 Windows 运行记录不作为本机执行计数。真实路由读者、portable metadata 生产者与隐式 CLI 已在本机实测;全部 Windows API 兼容性不在这一结论内。异常中止生成的其他未知临时项仍会保守地形成状态,未扩大精确名单去吞掉它们;这属于原有单独维护边界。

head 的语义 advisory 先于完整语义检查,正常 Chat 构建、wheel/sdist 构建与独立安装、改动文件编译和 diff 检查都通过。native canary 为 5 项直接检查、2 项选择检查,无阻塞失败;1 项 DSH 维护性 advisory 保留。独立运行 ratchet 在 base/head 的失败身份、完整内容及未改因果文件摘要一致,均为 _run_dsh_operation_host 超大函数,没有调阈值。最初旧忽略资源造成包构建拒绝,随后按正常 Chat 构建刷新再打包成功,未改产品源码或断言。没有查询、轮询、等待 GitHub CI,也没有在真实用户根目录做故障注入。

我的整体评价

APPROVE。它直接解除普通配置后持续存在的误拒绝,且真实用户命令、普通安装包和恢复步骤都有对应证据;未知状态与重定向仍保持原拒绝边界。未来整理已检查:继续复用当前小型分类 helper 与已有 RuntimeRouteStatus,暂不引入新抽象,保留有实际存量消费方的旧文件名兼容。原 issue 的 Expected 在这个明确边界内满足;不把代码批准当作锁迁移、全宿主兼容性或合并授权。

English verdict: APPROVE - exact head 5ccf12f2fd51744131ded56abb020fe2608c3e25. Own persisted lock metadata no longer creates a phantom route conflict. Actual implicit CLI source/wheel/sdist readback and unknown/redirected-state refusal/recovery pass; native Windows kernel coverage remains separate.

@huangruiteng
huangruiteng merged commit 3499434 into loopx-project:main Oct 16, 2026
25 of 28 checks passed
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.

[Bug]: native Windows: a released lock-holder record is read back as machine state, so a single-route host refuses every implicit-default command

3 participants