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

发布时间:2026/9/8 23:55:48

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/9/8 23:55:48

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

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

2026/9/8 23:55:48

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

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

2026/9/8 23:55:48

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

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

2026/9/9 1:00:55

unibest + uview-plus 下 tabBar 图标不显示?完整排查与解决方案

unibest uview-plus 这套组合最近在 uni-app 社区里讨论热度很高,尤其从老项目往 Vue3 Vite 迁移的同学,基本都会遇到一个问题:pages.json 里 tabBar 配置得好好的,四个导航项的文字都出来了,但底部图标就是不展示。…

2026/9/9 1:00:55

HAWC2_Matlab_tools实战:风电载荷仿真数据从预处理到疲劳分析

简介:这套MATLAB工具集面向风电领域工程师与研究人员,针对丹麦DTU风能公司开发的空气弹性仿真规范HAWC2,提供模型预处理和结果后处理的整套脚本方案,可覆盖湍流风场文件读取、二进制转换、HDF5结果解析、雨流计数与疲劳统计等高频…

2026/9/9 1:00:55

微信小程序咖啡点单系统开发实战:支付对接与蓝牙打印

简介:这是一份用于学习微信小程序开发的完整星巴克咖啡门店界面源码,适合小程序初学者以及想提升移动端界面布局与交互设计能力的开发者。项目中通过WXML与WXSS构建了商品展示、购物车、订单处理、历史记录、个人中心等典型页面,并演示了内置…

2026/9/9 0:55:54

NVIDIA收购Hugging Face后,开发者部署、驱动与容器的技术变局

NVIDIA 以 129.3 亿美元收购 Hugging Face,这个数字刚出来的时候,我朋友圈里做 AI 的朋友基本分成了两派。一派觉得太贵了,一个模型托管平台凭什么值这么多钱;另一派觉得买便宜了,因为 Hugging Face 早就不是“AI 圈的…

2026/9/8 7:15:10

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/8 7:15:15

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/8 7:15:10

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/9 0:00:48

MHS模型硬件标准:让大模型像调用软件一样控制物理设备

让Claude真正看着显微镜说“这个细胞形态不太对”,或者让大模型自己调一版机械臂的运动轨迹,这事儿听上去已经很接近科幻片了。但你真上手试一次就会发现,模型不缺智商,缺的是一个能插进显微镜、机械臂、激光控制器里的“通用插座…

2026/9/9 0:00:48

AI五大核心方向详解:从机器学习到大模型,零基础转行选哪条?

会有人告诉我,他想转行学AI,但打开招聘网站一看直接傻眼:机器学习、深度学习、自然语言处理、计算机视觉、大模型应用……满屏都是这些词,好像每个都会一点,又好像每个都离自己很远。还有人上来就问“学Python还是学Ja…

2026/9/9 0:00:49

从50行最小循环到生产级AI引擎:工程化改造全解析

直接说干货。这一章我写的不是那种"hello world跑通某个模型"的教程,而是把AI引擎当做一个真正要上线、要被人调用、要扛流量的系统来聊。从最初只有50行的最小循环,到能够承载生产流量的AI引擎,中间差的不是代码量,而是…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/7 22:46:00

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/7 22:45:59

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

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

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

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