【免费下载链接】gsd-core
Git. Ship. Done - Core
本文介绍 gsd-core 的Existing Code Onboarding Module(现有代码接入模块):它通过一个纯函数式、只读的投影(projection)模块,确定性检测 brownfield(既有代码)仓库的文件系统状态,并据此选择下一步该运行的接入原语(/gsd:map-codebase、/gsd:ingest-docs、/gsd:new-project)。读完本文,你将理解/gsd:onboard的路由判定逻辑、状态检测规则、安全不变量,以及它如何被 Init Command Module 和工作流渲染层消费。
该模块的架构决策记录在 docs/adr/1990-existing-code-onboarding.md(ADR-1990,Issue #1990,实现 PR #1994),核心实现位于 src/onboard-projection.cts。
背景:为什么需要一个接入编排入口
GSD 早已具备若干处理既有代码库的独立原语:
/gsd:map-codebase—— 并行代码库分析,产出.planning/codebase/地图;/gsd:ingest-docs—— 分类并整合仓库中已有的 ADR/PRD/SPEC/RFC 文档;/gsd:new-project—— 规划初始化,建立PROJECT.md/REQUIREMENTS.md/ROADMAP.md/STATE.md。
问题在于:缺少一个统一的引导入口,告诉用户在某个 brownfield 仓库中哪一个原语应该先执行。如果只靠自然语言描述,执行顺序是模糊且不安全的——用户可能在代码库地图尚未生成前就初始化规划、跳过相关设计文档、或者覆盖/重复.planning/上下文而不是复用它。
ADR-1990 的核心判断是:这个顺序问题不是口味偏好,而是一个依赖图——地图应在规划之前存在;已有设计文档应在全新/gsd:new-project之前被 ingest;任何操作都不应破坏进行中的.planning/。而"从文件系统状态决定下一步安全动作"的依赖图,本质上是一个投影(projection)——它无法被工作流中的自然语言指令可靠地求值或测试。
三层架构:投影、处理器与渲染
该模块遵循 GSD 已有的两个投影模块先例——Planning Path Projection Module(ADR-0006,负责.planning路径解析)与Shell Command Projection Module(ADR-0009,负责运行时感知的命令投影)。如果/gsd:onboard以自由形式的工作流正文去内联扫描目录树,就会把不可测试、不确定的文件系统逻辑塞进 markdown——这正是那些投影模块要防止的反模式。
因此,模块被切分为三个明确层次:
- 投影(投影模块本身):src/onboard-projection.cts 编译为
gsd-core/bin/lib/onboard-projection.cjs。它是纯函数、无副作用的投影:只读仓库状态,计算next_action,从不写入任何文件。 - 处理器(Init Command Module):src/init.cts 是
init.*查询处理家族的所有者,其 cmdInitOnboard 处理器(L1865-L1881) 调用buildOnboardProjection,并合并getInitGitState的 Git 状态字段,最终以与兄弟处理器相同的{ data: <flat JSON> }契约输出。命令路由在 src/init-command-router.cts 的 onboard: 分支(L182-L185) 中接线:解析--fast/--text布尔标志后调用cmdInitOnboard。 - 渲染(工作流层):gsd-core/workflows/onboard.md 只负责菜单/门禁呈现;commands/gsd/onboard.md 及其技能镜像 skills/gsd-onboard/SKILL.md 负责委托。模块本身只拥有它们消费的"状态 → 路由"决策。
在 CLI 层面,你可以直接查看投影结果:
# 默认模式(完整地图门槛) gsd_run --cwd "$PWD" init onboard --raw # 快速模式(接受 fast map 用于轻量接入) gsd_run --cwd "$PWD" init onboard --fast --raw # 文本模式(无交互选择器的运行时) gsd_run --cwd "$PWD" init onboard --text --raw--raw输出为扁平 JSON 结构(如next_action.kind、is_brownfield、map_readiness、handoff_commands等字段),供工作流解析渲染。
状态检测:投影的输入信号
投影从仓库文件系统读取五类信号。下面的信号表来自 ADR-1990,括号内为源码中的实际实现细节。
| 信号 | 规则 / 不变量 |
|---|---|
| Brownfield 代码存在 | 深度受限的递归代码文件扫描(hasCodeFilesInternal)或识别到包清单(hasPackageFileInternal) |
| 生成 / vendor 目录排除 | 扫描跳过CODE_SCAN_SKIP_DIRS,避免 vendored 树产生错误的 brownfield 判定 |
| 代码库地图完整性 | .planning/codebase/是否持有规范地图产物 |
| 既有设计文档 | 是否存在 ADR/PRD/SPEC/RFC 风格的候选(根级、嵌套目录、以及路径段匹配) |
| 部分规划状态 | PROJECT.md/REQUIREMENTS.md/ROADMAP.md/STATE.md是否只存在一部分 |
深度受限的代码扫描与包清单
hasCodeFilesInternal(src/onboard-projection.cts L116-L133)以深度上限 3递归扫描,匹配 31 种源码扩展名:.ts.tsx.js.jsx.mjs.cjs.py.go.rs.swift.java.kt.kts.c.cpp.cc.h.hpp.cs.rb.php.dart.m.mm.scala.groovy.lua.r.R.zig.ex.exs.clj。深度上限保证扫描成本可控,不会遍历整个仓库。
hasPackageFileInternal(L135-L137)检查 18 种常见包清单文件:package.json、requirements.txt、pyproject.toml、Cargo.toml、go.mod、Package.swift、build.gradle、build.gradle.kts、pom.xml、Gemfile、composer.json、pubspec.yaml、CMakeLists.txt、Makefile、build.zig、mix.exs、project.clj。即使没有任何源码文件,只要存在包清单也被视为 brownfield——这是测试中明确覆盖的语义(见下文"测试与回归")。
最终isBrownfield = hasCode || hasPackageFile(L345),同时投影输出has_existing_code与has_package_file两个细分字段。
生成 / vendor 目录排除
ADR 中记录的CODE_SCAN_SKIP_DIRS为node_modules、dist、build、.next、.nuxt、.svelte-kit、coverage、vendor、.venv、venv;源码中的实际集合(L19-L22)更完整,还包含.git、.planning、.claude、.codex、__pycache__、target。这些目录在代码扫描与文档候选扫描中都会被跳过,确保一个只有node_modules/dist的"空"仓库不会被误判为 brownfield——对应测试ignores generated and vendor directories when detecting existing code。
代码库地图完整性
完整地图与快速地图各有一套规范文件清单(L31-L38):
| 模式 | 必需文件 |
|---|---|
完整地图(REQUIRED_CODEBASE_MAP_FILES,7 个) | STACK.md、ARCHITECTURE.md、STRUCTURE.md、CONVENTIONS.md、TESTING.md、INTEGRATIONS.md、CONCERNS.md |
快速地图(FAST_CODEBASE_MAP_FILES,4 个) | STACK.md、INTEGRATIONS.md、ARCHITECTURE.md、STRUCTURE.md |
listCodebaseMapFiles(L202-L212)只读取项目作用域下的.planning/codebase/(注释标注了 Issue #3964:扁平根读取会让GSD_PROJECT下的has_codebase_map/needs_codebase_map答错项目)。由此得到三态map_readiness: 'none' | 'fast' | 'complete'(L214-L218)。投影还会输出missing_codebase_map_files、missing_fast_codebase_map_files、codebase_map_summary_status、codebase_map_final_status等诊断字段。
设计文档候选
listPlanningDocCandidates(L139-L200)在深度 ≤ 3 内识别设计文档,命中规则为(满足其一即算候选,且仅限.md文件):
- 文件名匹配
/(^|[-_ ])(ADR|PRD|SPEC|RFC)([-_ ]|\.)/i(如ADR-001.md、0001-PRD.md); - 文件名匹配
/^\d{4}[-_].+\.md$/i(如0001-decision.md); - 相对路径的某个路径段属于
PLANNING_DOC_SEGMENTS:adr/adrs/prd/prds/spec/specs/rfc/rfcs(如docs/adr/0001-runtime.md); - 文件名恰为
REQUIREMENTS.md。
扫描覆盖根级文件以及docs、adr、adrs、prd、prds、spec、specs、rfc、rfcs根目录(L140),同样跳过CODE_SCAN_SKIP_DIRS。
规划状态
投影逐一探测四个规划文档的存在性(L352-L361):PROJECT.md(同时检查根级与项目作用域两个候选路径)、REQUIREMENTS.md、ROADMAP.md、STATE.md,并生成planningMissing缺失清单(L232-L244)。hasPlanningArtifacts为四者任一存在。同时探测.planning/config.json、.planning/onboarding/SUMMARY.md等辅助状态。
路由选择:依赖序优先于便利序
ADR-1990 给出的路由选择(输出)按依赖序排列:
- Brownfield 代码且
.planning/codebase/地图不完整 → 交给/gsd:map-codebase(fast 模式为/gsd:map-codebase --fast); - 存在设计文档候选且尚无项目 → 在
/gsd:new-project之前提供/gsd:ingest-docs; - 否则 →
/gsd:new-project。
并且 ADR 强调:partial-planning 与 fast-map-completeness 必须在 docs-ingest 分支之前求值,这样半地图或半初始化的仓库永远不会被路由越过它尚欠的步骤。
源码中nextAction(src/onboard-projection.cts L246-L319)实现了更细粒度的 8 分支判定,顺序即优先级:
| 优先级 | 条件 | next_action.kind | 含义 |
|---|---|---|---|
| 1 | isBrownfield && needsOnboardCodebaseMap | map-codebase | 检测到既有代码但缺必需地图;fast 模式用map_codebase_fast |
| 2 | hasPlanningArtifacts && missingPlanningFiles.length > 0 | partial-planning | 规划存在但不完整,列出missing |
| 3 | fastMode && mapReadiness === 'fast' && !projectExists | complete-map-before-new-project | fast map 只够轻量接入,项目初始化前仍需完整地图 |
| 4 | hasDocsCandidates && !projectExists | ingest-docs | 项目建立前应先 ingest 已有设计文档 |
| 5 | !isBrownfield && !projectExists && !hasDocsCandidates | new-project | 未检测到代码或规划文档(greenfield) |
| 6 | !projectExists | new-project | 代码库上下文已就绪,可初始化项目 |
| 7 | !onboardingSummaryExists | write-summary | 缺少接入摘要 |
| 8 | 兜底 | ready | 接入摘要已存在 |
fast 模式通过needsOnboardCodebaseMap = options.fast ? needsFastCodebaseMap : needsCodebaseMap(L351)切换门槛:fast 模式只要求 4 个快速地图文件齐全,而非 7 个完整地图文件。但注意第 3 分支:在项目建立前,fast map 仍不足以放行new-project——只有当项目设置已完整时(分支 7),fast map 才足够推进到摘要阶段。这正是回归测试#1990中"fast map gate misroute"修正后的行为。
每个分支都携带人类可读的reason,例如Existing code was detected, but the required .planning/codebase/ map is missing.,工作流直接将其呈现给用户。
安全不变量:为什么这是一个模块而非辅助函数
ADR-1990 明确列出四条安全不变量,全部由投影的只读设计保证:
- 幂等 / 无静默覆盖。接入过程绝不修改既有被跟踪的
.planning/产物;重复运行保持字节不变。测试reports complete codebase map and onboarding summary in existing planning在运行前后对PROJECT.md、ROADMAP.md、STATE.md、SUMMARY.md做字节级比对断言(tests/onboard-command.test.cjs L114-L153)。 SUMMARY.md是尾随产物。.planning/onboarding/SUMMARY.md只在项目设置已存在且文件缺失时才写入;工作流明确"不覆盖已有摘要"。- "完成"是合取而非析取。只有
PROJECT.md、REQUIREMENTS.md、ROADMAP.md、STATE.md全部存在才报告完成——不存在单文件短路。 - 文本模式等价。
--text渲染与交互式选择器完全相同的门禁决策为编号纯文本提示,使没有交互选择器的运行时(OpenAI Codex、Antigravity 等)获得一致路由。
运行时感知的 handoff 命令
投影输出的handoff_commands(buildHandoffCommands,L321-L331)不是硬编码的/gsd:xxx字符串,而是通过 src/runtime-slash.cts 的 formatGsdSlash(L31-L74) 按运行时解析格式:resolveRuntime读取运行时身份,formatGsdSlash依据能力注册表中的commandStyle决定输出——Claude 风格输出/gsd-<cmd>,Codex 等 shell-var 运行时输出$gsd-<cmd>(命令 token 小写)。这样投影出的"下一步命令"对当前安装的运行时的斜杠语法总是正确的。
{ "handoff_commands": { "map_codebase": "/gsd-map-codebase", "map_codebase_fast": "/gsd-map-codebase --fast", "ingest_docs": "/gsd-ingest-docs", "new_project": "/gsd-new-project", "manager": "/gsd-manager", "onboard": "/gsd-onboard" } }在GSD_RUNTIME=codex环境下,同样的命令会渲染为$gsd-map-codebase等(对应测试formats onboard handoff commands for the resolved runtime)。另外,onboarding_summary_path使用锚定在 cwd/project_root 的绝对路径(源码注释标注 Issue #2376),避免派生子代理的 cwd 与编排者不一致导致路径错位。
工作流渲染:菜单、门禁与文本模式
gsd-core/workflows/onboard.md 是围绕投影的"薄渲染器":它通过gsd_run自举解析器(来自共享的references/gsd-run-resolver.md,而非内联)运行init onboard --raw/init onboard --fast --raw,解析 JSON 字段后按next_action.kind分派:
map-codebase:询问用户"先映射代码库?"(推荐)或"跳过映射"。跳过路径仍有守卫:若规划存在但不完整,改走 partial-planning 守卫;若存在文档候选且无项目,改走 docs ingest;否则提示跳过会削弱new-project的上下文。ingest-docs:询问是否先 ingest 检测到的 N 个文档候选;跳过则警告会遗漏既有文档上下文。complete-map-before-new-project/new-project/partial-planning:直接打印下一步命令并要求在ONBOARDING_ROOT({git_worktree_root || _GSD_RUNTIME_ROOT})下运行后重跑/gsd:onboard。write-summary:创建.planning/onboarding/SUMMARY.md(不覆盖),模板记录项目状态、代码库上下文、文档上下文与推荐下一步;若commit_docs为真,仅提交摘要路径(query commit "docs: create onboarding summary" --files .planning/onboarding/SUMMARY.md)。ready:打印最终状态并结束。
工作流同时处理response_language(用户可见输出翻译为指定语言,技术术语与路径保持英文)、嵌套 Git worktree 警告(has_git && in_nested_subdir时提示产物归属外层 worktree 且不执行git init),以及 Copilot 的vscode_askquestions等价适配。接入流程绝不执行实现阶段或 ship 工作——这也是命令契约测试断言!content.includes('execute-phase')与!content.includes('gsd:ship')的原因。
模块边界:什么留在模块之外
ADR-1990 明确划定了边界,防止模块膨胀:
- 原语本身:
/gsd:map-codebase、/gsd:ingest-docs、/gsd:new-project保持各自行为不变;模块只选择并排序它们,投影路由而不重实现目的地。 - 写入规划产物:所有
.planning/写入仍归目的地命令与 Installer/规划模块所有;投影是只读的。 - 工作流的渲染:菜单/门禁呈现归 gsd-core/workflows/onboard.md,命令委托归 commands/gsd/onboard.md 及其技能镜像 skills/gsd-onboard/SKILL.md;模块只拥有它们消费的状态→路由决策。
测试与回归
投影的负载行为全部由 tests/onboard-command.test.cjs(约 25 KB)覆盖,主要用例包括:
- brownfield 代码 / 文档 / 缺失规划状态的整体报告;
- 顶层
ADR/PRD/RFC目录与根级设计文档的候选检测; --text标志透传为text_mode: true;- 完整地图与既有摘要下的幂等性(无突变断言);
- fast 地图就绪但缺完整地图时,路由到
complete-map-before-new-project(next_action.command为/gsd-map-codebase); - partial-planning 在 docs-ingest 与 complete-map 门禁之前求值——即回归测试
#1990:fast mode routes incomplete planning to partial-planning before the complete-map gate; - 对全部状态(code / docs / greenfield / partial planning / summary / ready)的
next_action精确断言; - vendor 目录排除与包清单 brownfield 判定;
- 运行时格式化(
GSD_RUNTIME=codex→$gsd-*); - 点号查询
query init.onboard与直接init onboard的输出一致性; - 命令契约测试:工作流必须引用共享 resolver、渲染全部 7 种
next_action.kind、跳过路径必须显式 handoff 且保持 partial-planning 先于 docs-ingest 的守卫顺序。
后果与维护耦合
ADR-1990 记录的后果包括:
- Brownfield 接入从"散文中的操作顺序民俗"变为单一、可测试、确定性路由的入口。
- Init Command Module 新增一个重量级处理器
initOnboard,沿用与兄弟处理器相同的{ data: <flat JSON> }契约,没有新的分发形态。 - 新增的维护耦合已被显式记录:模块的完整性检查必须跟随规范
.planning/codebase/产物清单及路由目标的身份变化;若三个目的地命令更改其入口契约,投影必须跟进。ADR 将这一耦合记录为集中化路由决策的已知成本——与之相对的替代方案(在每个原语内复制该决策)更糟。 - 无新增运行时依赖,不改变既有命令语义(纯增量)。
开放问题
ADR-1990 留有两个开放问题,供后续演进观察:
- 代码库地图完整性是否应改为从地图模块持有的单一共享谓词获取,而非在本模块中重新编码,以避免两处漂移?
/gsd:onboard工作流从共享的references/gsd-run-resolver.md片段获取gsd_run自举而非内联;如果该委托模式被其他工作流采用,可能值得单独成文一份简短 ADR——此处记录该先例使其可见,而非悄然确立。
快速上手
对刚克隆的既有仓库,接入流程是:
# 1. 从仓库根运行 onboard,查看投影出的下一步 gsd_run --cwd "$PWD" init onboard --raw # 2. 按 next_action 指示执行: # - map-codebase → gsd_run --cwd "$PWD" init map-codebase --raw(或带 --fast) # - ingest-docs → gsd_run --cwd "$PWD" init ingest-docs --raw # - new-project → gsd_run --cwd "$PWD" init new-project --raw # - partial-planning → 补齐缺失的 PROJECT.md / REQUIREMENTS.md / ROADMAP.md / STATE.md # - write-summary/ready → 接入完成,接下来运行 /gsd-manager # 3. 每个目的地命令执行完毕后,重新运行 onboard,直到 next_action.kind 变为 ready接入的终点是ready:四份规划文档齐全、代码库地图完整(fast 模式除外,见上文第 3 分支约束)、接入摘要已写入.planning/onboarding/SUMMARY.md,系统推荐的下一步是/gsd-manager。整个过程不执行任何实现阶段,不 ship 任何工作——/gsd:onboard只负责把你安全地领到正确的起点。
【免费下载链接】gsd-core
Git. Ship. Done - Core
相关推荐
GSD Core 既有代码库上手指南:`/gsd:onboard` 一站式接入流程与底层路由原理
GSD Core 既有代码库上手指南: /gsd:onboard 一站式接入流程与底层路由原理 本文聚焦 Git Ship Done(GSD)Core 为“已有
GSD Core 现有代码库接入指南:/gsd-onboard 的 Brownfield 检测、安全交接与规划产物生成机制
GSD Core 现有代码库接入指南:/gsd onboard 的 Brownfield 检测、安全交接与规划产物生成机制 /gsd onboard 是 GSD
如何把存量代码库接入GSD Core:代码库映射与onboard技巧全清单
如何把存量代码库接入GSD Core:代码库映射与onboard技巧全清单 GSD Core (Git. Ship. Done)是一个面向 AI 编码代理的上下
移动开发数据库
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考