news 2026/9/28 2:57:32

gsd-core 阶段目录前缀一致性修复:project_code 在 /gsd-discuss-phase 与 /gsd-plan-phase 首次触达路径的统一

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
gsd-core 阶段目录前缀一致性修复:project_code 在 /gsd-discuss-phase 与 /gsd-plan-phase 首次触达路径的统一

【免费下载链接】gsd-core

Git. Ship. Done - Core

项目地址:https://gitcode.com/gh_mirrors/ge/gsd-core
点击查看免费下载

导读

本文基于 gsd-core 仓库中已归档的 changeset(.changeset/archived/witty-geese-purr.md),剖析一次针对"阶段目录命名两套体系并存"问题的修复:当项目在.planning/config.json中配置了project_code后,/gsd-discuss-phase与/gsd-plan-phase两条"首次触达"路径不再生成不带前缀的目录,而是与phase.add/phase.insert保持一致的{PROJECT_CODE}-{NN}-{slug}命名。读完本文,你将理解project_code前缀在整个阶段目录创建链路中的统一规则、共享 helper 的职责边界、对应的工作流改造与回归测试保障,并掌握排查与修复自身仓库中"双头目录"的方法。

一、背景:project_code与阶段目录命名规则

gsd-core 的规划工作区以.planning/phases/存放每个阶段的目录,目录名遵循{NN}-{slug}形态(如01-foundation/)。当一个仓库需要同时管理多个项目(或需要跨项目消歧)时,可以通过在.planning/config.json中设置project_code为阶段目录名增加前缀:

{ "project_code": "XR" }

配置后,阶段目录名变为{PROJECT_CODE}-{NN}-{slug},例如XR-01-foundation/。这一能力在 docs/features/project-code-prefixing.md 中被定义为 v1.31 特性,并明确了三条需求约束:

需求内容
REQ-PREFIX-01配置了project_code时,阶段目录必须以项目代码为前缀(如ABC-01-setup/)
REQ-PREFIX-02未配置project_code时,必须保持标准命名(即无前缀的01-setup/)
REQ-PREFIX-03前缀必须在所有阶段操作中一致应用

配置项的完整说明见 docs/CONFIGURATION.md:

设置类型默认值说明
project_codestring(无)阶段目录名前缀,如"ABC"生成ABC-01-setup/(v1.31 新增)

从 src/phase-id.cts 的注释与实现可以看出,project_code遵循[A-Z][A-Z0-9_]*语法(允许数字与下划线,且不限于 6 位),这一形态与配置校验、以及解析路径上用于剥离前缀的stripProjectCodePrefix(见 docs/adr/2121-phase-identifier-parsing-consolidation.md)保持同一份语法来源。

二、问题:两条创建路径造成的"双头目录"

2.1 现象描述

changeset 原文给出了非常具体的故障画像:当project_code已配置时,同一仓库的.planning/phases/下会出现两种风格并存的目录名——

01-foundation/ # 无前缀(由 discuss-phase / plan-phase 首次触达创建) XR-02.1-spike/ # 带前缀(由 phase.add / phase.insert 创建)

这种01-foundation/与XR-02.1-spike/混排的"两套命名体系"(two-headed naming convention)会造成阶段目录在解析、归档、状态统计等多个环节的歧义。

2.2 成因:首次触达路径漏掉前缀

/gsd-discuss-phase与/gsd-plan-phase属于"首次触达"路径——当一个阶段尚未建立目录时,由这两条流程负责把目录"从无到有"创建出来。修复前,这两条路径在计算目标目录名时没有套用project_code前缀,而phase.add/phase.insert在创建目录时却已经带上了前缀。于是同一条 ROADMAP 上后续追加的阶段带前缀、先前由讨论/规划流程首建的阶段不带前缀,目录树便出现了"双头"。

2.3 潜在危害

  • 解析歧义:目录匹配逻辑(如 src/phase-locator.cts 中的searchPhaseInDir/findPhaseInternal)依赖目录名的 token 匹配;同一裸阶段号可能命中多个目录,触发歧义告警(源码中会提示 "Set a distinct project_code in .planning/config.json to scope resolution",见 src/phase.cts)。
  • 状态漂移:验证、UAT、归档等按目录扫描的功能会漏读或重复读到阶段产物。
  • 跨项目串扰:多个项目共享一个.planning/phases/树时,无前缀目录无法归属到正确的项目。

