claw-code Runtime 报告模式 v1 测试夹具集:验证命令、六大契约覆盖点与源码级实现解读
本篇技术指南围绕 runtime crate 中报告模式 v1(claw.report.v1)的测试夹具集展开:先说明该夹具集的位置、验证命令与覆盖范围,再逐条对应到 report_schema.rs 中的真实实现——规范报告(canonical report)、claim 标签体系、负证据(negative evidence)、字段级差异归因、确定性投影与脱敏溯源、消费方能力协商与降级投影。读完后你应当能够独立运行夹具验证、读懂报告模式的每一条契约语义,并知道每个语义在源码中的落点。
夹具集是什么、如何验证
夹具集位于 rust/crates/runtime/tests/fixtures/report_schema_v1/README.md,官方给出的验证命令是:
cargo test -p runtime report_schema -- --nocapture
需要特别注意一点:这个 fixture 目录本身只承载 README 说明,真正的夹具数据是内嵌在测试代码中的。README 明确指出,实际的夹具由 runtime::report_schema::tests::fixture_report 这一 in-code fixture 提供,它构造并规范化了一份完整的 CanonicalReportV1 报告,然后由 report_schema 测试模块中的四个测试用例分别验证六大覆盖点。也就是说,"读取外部 JSON 夹具"与"内联夹具 + 断言"是两种不同的夹具策略,本 crate 选择了后者,保证夹具与测试断言在同一模块内演进、不会失配。
六大覆盖点(原文档逐条列出)如下,后文逐一展开:
- fact / hypothesis / confidence 标签;
- 带 checked surfaces 与 query window 的负证据;
- 字段级差异归因(field-level delta attribution);
- 规范报告 ID 加内容哈希;
- 确定性投影/脱敏溯源(provenance);
- 消费方能力协商与降级投影。
模块通过 lib.rs 对外导出全部公开 API:canonicalize_report、project_report、report_content_hash、report_schema_v1_registry 以及全部相关类型与常量 REPORT_SCHEMA_V1、DEFAULT_PROJECTION_POLICY_V1,任何下游 crate(例如 CLI 侧)都可以直接依赖这套契约。
覆盖点一:fact / hypothesis / confidence 标签
报告中的每一条断言(claim)都必须标注"证据类别"和"置信度",而不是以自由文本形式混在报告里。源码中对应两组枚举(report_schema.rs):
pub enum ClaimKind {
ObservedFact, // 观察到的事实
Inference, // 基于证据的推断
Hypothesis, // 假设
Recommendation, // 建议
}
pub enum ReportConfidence {
High, Medium, Low, Unknown,
}
两个枚举都通过 #[serde(rename_all = "snake_case")] 序列化为 observed_fact、hypothesis 等小写下划线形式。ReportClaim 结构(同文件 L54-L62)还带有 evidence: Vec<String>(支持该 claim 的证据 ID 列表,为空时不序列化)和 sensitivity: SensitivityClass 字段。
夹具报告 fixture_report()(report_schema.rs#L407-L458)构造了三条覆盖不同标签组合的 claim:
claim-fact:ObservedFact+High置信度,证据为event:lane.finished,敏感度Public;claim-hypothesis:Hypothesis+Medium置信度,证据为event:transport,敏感度Internal;claim-secret:ObservedFact+High置信度,但敏感度为Secret——这条专门用于验证后文的脱敏路径。
对应测试 canonical_report_labels_claims_negative_evidence_and_deltas(L490-L504)断言了规范化后 claims[1] 的 kind 是 Hypothesis、confidence 是 Medium,即标签语义在规范化排序后仍然可被逐位验证。
这套"标签 + 置信度 + 证据引用"的设计与项目 G004 事件/报告契约文档中的要求一致:docs/g004-events-reports-contract.md 明确写有"断言必须标注为 fact、hypothesis 或另声明的证据类别,并附带置信度和来源引用"。
覆盖点二:带 checked surfaces 与 query window 的负证据
"没查到"和"没查过"在自动化消费中是两种完全不同的状态,报告模式把负证据提升为一等公民(first-class)结构:
pub enum NegativeFindingStatus {
NotObservedInCheckedScope, // 在已检查范围内确认未出现
UnknownNotChecked, // 未检查,状态未知
}
pub struct NegativeEvidence {
pub id: String,
pub status: NegativeFindingStatus,
pub checked_surfaces: Vec<String>, // 检查过哪些面
pub query: String, // 执行的查询
pub window: String, // 查询时间窗口
pub sensitivity: SensitivityClass,
}
(定义见 report_schema.rs#L46-L73)
夹具中的负证据条目是:
NegativeEvidence {
id: "neg-blocker",
status: NegativeFindingStatus::NotObservedInCheckedScope,
checked_surfaces: vec!["lane_events", "worker_status"],
query: "current blocker",
window: "2026-05-14T00:00:00Z/2026-05-14T00:05:00Z",
sensitivity: SensitivityClass::Public,
}
语义是:"在 lane_events 与 worker_status 这两个面上,按 current blocker 查询、在 2026-05-14 前 5 分钟窗口内,确认没有发现 blocker"。checked_surfaces + query + window 三元组让消费方可以判断"未出现"结论的适用边界——这正是 G004 契约文档中"not observed、checked and absent 与 redacted 是不同状态"这条契约在 v1 实现里的最小落地。测试 canonical_report_labels_claims_negative_evidence_and_deltas 断言了该条目状态为 NotObservedInCheckedScope。
覆盖点三:字段级差异归因(field-level delta attribution)
当报告要表达"某个状态字段相对上一份报告发生了什么变化"时,不能只说"变了",而要说明变前变后的值、变化类型以及归因来源。FieldDelta 结构(report_schema.rs#L75-L84):
pub enum FieldDeltaState {
Changed, // 值改变
Unchanged, // 保持不变
Cleared, // 被清空
CarriedForward, // 从上份报告沿用
}
pub struct FieldDelta {
pub field: String,
pub state: FieldDeltaState,
pub previous_hash: Option<String>, // 变化前值哈希
pub current_hash: Option<String>, // 变化后值哈希
pub attribution: String, // 归因说明
}
夹具中的差异条目为 field: "blocker"、state: Cleared、previous_hash: Some("prev123")、current_hash: None、attribution: "lane.failed reconciled to lane.finished"——即 blocker 字段从"有"到"清空",归因是 lane 失败事件与 lane 完成事件完成调和后的结果。previous_hash/current_hash 使用哈希而非原始值,配合 Cleared 状态下 current_hash 为 None 的约定,让"清空"这类没有现值的差异也能被精确描述。
这个设计与 G004 契约文档中"字段差异必须命名字段、前后状态、归因,以及差异来自源内容/投影/降级/脱敏策略中的哪一种"的要求直接对应。
覆盖点四:规范报告 ID 加内容哈希
报告身份由 ReportIdentity 承载:
pub struct ReportIdentity {
pub report_id: String,
pub content_hash: String,
}
核心是 canonicalize_report 函数(report_schema.rs#L222-L233),它做了四件事:
- 强制
schema_version为claw.report.v1; - 对
claims(按id)、negative_evidence(按id)、field_deltas(按field)做稳定排序——这是内容哈希确定性的前提:同一组数据以任何顺序输入,规范化后都得到同一份报告; - 计算内容哈希;若
report_id为空,则以report-{content_hash}自动填充,保证每份报告拥有稳定且可由内容推导的标识; - 回填
identity.content_hash。
哈希本身由 report_content_hash(L236-L241)计算:克隆一份报告、清空 identity.report_id 与 identity.content_hash 两个自引用字段后,对剩余规范载荷做稳定 JSON 哈希。这个"排除 identity 再哈希"的设计避免了循环依赖(哈希不能包含自己),同时让"内容未变则哈希不变"成立——下游消费方可以只凭 content hash 判断两份报告是否同源。
测试 canonical_report_labels_claims_negative_evidence_and_deltas 验证了这些行为:report_id 以 report- 开头、content_hash 长度为 16(十六进制表示的 8 字节)。
稳定哈希的底层是 stable_json_hash(report_schema.rs#L372-L396):先经 normalize_json 递归把任意对象重排为按 key 排序的 BTreeMap 结构,再序列化、取 SHA-256 的前 8 字节转成 16 位十六进制。因此哈希与 JSON 字段书写顺序无关,跨语言、跨序列化器只要 key 集合与值相同就能得到同一哈希——这是跨进程比较报告/投影是否一致的基础设施。
覆盖点五:确定性投影与脱敏溯源
project_report(report_schema.rs#L244-L323)把一份规范报告按消费方能力裁剪为 ReportProjectionV1。投影不是简单删字段,而是"删了必须留痕":
pub struct RedactionProvenance {
pub field_path: String, // 如 "claims[2]"、"claims[1].text"
pub reason: String, // 省略或转换的原因
pub policy_id: String, // 应用的策略(claw.report.projection.v1)
pub original_hash: String, // 被脱敏原文的哈希
}
pub struct ProjectionProvenance {
pub policy_id: String,
pub source_schema_version: String,
pub source_report_id: String, // 回溯到源报告 ID
pub source_content_hash: String, // 回溯到源内容哈希
pub consumer: String,
pub downgraded: bool, // 本次投影是否发生了降级
pub omitted_field_families: Vec<String>,
pub redactions: Vec<RedactionProvenance>,
}
脱敏的具体决策在 redact_claim(L338-L370)中,按 SensitivityClass 与消费方 max_sensitivity 的比较分三级处理(敏感度枚举为 Public < Internal < OperatorOnly < Secret):
- claim 敏感度 ≤ 消费方上限:原样输出;
- claim 敏感度低于
Secret但超限:转换式脱敏——text替换为"<redacted>"、清空evidence,保留 claim 骨架,溯源记为transformed: sensitivity exceeds consumer policy,字段路径claims[{i}].text; - claim 敏感度为
Secret且超限:整体省略,溯源记为omitted: sensitivity exceeds consumer policy,字段路径claims[{i}]。
无论转换还是省略,都会附上原文的 original_hash,让有权限的审计方能够确认"这里确实存在一条被隐藏的 Secret 级断言,且其原文是这份哈希对应的内容",而普通消费方只能看到存在性,看不到内容。
投影自身的 projection_id 也是内容哈希:对 {view, provenance, payload} 三元组做稳定 JSON 哈希(L317-L321)。测试 projections_are_deterministic_and_record_redaction_provenance(L506-L535)对同一报告、同一能力集调用两次 project_report,断言:
- 两次结果完全相等(确定性);
provenance.source_report_id/source_content_hash与源报告一致(血缘可回溯);downgraded为 true;- 恰好产生 2 条脱敏记录,分别落在
claims[1].text(claim-hypothesis的 Internal 级对 Public 上限的转换)与claims[2](claim-secret的整体省略)。
注意夹具中 claim 在规范化后按 id 排序为 claim-fact、claim-hypothesis、claim-secret,所以脱敏溯源用的是排序后的下标——这条断言同时隐性地验证了"规范化排序先于投影裁剪"的执行顺序。
覆盖点六:消费方能力协商与降级投影
消费方通过 ConsumerCapabilities 声明自己能消化什么:
pub struct ConsumerCapabilities {
pub consumer: String,
pub schema_versions: BTreeSet<String>, // 支持的报告模式版本
pub field_families: BTreeSet<String>, // 支持的字段族:claims / negative_evidence / field_deltas
pub max_sensitivity: SensitivityClass, // 可接收的最高敏感度
}
字段族的判定规则在 supports_family(L334-L336)里有一条关键约定:field_families 为空集合表示"支持全部字段族",非空集合才做白名单过滤。这让老消费方(尚未实现字段族声明)默认获得完整载荷,新消费方则显式声明需求。测试 capability_negotiation_omits_unsupported_field_families(L537-L551)验证了白名单路径:能力集只声明 claims 后,投影载荷中存在 claims 而 negative_evidence、field_deltas 两个字段被整体移除,且 omitted_field_families 精确记录了 ["negative_evidence", "field_deltas"]、downgraded 为 true。
downgraded 标志的判定条件是三者任一成立:消费方不支持当前 schema 版本、有字段族被整体省略、或发生过任何脱敏。这意味着"降级"不是可选的元数据,而是每次投影都会诚实计算并记录的状态——G004 契约文档中"生产者只保留一份全保真规范报告,仅携带 downgraded 语义的元数据输出降级投影"的要求正是这样实现的。
自描述模式注册表
除了运行时代码,report_schema_v1_registry()(report_schema.rs#L163-L219)返回一份自描述的字段注册表:schema_version 为 claw.report.v1,兼容性策略声明为 "additive fields are compatible; missing required fields are breaking"(新增字段向前兼容,缺失必填字段即为破坏性变更),并逐条列出 7 个模式字段:
| 字段 ID | 说明 | 必填 | 字段族 |
|---|---|---|---|
identity.report_id |
稳定的规范报告身份 | 是 | identity |
identity.content_hash |
规范载荷(除 identity 外)的哈希 | 是 | identity |
claims[].kind |
fact/inference/hypothesis/recommendation 标签 | 是 | claims |
claims[].confidence |
claim 的置信度分桶 | 是 | claims |
claims[].evidence |
支持该 claim 的证据 ID | 否 | claims |
negative_evidence[] |
带检查范围的"搜索后未发现"结论 | 否 | negative_evidence |
field_deltas[] |
字段级 changed/unchanged/cleared/carried-forward 归因 | 否 | field_deltas |
projection.provenance.redactions[] |
投影字段的脱敏策略溯源 | 否 | projection |
测试 report_schema_registry_is_self_describing(L472-L488)验证了注册表版本正确且包含上述关键字段。这份注册表的价值在于把"哪些字段缺失会导致破坏性不兼容"(当前仅 identity.report_id 与 identity.content_hash,以及 claim 的 kind/confidence 标签)固化成了机器可读的单一事实来源,下游做版本协商时不必去解析散文文档。
运行验证与结果解读
在仓库根目录(rust/ 所在层级)执行:
cargo test -p runtime report_schema -- --nocapture
该命令以 report_schema 为过滤器命中 report_schema.rs 测试模块中的四个测试用例,各自守护一组契约:
| 测试 | 守护的契约 |
|---|---|
report_schema_registry_is_self_describing |
注册表版本与关键字段自描述 |
canonical_report_labels_claims_negative_evidence_and_deltas |
规范化排序、report id 前缀、16 位内容哈希、claim/负证据/差异标签语义 |
projections_are_deterministic_and_record_redaction_provenance |
两次投影结果相等、血缘字段回填、2 条脱敏记录的路径 |
capability_negotiation_omits_unsupported_field_families |
字段族白名单过滤、omitted 列表与 downgraded 标志 |
由于夹具是 in-code 的,运行该命令不需要任何外部文件准备;-- --nocapture 参数则用于在测试输出较多时保留 stdout 可见性,便于直接观察投影载荷内容。
与 G004 契约一致性校验的衔接
报告模式 v1 不是孤立存在的,它还参与 G004 事件/报告契约包(contract bundle)的机器校验。g004_conformance.rs 中的 validate_g004_contract_bundle 对 JSON 形态的 bundle 做跨语言一致性验证,其中 reports[] 部分要求报告携带 schema 身份、内容哈希、投影/脱敏溯源、能力协商、fact/hypothesis 标签与字段级差异归因——与夹具集的六大覆盖点逐条同构。对应的黄金夹具 g004_contract_bundle.valid.json 与 g004_conformance.rs 测试验证:合法 bundle 零错误通过;非法 bundle(例如 finding 的 kind 写成 guess、confidence 写成 certain)会以机器可读的 JSON 路径形式(如 /reports/0/findings/0/kind)逐条报告缺漏。可以推断,G004 一致性校验面向的是 bundle 序列化形态(camelCase 的 JSON 契约),而 report_schema.rs 面向的是进程内的规范报告类型——两者共享同一套语义契约,前者保证跨实现的可比对性,后者(即本篇的夹具集)保证语义实现本身不退化。
小结
这份夹具集用一份内联规范报告加四个测试用例,把报告模式 v1 的六类契约语义全部钉死:claim 的证据标签与置信度、带检查范围与时间窗的负证据、带前后哈希与归因的字段差异、可推导且排除自引用的内容哈希、留痕完整的确定性投影与脱敏、以及显式的消费方能力协商与降级标记。验证入口始终是同一命令 cargo test -p runtime report_schema -- --nocapture,实现细节则全部集中在 report_schema.rs 单一模块中,配合 docs/g004-events-reports-contract.md 的契约导读,构成"文档 → 类型 → 夹具 → 测试"一条完整的可追溯链路。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00