首页 / 资讯中心 / 文章详情

gsd-core Worktree 测试集群合并重构:从 13 个文件到 3 个文件的收敛实践

gsd-core Worktree 测试集群合并重构:从 13 个文件到 3 个文件的收敛实践 ★ FEATURED ARTICLE
【免费下载链接】gsd-coreGit. Ship. Done - Core项目地址https://gitcode.com/gh_mirrors/ge/gsd-core点击查看免费下载本篇技术指南聚焦 gsd-core 仓库中一次典型的测试维护重构——将 Worktree 模块测试集群从 13 个文件合并收敛为 3 个文件以满足lint-test-file-count允许名单allowlist的上限要求。读者将了解到该仓库如何用每个生产模块最多 2 个测试文件的 CI 门禁约束测试蔓延、如何按接缝seam合理切分合并后的测试文件、如何借助允许名单的身份棘轮identity ratchet机制防止测试文件数量反弹以及 Worktree 模块底层的工作树安全策略与生命周期校验逻辑。背景测试文件数量上限与允许名单机制在 gsd-core 仓库中每个生产模块的测试文件数量并非自由生长而是由 scripts/lint-test-file-count.cjs 这一 CI 检查脚本强制约束的。该脚本的核心规则是扫描sdk/src/query/、sdk/src/、gsd-core/bin/lib/、bin/下的生产模块在tests/与sdk/src中递归统计与模块同名的*.test.cjs/*.test.ts测试文件每个模块最多允许 2 个测试文件MAX_FILES 2即主测试 一个集成测试超过上限的集群必须出现在 scripts/lint-test-file-count.allowlist.json 允许名单中且必须列出被豁免的具体文件名允许名单由 scripts/lib/allowlist-ratchet.cjs 的assertWithinAllowlist做身份棘轮校验新增一个测试文件到已封顶的模块会判定为FAIL_NOVEL_FILESnovel offender移除文件则要求同步裁剪允许名单条目否则判定为FAIL_STALE_ALLOWLISTstale。允许名单的_doc字段写明了这一设计的动机它锁定的不是数量不超过 N这种可被抵消的计数棘轮fix 一个又引入一个仍保持数量不变新缺陷会溜过绿灯而是身份identity级别的逐文件可见性强制向零缺陷单调收敛。允许名单条目的完整形态如下以ui-consideration-probe为例ui-consideration-probe: { files: [ ui-consideration-probe-docs-fixtures.test.cjs, ui-consideration-probe-ui-phase-contract.test.cjs, ui-consideration-probe.test.cjs ], issue: 4657, justification: ... }当某个集群合并后降到 ≤ 2 个文件时按规则应完全移除其允许名单条目。本次变更Worktree 集群 13 → 3 的核心内容本次合并PR #3752关闭 issue #3742将 Worktree 模块测试集群从13 个文件收敛为 3 个文件从而无需在允许名单中为worktree保留条目3 个文件 ≤ 4 的集群预算且默认上限 2 亦满足——合并后三个文件各自归属不同的接缝切分见下文。合并的具体映射如下合并目标文件承载的测试主题合并自原文件tests/worktree.test.cjs分支检查branch-check与工作区安全workspace-safetybug-2015-worktree-base-branch.test.cjs#2015reset --hard、bug-2075-worktree-deletion-safeguards.test.cjs#2075git clean 禁令、bug-2431-worktree-locked-surfacing.test.cjs#2431锁定工作树错误呈现、bug-2774-worktree-cleanup-workspace-safety.test.cjs#2774发现流水线tests/worktree-cleanup.test.cjsHEAD 附着HEAD-attachment与执行后清理cleanupbug-2924-worktree-head-attachment.test.cjs#2924HEAD 附着、worktree-cleanup.test.cjs#1496执行后清理、worktree-merge-protection.test.cjs#1756编排器文件保护、worktree-safety.test.cjs#1977提交安全加固、worktree-stagger.test.cjs#1511串行调度、bug-3384-worktree-cleanup-manifest.test.cjs工作流契约侧、bug-3425-worktree-cleanup-cwd-pin.test.cjs#3425CWD 钉扎tests/worktree-safety.test.cjs安全策略函数单元测试原安全策略测试与超时/退化路径测试等并入拆分原则正如两个测试文件头部的注释所言沿清理接缝cleanup seam切分且每个文件控制在 ≤ 800 LOC 以内避免合并出一个无法维护的巨型文件。合并后每个文件仍保留自身的 seam 声明例如worktree.test.cjs声明其 seam 为gsd-core/workflows/{execute-phase,execute-plan,quick}.md与agents/gsd-executor.md。测试执行方式合并后的测试文件全部基于node:test编写可直接运行node --test tests/worktree.test.cjs tests/worktree-cleanup.test.cjs tests/worktree-safety.test.cjsWorktree 生命周期校验接缝worktree_branch_checkWorktree 集群测试的验证对象并非普通单元函数而是工作流 Markdown 中的编排契约——这正是两个测试文件头部// allow-test-rule: source-text-is-the-product注释的含义工作流 Markdown 是被安装的编排契约测试以它为产品文本进行断言。核心校验接缝是 gsd-core/references/worktree-branch-check.md 中的worktree_branch_check块。这是嵌入到每个工作树子 Agent 提示词中的唯一权威来源单一事实源禁止他处内联副本被execute-phase.md、quick.md、diagnose-issues.md、execute-plan.md在派发时共同嵌入。测试通过_extractNamedBlock/extractNamedBlock工具从 Markdown 中抽取该命名块再用_shellStatements/shellStatements将 shell 脚本分词成命令数组最后断言特定 git 调用的存在与禁令。该守卫的核心不变量包括verify-only 且 fail-closed子 Agent 只验证、失败即退出绝不自行修复。编排器工作树生命周期所有者负责任何基线恢复子 Agent 永远不重写它未创建的工作树#48、#2924HEAD 附着必须用git symbolic-ref检查且块内不得出现任何git reset自恢复指令测试用assert.strictEqual(resetIdx, -1)钉死失败必须exit 42并输出 FATAL 行交由编排器决策恢复受保护分支必须按名称禁止main|master|develop|trunk|release/.*通过grep -Eq ^(...)$交替模式逐名断言测试硬编码了这五个必选项正向允许名单^((worktree-)?agent-|worktree-wf_)工作树分支必须落在agent-id/worktree-agent-id/worktree-wf_runid-n命名空间内#2924 加固#3021 将worktree-wf_纳入允许名单显式禁止git update-ref自恢复并在指导文本中写明。该块的 shell 断言骨架节选自 gsd-core/references/worktree-branch-check.mdHEAD_REF$(git symbolic-ref --quiet HEAD || echo DETACHED) ACTUAL_BRANCH$(git rev-parse --abbrev-ref HEAD) if [ $HEAD_REF DETACHED ] || echo $ACTUAL_BRANCH | grep -Eq ^(main|master|develop|trunk|release/.*)$; then echo FATAL: worktree HEAD on $ACTUAL_BRANCH (expected agent-* or worktree-agent-*); refusing to commit or self-recover via git update-ref (#2924). 2 exit 42 fi if ! echo $ACTUAL_BRANCH | grep -Eq ^((worktree-)?agent-|worktree-wf_)[A-Za-z0-9._/-]$; then echo FATAL: worktree HEAD $ACTUAL_BRANCH is not in the agent-* / worktree-agent-* / worktree-wf_* namespace; refusing to commit (#2924). 2 exit 42 fi ACTUAL_BASE$(git rev-parse HEAD) EXPECTED_BASE_ALTERNATE{EXPECTED_BASE_ALTERNATE} if [ $ACTUAL_BASE ! {EXPECTED_BASE} ] { [ -z $EXPECTED_BASE_ALTERNATE ] || [ $ACTUAL_BASE ! $EXPECTED_BASE_ALTERNATE ]; }; then echo FATAL: worktree base mismatch — HEAD is $ACTUAL_BASE, expected {EXPECTED_BASE}${EXPECTED_BASE_ALTERNATE: or $EXPECTED_BASE_ALTERNATE}. Orchestrator owns recovery; sub-agent refuses to rewrite the worktree (#48). 2 exit 42 fi编排器侧的契约是派发前先捕获EXPECTED_BASE$(git rev-parse HEAD)再将该块原样嵌入子 Agent 提示词并替换{EXPECTED_BASE}。若编排器有意创建仅文档docs-only的派发前计划提交还可将{EXPECTED_BASE_ALTERNATE}替换为该提交的直接父提交使从提交两侧分叉的运行时都通过同一 fail-closed 守卫#1265。测试如何钉死这些不变量tests/worktree-cleanup.test.cjs 的bug #2924describe 块展示了逐条钉死的方法execute-phase.md references the canonical fragment断言execute-phase.md必须引用权威片段文件而非内联副本block invokes git symbolic-ref to inspect HEAD attachment在抽取的代码块中查找git symbolic-ref ... HEAD命令block is verify-only: HEAD assertion present, no git reset, fails closed (#48)同时断言symbolic-ref存在、git reset不存在、文本含exit 42block names protected branches that must NOT be the agent branch从grep -Eq ^(...)$中解析交替分支列表断言必须包含main、master、develop、trunk、release/.*block enforces positive worktree-agent-* allow-list (#2924 hardening)断言允许名单正则同时覆盖(worktree-)?agent-与worktree-wf_block forbids git update-ref self-recovery in its guidance text断言文档文本中出现update-ref禁令。另一个值得注意的断言来自execute-phase.md的parallel_execution块测试逐句扫描块文本拒绝任何默认指令使用--no-verify的句子——只有当句子处于否定/退出语境如 Do NOT pass --no-verify时才允许--no-verify出现防止并行模式下跳过钩子校验成为默认行为。源码侧的两大支撑模块测试所保护的逻辑落在两个核心生产模块中注意仓库同时存在 TypeScript 源与构建产物测试通过gsd-core/bin/lib/下的构建产物加载如 tests/worktree-safety.test.cjs 头部的require(WORKTREE_SAFETY_PATH)1. Worktree Safety Policy Module工作树安全策略源码位于 src/worktree-safety.ctsADR-457 build-at-publish 后由手写bin/lib/worktree-safety.cjs折叠为 TypeScript 单一事实源行为逐字节保留。CONTEXT.md的第 217 行将其登记为Worktree Safety Policy Module接口包括resolveWorktreeContext、parseWorktreePorcelain、planWorktreePrune、executeWorktreePrunePlan、listLinkedWorktreePaths、inspectWorktreeHealth、snapshotWorktreeInventory、planWorktreeWaveCleanup等。其职责要点工作树根解析resolveWorktreeLinkage通过git rev-parse --git-dir与--git-common-dir的无快捷方式比较区分链接工作树与主工作树——这是判断当前目录是否隔离执行环境的真正原语。设计上刻意避免使用.planning/存在性快捷方式为隔离执行器创建的工作树本身就是完整检出通常自带.planning/若先跑快捷方式会把正确隔离的工作树误读为未隔离见resolveWorktreeLinkage的注释#3045非破坏性 prune 策略planWorktreePrune默认只产出metadata_prune_only计划git worktree pruneallowDestructive为 false 时永不执行破坏性清理解析 porcelain 失败与无工作树被区分为不同 reasonparse_failedvsno_worktrees避免基于不可读数据做出 prune 决策#3050/#3057 B6超时降级模块为 git 子进程设置默认 10 秒超时DEFAULT_GIT_TIMEOUT_MS 10000超时被表面为独立 reasongit_timed_out与干净的非零退出严格区分——超时意味着命令确实未完成绝不能当作真实答案#3050清单化波次清理normalizeCleanupManifest从 JSON 清单中提取worktree_path/branch/expected_base/allowed_bases/files_modified/declared_deletions分支必须匹配WORKTREE_AGENT_BRANCH_RE重复条目按键去重空清单判定empty_manifest并跳过。2. Worktree Base-Ref Module工作树基线退化检测源码位于 src/worktree-base-ref.ctsissue #683。该模块判定工作树 HEAD 是否已偏离fresh并行工作树的 fork 基线origin/HEAD偏离时调用方应降级为主工作树上的串行执行避免基线不匹配。核心接口tests/worktree-base-ref.test.cjs 中逐一载入shortSha、readBaseRefFromSettings、applyWorktreeBaseRef、resolveEffectiveBaseRef、evaluateWorktreeBaseDegrade、classifyGitHead、cmdWorktreeBaseCheck、cmdWorktreeSetBaseRef。三层层叠配置解析resolveEffectiveBaseRef按 项目本地settings.local.json→ 项目共享settings.json→ 用户/全局settings.json的优先级级联读取worktree.baseRef仅当用户目录与项目目录不同路径时才参与第三层worktree.baseRef: head的语义边界evaluateWorktreeBaseDegrade只有在orchestrator-worktree模式GSD 自己执行git worktree add path start-point以编排器 HEAD 为起点下才尊重head并抑制比较在harness-worktree模式运行时 harness 创建工作树不经过项目设置下head会被忽略仍按origin/HEAD比较——因为 #48 以 5/5 验证了 Agent-isolation 派发路径从不经过项目设置上游 claude-code#44965git HEAD 四态分类classifyGitHead以git rev-parse HEAD的退出码为稳定契约stderr 文本因本地化不可靠presentexit 0 非空 SHA、definitive-absenceexit 128git 明确答复无 HEAD如非 git 仓库或无提交仓库、ambiguous-absenceexit 0 空 stdoutgit 完成但未给出明确答复、indeterminate超时或其它非成功——绝不作为证据调用方必须 fail-closed#3050。其中 exit 128 的明确缺席会降级#4734 决策exit 0 空 stdout 保持不降级被既有回归测试钉死产品问题仍开放#3057 B8fork-from-HEAD 观察探针observeHarnessForkFromHead检查repo/.claude/worktrees/agent-*下仍干净无已跟踪改动未跟踪的 review notes 无妨且 HEAD 等于当前编排器 HEAD 的先前工作树——只有这种组合是从 HEAD fork的正证据结论缓存于cwd/.gsd/harness-fork-probe.json以编排器 HEAD 为键HEAD 变动即重新探测#4588 decision A2。evaluateWorktreeBaseDegrade的完整退化判定矩阵从源码src/worktree-base-ref.cts的返回分支归纳条件reasonshouldDegrade说明baseRefhead且 orchestrator-worktree 模式baseref-headfalseGSD 自建工作树head天然生效HEAD 解析超时/非 128 非成功head-unresolvabletruefail-closed提示检查停滞挂载或.git/index.lockHEAD 解析 exit 128无仓库/无提交no-headtrue工作树永远无法创建继续要求隔离只会阻塞派发#4734HEAD 解析 exit 0 空 stdoutno-headfalsegit 未给出明确答复不降级回归测试钉死观察到干净工作树 HEAD 编排器 HEADharness 模式fork-from-head-observedfalse正证据harness 从 HEAD fork#4588fork 基线解析失败fork-ref-unknowntrueorigin/HEAD与 symbolic-ref 回退均失败forkSha headShahead-matches-forkfalse基线一致可并行已设head但 harness 模式忽略之baseref-head-ignored-by-harnesstrue提示head不适用于 runtime harness#48、#3659HEAD 偏离 fork 基线head-diverged-from-forktrue降级为主工作树串行执行两个 CLI 命令cmdWorktreeBaseCheck输出当前退化状态 JSON与cmdWorktreeSetBaseRef无覆盖式写入worktree.baseRefhead都刻意通过fs.writeSync(1, …)而非process.stdout.write输出以配合 CLI 的--pick捕获机制与命令替换$(gsd-tools … --pick x)场景#3659 review 发现的坑。安全收割reap路径的独立测试文件合并后另有一个独立文件 tests/worktree-safety-reap.test.cjs996 行专门覆盖reapOrphanWorktrees的故障注入判定#3057 wave 3。它之所以独立成文是因为主测试文件worktree-safety.test.cjs约 6.4k 行且收割套件需要不同的 fixture 形状注入委托给真实 git 的execGit 逐测试变更.git/worktrees/name/管理目录。该文件钉死了此前任何测试都未覆盖的路径既有测试只注入mtimeSafe/nowMs/isPidAlive导致reapOrphanWorktrees内部每个 fail-closed 分支都不可达新文件为每条分支精确注入一个故障断言特定{status, reason}判定而不是仅仅断言返回了数组。确定性方面不读墙钟mtimeSafe/nowMs注入、不探测真实 PIDisPidAlive注入唯一真实依赖是 git 本身。合并的配套动作CONTEXT.md 词条登记除测试文件合并外本次变更还向 CONTEXT.md 新增了 Worktree Workstream Seam Module 词条见第 719 行起的WORKTREE.SEAM.*登记块。这些登记以结构化键值对锁定模块的接缝契约WORKTREE.SEAM.currentWorktree Safety Policy Module WORKTREE.SEAM.files[gsd-core/bin/lib/worktree-safety.cjs] WORKTREE.SEAM.interface[resolveWorktreeContext, parseWorktreePorcelain, planWorktreePrune, executeWorktreePrunePlan, planWorktreeRecordAgent, cmdWorktreeRecordAgent] WORKTREE.SEAM.test-anchors[resolveWorktreeContext:has_local_planning|linked_worktree|not_git_repo|main_worktree, planWorktreePrune:git_list_failed|worktrees_present|no_worktrees|parser_throw_fallback, executeWorktreePrunePlan:missing_plan|skip_passthrough|unsupported_action|metadata_prune_only] WORKTREE.SEAM.inventory-interface[listLinkedWorktreePaths, inspectWorktreeHealth] WORKTREE.SEAM.caller-ruleverify.cjs must consume inspectWorktreeHealth for W017 classification; no ad-hoc porcelain parsing in callers这保证了合并后模块的接口、测试锚点与调用方规则仍然可被工具与后续维护者机械式验证。CONTEXT.md同时在健康诊断规则目录中登记了worktree-health.cts的 W027 规则active-worktree 排除isActiveWorktreePath为 win32-only 路径大小写比较的纯谓词#3663。维护模式总结这类重构的通用步骤从本次 13 → 3 的合并可以提炼出可复用的收敛模式统计现状运行node scripts/lint-test-file-count.cjs --json查看各模块测试文件数量与允许名单匹配情况OK_UNDER_LIMIT/OK_IN_ALLOWLIST/FAIL_EXCEEDS_LIMIT/FAIL_NOVEL_FILES/FAIL_STALE_ALLOWLIST五种判定沿接缝归并不是盲目拼接而是按功能接缝如分支检查/工作区安全与HEAD 附着/清理归并并设定每个合并文件的行数上限本案例为 ≤ 800 LOC防止新的巨型文件验证允许名单合并后若集群 ≤ 上限默认 2本案例描述中的集群预算为 4应移除允许名单条目而不是保留——保留会产生 stale 判定若仍需保留则files数组必须与真实文件名逐字一致身份棘轮登记接缝契约同步更新CONTEXT.md的*.SEAM.*词条确保测试锚点与调用方规则可被工具验证全量回归运行npm test或按模块执行node --test tests/worktree*.test.cjs确认合并未改变断言语义——合并本质上是文件的物理归并测试逻辑与 seam 声明必须原样保留。对本仓库的贡献者而言新增测试到已封顶模块前应优先考虑合并进既有测试文件而非新增文件这正是本案例演示的仓库纪律。赞分享【免费下载链接】gsd-coreGit. Ship. Done - Core项目地址https://gitcode.com/gh_mirrors/ge/gsd-core点击查看免费下载相关推荐Get Shit Done Worktree 测试集群合并实战从 13 个文件收敛到 3 个由 lint-test-file-count 棘轮治理驱动Get Shit Done Worktree 测试集群合并实战从 13 个文件收敛到 3 个由 lint test file count 棘轮治理驱动 本篇人工智能AI 应用提示工程开发工具工作流自动化AI Agentgsd-core 安装器测试整合3758 将 11 个测试文件收敛为 2 个参数化套件gsd core 安装器测试整合 3758 将 11 个测试文件收敛为 2 个参数化套件 本文以 .changeset/archived/3758 consogsd-core/gsd-quick Worktree 合并的复活检测守卫与 3195 对新建 .planning/ 文件的误删修复gsd core/gsd quick Worktree 合并的复活检测守卫与 3195 对新建 .planning/ 文件的误删修复 本文围绕 gsd创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站