三、修复方案:把阶段目录创建收敛到共享 helper

changeset 的修复策略很清晰:将阶段目录命名与创建统一路由到单个共享 helper(当时命名为getPhaseDirName),从源头杜绝各调用方各自拼装目录名导致的漂移。

在当前仓库中,承担这一"规范化切点"职责的代码可以对应到两处:

  1. src/phase-id.cts 的getPhaseDirFromPhaseId:纯函数式地根据phaseId、阶段名与projectCode生成规范化目录名——零填充的里程碑/阶段号 + 规范化 slug + 可选的项目代码前缀,并委托core-utils的共享 slug 生成逻辑,避免各调用方重复实现 slug 公式。
  2. src/init.cts 中expected_phase_dir的推导:cmdInitPlanPhase(约 L1210-L1221)与cmdInitPhaseOp(约 L2304-L2315)在阶段目录尚不存在时,按同一公式计算出"应当存在的目录路径"并回传给工作流,工作流只需mkdir -p "${expected_phase_dir}"即可。

3.1 各创建路径的前缀统一现状

修复后,仓库内所有阶段目录创建入口都遵循{prefix}{NN}-{slug}或{prefix}{NN.MM}-{slug}的同一公式:

创建入口位置前缀应用
phase.addsrc/phase.ctsconst prefix = projectCode ? \${projectCode}-` : '',目录名拼入prefix`
phase.add-batchsrc/phase.cts同一prefix公式
phase.insertsrc/phase.cts_dirName = \${pfx}${_decimalPhase}-${slug}`(含子阶段号,如XR-02.1-spike`)
scaffold phase-dirsrc/commands.cts代码注释明确标注#3287:应用project_code前缀以与phase.add/phase.insert保持一致
init plan-phase/init phase-op的expected_phase_dirsrc/init.cts推导时拼入前缀,供工作流首次触达使用

可见#3287这条修复不仅覆盖了 changeset 中提到的两条工作流命令,还顺带补齐了scaffold phase-dir这一手工脚手架入口,形成全链路一致。

四、工作流侧配套:从mkdir裸拼到expected_phase_dir

仅靠命令侧统一还不够——工作流文档中的"首次触达"步骤此前可能使用裸的mkdir -p .planning/phases/{NN}-{slug},一旦手工拼装就会再次漏掉前缀。修复后,工作流统一改为消费 init 返回的expected_phase_dir:

  • gsd-core/workflows/discuss-phase.md(Find or create phase directory):读取 init 返回的phase_dir、expected_phase_dir、phase_slug、padded_phase;当phase_dir为 null 时执行mkdir -p "${expected_phase_dir}",然后phase_dir="${expected_phase_dir}"。
  • gsd-core/workflows/plan-phase.md:phase_found为 false 时先校验 ROADMAP 中存在该阶段,再使用expected_phase_dir(注释明确说明"includesproject_codeprefix when set")创建目录。
  • gsd-core/workflows/add-backlog.md:先读取project_code,再拼接带前缀的 backlog 目录名,保持与所有其他阶段创建路径一致。

这种"让命令计算、让工作流消费"的设计,把命名规则收敛在单一职责的 helper 中,避免了工作流文档各自为政地手写目录名公式。

五、测试保障:前缀一致性回归套件

