首页
/ Halo 的 OpenSpec 实践:OPSX:propose 一步生成变更提案、设计与任务工件

Halo 的 OpenSpec 实践:OPSX:propose 一步生成变更提案、设计与任务工件

2026-09-05 17:13:42作者:毕习沙Eudora

Halo 仓库内置了一套基于 OpenSpec 的 AI 辅助开发工作流,其中 /opsx:propose 命令(定义于 .claude/commands/opsx/propose.md)是整个流程的起点:它把"要做什么"一次性地落成三类工件——proposal.md(What & Why)、design.md(How)、tasks.md(实现步骤)。读完本文,你可以完整掌握该命令的输入约定、五步执行流程、工件生成的约束规则(Guardrails),以及 Halo 仓库中真实的变更目录结构与归档案例,从而在自己的 OpenSpec 项目里复现这套"先规划、后实现"的规范化变更管理方式。

命令定位与基本约定

OPSX: Propose 是一个 Slash Command(斜杠命令),文件以 YAML frontmatter 声明其元信息:

---
name: "OPSX: Propose"
description: Propose a new change - create it and generate all artifacts in one step
allowed-tools: Bash(openspec:*)
category: Workflow
tags: [workflow, artifacts, experimental]
---
  • allowed-tools: Bash(openspec:*):该命令仅依赖 openspec CLI 的 Bash 调用,不直接操作其他工具;
  • 核心能力一句话概括:创建一个 change(变更)并在同一步骤中生成其全部工件,待实现就绪后引导用户执行 /opsx:apply 进入实现阶段。

在 Halo 仓库中,同一逻辑还以 Skill 形式存在于 .claude/skills/openspec-propose/SKILL.md,其步骤与命令版几乎一致(命令版面向交互式会话,要求无输入时主动询问用户;Skill 版则假定用户请求中已包含变更名或描述),二者互为佐证,说明这是 Halo 团队对 "propose" 环节的统一规范。

Store 选择(多仓库规划场景)

文档开头给出了 Store selection 规则:如果用户指定了某个 store(一个注册在本机上的独立 OpenSpec 仓库),或工作发生在其中,则:

  1. 先运行 openspec store list --json 发现已注册的 store id;
  2. 在读写 specs 与 changes 的命令上附加 --store <id>,受影响的命令包括 new changestatusinstructionslistshowvalidatearchivedoctorcontext;其他命令不接受该参数。命令输出的提示(hints)中已自带该 flag,后续调用应沿用。
  3. 未指定 store 时,命令作用于最近的本机 openspec/ 根目录——在 Halo 中即仓库根目录下的 openspec/ 目录。

输入约定

/opsx:propose 后的参数有两种形态:

  • kebab-case 变更名,如 add-user-auth
  • 一段自然语言描述,描述用户想构建或修复什么。

若未提供输入,命令要求使用 AskUserQuestion 工具(开放式、无预设选项)询问 "What change do you want to work on? Describe what you want to build or fix.",并从用户描述中推导出 kebab-case 名称(例如 "add user authentication" → add-user-auth)。文档特别强调:在理解用户意图之前不得继续推进。

五步执行流程:从创建变更到 apply-ready

第 1 步(条件步骤):确认用户意图

如上所述,无输入时先询问。这是唯一允许"打断"的步骤,目的是保证后续工件生成有明确的变更边界。

第 2 步:创建变更目录

openspec new change "<name>"

该命令会在 CLI 依据 .openspec.yaml 解析出的 planning home(规划根目录)中创建一个 scaffold 好的变更。从仓库实际产物看,每个变更目录都带有一个 .openspec.yaml 记录 schema 与创建时间,例如归档变更 2026-08-03-refactor-editor-table/.openspec.yaml 的内容为:

schema: spec-driven
created: 2026-07-31

这印证了 openspec new change 落盘的就是"schema 声明 + 目录骨架"。

第 3 步:获取工件构建顺序

openspec status --change "<name>" --json

解析返回的 JSON,需要提取四组信息:

字段 含义
applyRequires 实现前必须完成的工件 ID 数组,例如 ["tasks"]
artifacts 全部工件列表,含各自状态(status)与依赖关系(dependencies)
planningHome / changeRoot / artifactPaths / actionContext 路径与作用域上下文——文档明确要求以这些字段为准,不要假设仓库内相对路径

最后一点体现了工作流的稳健性:工件实际写入位置由 CLI 解析(可能落在 store 或其他 planning home 中),执行者必须遵循 CLI 给出的 resolvedOutputPath,而不是硬编码 openspec/changes/<name>/...

