
get-shit-doneWorkstream Inventory Builder 生成式接缝与新鲜度守卫的设计解析【免费下载链接】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本文围绕 changeset3544-workstream-inventory-builderPR 3548所引入的“生成式 Workstream Inventory Builder 接缝”展开它把sdk/src/workstream-inventory/builder.ts确立为唯一权威实现canonical source自动生成安装器侧消费的 CJS 产物get-shit-done/bin/lib/workstream-inventory-builder.generated.cjs并用check-workstream-inventory-builder-fresh守卫将其接入钩子/CI使 SDK 与安装器面向的工件始终保持同步。读完后你能理解这条“单一源码 → 双端产物 → 字节级新鲜度校验”的同步链路如何工作并掌握进度/状态投影算法与等价性测试的写法。1. 背景为什么要建这条接缝changeset.changeset/3544-workstream-inventory-builder.md声明的核心内容是Adds a generated Workstream Inventory Builder seam with freshness guards — introducessdk/src/workstream-inventory/builder.ts(and tests) as the canonical builder source, generatesget-shit-done/bin/lib/workstream-inventory-builder.generated.cjs, wirescheck-workstream-inventory-builder-freshinto hooks/CI, and updates inventory docs/manifests so SDK and installer-facing artifacts stay synchronized.拆解开这一条 Added 变更记录了四件事权威源码新增sdk/src/workstream-inventory/builder.ts及其测试作为工作流清单workstream inventory构建逻辑的唯一手写来源生成产物由脚本生成 get-shit-done/bin/lib/workstream-inventory-builder.generated.cjs供get-shit-done/安装器/CLI 侧CommonJS 环境直接require新鲜度守卫check-workstream-inventory-builder-fresh接入钩子/CI防止生成产物与源码漂移库存登记更新 inventory 文档与清单见 docs/INVENTORY.md、docs/INVENTORY-MANIFEST.json让产物进入仓库资产台账。从源码结构看这个接缝属于该项目“CJS/SDK 硬接缝”体系的一部分SDK 以 ESM/TypeScript 开发而安装后的 CLI 以 CJS 脚本运行二者不能互相 import。为同一份纯逻辑同时维护两份代码必然漂移因此仓库采用“手写一份 生成一份 守卫保鲜”的模式。该模式在 docs/adr/3524-cjs-sdk-hard-seam.md 与 docs/prd/3524-cjs-sdk-hard-seam.md 中有对应的架构记录。2. 权威实现一个“无 I/O、无异步”的纯投影函数sdk/src/workstream-inventory/builder.ts 顶部注释定义了模块契约Workstream Inventory Builder — pure projection from pre-collected filesystem data to typed WorkstreamInventory. No I/O. No async.即调用方负责从文件系统或测试 fixture采集全部输入本模块只做无状态变换。这是它能被安全复制进 CJS 产物、并可用 fixture 做字节级等价测试的前提。2.1 输入契约 BuilderInputsBuilderInputs接口sdk/src/workstream-inventory/builder.ts#L51-L73把“已采集事实”结构化字段含义name工作流名称目录 basenameprojectDir项目根目录绝对路径workstreamDir工作流目录绝对路径phaseDirNames阶段目录名列表未排序builder 内部会排序activeWorkstreamName当前激活的工作流名无则nullphaseFilesCounts预采集的各阶段目录 plan/summary 计数directory必须与phaseDirNames中条目对应roadmapPhaseCount从 ROADMAP.md 解析出的阶段数fallback 已应用stateProjection从该工作流的 STATE.md 投影出的{ status, current_phase, last_activity }filesExist三个规范文件roadmap/state/requirements是否已存在所有输入都带注释强调“already resolved / already read / already checked”把 I/O 边界彻底留在模块之外。2.2 输出结构 WorkstreamInventoryWorkstreamInventorybuilder.ts#L20-L39是清单条目结构关键字段name/path名称与相对项目根的 POSIX 路径toPosixPath(relative(projectDir, workstreamDir))Windows 反斜杠统一转为正斜杠activename activeWorkstreamName的布尔标记files{ roadmap, state, requirements }三个布尔位status/current_phase/last_activity直接透传stateProjectionphases按目录名字典序排序后的阶段数组每阶段含directory、statuscomplete | in_progress | pending、plan_count、summary_count汇总字段phase_count、completed_phases、roadmap_phase_count、total_plans、completed_plans、progress_percent。另有WorkstreamInventoryListmode: flat | workstream、active、workstreams、count、可选message用于整份清单输出供上层命令组装。2.3 状态判定与进度算法核心算法在buildWorkstreamInventorybuilder.ts#L102-L170先把phaseFilesCounts建成Map按目录名 O(1) 查询缺失目录回落为{ planCount: 0, summaryCount: 0 }对[...phaseDirNames].sort()逐个阶段判定状态const status counts.summaryCount counts.planCount counts.planCount 0 ? complete // summary 数达到 plan 数且 plan 数大于 0 : counts.planCount 0 ? in_progress // 有 plan 但 summary 未齐 : pending; // 还没有任何 plan累计totalPlans planCountcompletedPlans Math.min(summaryCount, planCount)summary 多于 plan 时不多算complete阶段计入completedPhases进度百分比以ROADMAP 中的阶段数为分母并夹到 100progress_percent: roadmapPhaseCount 0 ? Math.min(100, Math.round((completedPhases / roadmapPhaseCount) * 100)) : 0,注意进度分母不是磁盘上的阶段目录数而是 ROADMAP.md 的roadmapPhaseCount——这使“磁盘上多做了几期”不会把百分比推到 100% 以上多出的部分由Math.min(100, …)兜底。另一个导出函数isCompletedInventorybuilder.ts#L91-L94是工作流级别的完成态分类器对状态串做小写、去首尾空白后用词边界正则匹配milestone complete或archived大小写不敏感。这两个函数就是生成产物需要对外提供的全部 API。3. 生成器如何从编译产物提取函数并拼装 CJS生成脚本是 sdk/scripts/gen-workstream-inventory-builder.mjs运行方式为 changeset 与产物头注释中一致声明的cd sdk npm run gen:workstream-inventory-builder它从编译后的 ESM 输出sdk/dist/workstream-inventory/builder.js即 TypeScript 已转译、无类型标注的 JS出发用两种互补手段提取函数源码导出函数import编译产物后直接取buildWorkstreamInventory.toString()与isCompletedInventory.toString()——用Function.prototype.toString()拿函数体保证与真实导出的运行时行为一致非导出内部助手toPosixPath没有导出脚本改为把编译产物当文本读入用extractFunctionFromSourcegen-workstream-inventory-builder.mjs#L36-L62扫描function name(标记再从头括号起做花括号配对计数截取整个函数体找不到标记或花括号不闭合都会抛错属于 fail-fast。最终拼装顺序固定use strict 生成头注释BANNER→const path require(path); const relative path.relative;因为原模块的import { relative } from node:path在 CJS 下要手工替换→ 内部助手toPosixPath→isCompletedInventory→buildWorkstreamInventory→module.exports { buildWorkstreamInventory, isCompletedInventory };。这个头注释就是生成产物里可见的内容workstream-inventory-builder.generated.cjs#L1-L11GENERATED FILE — DO NOT EDIT. Source: sdk/src/workstream-inventory/builder.ts Regenerate: cd sdk npm run gen:workstream-inventory-builder产物 get-shit-done/bin/lib/workstream-inventory-builder.generated.cjs 与 SDK 源码逻辑逐段对应countsMap建索引、排序后的阶段状态机、Math.min(100, …)进度夹取、以及正则化的isCompletedInventory在 builder.generated.cjs#L26-L77 中可以一一找到。4. 新鲜度守卫把“是否过期”变成 CI 可执行的布尔守卫脚本 sdk/scripts/check-workstream-inventory-builder-fresh.mjs 的做法很直接复用生成器的buildWorkstreamInventoryBuilderCjs()在内存中重新生成期望内容不写盘前置条件是sdk/dist已构建即先npm run build读取已提交的 workstream-inventory-builder.generated.cjs逐字节比对一致输出is fresh并以 0 退出不一致输出is STALE、打印重新生成命令并以 1 退出check-workstream-inventory-builder-fresh.mjs#L24-L31。由于 changeset 声明该检查已“wired into hooks/CI”其效果是任何只改了builder.ts却没重新生成 CJS 的提交会在钩子或流水线阶段被直接拦下从而把“SDK 与安装器工件同步”从口头约定变成退出码。5. 等价性测试fixture 驱动的 ESM/CJS 双跑比对新鲜度守卫保证“文本一致”而 tests/workstream-inventory-builder-generator.test.cjs 保证“行为一致”它对同一组输入分别调用编译后的 SDK ESM 模块sdk/dist/workstream-inventory/builder.js经pathToFileURL动态 import与生成的 CJS 产物断言deepStrictEqual。fixture 覆盖test 文件#L30-L106fixture验证点空清单无阶段目录、无 STATE.md零输入不崩、默认值齐全单阶段in_progress3 plan / 1 summary部分完成的状态判定单阶段complete2 plan / 2 summarysummary ≥ plan 且 plan 0单阶段pending0 plan空目录不算进行中多阶段混合状态排序 多状态并存completedPhases roadmapPhaseCountprogress_percent夹取到 100activeWorkstreamName nameactive: true标记activeWorkstreamName为其他工作流active: false另有isCompletedInventory的 8 组用例test 文件#L108-L117milestone complete/Milestone Complete/archived/Archived判真executing/planning/unknown/ 空串判假验证了大小写不敏感与词边界匹配。6. 库存登记与同步闭环changeset 的最后一句要求“updates inventory docs/manifests”生成产物作为仓库资产被登记进 docs/INVENTORY.md 与 docs/INVENTORY-MANIFEST.json两者均包含workstream-inventory-builder条目配合仓库的清单计数类测试如tests/inventory-counts.test.cjs、tests/inventory-manifest-sync.test.cjs形成台账一致性检查。至此整条链路闭合为sdk/src/workstream-inventory/builder.ts 唯一手写权威源码TS/ESM │ 编译到 sdk/distnpm run build ▼ sdk/scripts/gen-workstream-inventory-builder.mjs toString 源码文本提取 ▼ get-shit-done/bin/lib/workstream-inventory-builder.generated.cjs CJS 产物DO NOT EDIT │ ├── check-workstream-inventory-builder-fresh.mjs内存重生成 逐字节比对hooks/CI ├── tests/workstream-inventory-builder-generator.test.cjsfixture 行为等价测试 └── docs/INVENTORY.md / INVENTORY-MANIFEST.json资产台账登记7. 可复用的工程要点这条接缝对任何“TypeScript ESM 源码 CommonJS 分发产物”的项目都有参考价值纯函数边界把逻辑写成“输入全是已采集事实”的纯投影no I/O, no async才可能安全地复制/生成到另一语言模块体系并做确定性测试双通道提取导出函数用Function.prototype.toString()取运行时真实函数体非导出助手用括号配对的源码文本提取两种手段互补且都能 fail-fast 抛错生成头即契约GENERATED FILE — DO NOT EDIT 权威源码路径 重新生成命令写进产物头部让人工误改在 diff 评审阶段就可见守卫只比文本不写盘新鲜度检查在内存中重算期望内容再与提交文件比对退出码 0/1 语义清晰天然适合挂到钩子或 CI测试分层新鲜度检查管“文本同步”fixture 双跑等价测试管“行为同步”两者叠加才能同时挡住漏生成与实现回归。适用前提与限制新鲜度检查依赖先构建sdk/dist脚本注释明确要求npm run build生成器依赖编译产物中存在function toPosixPath(这样的顶层函数声明形态若上游编译目标改变函数声明形式文本提取需要同步适配progress_percent的分母是 ROADMAP 阶段数而非磁盘阶段数解读输出时应以 builder.ts#L165-L168 的公式为准。【免费下载链接】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),仅供参考