☰
gsd-2 无分支工作树架构(Branchless Worktree Architecture)深度解析:用单分支顺序提交根治自治编码的 Git 状态问题
2026/9/28 2:48:20 网站建设 项目流程
  • 人工智能
  • AI Agent
  • 代码智能体
  • Agent 编排
  • CLI
  • AI 应用

【免费下载链接】gsd-2

A powerful meta-prompting, context engineering and spec-driven development system that enables agents to work for long periods of time autonomously without losing track of the big picture

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

本文基于docs/dev/ADR-001-branchless-worktree-architecture.md,系统拆解 gsd-2 早期为自治(Auto)编码会话设计的 Git 隔离架构:从「每个里程碑一个工作树 + 树内切片分支」到「完全消灭切片分支、单分支顺序提交」的演进逻辑。你将掌握 GSD 工作树/分支模型的设计动机、.gsd/状态归属(tracked vs gitignored)的分类治理原则、合并管线与smartStage()等关键函数的简化路径,以及该方案如何被后续 ADR-016 系列与 DB 权威(DB-authoritative)运行时模型取代的完整来龙去脉——这些知识对理解本仓库src/resources/extensions/gsd/下的工作树、合并与状态同步代码至关重要。

背景:GSD 如何用 Git 隔离自治编码会话

GSD(gsd-2)使用 Git 作为自治编码会话期间的隔离机制。该 ADR 记录的当前架构(在 M003、v2.13.0 中发布)为每个里程碑创建一个工作树(worktree),并在每个工作树内部使用切片分支(slice branch):每个切片(S01、S02、...)在自己的分支(gsd/M001/S01)上工作,切片完成时通过--no-ff合回里程碑分支(milestone/M001),里程碑完成时里程碑分支再 squash-merge 回main。

这套架构是为了取代更早的「每切片一分支(branch-per-slice)」模型——后者引发了严重的.gsd/合并冲突。M003 解决了合并冲突,却保留工作树内的切片分支,继承了大量复杂逻辑,并持续产生面向用户可见的失败。

从源码看,这一「每里程碑一个工作树」的模型至今仍是仓库的骨架:src/resources/extensions/gsd/auto-worktree.ts拥有工作树的创建/进入/退出/合并生命周期,src/resources/extensions/gsd/git-service.ts提供底层 Git 原语与暂存逻辑。

五大问题:切片分支模型为什么必须被推翻

ADR-001 用五个层面的证据论证切片分支是「根因性的复杂度来源」。

1. 规划产物不可见(循环检测失败)

当research-slice或plan-slice分派时,Agent 在切片分支上写入产物(例如S02-RESEARCH.md)。Agent 完成后,handleAgentEnd会切回里程碑分支准备下一次分派——此时产物停留在切片分支上,里程碑分支看不到它。verifyExpectedArtifact()(当前实现于 auto-recovery.ts)检查的是里程碑分支,找不到文件就递增循环计数器并重试:3 次重试后硬停止,6 次生命周期分派后永久停止。结果就是烧掉预算、阻塞进度。

这个问题在 auto-stop 架构文档中被记录为"The Branch-Switching Problem"(分支切换问题)。

2..gsd/状态跨分支互相覆盖

.gsd/是 gitignored 的(.gitignore中的.gsd/),规划产物(roadmap、plans、summaries、decisions、requirements)位于.gsd/milestones/但对 Git 不可见。当多个分支或工作树在同一个仓库上运行时,它们共享磁盘上同一个.gsd/目录:分支 A 的 M001 roadmap 会覆盖分支 B 的 M001 roadmap。GSD 读取到损坏的状态,把错误的里程碑判为完成,或陷入无限分派循环。

