首页
/ get-shit-done 文件操作安全策略演进实录:从 ADR-0010「文件操作引擎模块」到 Shell 命令投影接缝的收敛设计

get-shit-done 文件操作安全策略演进实录:从 ADR-0010「文件操作引擎模块」到 Shell 命令投影接缝的收敛设计

2026-09-08 15:31:38作者:劳婵绚Shirley

导读

本指南解读 get-shit-done(GSD,一个面向 Claude Code 的 meta-prompting 与 spec-driven 开发系统)中的一份关键架构决策文档:ADR-0010 提出的 File Operation Engine Module(文件操作引擎模块)。文档记录了安装器、迁移与规划链路中文件读写的安全策略(原子写、路径包含、锁文件、备份/回滚)如何从多处分叉收敛为单一接缝,以及该提案为何在次日被 ADR-0009(Shell Command Projection Module)的 Phase 3–4 扩充所吸收取代。读完本文,你将掌握 GSD 当前统一的原子写 / .md 规范化 / 锁文件语义的实现位置与调用方式,理解"为什么计划被简化掉"的架构判断过程,并可直接在源码中定位 platformWriteSyncwithPlanningLock 等真实实现。

ADR-0010 的定位与命运:被吸收而非被推翻

ADR-0010 的状态字段给出了理解全文的第一把钥匙:

  • Status: Superseded by ADR-0009(Shell Command Projection Module 扩充,Phase 3–4,#3467#3468
  • Date: 2026-05-12
  • Superseded: 2026-05-13

也就是说,这份 ADR 从提出到被取代仅隔一天。但它不是一份失败文档,其价值恰恰在于完整记录了被放弃的设计路线(typed plan IR 引擎)以及最终采纳的更简替代方案。Supersession note 明确写道:

与其构建独立的 File Operation Engine,本 ADR 提议的文件变更安全策略被并入 Shell Command Projection Module(ADR-0009)。Phase 3(#3467)为该接缝新增了 platformWriteSync / platformReadSync / platformEnsureDir / normalizeContent,以单一的平台条件化表面统一持有原子写(tmp+rename)、.md 规范化与目录创建;Phase 4(#3468)则从 core.cjs 移除了重复的 atomicWriteFileSync / safeReadFile / normalizeMd 包装函数。下文提议的 applyFileMutationPlan / typed plan IR 设计并未构建——对实际漂移点而言,更简单的按调用接缝已被证明足够。

这段结论是阅读全文后最值得带回的信息:架构演进中,被证明更简单的替代方案同样是一种胜利

问题背景:跨三条链路的文件变更漂移

ADR 开篇描述了驱动该提案的现状问题:

今天,文件变更行为在 bin/install.jsget-shit-done/bin/lib/installer-migrations.cjs 以及多个规划模块中重复出现,且原子写保证、路径安全检查与归属分类存在漂移。

具体表现为三类文件变更职责散落在多个模块:

  • installer(bin/install.js:本地自带 atomicWriteFileSync 与临时文件清理注册表;运行时配置与 hook 中存在大段内联的 read/modify/write + backup/rollback 逻辑。
  • migration(get-shit-done/bin/lib/installer-migrations.cjs:维护一套单独的 writeFileAtomicSync、回滚日志、锁处理与路径包含检查。
  • planning(get-shit-done/bin/lib/planning-workspace.cjsstate.cjs 等):重复实现锁文件的创建/释放/删除模式与 best-effort 清理语义。

同一类操作(比如"安全地覆写一个配置文件")在三条链路上各有各的写法,导致原子性保证、.md 内容规范化和归属判定(managed vs user 文件)逐步发散,形成独立的 bug 类别。

决策内容:设立单一接缝、双轨迁移

ADR-0010 的 Decision 提出四件事:

  1. get-shit-done/bin/lib/ 下新增 File Operation Engine Module,作为文件变更安全策略的单一接缝(single seam)。
  2. 命令文本投影继续留在 Shell Command Projection Module(ADR-0009),但投影相邻的 hook 文件变更统一走共享的 managed-hook 归属策略。
  3. 文件操作适配器分两条轨道迁移:
    • Track A(投影相邻,projection-adjacent):运行时配置的 hook 命令检测/重写/删除路径,消费投影接缝的共享 managed-hook 策略。
    • Track B(方案级,solution-wide):共享文件操作引擎统一持有原子写、路径包含、锁行为、回滚记账与 best-effort 清理策略。
  4. 内部子进程执行不进入该接缝(与 ADR-0009 同边界):这是文件操作接缝,不是命令执行器。

初始范围:四个统一目标

Initial Scope 将上述意图细化为四条可验收的收敛目标:

  1. 统一 managed-hook 归属分类:install/uninstall/migration 各处的 hook 配置重写所依赖的"这条 hook 命令是不是 GSD 管理的"判定逻辑。
  2. 统一原子写行为:目前散落在 installer/core/migration 路径中、行为互不一致的原子写实现。
  3. 统一锁文件生命周期策略:规划工作区与安装器迁移日志流程共用的锁创建/释放/清理。
  4. 暴露类型化的文件变更计划 IR 供测试断言:包含 rewrite-jsonrewrite-text(带 toml / markdown / plain 三种格式)、delete-filebackup-filerestore-fileensure-dir 等操作类型。

迁移盘点:精确到文件的漂移清单

ADR 用两节逐一列出现存的漂移点,这是全文最具"审计价值"的部分,也是后续代码重构的直接输入。

Track A:投影相邻的文件变更漂移

位置 漂移内容
bin/install.js hook 清理命令检测(isGsdHookCommand);过期 Codex hook 剥离 basename 列表(STALE_HOOK_BASENAMES);settings/config 中 hook 条目的修剪/重写路径
get-shit-done/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs isManagedCodexHookCommand 的正则/路径检测,是从安装器自有 hook 策略复制而来
get-shit-done/bin/lib/shell-command-projection.cjs isManagedHookBasename 已持有该策略的一部分,应成为规范归属方

Track B:方案级文件操作漂移

位置 漂移内容
bin/install.js 本地 atomicWriteFileSync 与临时清理注册表;运行时配置与 hook 的大段内联 read/modify/write + 备份/回滚逻辑
get-shit-done/bin/lib/core.cjs atomicWriteFileSync 辅助函数在回退行为上与 installer/migration 变体分叉
get-shit-done/bin/lib/installer-migrations.cjs 独立的 writeFileAtomicSync、回滚日志、锁处理与路径包含检查
get-shit-done/bin/lib/planning-workspace.cjsstate.cjs 重复的锁文件 create/release/remove 模式与 best-effort 清理语义
roadmap.cjsphase.cjsmilestone.cjsfrontmatter.cjsdrift.cjs 直接的 read/modify/write 流程,原子性与规范化策略应用不一致

注意一个结构细节:state.cjsroadmap.cjsfrontmatter.cjsdrift.cjs 等模块都处理 Markdown 状态文档或需求文档,它们的"直接改写"与 ADR-0009 后来提供的 .md 感知规范化(normalizeContent)密切相关——这正是 Track B 清单将规范化策略纳入统一范围的原因。

接口草案:类型化文件变更计划 IR

ADR 为未竟的方案给出了接口草图。规划引擎应当暴露类型化的变更计划与执行器:

planFileMutations({
  rootDir,
  operations: [
    { type: 'rewrite-json', relPath, mutate },
    { type: 'rewrite-text', relPath, mutate, format: 'toml' | 'markdown' | 'plain' },
    { type: 'delete-file', relPath },
    { type: 'ensure-dir', relPath },
  ],
  ownership: { mode: 'managed-only' | 'allow-user', classifier },
})
applyFileMutationPlan({
  plan,
  atomic: true,
  rollback: true,
  lock: { scope: 'config' | 'planning', id: '...' },
})

面向投影相邻路径,适配器应当消费投影策略:

isManagedHookCommand(commandText, { surface, configDir })

该草案的亮点在于设计意图本身:把"要做什么变更"(类型化 IR)与"以何种安全策略执行"(原子性、回滚、锁)分离,使测试可以直接对计划 IR 与 reason code 断言,而非依赖源码正则搜索或复制一份判定谓词镜像。

后果与开放问题:这份 ADR 留下的判断框架

Consequences

  • 文件变更安全策略收敛到单一模块,降低 installer/migration/planning 三条路径之间的漂移。
  • Shell 命令投影与 hook 归属分类在同一接缝家族内保持对齐。
  • 测试可断言类型化 mutation IR 与 reason code,取代源码 grep 与重复谓词镜像。
  • 初始迁移面广;排序上应优先处理投影相邻的 hook 配置路径,随后再收敛原子写与锁语义。

Open questions(后文逐一给出走向)

  1. 锁语义应作为 installer + planning 的单一共享策略,还是基于同一锁原语的两个适配器?——最终答案见下文 withPlanningLock
  2. SDK query 写路径是否应在第一轮就消费同一引擎,还是等待 CJS 收敛后跟进?
  3. 文件变更遥测(按操作的 reason codes 与回滚事件)是否应对所有引擎适配器强制要求?

最终落地:被 ADR-0009 吸收后的真实接缝

这一节是文档从"提案"走向"现实"的桥梁。ADR-0009 的 Update(2026-05-13)记录了三项范围扩充,而 ADR-0010 的文件变更安全策略正落于其中两项:

  • Phase 3(#3467:平台文件 I/O —— platformWriteSync / platformReadSync / platformEnsureDir / normalizeContent
  • Phase 4(#3468:从 core.cjs 移除遗留包装 atomicWriteFileSync / safeReadFile / normalizeMd

现状一:平台文件 I/O 已收归 shell-command-projection.cjs

get-shit-done/bin/lib/shell-command-projection.cjs 中,ADR 提议的原子写与规范化语义以四个函数收束(约 第 484–524 行):

  • normalizeContent(filePath, content, opts):根据扩展名分流——.md 文件走 Markdown 规范化(补齐标题/围栏/列表前后的空行并压缩多余换行),其余文本统一 \r\n\n 并确保文件以单个 \n 结尾;opts.encoding 默认 utf-8
  • platformWriteSync(filePath, content, opts):实现原子写 tmp + rename——先 mkdirSync(dirname, { recursive: true }),写入 filePath + '.tmp.' + process.pid 后再 renameSync 覆盖目标;若 rename 失败则尽力删除临时文件并回退为直接写入(保证无悬挂临时文件)。
  • platformReadSync(filePath, opts):读取文件;ENOENT 时若 opts.required 为真则抛错,否则返回 null
  • platformEnsureDir(dirPath)mkdirSync(dirPath, { recursive: true })

它们的导出位于该模块导出表(第 526–552 行),同表还包含投影核心的 isManagedHookBasename(第 148 行)与 isManagedHookCommand(第 164 行)——这正是 ADR-0010 期望 shell-command-projection.cjs "成为 managed-hook 归属策略规范归属方"的兑现。

现状二:锁文件语义仍由 withPlanningLock 独立持有

ADR-0010 的 Supersession note 对 Open question 1 给出了明确答案:锁文件生命周期没有并入共享引擎,仍由 planning-workspace.cjs 中的 withPlanningLock 持有。理由很本质:锁的 { flag: 'wx' } 排他创建语义与原子写的 rename 语义不同,不应强行合并为一种策略。

planning-workspace.cjs 中可看到实际接线:模块顶部从投影接缝导入 probeTty, platformWriteSync, platformReadSync, platformEnsureDir(第 14 行),而 withPlanningLock 定义于第 248 行,核心在 第 262 行 使用 { flag: 'wx' } 创建锁文件。也就是说:原子写走共享接缝、锁文件走专用原语、二者各司其职,正是 ADR 讨论后收敛出的最终架构形态。

现状三:core.cjs 遗留包装已移除、消费面铺开

Phase 4 移除的 atomicWriteFileSync / safeReadFile / normalizeMd 已被 platformWriteSync / platformReadSync / normalizeContent 替代。从源码结构看,当前仓库中 core.cjsstate.cjsroadmap.cjsphase.cjsmilestone.cjsfrontmatter.cjsdrift.cjstemplate.cjsverify.cjsaudit.cjsgraphify.cjssurface.cjsintel.cjslearnings.cjscommands.cjsconfig.cjs 等二十余个 bin/lib 模块均已引用或间接消费上述文件操作/规范化能力——ADR-0010 盘点表中的 Track B 漂移点大体完成了向统一语义的迁移。

测试与后续验证方向

ADR 的两处措辞隐含了可验证断言,也呼应仓库中既有的测试结构:

  • typed IR 断言优于源码 grep:可在 tests/shell-command-projection-dispatch.test.cjstests/installer-migrations.test.cjs 一类测试中观察接缝行为如何被直接验证。
  • 遗留回归 bug 清单:ADR 参考文献列出 #1755#2866#2979#3002#3017#3439 等历史缺陷。其中 hook 绝对路径、跨 shell 引号、SDK 路径诊断等类别在仓库中均有对应回归测试(如 tests/bug-2979-hook-absolute-node.test.cjstests/bug-3011-sdk-path-diagnostic.test.cjstests/bug-3017-codex-hook-absolute-node.test.cjs),可作为继续追踪这一 bug 类收敛效果的切入点。

给架构读者的经验总结

  • 接缝的取舍以"是否值得独立成模块"为准:ADR-0010 提议的类型化计划引擎最终未构建,因为实际漂移点的复杂度没有高到需要一层 IR 抽象——逐调用的 platformWriteSync 等函数即足够。
  • 语义不同的操作不要强行共用一个抽象:原子写(rename 语义)与锁文件(wx 排他创建语义)最终保留为两种独立策略,这本身就是一个 ADR 级结论。
  • 被取代的 ADR 依然有档案价值:它完整记录了漂移现状(可作为 drift 审计底稿)、替代方案的否定理由与收敛后的归属关系,是理解 shell-command-projection.cjs 为何同时持有"投影 + 子进程 + 文件 I/O"三项职责的第一手资料。

关联文档与延伸阅读

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
858
1.35 K
docsdocs
暂无描述
Markdown
899
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
923
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.83 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
532
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.03 K
524
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
393