第 4 步:按依赖顺序逐个生成工件,直到 apply-ready

文档要求用 TodoWrite 工具跟踪工件进度,并按"依赖优先"顺序循环处理。对每个状态为 ready(依赖已满足)的工件:

  1. 获取生成指令

    openspec instructions <artifact-id> --change "<name>" --json
    
  2. 解析 instructions JSON,其中各字段职责不同:

    • context:项目背景——对执行者是约束,不得写入产物文件
    • rules:该工件类型的专属规则——同样是约束,不得写入产物;
    • template:产物文件应遵循的结构骨架;
    • instruction:针对该工件类型的 schema 级写作指引;
    • resolvedOutputPath:工件应写入的解析路径(或模式);
    • dependencies:需要先读取的已完成工件。
  3. 读取已完成依赖工件获取上下文,然后以 template 为结构创建工件文件并写入 resolvedOutputPath

  4. 每完成一个,显示简短进度("Created <artifact-id>")。

循环终止条件:重新运行 openspec status --change "<name>" --json,检查 applyRequires 中每个工件 ID 在 artifacts 数组里是否都为 status: "done";全部 done 即停止。若某工件因上下文不清需要用户输入,则用 AskUserQuestion 澄清后继续。

spec-driven schema(Halo 采用的 schema)下,工件即文档开头所列的 proposal.md(What & Why)、design.md(How)、tasks.md(实现步骤),外加 specs 目录下的能力规格文件。

第 5 步:展示最终状态

openspec status --change "<name>"

完成后输出汇总:变更名与位置、已创建工件的简要描述、就绪状态("All artifacts created! Ready for implementation."),并提示"Run /opsx:apply to start implementing.",将流程衔接到实现阶段。

工件创作准则与 Guardrails

文档对"如何写工件"给出了明确边界,核心原则是约束与产物分离

  • 遵循 openspec instructions 返回的 instruction 字段,按 schema 定义的内容组织工件;
  • template 只作为输出文件的结构——填充其各节,而非原样照搬;
  • 在创建新工件前先读取其依赖工件;
  • contextrules 是写给执行者的约束,绝不能复制进工件——<context><rules><project_context> 块不应出现在输出文件中。

Guardrails(护栏)条款:

  • 必须创建实现所需的全部工件(以 schema 的 apply.requires 定义为准);
  • 创建新工件前必须先读取依赖工件;
  • 上下文严重不清时才询问用户,优先做出合理决策以保持推进节奏;
  • 若同名变更已存在,询问用户是继续还是新建;
  • 每写入一个工件后先验证文件确实存在,再进入下一个。

仓库佐证:Halo 的 OpenSpec 配置与真实变更案例

配置如何注入约束:openspec/config.yaml

约束的来源是 openspec/config.yaml。该文件声明 schema: spec-driven,并包含两大块会被 CLI 注入到 instructions 输出中的内容:

  • context(注入到所有 AI 提示):Halo 的技术栈与架构约定——Java 21 + Gradle (Groovy DSL) + Spring Boot 4.x / WebFlux/Reactor / R2DBC;前端 Vue 3 + TypeScript + Vite + pnpm workspaces + TailwindCSS;Monorepo 模块划分(apiapplicationplatform:applicationplatform:pluginui);PF4J 插件体系与扩展点;Conventional Commits 约定;后端 ./gradlew spotlessApply、前端 pnpm lint / pnpm typecheck / pnpm test:unit 检查。
  • rules(按工件类型的规则):
    • proposal:需评估对现有插件/主题 API 的兼容性影响;数据库 schema 变更必须包含迁移策略;安全相关变更须评估认证/授权影响;UI 变更须考虑 i18n;
    • tasks:后端变更须通过 ./gradlew spotlessCheck;前端变更须通过 pnpm lintpnpm typecheck;API 变更须更新 OpenAPI 文档并重新生成 api-client;新依赖须做许可证兼容性检查。

这正好对应了 propose 流程中"rules 是约束而非内容"的设计:生成 proposal.md 时,AI 会按上述四条规则审视提案(例如是否涉及数据库迁移),但这些规则文本本身不会出现在工件里。

真实归档变更:refactor-editor-table

