get-shit-done 并发锁重试白名单修复:acquireStateLock / withPlanningLock 对 Docker overlay-fs 与 NFS 瞬时 errno 的处理

发布时间:2026/10/23 11:49:43

get-shit-done 并发锁重试白名单修复:acquireStateLock / withPlanningLock 对 Docker overlay-fs 与 NFS 瞬时 errno 的处理 get-shit-done 并发锁重试白名单修复acquireStateLock / withPlanningLock 对 Docker overlay-fs 与 NFS 瞬时 errno 的处理【免费下载链接】get-shit-doneA light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.项目地址: https://gitcode.com/GitHub_Trending/getshi/get-shit-done本文基于 changeset 记录 .changeset/3776-fix-acquirestatelock-retry-allowlist.md深入剖析 get-shit-doneTÂCHES 出品的面向 Claude Code 的上下文工程与规格驱动开发系统中 STATE.md 与 .planning 工作区写入锁的实现。读者将掌握该修复引入的「可重试 errno 白名单」语义哪些文件系统错误码会在锁竞争时自动重试哪些致命错误码会立即抛出以及这套机制在 Docker overlay-fs、NFS 与 Windows/macOS 杀毒软件场景下的工程取舍。一、这次修复改了什么该 changeset 类型为Fixed关联 PR #3777关闭 issue #3776原文完整内容如下acquireStateLock与withPlanningLock现在会在瞬时性文件系统错误上自动重试新增的错误码覆盖Docker overlay-fs的ENOENT/EINVAL/EIO以及NFS的ESTALE此前已支持的EPERM/EBUSY重试行为保持不变真正致命、不可恢复的错误码EMFILE/ENOSPC/EROFS/EACCES仍会立即抛出绝不因重试而吞掉。一句话概括锁获取不再把「瞬时性存储故障」当成「锁被占用」以外的新错误盲目抛出也不再把「真正致命错误」拖入无休止重试而是给两者划出清晰边界。这一行为在两个运行时模块中各有镜像实现是本文后续逐一拆解的对象。二、为什么要修并发写入下的锁与错误码混杂问题get-shit-done 的规划与状态数据都落在工作区文件里例如STATE.md与.planning目录下的STATE.md/ROADMAP.md/PROJECT.md等。并行 Agent或同一用户的多个会话都会对这些文件做更新若直接裸写互相覆盖会造成丢失更新。因此两个关键模块都实现了基于锁文件的互斥机制state.cjs 中的acquireStateLock(statePath)为STATE.md写入提供互斥planning-workspace.cjs 中的withPlanningLock(...)为.planning规划工作区的操作加锁。两处都以O_CREAT | O_EXCL原子创建目标文件.lock用「谁先成功创建锁文件谁就持有锁」的朴素方式实现分布式互斥。但实际运行环境远比「锁文件已存在EEXIST」复杂——在容器、共享存储、桌面操作系统上open会抛出各种各样语义截然不同的错误而早期实现的处理方式过于粗糙由此引出系列回归#3772acquireStateLock在遇到非EEXIST的openSync错误如高负载下的EMFILE/EINTR/ENOSPC时静默返回一个假成功的锁路径调用方以为拿到了锁实际写入没有互斥保护造成丢失更新#3773EPERM/EBUSY典型场景是 Windows / macOS 杀毒软件临时占用锁文件被识别为可重试#3776本 changeset把 Docker overlay-fs 与 NFS 下的瞬时错误也纳入可重试范围同时坚决排除致命错误码。这些背景信息可从配套回归测试的头部注释得到印证tests/state-acquirestatelock-non-eexist.test.cjs 中明确写着该测试「为 #3772 编写并在 #3776 中扩展以覆盖 Docker overlay-fs 和 NFS 瞬时 errno 码」。三、重试白名单可重试 errno 与致命 errno 的完整清单修复的核心是一张集中维护的错误码集合常量。state.cjs中定义如下get-shit-done/bin/lib/state.cjs// Transient errno codes that indicate a temporary filesystem condition under // concurrent O_EXCL races — Docker overlay-fs (ENOENT/EINVAL/EIO), NFS // (ESTALE), and OS-level interrupt/retry signals (EAGAIN/EINTR). These are // recoverable; acquireStateLock retries instead of propagating them. // Truly fatal codes (EMFILE, ENOSPC, EROFS, EACCES) are NOT in this set and // will still throw immediately. const ACQUIRE_LOCK_RETRY_ERRNOS new Set([ EPERM, // Windows / macOS AV scanner holds the file open during delete EBUSY, // Windows: file in use by another process EAGAIN, // POSIX: resource temporarily unavailable EINTR, // POSIX: syscall interrupted by signal EINVAL, // Docker overlay-fs: transient during concurrent O_EXCL creation EIO, // Docker overlay-fs / NFS: transient I/O error ENOENT, // Docker overlay-fs: parent dir transiently missing during race ESTALE, // NFS: stale file handle (self-resolves on retry) ]);而在 planning-workspace.cjs 中存在内容与注释完全一致的PLANNING_LOCK_RETRY_ERRNOS供withPlanningLock使用。两套集合刻意保持同步这正是「runtime 层两模块共享同一锁策略」的直接证据。可重试 errno 分类表错误码触发场景据源码注释为什么可重试EPERMWindows / macOS 杀毒软件在删除期间持有文件属于瞬时占用稍后即释放EBUSYWindows文件正被另一进程使用占用结束时即可创建成功EAGAINPOSIX资源暂时不可用标准瞬时信号EINTRPOSIX系统调用被信号中断重试即可完成调用EINVALDocker overlay-fs并发O_EXCL创建期间的瞬时状态重试可越过竞争窗口EIODocker overlay-fs / NFS瞬时 I/O 错误存储层抖动重试即恢复ENOENTDocker overlay-fs竞争期间父目录瞬时缺失目录创建完成后重试成功ESTALENFS文件句柄过期重试会重新解析自行恢复仍立即抛出的致命 errno与白名单相对以下四个错误码刻意不进入集合一旦出现直接向上抛出这是本次修复与既有 #3772 语义一致的关键点错误码含义为什么立即抛出EMFILE进程文件描述符耗尽重试不会释放 fd只会加剧资源耗尽ENOSPC磁盘已满重试无法凭空腾出空间EROFS只读文件系统任何重试都不可能成功EACCES权限不足需要人工介入而非机械重试值得强调的是白名单的保守性集合对集合外的一切未知错误码保持「不匹配」从而走抛出分支避免把未曾预料到的错误误判成可重试、导致无限循环或假成功。这一点同样被测试显式锁定见下文 C6。四、源码级拆解acquireStateLock 的完整重试循环state.cjs中的acquireStateLock主体实现位于 get-shit-done/bin/lib/state.cjs关键工程参数如下参数值作用锁文件路径statePath .lock与目标状态文件同目录同前缀retryDelay200ms每次重试的基础等待时间staleThresholdMs1000010 秒判断锁是否由崩溃进程残留陈旧锁maxWaitMs3000030 秒对存活持有者的最长等待预算jitter0–50ms 随机打散多进程同时重试的羊群效应创建方式fs.constants.O_CREAT \| O_EXCL \| O_WRONLY原子创建杜绝「检查再创建」竞态其核心逻辑可拆解为以下流程尝试原子创建锁文件并写入持有者 PIDfs.writeSync(fd, String(process.pid))创建成功后把锁路径登记到进程级_heldStateLocks集合用于退出时清理避免崩溃留下陈旧锁注释中关联 #1916捕获错误后先查白名单if (ACQUIRE_LOCK_RETRY_ERRNOS.has(err.code)) { continue; }—— 命中瞬时错误码立即进入下一轮重试不抛出、不误判为锁占用非EEXIST且不在白名单 → 直接抛出if (err.code ! EEXIST) throw err;代码注释点明原因——「静默绕过会造成丢失更新」EEXIST锁真实存在进入陈旧锁判定读取锁文件的statSync().mtimeMs若已超过 10 秒陈旧阈值则尝试unlinkSync后重试崩溃持有者清理若在 stat 与 unlink 之间锁已被释放静默继续若锁仍被存活进程持有且等待已超过 30 秒maxWaitMs抛出超时错误消息中会带上锁路径与已等待毫秒数否则以retryDelay jitter休眠后进入下一轮。用一张简化的分支决策图理解捕获逻辑catch (err) ├─ err.code 命中 RETRY_ERRNOSEPERM/EBUSY/EAGAIN/EINTR/ │ EINVAL/EIO/ENOENT/ESTALE → continue立即重试 ├─ err.code ! EEXISTEMFILE/ENOSPC/EROFS/EACCES/未知→ throw └─ err.code EEXIST ├─ 锁文件 mtime 超过 10s陈旧 → unlink 后 continue ├─ 等待已超过 30s → throw 超时 └─ 否则 → sleep(200msjitter) 后 continue释放侧则由releaseStateLock完成get-shit-done/bin/lib/state.cjs从_heldStateLocks移除并尝试unlinkSync即使锁已被他人清理也静默容错。五、withPlanningLock 与规划工作区的一致性实现规划工作区是另一条同样高频的写入路径。在 planning-workspace.cjs 中PLANNING_LOCK_RETRY_ERRNOS与ACQUIRE_LOCK_RETRY_ERRNOS保持同一份 8 项白名单与同一份注释约定其捕获分支在 planning-workspace.cjs 处使用同样的PLANNING_LOCK_RETRY_ERRNOS.has(err.code)决策方式。withPlanningLock所保护的.planning目录布局在该文件中有明确定义planning-workspace.cjs规划根目录下包含STATE.md、ROADMAP.md、PROJECT.md、config.json、REQUIREMENTS.md与phases/子目录并支持GSD_PROJECT/GSD_WORKSTREAM环境变量实现多项目 / 多 workstream 隔离。锁定规划目录的写入是为了防止并行会话在这些关键产物上互相践踏——这是本 changeset 把重试语义同时落到两个函数的原因。六、SDK 侧的 TypeScript 镜像实现同一系统在 sdk/src/query/state-mutation.ts 提供 TypeScript 版acquireStateLock。两处实现策略略有分工值得对比运行时 CJS 版上文维护集中 errno 白名单通过「命中重试、非EEXIST抛出」严格区分瞬时错误与致命错误SDK TS 版以maxRetries 10200ms退避为上限专门处理EEXIST竞争并在注释中声明其非EEXIST分支采用「与 CJSstate.cjs对齐的降级语义」源码注释D3: Graceful degradation on non-EEXIST errors (match CJS state.cjs:889)同时内置「持有 PID 已死则解锁」与「锁文件 mtime 超过 10 秒则视为陈旧并清理」的启发式逻辑。两个镜像各自被其调用链使用CJS 版服务于安装/运行时工作流TS 版服务于 SDK 查询层被 phase-lifecycle.ts 等模块引用。需要读者留意的是本 changeset 针对的白名单重试修复落点主要是运行时 CJS 模块文章中的 errno 分类表与决策分支均以 state.cjs 为准。七、回归测试如何锁住这套契约配套测试 tests/state-acquirestatelock-non-eexist.test.cjs 是一个「architectural-invariant」架构不变量测试acquireStateLock是未导出的私有函数无法通过公开 CLI 稳定触发时序敏感行为因此测试采取源码级检查这一权威方式直接从 get-shit-done/bin/lib/state.cjs测试中通过path.join(__dirname, .., get-shit-done, bin, lib, state.cjs)定位提取函数体与常量集合逐项断言测试组断言内容C1非EEXIST错误必须throw不得返回假成功锁路径#3772 回归C2 / C3成功路径返回锁路径EEXIST重试/等待语义不受本修复影响C4a–C4fEAGAIN/EINTR/EINVAL/EIO/ENOENT/ESTALE必须在白名单中#3776 新增C5a–C5dEMFILE/ENOSPC/EROFS/EACCES必须不在白名单中致命C6未知错误码ESOMETHING不得进入白名单保守默认面向上层抛错C7a–C7bEPERM/EBUSY仍在白名单#3773 回归守护C8重试决策必须使用ACQUIRE_LOCK_RETRY_ERRNOS.has()而非旧的硬编码EPERM||EBUSY内联比较C8 尤为关键——它确保后续开发者不会退化成「改条件表达式」的脆弱写法而是持续维护唯一事实来源的集合常量。此外锁行为在更高层还有多套集成测试呼应如 concurrency-safety.test.cjs、planning-workspace.test.cjs 以及针对陈旧锁清理历史缺陷的 locking-bugs-1909-1916-1925-1927.test.cjs共同构成从错误码分类到并发互斥的完整验证体系。八、工程启示什么时候该重试什么时候该抛错从这次修复中可以提炼出三类经验适用于一切基于文件系统原子操作的分布式锁实现errno 不是非黑即白必须显式分类。EEXIST锁占用与EIO/ESTALE存储抖动虽然都让open(O_EXCL)失败但语义与恢复路径完全不同前者要走「等待 陈旧锁清理」后者应走「立即重试」而ENOSPC/EROFS这类错误重试只会放大故障。「假成功」比报错更危险。历史上最严重的问题不是抛错而是在异常时返回一个虚假的锁路径让上层误以为拿到互斥权导致并发写互相覆盖、状态文件损坏。任何拿不到锁的分支都必须显式要么重试要么抛出。白名单应保持保守并配测试守护。集合外未知错误码默认抛出对新增代码的归属判断交给源码级断言测试防止回归。九、总结与验证建议acquireStateLock/withPlanningLock的 errno 重试白名单修复是 get-shit-done 在异构运行环境Docker overlay-fs 容器、NFS 共享存储、Windows/macOS 桌面下保证状态文件写入互斥可靠性的关键一环。修复后瞬时文件系统抖动会被自动吸收而致命错误会第一时间暴露给上层避免静默损坏状态文件。若你正在排查本系统的锁超时或异常建议按如下路径复查确认运行环境命中哪类错误码——容器 overlay-fsENOENT/EINVAL/EIO、NFSESTALE还是杀毒软件占用EPERM/EBUSY对照 state.cjs 的ACQUIRE_LOCK_RETRY_ERRNOS检查期望的重试行为若出现「等待 30 秒后超时」错误错误消息会包含锁路径与已等待毫秒数可据此判断是陈旧锁清理失败还是存活持有者写死修改前先跑 state-acquirestatelock-non-eexist.test.cjs其中 C4/C5/C8 会直接守护白名单集合的完整性。【免费下载链接】get-shit-doneA light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.项目地址: https://gitcode.com/GitHub_Trending/getshi/get-shit-done创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/22 21:49:08

