docs: 新增 configurable-pipeline 变更提案,探索可配置多阶段流水线模型
- proposal/design/specs/tasks:将固定 5 阶段重构为可配置 pipeline(discuss + pipeline + archive 三明治结构) - 引入 stage 完成判定(文档落地基线 + 可选 validate 实质门禁)与 finish 硬门禁 - 保留 explore 会话记录 session-ses_1357.md 作为思考溯源
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 三明治流程结构
|
||||
|
||||
系统 MUST 采用三明治结构:`discuss`(固定,流水线之前)→ `pipeline`(可配置的线性阶段序列)→ `finish`(固定,流水线之后,终端收尾)。discuss 和 finish MUST NOT 在 pipeline stages 中定义——它们是框架级固定命令。
|
||||
|
||||
#### Scenario: 完整流程顺序
|
||||
|
||||
- **WHEN** 用户执行一个变更的完整流程
|
||||
- **THEN** 流程顺序为:先可选执行 discuss,再按序执行 pipeline 中每个 stage,最后执行 finish 归档
|
||||
|
||||
#### Scenario: discuss 不可作为 stage id
|
||||
|
||||
- **WHEN** 用户尝试在 stages 中定义 id 为 `discuss` 的阶段
|
||||
- **THEN** 配置校验失败(被保留字规则拦截)
|
||||
|
||||
#### Scenario: finish 不可作为 stage id
|
||||
|
||||
- **WHEN** 用户尝试在 stages 中定义 id 为 `finish` 的阶段
|
||||
- **THEN** 配置校验失败(被保留字规则拦截)
|
||||
|
||||
### Requirement: discuss 命令
|
||||
|
||||
系统 MUST 提供 `rune discuss <change>` 命令。该命令 MUST 输出讨论模式提示词文本。该命令 MUST NOT 检查任何前置条件(无门禁)。该命令 MUST NOT 要求产出任何文档。该命令的行为与当前 0.1.x 实现(输出探索模式 prompt)保持一致。
|
||||
|
||||
#### Scenario: 输出讨论提示词
|
||||
|
||||
- **WHEN** 用户执行 `rune discuss my-feature`
|
||||
- **THEN** 命令输出讨论模式提示词文本,退出码为 0
|
||||
|
||||
#### Scenario: discuss 无前置门禁
|
||||
|
||||
- **WHEN** 变更目录为空(无任何 stage 文档),用户执行 `rune discuss my-feature`
|
||||
- **THEN** 命令正常输出提示词,不报任何前置条件错误
|
||||
|
||||
### Requirement: 动态 Stage 命令注册
|
||||
|
||||
系统 MUST 为配置中的每个 pipeline stage 注册对应的 CLI 命令 `rune <stage-id> <change>`。命令名由 stage id 决定,运行时从配置动态生成,MUST NOT 硬编码任何特定 stage。
|
||||
|
||||
#### Scenario: 从配置注册命令
|
||||
|
||||
- **WHEN** 配置定义 stages 为 `[requirements, design, plan, task, build]`
|
||||
- **THEN** CLI 注册 5 个命令:`rune requirements`、`rune design`、`rune plan`、`rune task`、`rune build`
|
||||
|
||||
#### Scenario: 自定义 stage 注册命令
|
||||
|
||||
- **WHEN** 配置定义 stages 包含 `{ id: "code-review", prompt: "..." }`
|
||||
- **THEN** CLI 注册 `rune code-review <change>` 命令
|
||||
|
||||
#### Scenario: 执行 stage 命令输出提示词
|
||||
|
||||
- **WHEN** 用户执行 `rune plan my-feature`,且前置阶段全完成
|
||||
- **THEN** 命令输出 plan 阶段的 prompt 文本,退出码为 0
|
||||
|
||||
### Requirement: Stage 命令软门禁
|
||||
|
||||
`rune <stage-id> <change>` MUST 在输出提示词前检查该 stage 的所有前置 stage 是否完全完成(fully done)。若任一前置 stage 未完全完成,命令 MUST 拒绝输出提示词,报错指出第一个未完成的前置 stage,并退出码非零。
|
||||
|
||||
#### Scenario: 前置全完成则放行
|
||||
|
||||
- **WHEN** 流水线为 [A, B, C],A 和 B 完全完成,用户执行 `rune C my-feature`
|
||||
- **THEN** 命令输出 C 的提示词
|
||||
|
||||
#### Scenario: 前置有未完成则拒绝
|
||||
|
||||
- **WHEN** 流水线为 [A, B, C],A 完全完成但 B 未完成,用户执行 `rune C my-feature`
|
||||
- **THEN** 命令拒绝输出提示词,报错指出 B 阶段未完成,退出码非零
|
||||
|
||||
#### Scenario: 第一个 stage 无前置门禁
|
||||
|
||||
- **WHEN** 流水线为 [A, B, C],变更目录为空,用户执行 `rune A my-feature`(第一个 stage)
|
||||
- **THEN** 命令输出 A 的提示词,不报前置条件错误
|
||||
|
||||
### Requirement: finish 命令硬门禁
|
||||
|
||||
`rune finish <change>` MUST 在执行归档操作前检查所有 pipeline stage 是否完全完成。若任一 stage 未完全完成,命令 MUST 拒绝归档,报错列出所有未完成的 stage 及其原因(文档缺失或 validate 失败),退出码非零。所有 stage 完全完成时,命令 MUST 执行归档操作(移动变更目录到 archive/)。
|
||||
|
||||
#### Scenario: 全完成则归档
|
||||
|
||||
- **WHEN** 流水线为 [A, B, C],所有 stage 完全完成,用户执行 `rune finish my-feature`
|
||||
- **THEN** 命令将变更目录移动到 archive/,退出码为 0
|
||||
|
||||
#### Scenario: 有未完成则拒绝归档
|
||||
|
||||
- **WHEN** 流水线为 [A, B, C],A 和 C 完全完成但 B 文档缺失,用户执行 `rune finish my-feature`
|
||||
- **THEN** 命令拒绝归档,报错指出 B 阶段文档缺失,退出码非零
|
||||
|
||||
#### Scenario: validate 失败则拒绝归档
|
||||
|
||||
- **WHEN** 所有 stage 文档存在,但 build 阶段的 validate 返回 falsy,用户执行 `rune finish my-feature`
|
||||
- **THEN** 命令拒绝归档,报错指出 build 阶段 validate 未通过(含失败原因),退出码非零
|
||||
|
||||
#### Scenario: finish 列出所有未完成项
|
||||
|
||||
- **WHEN** 流水线为 [A, B, C],A 缺文档且 B validate 失败,用户执行 `rune finish my-feature`
|
||||
- **THEN** 报错同时列出 A(文档缺失)和 B(validate 失败),不只是第一个
|
||||
|
||||
### Requirement: status 命令
|
||||
|
||||
系统 MUST 提供 `rune status <change>` 命令,输出变更的流水线进度。输出 MUST 包含每个 stage 的状态(文档是否存在、validate 是否通过、是否完全完成)和整体进度(当前阶段索引、是否全部完成)。该命令 MUST NOT 有任何门禁——纯只读审计。
|
||||
|
||||
#### Scenario: 显示全部阶段状态
|
||||
|
||||
- **WHEN** 流水线为 [A, B, C],A 完全完成,B 文档存在但 validate 失败,C 文档不存在
|
||||
- **THEN** status 输出显示:A done、B 文档存在但 validate 失败、C 文档缺失,当前进度为 B
|
||||
|
||||
#### Scenario: 全新变更状态
|
||||
|
||||
- **WHEN** 变更目录为空,用户执行 `rune status my-feature`
|
||||
- **THEN** status 输出显示所有阶段未完成,当前进度为第一个阶段
|
||||
|
||||
#### Scenario: 全完成状态
|
||||
|
||||
- **WHEN** 所有阶段完全完成,用户执行 `rune status my-feature`
|
||||
- **THEN** status 输出显示所有阶段 done,可执行 finish 归档
|
||||
|
||||
### Requirement: 变更目录约定
|
||||
|
||||
所有 stage 文档 MUST 位于变更目录内(如 `changes/<change-name>/`)。discuss 不产出文档。finish 执行后变更目录 MUST 被移动到 `archive/<change-name>/`。
|
||||
|
||||
#### Scenario: stage 文档路径
|
||||
|
||||
- **WHEN** stage id 为 `design`,变更名为 `my-feature`
|
||||
- **THEN** 文档路径为 `changes/my-feature/design.md`
|
||||
|
||||
#### Scenario: finish 后目录移动
|
||||
|
||||
- **WHEN** finish 成功执行
|
||||
- **THEN** `changes/my-feature/` 目录移动到 `archive/my-feature/`
|
||||
105
openspec/changes/configurable-pipeline/specs/pipeline/spec.md
Normal file
105
openspec/changes/configurable-pipeline/specs/pipeline/spec.md
Normal file
@@ -0,0 +1,105 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 流水线配置格式
|
||||
|
||||
流水线 MUST 在配置文件中通过 `stages` 有序数组定义。每个 stage MUST 包含 `id`(字符串)和 `prompt`(字符串),MAY 包含 `validate`(JS 函数体字符串)。stages 数组的顺序即为阶段执行顺序。
|
||||
|
||||
#### Scenario: 最小合法配置
|
||||
|
||||
- **WHEN** 配置文件包含 `stages: [{ id: "plan", prompt: "..." }]`
|
||||
- **THEN** 配置加载成功,流水线包含 1 个阶段
|
||||
|
||||
#### Scenario: 含 validate 的完整配置
|
||||
|
||||
- **WHEN** 配置文件包含 `stages: [{ id: "build", prompt: "...", validate: "return true;" }]`
|
||||
- **THEN** 配置加载成功,build 阶段关联了 validate 函数
|
||||
|
||||
#### Scenario: 缺少 id 的 stage
|
||||
|
||||
- **WHEN** 配置文件中某个 stage 缺少 `id` 字段
|
||||
- **THEN** 配置加载失败,报错指出哪个 stage 缺少 id
|
||||
|
||||
#### Scenario: 缺少 prompt 的 stage
|
||||
|
||||
- **WHEN** 配置文件中某个 stage 缺少 `prompt` 字段
|
||||
- **THEN** 配置加载失败,报错指出哪个 stage 缺少 prompt
|
||||
|
||||
### Requirement: Stage ID 命名规则
|
||||
|
||||
Stage id MUST 匹配 `^[a-z][a-z-]*$`(小写字母开头,后续只允许小写字母和短横线)。Stage id MUST 在同一流水线内唯一。Stage id MUST NOT 使用保留字:`discuss`、`finish`、`status`、`archive`、`init`。
|
||||
|
||||
#### Scenario: 合法 id
|
||||
|
||||
- **WHEN** stage id 为 `code-review`
|
||||
- **THEN** 配置校验通过
|
||||
|
||||
#### Scenario: 包含大写字母的 id
|
||||
|
||||
- **WHEN** stage id 为 `CodeReview`
|
||||
- **THEN** 配置校验失败,报错指出 id 必须为 `[a-z][a-z-]*` 格式
|
||||
|
||||
#### Scenario: 包含数字的 id
|
||||
|
||||
- **WHEN** stage id 为 `stage1`
|
||||
- **THEN** 配置校验失败,报错指出 id 只允许小写字母和短横线
|
||||
|
||||
#### Scenario: 重复 id
|
||||
|
||||
- **WHEN** 流水线包含两个 id 为 `plan` 的 stage
|
||||
- **THEN** 配置校验失败,报错指出 stage id 不可重复
|
||||
|
||||
#### Scenario: 保留字 id
|
||||
|
||||
- **WHEN** stage id 为 `finish`
|
||||
- **THEN** 配置校验失败,报错指出 `finish` 是保留字不可用作 stage id
|
||||
|
||||
### Requirement: Stage 文档约定
|
||||
|
||||
每个 pipeline stage 产出且仅产出一个文档,文件名由 stage id 派生:`<stage-id>.md`。文档 MUST 位于变更目录内(如 `changes/my-feature/plan.md`)。Stage id 到文件名的映射 MUST NOT 可配置——统一规则不可覆盖。
|
||||
|
||||
#### Scenario: 文档文件名派生
|
||||
|
||||
- **WHEN** stage id 为 `design`
|
||||
- **THEN** 该阶段的产出文档路径为 `<changeDir>/design.md`
|
||||
|
||||
#### Scenario: 含短横线的 id 文件名
|
||||
|
||||
- **WHEN** stage id 为 `code-review`
|
||||
- **THEN** 该阶段的产出文档路径为 `<changeDir>/code-review.md`
|
||||
|
||||
### Requirement: 默认流水线
|
||||
|
||||
当用户执行 `rune init` 或配置中未定义 `stages` 时,系统 MUST 提供默认流水线,包含 5 个阶段,按序为:`requirements`、`design`、`plan`、`task`、`build`。每个默认阶段 MUST 包含对应的默认 prompt。
|
||||
|
||||
#### Scenario: init 生成默认流水线
|
||||
|
||||
- **WHEN** 用户执行 `rune init`
|
||||
- **THEN** 生成的配置文件包含 5 个阶段:requirements、design、plan、task、build,各含默认 prompt
|
||||
|
||||
#### Scenario: 配置中无 stages 时回退默认
|
||||
|
||||
- **WHEN** 配置文件加载后 stages 为空或未定义
|
||||
- **THEN** 系统使用默认 5 阶段流水线
|
||||
|
||||
### Requirement: 流水线线性有序
|
||||
|
||||
流水线 MUST 是线性有序序列——不支持分支、条件跳过或并行阶段。阶段的执行顺序由配置数组顺序决定。Stage N 的前置条件是 Stage 0 至 Stage N-1 全部完成。
|
||||
|
||||
#### Scenario: 阶段顺序由数组决定
|
||||
|
||||
- **WHEN** 配置定义 stages 为 `[A, B, C]`
|
||||
- **THEN** 执行顺序固定为 A→B→C,B 的前置是 A 完成,C 的前置是 A 和 B 完成
|
||||
|
||||
### Requirement: 配置 schema 校验
|
||||
|
||||
配置加载时 MUST 执行以下校验,任一失败则拒绝加载并给出明确错误信息:stage id 格式、唯一性、保留字检查;pipeline 至少 1 个 stage;validate(若有)是可构造为函数的合法 JS 字符串。
|
||||
|
||||
#### Scenario: validate 语法错误
|
||||
|
||||
- **WHEN** 某个 stage 的 validate 值为 `return {{{`
|
||||
- **THEN** 配置加载失败,报错指出该 stage 的 validate 不是合法 JS
|
||||
|
||||
#### Scenario: 空流水线
|
||||
|
||||
- **WHEN** 配置文件中 stages 为空数组
|
||||
- **THEN** 配置加载失败,报错指出流水线至少需要 1 个阶段
|
||||
@@ -0,0 +1,105 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 基线完成判定(文档落地)
|
||||
|
||||
Stage 的基线完成状态由文档是否存在决定:变更目录内 `<stage-id>.md` 存在(即使内容为空)即视为基线完成。此检查 MUST 始终执行,不可通过配置关闭。文档不存在则该 stage 基线未完成。
|
||||
|
||||
#### Scenario: 文档存在即基线完成
|
||||
|
||||
- **WHEN** 变更目录中存在 `plan.md` 文件(无论内容是否为空)
|
||||
- **THEN** plan 阶段的基线完成状态为 true
|
||||
|
||||
#### Scenario: 文档不存在即基线未完成
|
||||
|
||||
- **WHEN** 变更目录中不存在 `plan.md` 文件
|
||||
- **THEN** plan 阶段的基线完成状态为 false
|
||||
|
||||
### Requirement: 实质完成判定(validate)
|
||||
|
||||
Stage 的实质完成状态由 validate 函数决定。若 stage 未配置 validate,则实质完成恒为 true(基线完成即完全完成)。若配置了 validate,实质完成要求 validate 函数执行后返回 truthy 值。
|
||||
|
||||
#### Scenario: 无 validate 时基线即实质
|
||||
|
||||
- **WHEN** stage 未配置 validate,且文档存在
|
||||
- **THEN** 该 stage 实质完成状态为 true
|
||||
|
||||
#### Scenario: validate 返回 truthy
|
||||
|
||||
- **WHEN** stage 配置了 validate,文档存在,validate 执行返回 `true`
|
||||
- **THEN** 该 stage 实质完成状态为 true
|
||||
|
||||
#### Scenario: validate 返回 falsy
|
||||
|
||||
- **WHEN** stage 配置了 validate,文档存在,validate 执行返回 `false`
|
||||
- **THEN** 该 stage 实质完成状态为 false
|
||||
|
||||
#### Scenario: validate 抛出异常
|
||||
|
||||
- **WHEN** stage 配置了 validate,validate 执行时抛出 `new Error("task.md 缺少 checkbox")`
|
||||
- **THEN** 该 stage 实质完成状态为 false,且错误信息 "task.md 缺少 checkbox" 被捕获并记录
|
||||
|
||||
### Requirement: 阶段完全完成定义
|
||||
|
||||
Stage 完全完成(fully done)MUST 同时满足基线完成和实质完成:`done = docExists && validated`。所有门禁检查 MUST 使用 fully done 作为判定条件。
|
||||
|
||||
#### Scenario: 文档存在但 validate 失败
|
||||
|
||||
- **WHEN** stage 文档存在,但 validate 返回 falsy
|
||||
- **THEN** 该 stage fully done 为 false
|
||||
|
||||
#### Scenario: 文档不存在
|
||||
|
||||
- **WHEN** stage 文档不存在(无论 validate 配置或结果如何)
|
||||
- **THEN** 该 stage fully done 为 false
|
||||
|
||||
### Requirement: validate 函数执行模型
|
||||
|
||||
validate MUST 通过 `new AsyncFunction('ctx', body)` 构造执行,其中 `ctx` 为上下文对象。`ctx` MUST 包含 `changeDir` 字段(变更目录绝对路径)。validate 函数体 MAY 使用 `await` 语法。返回值 truthy 判定为通过;抛出异常判定为失败,异常 message MUST 作为失败原因传递给用户。
|
||||
|
||||
#### Scenario: 读取文档内容校验
|
||||
|
||||
- **WHEN** validate 函数体为 `const c = await Bun.file(\`${ctx.changeDir}/task.md\`).text(); return /- \\[x\\]/.test(c);`
|
||||
- **THEN** 函数被正确构造和执行,能读取 task.md 内容并返回正则匹配结果
|
||||
|
||||
#### Scenario: 执行命令校验
|
||||
|
||||
- **WHEN** validate 函数体为 `const p = Bun.spawnSync(["bun", "test"], { cwd: ctx.changeDir }); return p.exitCode === 0;`
|
||||
- **THEN** 函数被正确执行,`bun test` 的退出码决定验证结果
|
||||
|
||||
#### Scenario: ctx 包含 changeDir
|
||||
|
||||
- **WHEN** validate 函数被调用
|
||||
- **THEN** 参数 `ctx` 是一个对象,且 `ctx.changeDir` 为变更目录的绝对路径字符串
|
||||
|
||||
### Requirement: 变更状态派生
|
||||
|
||||
系统 MUST 从文件系统(文档存在性)和配置(validate 定义)实时派生变更状态,不持久化任何状态文件。变更状态 MUST 包含每个 stage 的 docExists、validated、done 状态,以及流水线的 currentIndex(第一个未完成阶段的索引)和 allDone(所有阶段是否完全完成)。
|
||||
|
||||
#### Scenario: 全新变更目录
|
||||
|
||||
- **WHEN** 变更目录刚创建,无任何 stage 文档
|
||||
- **THEN** 所有 stage 的 docExists 为 false,currentIndex 为 0,allDone 为 false
|
||||
|
||||
#### Scenario: 部分完成的变更
|
||||
|
||||
- **WHEN** 流水线为 [A, B, C],A 文档存在且 validate 通过,B 文档不存在
|
||||
- **THEN** A 的 done 为 true,B 的 done 为 false,currentIndex 为 1,allDone 为 false
|
||||
|
||||
#### Scenario: 全部完成的变更
|
||||
|
||||
- **WHEN** 流水线为 [A, B, C],所有阶段文档存在且 validate 通过
|
||||
- **THEN** 所有 stage 的 done 为 true,currentIndex 等于 stages 长度(越界表示无待办),allDone 为 true
|
||||
|
||||
### Requirement: validate 结果缓存限制
|
||||
|
||||
同一变更目录在同一 CLI 调用内,validate 结果 MAY 被缓存以避免重复执行。但跨 CLI 调用 MUST NOT 缓存——每次 CLI 调用 MUST 重新执行 validate,因为用户可能在两次调用间修改了文档。
|
||||
|
||||
#### Scenario: 同一次调用内不重复执行
|
||||
|
||||
- **WHEN** 同一次 CLI 调用中需要多次获取某 stage 的完成状态
|
||||
- **THEN** validate 函数至多执行一次
|
||||
|
||||
#### Scenario: 不同调用重新执行
|
||||
|
||||
- **WHEN** 用户先执行 `rune status`,再执行 `rune finish`
|
||||
- **THEN** finish 命令 MUST 重新执行所有 validate,不使用 status 调用的缓存结果
|
||||
Reference in New Issue
Block a user