第十二章:正确性保证

12.1 概述

协议通过形式化的正确性属性(Correctness Properties)定义系统应满足的不变量。这些属性是人类可读规范与机器可验证正确性保证之间的桥梁,通过属性测试(Property-Based Testing)进行验证。

12.2 正确性属性列表

属性 1:技能描述符结构完整性

对于任意有效的 SkillDescriptor 对象,它必须包含所有必需字段:protocol(含 version)、idnameversioncapability_typedescriptionproviderendpointinputsoutputauthaccess。缺少任何必需字段的对象不应通过验证。

属性 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(含 idtype)、skill_idinputs 字段
  • 对于任意有效的 InvocationResponse,必须包含 execution_idstatusskill_idtimestamps 字段

属性 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 }
);