多模态情感分析工程落地:四模态对齐与16G显存部署实战

简介:本资源是一套完整的多模态融合情感分析实战项目,面向计算机专业本科生及人工智能初学者,聚焦文本、语音、图像与视频四模态数据的情感联合建模问题,适用于毕业设计、课程设计与期末大作业等高分实践场景。压缩包共20个文件&a…

2026/10/22 21:49:07

opencode 终端AI编程助手实战指南:安装、配置与高级玩法

最近后台陆续有人问 opencode 的安装和使用,我翻了翻公域关键词趋势,这个搜索量涨得确实快。先说结论:opencode 是一个开源的终端AI编程助手,它让你在命令行里直接跟大模型协作,改代码、跑命令、查报错、做回归测试都能…

2026/10/22 23:45:29

Python人脸识别系统实战:从OpenCV到face_recognition的完整指南

简介:一套基于Python的人脸识别系统完整工程,适合Python初学者、计算机视觉入门者以及需要快速搭建人脸识别Demo的开发者。资源覆盖从摄像头人脸采集、特征提取、数据库建库到实时识别比对的整套流程,并配有tkinter图形界面与运行说明文档&am…

2026/10/24 8:08:44

数据库范式详解:从1NF到3NF的设计规范与实践

1. 数据库范式的前世今生第一次接触数据库范式是在2008年做电商系统时,当时为了处理商品属性表的设计问题,被导师要求必须满足第三范式。那时的我完全不明白为什么要把一个简单的表拆分成三四个表,直到系统上线后遇到数据冗余和更新异常问题&…

