自动化与脚本
批量归档多个已完成的 OpenSpec…
当需要一次性归档多个并行完成的 OpenSpec 变更时,按代码实现证据解决规格冲突并批量归档。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
批量归档多个已完成的 OpenSpec 特定
解决思路
- 选择 store:若用户指定 store 或工作位于某个 store 中,运行 `openspec store list --json` 获取已注册的 store id,然后在读写 specs 和 changes 的命令(`new change`、`status`、`instructions`、`list`、`show`、`validate`、`archive`、`doctor`、`context`、`schemas`、`view`)上追加 `--store <id>`;一旦选定,该 flag 在整个工作流中保持粘性,后续命令也要带上。其他命令不接受该 flag。未指定 store 时命令作用于最近的本地 `openspec/` 根。
- 项目检查:在第一个写入操作(`new change`、`archive`、`sync specs` 或编写 artifact 文件)之前,运行 `openspec list --json`(选定 store 时带 `--store <id>`)并读取 `root`。有 root 对象表示项目已配置;`"root": null` 表示未配置(此处没有 `openspec/` 目录,写入命令会作为副作用创建它),该命令也会以非零退出,这是答案而非 CLI 故障,应读取 JSON 而不是重试或绕过。
- 处理 `root: null` 的两种分支:若为自动选择(用户未提及 OpenSpec、未指定此技能、未运行其斜杠命令),停止使用 OpenSpec 并按普通请求回答,不要要求用户配置也不要提及 OpenSpec 配置;若为显式 OpenSpec 请求,停止写入并询问用户如何继续:配置项目(`openspec init`)、指向已有 store(`--store <id>`)或本次不使用 OpenSpec,等待回答。两种分支都不得作为副作用创建 root:未经用户要求不运行 `openspec init`,不手工创建 `openspec/` 文件,也不让命令创建它。
- 处理 store 声明错误:当 `status` 错误消息以 `Declared in` 或 `Invalid store declaration in` 开头并指向本项目的 `openspec/config.yaml`(或 `config.yml`)时,说明项目通过其声明的 store 使用 OpenSpec,但本机无法解析(store 未注册或 `store:` 行格式错误)。不要当作未初始化而跳过后续分支:在写入前停止,向用户展示该错误的 `message` 和 `fix`。
- `<capability-path>` 是相对于 `specs/` 的 spec 目录(例如 `user-auth` 或 `identity/user-auth`)。解析主 spec 时保留每个 delta spec 的完整路径。
- 步骤 1 获取活动变更:运行 `openspec list --json` 获取所有活动变更;若无活动变更,告知用户并停止。
- 步骤 2 提示选择变更:让用户多选变更,展示每个变更及其 schema,包含“All changes”选项,允许任意数量选择(1+ 可行,2+ 是典型场景)。重要:不要自动选择,始终让用户选择。
- 步骤 2 加载归档输入:在批量校验前,从该 root 中选一个已选变更,运行 `openspec instructions archive --change "<selected-change>" --json`(带相同的 selected-root flag)。此查询是建议性且可选的,只提供额外提示输入,绝不能阻塞批量。若失败或返回无效 JSON(例如旧版 CLI 不支持此命令),继续批量且不带 context 和 operation guidance,不报错也不停止。
- 步骤 2 处理归档输入响应:有效响应可省略 `context` 和 `operationGuidance`。将 `context` 视为批量中必需的提示级输入,阅读并考虑,应用相关项目事实、约定和约束。将 `operationGuidance` 视为可选附加建议,阅读并考虑每条,遵循适用且与内置批量工作流兼容的条目。将这两个字段与冲突分析、用户显式选择、解析路径、CLI 检查和命令契约分开。若 context 与这些控制输入冲突,报告冲突并保留控制值。若 guidance 不适用或与控制输入冲突,不遵循并解释原因。不要从任一字段推断被跳过的提示、替换路径或 flag,也不要将其文本逐字复制到 specs、changes 或摘要中。
- 步骤 3 批量校验:对每个已选变更收集以下信息。
- 步骤 3a 收集 artifact 状态:运行 `openspec status --change "<name>" --json`,解析 `schemaName`、`artifacts`、`planningHome`、`changeRoot`、`artifactPaths` 和 `actionContext`,记录哪些 artifact 为 `done` 与其他状态。
- 步骤 3b 收集任务完成情况:从 status JSON 读取 `artifactPaths.tasks.existingOutputPaths`。完成意味着复选框仅含 `x`/`X`(忽略空格,`- [ x]` 也算完成);其他任何标记均为未完成(`- [ ]`、`- []` 以及 `- [~]`、`- [-]` 等不熟悉的标记)。若无 tasks 文件,记为 "No tasks"。
- 步骤 3c 收集 delta specs:检查 status JSON 中的 `artifactPaths.specs.existingOutputPaths`,列出存在哪些 capability specs,对每个提取需求名(匹配 `### Requirement: <name>` 的行)。将此列表视为唯一的 delta-spec 来源。若 `specs` 条目缺失或列表为空,对该变更不执行 spec 同步或 specs 指令查询,不要从无关 artifact 推断 delta。对每个变更独立评估,包括某些 schema 没有 `specs` artifact 的混合 schema 批量。
- 步骤 3d 计算归档目标:为每个变更计算一次目标名并记录为该变更的 `<target-name>`。若变更名已以 `YYYY-MM-DD-` 前缀开头则原样使用,否则前置当前日期为 `YYYY-MM-DD-<name>`(与 `openspec archive` 规则相同)。检查 `<planningHome.changesDir>/archive/<target-name>` 是否已存在。若存在,或另一个已选变更解析到相同目标名,将所有这些变更标记为 `Blocked` 并附 `Archive directory already exists`。被阻塞的变更绝不同步或移动:在步骤 6 表格中显示为 `Blocked`,将其排除在冲突解决之外(仅用其他变更解决其冲突),并在步骤 8d 记录为 Failed。在任何主 spec 写入前检查此处,与 `openspec archive` 一致:同步后才发现冲突会导致主 spec 被重写但归档从未发生。
- 步骤 4 检测 spec 冲突:构建以 `<capability-path>`(相对于 `specs/` 的精确路径)为键的映射,例如 `identity/user-auth -> [change-a, change-b]` 为冲突(2+ 变更),`billing/user-auth -> [change-c]` 为 OK(不同完整路径)。当 2+ 已选变更对完全相同的 `<capability-path>` 有 delta specs 时存在冲突。
- 步骤 5 以 agent 方式解决冲突:对每个冲突执行以下操作。
- 步骤 5a 读取每个冲突变更的 delta specs,理解各自声称添加/修改的内容。
- 步骤 5b 在代码库中搜索实现证据:查找实现各 delta spec 需求的代码,检查相关文件、函数或测试。
- 步骤 5c 确定解决方案:若只有一个变更实际实现,则同步该变更的 specs;若两者都实现,按时间顺序应用(较旧先,较新覆盖);若都未实现,跳过 spec 同步并警告用户。
- 步骤 5d 记录每个冲突的解决方案:对每个 delta spec 按变更和 `<capability-path>` 记录包含或排除决定;记录要应用哪些包含的 delta specs 及顺序;记录因实现缺失而排除同步的 delta specs;记录理由(代码库中发现的内容)。
- 步骤 6 展示合并状态表:显示汇总所有变更的表格,列包括 Change、Artifacts、Tasks、Specs、Conflicts、Status。对冲突显示解决方案(例如 `identity/user-auth spec: Will apply add-oauth then add-jwt (both implemented, chronological order)`)。对未完成变更显示警告(例如 `add-verify-skill: 1 incomplete artifact, 3 incomplete tasks`)。
- 步骤 7 确认批量操作:向用户询问单个确认问题,如 "Archive N changes?",选项基于状态,可能包括 "Archive all N changes"、"Archive only N ready changes (skip incomplete)"、"Cancel"。若有未完成变更,明确说明它们将带警告归档。按意图而非确切标签路由回答:"Cancel" 则停止,不归档,报告未归档任何内容并跳过剩余步骤;归档全部选项则继续所有非 `Blocked` 的已选变更;仅就绪选项则仅继续步骤 6 表格标记为 `Ready` 或 `Ready*` 的变更,其余在步骤 8d 记录为 Skipped,但 `Blocked` 变更保持 Failed 并附 `Archive directory already exists`;若 `Ready*` 变更的冲突伙伴被跳过,仅用正在归档的变更重新推导该冲突的解决方案;其他任何回答则重新询问而不是归档。
- 步骤 7 预取 specs 规则快照:在步骤 8 写入第一个主 spec 或移动任何变更之前,为已确认批量获取所有必需的 specs 规则快照。对每个将同步具体 `artifactPaths.specs.existingOutputPaths` 的变更,运行 `openspec instructions specs --change "<name>" --json` 恰好一次,带相同的 selected-root flag。在第一次写入或移动前获取所有快照。若任何查询以非零退出或返回无效的 artifact-instruction JSON,识别受影响的变更,报告错误,并在任何主 spec 写入或变更移动前停止整个批量。不要将查询失败视为省略规则。有效响应无 `rules` 即为无规则情况。
- 步骤 8 执行归档:处理前,将步骤 5 记录的决定(经步骤 7 重新推导后)带入两个按 delta 的集合:`includedDeltas`(所有来自已确认变更的非冲突 delta specs 加上选择同步的冲突 delta)和 `excludedDeltas`(因实现缺失而排除同步的已确认变更的冲突 delta)。单个变更可同时有包含和排除的 delta specs。保持按 delta 的决定,不要合并为按变更的同步标志。按确定顺序处理变更(遵循冲突解决)。
- 步骤 8a 同步包含的 delta specs:仅对在 `includedDeltas` 中有条目的变更,内联运行 `openspec-sync-specs` 工作流(agent 驱动的智能合并),仅传入包含的 delta 路径,并明确指示忽略该变更的 `excludedDeltas`。等待其完成。对冲突按解决顺序应用。将该变更已获取的 specs 规则快照传入内联同步;内联同步必须复用它而不再次获取指令。仅将 artifact 规则应用于该变更产生的主 specs。它们不改变冲突解决、归档行为或 CLI 契约,其文本不复制到输出文件。不要委托给后台任务——步骤 8c 会在同步仍在读取 `changeRoot` 时将其移走。若变更没有包含的 delta specs,不为其运行同步工作流。
- 步骤 8b 在移动 changeRoot 前验证包含的 delta specs:仅对 `includedDeltas` 中的 delta specs 重新运行与主 spec 在 `<planningHome.root>/openspec/specs/<capability-path>/spec.md` 的比较(使用步骤 3 status JSON 中 store 感知的 `planningHome.root`,而非硬编码仓库路径)。验证主 specs 已更新:ADDED 需求存在;MODIFIED 需求带有 delta 中命名的场景和描述变更,且其他场景完好;REMOVED 需求已消失——若此次同步退役了某 capability(移除其最后一个需求,使 `## Requirements` 为空),其主 spec 应被删除而非留空;同步有意保留并报告的 spec 也算匹配;RENAMED 需求以新名称存在且旧名称不存在。不要验证 `excludedDeltas` 中的 delta specs,它们被有意保持未同步。若同步失败或任何 capability 不匹配验证,报告差异并失败/跳过移动该变更的 `changeRoot`——不归档该变更,`changeRoot` 保持完整。
- 步骤 8c 执行归档:目标名使用步骤 3d 为该变更记录的 `<target-name>`,不变。绝不要在此重新计算:跨午夜的批量会在步骤 3 检查一个日期而在移动时使用另一个。再次检查目标是否已存在:即使步骤 3 已检查,也要在移动前立即再检查,因为目标可能在批量中途出现。若存在:将该变更记录为 Failed 并附 `Archive directory already exists`,保持 `changeRoot` 原位,报告步骤 8a 已为其同步的任何主 specs,并继续剩余变更。若不存在:将 `changeRoot` 移动到归档目录,运行 `mkdir -p "<planningHome.changesDir>/archive"` 和 `mv "<changeRoot>" "<planningHome.changesDir>/archive/<target-name>"`。确认移动未嵌套:`mv` 即使目标在检查后出现也会以 0 退出,将变更移入其中。若 `<planningHome.changesDir>/archive/<target-name>/<change-directory-name>`(`changeRoot` 的最后路径段)现在存在,将该目录移回 `changeRoot` 并将此变更记录为 Failed 并附 `Archive directory already exists`。绝不报告为已归档。
- 步骤 8d 跟踪每个变更的结果:Success(成功归档)、Failed(归档或 spec 验证期间出错,记录错误)、Skipped(用户选择不归档,如适用)、Sync skipped(对 `excludedDeltas` 中的每个 delta,报告 `sync skipped` 并附变更、`<capability-path>` 和记录的原因;这与跳过归档不同)。
- 步骤 9 显示摘要:展示最终结果,包括已归档变更及其目标路径、跳过的变更及原因、spec 同步摘要(同步的 delta specs 数量、跳过的 delta spec 及原因、解决的冲突及详情)。若有失败,列出 Failed 变更及错误。
- 冲突解决示例 1(仅一个实现):冲突为 `<planningHome.root>/openspec/specs/auth/spec.md` 被 [add-oauth, add-jwt] 触及。检查 add-oauth:delta 添加 "OAuth Provider Integration" 需求,搜索代码库发现 src/auth/oauth.ts 实现 OAuth 流程。检查 add-jwt:delta 添加 "JWT Token Handling" 需求,搜索代码库未发现 JWT 实现。解决方案:仅 add-oauth 已实现,仅同步 add-oauth specs。
- 冲突解决示例 2(两者都实现):冲突为 `<planningHome.root>/openspec/specs/api/spec.md` 被 [add-rest-api, add-graphql] 触及。检查 add-rest-api(创建于 2026-01-10):delta 添加 "REST Endpoints" 需求,搜索代码库发现 src/api/rest.ts。检查 add-graphql(创建于 2026-01-15):delta 添加 "GraphQL Schema" 需求,搜索代码库发现 src/api/graphql.ts。解决方案:两者都实现,先应用 add-rest-api specs,再应用 add-graphql specs(时间顺序,较新优先)。
- 成功输出:`## Bulk Archive Complete`,列出已归档 N 个变更及目标路径,spec 同步摘要(N 个 delta specs 同步到主 specs,无冲突或 M 个冲突已解决)。
- 部分成功输出:`## Bulk Archive Complete (partial)`,列出已归档 N 个、跳过 M 个(原因)、失败 K 个(错误)。
- 无变更输出:`## No Changes to Archive`,未找到活动变更,创建新变更以开始。
- Guardrails:允许任意数量变更(1+ 可行,2+ 典型);始终提示选择,绝不自动选择;尽早检测 spec 冲突并通过检查代码库解决;当两个变更都实现时按时间顺序应用 specs;仅当实现缺失时跳过 spec 同步(警告用户);确认前显示清晰的每变更状态;整个批量使用单次确认;用户取消确认后绝不归档——取消的批量不归档任何内容;跟踪并报告所有结果(成功/跳过/失败);移动到归档时保留 .openspec.yaml;归档目录目标使用当前日期,在步骤 3d 计算一次并在移动时复用:`YYYY-MM-DD-<name>`;已以 `YYYY-MM-DD-` 前缀开头的名称原样使用(绝不叠加第二个日期);若归档目标存在,该变更失败但继续其他变更;在步骤 3 中、第一次主 spec 写入前检查每个归档目标;目标存在的变更绝不同步或移动;若请求同步,对每个有包含 delta specs 的变更内联运行 `openspec-sync-specs` 工作流(agent 驱动);将按 delta 的 `includedDeltas` 和 `excludedDeltas` 决定带入执行,仅同步和验证包含的 delta;将每个排除的 delta 报告为 `sync skipped`,不将归档本身视为跳过;绝不在 spec 同步仍在进行时归档变更——内联运行同步并在移动 `changeRoot` 前验证主 specs 在 `<planningHome.root>/openspec/specs/<capability-path>/spec.md`;在 spec 检查或移动前为每个已选 root 获取一次归档输入;在批量第一次主 spec 写入或移动前获取所有必需的 specs 规则快照;归档输入查询失败绝不阻塞批量,继续且无 context 或 guidance;specs 指令查询失败则原子地停止整个批量;没有具体 `artifactPaths.specs.existingOutputPaths` 的变更继续且不进行 spec 同步;在批量中应用相关运行时 context 并报告冲突;operation guidance 保持建议性,考虑每条并解释被拒绝的建议;将运行时输入、冲突分析、CLI 派生值和 artifact 规则分开;artifact 规则仅约束写入的 specs;绝不将运行时输入或 artifact 规则文本逐字复制到输出文件。
适用边界 · 注意事项
- 项目未使用 OpenSpec 且用户未显式请求 OpenSpec 时(自动选择分支),不应使用此工作流,应按普通请求回答。
- 项目通过本机无法解析的 store 声明使用 OpenSpec 时(`Declared in` 或 `Invalid store declaration in` 错误),应在写入前停止并展示错误,而非当作未初始化处理。
- 用户取消确认后不应归档任何变更。