评测框架¶
本地胶囊、检查、fixture 重放和回执构建模块。
候选执行和晋升不可用。
它们位于 scripts/lib/eval-harness/,随附一个位于
scripts/eval-harness.js 的 CLI,并在
examples/eval-harness/ 下有一个端到端示例。示例在本地、离线以及临时目录中运行。它不会合并、部署、发布或花费。
为什么是这五个¶
评测工程计划 v2(2026 年 8 月)描述了一个十二层栈。 属于可移植 ECC 包的部分是任何评测框架都可以安装并执行的契约表面:记录发生了什么,证明其未被篡改,将提议的变更置于外部检查器之后进行门控,重放工具调用而不重新触发效果,并向验证者提供其可以在不信任生产者的情况下检查的内容。执行门控在等待经过验证的操作系统隔离后端之前保持禁用。 其他模块暴露的是本地实用工具,而不是关于代码的信任决策。
| 框架 | 模块 | 计划 Epic | 当前提供的能力 |
|---|---|---|---|
| 信封 | envelope.js, schemas/capsule-envelope.schema.json |
01 遥测与胶囊契约 | capsule-envelope/v1、稳定标识符、效果类 SE0 到 SE4、默认拒绝的 payload 允许列表、秘密金丝雀 |
| 胶囊 | capsule.js |
02 本地执行胶囊 | 仅追加 NDJSON 日志、五种谱系、sha256 前驱链接、在确切条目处失败的 verify、字节稳定投影、最小导出包 |
| 门控 | gate.js, gate-child.js |
03 验证门控 | 静态源摘要和语法警告;所有执行入口点均拒绝 |
| 重放 | replay.js, effect-fence.js |
04 重放安全分支 | 每个工具声明的确定性和效果类、内容寻址 fixture、tool.fixture_missing 失败关闭重放、已退役子进程预加载拒绝执行 |
| 回执 | receipt.js |
07 可验证回执 | 针对胶囊根、条目数、工件摘要和门控回执的离线回执;分离签名接口;验证会指明失败的检查 |
Epic 05 提供了一个离线、仅报告的胶囊分组实用工具,如下所述。 自我改进、运营回顾验证以及 Epic 06(因果分诊和压缩不变性)仍未实现。它们消费这五个框架产生的记录。
效果类¶
每个日志条目、工具声明和变体清单都携带一个类。
| 类 | 含义 | 允许位置 |
|---|---|---|
| SE0 | 只读评估或模式验证 | 任何地方 |
| SE1 | 胶囊或工作根内的可逆本地写入 | 日志、门控元数据 |
| SE2 | 进程或文件系统变更,无实时网络写入 | 候选执行不可用 |
| SE3 | 仅追加的远程证据发布 | 重放中永不;可信记录模式调用方控制授权;在重放中拒绝 |
| SE4 | 经济、交易对手、支付、提供商或秘密处理效果 | 重放中永不;记录模式要求可信调用方禁止它 |
效果类是声明,而不是操作系统权限。静态检查会报告效果类扩展,但无法强制实施声明。重放器在重放模式下无论 fixture 如何都拒绝 SE3 及以上;记录模式会调用调用方提供的实现,直至其配置的最大值。只注册可信实现。没有 JavaScript 工具包装器能隔离任意代码。
胶囊日志¶
胶囊是一个目录,包含 capsule.json、journal.ndjson 和可选的
projection.json。日志的每一行都是一个规范 JSON 信封。第一个条目链接到六十四个零;每个后续条目链接到前一个
entry_hash。
const { capsule } = require('./scripts/lib/eval-harness');
const c = capsule.Capsule.create('.ecc/capsules/run-42', { task_family: 'slugify' });
c.append('plan', 'inspection.start', { task_id: 't01' });
c.append('attempt', 'gate.unavailable', { status: 'blocked', reason: 'gate.isolation_required' });
capsule.verify('.ecc/capsules/run-42'); // { ok, code, failed_at, root_hash }
verify 对于被更改的字节(capsule.invalid_entry)、被丢弃或交换的条目
(capsule.reordered 或 capsule.broken_link)以及部分尾部写入
(capsule.truncated_tail),会返回带有稳定代码和确切失败索引的 ok: false。日志摘要覆盖原始字节;
无效 UTF-8 会被拒绝为 capsule.non_canonical。project 从已验证的日志快照和已验证的元数据派生稳定
内容。exportBundle
复制三个胶囊文件,而不复制工作区中的任何内容。
元数据在创建写入之前以及打开、验证或投影胶囊时都会经过验证。ID 使用信封 ID 模式;harness/task family 必须
非空,并且 created_at 必须使用由
Date.toISOString() 生成的规范 ISO 时间戳。缺失、不可读或格式错误的元数据会返回
capsule.metadata_invalid;无效 UTF-8 也会被拒绝。每个日志条目都必须匹配元数据模式、
run_id、capsule_id、harness_version 和 task_family,否则验证会在该条目处返回
capsule.metadata_mismatch。空日志没有历史身份绑定;其投影和回执绑定元数据值。
created_at 会进行形状检查,但不由日志条目进行认证。
Envelope v1 强制执行在
schemas/capsule-envelope.schema.json 中声明的标量 payload 类型。字符串字段要求字符串;数字
字段要求有限数字,整数字段要求整数。只有
exit_code 接受 null。这些
payload 数字不会施加额外的非负限制。省略的 append payload 仍默认为空对象。
显式 null、数组、原始值、特殊对象、访问器、symbol 键和
不可枚举属性会被拒绝。具有普通
原型或 null 原型的普通数据对象会被接受。验证在读取
值之前检查描述符;它不会隔离代理或任意调用方 JavaScript。
保留字段会在金丝雀扫描或哈希之前进行验证。未定义值、非有限数字、函数、符号、BigInt 以及嵌套/循环对象会被拒绝,而不是被强制转换、从序列化字节中丢弃或递归扫描。
redactPayload 会向其现有结果中添加一个 errors 数组;调用方必须将其与 dropped 和 findings 一起检查。Append 会报告 capsule.payload_invalid,而不写入日志条目;现有的 finally 路径会释放其拥有的锁。严格模式下的未知 payload 键仍会报告 capsule.payload_denied。
strict: false 允许丢弃未知键,但绝不丢弃无效的保留值。自定义允许列表只能缩小 v1 字段,不能扩大持久化模式。
信封验证还要求其自身模式定义的字段,并拒绝未知的顶层字段,即使所提供的哈希已被重新计算。无效的已存储记录会在其日志索引处返回 capsule.invalid_entry。这收紧了对格式错误的 v1 数据的接受:现有不符合规范的调用方/日志需要显式修正;不会执行自动迁移或修复。有效的 v1 字节和哈希保持不变。通用键保留以及其余非 JSON 限制如下所述;两者均不提供操作系统级隔离。
通用规范化器会将每个选定的自有可枚举 JSON 键保留为自有数据属性,包括 __proto__、constructor 和 prototype。在构造规范化对象时,它不会调用继承的 setter。结果保留其普通对象原型。信封模式拒绝是独立的:自有 __proto__ 键是有效的通用 JSON 数据,但仍是一个未知信封字段。回执模式接受保持不变;对某个字段进行哈希并不构成来自更高级别模式的许可。
遍历、键排序、数组处理、未定义值省略、JSON.stringify 以及 UTF-8 哈希均保留其先前策略,包括 JavaScript 对数字外观键的排序。模式有效的 v1 日志/投影字节以及未受影响回执/测试夹具字节保持相同。回归向量是从修复前的实现中捕获的,包括未签名回执和合成字符串签名回执。验证不会重写这些已存储工件。
早期规范化器会省略自有 __proto__ 键,从而创建哈希别名。保留该键的修正后输入会故意产生不同的哈希。保留该键并带有旧摘要的工件会未通过现有哈希检查;测试夹具查找不会回退到旧的别名键。现有无键存储字节仍可作为这些字节读取,但无法认证那些键已丢失的更丰富原始输入。恢复需要从可信来源显式重新记录,或重建/重新签名回执;没有自动重新密钥、迁移、重写、双哈希接受或对已丢弃信息的恢复。
此修正并未为未定义值、函数/符号、非有限数字、稀疏数组、类/toJSON/getter 行为、循环、资源限制或恶意代理定义更严格的通用策略。它们先前的行为保持不变;不声称对每个 JavaScript 值都能进行无歧义哈希。信封更严格的标量验证仍是一个独立层。
Append 操作使用独占的本地 .append.lock 文件来串行化协作写入者。获取使用 wx,当路径存在时立即以 capsule.busy 失败,无论其年龄或内容如何。没有等待、重试、PID/年龄启发式或自动过期解锁。在拥有所有权时,每次追加都会重新加载并验证完整日志和元数据,然后从该快照推导其序列和前驱哈希。预打开句柄从不使用缓存的序列/哈希值作为权威状态。完整验证每次追加的成本为 O(日志大小);此实现旨在用于小型本地日志。
写入器会处理短写入,直到完整的 UTF-8 条目已写入,然后对日志执行 fsync。追加锁会在成功、验证拒绝或普通 I/O 异常时于 finally 中释放。零进度写入返回 capsule.write_failed。释放前会检查打开的锁描述符的设备/inode 是否与路径匹配;检测到缺失/被替换的锁会返回 capsule.lock_lost,并保留替换锁。这是协作式所有权检查,而不是针对主体在系统调用之间替换路径的原子保护。本地文件系统必须支持独占文件创建和稳定标识。
进程崩溃可能会留下 .append.lock。获取/清理 I/O 失败也可能留下一个未安全释放的锁。后续追加会保持忙碌;只有已停止所有写入者并检查了胶囊的操作员才应执行恢复。库从不猜测所有权、删除旧锁、截断尾部或自动修复日志字节。
写入失败可能会留下部分条目;后续追加会验证日志并拒绝无效尾部,以保留证据。当 fsync、close 或锁释放抛出异常时,完整条目可能已经存在。此类失败是模糊确认,而非回滚证明:重试前请检查磁盘,否则逻辑事件可能被记录两次。此处未添加事务、恰好一次重试、父目录 fsync 或断电持久性保证。
创建、读取/验证、投影、回执生成和导出不由追加锁串行化。使用静止胶囊以获得一致的回执/导出;没有并发导出保证或恶意文件系统隔离。追加修复不会更改已禁用的候选执行边界。
该链不声称:它不会阻止操作员替换整个日志。这是有见证的透明日志的职责,它是一个更晚的、可选加入的层,位于此包之外。
离线回顾准备¶
从同一任务族中选择 1 到 100 个现有胶囊目录:
const { retrospective } = require('./scripts/lib/eval-harness');
const report = retrospective.groupCapsules(['.ecc/capsules/run-41', '.ecc/capsules/run-42']);
该只读实用程序使用 capsule.project 从已验证的元数据和日志快照重新计算每个投影。它从不使用或修复已保存的 projection.json。输入必须是来自同一任务族的小型、无活动的本地胶囊;任何不匹配都会拒绝整个报告。没有目录发现、钩子激活、新的 rollout、fixture 重放或候选执行。
capsule-retrospective/v1 报告任务族、输入数量、唯一胶囊数量、重复数量,以及按声明的测试框架版本排序的分组。每个分组包含胶囊/条目数量、全部五个谱系计数、全部五个声明的效果类别计数,以及源摘要引用。计数描述的是已记录条目,而不是唯一任务、尝试、成功效果或独立验证的结果。空日志贡献一个胶囊和零个条目。负载分数、判定、成本、持续时间以及通过/失败总数均不使用。
配对 (run_id, capsule_id) 用于去重时标识一个胶囊。当已验证的投影哈希匹配时,重复路径或复制的快照只计一次。该标识的冲突快照(包括不同检查点)会以 retrospective.conflicting_identity 失败;该实用程序从不选择胜者。不同的胶囊标识即使其事件形状匹配也保持不同。源引用包含标识对的规范哈希、条目数量、根哈希、日志摘要和投影哈希。report_hash 覆盖其他所有报告字段;输入顺序不会改变结果。重复输入会改变输入/重复计数和报告哈希,但不会改变分组计数。
报告省略目录参数、原始 run/capsule ID、日志负载和时间戳。任务族和测试框架版本标签按原样返回,并且其本身可能包含私有文本或路径。摘要引用不是匿名化:它们仍然可链接,低熵 ID 可以被猜测。在共享之前,请审查标签和报告内容。哈希和声明标签均不能认证生产者或证明改进;report_only 始终为 true。
任何无效、不可读或不匹配的胶囊都会以 retrospective.invalid_capsule 和零基输入索引拒绝整个报告。诊断信息省略底层读取器消息和源路径。混合任务族和无效输入列表具有各自稳定的代码。CLI 成功时向 stdout 输出 JSON 并以 0 退出;错误用法以 2 退出,而验证/拒绝以 1 退出且不输出部分 JSON。该命令不接受任何标志,也不写入报告文件。对于以 -- 开头的目录名,请使用相对 ./ 前缀或绝对路径。
这继承了现有胶囊读取器的文件系统限制和内存限制。100 个输入上限不会限制日志字节数。它不会隔离恶意文件、序列化并发写入者、验证签名或建立实时溯源。执行器隔离、可选钩子记录、稳定任务集验证以及路线图中的运营回顾里程碑仍然是各自独立的前置条件。
验证门禁:不可用¶
支持的候选执行后端:任何操作系统上均无。 runGate 和 runVariant 无条件抛出 gate.isolation_required,发生在读取配置、复制文件、加载候选模块或创建回执之前。gate run 在读取其配置或创建胶囊之前以 1 退出。直接调用 gate-child.js 以及已退役的 effect-fence.js 预加载也会在加载请求或候选代码之前拒绝。信任标志和调用方提供的执行器对象无法启用执行。没有提升路径。
先前的目录复制和 JavaScript 拦截没有隔离主机读取、替代内置加载器,或文件系统描述符和 Promise。将答案保留在父进程中并没有隐藏磁盘上的任务集。拦截代码和分阶段执行实现已被移除。Node 的 权限模型 和 vm 模块 不能替代对恶意代码的隔离。
未来的执行器必须拥有经过单独审查的操作系统隔离实现,并且在每个受支持的操作系统上具备对抗性证据。至少必须:
- 仅在临时文件系统中暴露不可变的、已摘要的变体文件和任务输入。主机任务集、答案、凭据、配置、套接字以及其他工作区必须不可访问,包括通过链接和继承的 FD。
- 在候选运行时之外强制实施网络、进程、文件系统和资源限制,并使用非特权身份和有界生命周期。
- 将检查器、输出/协议验证、审计通道和回执创建保持在候选控制之外。在任何候选启动之前,使用独立金丝雀验证实际运行时策略;拒绝不可用的后端。
- 在评估候选改进之前,拒绝失败、超时、收到信号、不完整或格式错误的基线运行。要求每个任务具有完整且唯一的结果。容器可用性或调用方的
verified: true断言本身不是策略验证。
静态 API 仍然可用于受信任的、无活动的本地源树:loadTaskset、loadVariant、digestDir 和 scanTripwires。变体名称是 1–64 个 ASCII 字母、数字、下划线或连字符组成的单个组件,以字母或数字开头。条目必须是包含在摘要中的相对常规文件;绝对路径、父级遍历、符号链接以及被排除的条目会被拒绝。.git 和 node_modules 仍然被排除。检查不会抵抗并发主机文件系统变更,也不是沙箱或执行证明。任务 ID 必须唯一。语法警告在设计上是不完整的:零命中既不能证明安全性,也不能证明正确性。
parseChildResult 和 baselineFailure(run, tasks) 是用于有界协议和基线完整性回归检查的纯验证辅助函数。本版本中没有任何执行器调用它们。它们的测试不是运营门或已验证操作系统后端的证据。现有 manifest/config 测试夹具作为数据保留。
重放安全的工具调用¶
const { replay } = require('./scripts/lib/eval-harness');
const store = new replay.FixtureStore('.ecc/fixtures');
const tools = {
read_inventory: { effect_class: 'SE0', determinism: 'deterministic', impl: liveRead },
place_order: { effect_class: 'SE4', determinism: 'nondeterministic', impl: livePlace },
};
const r = replay.createReplayer(tools, { mode: 'replay', store, maxEffectClass: 'SE2' });
r.call('read_inventory', { sku: 'gpu-8x' }); // served from fixture or tool.fixture_missing
r.call('place_order', { sku: 'gpu-8x' }); // tool.effect_forbidden, always
Fixture 以 (tool, args) 的规范哈希作为键,并同时存储参数哈希和响应哈希,因此过期或被编辑的 fixture 会以 tool.fixture_mismatch 失败。记录模式执行调用方提供的受信任函数;重放使用 fixture。这些包装器不会约束实现内部的任意副作用。遗留的 EFFECT_FENCE_PRELOAD 导出仍保留以兼容导入,但加载该文件始终会抛出 gate.isolation_required。它不再尝试 JavaScript 拦截。
离线回执¶
node scripts/eval-harness.js receipt build .ecc/capsules/run-42 \
--artifact skills/my-skill/SKILL.md --out run-42.receipt.json
node scripts/eval-harness.js receipt verify run-42.receipt.json exported-bundle/ \
--artifact skills/my-skill/SKILL.md
回执会指明 capsule 根目录、条目数、journal 摘要、projection 哈希、artifact 摘要以及可选的 gate 回执摘要,外加其自身哈希。buildReceipt 现在会在返回回执之前,使用已验证的 journal 快照持久化 projection.json。这是生产者写入,在只读 capsule 上可能失败;构建前请将只读源复制到可写的本地目录。显式无效的 artifact_digest 会在 projection 写入之前抛出 receipt.schema_invalid。其他构建失败仍会抛出异常。
verifyReceipt 是只读的。它从不重新生成或修复缺失的 projection。提供的 projection 必须可解析,并与经过验证的 metadata/journal 快照生成的完整确定性 projection 匹配;其计算出的哈希必须同时匹配其存储的 projection_hash 和回执。缺失、不可读、损坏或被替换的 projection 会返回 check: 'projection';无效 UTF-8 会被拒绝。回执身份不匹配和无效的 capsule 元数据会返回 check: 'metadata'。
模式校验会在 journal 索引之前拒绝负数、小数、字符串或不安全的条目数、无效的 identity/schema 值以及格式错误的必需摘要。可选的 artifact/gate 摘要字段必须是 SHA-256 值或 null。其他有效回执在 projection/artifact 比较之前仍保留签名、journal 完整性、截断、capsule 根目录和过期检查点检查。缺失或不可读的 artifact 文件会返回 check: 'artifact',而不是抛出异常。对于这些经过验证的文件/内容情况,每个验证失败都具有 {ok: false, check, reason}。
现有 v1 导出 bundle 保留其格式。对于其回执在未保存 projection 的情况下构建的旧源目录,必须在验证前显式运行 capsule project 或重建回执;验证本身从不写入替代文件。CLI 会在文件读取或生产者写入之前验证 --artifact、--gate 和 --out:缺失值、作为另一个 flag 的值以及重复 flag 会以用法代码 2 退出。已禁用的 gate 命令仍会在配置/capsule I/O 之前拒绝执行。
签名仍是一个分离接口:构建时传入签名器,验证时传入验证器。本包中不会发生密钥生成、传输或轮换。签名证明的是谁为这些字节背书,而不是运行是否正确。可选的 gate 回执哈希仍保留以兼容现有 artifact;接受外部提供的字节既不能证明包含,也不能证明提升。
本切片处理回执/projection 验证和元数据身份绑定。OS 执行器仍不可用。协作式追加序列化如上所述;并发 export/create 以及更广泛的 envelope/review 发现仍相互独立。包/计数证据是独立的 ignore-scripts 测试范围,不会验证常规 prepack,也不会清除发布。
接入位置¶
skills/eval-harness/SKILL.md描述 eval 驱动开发。这些框架是其报告格式下的机械层。harness-optimizeragent 和/harness-audit命令必须报告 gate 不可用,直到存在经过审查的 OS 后端。它们不能使用该实现发出新的 gate 回执。- Rust 的
ecc2/src/harness_eval.rs有界评估循环是另一个更早的实验。Node 框架是可移植接口。
测试¶
node tests/lib/eval-harness/envelope.test.js
node tests/lib/eval-harness/capsule.test.js
node tests/lib/eval-harness/retrospective.test.js
node tests/lib/eval-harness/gate.test.js
node tests/lib/eval-harness/security.test.js
node tests/lib/eval-harness/replay.test.js
node tests/lib/eval-harness/receipt.test.js
node tests/lib/eval-harness/cli.test.js
node examples/eval-harness/run-example.js
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 ev/ECC