2026/10/24 8:08:44

解决WSL安装中的SECURE_PCI_CONFIG_SPACE_ACCESS_VIOLATION蓝屏问题

1. 问题现象与初步排查那天我正在Windows 11上安装WSL(Windows Subsystem for Linux),突然遭遇了令人头疼的蓝屏,错误代码显示"secure_pci_config_space_access_violation"。这个错误通常与硬件访问冲突有关&#xff0c…

2026/10/24 8:08:44

昇腾AI开发:PyAsc与Ascend-C算子开发实战

1. 项目概述在昇腾AI处理器的开发生态中,CANN(Compute Architecture for Neural Networks)作为核心计算架构,为开发者提供了从底层硬件到上层应用的全栈支持。其中PyAsc与Ascend-C的组合使用,构成了当前最前沿的算子开…

2026/10/24 8:08:44

智能文献综述工具:百考通技术解析与应用指南

1. 学术研究者的痛点与文献综述的困境每个经历过学术写作的人都知道,文献综述部分往往是最耗时耗力的环节。上个月我刚帮一位博士生朋友审阅论文,发现他花了整整三个月时间整理的文献综述,依然存在关键研究遗漏和脉络不清的问题。这让我深刻意…

2026/10/24 8:03:44

电荷与电流的工程本质:从物理概念到电路设计实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/23 22:56:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/24 0:02:40

MATLAB缩进快捷键与批量格式化实战指南

1. 被自动缩进折磨的那些年如果你写过MATLAB代码,大概率经历过这种场景:从某个文档、网页或者聊天记录里复制了一段代码,粘贴进编辑器之后,每一行前面都带着莫名其妙的空格,有的行首是四个空格,有的是一个T…

2026/10/24 0:02:40

用Poco实现Qt风格信号与槽的跨平台C++方案

简介:本资源是一份面向C中级开发者的技术实践工程,聚焦于在非Qt框架环境下模拟实现Qt核心通信机制——信号与槽,借助Poco网络库的事件系统完成解耦式对象通信。适用于需在跨平台C项目中引入松耦合回调机制、但受限于无法集成Qt依赖的开发场景…

2026/10/24 0:02:40

ESXi裸机虚拟化安装指南:从硬件兼容到虚拟机部署

1. 为什么还要折腾ESXi:从一台闲置机器说起手里攒了几台淘汰下来的迷你主机,配置不算差,i5加16G内存,扔了可惜,装个桌面系统又觉得浪费。这种场景下,ESXi就成了一个绕不开的选择。它是一套裸机虚拟化平台&a…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