该 ADR 还指出了代码库中的一个自相矛盾的 workaround:smartStage()(git-service.ts中的暂存逻辑)尽管.gitignore声明忽略.gsd/,仍会 force-addGSD_DURABLE_PATHS(milestones/、DECISIONS.md、PROJECT.md、REQUIREMENTS.md、QUEUE.md)。这意味着.gsd/milestones/在某些分支上确实被部分跟踪,而 gitignore 却声称相反——代码在与配置打架。

源码佐证(现状):这一「代码与配置打架」的张力在后续演进中被形式化而非消除——git-service.ts 定义了RUNTIME_EXCLUSION_PATHS(.gsd/activity/、.gsd/runtime/、.gsd/worktrees/、.gsd/gsd.db*、.gsd/STATE.md、.gsd/DISCUSSION-MANIFEST.json等),其注释明确要求与 gitignore.ts 中的GSD_RUNTIME_PATTERNS保持同步,两者互为 canonical 来源。这意味着暂存时只排除运行时路径,而不是整体排除.gsd/。

3. 合并/冲突代码复杂度

切片分支模型需要一整套合并与冲突处理管线:

  • mergeSliceToMilestone()—— 98 行,带withMergeHeal包装的--no-ff合并
  • mergeSliceToMain()—— 189 行,带冲突检测/分类/自动解决的 squash-merge
  • git-self-heal.ts—— 198 行,3 个针对合并失败的恢复函数
  • fix-merge分派单元 —— 专门用一个 LLM 会话来解决自动解决器搞不定的冲突
  • smartStage()—— 49 行运行时暂存排除逻辑
  • 冲突分类 —— 80 行,区分.gsd/冲突、运行时冲突与代码冲突

总计约 582 行、分布在 3 个文件中的合并/分支/冲突代码,外加fix-merge提示词模板与分派逻辑——而这些代码的存在仅仅是为了服务切片分支。

4. 双隔离模式

分支模式(git-service.ts:mergeSliceToMain)与工作树模式(auto-worktree.ts:mergeSliceToMilestone)是两套并行实现:不同的合并策略、不同的冲突处理、不同的分支命名。两条路径都必须被维护和测试——共有 11 个测试文件覆盖合并/分支/工作树逻辑。

5. Bug 历史

  • v2.11.1:因分支切换导致的解析缓存过期引发重复单元分派,发布 URGENT 修复
  • v2.13.1:Windows 热修复mergeSliceToMilestone中的多行提交信息
  • M003 之前:15+ 个针对.gsd/合并冲突的独立 bug 修复
  • 持续的用户投诉:循环检测失败与状态损坏

决策:彻底消灭切片分支

ADR-001 的核心决策是:

Eliminate slice branches entirely.(彻底消除切片分支。)里程碑工作树内的所有工作都按顺序提交在同一条分支(milestone/<MID>)上。没有分支创建、没有分支切换、没有切片合并、没有工作树内冲突解决。

同时把.gsd/规划产物纳入 Git 跟踪,只忽略运行时/临时状态。

目标架构

main ──────────────────────────────────────────── main │ ↑ └─ worktree (milestone/M001) │ │ │ commit: feat(M001): context + roadmap │ commit: feat(M001/S01): research │ commit: feat(M001/S01): plan │ commit: feat(M001/S01/T01): impl │ commit: feat(M001/S01/T02): impl │ commit: feat(M001/S01): summary + UAT │ commit: feat(M001/S02): research │ commit: ... │ commit: feat(M001): milestone complete │ │ │ └──────────── squash merge ──────────────────┘

使用的 Git 原语

原语用途
Worktrees(工作树)每个活动里程碑一个。文件系统级隔离。
Commits(提交)每次动作的粒度化顺序历史。
Squash merge每个里程碑在main上留下一个干净的单一提交。
Branches(分支)只有main和milestone/<MID>。没有其他分支。

不使用的 Git 原语

原语原因
切片分支切片是顺序执行的。分支只会增加复杂度,没有任何回滚收益。
--no-ff合并工作树内没有分支需要合并。
分支切换从不发生。所有工作都在一条分支上。
冲突解决工作树内没有合并,就没有工作树内冲突。

