【免费下载链接】gsd-coreGit. Ship. Done - Core项目地址https://gitcode.com/gh_mirrors/ge/gsd-core点击查看免费下载导读本文围绕 gsd-core 中一份编号为 PR #156 的修复记录.changeset/archived/graceful-jays-hop.md解析一次典型的CJS 工具层与 SDK 校验层代码漂移drift的修复过程sdk/src/query/validate.ts与gsd-core/bin/lib/verify.cjs之间因手工维护双份实现而造成的 W006/W007 误报最终通过gen-validate.mjs生成器模式收敛为「单一手写源 机械生成产物」。读完本文你将掌握 gsd-core 的生成器防漂移机制、phaseVariants()等纯函数被提取与复用的原理以及 W005/W006/W007/I001 四条健康诊断规则在双运行时下的行为契约。修复记录定位一次典型的跨运行时漂移修复.changeset/archived/graceful-jays-hop.md是 gsd-core 的一份归档 changeset全文如下--- type: Fixed pr: 156 --- Fix W007/phaseVariants/W006 drift between SDK validate.ts and CJS verify.cjs via generator pattern (gen-validate.mjs validate.generated.cjs). Per ADR-3524 generator framework introduced by PR #154 (issue #4).它记录的是一个bug 类别bug class而非单点缺陷gsd-core 同时维护两套执行运行时——同步的 CJS 工具层gsd-core/bin/lib/*.cjs与 SDK/TypeScript 层。历史上同一份校验逻辑被分别手写在validate.tsSDK 侧和verify.cjsCJS 侧导致同一修复只落在其中一侧另一侧随即再次产生误报。ADR-3524 的正文将这一反复出现的缺陷类别列为#1535、#1542、#2047/#2052、#2638/#2655、#2653/#2670、#2687/#2706、#2798/#2816、#3055/#3116、#3523其共同特征都是「一个修复只落在一侧另一侧没跟上」。本次 PR #156issue #6正是用 ADR-3524 引入的generator pattern生成器模式解决其中三个具体漂移项W007 的activeDiskPhases、phaseVariants()归一化、W006 的未开始阶段变体跳过逻辑。背景CJS/SDK 硬接缝与 Shared-Module 源策略要理解这次修复必须先理解 ADR-3524CJS↔SDK hard seamdocs/adr/3524-cjs-sdk-hard-seam.md。该 ADR 提出Shared-Module Source Policy共享模块源策略每个共享模块恰好一个手写源——有行为的模块以 TypeScript 形式放在sdk/src/module/纯数据模块以 manifest 形式放在sdk/shared/产物只允许由生成器机械产生——CJS 侧产物形如gsd-core/bin/lib/module.generated.cjs禁止手改每个模块配一个 freshness 检查脚本——CI 重新运行生成器若与已提交产物不一致则构建失败先例check-command-aliases-fresh.mjs禁止手工同步成对文件hand-synced pairs——lint-shared-module-handsync.cjs会拦截bin/lib/name.cjs与sdk/src/name.ts的非生成器成对实现。该 ADR 还特别强调第 4 节两侧的 I/O 适配器Adapter允许合理不同——CJS 调用方使用同步 fs/execSDK 调用方使用异步 I/O只有其背后的纯转换逻辑解析、投影、归一化才被提取为共享模块。validate.ts与verify.cjs中的健康校验正属于「同一份纯逻辑、两个运行时」的场景是生成器模式的天然适用对象。生成器模式的仓库内先例是command-aliasessdk/scripts/gen-command-aliases.ts从单一 TS 源同时产出sdk/src/query/command-aliases.generated.ts与gsd-core/bin/lib/command-aliases.generated.cjs。PR #154issue #4率先把它推广到 phase 生命周期gen-phase.mjs、gen-phase-lifecycle.mjs、gen-phase-lifecycle-policy.mjsPR #156 则把同一模式应用到 validate/verify 对。注意gen-validate.mjs与validate.generated.cjs属于历史归档形态。在当前仓库中这份逻辑已按 ADR-457docs/adr/457-generated-cjs-single-source.md 的「build-at-publish发布时构建」方向进一步演进手写源收敛为 TypeScript 的 src/validate.cts产物为构建产物而非提交物。但 ADR-3524 的「单一源 机械生成 freshness 门禁」原则仍然是当前实现的直接前身src/validate.cts的头部注释也明确标注了它的来源ADR-457、ADR-3524 §4、issue #6、issue #26、PR #154、PR #156。三个漂移项逐一拆解误报是如何产生的ADR-3524 的 2026-05-23 修订记录issue #6把本次修复拆成三个具体漂移项。tests/health-validation.test.cjstests/health-validation.test.cjs中保留了针对每一项的回归测试与详尽的注释见文件第 480–696 行附近的「Drift item 1/2/3」段落是理解误报机理的第一手材料。漂移项 1W007 的activeDiskPhases症状verify.cjs的 Check 8 在跑 W007「ROADMAP 中有、磁盘上没有」的警告时迭代的是diskPhases——它通过forEachArchivedPhaseToken把已归档里程碑的阶段也纳入了磁盘阶段集合。若某个归档阶段在当前 ROADMAP 中已不存在就会产生假 W007 警告。修复W007 改迭代activeDiskPhases仅来自collectDiskPhases()的活动阶段不再包含forEachArchivedPhaseToken的归档项与validate.tsCheck 8 的行为对齐。回归测试构造两个里程碑归档v1.0 旧、v1.1 活动v1.0 的阶段不在当前 ROADMAP 中——修复前verify.cjs对「1」误报 W007修复后不再误报tests/health-validation.test.cjs 第 539–620 行。漂移项 2phaseVariants()归一化症状verify.cjsCheck 8 用parseInt(p).padStart(2,0)做磁盘存在性与 ROADMAP 成员检查这一做法会丢弃字母后缀例如3B被归一化成03而不是03B。于是带字母后缀补零的阶段目录ROADMAP 写3B、磁盘上是03B-foo会产生假 W006 和假 W007。修复两处检查都改用生成的phaseVariants(p)。该函数返回包含原始形式、去零形式、补零形式、字母后缀形式的完整归一化集合因此01A与1A可以正确互相匹配。回归测试ROADMAP01A 磁盘1A-foo的补零错位场景修复前 W006/W007 双双误报修复后均不再误报tests/health-validation.test.cjs 第 641–696 行。漂移项 3W006 未开始阶段变体跳过症状verify.cjsCheck 8 构造notStartedPhases时只使用原始形式与parseInt补零形式同样丢字母后缀导致「3B」无法正确抑制「03B」反之亦然的 W006 检查。修复改用phaseVariants()构造未开始阶段集合使带后缀变体互相抑制。回归测试ROADMAP3B 磁盘03B-foo的补零字母后缀错位场景tests/health-validation.test.cjs Drift 3 段落。生成器如何提取纯函数phaseVariants的源码级细节phaseVariants在 SDK 编译产物中是validateHealth内部的闭包而不是模块级导出。ADR-3524 修订记录明确指出gen-validate.mjs采用brace-balanced source-text parsing括号配平的源码文本解析从sdk/dist/query/validate.js中把它提取出来——这与gen-phase-lifecycle-policy.mjs提取escapeRegex的技术相同。提取的前提是该函数确定性且纯不闭包外部状态、无副作用。当前仓库 src/validate.cts 中该函数的实现如下第 195–200 行起export function phaseVariants(phase: string): Setstring { const variants new Set([phase]); const dotIdx phase.indexOf(.); const head dotIdx -1 ? phase : phase.slice(0, dotIdx); const tail dotIdx -1 ? : phase.slice(dotIdx); // ... 依据头部数值前缀生成去零/补零等变体后加入集合 }同模块还导出了配套的buildRoadmapPhaseVariants()与buildNotStartedPhaseVariants()见src/validate.cts头部注释第 10–15 行前者替代 W007 循环中的原始roadmapPhases集合后者替代 W006 跳过逻辑中的原始 补零集合。从源码结构看这三个函数构成了 W006/W007 的统一变体归一化层任何一侧的检查都消费同一套集合从根上杜绝了「一侧改、一侧漏」的漂移。issue #26 的扩展W005/W006-archived/I001 的生成器迁移PR #156 解决的只是 issue #6 的三个漂移项ADR-3524 修订记录还记录了 issue #26 对生成器覆盖范围的扩展PR #3479 修复了三类误报PR #3806 手工移植到verify.cjs但未走生成器于是仍可能再漂移。issue #26 把gen-validate.mjs进一步扩展为额外导出四项phaseDirNameReW005——阶段目录命名正则原来内联在verify.cjsCheck 6。PHASE_DIR_NAME_RE/^\d{2,}(?:\.\d)*-[\w-]$/被提升为validate.ts的具名导出再由生成器提取。复现路径mkdir -p .planning/phases/999.1-foo应产生零 W005。PHASE_TOKEN_FROM_DIR_REW006-archived——原来内联在verify.cjs的forEachArchivedPhaseToken()与collectDiskPhases()改为从模块级常量提取。MILESTONE_ARCHIVE_DIR_REW006-archived——原来内联在listMilestoneArchiveDirs()同样被提取。两者共同保证归档目录遍历使用与validate.ts完全相同的模式。canonicalPlanStemI001——原来内联在verify.cjsCheck 7通过extractTopLevelFunction()括号配平解析器提取。修复效果68-01-scaffolding-PLAN.md能与68-01-SUMMARY.md正确匹配都归约到68-01不再误报 I001。提取方法上gen-validate.mjs新增了两个原语extractConstRegExp()处理const/export const的单行正则赋值用于前三项与extractTopLevelFunction()用于顶层命名函数声明用于canonicalPlanStem。对应地把PHASE_DIR_NAME_RE从内联匿名正则提升为export const正是为了让它在编译后的 ESM 产物中成为可提取的标识符。ADR-3524 修订记录对 W006-archived 有一个重要澄清issue #26 描述的 W006-archived「与 PR #156 的 W006 修复相关但不同」——调查确认两个修复其实都已在verify.cjs来自 PR #3806真正的缺口是生成器覆盖forEachArchivedPhaseToken使用的正则常量仍是无人保护的内联副本。因此该扩展的交付物不是新的行为修复而是生成器模式的覆盖补齐。双运行时下的 W005/W006/W007/I001 行为契约src/verify.cts中明确说明当前的健康诊断采用规则表复用W006/W007 等诊断由validate.health评估的同一套Rule对象驱动且当前所有 C0NN/W006/W007 诊断都是SEVERITY.WARNING。这四条诊断的语义可归纳如下诊断码触发条件修复相关的关键点W005阶段目录名不符合规范命名如两位数字起头等phaseDirNameRe统一999.1-foo不再误报W006磁盘上有阶段目录但 ROADMAP 中未开始含归档阶段目录遍历phaseVariants()归一化 归档正则统一W007ROADMAP 中声明的阶段在磁盘上缺失activeDiskPhases排除已归档阶段I001PLAN 文件与 SUMMARY 文件 stem 不匹配canonicalPlanStem()归约长 stemphaseVariants()的归一化集合行为在测试中有明确的断言例如canonicalPlanStem(68-01-scaffolding) 68-01、canonicalPlanStem(3A-01-feature) 3A-01同时保证46-6-rs-...这类「阶段号后跟单位数字 slug 词」不被误吸收#204314-2026-photos-...这类「≥3 位数字 slug 词年份」不被误当作连续段#2232——见 tests/health-validation.test.cjs 中canonicalPlanStem的专项测试段落。防漂移机制的三层门禁与演进方向ADR-3524 第 5 节把漂移拦截设计为三层每一层都有仓库内先例每模块 freshness 检查——sdk/scripts/check-module-fresh.mjs重跑生成器产物与已提交版本不一致即失败先例check-command-aliases-fresh.mjs每模块漂移 lint——当不变式不是纯文件等值时使用如scripts/lint-shell-command-projection-drift.cjs手工同步对 lint——scripts/lint-shared-module-handsync.cjs在 PR 阶段拒绝任何既非生成产物、又不在显式允许清单中的成对文件从源头堵死 #3523 反模式。生成器还会在产物文件顶部自动插入「GENERATED FILE — Source: …」横幅沿用command-aliases.generated.*的既有样式。需要强调的是这个框架本身也在持续演进ADR-457 已接受「build-at-publish」方向TypeScript 源为规范、.cjs为 gitignore 的构建产物这从机制上消解了「两份副本必须一致」的漂移治理负担。因此阅读本 changeset 时gen-validate.mjs validate.generated.cjs应理解为该演进路径上的中间形态其核心原则——单一手写源、机械生成产物、每模块 freshness 门禁——在今天的src/validate.cts与健康诊断规则表中依然清晰可辨。参考路径速查归档 changeset.changeset/archived/graceful-jays-hop.md决策记录docs/adr/3524-cjs-sdk-hard-seam.md含 issue #6 / #26 修订、docs/adr/457-generated-cjs-single-source.md当前实现src/validate.cts、src/verify.cts回归测试tests/health-validation.test.cjsDrift 1/2/3 段落及 W005/W006-archived/I001 专项这份 changeset 的价值不在于它修了三个告警而在于它示范了 gsd-core 治理「双运行时共享逻辑」的标准答案凡是纯函数就用生成器从单一源机械产出凡是 I/O就保留两侧适配器凡是手工同步的成对文件就在 PR 阶段拒绝合入。赞分享【免费下载链接】gsd-coreGit. Ship. Done - Core项目地址https://gitcode.com/gh_mirrors/ge/gsd-core点击查看免费下载相关推荐gsd-core 里程碑归档目录解析修复getActiveMilestoneArchiveDir 的 null 语义与 W007 误报消除gsd core 里程碑归档目录解析修复getActiveMilestoneArchiveDir 的 null 语义与 W007 误报消除 本文聚焦 gsdgsd-core validate health 误报修复实录W005/W006/I001 三类回归的根因与移植PR 3806gsd core validate health 误报修复实录W005/W006/I001 三类回归的根因与移植PR 3806 导读 gsd core vgsd-core 修复实践平面 Phase Details 导致的 Milestone 阶段泄漏与 W007 误报gsd core 修复实践平面 Phase Details 导致的 Milestone 阶段泄漏与 W007 误报 导读 在 gsd core 的规划工作流中上一篇告别U盘格式化烦恼Ventoy让你一个U盘装下所有操作系统下一篇如何捕获网页里的隐藏视频与音频猫抓浏览器资源嗅探扩展上手指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
