存储库:在独立仓库中规划
测试版。 存储库、引用、工作上下文和工作集是新增功能。命令名称、标志、文件格式和 JSON 输出在版本之间可能仍会发生变化。以下每个操作演练均针对当前版本运行,但在升级后请重新阅读本指南。
此功能解决的问题
OpenSpec 通常存在于一个代码仓库内:一个位于代码旁边的 openspec/ 文件夹,用于存放该仓库的规范和变更。
当你的规划超出单个仓库的范畴时,这种模式就不再适用:
- 你的工作跨越多个仓库——一个功能涉及 API 服务器、Web 应用和共享库。计划应该放在谁的
openspec/文件夹中? - 你的团队在代码存在之前进行规划,或规划永远不会成为此仓库中代码的内容。
- 需求由一个团队拥有,被其他团队使用。Wiki 版本会漂移,而且你的编码代理无论如何都无法读取它。
存储库就是答案:一个专门负责规划的独立仓库。它具有你已经熟悉的 openspec/ 结构——规范和变更——外加一个小的身份文件。你在机器上按名称注册一次,之后所有常规的 OpenSpec 命令就可以在任何地方对它进行操作。
结构
team-plans (一个存储:在独立的仓库中进行规划)
├── .openspec-store/store.yaml identity: "I am team-plans"
└── openspec/
├── specs/ 确定的事实
└── changes/ 进行中的变更
▲
│ 按名称在每个机器上注册;
│ 像普通仓库一样通过推送/克隆共享
┌─────────────┼─────────────┐
│ │ │
web-app api-server mobile-app
(代码仓库) (代码仓库) (代码仓库)两条规则让这一切保持简单:
- 存储只是一个 Git 仓库。 你可以自己提交、推送、拉取和审查它。OpenSpec 永远不会自行克隆、同步或推送任何内容。
- 声明而非机制。 仓库可以声明它们与存储的关系(如下所示)。声明会改变 OpenSpec 能告诉你的信息——但绝不会改变命令的作用范围。
五分钟创建第一个存储
两个命令让你从零开始,进入一个可工作的、基于存储范围的变更流程:
openspec store setup team-plans --path ~/openspec/team-plansStore ready: team-plans
Location: /Users/you/openspec/team-plans
OpenSpec root: ready
Registry: registered
Next: run normal OpenSpec commands against this store, for example:
openspec new change <change-id> --store team-plans
Share this store by committing and pushing it like any Git repo.openspec new change add-login --store team-plansUsing OpenSpec root: team-plans (/Users/you/openspec/team-plans)
Created change 'add-login' at /Users/you/openspec/team-plans/openspec/changes/add-login/
Schema: spec-driven
Next: openspec status --change add-login --store team-plans这就是整个模型。从这里开始,生命周期与你所熟知的完全一致——status、instructions、validate、archive——只需在每个命令后加上 --store team-plans,并且每个打印出的提示都会自动携带该标志。Using OpenSpec root: 行始终告诉你命令正在哪个位置执行。
案例:一个团队,一个规划仓库
一个团队将其规范和变更保存在 team-plans 中,而不是将它们分散在各个代码仓库里。
第一天(由设置者操作):
openspec store setup team-plans --path ~/openspec/team-plans \
--remote git@github.com:acme/team-plans.git
git -C ~/openspec/team-plans push -u origin main传递 --remote 会将克隆 URL 记录在存储自身的身份文件 (.openspec-store/store.yaml) 的初始提交中。每次未来的克隆都会“生来”就知道其来源,因此健康检查和错误消息可以为尚未拥有该存储的队友打印完整且可直接粘贴的修复方案。
每位队友(每台机器一次):
git clone git@github.com:acme/team-plans.git ~/openspec/team-plans
openspec store register ~/openspec/team-plans从那时起,每个人都通过名称在同一规划仓库中工作:
openspec status --store team-plans --change add-login
openspec show add-login --store team-plans共享工作就是 Git,这是有意为之。 你创建的变更仅存在于你的检出副本中,直到你提交并推送它——这与代码相同。计划天然拥有分支、拉取请求和审查功能,因为存储就是一个普通的仓库。
连接团队的代码仓库。 如果一个代码仓库的规划完全外部化,则只需在 openspec/config.yaml 中添加一行:
# web-app/openspec/config.yaml
store: team-plans现在,在 web-app 内部运行的每个 OpenSpec 命令都会作用于 team-plans,无需任何标志:
cd ~/src/web-app
openspec status --change add-loginUsing OpenSpec root: team-plans (/Users/you/openspec/team-plans)
...这个指针是后备方案,绝非覆盖项:显式的 --store 始终优先;如果仓库发展出属于自己的真实规划文件夹,这些文件夹将优先(同时会发出警告以移除过时的指针)。
为机器上的每个仓库设置一个默认值。 如果你在多个代码仓库之间工作,且它们都规划到同一个存储中,可以全局设置一次,而不是在每个仓库中添加 store: 行:
openspec config set defaultStore team-plans现在,在任何非规划根目录下运行命令——且没有 --store 和项目指针时——将解析为 team-plans。它位于优先级列表的底部,因此 --store、本地根目录和项目 store: 指针仍然优先。根横幅和 JSON root 块会报告 source: "global_default" 以及存储 ID,这样你就可以随时区分机器级默认值和仓库自身的指针。使用 openspec config unset defaultStore 清除它。如果 ID 未注册,命令将报错并提示你注册它或清除过时的默认值。
示例:一个功能,两个组件仓库
假设 add-checkout-promo 变更同时影响 checkout-api 和 checkout-web。团队希望有一个共享的产品契约,而每个代码仓库仍需要自己的实现任务、分支和审查。
使用两层结构:
- 将共享行为保留在
team-plans中。 - 将实现计划保留在每个组件仓库中,并将存储作为只读上游上下文进行引用。
首先,在存储中规划共享契约:
openspec new change add-checkout-promo --store team-plans
openspec status --change add-checkout-promo --store team-plans提案和规范应描述组件边界处的行为——例如,服务返回的促销字段以及前端如何处理不合格的结账流程。像对待其他分支和拉取请求一样,在存储仓库中审查此变更。
规划能看到什么上下文?
选择存储会更改 OpenSpec 根目录;它不会发现或读取使用该存储的每个代码仓库。存储指令只能看到存储中的工件和配置的上下文。只有当这些文件夹也对代理或编辑器可用,且代理读取它们时,才能看到组件代码。
工作集是一种方便的方式,可以同时打开规划存储和两个代码仓库:
openspec workset create checkout-promo \
--member ~/openspec/team-plans \
--member ~/src/checkout-api \
--member ~/src/checkout-web \
--tool code
openspec workset open checkout-promo这使得这些文件夹在一个 IDE 工作区中可见。它不会将源代码上下文复制到存储中,也不会选择受影响的仓库,更不会授予代理编辑它们的权限。将持久的跨组件事实放入共享规范中;不要依赖规划器记住它偶然检查过的源代码。
如何在每个仓库中开始实施?
当没有显式的 --store 或更近的 openspec/ 根目录适用时,store: team-plans 指针会将命令路由到该存储。它不会根据调用 apply 的目录将一个存储的任务列表拆分。OpenSpec 目前不会将任务路由到仓库。
当每个组件需要独立范围的 apply/审查周期时,为其提供一个本地 OpenSpec 根目录,并引用中央存储,而不是指向它:
# checkout-api/openspec/config.yaml (checkout-web 同理)
schema: spec-driven
references:
- team-plans在共享契约获得批准并在存储的主规范中可用后,为组件的部分创建一个小型本地变更:
cd ~/src/checkout-api
openspec new change implement-checkout-promo-api
cd ~/src/checkout-web
openspec new change implement-checkout-promo-ui每个仓库指令中的引用索引提供了存储规范的摘要和确切的 openspec show ... --store team-plans 获取命令。每个本地提案都引用该共享契约,其任务仅描述该组件内的工作。然后在每个仓库中分别运行 /opsx:apply;根解析将工件和实施编辑限制在该仓库范围内。服务和前端的变更现在可以独立进行测试、审查、合并和归档。
如果在共享存储变更仍处于活动状态时必须开始实施,请使用 openspec show add-checkout-promo --store team-plans 显式获取它;引用索引列出的是规范的存储,而不是活动的存储变更。在拉取请求描述中将存储分支和组件分支链接起来,以便审查者可以看到每个实施遵循的是哪个版本的契约。
案例:跨越团队界限的需求
一个平台团队负责需求。产品团队在自己的仓库中,按照自己的设计,针对这些需求进行构建。引用描述了这种关系,而无需移动任何人的工作。
platform-reqs (存储) api-server (代码仓库)
由平台团队拥有 由产品团队拥有
┌──────────────────────────┐ ┌──────────────────────────┐
│ openspec/specs/ │ ◀────────│ openspec/config.yaml │
│ payments/spec.md │ 读取 │ references: │
│ auth/spec.md │ │ - platform-reqs │
│ │ │ openspec/specs/ │
│ │ │ (他们自己的设计) │
│ openspec/changes/ │ │ openspec/changes/ │
│ 平台工作 │ │ (他们自己的工作) │
│ │ └──────────────────────────┘
└──────────────────────────┘产品团队在其仓库的 openspec/config.yaml 中声明它所引用的内容:
references:
- platform-reqs引用是只读上下文。该仓库保留自己的 openspec/ 根目录;工作留在那里。发生变化的是:该仓库中的 openspec instructions 现在包含了被引用存储规范的索引——每个规范都有一行摘要和确切的获取命令 (openspec show <spec-id> --type spec --store platform-reqs)。在 api-server 中工作的代理可以找到上游支付需求,引用它们,并在仓库自身的根目录中编写其底层设计——无需任何人手动粘贴上下文。
引用可以携带其克隆源,因此尚未拥有该存储的队友将获得完整的修复方案,而不是死胡同:
references:
- { id: platform-reqs, remote: "git@github.com:acme/platform-reqs.git" }当你希望计划和代码同时打开时,创建工作集。 这是个人且显式的:每个人选择他们在机器上实际使用的文件夹。这些本地检出路径的任何内容都不会提交到共享规划仓库中。
openspec workset create platform \
--member ~/openspec/platform-reqs \
--member ~/src/api-server \
--member ~/src/web-app两个你可以随时提出的问题
“我的设置是否健康?” —— openspec doctor 以只读方式检查当前根目录及其引用的存储,并为每个发现提供可粘贴的修复方案:
Doctor
Root
Location: /Users/you/src/api-server
OpenSpec root: ok
References
- platform-reqs: ok (/Users/you/openspec/platform-reqs)
- design-system: Referenced store 'design-system' is not registered on this machine.
Fix: git clone -- git@github.com:acme/design-system.git '/Users/you/openspec/design-system' && openspec store register '/Users/you/openspec/design-system' --id design-system“我正在处理什么?” —— openspec context 从 OpenSpec 声明中组装工作集:根目录及其引用的存储。
Working context for api-server (/Users/you/src/api-server)
OpenSpec root
api-server /Users/you/src/api-server
Referenced stores
platform-reqs /Users/you/openspec/platform-reqs
Fetch: openspec show <spec-id> --type spec --store platform-reqs两者都支持 --json 以供代理使用。openspec context --code-workspace <path> 还会写入一个包含整个集合的 VS Code 工作区文件——这是该命令执行的唯一写操作。
Workset:重新打开您一起工作的文件夹
与上述所有内容分开:大多数人每个会话都会一起打开相同的几个文件夹——规划仓库加上两三个代码仓库。workset 是对这些内容的个人命名视图,可通过您选择的工具中的一个命令重新打开。
workset "platform" openspec workset open platform
├── team-plans ~/openspec/team-plans │
├── api-server ~/src/api-server ▼
└── web-app ~/src/web-app all three open in your toolopenspec workset create platform \
--member ~/openspec/team-plans --member ~/src/api-server \
--tool code
openspec workset listplatform (opens in VS Code)
team-plans /Users/you/openspec/team-plans
api-server /Users/you/src/api-server然后,openspec workset open platform 会启动已保存的工具:编辑器(VS Code、Cursor)会用每个成员打开一个窗口并返回。第一个成员是主成员。随时用 --tool <id> 覆盖工具。
Workset 特意不共享状态。它们存在于您的机器上,永远不会被提交,也不对工作做任何声明——它们只记录您喜欢一起打开的内容。删除一个 workset 永远不会影响成员文件夹。新工具是配置而非代码:任何通过工作区文件或每文件夹附加标志启动的内容都可以添加到全局配置中的 openers 键下(openspec config edit)。
命令如何决定操作位置
每个常规命令都以相同方式解析其根目录,顺序如下:
1. --store <id> you said so explicitly → that store
2. nearest openspec/ a real planning root here → this repo
(walking up from cwd)
3. store: pointer config.yaml declares a store → that store
4. defaultStore global config sets a machine → that store
default
5. none of the above stores registered on this → error with a
machine? selection hint
no stores registered? → the current
directory
(classic behavior)Using OpenSpec root: 行(以及 --json 输出中的 root 块)告诉您当前属于哪种情况。
已知限制
- Beta 形态。 此页面上的所有内容可能在版本之间更改——名称、标志、文件格式、JSON 键。
- 每台机器每个 store id 仅支持一个 checkout。 在同一 id 下注册第二个 checkout 会失败,并提示先执行
store unregister。 - 永不同步——这是设计使然。 OpenSpec 从不克隆、拉取或推送。过时的 checkout 会显示过时的 spec,直到您自行拉取;引用实时从磁盘上的任何内容索引。
- 空的规划文件夹可以不存在。 新 store 可能还没有
openspec/changes/、openspec/specs/或openspec/changes/archive/在 Git 中。这在 beta 期间是可接受的;一旦正常命令为这些文件夹创建文件,它们就会出现。 - 指针仓库仍保持指针。 一个仅配置的仓库,其
openspec/config.yaml声明了store: <id>,被视为外部化规划,而不是要进行注册的 store checkout。如果您有意将该仓库转换为本地 store 根目录,请先移除store:行。 - 某些命令保持原位。
templates和已弃用的名词形式(openspec change show等)仅作用于当前目录——没有--store。schemas遵循标准的根目录选择优先级,并接受--store <id>,同时保持其成功的 JSON 数组形状不变。 - 每台机器的状态仅限该机器。 store 注册表和 workset 是本地设置。有关您的机器布局的任何内容都不会提交到共享规划中。
- workset 有两种启动方式。 无法通过工作区文件或每文件夹附加标志启动的工具不能作为 opener 添加。
- Agent JSON 存在已知的大小写拆分(store 系列键为 snake_case,工作流系列为 camelCase)。在 agent 合约 中有文档说明;统一该问题推迟到版本化发布中解决。
内容的存储位置
| 内容 | 位置 | 共享? |
|---|---|---|
| store 的规划 | <store>/openspec/ (specs, changes) | 是——提交并推送它 |
| store 的身份 | <store>/.openspec-store/store.yaml | 是——随 store 一起提交 |
| store 注册表 | <data dir>/openspec/stores/registry.yaml | 否——仅此机器 |
| Workset | <data dir>/openspec/worksets/ | 否——仅此机器 |
<data dir> 在 macOS 和 Linux 上是 ~/.local/share/openspec(或设置了 $XDG_DATA_HOME 时为 $XDG_DATA_HOME/openspec),在 Windows 上是 %LOCALAPPDATA%\openspec。
参考
此页面上每个命令的确切标志和 JSON 形状:CLI 参考(Stores、Doctor、工作上下文、个人 workset)以及 [agent 合约](../agent-contract.md)。