BLUEPRINT
第十二章:正确性保证
12.1 概述
协议通过形式化的正确性属性(Correctness Properties)定义系统应满足的不变量。这些属性是人类可读规范与机器可验证正确性保证之间的桥梁,通过属性测试(Property-Based Testing)进行验证。
12.2 正确性属性列表
属性 1:技能描述符结构完整性
对于任意有效的 SkillDescriptor 对象,它必须包含所有必需字段:protocol(含 version)、id、name、version、capability_type、description、provider、endpoint、inputs、output、auth、access。缺少任何必需字段的对象不应通过验证。
属性 2:枚举字段值域约束
对于任意有效的 SkillDescriptor 对象:
capability_type的值必须是["plugin", "api", "knowledge", "task"]之一access的值必须是["public", "restricted", "private"]之一auth.type的值必须是["api_key", "oauth2", "custom", "none"]之一
属性 3:序列化/解析往返一致性
对于任意有效的 SkillDescriptor 对象,将其序列化为 JSON 文档后再解析回对象,所得结果应与原始对象语义等价:
parse(serialize(descriptor)) == descriptor
属性 4:Schema 验证正确性
- 对于任意符合 Schema 的 JSON 文档,Schema Validator 应成功解析并返回结构化对象
- 对于任意不符合 Schema 的 JSON 文档,Schema Validator 应返回包含具体违规字段名称和错误原因的错误信息
属性 5:技能标识符唯一性
对于任意技能索引中的技能描述符集合,所有技能的 id 字段值必须互不相同。
属性 6:技能索引正确性与访问控制
对于任意包含 N 个技能的提供者域,当未认证请求访问 Well-Known URI 时:
- 返回的技能索引应包含所有
access不为"private"的技能 - 不包含任何
access为"private"的技能 - 索引中每个条目的
descriptor_url应指向有效的技能描述符
属性 7:能力类型过滤正确性
对于任意技能集合和任意 CapabilityType 过滤值:
- 过滤后的结果集中,每个技能的
capability_type等于过滤值 - 原始集合中所有匹配该类型的技能都出现在结果中
属性 8:调用消息结构完整性
- 对于任意有效的 InvocationRequest,必须包含
caller(含id和type)、skill_id和inputs字段 - 对于任意有效的 InvocationResponse,必须包含
execution_id、status、skill_id和timestamps字段
属性 9:协议版本 SemVer 格式
对于任意有效的 ProtocolVersion 对象,其 version 字段必须符合 MAJOR.MINOR.PATCH 格式,其中 MAJOR、MINOR、PATCH 均为非负整数。
属性 10:版本不兼容检测
对于任意技能描述符的协议版本和消费者支持的协议版本:
- 当描述符的主版本号(MAJOR)大于消费者支持的主版本号时,返回版本不兼容错误
- 当主版本号相同时,正常处理
属性 11:TypeScript 与 JSON Schema 语义一致性
- 对于任意由 TypeScript 类型定义生成的有效 SkillDescriptor 实例,该实例应通过 JSON Schema 验证
- 对于任意通过 JSON Schema 验证的 JSON 文档,它应能被 TypeScript 类型系统接受
12.3 测试策略
双轨测试方法
| 方法 | 目的 | 工具 |
|---|---|---|
| 单元测试 | 验证具体示例、边界情况和错误条件 | vitest |
| 属性测试 | 验证跨所有输入的通用属性 | fast-check |
属性测试配置
- 每个属性测试最少运行 100 次迭代
- 使用 fast-check 库生成随机测试数据
- 每个正确性属性由单个属性测试实现
- 测试标签格式:
Feature: skill-sharing-protocol, Property {N}: {description}
测试示例
import fc from 'fast-check';
fc.assert(
fc.property(
skillDescriptorArbitrary,
(descriptor) => {
// Feature: skill-sharing-protocol, Property 3: round-trip consistency
const serialized = serialize(descriptor);
const parsed = parse(serialized);
expect(parsed).toEqual(descriptor);
}
),
{ numRuns: 100 }
);
