Skip to content

fix(run): authority-root 跨盘 symlink 下 create_dir_all 假阳性自愈,两处消息补路径 (#279) - #280

Merged
2233admin merged 2 commits into
mainfrom
agent/authority-root-symlink-279
Aug 18, 2026
Merged

fix(run): authority-root 跨盘 symlink 下 create_dir_all 假阳性自愈,两处消息补路径 (#279)#280
2233admin merged 2 commits into
mainfrom
agent/authority-root-symlink-279

Conversation

@2233admin

Copy link
Copy Markdown
Owner

Fixes #279.

问题

README/--help 的 quick start——code-intel .(等价于 code-intel . --mode lite --json)——在本仓自身跑会崩:

{"diagnostic":"create repository authority root: 当文件已存在时,无法创建该文件。 (os error 183)","exitCode":74,"outcome":"error", ...}

sentrux check . 等窄命令完全正常,只有 authoritative-run 的 authority-root 创建这一步坏。细节、复现过程、根因排查见 #279

根因

fs::create_dir_all(path) 在 Windows 上有一个反直觉行为:当 path 的某个祖先分量是指向另一块盘的 symlink 时,为一个从未存在过的子目录创建目录会返回 ERROR_ALREADY_EXISTS(os error 183)——目标在调用前后都不存在,立即重试也是同样的错误(不是竞态)。create_dir_all 自带的兜底("AlreadyExists 就查 path.is_dir(),是目录就当成功")救不了这个场景,因为 is_dir() 也是穿过同一个 reparse point 解析的,同样返回 false

两处调用点用同一条消息模板把这个 IO error 直接折叠成裸文本,一个路径都不带:

  • crates/code-intel-cli/src/project_context.rsProjectContext::run())——quick start 实际经过的这一处
  • crates/code-intel-cli/src/authoritative_run/execution_kernel.rsnest_authority_root()run execute 用的姊妹调用点)

这和 #145 修的缺陷同源:把一个具体的 IO 情况塞进一个笼统的 error 分支,吐出不可执行的裸 OS error。#145 修的是发布一个已完成的 run 撞到已存在的发布名;这次是创建 authority root 目录本身这一步——调用点不同、症状也不同(这次多数情况下目标根本不存在,说"已经发布在这里"是假话),但缺陷类是同一个。

改动

  • artifacts.rs 新增 pub(crate) fn ensure_directory(path) -> io::Result<PathBuf>:正常情况直接 create_dir_all 后原样返回 path(对普通、非 symlink 场景零行为变化);命中"AlreadyExists 但 is_dir() 也是 false"这个 Windows 症状时,对最近一层已存在的祖先目录 canonicalize 后重试,返回解析出的真实路径;真撞车(比如目标被同名文件占用)时原样把底层 io::Error 传回去,不吞。
  • project_context.rs::run()execution_kernel.rs::nest_authority_root() 两处都换成调用这个共享 helper,并且都把目标路径补进错误消息(之前两处都是零路径上下文的裸 OS error,同一个缺陷类)。调用方现在用 helper 返回的(可能被解析过的)路径继续走下去。
  • 退出码维持 74(ProjectError::host_io / RunError::io)不变——run_error.rs 自己的注释已经把 74 定义为"host 侧真出问题",这本来就是正确分类,不需要像 fix(run): 发布名撞车给出独立退出码 73 与可解析 stdout,终结「exit 65 + 零字节」 #145 那样新开一档退出码。

为什么不是照抄 #145 的"就是消息写清楚":多数真实触发场景下(跨盘 symlink)目标压根不存在,"已经发布在这里,换个名字或删掉" 是假话,会把人往错误方向引导。看了 authority root 的创建/使用方式后发现:create_dir_all 本来就是为了"这个容器目录该被复用"这个语义选的(相对于会在已存在时报错的 create_dir)——bug 不是"没把已存在的 root 当可复用",而是这份幂等性在这一种 Windows 症状下被打破了。所以修法是把幂等性真正修好,而不是把假阳性包装成一条更好看的报错。

测试

