mcpField
S
Stratum MCP

Stratum MCP

本地服务
github.com/smartmemoryAI 与智能体1今天更新

关于 Stratum MCP

Stratum MCP 是面向编码 Agent 的状态机执行引擎,用类型化工作流契约、后置条件检查和可审计运行来约束任务执行。它支持 Claude Code、Codex 及其他 MCP 宿主,并提供人工审批关卡和持久化流程状态。

主要功能

  1. 01验证类型化 version 1 规范
  2. 02执行带后置条件的流程
  3. 03依据结构化失败信息重试
  4. 04在人工决策关卡暂停
  5. 05持久化状态并记录审计轨迹
  6. 06调度 Claude 和 Codex Agent

适用场景

  1. 01为编码任务强制执行检查
  2. 02在工作流继续前要求审批
  3. 03依据违规详情重试失败步骤
  4. 04跨会话检查执行轨迹

MCP 相关

TypeScript 引擎包含 MCP 服务,提供流程规划、步骤完成和审计工具。它可运行于 Claude Code、Codex 或其他 MCP 宿主。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"ai-smartmemory-stratum": {
"command": "npx",
"args": [
"-y",
"@smartmemory/stratum"
]
}
}
}
启动命令npx -y @smartmemory/stratum

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@smartmemory/stratum0.7.3

工具

26

收录的工具列表 · 更新于 2 周前

参数类型必填说明
specobject是

详细介绍

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/guard CLI、后台流和后台 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 工作流。


目录


安装

从 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 会自动将其用于非简单任务:

  1. Claude 在内部编写 .stratum.yaml 规范(不会向你显示)
  2. 调用 stratum_plan 验证规范并获取第一步
  3. 使用自身的工具执行每一步(读取文件、编写代码、运行测试)
  4. 每完成一步后调用 stratum_step_done——服务会检查后置条件
  5. 如果后置条件失败,Claude 会收到具体的违规信息并重试
  6. 最后调用 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[]类型化数组
`draftfinal`
`(draftfinal)[]`
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 步骤加 inputsrun 加 with
max_iterations 加 exit_criterioniterate: {max, until}
parallel_dispatch重新编写的 fanout
Pipeline 阶段的 whenFanout 阶段的 when
Flow 的 max_roundsFlow 的 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)-- 内联 YAML
  • flow(str)-- flow 名称
  • inputs(dict)-- flow 级输入

返回: 步骤调度对象,包含 status: "execute_step" 或 status: "await_gate",其中包括:

  • flow_id -- 此次执行的唯一标识符
  • step_id、step_number、total_steps
  • function、intent、inputs(已解析)
  • output_contract、output_fields、ensure
  • retries_remaining
  • agent、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_steps
  • trace -- 步骤记录数组(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。

内置函数
函数签名描述
lenlen(value)字符串、数组或对象的长度
any、allany(array)、all(array)对数组进行真假值测试
max、minmax(array)、min(array)最大或最小的数字或字符串
str、int、bool一个参数受限类型转换
matchesmatches(text, pattern)有界正则表达式匹配
file_existsfile_exists(path)检查工作区根目录内的文件
file_containsfile_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 字段。

重试流程
  1. 执行器使用当前调度令牌报告任务结果。
  2. 引擎根据 out 验证结果。
  3. 引擎按声明顺序评估 ensures。
  4. 配置了迭代时,引擎会评估 iterate.until。
  5. 只要仍有剩余尝试次数且证据发生变化,失败的任务就会重新调度。
  6. 尝试次数用尽时,如果配置了 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 == True
  • no 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 许可分发,并继续遵循该许可条款。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。