状态归属模型:.gsd/从「整体忽略」到「分类治理」

ADR-001 对.gsd/的归属做了关键切分:

纳入 Git 跟踪(随分支流转):

.gsd/milestones/ — roadmaps, plans, summaries, research, contexts, task plans/summaries .gsd/PROJECT.md — project overview .gsd/DECISIONS.md — architectural decision register .gsd/REQUIREMENTS.md — requirements register .gsd/QUEUE.md — work queue

保持 gitignored(临时、运行时、基础设施):

.gsd/runtime/ — dispatch records, timeout tracking .gsd/activity/ — JSONL session dumps .gsd/worktrees/ — git worktree working directories .gsd/auto.lock — crash detection sentinel .gsd/metrics.json — token/cost accumulator .gsd/completed-units.json — dispatch idempotency tracker .gsd/STATE.md — rendered state projection .gsd/gsd.db — authoritative runtime database (local, gitignored) .gsd/DISCUSSION-MANIFEST.json — discussion phase tracking .gsd/milestones/**/*-CONTINUE.md — interrupted-work markers .gsd/milestones/**/continue.md — legacy continue markers

.gitignore更新方案

用显式的「仅运行时忽略」替代一刀切的.gsd/忽略:

# ── GSD: Runtime / Ephemeral ───────────────────────────────── .gsd/auto.lock .gsd/completed-units.json .gsd/STATE.md .gsd/metrics.json .gsd/gsd.db .gsd/activity/ .gsd/runtime/ .gsd/worktrees/ .gsd/DISCUSSION-MANIFEST.json .gsd/milestones/**/*-CONTINUE.md .gsd/milestones/**/continue.md

规划产物(milestones/、PROJECT.md、DECISIONS.md、REQUIREMENTS.md、QUEUE.md)不在.gitignore中,按常规方式被跟踪。

现状对照:本仓库根目录 .gitignore 至今仍保留# ── GSD project state (per-worktree, never committed) ── .gsd/以及.gsd/gsd.db的整体忽略——这正是该 ADR 的决策「未被采纳」的直接体现(详见下文「取代与演进」)。但GSD_RUNTIME_PATTERNS的精细化模式已在 gitignore.ts 落地为可编程的运行时路径清单。

落地后的代码形态变化(Consequences)

代码删除清单

文件删除行数移除内容
auto-worktree.ts~246mergeSliceToMilestone()、shouldUseWorktreeIsolation()、getMergeToMainMode()、切片合并守卫
git-service.ts~250mergeSliceToMain()、冲突解决、合并后运行时剥离、ensureSliceBranch()、switchToMain()
git-self-heal.ts~86abortAndReset()、withMergeHeal()(合并专用恢复)
auto.ts~150合并分派守卫、fix-merge分派路径、分支模式路由
worktree.ts~40getSliceBranchName()、ensureSliceBranch()、mergeSliceToMain()委托
测试文件~11 个文件auto-worktree-merge.test.ts、auto-worktree-milestone-merge.test.ts及相关合并测试用例
总计~770+ 行

mergeMilestoneToMain()变成什么

该函数被大幅简化,流程变为六步:

  1. 自动提交工作树中的任何脏状态
  2. chdir回到 main 仓库根
  3. git checkout main
  4. git merge --squash milestone/<MID>
  5. 用里程碑摘要git commit
  6. 移除工作树并删除分支

不再有冲突分类、不再有运行时文件剥离、不再有.gsd/特殊处理。规划产物能干净合并,是因为它们位于.gsd/milestones/M001/——在合并之前main上根本不存在这个目录。

