跳到正文
Xingchi Guo
全部项目

Engineering Execution Control Plane

ExecRelay

ExecRelay 是一个面向软件工程执行过程的本地工具,把命令执行、进程结果、验证结果和代码变更记录为彼此独立、又可以追踪关联的工程事实。它不从终端文本或 AI Agent 的描述中“推测”任务是否成功,而是保留真实的进程结果和可核查的工程证据。

状态
v0.1.2 · Developer Preview · macOS on Apple Silicon
技术栈
  • Rust
  • Tauri
  • TypeScript
  • React
  • PTY
  • Shell 集成
  • 结构化证据
  • 验证
  • Git 证据
  • Agent 适配器

ExecRelay 是什么

ExecRelay 看起来像一个终端,但它关心的不只是“命令跑完了没有”。它把一次工程任务里经常被混为一谈的几件事分开记录:

  • 执行了什么:哪条命令、在哪个目录、以什么方式运行。
  • 进程实际发生了什么:生命周期、退出码,以及这个结果从哪里来。
  • 验证了什么:明确的验证条件是否被可信的进程结果满足。
  • 代码仓库发生了什么变化:在一段时间窗口前后,仓库里观察到了哪些改动。

这些都是相关但彼此独立的工程事实。ExecRelay 分别记录它们,再通过明确的引用关系把它们连接起来。

为什么需要它

开发者、脚本和 AI 编程 Agent 都会给出同样的说法:命令成功了,测试通过了,构建是干净的,这些文件改过了,任务完成了。

这些都是“声明”。大多数时候它们是真的,正因如此,人们很容易不再核对。终端输出会滚走或被截断,总结描述的往往是“打算做什么”而不是“实际运行了什么”,而 Agent 的收尾消息,本身就是被评估的那个系统写出来的文字。

传统终端和 AI 编码工具通常把命令、输出、验证和代码修改放在同一个交互流程里:

  • Agent 说“测试通过”,并不等于测试进程真的以成功状态退出;
  • 命令执行成功,也不等于修改后的代码已经通过验证;
  • Git diff 是代码变化的证据,但不是执行成功的证据。

随着越来越多的工程工作交给 Agent 完成,真正有用的问题从“它说了什么”变成了“实际发生了什么,这些事实能支持什么结论”。ExecRelay 用记录而不是叙述来回答后一个问题。

  • Agent 的声明is not the same as进程结果
  • 终端输出is not the same as结构化证据
  • 执行is not the same as验证
  • 仓库变化is not the same as变更归因

AI 生成的文字永远不会被当作进程事实。这是一条关于“每个事实从哪里来”的建模原则,并不是防篡改承诺,也不是通用的安全保证。

v0.1.2 当前提供什么

v0.1.2 是面向 macOS(Apple Silicon)的开发者预览版(Developer Preview)。公开版本包含四个相互配合的功能区:

功能区作用
Terminal在真实 PTY 中运行命令,支持分屏和日常命令行工作流。
Capture记录每次执行的真实进程结果和执行证据。
Verify按明确的验证条件运行检查,并单独保留验证结果。
Changes以只读方式观察 Git 仓库变化,并与对应的执行和验证关联起来。

终端本身被设计为独立于采集工作:观察工作的过程,不应该破坏工作本身。

Capture

每条命令都会成为一条 Execution 记录,包含生命周期、结果、退出码,以及这个结果的来源。

  • 结果默认为“未知”。 只有在拿到来自可信来源的真实退出码时,结果才会变成成功或失败;退出码从不靠推断得出。
  • 不读文字判断成败。 看起来像报错的输出只能作为提示,不能把结果改成失败。
  • Shell 集成按会话关联。 终端使用标准的提示符与命令标记,并带有每个会话独有的随机值;没有正确随机值的标记会被视为不可信,因此仅仅模仿标记的输出无法设定结果。这是会话关联,不是密码学认证。
  • 保留原始证据。 原始终端输出(包括控制序列)按证据块保存,阅读用的整洁文本由它派生。确定性的、带版本的解析器会从中提取 Jest、Vitest、pytest、cargo test 和 TypeScript 编译器的结构化结果,并注明来源证据块;未知的数值保持未知,不会被补全。

结构化证据用来解释结果,而不是决定结果。

Verify

执行回答的是“运行时实际发生了什么”,验证回答的是“可信证据是否满足某个具体要求”。两者是不同的事实。

验证配置是一组命令条件,每条都写明期望的退出码。运行它会生成一条 VerificationRun:

  • 验证条件会被快照进这次运行,之后修改配置也不会改写历史结果;
  • 每个条件作为普通执行运行,并按明确的身份匹配回来,用户在运行中途手动输入的命令无法满足某个条件;
  • 条件只依据可信退出码判定通过或失败;结果未知、不可信或被中断时,记为无法评估,而不是猜一个结论;
  • 判定过程中没有启发式规则,也没有模型判断,只有确定性的比较;
  • 每个条件可以有自己的工作目录,因此适用于 monorepo。

Changes

