关于 accelint-qrspi-archive
用于归档一个或多个 OpenSpec 变更,并在归档后维护能力间链接、规格索引和追加式变更日志。适合采用 OpenSpec 工作流且需要统一归档记录的项目。
主要功能
- 01调用 OpenSpec 单项或批量归档
- 02归档后建立能力间关联
- 03更新规格索引
- 04追加归档变更日志
- 05归档前检查元数据与规格
适用场景
- 01归档单个 OpenSpec 变更
- 02批量归档待处理变更
- 03维护规格间关联和索引
- 04检查归档所需的前置资料
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ gohypergiant/ agent-skills --skill accelint-qrspi-archive安装后在 Claude Code 中输入/accelint-qrspi-archive确认已加载。
附带文件CHANGELOG.mdREADME.md
详细介绍
Accelint QRSPI 归档
归档一个 OpenSpec 变更,并随后执行 OpenSpec 自身不会做的跨能力关联与索引记账。此技能会自行调用 openspec-archive-change 或 openspec-bulk-archive-change——它是归档一个变更的入口点,而不是在别人已经归档之后才做出反应的步骤——等待合并完全解决,然后通过共享的 related: frontmatter 列表关联该变更触及的每一对能力,维护一份包含所有 spec 的单一滚动索引并保持最新,并向一个仅追加的、记录所有已归档变更的变更日志追加内容。
交叉关联必须在合并解决之后发生,而不是之前,这正是为什么此技能把原生命令本身作为自己的第一个阶段来运行,而不是把“发生了合并”当作某种需要等待的外部事件。openspec/changes/<slug>/specs/ 中的 delta spec 仍然是暂定的——openspec-bulk-archive-change 可能会按时间顺序解决多个变更之间的冲突,之后某个能力的 spec 才会达到其最终形态。只有已合并、已归档的 spec 才值得索引;任何更早计算出的内容都可能是在关联仍可能在其下方发生变化的内容。
此技能做什么
自动化:在一次调用中,对一个或多个 OpenSpec 变更执行完整的归档操作——自行调用 openspec-archive-change 或 openspec-bulk-archive-change,然后立即跟进跨能力关联与索引维护。
范围:从“归档此变更”到更新后的索引的全部内容。此技能在归档和提取期间自行调用原生命令;它不会等待合并以其他方式先发生。
输出:通过 OpenSpec 自身的合并归档的变更,加上更新后的 related: frontmatter,以及每个被触及 spec 上重新生成的 ## Related Specs 部分,一份更新后的 openspec/specs/INDEX.md(针对本批次触及的能力进行补丁更新,或者在文件尚不存在时首次全项目范围地全新构建),以及 openspec/changes/archive/INDEX.md 中为每个已归档变更追加的一行。
不做:自行实现合并或冲突解决逻辑(那是 OpenSpec 自身的逻辑,此技能通过原生命令调用它,而不是重新实现)、删除任何 related: 条目、在变更的 Status 列初次写入后更改它、重排现有变更日志行,或者调用 OpenSpec CLI 来读取本地 spec 文件(普通文件读取就足够了——参见明确超出范围)。
前置条件
- 已安装并初始化 OpenSpec CLI,且有一个或多个变更已准备好归档。
- 支持子 Agent,仅用于按能力写入规范——这些总是无条件地作为子 Agent 运行,这是该处正常运行的硬性要求,而不是针对大批量操作的可选加速手段(如果不可用,请参阅错误处理了解降级回退方案)。归档与提取从不使用子 Agent,无论是否存在子 Agent 支持——原因请参阅归档与提取部分。
- 每个变更的
openspec/changes/<slug>/design.md都有 YAML frontmatter,其中包含specs_touched(一个非空的能力名称列表)和decisions(一个由{id, choice, rationale, alternatives}条目组成的列表)。 - 任何
specs_touched列表中提到的每个能力都已经有openspec/specs/<capability>/spec.md,且其正文中有## Purpose或### Purpose标题——此技能读取该标题,而不是将目的文本复制到 frontmatter 中,而重写正确行为取决于该标题确实存在(在预检检查任务 B 中验证)。
如果其中任何一项缺失,请报告缺口并引导用户在继续之前解决它——不要为缺失字段静默替换为猜测的默认值。这同样适用于规范写入的子 Agent 支持,以及被触及规范缺失的 ## Purpose 或 ### Purpose 标题。变更缺失 specs_touched/decisions frontmatter 的处理方式不同:预检任务 A 从变更自身的文件中推导出一个候选值,并在写入之前获得作者的明确确认,而不是直接停止——原因请参阅下面的任务 A,了解为什么这里实际上并不需要硬停止,以及为什么它仍然不是静默猜测。
工作流概览
┌────────────────────────────────────────────────────────────────────────┐
│ Stage Action Output │
├────────────────────────────────────────────────────────────────────────┤
│ 0 Preflight Verify frontmatter + Purpose Go / no-go │
│ headings before touching anything │
│ 1 Archive+Extract Run openspec-archive-change or Change │
│ openspec-bulk-archive-change yourself, records │
│ in this context (never a subagent); │
│ stay with any internal sync branch │
│ until archive's own steps finish, │
│ then read back specs_touched + decisions │
│ 2 Validate Confirm archive records are Checked │
│ structurally complete records │
│ 3 Link Combine new co-touch pairs across New │
│ this batch's changes (no file I/O) partners │
│ 4 Write specs SUBAGENT (one per capability, Updated │
│ always, never inline): merges new specs │
│ partners with existing related:, │
│ sorts, writes frontmatter + body │
│ 5 Specs index Patch specs/INDEX.md for the INDEX.md │
│ capabilities this batch touched │
│ (full rebuild only if the index is │
│ missing) │
│ 6 Change log Append one row per archived change INDEX.md │
│ to changes/archive/INDEX.md (append-only) │
│ 7 Report Summarize what changed Summary │
└────────────────────────────────────────────────────────────────────────┘
Critical: for openspec-bulk-archive-change, validation through reporting run exactly ONCE, after every
merge in the batch has resolved — never once per intermediate merge. Running
early would compute pairs against a specs_touched set that hasn't finished
accumulating cross-change conflicts, and would patch INDEX.md against a
half-finished batch.
Spec writing always delegates to subagents, regardless of batch size — one
capability or forty. This isn't a parallelization optimization that only
kicks in for large batches; it's how this skill keeps raw spec.md contents
out of the parent's context on every run, the same pattern
accelint-qrspi-propose and accelint-qrspi-apply use.
Archive and extraction is the mirror image: it never delegates to a subagent, regardless of
batch size or whether sub-agent support is even available. openspec-archive-change and
openspec-bulk-archive-change are themselves agent-driven, multi-step skills — not a
single deterministic CLI call — and a subagent handed instructions to run openspec-archive-change
has no reliable way to resume that skill's own remaining steps once it
branches internally into something like a separate sync skill, and no way
to surface an interactive prompt back to the user if one comes up. Both of
those are failure modes this skill hit in practice, not hypothetical ones —
see the Archive and Extract section for the full account.
实现步骤
按顺序执行这些步骤,除非发生错误,否则不要在它们之间停止:
预检检查
目标:在触碰任何规范或索引文件之前,确认归档操作的输入形式正确。任务 A 是一个狭窄的例外:一旦作者确认了推导出的 specs_touched/decisions 候选值,它就会将其写回 design.md——这是填写第 7 步预期已经存在的输入。
-
确定范围:单变更归档(openspec-archive-change 带变更名称)或批量归档(openspec-bulk-archive-change),跨越多个待处理变更。
-
验证任务 A — design.md frontmatter。 对于每个即将归档的变更,读取
openspec/changes/<slug>/design.md,并确认其 frontmatter 包含非空的specs_touched列表,以及一个decisions列表,其中每个条目至少有id和choice:--- change: add-live-sync specs_touched: [sync/protocol, ui/status-indicator] decisions: - id: D1 choice: polling with 5s interval rationale: no infra budget for a message broker this quarter alternatives: [websocket push, long polling] ---如果 frontmatter 存在且格式良好,则按原样继续——这是任何经过
accelint-qrspi-propose的变更的预期情况,因为specs_touched/decisions本就应该在设计时写入。如果变更持续在没有此 frontmatter 的情况下到达这里,这表明需要去修复accelint-qrspi-propose(或accelint-qrspi-apply),使其将这一块作为自身正常工作流的一部分写入——这样可以从源头堵住缺口,而不是一次又一次地依赖下面的恢复路径。如果某个变更的 frontmatter 缺失或格式错误,此技能仍然不会静默替换为猜测值——变更的作者必须明确做出这个决定,而不是此技能。但是,没有任何前进路径的硬停止并不是获得这种明确确认的唯一方式,而且大多数时候,缺失字段可以从变更作者已经写好的材料中恢复:
- 推导候选值。 对于
specs_touched,查看变更的proposal.md能力声明以及openspec/changes/<slug>/specs/下的增量规范目录。对于decisions,查看proposal.md中已有的任何 Decisions 部分,或design.md中已有的决策性文字。 - 先呈现以供确认——暂不写入。 将推导出的候选值展示给用户,请他们 (a) 按原样确认,(b) 先编辑,或 (c) 暂停,以便他们自行修复
design.md并在稍后重新调用此 skill。只有当用户选择 (a) 或 (b) 后,该候选值才会成为此 skill 写入design.mdfrontmatter 的值——此时它与格式良好的情况免费获得的显式作者确认相同,只是在 propose 时点之后一步才捕获。 - 只有在变更自身的文件中没有任何可推导内容时,才彻底停止,不提供候选值——例如增量规范目录为空,且
proposal.md中任何地方都没有能力声明。在这种情况下,与之前一样,准确报告是哪个变更、哪个字段缺失。
这是针对批量归档中每个变更逐一评估的——一个变更需要确认不会阻塞其他变更的 preflight。
- 推导候选值。 对于
-
验证任务 B——Purpose 标题约定。 对于本批次中所有
specs_touched列表里出现的每个能力,检查openspec/specs/<capability>/spec.md是否包含描述其用途的标题:可接受的标题(按此顺序检查,首个匹配即生效):
## Purpose或### Purpose## Overview或### Overview
将 Overview 和 Purpose 视为语义等价——两者都描述该能力做什么以及为什么存在,这正是索引更新和规范编写所需要的。
如果这些标题都不存在: 询问用户如何处理:
- (a) 暂时添加占位
## Purpose标题,文本为_Purpose not yet documented_ - (b) 暂停,以便用户先自行添加标题
- (c) 阅读规范内容,并根据规范描述生成
## Purpose标题
当规范有实质内容时,选项 (c) 通常最佳——Agent 可以根据已记录的内容综合出用途说明。选项 (b) 更适合那些是存根或需要领域专业知识才能准确描述的规范。选项 (a) 是当你需要立即解除阻塞、但之后还需回来修复时的最后手段。
注意: 此检查仅适用于已在
openspec/specs/<capability>/spec.md拥有 MAIN 规范的能力。全新能力(见第 4 步)尚无 MAIN 规范,因此跳过此检查——它们会在归档期间创建规范时获得其 Purpose 标题。 -
对于
specs_touched中尚无openspec/specs/<capability>/目录的任何能力——即此变更引入的全新能力——单独记录。第 19 步需要从零创建其spec.mdfrontmatter,而不是编辑现有文件,且第 20 步的 Purpose 列需要用户手动提供值,因为尚无可读取的内容。 -
在继续之前报告 preflight 摘要:范围内的变更、触及的能力,以及任何任务 A 或任务 B 的结果。如果任务 A 对某个变更以停止告终——用户选择暂停并自行修复
design.md,或完全无法推导出候选值——则不要对该变更继续执行第 6 步。用户确认的任务 A 候选值视为通过,与已经格式良好的 frontmatter 相同。如果任务 B 对某个能力失败,可以稍后解决——第 6-17 步不触及规范正文,因此仅当第 18 步即将到达该能力时才将其标记为阻塞。
归档与提取(内联运行——绝不使用 subagent)
- 让 OpenSpec 执行实际合并,然后读回第 17 步和第 23 步所需的数据——直接在此上下文中完成,不交给 subagent。
无论批处理规模大小,也无论是否支持子 Agent,此步骤都绝不会作为子 Agent 运行。这是对该技能
1.0.0行为的反转,是在实践中遇到两种具体失败模式后做出的:
- openspec-archive-change 和 openspec-bulk-archive-change 是由 Agent 驱动的技能,而不是一次确定性的 CLI 调用。 它们会读取项目状态,判断需要同步什么,并在需要同步时在内部交接给一个独立的同步技能,然后再返回完成归档工作流的其余部分(合并增量规格、将变更移入
openspec/changes/archive/)。一个被指示运行 openspec-archive-change 的子 Agent 无法可靠地区分「我完成了这次归档步骤让我去做的那个同步技能」和「我完成了我真正被要求做的事」——任务执行中途没有调用方可以回查。在实践中这种情况的表现正是如此:子 Agent 运行了同步步骤,认为自己的任务已完成,然后交回控制权,却从未执行到合并。在此上下文中直接运行归档,意味着发出指令的 Agent 正是观察它分支进入同步的那个 Agent,因此它能识别出这一分支的实质,并在同步完成后继续执行归档的剩余步骤——这与一个人亲自运行该命令时所具有的连续性相同。 - 子 Agent 无法向用户呈现交互式提示。 openspec-archive-change 和 openspec-bulk-archive-change 可能抛出的不只是常规的同步 y/n——尤其是 openspec-bulk-archive-change,在合并涉及重叠规格的变更之前可能会提示确认(见下文第 2 步)。遇到此类提示的子 Agent 会陷入僵局:它无法把问题交给用户并获得真实答复,而替用户猜测比不继续推进更糟。以内联方式运行归档意味着任何此类提示都会落在用户已经在其中的同一个对话里。
这确实要付出一些代价:openspec-archive-change 自身的内部工作——将增量规格与主规格进行比对、处理 bulk-archive 的跨变更排序——现在直接在此上下文中进行,而不是由一个隔离的子 Agent 吸收,因此进入上下文的内容比 1.0.0 设计所预期的更多。这是为了正确性而牺牲上下文经济性的可接受代价,并非疏忽;规格编写仍然像以前一样,在子 Agent 中隔离其自身的、通常更大的每个能力文件内容,因此这一代价仅限于归档步骤自身的范围。不要通过直接调用 openspec-archive-change/openspec bulk-archive 命令,而不是使用 openspec-archive-change/openspec-bulk-archive-change 技能来绕过这一点——OpenSpec 自身的增量规格比对和边界情况判断实际上就存在于该技能中(bulk-archive 的冲突解决无法用裸 CLI 标志复现),绕过它回到原始 CLI 调用会丢弃该技能存在的意义所在的那种混合 Agent 判断。
-
确定范围:单变更归档(带变更名称的 openspec-archive-change)还是跨越多个待处理变更的批量归档(openspec-bulk-archive-change)。
-
已知的交互式提示——始终同步。 openspec-archive-change 和 openspec-bulk-archive-change 往往会在运行中途暂停,询问是否同步。每次出现时都始终回答是——这是归档操作完成的常规部分,而不是需要用户输入的决策点。这是你始终要自行回答的唯一交互式提示。
-
运行相应的技能调用:
单变更:
Invoke the openspec-archive-change skill.
<change-name>
批量归档:
Invoke the openspec-bulk-archive-change skill.
-
如果该运行在内部发生分支 —— 最常见的情况是在中途移交给一个独立的 sync 技能 —— 这是此命令正常、预期的形态,并不表示出了什么问题,也不表示任务已经完成。请继续跟进:一旦分支完成,就接着执行 archive 自身剩余的步骤(将增量 spec 合并到主 spec 中,把变更移入
openspec/changes/archive/),而不是把分支的完成当作本节结束。 -
如果出现任何其他交互式提示 —— 最常见的是
openspec-bulk-archive-change在合并涉及重叠 spec 的变更前请求确认 —— 这是只有用户才能回答的真实问题,与第 8 步中例行的 sync 提示不同。请直接将其呈现给用户,并在继续之前等待其回答。 -
等到此操作中的每一次合并都完全解决。对于批量归档,这意味着批次中的所有变更,而不仅仅是第一个。
-
如果任何合并报告了未解决的冲突,请立即停止。不要尝试解决它,也不要对批次中的任何变更继续执行第 14 步 —— 部分提取比不提取更糟,因为否则无法区分停滞的批次与干净的批次。将冲突原样报告给用户并停止。不要继续进行验证 —— 从部分合并的批次中解析出任何内容都会将垃圾传播到该批次涉及的每一项能力中。
-
对于每一个成功归档的变更,从其新的归档路径读取其
design.mdfrontmatter(例如openspec/changes/archive/2026-03-02-add-live-sync/design.md)并构建一条记录:
{
change: "add-live-sync",
date: "2026-03-02", // from the archive folder's own
// YYYY-MM-DD prefix, NEVER from
// anything inside design.md
archivePath: "openspec/changes/archive/2026-03-02-add-live-sync/",
specsTouched: ["sync/protocol", "ui/status-indicator"],
decisions: [{ id: "D1", choice: "polling with 5s interval", ... }]
}
- 保留第 14 步中的记录列表,按变更分组,用于验证 —— 并明确提醒自己不存在任何未解决的冲突。
输出:每个已归档变更一条记录(change、date、archivePath、specsTouched、decisions),保存在此上下文中并直接传递给验证步骤。
验证提取的记录
目标:在交叉链接计算依赖这些归档记录之前,确认它们在结构上是健全的 —— 这是对已提取内容的健全性检查,而不是对源数据的重新验证(预检任务 A 已在归档运行前确认源 design.md frontmatter 格式良好)。
-
确认每条记录都有非空的
specsTouched,且至少有一条choice已填充的decisions条目。如果某条记录缺少其中任何一项,说明在归档期间构建它时出了问题(而不是原始数据的问题,原始数据已由任务 A 验证)—— 对该变更重新运行 archive,而不是带着不完整的记录继续。 -
保留记录按变更分组,与返回时完全一致。交叉链接计算在单个变更自身的
specsTouched内计算配对 —— 在同一次批量归档批次中归档的两个不相关变更,并不意味着它们的能力相互共同触及,即使它们是在同一时刻落地的。
输出:与归档时相同的记录集,经确认在结构上完整,可供交叉链接计算和 INDEX 追加使用。
计算交叉链接(全对并集)
目标:对每个变更,在其自身的 specs_touched 内计算对称的共同触及配对,并将此次归档操作中每个变更的这些配对合并为一组每项能力新贡献的伙伴。此步骤不触碰任何文件 —— 它只合并已验证记录中已有的 specsTouched 列表。将这些新伙伴与某个 spec 已有的 related: 条目合并(绝不丢弃其中任何一条)发生在 spec 写入期间,在已经打开该文件的 subagent 内部 —— 父级没有理由仅仅为了播种一个并集而去读取 spec frontmatter,而 spec 写入在读取自己文件的同时就能完成这件事。
18. 对于 specs_touched: [A, B, C] 的变更,贡献的配对是除自身外的所有 2-组合:(A,B)、(A,C)、(B,C)。共同触及没有方向 —— 配对 (A,B) 意味着 A 获得 B 作为相关伙伴,并且 B 在同一步骤中获得 A。
- 这是纯计算,不依赖文件 I/O,因此值得写成一个小型纯函数,而不是针对每项能力凭肉眼判断:
type ChangeLink = {
readonly change: string;
readonly specsTouched: readonly string[];
};
// All 2-combinations within one change's specs_touched. Pure, no self-pairs,
// no directionality — a co-touch relationship reads the same both ways.
const pairsFromChange = (
link: ChangeLink,
): ReadonlyArray<readonly [string, string]> =>
link.specsTouched.flatMap((a, i) =>
link.specsTouched.slice(i + 1).map((b) => [a, b] as const),
);
// Fold every change's pairs into one partner-set-per-capability map. This
// starts from an empty map every time — it combines pairs ACROSS the
// changes in this batch, not against a spec's on-disk related: list. That
// merge is deliberately left to spec writing, which is the step that opens the file.
const accumulateNewPartners = (
pairsByChange: ReadonlyArray<ReadonlyArray<readonly [string, string]>>,
): ReadonlyMap<string, ReadonlySet<string>> => {
const next = new Map<string, Set<string>>();
for (const pairs of pairsByChange) {
for (const [a, b] of pairs) {
next.set(a, (next.get(a) ?? new Set()).add(b));
next.set(b, (next.get(b) ?? new Set()).add(a));
}
}
return next;
};
-
将每个变更的配对折叠进同一个累积映射,从空开始 —— 一批触及重叠能力的三项变更的批量归档,应在 spec 写入还未触及任何文件之前,将三项变更的配对全部合并,这样每项能力的 subagent 只需以完整的新伙伴列表调用一次,而不是以部分数据调用三次。
-
将这个映射原样交给 spec 写入 —— 不排序,也不与任何 spec 现有的
related:列表合并。spec 写入 subagent 负责合并(与文件中已有内容取并集)以及最终的字母排序,因为它是实际读取这一切所依赖的文件的一方。
输出:一个 capability -> 新贡献的伙伴名称 映射,尚未与任何 spec 现有的 related: 列表合并,也尚未排序 —— 这两项都发生在 spec 写入期间。
写入 Spec Frontmatter 和正文(Subagent,始终执行)
目标:将新贡献的伙伴合并进每个被触及 spec 现有的 related: 列表,将其连同来源信息持久化到 frontmatter,并重新生成 ## Related Specs 部分以保持一致。
始终为每项被触及的能力生成一个 subagent 来执行此操作 —— 每次都要,而不是根据涉及多少项能力有条件地执行。一旦新伙伴映射固定,每项能力的编辑就完全独立(accelint-qrspi-apply 所依赖的、用于并行化切片的同一种独立性),并且在 subagent 内部执行读/合并/写,可使该 spec 的完整内容不进入父级上下文,无论这是一项能力的归档还是四十项能力的批量归档。将此事变成基于阈值的判断(“如果能力很多就并行化,否则就内联编辑”)意味着父级的上下文开销会无缘无故地因运行而异;无条件委派则使其保持平稳。这也意味着是这个 subagent、而不是父级,是唯一一个会读取 spec 当前 related: 值的地方 —— 这正是为什么与现有条目合并的逻辑属于这里,而不是交叉链接计算。
- 对于每项被触及的能力,使用此提示词生成一个 subagent(父级仅提供来自交叉链接计算的新贡献伙伴名称 —— 不是最终列表;subagent 才是将这些与文件中已有内容合并的一方):
Update openspec/specs/<capability>/spec.md only. Do not touch any other file.
Read this file's current related: value from its frontmatter (treat it as
empty if the file has no frontmatter yet — this may be a brand-new
capability). Union it with these newly contributed partners:
[<partner>, <partner>, ...]. Never drop an entry that was already there.
Sort the resulting full set alphabetically and write it in flow style on
one line — that's what keeps a no-op run byte-identical and a genuine
addition a clean one-line diff.
Also set:
last_touched_by: <change-name>
last_touched_on: <date>
as plain overwrites of whatever was there before, not an accumulated
history.
Do NOT add capability or purpose fields. The capability name is this
file's own path; purpose already lives in its ## Purpose or ### Purpose heading.
Then regenerate the ## Related Specs section in the body from the same
final, sorted related: list (replace any existing ## Related Specs
content entirely — this section is never hand-maintained).
For each partner in the sorted related: list, read its spec.md file to
extract the purpose heading text. Check for these headings in order:
- ## Purpose or ### Purpose
- ## Overview or ### Overview
Use whichever is found first (Overview and Purpose are semantically equivalent).
Format each line as:
- [<partner>](../<partner>/spec.md) - <Purpose or Overview heading text>
Example format (one entry per line, alphabetically sorted by partner name):
## Related Specs
- [auth-session-store](../auth-session-store/spec.md) - Secure session token storage and retrieval
- [sync-protocol](../sync-protocol/spec.md) - Defines the live-sync wire protocol for real-time updates
The format is: markdown link with partner name as link text, relative
path to its spec.md, then space-dash-space (not em-dash), then the
first sentence or paragraph from that partner's purpose heading (## Purpose, ### Purpose, ## Overview, or ### Overview).
If no ## Related Specs heading exists, insert one at the end of the file.
Report back ONLY: the file path, whether the write was a no-op
(byte-identical to what was already there) or an actual change, the
final related: list you wrote, and an index-ready purpose summary for
this capability derived from its current heading text (from ## Purpose,
### Purpose, ## Overview, or ### Overview — whichever exists). Use a
single concise sentence suitable for a table cell. Prefer the first
sentence if it is already concise; otherwise compress the heading text
to one sentence. You're not writing this back into the spec — just
reading and compressing it so the parent doesn't have to reopen the
file for index updates. Do not return the file's full contents.
-
如果该能力没有先前的
spec.md(根据预检第 4 步,是一个全新能力),subagent 从空的 frontmatter 块开始,而不是读取现有的 —— 除此之外提示词相同。 -
收集每个 subagent 的简短报告(文件、无操作 vs. 已更改、最终的
related:列表,以及可供索引用途摘要),用于索引更新和报告。索引更新直接复用此报告中的related:和 Purpose 值,而不是重新打开文件;最终报告只依据无操作/已更改状态。写入的其他任何内容都不需要进入父级上下文 —— 报告的无操作是预期信号,表明该能力实际上未受此批次影响,而不是需要重新读取文件来复核的事情。
输出:每个被触及能力的 spec.md 就地更新(保留现有 related: 条目,新条目取并集并排序),或确认未更改,只有一行简短状态加上其 related:/单句 Purpose 摘要值 —— 用于索引更新的小载荷 —— 进入父级上下文。
更新 openspec/specs/INDEX.md
目标:让 openspec/specs/INDEX.md 与刚刚写入的内容保持同步,代价是只改动实际发生变化的行 — 不重新扫描每个能力的 spec.md,甚至不完整读取 specs/INDEX.md 本身。编写 spec 的子 Agent 在完成自身写入时,已经读取了每个被触及能力当前的 related:、last_touched_by 以及 ## Purpose/### Purpose 标题,并将这些值回传 — 索引更新直接复用这些数据,而不是再付出一次全量扫描 openspec/specs/ 的代价。唯有在 specs/INDEX.md 尚不存在时,这一步才会查看本批次所触及能力之外的能力,或读取超过 specs/INDEX.md 中某一目标行的内容 — 此时没有可修补的现有状态,一次性全量构建才是正确做法,而非例行操作。
- 常见情况 —
specs/INDEX.md已存在。 不要为了做这件事而把整个文件读入上下文 — 只定位并编辑实际发生变化的行:
- 已存在的能力:grep 查找第一个单元格与能力名称完全匹配的那一行(锚定在前导
|与下一个|之间,而非宽松的子串匹配 —sync/protocol不得匹配sync/protocol-v2)。将该行替换为根据该能力的 spec 编写报告构建的行(Purpose、related:、last_touched_by)。文件中的其他内容一概不读取、不触碰。 - 全新能力(依据预检步骤 4):grep 查找能力名称单元格,找到按字母顺序排在新增项之后的第一个现有行,并将新行插入到它紧前方(若排在最后则追加到末尾),插入位置前后均不留空行。所有数据行必须保持连续,行与行之间不得有空行。这仍然只改动一个新行 — 定位插入点无需加载行内容,只需能力名称列。
- 每个未被触及能力的每一行都不会被打开、匹配或重写 — 不仅仅是在全量重建的结果中“保持相同”,而是字面意义上从未被此操作触碰。
- 构建每一行时不要做跨行对齐填充 — 单元格之间用单空格分隔(
| sync/protocol | Defines the live-sync wire proto | ui/status-indicator | add-live-sync |),而不是按每列最宽的值对齐。Purpose单元格应是一句适合放在表格单元格中的简洁句子,复用编写 spec 的子 Agent 返回的、可直接用于索引的摘要,而不是 spec 标题下的整段文字:
| Capability | Purpose | Related | Last touched by |
| --- | --- | --- | --- |
| sync/protocol | Defines the live-sync wire proto | ui/status-indicator | add-live-sync |
| ui/status-indicator | Renders connection state in UI | sync/protocol | add-live-sync |
Markdown 表格无论是否填充都能正确渲染 — 只有表头分隔线的短横线数量有意义,数据行之间的空白一致性并无影响。若保持跨行对齐,则每一行的填充都取决于其他所有行的内容长度,一旦任何单行文本长度发生变化,就迫使整张表重写 — 而这正是本设计要避免的代价。被修补的行与其未被触碰的相邻行在原始 diff 中会显得略微参差;这正是换取 diff 实际只有一行的代价。
- 引导情况 —
specs/INDEX.md尚不存在。 没有可修补的先前状态,而只修补被触及的行会永久遗漏所有未被触及的能力,直到它们碰巧被未来的某次变更触及。列出openspec/specs/下的每个目录,并针对每个目录直接读取其## Purpose或### Purpose标题、related:frontmatter 以及last_touched_by— 这是一次全量扫描。这每个项目最多运行一次,即此 skill 首次针对该项目归档任何内容时;此后的每次归档都改走步骤 25。 - 仅引导场景:根据步骤 27 中收集的完整扫描结果写入
openspec/specs/INDEX.md,使用步骤 26 的行格式,所有数据行连续排列,行与行之间不得有空行。如果文件在表格上方已有项目专属标题或前言,请予以保留。常见场景(步骤 25)已经直接写入了其编辑内容——它没有单独的写入步骤。
输出:openspec/specs/INDEX.md,本批次涉及的每个能力对应一行被更改或插入(不添加空行),其余所有行保持不变;或者,如果该文件尚不存在,则根据项目范围内所有能力一次性构建,所有数据行连续排列,行与行之间不得有空行。
被打补丁的 specs/INDEX.md 仍可能因本技能无法控制的原因而与实际不符——例如某个 spec.md 在归档工作流之外被手工编辑,某个能力目录被直接重命名或删除。本技能不会每次运行都重新推导整个索引来捕捉这类问题。目前也没有其他机制能做到这一点:accelint-archive-synthesis 的两项检查(decision-drift、structural-coupling)都把 specs/INDEX.md 当作事实来源来读取,而不是拿它所汇总的 spec.md 文件来审计它。要捕捉这类偏差,就需要给 accelint-archive-synthesis 增加一项索引核对检查——这对于一个本就用于执行周期性、全语料库范围检查的技能来说是顺理成章的,但这项检查目前尚不存在。在它出现之前,这是一个已被接受的、范围有限的缺口,而不是已被覆盖的缺口。
追加到 openspec/changes/archive/INDEX.md
目标:为每个已归档变更记录一行持久记录,插入到表格现有数据行的末尾——绝不能盲目插入到文件末尾。与 specs INDEX 的更新不同,这个文件是历史记录,而历史是仅追加的——绝不重新生成,绝不重新排序。
- 对于验证中得到的每个变更,构造一行:
| Change | Date | Decision | Specs touched | Status |
| --- | --- | --- | --- | --- |
| add-live-sync | 2026-03-02 | polling with 5s interval | sync/protocol, ui/status-indicator | current |
Date是归档文件夹的YYYY-MM-DD前缀——与验证中使用的来源相同,绝不从design.md中读取任何内容。Decision是从decisions[].choice派生的紧凑归档索引摘要;当来源冗长时,不是逐字复制。将每个选择改写为现有归档行所使用的简洁片段风格。不要将理由、替代方案、示例、注意事项或说明性文字复制到索引中。如果某个变更记录了多个决策,请先规范化每个选择,然后用分号将它们连接起来(例如polling with 5s interval; client-side dedup on reconnect),而不是只挑选其中一个。Status写作current,并且在此初始写入之后,本技能绝不再改动该列,无论对这一行还是任何其他行。诸如current→superseded之类的状态迁移属于accelint-archive-synthesis,它拥有判断某个决策是否实际已被取代所需的跨变更上下文——本技能一次只能看到一个归档操作,无法安全地做出这一判断。
- 定位插入点——即表格数据行的末尾,行与行之间不得有空行。 找到表头行(
| Change | Date | ... |)、其分隔行(| --- | --- | ... |),以及紧随其后的连续数据行块。将新行插入到这些数据行的最后一行之后,且新行之前不得有空行。所有数据行必须连续排列,行与行之间不得有空行——条目之间的空行会破坏表格结构,绝不能写入。这同时适用于changes/archive/INDEX.md和specs/INDEX.md。 - 绝不要在任一 INDEX.md 表格之后写入元数据或摘要内容。
openspec/changes/archive/INDEX.md和openspec/specs/INDEX.md都可能在表格之前包含 Markdown 标题/前言(例如 "# Capability Specifications Index" 或 "# Archive Index" 后跟一段简要描述),如果存在则必须保留。前言之后是表格(表头行、分隔行和数据行),文件在最后一个数据行之后立即结束。不要在表格之后写入以下这些常见但被禁止的尾部元素:
- "Total Changes: N" 或类似的行数统计
- "Generated: YYYY-MM-DD" 或类似的时间戳
- "Status Legend:" 或类似的说明性文字
- 表格之后的分隔线(
---) - 任何其他散文、元数据或摘要内容
如果任一文件中存在先前运行遗留的此类尾部内容,请将其删除。标准的 INDEX.md 格式为:可选前言,然后是表格,然后是文件结尾。最后一个数据行之后不得出现任何内容。
-
这是本 skill 输出中唯一一个在首次归档之前可能尚不存在的文件——如果缺失,请创建它,包含表头行和分隔行(且不得有尾部内容)。
-
不要对任何现有行重新排序、重新格式化或以其他方式改动。这里的仅追加纪律正是使该文件成为可靠审计追踪的关键;重新排列过去的行将使任何人都无法使用 git blame 查看某项决策相对于周围其他决策是何时记录的。
Output:openspec/changes/archive/INDEX.md,每个已归档变更对应一个新行,紧接在最后一个数据行之后插入,其前不得有空行,所有先前行保持不变,所有数据行连续且行间无空行,表格之后不得有元数据或摘要内容——文件在最后一个数据行之后立即结束。
Report
- 总结发生了哪些变更:
✅ Archive complete: add-live-sync
Specs updated:
- sync/protocol (+ui/status-indicator)
- ui/status-indicator (+sync/protocol)
openspec/specs/INDEX.md updated (2 rows patched)
openspec/changes/archive/INDEX.md: 1 row appended
Next steps:
- Review the diff on touched specs before committing
- related: entries are additive only here — if one looks wrong,
pruning is accelint-archive-synthesis's job, not this skill's
- 如果有任何能力被留下了占位用途,或因预检 Task B 从未得到解决而被完全跳过,请在此明确指出,而不要让它消失在 diff 中。
Output:本 skill 所触碰的每个文件的人类可读摘要。
- 检查综合触发阈值 —— 派生一个 subagent,判断归档语料是否已增长或老化到足以 warrant 运行
accelint-archive-synthesis,使用双触发机制,当累积 50 个变更或自上次综合运行以来已过 30 个工作日时触发,以先到者为准。
此检查在每次归档操作(单变更和批量)之后运行,以确保综合建议以正确的节奏浮现。双触发方式应对两种不同的综合需求:高速项目快速归档大量变更,需要在决策漂移积累过深之前进行定期 linting;而低速项目则需要基于时间的提醒,以确保即使变更量很低,归档语料也能被定期审查。
使用以下提示词派生一个 subagent:
Check whether accelint-archive-synthesis should run based on dual-trigger thresholds.
Current date: <YYYY-MM-DD from system context>
Thresholds (hard-coded):
- Change threshold: 50 archived changes since last synthesis run
- Time threshold: 30 working days since last synthesis run
Trigger condition: (change_delta >= 50) OR (time_delta >= 30 working days)
Steps:
1. Read openspec/changes/archive/SYNTHESIS-LOG.md if it exists.
- Parse the last line under "## Run History" section
- Extract: checkpoint date (YYYY-MM-DD) and row count (after "checked through row")
- If file doesn't exist or section is empty, this is first-run: no baseline
2. Count current archive size:
- Use: grep -c "^|" openspec/changes/archive/INDEX.md
- Subtract 2 (header and separator rows) = current archived change count
3. Calculate deltas:
- Change delta = current row count - checkpoint row count
- Time delta (working days):
* Calculate calendar days between checkpoint date and current date
* Subtract weekend days (Saturdays and Sundays) in that period
* Result = working days elapsed
4. Apply trigger logic:
- trigger_exceeded = (change_delta >= 50) OR (time_delta >= 30)
Return ONLY this structured response (no explanation):
{
"trigger_exceeded": true/false,
"first_run": true/false,
"change_delta": N,
"time_delta": X,
"last_checkpoint_date": "YYYY-MM-DD" or null,
"last_checkpoint_row": N or 0,
"current_row": N
}
subagent 返回后,使用其响应构造相应的输出:
当 trigger_exceeded = true 且 first_run = false 时:
⚠️ SYNTHESIS CHECKPOINT THRESHOLD EXCEEDED
You SHOULD run accelint-archive-synthesis now:
- Changes since last synthesis: <change_delta> (threshold: 50)
- Working days since last synthesis: <time_delta> (threshold: 30)
- Last checkpoint: <last_checkpoint_date> at row <last_checkpoint_row>
- Current state: row <current_row>
High-velocity projects need periodic linting before decision drift accumulates
too deeply. Lower-velocity projects need time-based nudges to ensure the
archive corpus gets reviewed periodically even when change volume is low.
Invoke the accelint-archive-synthesis skill to run the synthesis check.
当 trigger_exceeded = false 时:
✓ Synthesis check: <change_delta> changes since last run (<last_checkpoint_date>), <time_delta> working days elapsed
Thresholds: 50 changes OR 30 working days, whichever first
当 first_run = true 时:
⚠️ SYNTHESIS CHECKPOINT NOTICE
This project has <current_row> archived changes but no synthesis checkpoint exists yet
(openspec/changes/archive/SYNTHESIS-LOG.md not found).
You SHOULD run accelint-archive-synthesis to establish the first baseline.
The synthesis skill will check for decision drift, index reconciliation issues,
and structural coupling across the full archive corpus.
Invoke the accelint-archive-synthesis skill to run the initial synthesis check.
RFC 2119 的 SHOULD 措辞(而非被动的 "Consider" 散文)可确保 Agent 可靠地将其识别为可操作的指令,而非信息性评论。subagent 处理所有计算的复杂性,仅返回输出格式化所需的最少数据。
关键原则
一份快速参考摘要——以下每一条的完整说明都出现在它实际被应用(实现步骤)或被强制约束(绝不要这样做)的地方;这里只是索引。
- 交叉链接发生在归档阶段,而非提案阶段。 增量 spec 在
/openspec-bulk-archive-change按顺序解决冲突之前都是临时的;只有合并并归档后的 spec 才值得建立索引。 specs_touched/decisionsfrontmatter 属于提案阶段,而非归档阶段。accelint-qrspi-propose才是它本应被写入的地方,作为变更自身设计工作的一部分。任务 A 的推导并确认路径是为那些没有经过该流程的变更而存在的——这是一条仍需要作者确认的兜底方案,而不是本技能获取该数据的主要方式。- 本技能只做新增。 删除某个
related:条目或更改现有Status值都是accelint-archive-synthesis的职责——两者都需要跨变更的判断以及本技能不具备的人工确认。 - 每次写入都是幂等的。 Flow 风格排序的
related:以及对specs/INDEX.md的行级修补意味着,针对未变化的输入重新运行会得到逐字节相同的结果,因此在部分失败后重试绝不会重复条目或行。 specs/INDEX.md按本次运行所涉及的能力逐行修补(仅在首次引导时整体构建);changes/archive/INDEX.md为仅追加。 一个描述当前状态,另一个描述历史——把其中任何一个当作另一个来处理,要么会破坏历史,要么会让索引发生漂移。对整个语料库、对照磁盘上的每一个spec.md来校验specs/INDEX.md,并非本技能要做的事,目前也不是accelint-archive-synthesis要做的事——它现有的检查是把索引当作事实依据来读取,而不是审计它。这是一个值得在那里某天填补的缺口,而不是本技能每次归档热路径的任务。- spec 编写始终委托给子 Agent,从不附带条件;归档与提取则从不委托,无条件如此。 对 spec 编写的无条件委托让父级的上下文成本保持恒定,无论批次大小如何,这与
accelint-qrspi-propose和accelint-qrspi-apply对每一次涉及文件的操作所使用的准则相同。归档则出于相反的原因内联运行:openspec-archive-change//openspec-bulk-archive-change由 Agent 驱动,可能在运行中途分支或提示,而只有用户实际所处的上下文才能跟进该分支或回答该提示。
明确不在范围内
- 读取本地规格时不依赖 OpenSpec CLI。
openspec/specs/<capability>/spec.md是已知路径下的普通文件;直接读取文件即可;如此直接的事情无需通过 shell 调用 CLI。 - 不以 shell 调用
openspec archive/openspec bulk-archive来替代openspec-archive-change/openspec-bulk-archive-change技能。 裸 CLI 命令并不是等效的捷径——OpenSpec 自身的 delta-spec 比较和批量归档冲突解决实际上都发生在该技能中。归档始终走技能,绝不走原始 CLI,即便内联运行它(见 Archive and Extract 部分)已经放弃了旧 subagent 设计所具备的部分上下文隔离;用这种隔离去换取一个跳过真正判断的 CLI 调用,是一笔更糟的交易,而不是更好的。 - 没有逐边关系元数据。
related:条目不携带自身的理由或时间戳——该上下文已存在于原始变更的design.md及其在changes/archive/INDEX.md中的行里,必要时 grep 即可找到。 - 不自动修剪
related:。 只做增量添加。移除是accelint-archive-synthesis的职责,且必须经人工确认。 - 关系中无方向性。 共同触碰本质上是对称的,因此始终按所有配对计算,绝不按依赖方向计算——“A 依赖于 B”是另一种边,本技能完全不建模。
- 不在
specs/INDEX.md中做全语料库漂移检测。 索引更新只会修补本批次变更在specs_touched中声明的能力的行。如果某个能力的spec.md在本技能之外被编辑,导致其INDEX.md行因此过时,本技能的热路径不会发现——而且目前accelint-archive-synthesis也不会:它的决策漂移和结构耦合检查都把specs/INDEX.md当作基准事实来读取,而不是对照spec.md去验证它。这是一个真实且被接受的缺口,不是本技能应当吸收进自身每次归档成本中的工作。 - 两个 INDEX.md 表格之后均无元数据或摘要内容。
openspec/changes/archive/INDEX.md和openspec/specs/INDEX.md都可以在表格之前有前言(若存在则保留),然后是表格(表头、分隔行和数据行),然后是文件结尾。表格之后不允许有任何尾随内容。常见的禁止尾随元素包括“Total Changes:”“Generated:”“Status Legend:”以及水平分隔线。如果先前运行遗留了此类内容,必须删除——两个文件的规范格式都是可选前言、然后表格、然后 EOF。
错误处理
design.md frontmatter 缺失或格式错误(任务 A):从该变更自身的 proposal.md 和 delta spec 目录中推导出一个候选的 specs_touched/decisions,将其呈现给用户以获取明确确认,并在该变更运行 archive 之前,把确认后的值写回 design.md。仅当用户选择暂停并自行修复,或该变更自身的文件中完全没有任何内容支持候选值时,才在该变更的 archive 之前停止,并指明相关字段。
缺少 ## Purpose 或 ### Purpose 标题(任务 B):不要阻止 archive、校验或交叉链接计算(它们不涉及 spec 正文),但在 spec 编写进行到该能力之前停止,并请用户先添加占位内容或修复该 spec。
某个变更只涉及一个能力:specs_touched 只有单个条目会产生零个配对——这是预期行为,不是错误。仍要为该能力运行 spec 编写(写入 last_touched_by/last_touched_on,重新生成 ## Related Specs,即使其列表未变),并在 changelog 步骤中仍将其行添加到 changes/archive/INDEX.md。
全新的能力,没有先前的 spec.md:在预检检查(步骤 4)和 spec 编写(步骤 19 子步骤 2)中已明确处理——subagent 从一个空的 frontmatter 块开始,而不是把文件缺失视为错误。
原生命令报告未解决的冲突:archive 步骤立即停止,并将冲突原样报告给用户,不再继续执行提取(archive 步骤 8);不要继续执行校验。
openspec-archive-change 或 openspec-bulk-archive-change 在运行中途内部分支(最常见的是分入一个单独的 sync 技能):这对该命令来说是正常、预期的形态,不是失败——参见 archive 章节步骤 4-5。继续执行,并在分支完成后恢复 archive 自身剩余的步骤;如果该分支(或运行期间的其他任何环节)抛出一个超出常规 sync 是/否之外的问题,应将其呈现给用户并等待其回答,而不是自行猜测。
spec 编写没有可用的 subagent 支持(例如不支持 sub-agent 的环境):回退为直接在当前上下文中执行 spec 编写步骤,而不是每个能力使用一个 subagent。明确警告用户:因此本次运行会将完整的 spec.md 内容保留在上下文中——这是仅针对 spec 编写的降级回退方案,并非那里预期的默认做法,正常运行时始终应优先使用 subagent 进行 spec 编写。这与 archive 步骤无关,按设计它本来就在当前上下文中无条件运行,无论是否有 sub-agent 可用——参见 Archive and Extract 章节。
specs/INDEX.md 或 changes/archive/INDEX.md 尚不存在:在项目首次 archive 时这是预期情况。全新创建 changes/archive/INDEX.md,仅包含标题行和分隔行——不得有任何形式的结尾内容(步骤 32);specs/INDEX.md 在索引更新引导情形(步骤 27)中根据索引中的每个能力全新构建,此后每次 archive 都逐行修补。
任一 INDEX.md 文件在表格之后含有不允许的内容(总行数、Generated: 日期、状态图例、水平分隔线或类似内容):从 changes/archive/INDEX.md 和 specs/INDEX.md 两者中删除所有此类结尾内容——这两个文件的规范格式均为可选前言-然后-表格-然后-EOF,紧接最后一行数据之后结束,其后不再有空白行或元数据。参见步骤 30-31。
切勿这样做
绝不要在未发生错误的情况下在工作流步骤之间停下来等待用户确认 — 这是一个从预检到最终报告的连续工作流。归档成功完成后,立即继续执行校验,然后交叉链接,依此类推直到最终报告。唯一合理的停止点是:(1) 需要用户输入才能解决的错误,或 (2) 预检失败。不要把 openspec-archive-change 的完成视为停止并等待的信号 — 它是继续执行校验的信号。
绝不要在批量归档期间针对每个中间合并分别运行一次校验及后续报告 — 应在整个批次处理完毕后,恰好运行一次。提前运行会基于尚未累积完冲突的 specs_touched 集合计算配对,并针对半完成批次的不完整行修补 INDEX.md。
绝不要删减 related: 条目 — 本 skill 只做并集。删除看似过时的条目是 accelint-archive-synthesis 的职责,而且即便在那里也需要人工确认。
绝不要在未获得作者明确确认的情况下,将推导出的 specs_touched 或 decisions 候选写入变更的 design.md — 从 proposal.md 和增量 spec 推导候选(任务 A 的恢复路径)只是为了解除阻塞的便利手段,并不能替代作者自己对变更实际触及了哪些能力的判断。
绝不要在修补 specs/INDEX.md 中的某一行时,重新填充或重新对齐现有行 — 单元格填充是按行进行的,而不是对齐到表中最宽的值。为了保持列在视觉上对齐而改动每一行的空白,会彻底违背定向行编辑的初衷,把一行 diff 变回整文件重写。
绝不要更改 changes/archive/INDEX.md 中现有的 Status 值 — 在创建时写入一次 current,此后绝不再触碰该行的这一列。状态转换属于 accelint-archive-synthesis。
绝不要将完整的决策说明文字复制到 openspec/changes/archive/INDEX.md — 该文件是紧凑的变更日志,不是 design.md 的第二份副本。写入前,应将每条决策精简为简短的归档行片段。
绝不要从 design.md 读取 Date — 始终使用归档文件夹自身的 YYYY-MM-DD 前缀。变更在归档前可能处于评审状态数周,因此当本 skill 运行时,design.md 中关于日期的任何内容都已过时。
绝不要将 capability 或 purpose 重复写入 spec 的 frontmatter — 两者已分别作为文件自身的路径及其 ## Purpose 或 ### Purpose 标题存在。重述其中任何一项都会制造第二个事实来源,最终将与第一个产生分歧。
绝不要将完整的 Purpose/Overview 段落粘贴到 openspec/specs/INDEX.md — 应使用适合表格单元格的一句简洁描述,而不是从 spec 中复制的完整段落。
绝不要手动维护 ## Related Specs — 始终基于刚刚写入的 related: frontmatter 整体重新生成。将其视为可独立编辑的内容,会导致 frontmatter 所述与正文所示之间产生偏差。
绝不要重排或重写 changes/archive/INDEX.md 中的现有行 — 只追加。该文件作为审计追踪的价值完全取决于过去的行保持写入时的原样。
绝不要通过写入文件末尾来向任一 INDEX.md 追加新行 — 应找到表格现有数据行的末尾,并在那里插入新行,且新行前不留空行。这适用于 changes/archive/INDEX.md 和 specs/INDEX.md。
绝不要在任一 INDEX.md 文件的数据行之间添加空行 — 所有数据行必须连续,行间不得有空行分隔。条目之间的空行会破坏表格结构。这适用于 changes/archive/INDEX.md 和 specs/INDEX.md。
绝不要在任一 INDEX.md 表格之后写入元数据或摘要内容 —— openspec/changes/archive/INDEX.md 和 openspec/specs/INDEX.md 都可以在表格之前有一段前言(若存在则保留),然后是表格(表头、分隔行和数据行),随后即文件结束。绝不要在表格之后写入 "Total Changes:"、"Generated:"、"Status Legend:"、水平分隔线(---)或任何其他尾部内容。如果之前的运行留下了此类内容,请将其删除。每个文件必须在其最后一行数据行之后立即结束。
绝不要打乱排序,也不要对 related: 使用块式 YAML —— 使用流式风格、单行、按字母顺序排序。这正是让无操作运行保持字节级一致、并让真正的新增内容显示为干净、可审查的 diff 的原因。
绝不要在 openspec-archive-change 或 openspec-bulk-archive-change 期间拒绝、跳过或质疑同步提示词 —— 始终回答 yes。它出现的频率高于不出现的情况,而且它是归档操作完成过程中的例行环节,而不是需要父级或用户输入的决策点。
绝不要在归档和提取时派生子 Agent,即使子 Agent 支持可用 —— 始终在当前上下文中直接调用 openspec-archive-change 或 openspec-bulk-archive-change。被赋予该指令的子 Agent 无法可靠地在命令内部发生分支(例如分支到单独的同步技能)后继续执行该命令自身的剩余步骤,也无法将交互式提示词反馈给用户 —— 这两点在实践中都曾导致该技能在归档中途停止。完整说明见“归档与提取”部分。
当子 Agent 可用时,绝不要在当前上下文中直接运行规范编写 —— 始终委派,无论批次有多小。这不是为了并行化大批量任务;而是为了在每一次运行时都将原始 spec.md 内容排除在编排上下文之外,这与 accelint-qrspi-propose 和 accelint-qrspi-apply 无条件采用的纪律相同。
绝不要将 openspec-archive-change/openspec-bulk-archive-change 中的运行中途分支(最常见的是其自身的同步技能)视为归档步骤的结束 —— 在分支完成后,继续执行该命令的剩余步骤。将分支本身视为整个任务,正是当初促使以内联方式运行归档的失败模式。
示例用法
场景 1:单变更归档
User: Archive the add-live-sync change with cross-linking
Skill: Running preflight checks...
✓ design.md frontmatter valid (specs_touched: sync/protocol, ui/status-indicator)
✓ Both capabilities have a ## Purpose/### Purpose heading
Running openspec-archive-change add-live-sync directly (archive never uses a subagent)...
Sync now? (y/n) → yes
✓ Synced delta specs, resuming openspec-archive-change's remaining steps...
✓ openspec-archive-change add-live-sync merged, archived to
openspec/changes/archive/2026-03-02-add-live-sync/
✓ Extracted: specs_touched, decisions from design.md frontmatter
Computing new co-touch pairs...
- sync/protocol ↔ ui/status-indicator (new pair)
[spawns subagent: sync/protocol] [spawns subagent: ui/status-indicator]
✓ sync/protocol/spec.md: related: [ui/status-indicator] (merged, was empty)
✓ ui/status-indicator/spec.md: related: [sync/protocol] (merged, was empty)
Updating openspec/specs/INDEX.md... (2 rows patched, 12 rows untouched)
Appending to openspec/changes/archive/INDEX.md... (1 row)
✅ Archive complete: add-live-sync
Next steps:
- Review the diff on the two updated specs before committing
- related: entries are additive only — pruning is accelint-archive-synthesis's job
场景 2:批量归档触及已关联的能力
User: Run bulk-archive on the pending changes
Skill: Running preflight checks across 3 pending changes...
✓ All design.md frontmatter valid
⚠ auth/session-store is missing a ## Purpose or ### Purpose heading
How would you like to handle auth/session-store?
(a) Add a placeholder purpose now and continue
(b) Pause so you can add the heading yourself first
User: (b)
Skill: Paused. Add a ## Purpose or ### Purpose heading to openspec/specs/auth/session-store/spec.md,
then re-invoke this skill to continue.
场景 3:缺少 design.md frontmatter,从变更自身的文件中恢复
User: Archive the enhance-mock-consistency-rule change
Skill: Running preflight checks...
⚠ design.md has no specs_touched or decisions frontmatter
Checking proposal.md and openspec/changes/enhance-mock-consistency-rule/specs/
for a derivable candidate...
Derived from this change's own files:
specs_touched: [testing/mock-consistency]
decisions:
- id: D1
choice: enforce consistency at lint time, not runtime
rationale: catches drift before merge instead of in CI
alternatives: [runtime assertion, manual review checklist]
(a) Confirm this as written
(b) Edit it first
(c) Pause so I can fix design.md myself
User: (a)
Skill: Confirmed. Writing this into design.md's frontmatter, then proceeding...
Running openspec-archive-change enhance-mock-consistency-rule directly...
✓ openspec-archive-change enhance-mock-consistency-rule merged, archived to
openspec/changes/archive/2026-07-07-enhance-mock-consistency-rule/
...
场景 4:批量归档引发非例行提示词(反馈给用户,而非由技能回答)
User: Run bulk-archive on the pending changes
Skill: Running preflight checks across 2 pending changes...
✓ All design.md frontmatter valid
Running openspec-bulk-archive-change directly (archive never uses a subagent)...
Sync now? (y/n) → yes
⚠ add-dark-mode and update-footer both touch specs/ui/ — archive both,
merging in chronological order? (y/n)
This isn't the routine sync prompt, so I'm not answering it myself —
add-dark-mode and update-footer both touch specs/ui/. Archive both,
merging in chronological order (add-dark-mode, then update-footer)?
User: Yes
Skill: ✓ Archived add-dark-mode, then update-footer, specs/ui/ merged in that order
...
这正是以内联方式运行归档所真正解锁的场景:被赋予 openspec-bulk-archive-change 的子 Agent 无法将该排序问题转达给用户并得到真实答复,因此它要么停滞,要么只能猜测。在当前上下文中运行归档意味着该问题能到达真正能够回答它的人那里。