news 2026/10/10 8:15:18

get-shit-done 修复 roadmap update-plan-progress 的零填充不匹配:padded 相位参数如何正确驱动未填充 ROADMAP 进度更新

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
get-shit-done 修复 roadmap update-plan-progress 的零填充不匹配:padded 相位参数如何正确驱动未填充 ROADMAP 进度更新
  • 人工智能
  • AI 应用
  • 提示工程
  • 开发工具
  • 工作流自动化
  • AI Agent

【免费下载链接】get-shit-done

A 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
点击查看免费下载

导读

本文围绕 get-shit-done 仓库中一条type: Fixed的 changeset(.changeset/clever-wasps-parade.md,PR #3380)展开,深入剖析其描述的核心缺陷与修复:roadmap.update-plan-progress在收到零填充(padded)相位参数时,此前会静默跳过对未填充(unpadded)ROADMAP 正文中进度行与复选框的更新。读完本文,你将理解该命令的完整行为契约、零填充宽容正则(padding-tolerant regex)的实现原理、8 个调用点为何会集体“漂移”,以及仓库如何用奇偶一致性(parity)测试守住这条修复不倒退。

一、changeset 说了什么:一条短小但关键的修复记录

.changeset/clever-wasps-parade.md全文仅两段:

--- type: Fixed pr: 3380 --- roadmap.update-plan-progress now updates unpadded ROADMAP phase rows and checkboxes when called with zero-padded phase arguments.

这条 changeset 属于仓库的标准发布说明体系(scripts/changeset/下存在完整的解析、渲染、lint 与 GitHub release notes 生成管线,配套测试见 tests/changeset-*.test.cjs)。它记录了一次“Fixed”级别的缺陷修复,关键词有三个:

  • 命令:roadmap.update-plan-progress,一个把磁盘上的 PLAN/SUMMARY 计数同步回ROADMAP.md的命令;
  • 缺陷场景:调用方传入了零填充相位参数(如02.7);
  • 修复效果:现在能正确更新未填充的 ROADMAP 相位行(progress table row)与复选框(checkbox)。

要理解这次修复的份量,需要先弄清楚这个命令本身是做什么的、它更新哪些内容,以及“填充”不一致为什么会让更新静默失效。

二、命令定位:roadmap update-plan-progress是什么

在 get-shit-done 的规划体系中,.planning/ROADMAP.md是人与 Agent 共同维护的路标:它包含里程碑下的相位列表(- [ ] **Phase 2.7: ...**)、每个相位的详情小节(### Phase 2.7: ...、**Plans:** N plans、计划清单)以及进度总表(## Progress下的| Phase | Plans | Status | Completed |表格)。而真实的执行证据在磁盘上:phases/目录中每个相位的NN-01-PLAN.md与对应的NN-01-SUMMARY.md文件。

roadmap.update-plan-progress的作用就是以磁盘为事实源,回写 ROADMAP 的展示层。它有两种调用形式:

  • CLI 形式(见 docs/CLI-TOOLS.md 的 Roadmap Commands 一节):
node gsd-tools.cjs roadmap update-plan-progress <N>
  • SDK query 形式(工作流内统一使用,见 get-shit-done/workflows/execute-plan.md 与 get-shit-done/workflows/execute-phase.md):
gsd-sdk query roadmap.update-plan-progress "${PHASE}"

此外,命令还兼容--phase <N>旗标形式,以兼容 SDK 参数解析场景(对应 CHANGELOG 中 #2796 的修复:位置参数解析曾把字面量--phase当作相位值)。在查询注册表中,该命令被声明为mutation: true、outputMode: 'json'(见 sdk/src/query/command-manifest.roadmap.ts),路由经由 get-shit-done/bin/lib/roadmap-command-router.cjs 在 CJS 实现与 SDK 实现之间桥接(存在GSD_WORKSTREAM或 SDK 不可用时回退到 CJS)。

三、命令的完整行为契约:一次调用改四处

CJS 实现位于 get-shit-done/bin/lib/roadmap.cjs 的cmdRoadmapUpdatePlanProgress,SDK 移植版位于 sdk/src/query/roadmap-update-plan-progress.ts。两者逻辑一致,完整流程如下。

3.1 从磁盘统计相位

先通过findPhase定位相位目录并统计文件:plans为*-PLAN.md(或裸PLAN.md)文件列表,summaries为*-SUMMARY.md(或裸SUMMARY.md)列表(见 sdk/src/query/phase.ts 的getPhaseFileStats)。相位不存在时报错Phase ${phaseNum} not found;没有计划文件时直接返回:

{ "updated": false, "reason": "No plans found", "plan_count": 0, "summary_count": 0 }

ROADMAP.md不存在时返回{ "updated": false, "reason": "ROADMAP.md not found", ... }。

3.2 判定状态并计算日期

const isComplete = summaryCount >= planCount; const status = isComplete ? 'Complete' : summaryCount > 0 ? 'In Progress' : 'Planned'; const today = new Date().toISOString().split('T')[0];

状态机为:Complete(SUMMARY 数 ≥ PLAN 数)、In Progress(有部分 SUMMARY)、Planned(尚无 SUMMARY)。

3.3 四处 ROADMAP 突变

在 CJS 中以withPlanningLock包裹整个读-改-写(防并发写坏),SDK 中对应readModifyWriteRoadmapMd的原子写。共四处修改:

  1. 进度表行:匹配以相位号开头的表格行,支持 4 列(Phase | Plans | Status | Completed)与 5 列(多出 Milestone 列)两种形态,分别更新 Plans 列(summaryCount/planCount)、Status 列(status.padEnd(11)保持对齐)与 Completed 列(完成时写入YYYY-MM-DD,否则留空)。

  2. 详情小节**Plans:**行:在### Phase N:小节内定位**Plans:**,替换为N/N plans complete(完成)或N/N plans executed(进行中)。注意这里使用了[ \t]*而非\s*——CHANGELOG 记录了 #2728 的教训:\s*会跨换行匹配,导致**Plans:**单独成行时误吞下一行计划复选框;同时用小节边界前瞻防止越界改到下一个相位的**Plans:**。

  3. 相位总复选框:相位完成时,把总览清单里的- [ ] **Phase N: ...**翻转为- [x] **Phase N: ...** (completed YYYY-MM-DD)。

  4. 计划级复选框:遍历每个 SUMMARY 文件,把对应的- [ ] 50-01-PLAN.md、- [ ] 50-01:、- [ ] **50-01**等形态翻转为- [x](正则(-\\s*\\[) (\\]\\s*(?:\\*\\*)?<planId>(?:\\*\\*)?))。

成功后返回:

{ "updated": true, "phase": "<phaseNum>", "plan_count": 3, "summary_count": 3, "status": "Complete", "complete": true }

CLI 的非 raw 输出还会附带一行人类可读提示,如3/3 Complete。

3.4 工作流中的实际使用

该命令是执行流水线的“追踪写入点”:

  • get-shit-done/workflows/execute-plan.md 的update_roadmap步骤:每个计划完成后同步一次(仅非 worktree 模式;worktree 模式下各工作树各自持有 ROADMAP,由编排者统一合并,避免兄弟工作树写分叉——即 #2661 的单写者契约);
  • get-shit-done/workflows/execute-phase.md 的 5.7 节:每波 worktree 合并后,对每个完成计划调用gsd-sdk query roadmap.update-plan-progress "${PHASE_NUMBER}" "${plan_id}" "complete",且仅当TEST_EXIT为 0(测试通过)才更新追踪文件——测试失败或超时(124)时保留计划为进行中状态,并把ROADMAP.md/STATE.md的变更用commit --files提交。

四、缺陷根源:padded 参数 vs unpadded 正文(#3537)

为什么“传入零填充参数”会导致更新失败?问题出在正则匹配上。get-shit-done 的技能层在解析出相位目录后,倾向于把填充后的形态(如02.7,因为磁盘目录统一零填充为01-name、02.7-...样式)传给命令;而人类手写的 ROADMAP 正文约定俗成使用未填充形态(### Phase 2.7:、- [ ] **Phase 2.7:**)。若正则片段是escapeRegex(phaseNum)(精确转义),那么02.7永远匹配不到2.7,命令“报告成功、文件却毫无变化”——静默空转(silent no-op)。

回归测试 tests/bug-3537-padded-id-against-unpadded-roadmap.test.cjs 的头部注释精确记录了漂移过程:

v1.42.1 addedphaseMarkdownRegexSource()which renders0*<integer><...>— padding-tolerant on both sides — but wired it into only 1 of 8 call sites. The other 7 used rawescapeRegex(phaseNum)or0*${escapeRegex(...)}(tolerated extra padding, not missing), so passing the padded form silently no-op'd and the verbs returned success while ROADMAP.md was unchanged.

即:0*${escapeRegex(...)}只能容忍“多出的填充”(2能匹配02),不能容忍“缺失的填充”(02匹配不到2)。修复只接进了 1 个调用点,其余 7 个继续漂移。本次 changeset(PR #3380)所记录的,正是把宽容两侧填充的正则源接入roadmap.update-plan-progress相关调用点的完整落地,使“padded 参数 → unpadded 正文”也能产生真实的突变。

五、修复机制:phaseMarkdownRegexSource的宽容匹配原理

核心工具函数位于 get-shit-done/bin/lib/core.cjs 的phaseMarkdownRegexSource:

function phaseMarkdownRegexSource(phaseNum) { const stripped = String(phaseNum).replace(/^[A-Z]{1,6}-(?=\d)/i, ''); const match = stripped.match(/^0*(\d+)([A-Z])?((?:\.\d+)*)$/i); if (!match) return escapeRegex(phaseNum); const integer = match[1].replace(/^0+/, '') || '0'; const letter = match[2] ? escapeRegex(match[2]) : ''; const decimal = match[3] ? escapeRegex(match[3]) : ''; return `0*${escapeRegex(integer)}${letter}${decimal}`; }

原理三步:

  1. 剥离项目码前缀:^[A-Z]{1,6}-(?=\d)去掉CK-这类前缀(目录CK-02.7-...→ 相位号02.7);
  2. 解析数字结构:把0*的整数部分、可选字母后缀(12A中的A)、可选的十进制段(.7、.1.2)拆开;
  3. 重排为宽容片段:整数部分去掉前导零后,前面补0*。于是02.7生成0*2\.7,既能匹配2.7也能匹配02.7甚至002.7。

对非数字的自定义 ID(如PROJ-42)则回退为精确转义,保证调用点可以无条件替换使用。配套函数phaseMarkdownRegexSourceExact(#3599)处理项目码前缀形态(PROJ-42需先精确匹配### Phase PROJ-42:,再回退到数字宽容形态),避免与恰好共享尾号的裸### Phase 42:交叉误匹配。

从当前 get-shit-done/bin/lib/roadmap.cjs 的调用点可见修复已全面落地:roadmap analyze的复选框检测(第 270 行)、update-plan-progress的表行/Plans/复选框正则(第 374 行)以及annotate-dependencies(第 535 行)均使用phaseMarkdownRegexSource;phase.cjs的精确形态调用点则使用phaseMarkdownRegexSourceExact(第 151 行)。测试文件注释中还披露了一个边界:同一份 ROADMAP 内混合填充是合法且真实存在的,因此“heading 用2.7、总结区复选框用02.7”也必须同时可匹配。

六、测试防线:奇偶一致性断言

这次修复最值得借鉴的是测试方法论。tests/bug-3537-padded-id-against-unpadded-roadmap.test.cjs构造了一个精确复刻 #3537 报告的 fixture:项目码CK、填充目录CK-02.7-meta-lead-ads/、未填充正文Phase 2.7,然后对同一 fixture 分别用 padded 与 unpadded 参数各跑一遍,断言两个结果 ROADMAP 的字节完全一致(expectParity)。其核心思想是:

  • 非空洞通过(non-vacuous pass):除了断言两者相等,还断言- [x] **Phase 2.7:确实发生了翻转——否则“两边都静默空转”也能通过等值断言,测试就失去了意义;
  • 覆盖所有受影响动词:phase complete、roadmap get-phase(--raw)、phase next-decimal、phase insert、roadmap annotate-dependencies、roadmap update-plan-progress;
  • 反向回归:phase next-decimal中额外断言“不能因为扫描失败就跳过已存在的2.7而错误提议02.1”。

SDK 侧的单测 sdk/src/query/roadmap-update-plan-progress.test.ts 则用更细粒度断言锁住四处突变:padded 参数03驱动 unpaddedPhase 3的复选框翻转、**Plans:** 1/1 plans complete、表格行| 3. build | 1/1 | Complete | 2026-..-.. |、计划复选框- [x] 03-01-PLAN.md;并回归 #2728 的两个坑:**Plans:**单独成行时不得覆盖其后的计划列表,且不得越界改写下一个相位(Phase 8/Phase 10)的**Plans:**行。

七、结论与使用建议

roadmap.update-plan-progress是 get-shit-done“磁盘即事实源、ROADMAP 即展示层”设计的关键同步器。本次 changeset 修复的实质,是把相位号匹配从“精确形态”升级为“任意填充形态”:无论调用方传2.7还是02.7,无论 ROADMAP 正文写作Phase 2.7还是Phase 02.7,命令都应产生字节一致的突变结果。

给使用者的建议:

  1. 调用时可放心传 padded 形态(如技能层解析出的02.7),命令已能正确回写未填充正文,不必再手动归一化相位号;
  2. 利用--raw输出调试:CLI 的 raw 模式直接输出 JSON 载荷,便于确认updated、status、complete字段是否符合预期;
  3. 留意失败语义:No plans found/ROADMAP.md not found会返回updated: false而非报错,工作流编排时应检查该字段;
  4. 保持 fixture 覆盖:若未来新增任何解析 ROADMAP 相位号的调用点,应复用phaseMarkdownRegexSource,并补一条“padded 与 unpadded 奇偶一致”测试,防止下一个调用点重新漂移——这正是 #3537 用 8 个调用点漂移换来的教训。

相关参考:docs/CLI-TOOLS.md(命令速查)、CHANGELOG.md(#2728、#2796 等相邻修复)、sdk/src/query/QUERY-HANDLERS.md(query 注册表)。

  • 人工智能
  • AI 应用
  • 提示工程
  • 开发工具
  • 工作流自动化
  • AI Agent

【免费下载链接】get-shit-done

A 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
点击查看免费下载

相关推荐

上一篇:can2040项目安装与使用指南
下一篇:IDM-VTON快速入门指南:5分钟学会使用AI虚拟试穿技术

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

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

【MT32F006】MT32F006之systick定时器延时

本文最后修改时间&#xff1a;2026年09月10日一、本节简介本文介绍如何使用MT32F006的定时器做us、ms级的延时。二、实验平台库版本&#xff1a;V1.0.0编译软件&#xff1a;MDK5.37硬件平台&#xff1a;MT32F006开发板&#xff08;主芯片MT32F006&#xff09;仿真器&#xff1a…

作者头像 李华
网站建设 2026/10/10 8:12:13

CPU 飙到 100%,先别急着翻代码

CPU 飙到 100%&#xff0c;先别急着翻代码 Java 线上排查的四步法与三个坑 经典排查流程整理与修订 作者观点&#xff0c;仅供讨论 线上告警&#xff1a;某台机器 CPU 打满。很多人的第一反应是翻代码、猜哪里有死循环。我的观点是&#xff1a;**先定位&#xff0c;再推理&…

作者头像 李华
网站建设 2026/10/10 8:10:46

PCA9422与MKV42F256VLH16协同实现μA级嵌入式电源管理

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

作者头像 李华
网站建设 2026/10/10 8:08:42

基于PCA9422与PIC32MX的电源管理及低功耗设计

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

作者头像 李华
网站建设 2026/10/10 8:07:42

幼儿手工活动:创造力与动手能力培养的底层逻辑与实操指南

很多家长可能都遇到过这样的场景&#xff1a;孩子兴冲冲地举着一幅歪歪扭扭的剪纸或者一个看不出原型的黏土作品跑过来&#xff0c;满脸期待地问“好不好看”。我们嘴上夸着“真棒”&#xff0c;心里却在犯嘀咕&#xff1a;这到底有什么意义&#xff1f;半天时间就折腾出这么个…

作者头像 李华
网站建设 2026/10/10 8:02:20

ZBar-Win64.rar 实战指南:Windows 条码识别库的配置、调优与避坑

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

作者头像 李华