关于 Stratum MCP
Stratum MCP 是面向编码 Agent 的状态机执行引擎,用类型化工作流契约、后置条件检查和可审计运行来约束任务执行。它支持 Claude Code、Codex 及其他 MCP 宿主,并提供人工审批关卡和持久化流程状态。
主要功能
- 01验证类型化 version 1 规范
- 02执行带后置条件的流程
- 03依据结构化失败信息重试
- 04在人工决策关卡暂停
- 05持久化状态并记录审计轨迹
- 06调度 Claude 和 Codex Agent
适用场景
- 01为编码任务强制执行检查
- 02在工作流继续前要求审批
- 03依据违规详情重试失败步骤
- 04跨会话检查执行轨迹
MCP 相关
TypeScript 引擎包含 MCP 服务,提供流程规划、步骤完成和审计工具。它可运行于 Claude Code、Codex 或其他 MCP 宿主。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
26收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| spec | object | 是 |
详细介绍
Stratum
面向 AI Agent 工作流的状态机调度服务。
你的 Agent 提出步骤。Stratum 判断它是否真的完成了。
Stratum 为 AI 编码 Agent(Claude Code、Codex 等)提供正式的执行模型。Agent 不再临时编造计划并盲目重试,而是编写类型化规范,由服务跟踪状态、强制执行后置条件,并在重试时返回结构化的失败上下文。每个步骤都会生成可审计的跟踪记录。
它所处的位置。 Stratum 是执行内核,位于大多数人日常使用的工具之下。 Compose 驱动产品生命周期(设计、蓝图、计划、评审关卡),并调用 Stratum 执行每个步骤。当你只需要状态机和后置条件,而不需要其上的生命周期时,可以直接使用 Stratum。
这套机制背后的创立初衷记录在 docs/VISION.md 中:一种能让大模型在不显眼的约束下遵循轨道的规范语言,让同一段对话比自由形式的执行产生更好的结果。
已发布的组件:
ts/— TypeScript 引擎(@smartmemory/stratum):IR 验证(version: 1规范)、带 ensure 后置条件的流执行、面向 Claude Code 的 MCP 服务、query/gate/guardCLI、后台流和后台 Agent 运行。已作为@smartmemory/stratum发布到 npm(可执行文件:stratum、stratum-mcp),并列入 MCP 注册表,名称为ai.smartmemory/stratum-mcp。
引擎状态(2026-07-18,STRAT-PY-RETIRE): TS 引擎是唯一引擎。 Python 库(
stratum-py)和 Python MCP 服务(stratum-mcp)已停用。 它们的源代码已归档在python-legacy分支中,PyPI 软件包 已冻结在最终版本。该引擎仅执行version: 1规范。validate会拒绝旧版 v0.x 规范,而stratum migrate --check会对其进行分类(仅报告,不修改)。权威的 v1 结构定义见 Zod IR 架构ts/src/ir/以及stratum validate的输出。
可治理、可审计的工作流——适用于任何 Agent,而不局限于某个供应商。 与单一供应商的上下文内编排器不同,Stratum 作为 MCP 服务和库运行于 Claude Code、Codex 或任何 MCP 主机之下;在每次流执行中强制执行类型化契约和 ensure 后置条件;在真正需要人工参与的关卡处暂停;在同一流中调度 Claude 和 Codex Agent(因此,独立评审者可以使用与实现者不同的模型);并跨会话持久保存流状态。如果你需要原始的上下文内扇出,可选择主机内的工作流运行时;如果你需要受治理、可移植、可审计的运行方式,那就是 Stratum 工作流。
目录
- 安装
- 快速开始
- 核心概念
- YAML 规范参考
- MCP 工具 API
- 步骤类型
- Ensures(后置条件)
- 契约和输出验证
- 关卡(人工参与)
- 流组合
- 路由
- 迭代
- 检查点
- 恢复和重试逻辑
- 工作流
- 任务编译器
- Skills
- CLI 参考
- 配置
- Python 库(Track 1)
- 示例
- 开发
- 许可证
安装
从 npm 安装(Node >= 22):
npm install -g @smartmemory/stratum # provides `stratum` (CLI) and `stratum-mcp` (MCP server)
或者在开发时从检出目录运行:
git clone https://github.com/smartmemory/stratum
cd stratum/ts && npm install # or pnpm install
需要 node >= 22(可擦除语法的类型剥离;node >= 24 无需标志——CLI 引导程序会自动控制 --experimental-transform-types)。
MCP 服务(适用于 Claude Code)
在项目的 .mcp.json 中注册该服务。使用 npm 包:
{
"mcpServers": {
"stratum": {
"command": "npx",
"args": ["-y", "-p", "@smartmemory/stratum", "stratum-mcp"]
}
}
}
从代码检出目录使用:
{
"mcpServers": {
"stratum": {
"command": "node",
"args": ["/absolute/path/to/stratum/ts/src/mcp/bin.mjs"]
}
}
}
重启 Claude Code 以激活。也可以选择将 Stratum 执行模型块追加到 CLAUDE.md。
CLI
stratum help # validate | migrate | query | gate | guard | watch (npm install)
node ts/src/cli/bin.mjs help # same, from a checkout
从代码检出目录使用时,可以创建一个指向 ts/src/cli/bin.mjs 的轻量包装脚本(例如 ~/bin/stratum-ts),以避免与已安装的 stratum 二进制文件发生 PATH 冲突。
快速入门
安装 Stratum 后,Claude Code 会自动将其用于非简单任务:
- Claude 在内部编写
.stratum.yaml规范(不会向你显示) - 调用
stratum_plan验证规范并获取第一步 - 使用自身的工具执行每一步(读取文件、编写代码、运行测试)
- 每完成一步后调用
stratum_step_done——服务会检查后置条件 - 如果后置条件失败,Claude 会收到具体的违规信息并重试
- 最后调用
stratum_audit获取完整的执行跟踪记录
整个过程中,你看到的都是通俗易懂的文字说明。规范、状态管理和后置条件校验都会在幕后完成。
核心概念
规范与流程
规范是经过编写并纳入版本控制的 .stratum.yaml 文档。它声明契约和一个或多个流程。flows.entry 字段用于选择启动运行的流程。
流程是由步骤组成的有向无环可执行图。运行入口流程会创建一个持久化运行,并生成 runId。v0.x 顶层的 workflow: 注册块和 stratum_list_workflows 已随 Python 服务一起移除。
流程
流程声明带类型的 input 字段、带类型的 output、可选的限制以及 steps。引用和 after 列表构成数据边和顺序边。门控路由和 on_fail 会添加显式路由边。
步骤
一个步骤包含 id、可选的 after 依赖项、可选的 when 条件,以及且仅有一个构造:do、set、gate、fanout 或 run。
任务
do 步骤是由 Agent 分派的任务。任务文本以内联方式声明,${...} 引用会注入流程输入或先前步骤的输出值。v0.x 的 functions: 注册表和 function: 步骤不适用于 v1 文档。
契约
契约定义具名输出结构。do 或 set 步骤通过 out 声明其输出契约。流程会声明提供结果的步骤输出引用,以及用于验证该结果的契约。
后置条件
后置条件是针对 do、set 和 fanout 阶段结果的结构化约束。V1 支持表达式、文件存在性、文件内容和判定谓词。
重试
do 或 fanout 步骤可以设置正整数 attempts 限制。默认值为两次尝试。契约失败、后置条件失败、任务失败和迭代次数耗尽都会使用相同的失败路径。确定性的 set 失败会终止流程,不会重试。
门控
门控步骤会暂停执行,等待外部作出 approve、revise 或 kill 决策。Approve 和 kill 路由可以指定后续步骤,也可以使用 null。Revise 路由可以指定严格祖先步骤,并且需要设置流程级 max_rounds 限制。
YAML 规范参考
ts/src/ir/ 中的 Zod IR 架构以及 stratum validate 生成的错误信息是权威依据。根对象采用严格校验,且恰好包含三个字段:version、contracts 和 flows。未知字段会被拒绝。
最小示例
version: 1
contracts:
SentimentResult:
label: string
confidence: number
flows:
entry: classify
classify:
input:
text: string
output:
from: "${classify_text.output}"
contract: SentimentResult
steps:
- id: classify_text
do: "Classify the sentiment of ${input.text}"
agent: claude
out: SentimentResult
ensure:
- expr: "result.label != ''"
- expr: "result.confidence > 0.7"
attempts: 2
带门控的完整示例
version: 1
contracts:
WorkOutput:
result: string
quality_score: number
flows:
entry: reviewed_work
reviewed_work:
input:
text: string
output:
from: "${work.output}"
contract: WorkOutput
max_rounds: 3
steps:
- id: work
do: "Produce the deliverable requested in ${input.text}"
agent: codex
out: WorkOutput
ensure:
- expr: "result.quality_score >= 0.8"
attempts: 3
- id: review
after: [work]
gate:
on_approve: null
on_revise: work
on_kill: null
max_rounds: 2
完整字段参考
version(必填)
唯一接受的值是数字 1。带引号的字符串(如 "1")以及所有 v0.x 值都会被拒绝。
contracts(必需)
每个 contract 都将字段名映射到类型字符串。对象在运行时采用严格校验,因此会拒绝未声明的输出字段。
| 类型形式 | 含义 |
|---|---|
string、integer、number、boolean | 标量值 |
object、array | 无类型的 JSON 对象或数组 |
string[]、Result[] | 类型化数组 |
| `draft | final` |
| `(draft | final)[]` |
Result | 另一个命名 contract |
string?、Result[]? | 可选字段 |
命名 contract 引用以大写字母开头。不接受递归 contract 引用和未知的 contract 名称。
flows(必需)
flows.entry 必须指向同一映射中的某个 flow。每个 flow 都包含以下字段:
| 字段 | 必需 | 结构 |
|---|---|---|
input | 是 | 使用 contract 类型语言的字段到类型映射 |
output | 是 | {from: "${step_id.output}", contract: ContractName} |
steps | 是 | 严格 step 对象数组 |
budget | 否 | 为 usd、tokens、dispatches 或 ms 中一项或多项设置正数上限 |
max_rounds | 否 | 当 gate 可以修订到某个祖先时必需的正整数 |
carry | 否 | 由入口 flow 独占的命名循环携带 flow 值 |
output.from 值必须是对具有已知 contract 的 step 输出的完整引用。扇出输出是一个数组,因此 flow 输出必须选择一个元素,例如 ${fan.output[0]}。
步骤
Step ID 以小写字母开头,且只能包含小写字母、数字、下划线和连字符。每个 flow 内的 ID 都必须唯一。每个 step 都接受 id、可选的 after 和可选的 when,然后还必须包含且仅包含以下一种构造,并且只能使用下列字段。
| 构造 | 用途 | 其他字段 |
|---|---|---|
do | 分派内联任务 | agent、out、ensure、attempts、iterate、budget、on_fail |
set | 根据表达式构建输出对象 | 必需的 out,可选的 ensure |
gate | 暂停以等待决策 | 嵌套 gate 对象之外不得包含其他字段 |
fanout | 对数组运行各个阶段 | attempts、budget、on_fail |
run | 调用子 flow | 必需的 with,可选的 budget、on_fail |
agent 可以是 claude 或 codex。attempts 是正整数。iterate 对象要求 max 为正整数,且 until 为字符串表达式。
嵌套的 gate 对象要求包含可为空的 on_approve、on_revise 和 on_kill 字段。它还可以设置正整数 max_rounds。
嵌套的 fanout 对象要求包含 over、一个或多个 steps、正整数 concurrency、值为 worktree 或 none 的 isolation、值为 all、any 或正整数的 require,以及 merge: sequential。可选字段为 pre_merge 和 dispatch,其值可以是 engine 或 consumer。每个 fanout 阶段都必须包含 do,并且可以使用 agent、out、ensure、attempts 和 when。
参考
V1 在任务模板、子流输入、扇出源和流输出中使用 ${...} 引用。
| 模式 | 解析为 |
|---|---|
${input.field} | 流输入字段 |
${step_id.output} | 先前步骤的完整输出 |
${step_id.output.field} | 先前步骤输出中的字段 |
${fan.output[0].field} | 一个扇出项输出中的字段 |
${item} | 扇出阶段任务中的当前项 |
${prev} | 同一扇出项的上一阶段输出 |
${name} | 已声明的传递变量 |
${name.field} | 传递变量中的字段 |
完整值引用会在 with 和 fanout.over 中保留其 JSON 类型。嵌入其他文本中的引用会插值为字符串。步骤输出引用会创建数据依赖关系。对于引用未隐含的排序依赖,请使用 after。
ensure、when、set 和 iterate.until 中的表达式使用 V1 表达式绑定,而非 ${...} 插值。input 是流输入。在 ensure 中,result 是待测试的输出。在 when 和 set 中,result 是已完成步骤 ID 到输出的映射。扇出阶段表达式还会接收 item 和 prev。
Carry
入口流可以声明一个 carry: 块:将传递变量名映射到一个形状,其中包含一个必需的 initial 引用和一个可选的 on_revise 映射。
carry:
wave:
initial: "${plan.output.tasks}"
on_revise:
assess_gate: "${assess.output.tasks}"
initial 和每个 on_revise 值都必须是一个完整的 ${...} 引用,与步骤输出引用完全相同——不得使用插值或表达式语法。initial 源必须是一个无条件、非门控步骤,且不能是任何路由边(on_fail、on_approve 或 on_kill)的目标。每个源纪元只会物化一次:源步骤成功时立即写入该值;如果 revise 重置了源步骤,且该步骤再次成功,则会再次写入。
每个 on_revise 键都指定一个门控步骤 ID。当该门控步骤以 revise 结果结束时,引擎会根据运行的重置前作用域解析所声明的引用,并将结果作为变量的新值写入,同时记录溯源信息(哪个门控步骤、其消耗的令牌、源纪元和轮次)。如果某个门控步骤没有为某变量声明值,则该变量保持不变,因此合并重试会再次对同一列表进行扇出。
在已渲染字段中,只要允许使用步骤输出引用,就允许使用 carry 引用(${name} 或 ${name.field})——包括 do、with、evaluate.in、fanout.over 和扇出阶段的 do;但在六个表达式字段中会被拒绝:when、set、扇出阶段的 when、iterate.until 以及 ensure(流级和阶段级)。Carry 值不会参与 V1 表达式绑定。
Carry 路径不得以完全匹配的 output 段开头——${wave.output} 会与步骤输出语法冲突,因此会被拒绝;${wave.outputs}(或任何其他字段名)则可以使用。
Carry 不会创建依赖边。读取 ${wave} 的步骤需要显式使用 after,确保其排在该变量的 initial 源之后;为某变量声明 on_revise 的门控步骤必须具有一个 revise 目标,其重置闭包涵盖所有读取该变量的步骤。门控步骤本身必须通过 after 或步骤输出依赖排在这些步骤之后——指向该门控步骤的路由边不算作排序依据。
Carry 仅适用于入口流——子流的规范不得声明 carry,且子流内部无法访问 carry 变量。Carry 值会与检查点一起快照保存(stratum_commit / stratum_revert 会连同步骤输出一起恢复这些值),并通过 stratum_audit 暴露。
迁移 v0.x 规范
stratum migrate --check <old.yaml> 是仅生成报告的分类器。它不会输出转换后的 YAML。
| V0.x 构造 | V1 构造 |
|---|---|
带有 infer 或 compute 的 functions,以及 function 步骤 | 内联 do 任务。若步骤中有 agent,则保留;不要将 compute 转换为 set |
| Gate 函数加路由字段 | 嵌套的 gate 对象 |
内联 intent 步骤 | do 任务 |
| 具有一个前提项的确定性谓词或评估谓词 | 对该前提项设置 Ensures |
没有跨项依赖的 decompose | 带有 tasks: T[] 的任务契约,后接 fanout |
depends_on | 数据引用,以及用于其余排序边的 after |
skip_if | 条件取反后的 when |
flow 步骤加 inputs | run 加 with |
max_iterations 加 exit_criterion | iterate: {max, until} |
parallel_dispatch | 重新编写的 fanout |
Pipeline 阶段的 when | Fanout 阶段的 when |
Flow 的 max_rounds | Flow 的 max_rounds |
可归约的 next 路由 | DAG 边和 gate 路由 |
on_fail | 指向拓扑顺序靠后步骤的 on_fail |
| 旧版 ensure 表达式 | 重新编写的 v1 表达式 |
output_schema 或 output_contract | 命名的 v1 契约语言 |
| 依赖路由的 flow 输出 | 一个静态的 output: {from, contract} 生成器 |
部分旧版构造在 v1 中没有对应项,包括已验证或已应用 gate 的评估谓词、评估预算、分数和累加器字段、跨项 decompose 依赖、分支或手动并行合并模式、证书和超时字段、嵌套 pipeline 区域、pipeline exit_when、gate 策略、gate 超时,以及顶层 workflow 块。请展平或重新编写受支持的区域。必须重新设计不受支持的区域,才能在 v1 上运行。
MCP 工具 API
所有工具均通过 MCP 协议公开。Claude Code 会以工具调用的形式调用它们。
stratum_validate
验证 .stratum.yaml 规范,但不创建 flow。
输入: spec(str,内联 YAML)
返回: {valid: bool, errors: list}
stratum_plan
验证规范、创建执行状态,并返回要执行的第一个步骤。
输入:
spec(str)-- 内联 YAMLflow(str)-- flow 名称inputs(dict)-- flow 级输入
返回: 步骤调度对象,包含 status: "execute_step" 或 status: "await_gate",其中包括:
flow_id-- 此次执行的唯一标识符step_id、step_number、total_stepsfunction、intent、inputs(已解析)output_contract、output_fields、ensureretries_remainingagent、step_mode
stratum_step_done
报告已完成步骤的结果。服务端会根据输出架构和 ensure 表达式验证结果。
输入:
flow_id(str)step_id(str)result(dict)-- 符合输出契约的步骤输出
返回以下其中一项:
- 下一个要执行的步骤(
status: "execute_step") - Ensure 失败及重试信息(
status: "ensure_failed"、violations、retries_remaining) - 架构验证失败(
status: "schema_failed"、violations) - Flow 完成(
status: "complete"、output、trace、total_duration_ms) - 重试次数耗尽(
status: "error"、error_type: "retries_exhausted") - 路由至恢复步骤(
routed_from、violations)
stratum_audit
返回 flow 的完整执行轨迹。
输入: flow_id(str)
返回:
flow_id、flow_name、status(complete、in_progress、killed)steps_completed、total_stepstrace-- 步骤记录数组(step_id、function_name、attempts、duration_ms、type、round)round、rounds-- gate revise 周期的轮次历史iterations、archived_iterations-- 迭代历史child_audits-- 子 flow 执行的审计快照total_duration_ms
stratum_gate_resolve
根据人工、Agent 或系统的决策,处理一个 gate 步骤。
输入:
flow_id(str)step_id(str) —— 必须是当前的 gate 步骤outcome(str) ——"approve"、"revise"或"kill"rationale(str) —— 易于理解的原因resolved_by(str) ——"human"、"agent"或"system"
**返回:**下一步骤、工作流完成状态或工作流终止状态。
stratum_commit
保存当前工作流状态的命名检查点。
输入:flow_id (str)、label (str)
stratum_revert
将工作流状态回滚到先前提交的检查点。
输入:flow_id (str)、label (str)
stratum_compile_speckit
将 spec-kit 任务目录编译为 .stratum.yaml 工作流。
输入:tasks_dir (str)、flow_name (str,默认值为 "tasks")
**返回:**成功时返回 {status, yaml, flow_name, steps}。
stratum_resume
将持久化的工作流重新加载到此服务进程中(实时进程重设父级),使新会话可以继续运行并非由其启动的流程。
stratum_flow_run_bg / stratum_flow_poll / stratum_flow_bg_poll / stratum_flow_cancel_bg
分离式后台工作流执行:在后台驱动程序下启动工作流、轮询其进度并取消工作流。即使启动会话结束,流程仍会继续。
stratum_flow_cancel
按流程 ID 取消正在运行的前台流程。与 stratum_flow_cancel_bg 不同,后者会放弃后台运行;此工具会在每次运行的文件锁下将运行状态结算为终态 cancelled,使所有未完成的步骤、门控和扇出项签发失效,然后终止 Stratum 为该流程启动的前台 Agent。
哪些进程可以发起调用。 文件锁让任何进程都能安全调用,但如果某个运行的内存对象被活动驱动程序固定,则只有该驱动程序才能结算此运行。进程主动处理运行时就会发生固定:例如后台驱动程序循环、门控重新触发,或引擎分发使用者扇出。固定该运行的进程会在运行记录旁的驱动程序租约中声明自身;其他进程发起的取消会被拒绝,并返回 CANCELLATION_UNCONFIRMED 和 reason: "engine_dispatch_active",同时提供持有者的 pid,而不会冒险与正在修改该对象的进程发生竞争。若能确定租约所有者已死亡,则会回收该租约并继续取消。未被固定的运行——这是由外部 Agent 驱动的前台使用者扇出的常见情况——任何进程都可以取消。这正是拆分职责的目的:任何地方都可以通过注册表访问使用者扇出的 Agent,而活动驱动程序的内存运行则无法从其他地方访问。
输入: runId(str)
返回: {runId, status, flowSettled, acknowledged, reason?, ledger, agents}。flowSettled 表示运行已被持久化为 cancelled。acknowledged 还表示该流程认领的每个 Agent 组都已达到最终的已解决状态:要么由本次调用拆除(reaped),要么在首次探测时已不存在(gone);并且没有任何条目仍处于未结算、未解决或不可访问状态。
首先读取持久化状态,因此已经处于终态的运行绝不会被修改,也绝不会被拒绝:已完成的运行返回 flowSettled: false 和 reason: "already_<status>";已取消的运行则返回 flowSettled: true 和 reason: "already_cancelled",并仍会清理其 Agent。后一种情况是针对拆除超时的文档化恢复方式,即使原始驱动程序的租约仍处于活动状态,也能使用。
拆除未确认会作为错误处理,而非附带说明:CANCELLATION_TEARDOWN_TIMEOUT(某个组已收到信号,但在截止时间后仍未退出)或 CANCELLATION_UNCONFIRMED(某个条目不可访问、运行锁被持有〔以 reason: "run_lock_held" 和 holderPid 报告〕,或有活动驱动程序持有该运行〔以 reason: "engine_dispatch_active" 报告〕)。结算后引发的错误会携带引擎的实际 status 和 flowSettled,以及部分 agents 摘要。未能进行结算时引发的错误则会携带 flowSettled: false、全为零的 agents 摘要(因为没有尝试拆除),以及 status: "running"。该状态是调用方自己的假设,而非从记录中读取的结果,因为此时尚未加载记录。这样调用方就能区分“流程已停止,但仍有一个组存活”和“我们甚至还没有拿到锁”。
取消会按所有权划分:前台取消会结算流程并终止 Stratum 启动的 Agent;后台取消会放弃运行;使用者自己的进程内 Agent 仍由使用者负责中止,因为 Stratum 无法访问这些 Agent。
stratum_agent_run / stratum_agent_poll / stratum_cancel_agent_run
同步或在后台分发 Claude、Codex 或 Devin。后台运行会返回可用于轮询和取消的持久化 runId。在流程的步骤和扇出阶段中,也可以通过 agent: devin 使用 Devin。引擎分发的工作树阶段会授予 Devin 对其工作树的写入权限;额外的写入根目录来自工作树已提交的 stratum.toml [sandbox]。在 COMP-AGENT-DEVIN-1 添加支持之前,dispatch: consumer 扇出仍不允许在 Compose 中使用 Devin。
Devin 后台运行使用与前台运行相同的每次运行专属主目录和 macOS 沙盒。poll 会先返回来自 stdout.log 的实时过程说明,然后返回 ATIF 导出中的最终答案和令牌用量(usd: 0,针对 SWE-2 的估算值)。在 v1 中,运行结束前,stratum watch 不会显示 Devin 的过程说明;正在运行的 Devin 对等端的 updatedAt 则一直保持为其注册时间。
MCP 后台响应会包含 completionInstructions,其中有实际运行 ID,以及在可用时提供的对等端名称。请立即按此操作:使用 Claude Code 的 SendMessage 并设置 notify_when_idle: true 进行订阅;收到空闲通知后,再调用 stratum_agent_poll 获取报告。仅启动或轮询并不会订阅调用方。如果对等端或 SendMessage 不可用,请在运行期间持续轮询。MCP 工具说明也会介绍这一工作流,因此调用方无需从本 README 中自行发现。
**此订阅步骤存在的原因,以及 Stratum 无法移除它的原因:**MCP 规范支持服务器发起的进度通知(progressToken),而且 Claude Code 确实会在工具调用中发送该令牌——但 Claude Code 目前尚未将其接入可见的任务通知系统;该系统需要客户端支持 MCP“Tasks”模式(SEP-1686)。截至 Claude Code 2.1.272,该支持尚未发布(上游跟踪:anthropics/claude-code#18617、#52137)。已于 2026-09-19 确认:这是 Claude Code harness 的限制,并非 MCP 服务可以通过改变协议使用方式来绕过的问题。Bash run_in_background 能自动发送通知,是因为它是 Claude Code 原生机制,与 MCP 无关。如果未来的 Claude Code 版本发布了 SEP-1686,请重新查看本说明;届时,上述订阅/轮询工作流将不再需要。
**纯 Codex 调度的替代方案(不需要 Stratum 流程/检查点功能):**在 Bash 调用中直接运行 codex exec 并设置 run_in_background: true,确实会收到 Claude Code 的自动通知,无需订阅步骤,因为它使用的是原生 Bash 后台运行路径,而不是 MCP。这只能替代 stratum_agent_run 中普通的 Agent 调度部分——它无法替代与 flow 关联的运行、账本/预算跟踪或对等端注册。操作示例:codex exec -m <model> -c model_reasoning_effort='"<effort>"' -s <read-only|workspace-write|danger-full-access> [-c sandbox_workspace_write.network_access=true] "<prompt>" < /dev/null(必须使用 < /dev/null>——请参阅 Codex 连接器部分关于 stdin 停滞的说明)。不带 -s 标志的裸 codex exec 默认使用 danger-full-access;请务必显式传入该标志。
Codex、Claude 和 Devin 后台运行会在会话注册表可用时注册一个 Claude Code 对等端条目(codex-<model>-<runId6> · bg · busy、claude-<model>-<runId12> · bg · busy 或 devin-<effort>-<runId6> · bg · busy)。尝试注册时,响应会包含 peerName;注册成功后,轮询会报告 peer.registered: true。订阅 SendMessage(to=peerName, notify_when_idle=true),即可在运行结束时收到空闲通知。如果找不到对等端,请改用 stratum_agent_poll。空闲条目会保留 15 秒,然后清理;peer.registered 表示注册成功,而不是当前套接字是否可用。在默认的 exec 策略下,由于运行的提示词在启动时已固定,因此会拒绝传入的用户消息。使用下文所述的 app-server 策略时,Codex 后台运行可以接收这些消息。设置 STRATUM_PEER_REGISTER=0 可禁用注册,设置 STRATUM_PEER_LINGER_MS 可更改保留时间。STRATUM_PEER_SESSIONS_DIR 和 STRATUM_PEER_SOCK_DIR 可覆盖注册表目录和套接字目录;否则,注册表目录遵循 CLAUDE_CONFIG_DIR(默认为 ~/.claude),套接字目录默认为 /tmp/cc-socks。此集成遵循 Claude Code 2.1.272 的对等端协议;如果某个活动记录声明了更新的协议版本,则会禁用注册,但不会影响运行。
向正在运行的 Codex 后台运行发送消息(STRATUM_CODEX_BG_STRATEGY=app-server,选择启用)。 默认情况下,Codex 后台运行使用 codex exec,启动后没有输入通道。在 MCP 服务的环境中设置 STRATUM_CODEX_BG_STRATEGY=app-server,即可改用 codex app-server 执行 Codex 后台轮次。此时,发送到该运行对等端名称的 SendMessage 会以引导消息(turn/steer)的形式传递给活动轮次,模型会在同一轮次中收到消息。前台运行和 STRATUM_CODEX_TRANSPORT 不受影响,注入的 command 始终使用 exec。
- 如实报告送达状态。 Codex 接受针对活动轮次的引导消息后,发送方才会收到
delivered。服务器拒绝的消息,或结果无法确定的消息,会报告为已丢弃或未知,且不会重试。向已结束的运行发送消息会被拒绝(expired)。即使发送失败,消息也不会终止运行。 - 只有运行的所有者可以引导运行。 引导帧必须在第一帧中通过运行的对等端令牌进行身份验证。
notify_when_idle订阅的工作方式与exec运行完全相同,运行结束时也只会收到一条空闲通知。 - 沙盒和审批设置与
exec一致。 沙盒模式、可写根目录和网络访问权限的传递方式与exec相同,因此 Codex 配置中的设置(例如临时目录排除项)也会以相同方式生效。此策略绝不会授予exec不会授予的额外权限。approvalPolicy: "on-failure"没有对应的 app-server 设置,因此在启动任何操作之前会拒绝这一组合。由于没有人可以响应审批提示,运行会拒绝这些提示并继续。 - 进程所有权。 独立运行的驱动程序拥有 app-server 进程,即使 MCP 服务退出也会继续运行。
stratum_cancel_agent_run会同时停止驱动程序和 app-server。如果驱动程序被终止,轮询会报告错误,且 app-server 会在其输入关闭时退出。启动独立进程组的工具进程不在此清理范围内;exec也有相同限制。 - 版本固定。 app-server 协议固定为 Codex CLI 0.155.1。如果安装的版本不同,运行会在初始握手期间因不支持的版本而失败,而不会冒险使用不匹配的协议。如果使用其他 Codex 版本,请采用默认的
exec策略。 每个 Claude 后台工作线程(MCP 服务进程内的线程,而非独立的操作系统进程)都有自己的分离式对等端 sidecar,与 Codex 使用的每次运行一个进程的模式相同——工作线程没有自己的 pid 可供注册,因此由 sidecar 提供。sidecar 通过专用 IPC 通道获知工作线程已结束,而不是监视子进程 pid;如果工作线程在 MCP 服务仍运行时结束,会正常报告为闲置状态;如果整个 MCP 服务丢失(而非仅丢失一个工作线程),则会显示为unavailable,而不会错误地显示为闲置状态。Claude 工作线程不会在 MCP 服务重启后继续运行——它们没有可重新连接的分离进程,这一点不同于独立运行的 Codex 子进程。
在 stratum_agent_run 上传入可选的 peerLabel(仅适用于后台运行,任一 Agent 均可;前台调用会拒绝此参数),即可获得易读名称,而不是单独的运行 ID:对于名为 claude-sonnet-5-5-<runId12> 的 Claude 运行,设置 peerLabel: "schema review" 后会得到 claude-sonnet-5-5-<runId12>-schema-review。标签会经过规范化处理(转换为小写,并将连续的非字母数字字符合并为 -),它仅是显示提示,而非地址——订阅时务必使用响应返回的 peerName,而不是传入的标签。
前台 Agent 运行可在 cancellationId 旁声明 flow: {runId, stepId?, itemIndex?}。这样会为该运行生成一条持久记录,其中包含其子进程 pid 和进程启动时间,因此 stratum_flow_cancel 才能从另一个进程终止其进程组。若没有 cancellationId,则会拒绝 flow:没有受其管理的进程组,就无从取消。
要确认前台取消操作,请在 stratum_agent_run 上提供一个新的 UUID 作为 cancellationId,然后使用该 UUID 作为 runId 调用 stratum_cancel_agent_run。取消响应会等待连接器完成拆除。还应继续等待原始运行响应;取消错误会在该响应中报告。MCP 传输取消和断开连接也会中止前台工作,但 MCP 取消通知会抑制原始响应,且不会确认拆除已完成。前台 ID 的作用域仅限当前服务进程;最近结束的 ID 会返回 already_complete、already_error 或 cancelled。
allowedTools、disallowedTools 和 thinking 仅适用于 Claude。effort 接受 Claude 的 low、medium、high、xhigh 或 max,也接受 Codex 的 minimal、low、medium、high 或 xhigh。不受支持的提供商设置会在执行前报错。Codex 使用 sandboxMode(read-only 或 workspace-write);显式指定的 effort 必须与模型后缀一致。可取消的 Codex 运行会在 Codex CLI 外层创建由本次运行管理的进程组,以便在确认取消前停止 shell 后代进程;优先使用 PATH 中的 CLI,找不到时则回退到 SDK 内置的 CLI。Claude 使用 SDK 的自定义 spawn 钩子来实现相同的进程管理。只有提供 cancellationId 时,才会声明进程组的所有权,因此普通运行不受影响。此功能需要 POSIX 进程组:在 Windows 上,要求取消的运行会在启动前报错,错误码为 CANCELLATION_UNSUPPORTED_PLATFORM;不可取消的运行不受影响。主动脱离 Agent 进程组的分离进程不在此取消约定的范围内。
已通过身份验证的 Codex 在线冒烟测试为可选项:在 ts/ 目录下运行 STRATUM_LIVE_CODEX=1 npm test -- tests/connectors/codex.live.test.ts。普通测试运行不会调用付费模型。
权威工具接口位于
ts/src/mcp/server.ts。已退役而非移植的 Python 时代工具(并行生命周期、迭代、计时器/跳过、list_workflows、draft_pipeline、评审/目标接口)位于python-legacy;各工具的处理方式请参阅docs/plans/2026-07-11-strat-py-retire-progress.md中的第 2/3 阶段使用情况审计。下方的 distill 暂存管线是一个 TS 移植版本(STRAT-DISTILL-TS-1),并非已退役的接口之一——只有其应用/写入路径尚未移植。
stratum_distill
检测工作区下 Claude Code 转录记录中的重复工作流,并暂存技能/子 Agent/命令候选草稿——它绝不会直接写入 skills/、agents/ 或 commands/。必需参数:workspace_root。可选参数:project_dir(默认为当前转录记录的项目目录)、min_count、window_days 和 write(默认为 false——当其为 false 时,工具只报告将要暂存的内容;设置 write: true 可将候选内容实际追加到 sidecar 中)。MCP 没有 CLI 的 --all/--projects-root 扫描功能的等效项;MCP 工具每次调用始终只针对一个项目目录。
每个候选项都是完整且可供人工审核的草稿:完整渲染的 SKILL.md/agent/*.md/command/*.md 内容、据以构建的已观察工作流(单个工具或工具序列)、可解析的证据(带源句柄的转录记录出现项),以及确定性的 revisionId——相同输入始终生成相同候选项,新证据则会生成新的修订版本,而不是修改旧版本。候选项会暂存到独立的 .stratum/distill/ sidecar(架构为 distill-2.0,与 learn-1.0 MEMORY 补丁 sidecar 分离),并且绝不会自动安装:没有任何机制会在未经人工决定的情况下读取或应用暂存候选项,目前也完全没有允许候选项进入 skills//agents//commands/ 的门槛——这是未来的工作(STRAT-DISTILL-APPLY、STRAT-ADMIT),并非此工具的功能。
重复出现的工作流必须在至少 2 个不同会话中出现至少 min_count 次(默认值为 2)——只使用过一次的工作流,即使在一个会话中反复使用,也绝不会暂存。这种设计有意与 MEMORY 补丁收集器(ts/src/learn/)的保守策略保持一致。
CLI 等效命令:stratum distill <extract|top|stats> [--root <path>] [--project <path>] [--all --projects-root <path>] [--min-count N] [--window-days N] [--json]。extract 是唯一会暂存候选项的子命令(始终会写入;没有单独的 --write 标志);top [--n N] 会查看按重复次数排列的顶级工作流,但不会暂存任何内容;stats 会报告单次出现/序列的数量——这两者都会让 applied: false 保持不变。--root 是候选项的暂存位置(你的仓库);--project 是读取 Claude Code 转录记录的位置——它们是不同的目录,不能互换。--all --projects-root <path> 会一次性扫描根目录下的每个嵌套项目目录,而不是逐个指定 --project。
STRAT-GUARD —— 流程之外的受保护转换
授权模型。 三种权限逐级增强的策略变更能力:
stratum_guard_upgrade无需授权,因为它已证明不会削弱策略;stratum_guard_apply_upgrade会应用经过签名且预先审查的描述符;stratum_guard_migrate可执行任何操作,但需要经过签名的一次性授权。此外还有stratum_guard_override,它可绕过一条合法边上的谓词验证,同样需要经过签名的一次性授权。所有签名密钥均登记在contracts/guard-signers.allowed中,并从已安装的源代码树读取,绝不从环境中读取。该文件默认为空:没有默认信任。
stratum_judge/stratum_gate_resolve 仅在流程内实施保障。STRAT-GUARD 将同一独立验证引擎(run_judge)作为独立于资源的防篡改状态机提供给客户端,以管理分层流程之外的资源生命周期(例如 compose 的功能跟踪器)。客户端注册一个带有逐边证据谓词的转换图;随后,stratum 仅在转换边合法且其谓词依据受信任的服务端读取证据(而非调用方预先提交的声明)验证通过时才允许转换。每次尝试——无论成功应用还是被拒绝——都会追加到哈希链式账本中。参见 docs/features/STRAT-GUARD/。
stratum_guard_register
注册受保护的资源。(graph, edge_predicates, terminal, stakes) 策略会经过校验和验证且不可变——重新注册相同策略时不执行任何操作;不同策略则会被拒绝(若要进行增量变更,请使用 stratum_guard_upgrade;其他情况请使用 stratum_guard_migrate)。
输入: resource_id(str,客户端命名空间,例如 "compose:FEAT-1")、graph(dict[from -> list[to]])、edge_predicates(dict["from->to" -> list of {id,type,statement}])、initial(str)、terminal(list[str])、stakes(dict["from->to" -> "cheap"|"default"|"paranoid"])、workspace_root(绝对目录,用于文件/Git/命令证据)。
谓词 type:"deterministic" → 服务端可信证据(server_file_exists/git_commit_exists/command_exit_zero/verdict_receipt_clean);"verified"/"judged" → 大模型层级,通过 run_judge 路由。paranoid 边必须声明至少一个可信谓词。
返回: {guard_id, checksum, status}。
stratum_guard_transition
尝试执行 from_state -> to_state。可信谓词在服务端验证;任何大模型层级谓词均按该边的 stakes 通过 judge 处理。乐观并发控制:评估在每个资源对应的锁外执行,提交时会在锁内重新验证状态。
输入: resource_id、from_state、to_state、artifacts(dict[str,str])、modified_files(list[str])、idempotency_key(str|None)、resolved_by(str)。
返回: {status: applied|refused|replayed, verdict: JudgeResult-dict, ledger_ref, current_state}。ledger_ref 是提交给 verdict_receipt_clean 的回执令牌。
stratum_guard_override
唯一获准绕过谓词验证的方式。需要签名授权、人工处理者和理由。无需验证谓词即可迁移一条合法边,并写入一条注明签署者的 deviation 账本记录。取代 force 标志。
签名针对命名空间 stratum-guard-override 下的规范 JSON {action, resource_id, from_state, to_state, rationale, ledger_head}。服务器会自行重建该载荷,因此传输的只有签名;而且由于载荷包含资源当前的账本头,一份授权仅在该资源历史中的一个确切时点有效。使用后,账本头会发生变化,签名随即失效,因此无法重放,也无需调整有效期。
stratum guard authorize 会打印待签名的准确载荷:
echo '{"kind":"override","resource_id":"…","from_state":"…","to_state":"…","rationale":"…"}' \
| stratum guard authorize
# then: ssh-keygen -Y sign -f <key> -n stratum-guard-override <payload file>
已取代
STRATUM_GUARD_OVERRIDE_TOKEN(2026-08-17)。 该令牌会与正在运行的任意进程的环境变量进行比对,因此通过 CLI 调用时,调用者可以设置比对双方,且两者会匹配——我们已通过实证验证,即使守卫的谓词永远无法满足也会如此。签名不依赖发起请求的进程。详见docs/features/STRAT-GUARD-AUTHZ/design.md。
输入: resource_id、from_state、to_state、authorization、rationale、resolved_by("human")。返回: 添加 authorized_by。
stratum_guard_migrate
以任意方式演进已注册的策略——这是紧急处理路径。需要一份签名授权,其载荷中包含迁移后的策略校验和,因此不能将授权用于其他迁移:命名空间 stratum-guard-migrate 下的 {action, resource_id, policy_checksum, rationale, ledger_head}。此操作会递增 graph_version,写入一条注明签署者的 graph_version 账本记录,并且绝不会悄然放宽正在处理的资源所用的策略。当前状态必须仍是新图中的一个节点。
不采用签名描述符作为替代方案,这是有意为之:需要经过审查的产物的机制,不能成为产物本身损坏时的应急手段。
输入: resource_id、new_graph、new_edge_predicates、authorization、rationale、new_terminal、new_stakes。返回: 添加 authorized_by。
stratum_guard_upgrade
常规策略演进,不需要覆盖令牌。具有幂等性:若策略校验和已匹配,则返回 unchanged,且不写入任何内容——没有账本记录,也不递增 graph_version——因此可以安全地对数百个守卫重复运行按资源惰性迁移。任何其他变更都必须是仅增量:不得移除节点或边;现有边的谓词和风险等级必须逐字节相同;新增边只能以之前不存在的状态为终点;并且必须完全按照注册时的状态冻结 terminal,不得新增任何进入或离开终止状态的边。因此,它既不能新增完成方式,也不能将已完成的资源退回之前的状态——这两项仍由 stratum_guard_migrate 的授权决策处理。其他任何变更都会因 incompatible_policy_upgrade 而被拒绝。账本记录与 migrate 写入的 graph_version 类型相同,并以 resolved_by: "agent" 区分。
输入: resource_id、new_graph、new_edge_predicates、rationale、new_terminal、new_stakes。返回: {status: "migrated"|"unchanged", checksum, graph_version, ledger_ref?, rationale}。
stratum_guard_apply_upgrade
应用由服务器管理的升级描述符:由人工审核并安装、以 ID 命名的策略变更。无需覆盖令牌。这是三种能力中的中间一种——它可以执行 stratum_guard_upgrade 拒绝的操作(授予终止状态、移除边、收紧谓词),因为确切的结果策略已提前获得授权,而不是由调用方提供。
授权依据是签名,而非环境中的值。描述符文件必须在 <path>.sig 处附带一个分离式 sshsig,命名空间为 stratum-guard-descriptors,且签名密钥必须已登记在 contracts/guard-signers.allowed 中——该文件从已安装的源代码树读取,绝不会从环境变量读取。STRATUM_GUARD_UPGRADE_DESCRIPTORS 仍用于指定文件路径,因为定位制品并不等于授权它。
用只有你自己知道的密码短语保护私钥部分,并且绝不要将它添加到 ssh-add——任何能够访问你的 Agent 套接字的对象,都可以在不知道密码短语的情况下使用它。使用以下命令签名:
ssh-keygen -Y sign -f ~/.stratum/guard-signing -n stratum-guard-descriptors /path/to/guard-upgrades.json
contracts/guard-signers.allowed 初始为空:默认不信任任何密钥;信任根为空或缺失时,这些路径会报告自身不可用,而不会降级运行。验证由原生机制(node:crypto)完成,不会通过 shell 调用 ssh-keygen——该二进制文件通过 PATH 查找,而授权决策不得委托给调用方可以替换的程序。运行 stratum guard descriptors,查看已安装的描述符、签署者以及验证结果。
每个描述符包含 {id, rationale, from_checksum, to_policy},且仅适用于当前策略校验和恰好等于 from_checksum 的资源——授权针对的是两个指定策略之间的变更,而非抽象的目标策略。操作具有幂等性:资源已处于目标状态时会返回 unchanged,且不会写入任何内容,因此可以安全地重复运行整个集群范围的批处理。账本条目会记录描述符 ID 和文件摘要。
仅限 MCP,这是刻意设计的——没有 stratum guard apply-upgrade CLI 操作。CLI 进程会继承调用方的环境,因此调用方可以将两个变量都指向自己编写的描述符文件,从而自行伪造授权。特权应用操作仅在拥有固定环境的服务内部运行。(只读的 stratum guard descriptors 检查仍由 CLI 执行:它不会授予任何权限。)
输入:resource_id、descriptor_id。返回:{status: "applied"|"unchanged", checksum, graph_version, ledger_ref?, descriptor_id}。
stratum_guard_history
返回资源的当前状态,以及其仅追加、哈希链式的转换/偏差账本(防篡改审计轨迹)。
输入:resource_id。返回:{resource_id, current_state, graph_version, ledger: [...]}。
步骤类型
V1 有五种互斥的步骤构造。本示例包含全部五种:
version: 1
contracts:
Analysis:
summary: string
score: number
ItemResult:
value: string
Final:
message: string
flows:
entry: main
main:
input:
topic: string
items: string[]
output:
from: "${finish.output}"
contract: Final
steps:
- id: analyze
do: "Analyze ${input.topic}"
agent: claude
out: Analysis
- id: normalize
after: [analyze]
set:
summary: "result.analyze.summary"
score: "result.analyze.score"
out: Analysis
ensure:
- expr: "result.score >= 0"
- id: review
after: [normalize]
gate:
on_approve: null
on_revise: null
on_kill: null
- id: inspect
after: [review]
fanout:
over: "${input.items}"
concurrency: 2
isolation: none
require: all
merge: sequential
steps:
- do: "Inspect ${item}"
agent: codex
out: ItemResult
- id: child
after: [inspect]
run: summarize
with:
topic: "${input.topic}"
- id: finish
after: [child]
do: "Finalize ${child.output.message}"
out: Final
summarize:
input:
topic: string
output:
from: "${write_summary.output}"
contract: Final
steps:
- id: write_summary
do: "Summarize ${input.topic}"
out: Final
do 任务
do 包含发送给 Agent 的任务文本。agent 默认为 claude。out 指定结果契约。任务还可以声明 ensure、attempts、iterate、budget 和 on_fail。
set 步骤
set 会计算每个字段表达式,而不分派 Agent。生成的对象会先根据必需的 out 契约进行验证,再根据任何 ensure 条件进行检查。Set 步骤具有确定性,不会重试。
gate 步骤
gate 会暂停运行,直到收到外部决策。它的三个路由字段都是必需的,但其中任何一个都可以是 null。Gate 步骤不能包含任务、重试、预算或 ensure 字段。
fanout 步骤
fanout.over 必须是一个完整引用,且解析结果为数组。每个项目都会依次经过声明的阶段。${item} 是源项目,${prev} 是上一阶段的输出。如果后续步骤或流程输出引用 fanout 结果,则最后一个阶段需要 out。
isolation: worktree 会为每个项目创建一个 Git 工作树。merge 仅接受 sequential。require 决定合并前需要多少个项目成功。dispatch 默认为 engine。由 consumer 分发的工作树 fanout 有额外的验证规则,包括必须有一个无条件后继门控步骤。
run 步骤
run 用于指定同一规范中的另一个流程。必须提供 with,且其键必须与被调用流程的输入键完全匹配。run 步骤的输出契约继承自被调用流程。非入口流程不能包含 run 或 fanout,且不允许递归调用。
模式互斥
每个步骤必须且只能包含 do、set、gate、fanout 或 run 之一。不能将一种结构的字段与另一种结构混用。
保证条件(后置条件)
ensure 数组包含严格的谓词对象。谓词按顺序运行,首次失败时停止求值。
| 谓词 | 形式 |
|---|---|
| 表达式 | {expr: "result.confidence > 0.7"} |
| 文件存在 | {file_exists: "build/report.json"} |
| 文件包含文本 | {file_contains: {path: "build/report.json", text: "passed"}} |
| 判定声明 | {judged: {statement: "result is accurate", stakes: "default"}} |
判定所用的风险级别为 cheap、default 或 paranoid。如果未配置判定器运行器,判定谓词将默认判定失败。文件谓词需要工作区根目录,且访问范围限制在该根目录内。
表达式语法
v1 求值器是一种受限表达式语言,并非 Python。它支持 JSON 字面量、成员访问、非负字面量数组索引、函数调用、括号、一元 ! 和 -、算术运算、比较运算、in、&& 和 ||。
在 ensure 中,result 是步骤输出,input 是流程输入。Fanout 阶段的 ensure 还会接收 item 和 prev。
内置函数
| 函数 | 签名 | 描述 |
|---|---|---|
len | len(value) | 字符串、数组或对象的长度 |
any、all | any(array)、all(array) | 对数组进行真假值测试 |
max、min | max(array)、min(array) | 最大或最小的数字或字符串 |
str、int、bool | 一个参数 | 受限类型转换 |
matches | matches(text, pattern) | 有界正则表达式匹配 |
file_exists | file_exists(path) | 检查工作区根目录内的文件 |
file_contains | file_contains(path, text) | 检查工作区根目录内的文件内容 |
安全性
- 只能绑定
result、input、item和prev。 - 禁止访问
__proto__、constructor和prototype成员。 - 对解析、嵌套、正则表达式大小和正则表达式输入设有限制。
- 文件辅助函数不能越出配置的工作区根目录。
失败行为
ensure 失败时会记录结构化的失败原因。只要 attempts 预算尚未用完,就会重新分发 do 任务。如果输出证据相同,则会提前停止重试,因为相同的确定性谓词不会改变判定结果。
契约与输出验证
规范中的契约
契约使用 YAML 参考中定义的字段到类型字符串语言。任务输出会在运行 ensure 前根据 out 进行验证。被引用的生产者完成后,流程输出还会再次根据 output.contract 进行验证。
set 必须提供 out。仅当没有后续引用或流程输出需要该结果的契约时,do 步骤才可以省略 out。
输出架构(每步 JSON Schema)
V1 不接受每步的 output_schema 或 output_contract。请将受支持的字段移入命名契约,并通过 out 引用。v1 契约语言支持上述类型,但并非任意 JSON Schema。
Gate(人在回路中)
Gate 是审批检查点,会暂停流程执行,直到由外部解决。
定义 Gate
如完整的 Gate 示例所示,直接在步骤上声明 Gate。v1 中没有 Gate 函数声明。
Gate 限制
on_approve、on_revise和on_kill都是必填项,且允许为 null。- 命名的 approve 和 kill 目标必须保持路由图无环。
- 可用的 revise 目标必须是 Gate 的严格祖先。
- 任何非 null 的 revise 目标都要求在流程级别设置
max_rounds。 - Gate 级别的
max_rounds可以为该 Gate 设置更严格的正数上限。 - Gate 步骤只接受通用字段和嵌套的
gate对象。 - 如果 Gate 为某个 carry 变量声明了
on_revise,那么在重置前,Gate 处的 revise 会重写该变量。如果 Gate 未声明某个变量,则该变量保持不变,因此合并重试时的 revise 会对同一列表重新扇出。
解决
通过 stratum_gate_resolve 解决 Gate,并提供 runId、stepId、decision 和观察时的 gateToken。该令牌用于阻止过期决策。CLI 使用 approve、revise 和 reject,其中 reject 会映射到引擎的 kill 决策。
- approve 路由到
on_approve;如果路由为null,则完成该 Gate。 - revise 会重置受影响的后代区域,并在轮次限制允许时路由到
on_revise。 - kill 路由到
on_kill;如果路由为null,则终止运行。
v1 未移植 Gate 策略、策略回退和 Gate 超时。请明确解决每个等待中的 Gate。
轮次
每次成功的 revise 都会递增流程轮次计数器和 Gate 的本地修订计数器。再次开始修订前会强制执行流程上限和可选的 Gate 上限。
流程组合
带有 run 的步骤会调用同一规范中定义的子流程。只有在子流程生成并验证其输出后,父步骤才会完成。
version: 1
contracts:
TestResult:
all_passed: boolean
DeployResult:
status: string
flows:
entry: deploy
integration_tests:
input:
project: string
output:
from: "${run_tests.output}"
contract: TestResult
steps:
- id: run_tests
do: "Run integration tests for ${input.project}"
out: TestResult
ensure:
- expr: "result.all_passed == true"
deploy:
input:
project: string
output:
from: "${release.output}"
contract: DeployResult
steps:
- id: test
run: integration_tests
with:
project: "${input.project}"
- id: release
after: [test]
do: "Deploy after test result ${test.output.all_passed}"
out: DeployResult
with值可以包含嵌套字面量和${...}引用。with键必须与被调用方的输入字段完全匹配。- run 步骤会继承被调用方的输出契约。
- run 步骤可以设置
budget和on_fail。 - 递归调用会被拒绝。
- 非入口流程不能包含其他
run或fanout。
路由
on_fail 恢复路由
当任务、fanout 或子流程用尽失败路径后,on_fail 会激活一个命名的恢复步骤。该目标必须位于已验证 DAG 的后续位置。
version: 1
contracts:
TaskResult:
ok: boolean
details: string
flows:
entry: build
build:
input:
spec: string
output:
from: "${publish.output}"
contract: TaskResult
steps:
- id: generate
do: "Generate code for ${input.spec}"
out: TaskResult
attempts: 2
on_fail: recover
- id: verify
after: [generate]
when: "result.generate.ok == true"
do: "Verify the generated code"
out: TaskResult
- id: recover
do: "Repair generation failure"
out: TaskResult
- id: publish
after: [verify, recover]
do: "Publish the final result"
out: TaskResult
如果来源步骤成功,则只用于路由的恢复步骤会被跳过。下游步骤可以同时依赖正常分支和恢复分支,因为被跳过的依赖项仍满足排序边的要求。
成功路由
v1 没有 next 字段。成功后会沿由 after 和步骤输出引用构成的 DAG 继续执行。Gate 提供明确的 approve 和 kill 路由。不支持向前跳转的成功路由。
条件跳过
when 是 v1 的正向表达式。仅当表达式计算结果为 true 时,步骤才会运行。任何其他结果都会跳过该步骤。迁移 skip_if 时,请反转条件。被跳过的步骤没有输出,因此无法解析后续对该步骤的数据引用。
迭代
iterate 会重复执行 do 任务,直到满足其谓词或达到正数上限 max。
version: 1
contracts:
Draft:
text: string
quality: number
flows:
entry: refine
refine:
input:
brief: string
output:
from: "${improve.output}"
contract: Draft
steps:
- id: improve
do: "Improve the draft for ${input.brief}"
out: Draft
iterate:
max: 5
until: "result.quality >= 0.95"
引擎会在契约和确保条件通过后评估 until。如果结果为 false,则记录此次尝试并重新派发任务。如果输出重复且完全相同,则会提前停止。达到最大次数后会进入常规失败路径,包括 on_fail 路由。
已移除的迭代字段
V0.x 的 score_expr、accumulate 和 accumulate_key 在 v1 中没有对应项。v1 也没有单独的迭代生命周期工具。迭代属于常规任务派发和结果报告的一部分。
检查点
检查点是运行时操作,不是 YAML 结构。stratum_commit 接受 flow_id 和非空的 label。stratum_revert 接受相同字段,并会重新推进已恢复的运行。
{"flow_id": "<run-id>", "label": "after_analysis"}
检查点会捕获运行状态、输出、失败信息、已用预算、轮次、步骤状态、审计事件、取消状态和并行状态。它不会快照规范、输入、工作区文件、运行身份或检查点列表本身。提交和回滚仅操作状态。文件级回滚由调用方负责。
检查点与持久化运行一同存储,并能在服务重启后保留。重复使用某个标签会替换其快照,同时保留该标签原先的插入位置。
恢复和重试逻辑
重试预算
do 任务和扇出步骤使用 attempts,默认值为 2。扇出阶段可以覆盖外围扇出的尝试次数上限。set 是确定性的,不会重试。run 不接受 attempts 字段。
重试流程
- 执行器使用当前调度令牌报告任务结果。
- 引擎根据
out验证结果。 - 引擎按声明顺序评估 ensures。
- 配置了迭代时,引擎会评估
iterate.until。 - 只要仍有剩余尝试次数且证据发生变化,失败的任务就会重新调度。
- 尝试次数用尽时,如果配置了
on_fail,则激活该项;否则流程失败。
持久化
每次状态变更后,流程状态都会持久化到 ~/.stratum/ts/flows/{runId}.json。MCP 服务重启后,运行仍会保留。
服务重启后的恢复
引擎会从磁盘加载缺失的运行,重新验证持久化的规范,并从持久化的 DAG 状态恢复。调度令牌和门控令牌可防止过期工作修改较新的签发状态。
工作流
V1 没有顶层 workflow 声明,也没有发现注册表。文件名应便于人们识别,并通过 flows.entry 选择可执行的入口流程。原先的代码审查工作流将变为普通的 V1 规范:
version: 1
contracts:
ReviewResult:
summary: string
flows:
entry: code_review
code_review:
input:
files: string[]
depth: string?
output:
from: "${review.output}"
contract: ReviewResult
steps:
- id: review
do: "Review ${input.files} for security, logic, and performance"
out: ReviewResult
V1 输入契约支持可选字段,但不支持默认值。调用方会在规划运行前提供默认值。
任务编译器
任务编译器会将 spec-kit 任务文件(tasks/*.md)转换为 .stratum.yaml 流程。
任务文件格式
# Task: [P] Implement authentication
Add JWT-based authentication to the API.
## Acceptance Criteria
- [ ] file src/auth/middleware.ts exists
- [ ] file src/auth/middleware.ts contains "verifyToken"
- [ ] tests pass
- [ ] no lint errors
- [ ] Error messages are user-friendly
编译规则
- 标题中的
[P]表示任务可并行 file X exists会编译为file_exists("X")file X contains Y会编译为file_contains("X", "Y")tests pass会编译为result.tests_pass == Trueno lint errors会编译为result.lint_clean == True- 自由格式的标准会纳入步骤的
intent
依赖图
- 顺序任务依赖于前一个任务
- 并行任务(
[P])共享同一个前置任务,彼此之间没有边 - 并行组之后的下一个顺序任务依赖于该组中的所有任务
用法
通过 MCP 工具:stratum_compile_speckit(tasks_dir, flow_name)。
技能
这 11 个 /stratum-* 技能随 Python stratum-mcp install 命令一同发布并安装;该命令现已停用——这些技能的源文件已归档在 python-legacy
(stratum-mcp/src/stratum_mcp/skills/)。已安装到 ~/.claude/skills/ 下的技能仍可正常工作(它们会调用 TS 服务端提供的相同 stratum_plan / stratum_step_done / stratum_audit
工具)。目前还没有 TS 安装程序。
CLI 参考
两个可执行文件都通过 checkout 中的 node 运行:
node ts/src/mcp/bin.mjs # Start stdio MCP server (for Claude Code)
node ts/src/cli/bin.mjs <command> # aka `stratum` via a wrapper script:
stratum validate <file> # Validate a .stratum.yaml spec (version: 1)
stratum models --json # Shipped catalog, SHA-256 digest, absolute file path, package version
stratum migrate --check <file> # Report-only classification of a legacy v0.x spec
stratum query flows # List all persisted flows (JSON)
stratum query flow <id> # Full state for a single flow (JSON)
stratum query gates # List all pending gate steps (JSON)
stratum gate approve <flow_id> <step_id> [--note "reason"]
stratum gate reject <flow_id> <step_id> [--note "reason"]
stratum gate revise <flow_id> <step_id> [--note "reason"]
stratum guard <action> # Guard ledger operations (see STRAT-GUARD)
stratum flow cancel <flow_id> # Cancel a running foreground flow (exit 0 ok, 1 unconfirmed, 2 unknown flow)
stratum watch <flow_id> # Follow a flow's progress
stratum help # Show usage
配置
流程状态存储
持久化流程存储在 ~/.stratum/flows/{flow_id}.json 中。此目录会自动创建。
SmartMemory 回执导出
SmartMemory 回执导出默认关闭,即使已提供凭据也是如此。这与之前“提供凭据后自动开启”的行为相反。要选择启用,请设置以下全部四个必需变量:
STRATUM_LEARN_EGRESS=1
SMARTMEMORY_API_URL=https://memory.example
SMARTMEMORY_API_KEY=...
SMARTMEMORY_WORKSPACE_ID=...
启用后,Stratum 会将其持久化的用量、步骤重置和检查点回滚回执镜像到 SmartMemory。必须提供限定范围的 X-Workspace-Id 标头;如果缺少 SMARTMEMORY_WORKSPACE_ID,导出功能将拒绝启用,并发出一次警告。启用投递前,请先在 SmartMemory 服务环境中注册这三种记录类型:
SMARTMEMORY_EXTRA_MEMORY_TYPES=stratum_usage_debit:append:false,stratum_step_reset:append:false,stratum_checkpoint_reverted:append:false
投递采用至少一次语义。每一行都带有稳定的 metadata.receipt_id,格式为 <run_id>:<sequence>;使用者必须按此值去重,因为成功响应丢失可能导致重试时在 SmartMemory 中创建第二个条目。
stratum learn egress drain [--run <id>]
stratum learn egress verify --run <id>
stratum learn egress retry-dead --run <id>
由于 SmartMemory 没有精确列表 API,verify 会为每个本地回执执行一次精确搜索;仅当缺少预期类型时,才会探测另外两种类型。因此,网络请求量为 O(n)。它会报告缺失、重复、类型错误和失效回执的数量。retry-dead 会将因终端 HTTP 状态而被拒绝的行放回待处理队列,并再次排空该次运行的队列。
钩子
Python 安装程序的会话钩子(~/.stratum/hooks/)已随之停用;其源代码已归档到 python-legacy。旧安装遗留在 .claude/settings.json 中的任何钩子,在从该文件中移除后都不会生效。
MCP 注册
MCP 服务已注册在项目的 .mcp.json 中:
{
"mcpServers": {
"stratum": {
"command": "node",
"args": ["/absolute/path/to/stratum/ts/src/mcp/bin.mjs"]
}
}
}
CLAUDE.md 区块
追加到 CLAUDE.md 的执行模型区块会指示 Agent 对非简单任务使用 Stratum:
## Stratum Execution Model
For non-trivial tasks, use Stratum internally:
1. Write a .stratum.yaml spec -- never show it to the user
2. Call stratum_plan to validate and get the first step
3. Narrate progress in plain English as you execute each step
4. Call stratum_step_done after each step -- the server checks your work
5. If a step fails postconditions, fix it silently and retry
6. Call stratum_audit at the end and include the trace in the commit
Python 库(Track 1)
已停用。
stratum-py 装饰器库(@infer、@contract、@compute、@flow、
@refine、parallel、debate、await_human、预算、OTLP 导出)已随 Python 引擎于
2026-07-18 停用(STRAT-PY-RETIRE)。其最终源代码已归档在
python-legacy 分支(src/stratum/);其最后一个 PyPI
版本仍可供现有用户安装,但不会再收到任何更新。
示例
examples/ 中的可运行示例:
| 目录 | 演示内容 |
|---|---|
custom-tracker/ | 通过 stdio 暴露项目跟踪器的最小 MCP 服务(compose-mcp 模式) |
nextjs/ | 将 Stratum 管道监控嵌入 Next.js 应用 |
Python 装饰器示例(01_sentiment.py … 06_hitl.py)已随 stratum-py 停用;请参阅 python-legacy。
开发
git clone https://github.com/smartmemory/stratum
cd stratum/ts
npm install # or pnpm install
./node_modules/.bin/vitest run # full engine suite
切勿同时运行两轮完整的 vitest 测试——这些测试套件共用磁盘状态根目录。
测试数量
TS 引擎包含 640+ 个 vitest 测试,涵盖 IR、引擎、MCP 接口、guard、
parallel 和 migrate 测试套件,其中包括跨引擎黄金测试,用于确保与 Python 时代的账本和规范保持字节兼容。Compose 测试套件(4,600+ 个测试,
包括驱动此引擎真实二进制文件端到端运行的 ts-cutover-* 黄金测试)是下游集成测试框架。
CI/CD
PyPI 发布已随 Python 软件包一同停用。目前还没有 npm 发布 流水线——使用者从代码检出目录运行引擎(参见“安装”)。
许可证
Business Source License 1.1。任何人都可以免费将其用于非生产用途;上一个财政年度的总年收入(含关联公司)不超过 1,000,000 美元的组织也可以将其用于生产用途。规模更大的组织需要商业许可证:[email protected]。每个版本会在发布四年后转换为 Apache 2.0。
2026-09-25 之前发布的版本以 Apache 2.0 许可分发,并继续遵循该许可条款。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。