1
0

refactor: 移除顶层 defaults 配置段,简化为 target 显式字段 > 代码内置默认值

- 移除 DefaultsConfig 类型、ProbeConfig.defaults 字段
- 移除 CheckerSchemas.defaults、ResolveContext.defaults、CheckerValidationInput.defaults
- 更新所有 checker schema/resolve/validate 删除 defaults 合并逻辑
- 更新 config-loader 不再读取传递 defaults
- 更新测试、README、DEVELOPMENT、probes.example.yaml
- 重新生成 probe-config.schema.json(不含 defaults)
- 同步 delta specs 到主规范
- 归档 openspec change
This commit is contained in:
2026-05-21 16:53:12 +08:00
parent e448cb4654
commit 79358ba50d
52 changed files with 196 additions and 940 deletions

View File

@@ -24,25 +24,27 @@
- **THEN** 系统 SHALL 以配置错误退出并提示 `server` 中存在未知字段 `dataDir`
### Requirement: YAML 配置文件格式
系统 SHALL 支持通过 YAML 配置文件定义全部运行参数,包括 server 配置、probes 执行配置、可选的 variables 段、checker 默认值和 typed target 列表(含可选 group 字段。server 配置 SHALL 将 HTTP 监听参数放在 `server.listen` 分组,将本地数据目录和历史数据保留时长放在 `server.storage` 分组,将运行时日志配置放在 `server.logging` 分组。拨测全局执行策略 SHALL 放在 `probes.execution` 分组。target MUST 使用 `id` 字段作为唯一标识符MUST 使用 `type` 字段声明 checker 类型SHALL 支持可选的 `name` 字段作为展示名称元信息SHALL 支持可选的 `description` 字段作为目标说明。`name``description` 均 SHALL 允许省略或显式配置为 `null`;省略或显式 null 时解析结果 SHALL 保留为 null。HTTP 领域字段 MUST 放在 `http` 分组cmd 领域字段 MUST 放在 `cmd` 分组db 领域字段 MUST 放在 `db` 分组tcp 领域字段 MUST 放在 `tcp` 分组icmp 领域字段 MUST 放在 `icmp` 分组udp 领域字段 MUST 放在 `udp` 分组LLM 领域字段 MUST 放在 `llm` 分组。HTTP target 的 `http` 分组 SHALL 支持可选的 `ignoreSSL`(布尔值)和 `maxRedirects`非负整数字段。Db target 的 `db` 分组 SHALL 支持 `url`(必填)和 `query`可选字段。Tcp target 的 `tcp` 分组 SHALL 支持 `host`(必填)、`port`(必填)、`readBanner`(可选)、`bannerReadTimeout`(可选)和 `maxBannerBytes`可选字段。Icmp target 的 `icmp` 分组 SHALL 支持 `host`(必填)、`count`(可选,默认 3`packetSize`(可选,默认 56字段。Udp target 的 `udp` 分组 SHALL 支持 `host`(必填)、`port`(必填)、`payload`(可选,默认空字符串)、`encoding`(可选,默认 `text`)、`responseEncoding`(可选,默认 `text`)和 `maxResponseBytes`(可选,默认 4096字段。LLM target 的 `llm` 分组 SHALL 支持 `provider`(必填)、`url`(必填)、`model`(必填)、`prompt`(必填)、`mode`(可选,默认 `http`)、`key`(可选,默认空字符串)、`authToken`(可选)、`headers`(可选)、`ignoreSSL`(可选,默认 `false`)、`options`(可选)和 `providerOptions`(可选)字段。
`defaults.http` 分组 SHALL 仅支持 `headers`(可选)和 `maxBodyBytes`(可选)字段。`defaults.http` 分组 MUST NOT 支持 `method` 字段。`defaults.tcp` 分组 SHALL 仅支持 `bannerReadTimeout`(可选)和 `maxBannerBytes`(可选)字段。`defaults.icmp` 分组 SHALL 仅支持空对象。`defaults.udp` 分组 SHALL 仅支持 `encoding`(可选)、`responseEncoding`(可选)和 `maxResponseBytes`(可选)字段。`defaults.llm` 分组 SHALL 仅支持 `mode`(可选)、`headers`(可选)、`ignoreSSL`(可选)、`options`(可选)和 `providerOptions`(可选)字段。
系统 SHALL 支持通过 YAML 配置文件定义全部运行参数,包括 server 配置、probes 执行配置、可选的 variables 段和 typed target 列表(含可选 group 字段。server 配置 SHALL 将 HTTP 监听参数放在 `server.listen` 分组,将本地数据目录和历史数据保留时长放在 `server.storage` 分组,将运行时日志配置放在 `server.logging` 分组。拨测全局执行策略 SHALL 放在 `probes.execution` 分组。target MUST 使用 `id` 字段作为唯一标识符MUST 使用 `type` 字段声明 checker 类型SHALL 支持可选的 `name` 字段作为展示名称元信息SHALL 支持可选的 `description` 字段作为目标说明。`name``description` 均 SHALL 允许省略或显式配置为 `null`;省略或显式 null 时解析结果 SHALL 保留为 null。HTTP 领域字段 MUST 放在 `http` 分组cmd 领域字段 MUST 放在 `cmd` 分组db 领域字段 MUST 放在 `db` 分组tcp 领域字段 MUST 放在 `tcp` 分组icmp 领域字段 MUST 放在 `icmp` 分组udp 领域字段 MUST 放在 `udp` 分组LLM 领域字段 MUST 放在 `llm` 分组。HTTP target 的 `http` 分组 SHALL 支持可选的 `ignoreSSL`(布尔值)和 `maxRedirects`非负整数字段。Db target 的 `db` 分组 SHALL 支持 `url`(必填)和 `query`可选字段。Tcp target 的 `tcp` 分组 SHALL 支持 `host`(必填)、`port`(必填)、`readBanner`(可选)、`bannerReadTimeout`(可选)和 `maxBannerBytes`可选字段。Icmp target 的 `icmp` 分组 SHALL 支持 `host`(必填)、`count`(可选,默认 3`packetSize`(可选,默认 56字段。Udp target 的 `udp` 分组 SHALL 支持 `host`(必填)、`port`(必填)、`payload`(可选,默认空字符串)、`encoding`(可选,默认 `text`)、`responseEncoding`(可选,默认 `text`)和 `maxResponseBytes`(可选,默认 4096字段。LLM target 的 `llm` 分组 SHALL 支持 `provider`(必填)、`url`(必填)、`model`(必填)、`prompt`(必填)、`mode`(可选,默认 `http`)、`key`(可选,默认空字符串)、`authToken`(可选)、`headers`(可选)、`ignoreSSL`(可选,默认 `false`)、`options`(可选)和 `providerOptions`(可选)字段。顶层 `defaults` 不再是合法配置段。
#### Scenario: 完整配置文件解析
- **WHEN** 系统启动并读取包含 server.listen、server.storage、server.logging、probes.execution、variables、defaults、targets含 id、group 字段)的 YAML 配置文件
- **WHEN** 系统启动并读取包含 server.listen、server.storage、server.logging、probes.execution、variablestargets含 id、group 字段)的 YAML 配置文件
- **THEN** 系统 SHALL 正确解析所有字段并用于初始化服务、调度引擎和对应 checker runner
#### Scenario: defaults 配置段被拒绝
- **WHEN** 配置文件声明顶层 `defaults`
- **THEN** 系统 SHALL 以配置错误退出并提示存在未知字段 `defaults`
#### Scenario: 最简 HTTP 配置文件解析
- **WHEN** 系统读取只包含一个 `type: http` target`id``http.url`)的 YAML 配置文件(省略 server、probes、variables、defaults 和 expect
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段host=127.0.0.1, port=3000, dataDir=./data, interval=30s, timeout=10s, probes.execution.maxConcurrentChecks=20, server.storage.retention=7d, http.method=GET, http.maxBodyBytes=100MB, http.ignoreSSL=false, http.maxRedirects=0, group="default"),并保留 name=null、description=null
- **WHEN** 系统读取只包含一个 `type: http` target`id``http.url`)的 YAML 配置文件(省略 server、probes、variables 和 expect
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段host=127.0.0.1, port=3000, dataDir=./data, interval=30s, timeout=10s, probes.execution.maxConcurrentChecks=20, server.storage.retention=7d, http.method=GET, http.maxBodyBytes=100MB, http.ignoreSSL=false, http.maxRedirects=0, group=default并保留 name=null、description=null
#### Scenario: 最简 cmd 配置文件解析
- **WHEN** 系统读取只包含一个 `type: cmd` target`id``cmd.exec`)的 YAML 配置文件
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段interval=30s, timeout=10s, cmd.cwd 为配置文件所在目录, cmd.maxOutputBytes=100MB并保留 name=null、description=null
#### Scenario: per-target 配置覆盖全局默认值
- **WHEN** 某个 target 指定 interval、timeout 或对应领域分组中的默认字段
- **THEN** 该 target SHALL 使用其自身的值,不受 defaults 中对应字段影响
#### Scenario: per-target 配置覆盖内置默认值
- **WHEN** 某个 target 指定 interval、timeout 或对应领域分组中的可选字段
- **THEN** 该 target SHALL 使用其自身的值,不受对应内置默认值影响
#### Scenario: HTTP target 配置 ignoreSSL
- **WHEN** YAML 配置中 HTTP target 设置 `http.ignoreSSL: true`
@@ -58,43 +60,27 @@
#### Scenario: 最简 tcp 配置文件解析
- **WHEN** 系统读取只包含一个 `type: tcp` target`id``tcp.host``tcp.port`)的 YAML 配置文件
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段interval=30s, timeout=10s, group="default", tcp.readBanner=false, tcp.bannerReadTimeout=2000, tcp.maxBannerBytes=4096并保留 name=null、description=null
#### Scenario: defaults.tcp 配置 banner 默认值
- **WHEN** YAML 配置中 defaults.tcp 设置 `bannerReadTimeout``maxBannerBytes`
- **THEN** 未显式覆盖对应字段的 tcp target SHALL 使用 defaults.tcp 中的值
#### Scenario: defaults.http.method 触发校验错误
- **WHEN** 配置文件中出现 `defaults.http.method` 字段
- **THEN** 系统 SHALL 以配置错误退出,提示 defaults.http 中存在未知字段 method
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段interval=30s, timeout=10s, group=default, tcp.readBanner=false, tcp.bannerReadTimeout=2000, tcp.maxBannerBytes=4096并保留 name=null、description=null
#### Scenario: per-target http.method 仍然有效
- **WHEN** HTTP target 配置 `http.method: POST`
- **THEN** 系统 SHALL 使用 POST 作为该 target 的请求方法
#### Scenario: 未配置 http.method 使用内置默认值
- **WHEN** HTTP target 未配置 `http.method` 且 defaults.http 中无 method 字段
- **WHEN** HTTP target 未配置 `http.method`
- **THEN** 系统 SHALL 使用内置默认值 GET 作为该 target 的请求方法
#### Scenario: 最简 icmp 配置文件解析
- **WHEN** 系统读取只包含一个 `type: icmp` target`id``icmp.host`)的 YAML 配置文件
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段interval=30s, timeout=10s, group="default", icmp.count=3, icmp.packetSize=56并保留 name=null、description=null
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段interval=30s, timeout=10s, group=default, icmp.count=3, icmp.packetSize=56并保留 name=null、description=null
#### Scenario: 最简 udp 配置文件解析
- **WHEN** 系统读取只包含一个 `type: udp` target`id``udp.host``udp.port`)的 YAML 配置文件
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段interval=30s, timeout=10s, group="default", udp.payload="", udp.encoding="text", udp.responseEncoding="text", udp.maxResponseBytes=4096并保留 name=null、description=null
#### Scenario: defaults.udp 配置默认值
- **WHEN** YAML 配置中 defaults.udp 设置 `encoding``responseEncoding``maxResponseBytes`
- **THEN** 未显式覆盖对应字段的 udp target SHALL 使用 defaults.udp 中的值
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段interval=30s, timeout=10s, group=default, udp.payload=空字符串, udp.encoding=text, udp.responseEncoding=text, udp.maxResponseBytes=4096并保留 name=null、description=null
#### Scenario: 最简 llm 配置文件解析
- **WHEN** 系统读取只包含一个 `type: llm` target`id``llm.provider``llm.url``llm.model``llm.prompt`)的 YAML 配置文件
- **THEN** 系统 SHALL 使用内置默认值填充未指定字段interval=30s, timeout=10s, group="default", llm.mode="http", llm.key="", llm.ignoreSSL=false, llm.options.maxOutputTokens=16, llm.options.temperature=0并保留 name=null、description=null
#### Scenario: defaults.llm 配置默认值
- **WHEN** YAML 配置中 defaults.llm 设置 `mode``headers``ignoreSSL``options``providerOptions`
- **THEN** 未显式覆盖对应字段的 llm target SHALL 使用 defaults.llm 中的值
- **THEN** 系统 SHALL 使用内置默认值填充未指定字段interval=30s, timeout=10s, group=default, llm.mode=http, llm.key=空字符串, llm.ignoreSSL=false, llm.options.maxOutputTokens=16, llm.options.temperature=0并保留 name=null、description=null
### Requirement: CLI 参数
系统 SHALL 通过单一命令行参数接受 YAML 配置文件路径。
@@ -126,6 +112,10 @@
- **WHEN** YAML 中某个 target 缺少 id 或 type 字段
- **THEN** 系统 SHALL 以错误退出,提示哪个 target 缺少哪个字段
#### Scenario: 顶层 defaults 字段非法
- **WHEN** YAML 配置文件声明顶层 `defaults`
- **THEN** 系统 SHALL 以配置错误退出,提示 `defaults` 是未知字段
#### Scenario: HTTP target 缺少 url
- **WHEN** YAML 中某个 target 配置 `type: http` 但缺少 `http.url`
- **THEN** 系统 SHALL 以错误退出,提示该 target 缺少 http.url 字段
@@ -203,7 +193,7 @@
- **THEN** 系统 SHALL 以错误退出,提示该 target 的 http.body 必须为字符串
#### Scenario: maxBodyBytes 数字非法
- **WHEN** YAML 中某个 HTTP target 的 `http.maxBodyBytes` 或 defaults.http.maxBodyBytes 是负数、非整数或非安全整数
- **WHEN** YAML 中某个 HTTP target 的 `http.maxBodyBytes` 是负数、非整数或非安全整数
- **THEN** 系统 SHALL 以错误退出,提示 maxBodyBytes 必须为非负安全整数字节数或合法 size 字符串
#### Scenario: status 模式非法
@@ -291,7 +281,7 @@
- **THEN** 系统 SHALL 以错误退出,提示未知字段所在路径
#### Scenario: 动态 headers 字段允许
- **WHEN** YAML 中 `http.headers``defaults.http.headers``expect.headers` 包含任意 header 名称,且对应值符合契约
- **WHEN** YAML 中 `http.headers``expect.headers` 包含任意 header 名称,且对应值符合契约
- **THEN** 系统 SHALL 接受这些动态 header 名称
#### Scenario: 动态 env 字段允许
@@ -312,7 +302,7 @@
#### Scenario: 导出配置 JSON Schema
- **WHEN** 仓库生成或检查配置契约
- **THEN** 根目录 SHALL 存在 draft-07 `probe-config.schema.json`,且其内容 SHALL 与当前公共 fragments 和已注册 checker fragments 组装出的完整 schema 一致(包含 variables 段和 target 的 id/name 字段)。所有 `RawValueExpectation` 字段的 schema SHALL 声明为 `anyOf: [primitiveValue, matcherObject]` 联合类型,`RawKeyedExpectations` 的 dynamic value schema SHALL 复用 `RawValueExpectation`
- **THEN** 根目录 SHALL 存在 draft-07 `probe-config.schema.json`,且其内容 SHALL 与当前公共 fragments 和已注册 checker fragments 组装出的完整 schema 一致(包含 variables 段和 target 的 id/name 字段,且不包含顶层 defaults)。所有 `RawValueExpectation` 字段的 schema SHALL 声明为 `anyOf: [primitiveValue, matcherObject]` 联合类型,`RawKeyedExpectations` 的 dynamic value schema SHALL 复用 `RawValueExpectation`
#### Scenario: JSON Schema RawValueExpectation 接受原始值
- **WHEN** 使用 JSON Schema 校验配置文件中 RawValueExpectation 字段值为数字 `5000`
@@ -431,7 +421,7 @@
- **THEN** 系统 SHALL 接受该配置并在运行期使用完整执行耗时进行 matcher 校验
#### Scenario: 动态 headers 字段允许
- **WHEN** YAML 中 `http.headers``defaults.http.headers``llm.headers``defaults.llm.headers``expect.headers` 包含任意 header 名称,且对应值符合契约
- **WHEN** YAML 中 `http.headers``llm.headers``expect.headers` 包含任意 header 名称,且对应值符合契约
- **THEN** 系统 SHALL 接受这些动态 header 名称
#### Scenario: ContentExpectations 字段必须为数组
@@ -546,11 +536,11 @@
- **THEN** 系统 SHALL 以配置错误退出,提示 tcp.readBanner 必须为布尔值
#### Scenario: tcp bannerReadTimeout 非法
- **WHEN** YAML 中 tcp target 或 defaults.tcp `bannerReadTimeout` 不是非负有限数字
- **WHEN** YAML 中 tcp target 的 `bannerReadTimeout` 不是非负有限数字
- **THEN** 系统 SHALL 以配置错误退出,提示 bannerReadTimeout 格式错误
#### Scenario: tcp maxBannerBytes 非法
- **WHEN** YAML 中 tcp target 或 defaults.tcp `maxBannerBytes` 不是合法 size 值
- **WHEN** YAML 中 tcp target 的 `maxBannerBytes` 不是合法 size 值
- **THEN** 系统 SHALL 以配置错误退出,提示 maxBannerBytes 格式错误
#### Scenario: tcp expect connected 类型非法
@@ -569,12 +559,8 @@
- **WHEN** YAML 中 tcp target 的 `tcp` 分组包含 `tls: true` 等未知字段
- **THEN** 系统 SHALL 以配置错误退出,提示 tcp 分组包含未知字段
#### Scenario: defaults.tcp 未知字段失败
- **WHEN** YAML 中 defaults.tcp 包含 `host` 或其他非默认字段
- **THEN** 系统 SHALL 以配置错误退出,提示 defaults.tcp 包含未知字段
### Requirement: LLM 配置校验
系统 SHALL 在启动期对 llm checker 的配置契约和语义执行严格校验。LLM target 的 `llm` 分组 SHALL 只允许 `provider``url``model``prompt``mode``key``authToken``headers``ignoreSSL``options``providerOptions` 字段。`defaults.llm` 分组 SHALL 只允许 `mode``headers``ignoreSSL``options``providerOptions` 字段。LLM expect SHALL 只允许 `status``headers``output``finishReason``rawFinishReason``usage``stream``durationMs` 字段。`expect.output` MUST 为 `RawContentExpectations` 数组。`expect.finishReason``expect.rawFinishReason``expect.usage.*``expect.stream.firstTokenMs``expect.durationMs` SHALL 使用 `RawValueExpectation`。未知字段、非法 provider、非法 URL、非法 mode、非法认证组合、非法 options、非法 output expectation 和 `mode: http` 下配置 `expect.stream` MUST 导致启动期配置错误。语义校验 MUST NOT 修改 Raw llm expect 输入。
系统 SHALL 在启动期对 llm checker 的配置契约和语义执行严格校验。LLM target 的 `llm` 分组 SHALL 只允许 `provider``url``model``prompt``mode``key``authToken``headers``ignoreSSL``options``providerOptions` 字段。LLM expect SHALL 只允许 `status``headers``output``finishReason``rawFinishReason``usage``stream``durationMs` 字段。`expect.output` MUST 为 `RawContentExpectations` 数组。`expect.finishReason``expect.rawFinishReason``expect.usage.*``expect.stream.firstTokenMs``expect.durationMs` SHALL 使用 `RawValueExpectation`。未知字段、非法 provider、非法 URL、非法 mode、非法认证组合、非法 options、非法 output expectation 和 `mode: http` 下配置 `expect.stream` MUST 导致启动期配置错误。语义校验 MUST NOT 修改 Raw llm expect 输入。
#### Scenario: llm provider 非法
- **WHEN** YAML 中 llm target 的 `llm.provider` 不是 `openai``openai-responses``anthropic`
@@ -593,15 +579,15 @@
- **THEN** 系统 SHALL 以配置错误退出,提示 llm.prompt 必须为非空字符串
#### Scenario: llm mode 非法
- **WHEN** YAML 中 llm target 或 defaults.llm `mode` 不是 `http``stream`
- **WHEN** YAML 中 llm target 的 `mode` 不是 `http``stream`
- **THEN** 系统 SHALL 以配置错误退出,提示 llm.mode 不合法
#### Scenario: llm headers 类型非法
- **WHEN** YAML 中 llm target 或 defaults.llm `headers` 不是对象,或任一 header 值不是字符串
- **WHEN** YAML 中 llm target 的 `headers` 不是对象,或任一 header 值不是字符串
- **THEN** 系统 SHALL 以配置错误退出,提示 llm.headers 格式错误
#### Scenario: llm ignoreSSL 类型非法
- **WHEN** YAML 中 llm target 或 defaults.llm `ignoreSSL` 不是布尔值
- **WHEN** YAML 中 llm target 的 `ignoreSSL` 不是布尔值
- **THEN** 系统 SHALL 以配置错误退出,提示 llm.ignoreSSL 必须为布尔值
#### Scenario: llm authToken provider 非法
@@ -613,11 +599,11 @@
- **THEN** 系统 SHALL 以配置错误退出,提示 key 与 authToken 不能同时配置
#### Scenario: llm options 非法
- **WHEN** YAML 中 llm target 或 defaults.llm `options.maxOutputTokens` 不是正整数,`options.temperature`/`topP`/`topK`/`presencePenalty`/`frequencyPenalty`/`seed` 类型不合法,或 `options.stopSequences` 不是字符串数组
- **WHEN** YAML 中 llm target 的 `options.maxOutputTokens` 不是正整数,`options.temperature`/`topP`/`topK`/`presencePenalty`/`frequencyPenalty`/`seed` 类型不合法,或 `options.stopSequences` 不是字符串数组
- **THEN** 系统 SHALL 以配置错误退出,提示 llm.options 格式错误
#### Scenario: llm providerOptions 非法
- **WHEN** YAML 中 llm target 或 defaults.llm `providerOptions` 不是 JSON object
- **WHEN** YAML 中 llm target 的 `providerOptions` 不是 JSON object
- **THEN** 系统 SHALL 以配置错误退出,提示 llm.providerOptions 格式错误
#### Scenario: llm 禁止字段失败