fix(cli): Keep transient runtime record I/O retryable - #9362
Conversation
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
🩺 serve daemon A/BBuilt the PR base vs this PR head — Qwen Code · serve A/B |
|
Thanks for the PR!
Moving on to code review. 🔍 中文说明感谢贡献!
进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewMy independent proposal for this problem was exactly what this PR does: classify errno at the two record readers' What I verified by tracing consumers, since this is security-adjacent fail-closed code:
No critical findings. Not verified statically: kernel-level CI evidenceThe PR's own CI at the reviewed commit — quoted from the check-runs API: Final CI results for
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 All five red checks failed in setup, before any PR code ran: four died downloading Sandboxed verification would settle the one gap CI cannot: the central claim here is behavioural (a transient Real-scenario tmux: N/A — this changes daemon-internal error classification that only surfaces under injected 中文说明代码审查我对该问题的独立方案与本 PR 完全一致:在两个记录读取器的 这是安全相关的 fail-closed 代码,我逐一追踪了消费方:
无 Critical 发现。静态未验证:Windows/Linux 上的内核级 CI 证据上表引自 reviewed commit 的 check-runs API。五个红色检查全部在 setup 阶段失败、未运行任何 PR 代码:四个在下载 沙箱验证可补上 CI 无法覆盖的一环:本 PR 的核心主张是行为性的(瞬时 真实场景 tmux 测试:N/A——本改动是守护进程内部的错误分类,仅在记录读取时注入 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — clean, minimal, well-pinned fix for a documented latch bug; the two remaining notes are non-blocking (infra-red CI needs a re-run before merge, and errno behavior beyond the mocked injection is unexercised). Stepping back: this is the follow-up those two Critical findings on #9181 deserved. The probe witnesses there showed a transient Two honest reservations, neither blocking. First, the five red CI checks all died in setup on Approving, pinned to the reviewed commit. 中文说明置信度:4/5 —— 针对已记录的锁存 bug 的干净、最小、测试钉扎良好的修复;剩余两点保留意见均非阻塞(CI 因基础设施飘红需重跑才能合并;mock 注入之外的 errno 行为未实测)。 退一步看:这正是 #9181 那两条 Critical 意见应得的后续修复。当时的探针证据显示一次瞬时 两点诚实的保留意见,均不阻塞。其一,五个红色 CI 检查全部在 setup 阶段因 按 reviewed commit 批准。 — Qwen Code · qwen3.8-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
yiliang114
left a comment
There was a problem hiding this comment.
Reviewed the full diff at 385e697d and ran the targeted suite locally: 27 passed, 1 skipped (platform-gated, pre-existing).
What I checked:
- Error mapping is correct end-to-end: rethrown open/read errors (EMFILE/EIO) fall through
mapAcquireErrortoconversationRuntimeUnavailableError(retryable), while ELOOP, malformed JSON, and identity changes still map to compromise (terminal). Same shape inreadExistingRecordviaLiveDiscoveryStateError. - Moving
handle.readFileoutside the JSON.parse try is the right split — parse failures stay terminal, I/O failures become retryable. - Tests inject at both open and readFile layers for both record types and verify recovery on the second acquisition, plus ELOOP staying terminal.
LGTM.
|
Released in v0.21.14. |
What this PR does
This PR preserves fail-closed integrity checks while distinguishing transient ownership-record I/O failures from invalid persisted state. Resource-exhaustion and read I/O errors can now surface as retryable unavailability and recover on a later acquisition, while symlink loops, file identity changes, malformed JSON, and invalid record shapes remain terminal.
Why it's needed
The secure record readers introduced by #9181 treated every open and read failure as evidence of corruption. A one-off
EMFILEorEIOtherefore latchedconversation_runtime_ownership_compromisedfor the daemon lifetime even when the record was valid, preventing Conversations and Live recovery until restart. This follows up on two Critical review findings that arrived immediately before #9181 was merged.Reviewer Test Plan
How to verify
Exercise ownership acquisition with an existing dead owner record and a stale Live discovery record. For each record type, inject
EMFILEduring open andEIOduring file-handle read: the first acquisition should returnconversation_runtime_unavailablewithretryable: true, and the second should reclaim successfully after the injected fault clears. InjectingELOOPduring open should still returnconversation_runtime_ownership_compromisedwithretryable: false. Existing malformed-record, unsafe-file, and identity-change cases should remain terminal and must not overwrite state.Local validation passed 42/42 targeted tests together with the repository build, typecheck, lint, formatting, and diff checks. An independent probe reproduced all four permanent-latch paths on the base commit and confirmed recovery with this change.
Evidence (Before & After)
N/A (non-UI daemon reliability fix).
Tested on
Environment (optional)
Node.js 22-compatible repository toolchain on macOS; local npm workspace build, typecheck, lint, and targeted Vitest tests.
Risk & Scope
ELOOP, lock compromise, permission and identity changes, malformed content, and provisional ownership failures terminal.Linked Issues
Follow-up to #9181. Addresses the owner-record Critical finding and the Live discovery Critical finding.
中文说明
本 PR 的改动
本 PR 在保留完整性校验 fail-closed 语义的同时,区分所有权记录的瞬时 I/O 失败与无效持久化状态。资源耗尽和读取 I/O 错误现在会表现为可重试的暂时不可用,并可在后续 acquisition 中恢复;符号链接循环、文件身份变化、JSON 损坏和记录结构无效仍保持终态。
为什么需要此改动
#9181 引入的安全记录读取逻辑把所有 open 和 read 失败都视为状态损坏。即使记录本身有效,一次瞬时
EMFILE或EIO也会在整个守护进程生命周期内锁存conversation_runtime_ownership_compromised,导致 Conversations 和 Live 必须重启才能恢复。本修复跟进 #9181 合并前最后出现的两条 Critical 评审意见。审查者测试计划
验证方式
分别使用已有的失效 owner 记录和过期 Live discovery 记录执行所有权 acquisition。对每种记录,在 open 阶段注入
EMFILE,在文件句柄读取阶段注入EIO:第一次 acquisition 应返回conversation_runtime_unavailable且retryable: true,故障清除后的第二次 acquisition 应成功回收记录。open 阶段注入ELOOP时仍应返回conversation_runtime_ownership_compromised且retryable: false。现有的记录损坏、不安全文件和身份变化场景仍应保持终态,且不得覆盖原状态。本地验证已通过 42/42 个定向测试,以及仓库 build、typecheck、lint、格式和 diff 检查。独立探针在基线提交上复现了全部四条永久锁存路径,并确认本改动可以恢复。
证据(改动前后)
N/A(非 UI 的守护进程可靠性修复)。
测试平台
环境(可选)
macOS 上兼容 Node.js 22 的仓库工具链;本地 npm workspace build、typecheck、lint 和定向 Vitest 测试。
风险与范围
ELOOP、锁损坏、权限和身份变化、内容损坏以及 provisional 所有权阶段的失败视为终态,以降低该风险。关联问题
本 PR 是 #9181 的后续修复,处理 owner 记录的 Critical 评审意见和 Live discovery 的 Critical 评审意见。