ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

spec-kit Workflows 详解:用 YAML 构建可恢复、带人工审核门的 SDD 自动化流水线

spec-kit Workflows 详解:用 YAML 构建可恢复、带人工审核门的 SDD 自动化流水线 spec-kit Workflows 详解用 YAML 构建可恢复、带人工审核门的 SDD 自动化流水线【免费下载链接】spec-kit Toolkit to help you get started with Spec-Driven Development项目地址: https://gitcode.com/GitHub_Trending/sp/spec-kit本文基于 spec-kit 仓库的workflows/README.md及配套架构文档系统讲解 Workflows 子系统的完整用法与实现原理从 YAML 工作流定义的 11 种步骤类型、{{ expression }}表达式引擎到状态持久化与断点续跑resume机制、目录catalog发现体系。读完后你可以用specify workflow命令组直接编排 specify → plan → tasks → implement 全流程并理解引擎在每一步落盘状态、在 gate 处暂停等待人工决策的底层实现。1. 什么是 WorkflowWorkflows 是 spec-kit 中用 YAML 定义的多步骤、可恢复的自动化流水线。它把 Spec Kit 命令如speckit.specify、speckit.plan跨 AI 集成Claude、Copilot、Gemini 等编排起来支持条件分支、循环、并行扇出并能在人工审核门gate处暂停——从而无需手动逐步调用即可完成端到端的 Spec-Driven DevelopmentSDD循环。一个最小工作流长这样摘自 workflows/README.mdsteps: - id: specify command: speckit.specify input: args: {{ inputs.spec }} - id: review type: gate message: Review the spec before planning. options: [approve, reject] on_reject: abort - id: plan command: speckit.plan从执行模型看对应 引擎执行流程步骤顺序执行。每个步骤拿到一个StepContext其中包含解析后的输入、已累积的步骤结果和 workflow 级默认值integration、model、options。执行完毕后该步骤的输出存入context.steps[step_id]后续步骤即可通过{{ steps.specify.output.file }}之类的表达式读取。控制流步骤if、switch、while、do-while返回next_steps——引擎通过_execute_steps()递归执行这些内联步骤。嵌套步骤与顶层步骤共享同一个StepContext和RunState因此嵌套体里的输出对后续顶层步骤可见。每步落盘状态。引擎在每个步骤执行前都保存一次RunState见 引擎实现 中_execute_steps开头对current_step_index/current_step_id的更新与state.save()调用因此中途中断后可以从确切位置恢复。2. 快速上手命令组全览以下是 workflows/README.md 给出的完整命令序列# Search available workflows specify workflow search # Install the built-in SDD workflow specify workflow add speckit # Or run directly from a local YAML file specify workflow run ./workflow.yml --input specBuild a user authentication system with OAuth support # Run an installed workflow with inputs specify workflow run speckit --input specBuild a user authentication system with OAuth support # Check run status specify workflow status # Resume after a gate pause specify workflow resume run_id # Get detailed workflow info specify workflow info speckit # Remove a workflow specify workflow remove speckit在 CLI 命令实现 中这些命令分别落在workflow_search、workflow_add、workflow_run、workflow_resume、workflow_status、workflow_info、workflow_remove等处理函数上全部注册在一个 Typer 子命令树上specify workflow另含catalog、step、overlay等子组。2.1 从已安装工作流运行specify workflow add speckit specify workflow run speckit --input specBuild a user authentication system with OAuth supportspecify workflow add id会从 catalog 条目声明的url字段下载 workflow YAML安装到.specify/workflows/id/workflow.yml详见 catalog 系统。仓库内置的speckit工作流在 官方 catalog 中注册其定义文件见 workflows/speckit/workflow.yml。2.2 从本地 YAML 文件运行specify workflow run ./my-workflow.yml --input specBuild a user authentication system with OAuth support引擎加载时先按文件路径尝试只要传入的源是.yml/.yaml后缀且文件存在就直接从该路径解析见 load_workflow否则才按已安装 ID解析。两种运行方式对引擎而言完全等价。2.3 多个输入--input可重复出现specify workflow run speckit \ --input specBuild a user authentication system with OAuth support \ --input scopebackend-only在源码层面--input keyvalue由_parse_input_values解析每条必须包含键值两侧会被strip()任何缺少的条目都会直接报错退出。注意 CLI 传入的输入值在引擎眼中都是字符串之后才按 workflow 声明的inputs:schema 做类型转换见第 8 节。3. 内置的 11 种步骤类型Workflows 内置 11 种步骤类型每种都是 steps 子包 下独立的实现类并通过STEP_REGISTRY注册type:字段缺省值为command。下表汇总自 ARCHITECTURE.mdType Key类用途返回next_stepscommandCommandStep通过集成 CLI 调用已安装的 Spec Kit 命令否promptPromptStep向集成 CLI 发送任意内联提示否shellShellStep运行 shell 命令并捕获输出否initInitStep引导项目等价于specify init否gateGateStep交互式人工审核/批准否CI 中暂停ifIfThenStep条件分支then/else是switchSwitchStep基于表达式的多分支分发是whileWhileStep条件为真时循环是条件为真时do-whileDoWhileStep循环主体至少执行一次是总是fan-outFanOutStep对集合逐元素分发否引擎展开fan-inFanInStep聚合 fan-out 结果否3.1 Command 步骤默认类型按名称调用已安装的 Spec Kit 命令经由集成 CLI 下发- id: specify command: speckit.specify input: args: {{ inputs.spec }} integration: claude # Optional: override workflow default model: claude-sonnet-4-20250514 # Optional: override modelintegration与model可覆盖 workflow 顶层的workflow.integration/workflow.model默认值若步骤和 workflow 层都未指定则回落到项目初始化时记录在.specify/integration.json的集成见 8.2 节的integration: auto哨兵。3.2 Prompt 步骤向集成 CLI 发送任意内联提示无需命令文件- id: security-review type: prompt prompt: Review {{ inputs.file }} for security vulnerabilities integration: claude3.3 Shell 步骤运行 shell 命令并捕获输出- id: run-tests type: shell run: npm test # runs from the project root; no interpolation needed timeout: 1800 # Optional: max seconds before the command is killed (default 300)timeout是命令被强杀前的最长秒数必须是正数缺省3005 分钟长耗时任务全量构建、lint 聚合、集成测试应调大。结合 ShellStep 实现 可以看到几个文档未展开的实现细节命令在cwd 项目根目录下以shellTrue执行刻意选择以支持管道、重定向和多命令表达式capture_outputTrue捕获 stdout/stderr输出统一为{exit_code: ..., stdout: ..., stderr: ...}非零退出码返回StepStatus.FAILED超时时exit_code记为-1可选声明output_format: json把 stdout 解析成结构化数据放入output.data供后续步骤如 fan-out 的items:消费若 stdout 不是合法 JSON步骤直接失败——声明即契约步骤级超时校验非常严格timeout: true布尔、.inf/.nan、超大整数都会被_timeout_error拒绝并返回带清晰错误信息的 FAILED而不是让subprocess.run()崩溃。安全警示原文档强调run字段由系统 shell 执行其中的{{ ... }}表达式是按原始文本替换没有任何自动引号或转义。inputs.*的值或前序步骤的输出包括 AI 生成的 prompt 输出会被解析为 shell 语法可能改变实际执行的命令。唯一可靠的控制手段是在源头用enum/白名单约束这类值或把无法约束的值完全排除在run之外。给替换值加引号只能帮助可信值避免词法拆分不是安全边界——包含匹配引号的值仍可逃逸。详见 Interpolation and shell safety。3.4 Init 步骤以与specify init相同的方式引导项目——铺设模板、脚本、共享基础设施以及所选编码代理集成。以非交互方式运行默认--ignore-agent-tools集成从步骤配置或 workflow 默认值解析- id: bootstrap type: init here: true # or: project: my-project integration: copilot # Optional: defaults to workflow integration integration_options: --skills # Optional: extra options for the integration script: sh # Optional: sh, ps, or py force: true # Optional: required when target directory already exists preset: healthcare-compliance # Optional preset ID3.5 Gate 步骤暂停等待人工审核调用specify workflow resume时恢复- id: review-spec type: gate message: Review the generated spec before planning. options: [approve, edit, reject] on_reject: aborton_reject的三种取值abort为默认、skip、retry对运行语义的影响见第 4 节错误处理部分。3.6 If/Then/Else 步骤基于表达式做条件分支- id: check-scope type: if condition: {{ inputs.scope full }} then: - id: full-plan command: speckit.plan else: - id: quick-plan command: speckit.plan options: quick: true3.7 Switch 步骤基于表达式取值做多路分发- id: route type: switch expression: {{ steps.review.output.choice }} cases: approve: - id: plan command: speckit.plan reject: - id: log type: shell run: echo Rejected default: - id: fallback type: gate message: Unexpected choice3.8 While 循环步骤条件为真时重复执行- id: retry type: while condition: {{ steps.run-tests.output.exit_code ! 0 }} max_iterations: 5 steps: - id: fix command: speckit.implement3.9 Do-While 循环步骤主体至少执行一次然后按条件继续- id: refine type: do-while condition: {{ steps.review.output.choice edit }} max_iterations: 3 steps: - id: revise command: speckit.specify从 引擎的循环实现 可以补充两点max_iterations缺省或非法如布尔值、小于 1时回落到默认10每轮迭代的嵌套步骤会生成带迭代编号的命名空间化 IDstep-id:base-id:iter保证日志和状态键唯一随后再把结果回写到无后缀的原始 ID 上使循环体内后续步骤和循环条件都能读到最新值。3.10 Fan-Out 步骤对集合中每个元素分发一个步骤模板默认顺序执行- id: parallel-impl type: fan-out items: {{ steps.tasks.output.task_list }} max_concurrency: 3 step: id: impl command: speckit.implement3.11 Fan-In 步骤聚合 fan-out 步骤的结果- id: collect type: fan-in wait_for: [parallel-impl] output: {}关于 fan-out/fan-in引擎实现 给出了文档之外的关键细节fan-out 并非真并行max_concurrency 1缺省时逐元素顺序执行 1时用有界线程池 滑动窗口并发。无论哪种模式结果始终按元素顺序组装不是完成顺序。每个元素的 ID 遵循parentId:templateId:index文法因此步骤 ID 中不允许出现:校验器会直接拒绝。任一元素使运行进入暂停/失败/中止状态时未开始的元素会被取消运行状态按元素顺序中第一个使运行停摆的元素归因语义与顺序模式完全一致。fan-in 的wait_for有静态校验引用的 ID 必须已声明且位于 fan-in 之前见 步骤校验逻辑否则在specify workflow run创建运行之前就会被拒绝——拼写错误和前向引用都会在运行前暴露而不是运行时静默得到空结果。4. 错误处理continue_on_error 与 gate 的交互默认情况下任何返回StepResult(statusStepStatus.FAILED, ...)的步骤都会终止整个运行——最常见的是shell或command步骤以非零退出。在步骤上设置continue_on_error: true则记录其结果并继续执行下一个兄弟步骤- id: heavy-thing type: command integration: claude command: speckit.heavy-thing continue_on_error: true - id: check-result type: if condition: {{ steps.heavy-thing.output.exit_code ! 0 }} then: - id: review type: gate message: Step failed (exit {{ steps.heavy-thing.output.exit_code }}). Approve to run the recovery path, or reject to leave the failure recorded and move on. on_reject: skip - id: recover type: if condition: {{ steps.review.output.choice approve }} then: - id: rerun command: speckit.recovery else: - id: next-thing command: speckit.next-thing失败时退出码仍保留在steps.id.output.exit_code上供下游if/switch分支判断或经 gate 的message插值呈现给操作者。对这个示例原文档强调的三个语义点gate 的两个选项approve、reject都返回StepStatus.COMPLETED。on_reject: skip只控制引擎是否在 reject 时中止skip时不中止它不会自动跳过then:列表中后续的兄弟步骤。下游分支是工作流作者的责任在后续if/switch中读取{{ steps.gate-id.output.choice }}如上例recover步骤所示。on_reject三个取值abort默认——reject 产生StepStatus.FAILED且output.aborted True运行中止、skipreject 产生StepStatus.COMPLETED分支由作者自行处理、retryreject 产生StepStatus.PAUSED下一次specify workflow resume重新运行该 gate。Gate 不会自动重跑失败步骤。要表达重试路径要么自定义 gate 选项并下游分支要么把失败步骤包进自己的循环。实现层面的补充约束对应 引擎失败处理分支字段必须是字面布尔值true/falsetrue这类被强转的字符串会在校验期被拒绝校验器对continue_on_error的类型检查 明确要求isinstance(coe, bool)。作用域仅限返回的失败该标志只处理statusStepStatus.FAILED的步骤结果。从步骤execute()中抛出的未捕获异常会被上层的WorkflowEngine.execute()捕获记为workflow_failed事件并无条件中止运行continue_on_error拦不住。若步骤作者希望覆盖这类异常路径必须在步骤内部捕获异常并返回StepResult(statusStepStatus.FAILED, ...)把失败信息编码进output如exit_code、stderr或自定义字段。gate 的主动中止on_reject: abort总是终止运行continue_on_error不覆盖它——这个标志是为瞬时/可预期的步骤失败设计的不是用来推翻操作者刻意决策的。结构性校验前置specify workflow run在创建运行前就拒绝非法的工作流定义校验失败根本到不了上述运行时代码路径。省略该字段时行为与此特性引入前逐字节等价。5. 表达式引擎工作流定义使用{{ expression }}语法表达动态值# Access inputs args: {{ inputs.spec }} # Access previous step outputs args: {{ steps.specify.output.file }} # Comparisons condition: {{ steps.run-tests.output.exit_code ! 0 }} # Filters message: {{ status | default(pending) }}支持 5 种过滤器default、join、contains、map、from_json。表达式引擎位于 expressions.py按 ARCHITECTURE.md 的完整能力表还支持比较运算符、!、、、、、布尔逻辑and/or/not、成员判断in/not in以及字符串/数字/布尔/列表字面量。其中from_json过滤器可把 JSON 字符串解析为类型化值如{{ steps.emit.output.stdout | from_json }}解析失败会抛错。求值规则纯单表达式整个值只有{{ expr }}返回类型化值数字、布尔、列表等混合模板text {{ expr }} more返回插值后的字符串。命名空间由StepContext构建Key来源可用时机inputs解析后的 workflow 输入始终steps累积的步骤结果首个步骤之后item当前迭代元素fan-out 内部fan_in聚合结果fan-in 内部5.1 运行时上下文{{ context.* }}{{ context.* }}暴露引擎管理的当前运行元数据变量说明context.run_id当前 workflow 运行 ID与workflow run结束时打印的Run ID:同值。自动生成的 ID 是uuid4的前 8 位十六进制操作者提供的 ID 可以是含连字符/下划线的任意字母数字串。运行上下文之外为空字符串。context.workflow_dir解析后的、包含 workflow 源文件的目录绝对路径。文件加载的 workflow 即 YAML 文件的父目录按 ID 安装的 workflow 为安装目录绝对路径如project/.specify/workflows/id/字符串加载的 workflow 为空字符串。恢复resume时保留首次执行时的原始源目录。四个典型用法# Stamp telemetry events with the run id for cross-system join. - id: emit-event type: shell run: echo {\run_id\:\{{ context.run_id }}\,\event\:\started\} events.jsonl # Per-run scratch directory. - id: prep-scratch type: shell run: mkdir -p /tmp/run-{{ context.run_id }} # Pass run id into a command for artifact metadata. - id: tag-artifact command: speckit.specify input: args: {{ context.run_id }} # Reference a sibling file shipped alongside the workflow definition. - id: apply-config type: shell run: cp {{ context.workflow_dir }}/defaults.yml ./config.yml源码佐证run_id的自动生成就是RunState.__init__中的str(uuid.uuid4())[:8]且 ID 会被_RUN_ID_PATTERN严格限制为字母数字 连字符/下划线防止路径穿越SPECKIT_WORKFLOW_DIR环境变量则由 ShellStep 在执行前注入取值与{{ context.workflow_dir }}完全相同。6. 输入类型与类型转换Workflow 输入会经过类型检查并从 CLI 字符串值强制转换inputs: spec: type: string required: true prompt: Describe what you want to build task_count: type: number default: 5 dry_run: type: boolean default: false scope: type: string default: full enum: [full, backend-only, frontend-only]TypeAcceptsExamplestring任意字符串user-authnumber数字字符串 → int/float42→42booleantrue/1/yes→Truefalse/0/no→Falsetrue→True转换由_coerce_input完成源码里还有几个文档表格未覆盖的边界行为number会显式拒绝布尔值Python 中bool是int子类default: true配type: number这种编写错误会快速失败而不是被静默转成1浮点整数归一为int42→42。boolean只接受true/1/yes/false/0/no不区分大小写及原生布尔其余一律报错。string要求值确实是字符串——YAML 里default: 5配type: string会在解析期被拒绝。enum必须声明为列表标量或字符串形式的 enum 直接报错值不在枚举内则抛出value ... not in allowed values错误。缺省与哨兵解析由_resolve_inputs负责未提供且有default的用默认值未提供且required: true的抛ValueError其余跳过。6.1 内置 SDD 工作流的真实输入声明仓库自带、也是 catalog 中唯一官方工作流的 workflows/speckit/workflow.yml 给出了生产级写法。它声明了三个输入inputs: spec: type: string required: true prompt: Describe what you want to build integration: type: string default: auto prompt: Integration to use (e.g. claude, copilot, gemini; auto uses the projects initialized integration) scope: type: string default: full enum: [full, backend-only, frontend-only]并带一个requires前置条件块advisory声明speckit_version: 0.8.5和一组建议集成requires: speckit_version: 0.8.5 integrations: any: - alquimia - claude - copilot - gemini - opencode注意两点其一requires是咨询性声明源码注释明确说明它不是运行时强制边界更不存在requires.permissions能力门shell 步骤始终以用户权限运行校验器会显式拒绝permissions键见 requires 校验其二集成列表是非穷尽的兼容提示项目用任何提供四个核心命令specify/plan/tasks/implement的集成初始化都能跑。该工作流的步骤编排正是 SDD 全循环specify→review-specgate→plan→review-plangate→tasks→implement所有 command 步骤的integration都写作{{ inputs.integration }}。6.2integration: auto哨兵_resolve_default对integration输入做了特殊处理当默认值或显式传入值是auto时引擎读取项目.specify/integration.json把哨兵解析为项目实际初始化的集成见_resolve_default——这就是内置工作流无需硬编码 AI 供应商的原因也解释了 catalog 注释里0.8.5 之前会把auto当字面集成键导致分发失败的版本要求。7. 状态持久化与 Resume每次运行都把状态持久化到.specify/workflows/runs/run_id/# List all runs with status specify workflow status # Check a specific run specify workflow status run_id # Resume a paused run (after approving a gate) specify workflow resume run_id # Resume a failed run (retries from the failed step) specify workflow resume run_id运行状态机为created→running→completed|paused|failed|aborted与 base.py 中的 RunStatus 枚举 一一对应。每个 run 目录下实际包含四类文件见 状态与配置位置表文件内容state.json持久化执行状态状态、当前步骤索引、全部步骤结果inputs.json解析后的输入值log.jsonl追加式事件日志step_started/step_completed/workflow_finished等workflow.yml工作流定义的副本——引擎在 execute() 开头写入保证即使原始 YAML 路径被移动或删除resume 也能重新加载定义RunState 实现 还体现了两个工程细节save()在锁内以临时文件 os.replace方式原子写避免并发 fan-out 期间出现半写文件load()在把run_id拼进路径之前就先做字符集校验堵住路径穿越读取任意文件的可能。Resume 语义对应WorkflowEngine.resume只有paused或failed状态的运行可恢复其他状态直接报错恢复点记录在顶层步骤索引上。恢复时会重新执行暂停/失败的那个步骤本身因此 gate 能再次交互式提问。注意一个已知限制如果暂停发生在嵌套步骤if/switch/while内部resume 会重跑整个父控制流步骤及其嵌套体——嵌套路径栈式的精确恢复是规划中的增强resume 可以合并新输入传入的inputs会覆盖持久化输入并重新走一遍类型校验路径未提供的键保持原值。gate暂停时引擎持久化current_step_index和全部累积的step_resultsspecify workflow resume run_id恢复上下文后从暂停步骤继续KeyboardInterrupt也会把状态安全置为paused并落盘同样可恢复。8. Catalog 管理Workflows 通过 catalog 发现。默认启用官方default与社区community两个 catalog# List active catalogs specify workflow catalog list # Add a custom catalog specify workflow catalog add https://example.com/catalog.json --name my-org # Remove a catalog specify workflow catalog remove index注意社区工作流由各自作者独立创建和维护。维护者可能审核加入社区 catalog 的 PR 的格式与结构但不审核、不审计、不背书、不支持工作流定义本身。安装前请审查工作流源码风险自负。catalog 的解析优先级见 catalog 流程图SPECKIT_WORKFLOW_CATALOG_URL环境变量设置后替换全部默认值→ 项目级.specify/workflow-catalogs.yml→ 用户级~/.specify/workflow-catalogs.yml→ 内置默认default允许安装community仅用于发现。catalog 按 URL 做 SHA256 哈希缓存在.specify/workflows/.cache/1 小时 TTL每个条目带priority合并排序与install_allowed标志。官方 catalog 文件 当前只注册了speckit一条Full SDD Cyclesdd/full-cycle标签url指向仓库内的workflows/speckit/workflow.yml。下载与安装路径有很强的安全约束可从 workflow add 实现 确认workflow YAML 下载上限5 MiB超限即失败且不信任Content-Length头——实际读取的字节流同样计数重定向必须保持 HTTPS 且不得进入本地目标工作流 ID 必须匹配^[a-z0-9][a-z0-9-]*[a-z0-9]$且避开保留名overlays/runs/steps安装写入全程走暂存文件 原子替换 失败回滚事务并拒绝符号链接路径组件。9. 创建自己的工作流按上文 schema 编写workflow.yml本地测试specify workflow run ./workflow.yml --input keyvalue校验specify workflow info ./workflow.yml参考 PUBLISHING.md 提交到 catalog。info命令对本地文件路径同样有效load_workflow的文件路径优先策略适合在提交前做静态检查——非法类型、重复步骤 ID、fan-in 前向引用、非布尔continue_on_error等问题都会在结构化校验阶段被列出。10. 环境变量与配置文件变量说明SPECKIT_WORKFLOW_CATALOG_URL覆盖 catalog URL替换所有默认值SPECKIT_WORKFLOW_DIR由 shell 步骤自动设置包含解析后的 workflow 源目录绝对路径同{{ context.workflow_dir }}的值。字符串加载的 workflow 无源路径时不设置。文件作用域说明.specify/workflow-catalogs.yml项目本项目的自定义 catalog 栈~/.specify/workflow-catalogs.yml用户所有项目的自定义 catalog 栈另有SPECKIT_WORKFLOW_RUN_ID环境变量设置时execute()会优先使用它作为运行 ID见 execute 的 run_id 解析同样受 run_id 字符集约束。11. 仓库布局与延伸阅读workflows/ ├── ARCHITECTURE.md # Internal architecture documentation ├── PUBLISHING.md # Guide for submitting workflows to the catalog ├── README.md # This file ├── catalog.json # Official workflow catalog ├── catalog.community.json # Community workflow catalog └── speckit/ # Built-in SDD cycle workflow └── workflow.yml对应 ARCHITECTURE.md 的模块结构引擎源码位于src/specify_cli/workflows/模块职责__init__.pySTEP_REGISTRY 内建步骤注册base.pyStepBase、StepContext、StepResult、StepStatus、RunStatuscatalog.pyWorkflowCatalog、WorkflowCatalogEntry、WorkflowRegistryengine.pyWorkflowDefinition、WorkflowEngine、RunState、validate_workflow()expressions.pyevaluate_expression()、evaluate_condition()、过滤器steps/11 个步骤类型子包command/shell/init/gate/if_then/prompt/switch/while_loop/do_while/fan_out/fan_in测试覆盖可参考tests/unit/test_workflows.py、tests/unit/test_workflow_run_without_project.py及tests/workflows/下的 overlay 系列测试验证了引擎、表达式求值与运行行为。适用前提小结workflow 需要已安装 spec-kit CLIspecify命令且目标项目已specify init内置speckit工作流要求speckit_version 0.8.5shell 步骤以当前用户权限运行且无沙箱requires声明不构成功能门控——编排敏感操作时请依赖gate步骤与输入enum白名单做人工/源头约束。【免费下载链接】spec-kit Toolkit to help you get started with Spec-Driven Development项目地址: https://gitcode.com/GitHub_Trending/sp/spec-kit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表