| name | openspec-workflow-propose |
| description | 分析项目文档与代码的差距,生成 propose 建议列表,用户选择后调用 openspec-propose 创建 artifacts。可多次调用生成多个 propose。 |
| license | MIT |
| compatibility | Requires openspec CLI v1.3.1+. Reads docs/adr/, docs/architecture/, docs/developer_guide/. |
| metadata | {"author":"sisyphus","version":"1.3","generatedBy":"1.3.1","replaces-step":"step1-manual"} |
OpenSpec 工作流 — Propose
分析项目文档与代码之间的对齐情况,生成 propose 建议,用户选择后串行执行 openspec-propose 创建 artifacts。
proposal-suggestions.md 是持久化文件(随 git 版本控制),每次执行时更新:新增扫描发现的建议,移除已创建的 propose。
工作流位置
本技能:扫描文档/代码 → 合并现有建议 → 用户选择 → 串行创建 propose → 更新 proposal-suggestions.md
↓
openspec-workflow-plan: COMMIT GATE → 创建 worktree → 生成 Prometheus 计划
流程
Phase 0:加载现有建议列表
读取已有的 proposal-suggestions.md(如果存在),并移除已创建为 change 的条目:
使用 Python 读取 YAML 结构并过滤(比 bash 解析 YAML 更可靠):
if [ -f "proposal-suggestions.md" ]; then
echo "📂 加载已有的 proposal-suggestions.md"
python3 -c "
import yaml, os, sys
try:
with open('proposal-suggestions.md') as f:
# 读取纯 YAML 列表(格式:以 '- name:' 开头行列表)
content = f.read()
# 用行级解析:按 '- name:' 分割成条目块
# 每个条目块从 '- name:' 开始到下一个 '- name:' 或文件结束
lines = content.split('\n')
entries = []
current_entry = []
for line in lines:
if line.strip().startswith('- name:'):
if current_entry:
entries.append('\n'.join(current_entry))
current_entry = [line]
elif current_entry:
current_entry.append(line)
if current_entry:
entries.append('\n'.join(current_entry))
# 过滤:移除 name 对应的 change 目录已存在的条目
kept = []
removed = []
for entry in entries:
name = None
for line in entry.split('\n'):
if line.strip().startswith('- name:'):
name = line.split(':', 1)[1].strip().strip('\"').strip(\"'\")
break
if name and os.path.isdir(f'openspec/changes/{name}/'):
removed.append(name)
else:
kept.append(entry)
# 写回过滤后的内容
with open('proposal-suggestions.md', 'w') as f:
f.write('\n'.join(kept))
if kept:
f.write('\n')
if removed:
print(f' 已从建议列表移除: {', '.join(removed)}')
print(f' 剩余 {len(kept)} 个建议')
except Exception as e:
print(f'⚠️ proposal-suggestions.md 解析失败: {e}')
print(' 保留原文件,继续执行扫描阶段')
" || echo "⚠️ Python 执行失败,跳过加载"
fi
首次执行(文件不存在时):跳转到 Phase 1。
Phase 1:扫描项目文档与代码
扫描以下资料,收集新的差距信息。新发现的建议会与 Phase 0 加载的现有建议合并(按 name 去重)。
1a. 扫描 ADR 文件
ls docs/adr/ADR-*.md
逐个读取,对每个 ADR 提取:
| 信息 | 来源 |
|---|
| ADR 编号和标题 | 文件头 # ADR-NNN: |
| 状态 | **状态**: 行(已采纳/已拒绝/待定) |
| 决策日期 | **日期**: 行 |
| 未实现项 | 文档正文中的待修复、暂不修复、未来参考等标记 |
| 具体待办 | 文件中的任务列表、TODO 标记 |
1b. 扫描架构文档
ls docs/architecture/*-gap-analysis.md
ls docs/architecture/*-architecture.md
ls docs/architecture/PHASE*-ARCHITECTURE.md
ls docs/developer_guide/tech-reports/
ls docs/developer_guide/patterns/
提取:
- 差距分析表中的 ❌ 缺失项和 ⚠️ 部分完成项
- 计划但未实现的功能
- 设计评审中提出的待办项
- 明确标注了工作量的改进项
1c. 扫描代码标记
grep -rn "TODO\|FIXME\|HACK\|WORKAROUND" include/ src/ \
--include="*.h" --include="*.hpp" --include="*.cpp" --include="*.cu" \
| grep -v "archive/" > /tmp/todo_raw.txt
head -30 /tmp/todo_raw.txt
记录每个标记的文件位置、上下文、紧急程度(从注释推断)。只取前 30 条最关键的。
1d. 扫描测试覆盖缺口
for subdir in $(ls -d include/*/ 2>/dev/null | xargs -n1 basename); do
ls include/$subdir/ 2>/dev/null | sed 's/\..*$//' | sort > /tmp/headers_$subdir.txt
done
cat /tmp/headers_*.txt | sort -u > /tmp/all_headers.txt
ls tests/ | sed 's/\..*$//' | sort > /tmp/all_tests.txt
comm -23 /tmp/all_headers.txt /tmp/all_tests.txt
Phase 2:合并并写入建议列表
将 Phase 1 新发现的建议与 Phase 0 加载的现有建议合并:
合并规则:
- 按 name(kebab-case)去重,重复时保留旧的(用户可能已经评估过)
- 新增的建议追加到末尾
建议条目格式(含结构化需求描述):
每条建议包含以下字段。其中 description 字段使用 /opsx:propose 格式,这是后续传递给 openspec-propose 的完整需求描述:
- name: "add-stream-pipe-ops"
priority: "P0"
source: "ADR-022 §已采纳 §未实现"
status: "待创建"
description: |
- ADR-022 §3.2: Stream 管道操作符设计决策(已采纳,代码未实现)
- ADR-019 §4.1: Verilog 代码生成完整性要求(影响 codegen 适配)
- **In Scope**:
- 实现 Stream 管道操作符 m2sPipe/s2mPipe/halfPipe
- 修改文件:include/chlib/stream_operators.h
- 配套单元测试
- **Out Scope**:
- 不修改现有 FIFO/Arbiter/Fork 实现
- 不涉及跨时钟域适配
- GIVEN 一个 ch_stream<T> 实例, WHEN 调用 .m2sPipe(), THEN 返回带一级流水寄存器的新 Stream
- GIVEN 两个 Stream 通过 s2mPipe 连接, WHEN 反压, THEN 寄存器缓存一拍数据
- MUST 保持与 SpinalHDL m2sPipe/s2mPipe/halfPipe 语义一致
- MUST NOT 引入新的模板特化
- SHOULD 覆盖 pipeline 延迟场景测试
- 3 个操作符编译通过
- 4 个 Catch2 测试覆盖正常/反压/复位场景
- 现有 stream 测试全部通过
effort: "2-3天"
优先级归类:
| 类别 | 来源 | 默认优先级 |
|---|
| 🔴 ADR 未实现 | ADR 中 "已采纳,暂不修复" 且有明确待办 | P0 |
| 🟡 架构差距 | 差距分析表中的 ❌/⚠️ 项 | P1 |
| 🔵 计划功能 | PHASE 文档中的未实现计划 | P1-P2 |
| 🟢 代码标记 | TODO/FIXME 注释 | P2 |
| ⚪ 测试缺口 | 有头文件无测试 | P2 |
写入文件:
Phase 3:与用户交互确认
展示建议列表,让用户选择。使用 Question 工具提供选项。
展示格式:
## 📋 建议变更清单
已扫描 <N> 个 ADR,<M> 个架构文档,发现 <X> 个可 propose 的变更:
### 🔴 P0(ADR 未实现)
1. fix-ns-pollution — 修复 8 文件命名空间污染(ADR-033,高风险)
2. add-stream-pipe-ops — 实现 Stream 管道操作符(ADR-022,2-3天)
### 🟡 P1(架构差距)
3. add-cdc-support — 跨时钟域支持(架构差距分析,3-5天)
### 🟢 P2(代码标记/测试缺口)
4. refactor-sim-eval — 重构仿真评估顺序(context.cpp:152 TODO)
---
选择要创建的 propose(可多选):
构建 Question 工具的选项列表:
{
header: "选择 propose",
question: "请选择要创建的 propose(可多选;若列表为空可选择跳过)",
multiple: true,
options: [
{ label: "fix-ns-pollution", description: "P0: 修复命名空间污染 (ADR-033)" },
{ label: "add-stream-pipe-ops", description: "P0: 实现Stream管道操作符 (ADR-022, 2-3天)" },
{ label: "add-cdc-support", description: "P1: 跨时钟域支持 (架构差距, 3-5天)" },
{ label: "...", description: "..." },
{ label: "其他 (自定义)", description: "描述新的 propose 需求" }
]
}
用户选择处理:
| 选择 | 行为 |
|---|
| 一个或多个建议 | 记录选中的 name + description,进入 Phase 4 |
| "其他 (自定义)" | 用户描述需求,AI 按相同格式生成新条目,进入 Phase 4 |
| 跳过(未选择) | 直接进入 Phase 5,跳过创建 |
| Phase 4 完成后回到 Phase 3 继续选择 | 支持多次选择,直到用户完成 |
Phase 4:串行创建每个 propose
对每个选中的 propose,按以下步骤串行创建(每次成功后继续下一个):
for each selected propose <name>:
if [ -d "openspec/changes/<name>/" ]; then
echo "⚠️ Change <name> 已存在,跳过"
continue
fi
openspec new change "<name>"
if [ $? -ne 0 ]; then
echo "❌ 创建 change <name> 失败,跳过"
continue
fi
STATUS=$(openspec status --change "<name>" --json)
APPLY_REQUIRES=$(echo "$STATUS" | jq -r '.applyRequires | join("\n")')
ARTIFACTS=$(echo "$STATUS" | jq -r --arg req "$APPLY_REQUIRES" '
.artifacts[] | select(
.id as $id | ($req | split("\n") | index($id))
) | .id
')
for each artifact_id in artifact_order:
INSTR=$(openspec instructions "$artifact_id" --change "<name>" --json)
OUTPUT_PATH=$(echo "$INSTR" | jq -r '.outputPath')
DEPS=$(echo "$INSTR" | jq -r '.dependencies[]')
for each dep in DEPS:
读取 dep 文件内容
test -f "$OUTPUT_PATH" || { echo "❌ artifact $artifact_id 创建失败"; break; }
echo " 已创建: $artifact_id → $OUTPUT_PATH"
FINAL_STATUS=$(openspec status --change "<name>" --json)
echo "✅ propose <name> 所有 artifacts 已就绪"
Phase 5:更新 proposal-suggestions.md + 汇总输出
5a. 从建议列表中移除已创建的 propose
从 proposal-suggestions.md 中删除已成功创建的条目(按 name 匹配)。保留未选中和跳过的条目供下次使用。
5b. 汇总输出
✅ Propose 阶段完成
本次创建的 propose:
1. fix-ns-pollution → openspec/changes/fix-ns-pollution/ (已完成)
2. add-stream-pipe-ops → openspec/changes/add-stream-pipe-ops/ (已完成)
建议列表剩余 2 个 propose(未创建):
- add-cdc-support (P1)
- refactor-sim-eval (P2)
下一步:
1. ⚠️ 必须提交 artifact(否则 plan 阶段的 COMMIT GATE 会拒绝):
git add openspec/changes/fix-ns-pollution/ openspec/changes/add-stream-pipe-ops/
git add proposal-suggestions.md
git commit -m "feat: propose fix-ns-pollution + add-stream-pipe-ops"
(注意: 每个 change 应独立提交,或按批次提交。不 commit 就无法创建 worktree!)
2. 对每个 change 执行 plan:
skill_use("openspec-workflow-plan add-stream-pipe-ops")
5c. 用户未选择任何 propose 时的输出
✅ 扫描完成,未创建新的 propose。
建议列表已更新(含 X 个待办建议):
- fix-ns-pollution (P0)
- add-stream-pipe-ops (P0)
- ...
下次执行本技能时会从现有建议列表继续。
5d. Phase 5 后自动检查剩余建议(循环机制)
if [ -f "proposal-suggestions.md" ]; then
REMAINING=$(grep -c "status: 待创建" "proposal-suggestions.md" 2>/dev/null || echo "0")
if [ "$REMAINING" -gt 0 ]; then
echo ""
echo "📋 proposal-suggestions.md 中还有 $REMAINING 个未创建的 change"
echo ""
echo "请选择:"
echo "1. 继续创建其他 change(返回 Phase 3 选择)"
echo "2. 完成 Propose 阶段(提交当前 artifacts)"
echo "i. 其他输入"
fi
fi
多次调用说明
本技能可以反复调用,每次调用:
- Phase 0:读取已有的
proposal-suggestions.md,移除已创建为 change 的条目
- Phase 1-2:扫描是否有新产生的建议(新 ADR、新 TODO),与新发现合并
- Phase 3-4:只展示尚未被创建的 propose
- Phase 5:更新 proposal-suggestions.md(移除已创建的)
这样 proposal-suggestions.md 成为持续的待办清单,多次调用逐步消耗。
关键约束
- 只读不写代码:本技能只分析文档和创建 artifacts,不修改源代码
- 串行执行:每个 propose 依次创建,不并行
- 建议 vs 决定:建议列表只是参考,用户决定最终创建哪些
- proposal-suggestions.md 是持久化文件:随 git 版本控制,每次执行时增量更新
- 错误容错:单个 propose 创建失败不影响后续(skip 继续)