仓库 openspec/changes/archive/ 下保存了完整的提案-实现-归档历史,可直接作为 propose 产物的范例。以 2026-08-03-refactor-editor-table/proposal.md 为例,其结构完全遵循 template:

  • Why:说明 Halo 表格扩展在多次 bug 修复中累积了重复的 Tiptap 内部实现、自定义 schema 节点、全局编辑器状态与 DOM 监听器,导致小修复风险高、列宽自适应与编辑器/控制台/主题间表格输出不一致等问题未解决;
  • What Changes:列出一系列变更项(基于上游 Tiptap table 扩展重建模型、引入自动/固定两种 layout 模式、定义规范化的可移植 HTML 契约、兼容解析历史表格包装而无需全库迁移、分离文档语义与 Vue 交互层、补齐测试覆盖、保持 ExtensionTable 导出兼容);
  • Capabilities:划分 New Capabilities(editor-table-modeleditor-table-interactionseditor-table-rendering)与 Modified Capabilities;
  • Impact:明确影响面——ui/packages/editor/src/extensions/table/ 及其菜单、翻译、样式与测试;getHTML() 输出变化、主题作者契约、公开导出兼容性、无需新运行时依赖、i18n 参与。

对照 config.yaml 的 proposal 规则可以看到该提案确实评估了插件/主题 API 兼容性("Keep ExtensionTable and its current package export compatible")、i18n("All new labels, tooltips, and accessible names participate in Halo's existing i18n system"),说明 rules 约束在真实工件中得到了落实。

tasks.md 则展示了 tasks 工件的形态:按 Baseline/兼容夹具、基于上游的表格模型、布局模式与规范 HTML、视图隔离与生命周期、命令与剪贴板行为、Vue 交互 UI、渲染与主题契约、端到端验证等分节,每节下是可勾选的任务项(- [x] / - [ ]),并内嵌了 config.yaml 要求的验证命令,如 pnpm -C ui typecheckpnpm -C ui lint。这个变更还同时归档了三份能力规格(specs/editor-table-model/specs/editor-table-interactions/specs/editor-table-rendering/ 下的 spec.md),体现了 spec-driven schema 中"提案声明能力、规格定义需求场景"的两层结构。

已合并进主规格库的能力可见 openspec/specs/menu-hierarchy/spec.md:以 ## Purpose + ### Requirement + #### Scenario(WHEN/THEN 句式)组织,描述菜单层级从 Menu.spec.menuItems 迁移到 MenuItem.spec.menuName / MenuItem.spec.parent 的需求与兼容场景——这是变更归档后同步到 openspec/specs/ 的最终形态,也是 propose 阶段"Modified Capabilities"所指向的基线。

工作流全景:propose 在整个 OPSX 链路中的位置

.claude/commands/opsx/ 目录下还有与 propose 配对的命令,共同构成完整生命周期:

命令 文件 职责
propose .claude/commands/opsx/propose.md 创建变更并生成全部工件(本文主题)
apply .claude/commands/opsx/apply.md 读取工件上下文,逐条实现 tasks 并回写勾选状态
explore .claude/commands/opsx/explore.md 探索已有变更与规格
sync .claude/commands/opsx/sync.md 同步规格
update .claude/commands/opsx/update.md 更新变更工件
archive .claude/commands/opsx/archive.md 归档已完成的变更

apply.md 可看到衔接细节:apply 通过 openspec status 读取 schemaName(如 spec-driven),通过 openspec instructions apply --change "<name>" --json 获得 contextFiles(spec-driven 下为 proposal、specs、design、tasks)与任务进度,然后逐任务实现并把 tasks 文件中的 - [ ] 改为 - [x],全部完成后可用 /opsx:archive 归档——对应仓库中 openspec/changes/archive/ 下带日期前缀的 20+ 个归档变更目录。

适用前提与小结

复现这套工作流的前提:本地已安装并可用 openspec CLI;工作目录中存在 openspec/ 根目录(含 config.yaml),或已注册 store。本文描述的流程以 Halo 当前仓库的实际命令文件与 openspec/ 目录结构为准,命令文件标注了 experimental 标签,意味着流程细节可能随 OpenSpec 版本演进。

回到核心价值:/opsx:propose 把"口头需求"转化为可审查、可追溯、依赖有序的三类工件,且所有路径、结构、约束都由 CLI 的 JSON 输出动态解析而非硬编码假设;Halo 仓库的归档变更则证明这套工件能够承载从"编辑器表格扩展重构"这样的大型任务到"菜单层级迁移"这样精确到 spec 字段的中型任务,是规范驱动(spec-driven)开发在 AI 协作场景下的一次落地实践。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384