新增:

  • artifacts_tests.rsensure_directory 的三个可移植单测(新建、幂等、真文件挡路时报错)+ 一个 #[cfg(windows)] 尽力而为单测(真实复现过一次,但发现这个 Windows 症状依赖某种"陈旧"OS 状态——同进程现建的跨盘 symlink 无论对哪块盘都摇不出来,只有长期存在的那个稳定复现;测试如实尝试,摇不出来就 skip 而不是断言假象)+ 一个纯路径运算的确定性单测覆盖恢复逻辑本身。
  • project_context/tests.rsexecution_kernel.rsmod tests:各一个单测,用"目标位置被同名文件占用"这个可移植、确定性的方式验证真撞车时报错仍然失败,且消息里带上了被挡住的路径。
  • tests/primary_entry.rsroot_entry_names_the_authority_root_path_when_a_file_blocks_it,走编译后的二进制端到端复现 code-intel . 这条调用链,同样用文件占位的方式验证。

cargo check -p code-intel --tests:clean。所有新增/相关测试单独跑通过;cargo test -p code-intel --locked 全量结果见下方评论(或 CI)。legacy/tools/check-hardcoded-paths.ps1:OK(129 files;本 PR 未新增/修改任何 .ps1/.psm1/.md/.yml,也未触碰 orchestration/**/*.json 的任何 digest pin)。

DR-0001 适用性

这不是"装不上/装上跑不了"那一类——普通 checkout + cargo build --release + 直接跑二进制就能稳定复现,--helpsentrux check 都正常。不需要进 install-smoke 闸;已在 #279 里写明这个判断。

Sentrux 会话闸

legacy/Invoke-SentruxAgentTool.ps1 session_start "." 已在改动前跑过(baseline 已存,gate pass);session_end 将在推送前跑,本 PR 不会带着未过 session_end 的改动。

@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown

Code Intel change risk

Score Percentile Level
46/100 17th (vs last 48 commits) 🟢 low

Top signals

  • Diff shape: 6 file(s), +378/-8 (max file share 0.44)
  • Test asymmetry: source changed, tests touched
  • Bug-magnet: 3 fix commit(s) in touched files (180d)
  • Churn: 21 commit(s) touching these files (90d)