修复不是孤证,tests/phase.test.cjs 中沉淀了完整的回归测试:

  • bug-3287 套件(tests/phase.test.cjs#L7088-L7252):构造project_code: "XR"的临时项目(makeXRProjectfixture),断言:
    • phase.add生成XR-02-auth-service而非裸02-auth-service;
    • init phase-op 1在目录不存在时返回expected_phase_dir,且路径同时包含XR-前缀与阶段 slug;
    • 未设置project_code时,expected_phase_dir不得匹配^[A-Z][A-Z0-9]*-前缀形态(即保持标准命名,对应 REQ-PREFIX-02);
    • init plan-phase 1对expected_phase_dir的同一组断言。
  • bug-3298 套件(tests/phase.test.cjs#L7255-L7304):直接扫描gsd-core/workflows/import.md等工作流文件内容,用正则禁止出现裸mkdir .planning/phases/{NN}-{slug}模板与裸 shell 变量拼装模式,并要求必须引用expected_phase_dir——把"防漂移"推进到了文档内容层。

这两层测试分别从"命令输出正确"与"工作流文档不再手写裸目录名"两个维度锁死了回归。

六、实践指南:在自己的仓库中检查与落地

6.1 检查是否存在"双头目录"

在使用了project_code的 gsd-core 项目中,直接查看规划目录:

ls .planning/phases/

如果同时出现01-foundation/(无前缀)与XR-02.1-spike/(有前缀)两种形态,即命中本文所述问题;可进一步通过gsd_run query config-get project_code --raw确认配置值。

6.2 如何让后续创建保持一致

  • 确保.planning/config.json中的project_code为期望的项目代码(如XR);
  • 阶段目录的创建一律交给命令侧完成:使用phase add/phase insert/scaffold phase-dir,或在init plan-phase/init phase-op返回expected_phase_dir后由工作流mkdir -p,不要在脚本中手工拼装{NN}-{slug};
  • 若已存在无前缀的历史目录,可将其重命名为带前缀的规范名(注意同步更新引用该目录路径的文档与状态),使命名收敛到单一体系。

6.3 与 bracket 约定的关系

project_code前缀是独立于phase_id_convention的维度。即便采用"bracket"约定(标题形如### [GSD.02] 05: Name、目录形如GSD.02-05-name),bracket 发射路径也只由config.phase_id_convention === 'bracket'门控,而非由project_code是否存在门控——详见 docs/adr/612-bracket-phase-id-convention.md。也就是说,配置了project_code但未启用 bracket 的仓库,同样必须遵守本文所述的前缀一致性规则。

七、相关证据与延伸阅读

  • 原始变更记录:.changeset/archived/witty-geese-purr.md(type: Fixed,关联 PR 3292,已归档)
  • 发布说明中的同源记录:docs/RELEASE-NOTES-LEGACY.md(标注为 #3287)
  • 特性规格:docs/features/project-code-prefixing.md
  • 配置参考:docs/CONFIGURATION.md
  • 核心实现:src/phase.cts、src/commands.cts、src/init.cts、src/phase-id.cts
  • 工作流:gsd-core/workflows/discuss-phase.md、gsd-core/workflows/plan-phase.md、gsd-core/workflows/add-backlog.md、gsd-core/workflows/import.md
  • 回归测试:tests/phase.test.cjs
  • 关联架构决策:docs/adr/612-bracket-phase-id-convention.md、docs/adr/2121-phase-identifier-parsing-consolidation.md

小结

witty-geese-purr这条 changeset 代表了一类典型的工程治理手段:当多个入口各自实现同一命名规则时,漂移只是时间问题。gsd-core 的解法——统一共享 helper、命令侧计算期望路径、工作流侧只消费不拼装、测试同时锁定命令行为与文档内容——为project_code前缀的一致性提供了从实现到文档再到回归的完整闭环,值得在阅读源码与二次开发时对照参考。

【免费下载链接】gsd-core

Git. Ship. Done - Core

项目地址:https://gitcode.com/gh_mirrors/ge/gsd-core
点击查看免费下载
上一篇:gifuct-js:颠覆传统,JavaScript GIF解码的终极性能革命
下一篇:如何快速上手MusePose:虚拟人视频生成的终极实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 2:55:24

FontForge 内置 INI 解析库 mINI:插件配置读写机制与源码深度剖析

桌面应用图形学 【免费下载链接】fontforge Free (libre) font editor for Windows, Mac OS X and GNULinux 项目地址: https://gitcode.com/gh_mirrors/fo/fontforge 点击查看 免费下载 mINI 是一个单头文件、header-only 的 INI 文件读写库,FontForge…

作者头像 李华
网站建设 2026/9/28 2:54:57

基于CGH40010F的Doherty功放半理想架构ADS仿真流程详解

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

作者头像 李华