源码佐证(现状):mergeMilestoneToMain()至今仍存在于 auto-worktree.ts,其实现保留了 ADR 规划的第一要素——「自动提交脏状态」并增加了并行模式守卫(仅当 cwd 处于里程碑分支时才自动提交,防止在集成分支上误提交他里程碑的脏文件污染main,#2929),随后还会先把工作树 DB 回灌到 main DB 再离开工作树上下文。测试可参考 auto-worktree-milestone-merge.test.ts 与 all-milestones-complete-merge.test.ts。

smartStage()变成什么

GSD_DURABLE_PATHS的 force-add 不再需要——规划产物不再被 gitignore,git add -A自然收录它们。函数缩减为:

  1. git add -A
  2. git reset HEAD -- <runtime paths>(取消暂存运行时文件)

_runtimeFilesCleanedUp的一次性迁移逻辑也可以删除。

源码对照:当前 git-service.ts 的smartStage()已经演进为「用 pathspec 排除暂存」模式:排除RUNTIME_EXCLUSION_PATHS而非整个.gsd/,以避免大型未跟踪产物树导致git add -A挂起(#1605),并防止里程碑中途整体排除.gsd/造成后半段产物永不提交的静默失败(#1326)。它还包含并行工作者的里程碑锁(GSD_MILESTONE_LOCK)逻辑:只暂存属于本里程碑的文件(#1991)。这验证了 ADR 判断——暂存逻辑的复杂度中心从「合并」转移到了「运行时路径排除与并行作用域」。

handleAgentEnd()变成什么

任一单元完成后:

  1. 使缓存失效
  2. autoCommitCurrentBranch()—— 在唯一的分支上提交
  3. verifyExpectedArtifact()—— 文件总是在当前分支上(没有分支切换)
  4. 持久化完成键

"Path A fix"(原 937-953 行)成为唯一路径,分支不匹配不可能发生。

fix-merge消亡

fix-merge分派单元类型被彻底消除。工作树内没有任何会产生冲突的合并。唯一的合并是里程碑→main(squash),万一冲突(罕见的并行里程碑边缘情况),就在里程碑完成时做一次性解决——而不是进入分派循环。

向后兼容

shouldUseWorktreeIsolation()的三级偏好解析被单一行为取代:始终使用工作树隔离。git.isolation: "branch"偏好被弃用。

拥有既有gsd/M001/S01切片分支的项目仍可被状态推导(state derivation)读取,但新工作不再创建切片分支。

风险与缓解(Risks)

1. 并行里程碑在 squash-merge 时的代码冲突

如果两个里程碑修改同一源文件,第二个 squash-merge 回main时会冲突。缓解:squash-merge 前先git fetch origin main && git rebase main。这是标准实践,在单用户工作流中很少见。

2. squash 后丢失逐切片 Git 历史

squash 合并把main上所有提交折叠成一个。缓解:

  • 提交信息带切片标签(feat(M001/S01/T01):)——可用git log --grep过滤
  • 里程碑分支可以保留(不删除)以备需要历史
  • 备选:用merge --no-ff替代--squash以在main上保留历史

3.git reset后 SQLite DB 失同步

如果被跟踪的 markdown 通过git reset --hard回滚,被 gitignored 的gsd.db不会回滚。当前 GSD 在运行时把数据库视为权威,不会静默导入 markdown 投影。当数据库丢失或损坏、而 markdown 才是意图来源时,操作者应使用显式的恢复/导入命令。

4. 多工作树的磁盘占用

每个工作树都会复制工作目录(包括node_modules)。缓解:同一时间只保持一个活动里程碑(单用户工作流),完成后立即清理。

备选方案分析(Alternatives Considered)

A. 保留切片分支,用即时迷你合并修复可见性

research-slice或plan-slice后立即把切片分支合并回里程碑分支。这能修复循环检测 bug,但保留全部合并复杂度。被拒:新增一条合并路径而非移除根因,仍需要冲突解决、自愈、分支切换。

B. 保持.gsd/gitignored,对手动工作树从 Git 历史引导状态

当 GSD 在工作树中检测到空的.gsd/时,用git show <commit>:.gsd/...从分支的 Git 历史重建状态。被拒:这是恢复逻辑,不是架构。不解决分支无关状态的根本问题;Git 历史被重写时会失效。

C. 分支作用域的.gsd/目录(.gsd/branches/<branch-name>/milestones/...)

每个分支在.gsd/内的命名空间子目录中写入。被拒:增加复杂度而非移除复杂度;分支创建时需要重命名/移动目录,且与标准 Git 工具不兼容(git checkout不会重命名目录)。

验证:三方模型压力测试与 Codex 异议

该架构由三个独立模型压力测试过:

Gemini 2.5 Pro识别出 6 个攻击向量,无一击穿核心模型。建议:squash-merge 前预检 rebase(已采纳)、心跳锁(已存在)、启动时重建 DB(已通过 M001/S02 importers 采纳)。

GPT-5.4 (Codex)通读全代码库后确认模型健全,并指出smartStage()已经在 force-add 持久化路径(印证了「跟踪产物」思路),以及 PR #487 中的resolveMainWorktreeRoot在架构上是错误的(已采纳——PR 将关闭)。

代码库分析确认.gsd/milestones/尽管被.gitignore声明忽略,main上实际已被部分跟踪;GSD_DURABLE_PATHS的存在本身就是代码层面对「规划产物应当被跟踪」的承认;README 也已记录了正确的「仅运行时忽略」模式。

Codex 异议与逐条回应

异议 1:「切片完成后、集成前崩溃——今天运行时能检测孤儿切片分支并合并它们。」回应:无分支模型下没有可崩溃的集成步骤。切片工作直接提交在里程碑分支上,重启时deriveState()按原样读取分支状态。孤儿分支恢复路径的存在仅仅是因为切片分支——移除分支就移除了它要恢复的故障模式。

异议 2:「两个终端并发编辑共享根文档(PROJECT.md、DECISIONS.md)。」回应:有效的边缘情况。如果/gsd queue在main上编辑DECISIONS.md,同时 auto 模式在工作树中编辑它,squash-merge 时会有内容冲突。这是标准的 Git 内容冲突——与两个开发者编辑同一文件无异,交给常规合并解决处理。既不是切片分支导致的,也不能靠切片分支解决。

异议 3:「切片→里程碑合并提供了持续集成。移除它们会把冲突发现推迟到最后。」回应:在单用户顺序工作流中,工作树内没有东西需要集成——每个切片都构建在前一个之上。唯一冲突源是main分叉(例如另一个里程碑先合并),而切片→里程碑合并本来就抓不到这种冲突——它在工作树内合并,不是对main合并。squash-merge 前的预检 rebase 能更直接地捕获它。

异议 4:「用另一个显式的切片边界原语替代切片分支,而不是直接删除。」回应:精神上接受。带规范标签的提交(feat(M001/S01):、feat(M001/S01/T01):)就是切片边界原语;git log --grep="M001/S01"可隔离切片历史;git revert可定位特定提交;Git 标签(gsd/M001/S01-complete)可在需要时标记切片完成。边界原语是提交元数据,而不是分支。

行动清单回顾

  1. 关闭 PR #487(resolveMainWorktreeRoot)——与该架构矛盾
  2. 作为一个带阶段的 GSD 里程碑实施:
    • 更新.gitignore并 force-add 既有规划产物
    • 移除切片分支的创建/切换/合并代码
    • 简化mergeMilestoneToMain()与smartStage()
    • 移除fix-merge分派单元
    • 移除分支模式隔离(git.isolation: "branch")
    • 更新/删除 11 个测试文件
    • 更新 README 建议的 gitignore
    • 为既有切片分支项目提供迁移路径

取代与演进:ADR-001 的历史坐标

阅读本 ADR 必须注意其状态栏:Superseded(2026-05-08 被取代)。替代它的不是单一文档,而是一个「三胞胎 + 一」的决策簇:

  • ADR-016-worktree-lifecycle-and-projection.md:把工作树处理拆成「生命周期」与「状态投影」两个模块,用WorktreeLifecycle(enterMilestone/exitMilestone/restoreToProjectRoot等动词)与WorktreeStateProjection(projectRootToWorktree/projectWorktreeToRoot/finalizeProjectionForMerge)接口收拢s.basePath变更与process.chdir纪律,并把WorktreeResolver的 28 字段依赖接口退役
  • ADR-016-worktree-safety-fail-closed.md:工作树状态的 fail-closed 校验
  • ADR-016-worktree-lifecycle-and-projection 的 phase-2 设计
  • ADR-017-state-reconciliation-drift-driven.md:漂移驱动的状态调和

取代的核心原因是问题定义变了:ADR-001 的原始动机(.gsd/合并冲突、规划产物可见性)被DB 权威运行时模型取代——项目根数据库(.gsd/gsd.db)成为权威,markdown 文件成为投影(projection),跨分支状态互相覆盖不再是该 ADR 试图修复的故障模式。这一点在源码中有明确印记:auto.ts 标注「DB status is the authoritative」、auto-recovery.ts 对execute-task/complete-slice以 DB 状态为权威;同时 query-tools.ts 提供「flush SQLite WAL 后再git add .gsd/gsd.db」的工具,说明gsd.db在部分路径下也会被提交。

剩余的与工作树相关的关切(生命周期归属、fail-closed 源码写入、漂移修复)由ADR-014(Auto Orchestration 深度模块)、ADR-015(运行时不变性模块)、ADR-016 三件套与 ADR-017 分别收敛。同时里程碑工作树内的切片分支被保留了下来——ADR-016 的 carve-out 清单中仍可见mergeSliceToMain(当前实现于 slice-cadence.ts,由git.collapse_cadence: "slice"配置驱动,#4765)等合并原语,说明「每切片即时合并回 main」的片节奏(slice-cadence)模式在现行版本中成为可选项。

一句话概括这个演进:ADR-001 的诊断(切片分支是复杂度的源头)和药方(更简单的合并面、更清晰的.gsd/归属、用提交元数据做切片边界)大部分被继承;但它的主攻方向——把规划产物变成被跟踪的 Git 状态——被「SQLite 数据库权威 + markdown 投影」的新模型取代,后者成为当前仓库处理自治编码状态的根本范式。因此本文第一部分的历史分析(§Decision 之前的全部内容)保留作上下文参考,而 §Decision 中的决策未被原样采纳。

延伸阅读

  • PRD-branchless-worktree-architecture.md:该架构的产品需求文档,补充动机与验收标准
  • ADR-016-worktree-lifecycle-and-projection.md:工作树生命周期与状态投影模块拆分(直接继任者)
  • ADR-017-state-reconciliation-drift-driven.md:漂移驱动的状态调和
  • ADR-002-external-state-directory.md:外部状态目录设计,理解.gsd/归属争论的姊妹决策
  • 核心实现:git-service.ts、auto-worktree.ts、gitignore.ts
  • 关键测试:auto-worktree-milestone-merge.test.ts、gitignore-staging-2570.test.ts、git-service.test.ts(其中对RUNTIME_EXCLUSION_PATHS的断言直接检验了「运行时路径永不入库」这一不变性)
  • 人工智能
  • AI Agent
  • 代码智能体
  • Agent 编排
  • CLI
  • AI 应用

【免费下载链接】gsd-2

A powerful meta-prompting, context engineering and spec-driven development system that enables agents to work for long periods of time autonomously without losing track of the big picture

项目地址:https://gitcode.com/gh_mirrors/gs/gsd-2
点击查看免费下载
上一篇:AndroidLibs实操教程:如何快速集成图片加载框架
下一篇:在 Vercel 上零配置部署 Create React App:从项目初始化到 vercel dev 联调

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询