Halo 的 OpenSpec 实践:OPSX:propose 一步生成变更提案、设计与任务工件
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:*):该命令仅依赖openspecCLI 的 Bash 调用,不直接操作其他工具;- 核心能力一句话概括:创建一个 change(变更)并在同一步骤中生成其全部工件,待实现就绪后引导用户执行
/opsx:apply进入实现阶段。
在 Halo 仓库中,同一逻辑还以 Skill 形式存在于 .claude/skills/openspec-propose/SKILL.md,其步骤与命令版几乎一致(命令版面向交互式会话,要求无输入时主动询问用户;Skill 版则假定用户请求中已包含变更名或描述),二者互为佐证,说明这是 Halo 团队对 "propose" 环节的统一规范。
Store 选择(多仓库规划场景)
文档开头给出了 Store selection 规则:如果用户指定了某个 store(一个注册在本机上的独立 OpenSpec 仓库),或工作发生在其中,则:
- 先运行
openspec store list --json发现已注册的 store id; - 在读写 specs 与 changes 的命令上附加
--store <id>,受影响的命令包括new change、status、instructions、list、show、validate、archive、doctor、context;其他命令不接受该参数。命令输出的提示(hints)中已自带该 flag,后续调用应沿用。 - 未指定 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(依赖已满足)的工件:
-
获取生成指令:
openspec instructions <artifact-id> --change "<name>" --json -
解析 instructions JSON,其中各字段职责不同:
context:项目背景——对执行者是约束,不得写入产物文件;rules:该工件类型的专属规则——同样是约束,不得写入产物;template:产物文件应遵循的结构骨架;instruction:针对该工件类型的 schema 级写作指引;resolvedOutputPath:工件应写入的解析路径(或模式);dependencies:需要先读取的已完成工件。
-
读取已完成依赖工件获取上下文,然后以
template为结构创建工件文件并写入resolvedOutputPath。 -
每完成一个,显示简短进度("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只作为输出文件的结构——填充其各节,而非原样照搬;- 在创建新工件前先读取其依赖工件;
context与rules是写给执行者的约束,绝不能复制进工件——<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 模块划分(api、application、platform:application、platform:plugin、ui);PF4J 插件体系与扩展点;Conventional Commits 约定;后端./gradlew spotlessApply、前端pnpm lint/pnpm typecheck/pnpm test:unit检查。rules(按工件类型的规则):proposal:需评估对现有插件/主题 API 的兼容性影响;数据库 schema 变更必须包含迁移策略;安全相关变更须评估认证/授权影响;UI 变更须考虑 i18n;tasks:后端变更须通过./gradlew spotlessCheck;前端变更须通过pnpm lint与pnpm 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-model、editor-table-interactions、editor-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 typecheck 与 pnpm -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 协作场景下的一次落地实践。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00