revspec: origin/main..HEAD · threshold: score >= 80 blocks unless labeled risk-accepted; percentile is reported, not gated (#201) · code-intel change risk

@repowise-bot

repowise-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown

✅ Health of changed files: 7.2 → 7.4 (+0.2)
🚨 Change risk: high, riskier than 79% of this repo's commits.

📋 At a glance
2 files changed health · 5 hotspots touched · 3 new findings introduced · 2 files with recent fix history · 1 dead-code finding. Scoped to crates.

✅ Health gate: passed

🔎 More signals (3)

🗺️ Change map

flowchart LR
  subgraph PR ["Changed in this PR (3 with dependents)"]
    f_crates_code_intel_cli_src_artifacts_rs[".../src/artifacts.rs 🔥"]:::changed
    f_crates_code_intel_cli_src_project_context_rs[".../src/project_context.rs 🔥"]:::changed
    f_crates_code_intel_cli_tests_primary_entry_rs[".../tests/primary_entry.rs"]:::changed
  end
  f_crates_code_intel_cli_src_main_rs[".../src/main.rs"]
  f_crates_code_intel_cli_src_artifacts_rs --> f_crates_code_intel_cli_src_main_rs
  f_crates_code_intel_cli_src_run_cli_rs[".../src/run_cli.rs"]
  f_crates_code_intel_cli_src_artifacts_rs --> f_crates_code_intel_cli_src_run_cli_rs
  f_crates_code_intel_cli_src_cli_primary_rs[".../cli/primary.rs"]
  f_crates_code_intel_cli_src_project_context_rs --> f_crates_code_intel_cli_src_cli_primary_rs
  f_crates_code_intel_cli_src_cli_project_query_rs[".../cli/project_query.rs"]
  f_crates_code_intel_cli_src_project_context_rs --> f_crates_code_intel_cli_src_cli_project_query_rs
  f_crates_code_intel_cli_src_project_context_rs --> f_crates_code_intel_cli_src_main_rs
  f_crates_code_intel_cli_src_mcp_serve_handlers_rs[".../mcp_serve/handlers.rs"]
  f_crates_code_intel_cli_src_project_context_rs --> f_crates_code_intel_cli_src_mcp_serve_handlers_rs
  f_crates_code_intel_cli_src_cli_command_catalog_mod_rs[".../command_catalog/mod.rs"]
  f_crates_code_intel_cli_tests_primary_entry_rs --> f_crates_code_intel_cli_src_cli_command_catalog_mod_rs
  f_crates_code_intel_cli_tests_primary_entry_rs --> f_crates_code_intel_cli_src_cli_primary_rs
  f_crates_code_intel_cli_tests_primary_entry_rs --> f_crates_code_intel_cli_src_main_rs
  more(["+2 more dependents"])
  PR --> more
  classDef changed fill:#dbeafe,stroke:#1d4ed8,color:#1e3a5f
  classDef warn fill:#fef3c7,stroke:#b45309,color:#78350f
  classDef guard fill:#dcfce7,stroke:#15803d,color:#14532d
Loading

Solid arrows: code that imports the changed files (8 direct dependents, from the last indexed snapshot). Dashed: history/tests.

🔥 Hotspots touched (5)

  • .../authoritative_run/execution_kernel.rs: 4 commits/90d, 1 dependents
  • .../tests/primary_entry.rs: 10 commits/90d, 4 dependents
  • .../src/project_context.rs: 1 commits/90d, 7 dependents
2 more
  • .../project_context/tests.rs: 1 commits/90d, 1 dependents
  • .../src/artifacts.rs: 4 commits/90d, 3 dependents

💀 Dead code (1 finding)

  • 💀 .../authoritative_run/execution_kernel.rs (file-level) (confidence 0.40)

📊 See the full report for this PR
Your repo map with this PR's blast radius lit up, every caller of the contracts it changes, and health before and after. No sign-in. · ⭐ Star Repowise · 📥 Install bot · Silence on a single PR with [skip repowise] in the title · Per-repo toggle on repowise.dev/settings?tab=bot · Updated 2026-08-18 12:37 UTC

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@2233admin, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 57 minutes

Limit details: You’ve used all 1 included review currently available under your plan.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f965fd33-0ec7-43ac-88f8-b02c0b04831f

📥 Commits

Reviewing files that changed from the base of the PR and between a4251a8 and a0104f9.

📒 Files selected for processing (6)
  • crates/code-intel-cli/src/artifacts.rs
  • crates/code-intel-cli/src/artifacts_tests.rs
  • crates/code-intel-cli/src/authoritative_run/execution_kernel.rs
  • crates/code-intel-cli/src/project_context.rs
  • crates/code-intel-cli/src/project_context/tests.rs
  • crates/code-intel-cli/tests/primary_entry.rs
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@2233admin

Copy link
Copy Markdown
Owner Author

收口补充:加 agent-approved 后 agent-gate 已通过,但当前 base main 仍使用旧 percentile gate,本次 change-risk 以 percentile 94 失败。正确依赖是先整合 PR #202(将 gate 切换到绝对 score),再让本 PR 在新 base/current head 上重跑;不使用 risk-accepted 绕过旧 gate。

@2233admin
2233admin force-pushed the agent/authority-root-symlink-279 branch 2 times, most recently from 46807c1 to 261e369 Compare August 18, 2026 12:35
2233admin and others added 2 commits August 18, 2026 20:37
…s,两处消息补路径 (#279)

`code-intel .` quick start 在 authority root 是跨盘 symlink 时首次调用就撞
"create repository authority root: ... already exists (os error 183)"
exit 74——`CreateDirectoryW` 对经过跨卷 symlink 的全新子目录会误报
ERROR_ALREADY_EXISTS,`create_dir_all` 自带的 is_dir() 兜底救不了,因为
is_dir() 走的是同一个 reparse point。

新增 artifacts::ensure_directory 对最近已存在祖先目录 canonicalize 后重试
自愈;project_context.rs::run() 与 execution_kernel.rs::nest_authority_root()
两处共享调用点都换过去,真撞车时错误消息补上目标路径。退出码维持 74 不变。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…t --check 挂了)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@2233admin
2233admin force-pushed the agent/authority-root-symlink-279 branch from 261e369 to a0104f9 Compare August 18, 2026 12:37
@2233admin
2233admin merged commit 21fe3f2 into main Aug 18, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-approved 人已审过该 agent PR,允许合并

Projects

None yet

1 participant