- 新增 CheckerDefinition.normalize 必需方法,typecheck 兜底遗漏实现 - 新增 expect/normalize.ts 共享 helper(compactExpect、normalizeValue、 normalizeContent、normalizeKeyed) - 为 HTTP、Cmd、DB、TCP、UDP、ICMP、LLM、WS、DNS 各新增独立 normalize.ts - 简化 normalizer.ts:删除所有 checker type switch,改为 registry 委托 - 修复 DNS authoring 简写 bug:durationMs、valueCount、result 等字段 现可通过完整加载链路 - 新增 DNS 回归测试和 registry 级合同测试 - 更新 docs/development/checker.md:补充 normalize 规范、文件结构、 测试要求和 checklist
10 KiB
Checker 开发
Checker 是 DiAL 的核心扩展单元。每个 checker 是 src/server/checker/runner/<type>/ 下的自包含目录,包含类型、schema、语义校验、执行逻辑、序列化和断言。
适用场景:新增 checker、修改 checker 配置或 expect、调整 checker 注册机制、改动 checker 测试或用户文档同步规则。
新增或修改 checker 前必须阅读 开发入口、配置文件、校验规则 和 Checker 用户文档。还应阅读现有同类 checker 的实现和测试,例如 src/server/checker/runner/http/ 与 tests/server/checker/runner/http/。
设计原则
- 每个 checker 必须自包含在
src/server/checker/runner/<type>/。 - checker 专属类型、schema、validate、execute、expect、normalize 和协议辅助逻辑放在同一目录。
- 注册只修改
src/server/checker/runner/index.ts,中间层不新增 type switch。 - schema 层只描述契约,语义规则放入
validate.ts。 resolve()只做默认值填充、路径解析和单位转换,不执行校验。execute()必须支持CheckerContext.signal超时取消。- expect 字段必须选择合适断言模型,不为了统一而滥用 ValueMatcher。
- failure phase 命名遵循去单位后缀规则,例如
durationMs对应duration。
架构目标
checkerRegistry
├── runner/index.ts
├── schema/builder.ts
├── schema/validate.ts
├── config-loader.ts
├── engine.ts
└── store.ts
注册后,中间层通过 registry 自动委托 schema 生成、契约校验、配置 normalize、配置 resolve、执行和序列化。新增 checker 不应在中间层新增 switch/case 或类型分支。
标准文件结构
| 文件 | 职责 |
|---|---|
index.ts |
模块入口,re-export Checker 类 |
types.ts |
Checker 专属类型 |
schema.ts |
TypeBox 契约 schema,包含 config 和 expect |
validate.ts |
启动期语义校验 |
normalize.ts |
Checker 专属 authoring expect 归一化 |
execute.ts |
Checker 类,实现 normalize、resolve、execute、serialize |
expect.ts |
Checker 专用断言函数 |
| 其他文件 | 协议解析、编码、provider 适配、平台命令封装等专属逻辑 |
类型定义
在 types.ts 中定义:
RawXxxTargetConfigRawXxxExpectConfigResolvedXxxExpectConfigResolvedXxxTarget extends ResolvedTargetBase
不需要修改顶层 checker/types.ts。base interface 使用 index signature 支持扩展。
Schema
checker 必须提供 CheckerSchemas,包含 Authoring 和 Normalized 两套 config/expect 片段。Authoring 描述用户 YAML 可写 DSL,Normalized 描述 normalizer 输出。
常用 fragments:
| Fragment | 用途 |
|---|---|
durationSchema |
时长字符串 |
sizeSchema |
大小单位 |
statusCodePatternSchema |
HTTP 状态码或范围 |
stringMapSchema |
headers、env 等字符串映射 |
createValueMatcherSchema() |
ValueMatcher |
createContentExpectationsSchema() |
ContentExpectations |
createKeyedExpectationsSchema() |
KeyedExpectations |
默认对象策略为 additionalProperties: false。只有明确的动态键值表可以开放任意键名。
语义校验
在 validate.ts 中实现 JSON Schema 无法表达的规则,统一返回 ConfigValidationIssue[],不要直接拼接最终错误字符串。
共享校验工具包括:
| 函数 | 用途 |
|---|---|
validateRawValueExpectation |
校验 Raw ValueExpectation |
validateRawContentExpectations |
校验 ContentExpectations |
validateRawKeyedExpectations |
校验 KeyedExpectations |
validateJsonPath |
校验项目支持的 JSONPath 子集 |
isJsonValue |
判断合法 JSON value |
normalize 规范
normalize() 在 CheckerDefinition 中定义为必需方法,负责将 authoring expect DSL 转换为 normalized 形态。输入为变量已解析后的 target,输出为适配 normalized schema 的 target。该方法在 resolve() 和 normalized contract 校验之前执行。
在 normalize.ts 中实现 normalizeTargetExpect 函数,execute.ts 中的 normalize 方法委托到该函数。
共享 normalize helper 位于 src/server/checker/expect/normalize.ts:
| 函数 | 用途 |
|---|---|
compactExpect |
合并两个 expect record,过滤 undefined 字段 |
normalizeValue |
ValueMatcher 原始值简写展开为 {equals: value} |
normalizeContent |
ContentExpectations 简写展开为 normalized 形态 |
normalizeKeyed |
KeyedExpectations 对象形态展开为 [{key, matcher}] 数组 |
import { compactExpect, normalizeContent, normalizeKeyed, normalizeValue } from "../../expect/normalize";
export function normalizeTargetExpect(target: RawTargetConfig): RawTargetConfig {
if (target.expect === undefined || !isPlainObject(target.expect)) return target;
const raw = target.expect as Record<string, unknown>;
return {
...target,
expect: compactExpect(raw, {
/* checker 专属字段映射 */
}),
};
}
expect 字段的归一化规则:ValueMatcher 字段调用 normalizeValue(),ContentExpectations 字段调用 normalizeContent(),KeyedExpectations 字段调用 normalizeKeyed(),boolean/enum/array 等非断言模型字段直接透传。
resolve 规范
resolve() 只做内置默认值填充、路径解析、单位转换,不执行校验。输入已经通过 Normalized schema 和语义校验,expect 已是 normalized 形态。
const expect = target.expect as ResolvedXxxExpectConfig | undefined;
const resolvedExpect: ResolvedXxxExpectConfig = expect
? { ...expect, status: expect.status ?? [200] }
: { status: [200] };
返回值使用 satisfies ResolvedXxxTarget 确保类型正确。
execute 规范
- 始终记录
timestamp和start = performance.now()。 - 通过
ctx.signal支持超时取消。 - 首个 expect 失败即停止,返回带
failure的结果。 - 成功时
failure: null, matched: true。 - 异常时使用
errorFailure()。 - 不匹配时使用
mismatchFailure()。 expected参数应传用户可读值,必要时使用displayValueExpectation()。
expect 字段选择
| 场景 | 模型 |
|---|---|
| 状态类结果且集合小而稳定 | enum 或 boolean |
| 单值数字指标或字符串元数据 | ValueMatcher |
| 文本、JSON、HTML、XML 或半结构化内容 | ContentExpectations |
| 动态键值表 | KeyedExpectations |
不要为了统一而把状态类字段改成 ValueMatcher。一个 expect 字段只能对应一种断言模型。
注册
- 创建
src/server/checker/runner/<type>/index.ts。 - 在
src/server/checker/runner/index.ts添加导入。 - 在 registry 初始化数组中添加 checker 实例。
注册后,schema builder、validate、config-loader、engine、store 会自动按 registry 分发。
测试要求
测试文件放在 tests/server/checker/runner/<type>/,结构镜像源文件。
| 测试类别 | 覆盖内容 |
|---|---|
| 契约测试 | TypeBox schema 与 JSON Schema 导出一致性 |
| 语义校验测试 | 合法和非法配置 |
| normalize 测试 | authoring expect 简写展开和 normalized contract 通过 |
| resolve 测试 | 默认值合并、路径解析、单位转换 |
| execute 测试 | 成功、失败、超时、expect 组合 |
| 注册测试 | registry 注册行为 |
| 配置加载测试 | 含新 checker 的 YAML 完整加载流程 |
文档和 schema 更新
新增或修改 checker 时通常需要更新:
probes.example.yamlprobe-config.schema.json,通过bun run schema生成docs/user/checkers/<type>.mddocs/user/checkers/README.mddocs/user/expectations.md,仅当断言模型、状态模型或通用规则变化docs/user/configuration.md,仅当 target 通用字段或配置加载形态变化docs/development/checker.md,仅当 checker 开发机制、测试要求或 checklist 变化docs/README.md和openspec/config.yaml,仅当文档同步规则变化
验证命令
新增或修改 checker 后通常需要运行:
bun run schema
bun run schema:check
bun run check
影响构建、Docker 或发布包时追加运行 bun run verify。
完成检查清单
□ checker 类型、schema、validate、normalize、resolve、execute、serialize 已实现
□ checker 已在 runner/index.ts 注册
□ 配置契约、语义校验和 JSON Schema 导出已同步
□ probes.example.yaml 已添加或更新示例
□ tests/server/checker/runner/<type>/ 已覆盖契约、校验、normalize、resolve、execute、注册和配置加载
□ docs/user/checkers/<type>.md 已添加或更新
□ docs/user/checkers/README.md 已添加或更新
□ 文档影响分析已完成,必要文档已同步
□ bun run schema 和 bun run schema:check 已通过
□ bun run check 已通过
□ bun run verify 已通过或记录未执行原因
更新触发条件
修改 checker 开发机制、目录结构、schema/validate/normalize/resolve/execute/expect 约定、测试要求、验证命令或文档同步 checklist 时,必须更新本文档。