仓库状态由只读的 Git 采集器收集,并且有意加了限制:

  • 不经过 shell,只允许运行 git 和批准过的子命令,带有修改性质的参数会在执行前被拒绝;
  • 输出有上限,运行有时限;
  • 只收集摘要(分支、HEAD 提交、工作区状态、按文件的 diff 统计),默认不保存完整 diff 内容;
  • 每条记录都注明采集器、版本、采集批次和仓库根目录。

ChangeAttribution 比较时间窗口前后的两个仓库快照。它会先对基线分类(干净、本来就有改动、部分可用、不是 Git 仓库),再对每个文件分类,并把局限性一起记录下来,例如同一时间窗口内可能有其他进程也改动了仓库。

它说明的是仓库在这段时间内发生了变化,而不是某次执行是唯一原因。采集器出错,也不会改变任何执行结果或验证结论。

工程模型

无论是开发者输入的命令、验证检查、只读的 Git 采集器,还是 Agent 发出的命令,最终都会成为同一种 Execution 记录。证据、验证和变更归因是各自独立的记录,通过 id 指回执行。

  1. 谁在运行

    • 开发者

      真实 PTY 中的交互式终端会话。

    • 验证运行

      执行明确验证条件里的命令。

    • AI 编程 Agent开发中

      AgentRun 与 AgentEvent 关联到执行记录,而不是取代它们。

    每条命令都成为

  2. 进程事实

    • Execution

      每条命令一条记录:生命周期、结果、退出码,以及结果来源。

    产生

  3. 证据

    • 原始证据块

      原始终端字节,以及由此派生的整洁文本。

    • 结构化证据

      解析出的测试与类型检查结果,注明来源证据块。

    • 仓库快照

      只读的 Git 状态:分支、HEAD、状态、diff 摘要。

    支撑

  4. 结论

    • VerificationRun

      可信退出码是否满足这次运行快照下来的验证条件?

    • ChangeAttribution

      在前后两个快照之间,仓库里发生了什么变化?

每个方框都是一种独立的记录类型。记录之间通过 id 相互引用,任何一种都不会覆盖另一种;关联并不意味着一个事实证明了另一个。

为什么“执行事实”很重要

决定结果的

  1. 命令

    then
  2. Execution

    then
  3. 可信退出码

    then
  4. 条件结果

解释结果的

  1. Execution

    then
  2. 原始证据块

    then
  3. 解析器

    then
  4. 结构化证据

只有可信的进程结果能决定成败;输出、解析结果和 Agent 的描述只能帮助理解这个结果。这样做的代价是界面要能坦然地显示“未知”,而不是一个绿色的对勾;换来的是每个结论都能追溯到它的来源。

面向 AI 编程 Agent 的设计

这一部分描述的是正在开发中的架构,不属于当前公开版本。

ExecRelay 的领域模型包含一套与厂商无关的 Agent 模型。AgentRun 表示 Agent 对某个任务的一次尝试,它引用这次尝试中发生的执行、验证和变更归因,而不是复制它们的状态;AgentEvent 是 Agent 时间线上的一个标准化步骤。

  • Agent 运行的命令就是普通的 Execution,不会出现第二套“Agent 专用”的执行事实;
  • Agent 说“完成了”,只会被记录为一条消息事件,它不会结束运行、设置退出码,也不会让任何验证条件通过;
  • 原始的适配器数据会和标准化后的事件一起保留。

Claude Code 和 Codex 的适配器存在于开发代码库中,标准化逻辑有基于样例数据的测试覆盖。对正在运行的 Agent 进程的原生监管仍在开发中,公开版本不包含 Agent 界面。这个边界是有意的:先发布执行、证据和验证这一层,Agent 再接入它。

实现概览

运行时边界

  1. Workbench

    React / TypeScript,负责界面与领域状态。

    then
  2. Tauri 边界

    命令向下,事件向上。

    then
  3. Rust 运行时

    PTY 会话、进程生命周期、采集器。

    then
  4. 操作系统

    Shell、进程、代码仓库。

  • 原生进程管理。 PTY 分配、退出状态和只读采集器运行在 Rust 运行时中,界面层只订阅它们报告的结果,从不持有长期运行的进程。
  • 受限采集。 采集器的输出有上限、运行有时限,大型仓库也不会耗尽内存。
  • 明确的来源。 结果、证据、结构化记录和 Agent 关联都各自记录来源。
  • 故障隔离。 解析器或采集器出错时,执行记录和验证结论不受影响;采集失败被设计为不影响终端本身。
  • 本地优先。 不需要账号;除非你运行的命令主动发送数据,执行记录、终端内容和证据都留在本机。
  • 有测试覆盖的边界。 自动化测试覆盖执行证据、Shell 集成的信任边界、验证、结构化证据解析器、Git 采集器和变更归因。

当前状态与限制

  • 开发者预览版。 v0.1.2 仍是 Developer Preview,仅支持 macOS(Apple Silicon)。
  • 公开范围。 当前版本包含终端、Capture、Changes 和 Verify;更深入的 Agent 运行时集成仍在开发中,公开版本不包含 Agent 界面。
  • 证据没有加密签名。 来源记录是建模上的约束,不是防篡改保证。
  • 归因不等于因果证明。 变更归因只说明时间窗口内观察到的变化。
  • 验证条件有限。 目前的验证条件是“命令加期望退出码”,任何不可信的结果都会被记为无法评估,而不是通过。