1
0
Files
DiAL/openspec/specs/checker-runner-abstraction/spec.md

7.2 KiB
Raw Blame History

Purpose

定义 Checker 接口规范、注册机制、CheckerContext 上下文注入,以及共享 expect 断言函数的职责边界。此 capability 是 checker 系统的架构基础,不定义任何具体 checker 类型的业务行为。

Requirements

Requirement: Checker 接口定义

系统 SHALL 在 src/server/checker/runner/types.ts 中定义 Checker 接口,包含 typeresolveexecuteserialize 四个成员。CheckerContext SHALL 包含引擎注入的 AbortSignal

Scenario: Checker 接口包含必要方法

  • WHEN 开发者实现一个新的 Checker
  • THEN 该实现 MUST 提供 type(字符串标识)、resolve(target, context)(解析配置并校验)、execute(target, ctx)(执行探测返回 CheckResultserialize(target)(返回 target 展示文本和 config JSON

Scenario: CheckerContext 注入 signal

  • WHEN 引擎调用 checker.execute(target, ctx)
  • THEN ctx.signal SHALL 是一个由引擎创建的 AbortSignal,在超时或引擎关闭时 abort

Requirement: CheckerRegistry 注册中心

系统 SHALL 在 src/server/checker/runner/registry.ts 中提供 CheckerRegistry 类,支持 register(checker)get(type)supportedTypes。重复注册同一 type SHALL 抛出错误。

Scenario: 注册并获取 Checker

  • WHEN 调用 registry.register(new HttpChecker()) 后再调用 registry.get("http")
  • THEN 返回的 SHALL 是之前注册的 HttpChecker 实例

Scenario: 获取未注册的 type

  • WHEN 调用 registry.get("unknown") 且未注册对应 type 的 checker
  • THEN 系统 SHALL 抛出错误,提示不支持的 probe type

Scenario: 重复注册

  • WHEN 同一 type 值被重复 register()
  • THEN 系统 SHALL 抛出错误,提示该 type 已注册

Scenario: 查询支持的 type 列表

  • WHEN 注册了 "http" 和 "command" 两个 checker 后查询 registry.supportedTypes
  • THEN 返回的数组 SHALL 包含 ["http", "command"](按注册顺序)

Requirement: 引擎通过 registry 调度 checker

系统 SHALL 在 ProbeEngine.runCheck() 中通过 checkerRegistry.get(target.type).execute(target, ctx) 调度检查,替代原有的 switch/case 分支。

Scenario: 引擎使用 registry 调度

  • WHEN engine 需要执行一个 type 为 "http" 的 target
  • THEN engine SHALL 从 checkerRegistry 中获取对应 checker 并调用其 execute() 方法,不再使用 switch/case

Scenario: 引擎注入超时 signal

  • WHEN engine 调度一次 checker 执行
  • THEN engine SHALL 创建 AbortController,设置超时定时器,将 controller.signal 注入 CheckerContext,执行完成后清理定时器

Requirement: 配置解析通过 registry 委托 checker

系统 SHALL 在 config-loader.tsresolveTarget() 中通过 checkerRegistry.get(target.type).resolve(target, context) 委托解析,替代原有的 if/else 分支。validateConfig() SHALL 仅校验通用字段name 非空、name 不重复、group 类型),不再包含 type 专属字段校验。

Scenario: 配置解析委托 checker

  • WHEN config-loader 解析一个 type 为 "command" 的 target
  • THEN config-loader SHALL 调用 checkerRegistry.get("command").resolve() 进行解析、校验和默认值填充

Scenario: 通用字段校验保留在 config-loader

  • WHEN YAML 配置中某个 target 缺少 name 或 type 字段
  • THEN config-loader 的 validateConfig() SHALL 仍负责校验这些通用字段

Scenario: type 专属校验下沉到 checker

  • WHEN YAML 配置中 HTTP target 缺少 http.url
  • THEN HttpChecker 的 resolve() SHALL 抛出校验错误,提示缺少必填字段

Scenario: HTTP method 非法校验

  • WHEN YAML 配置中 HTTP target 的 http.method 不在合法方法列表中
  • THEN HttpChecker 的 resolve() SHALL 抛出校验错误,提示 method 不合法

Scenario: URL 格式校验

  • WHEN YAML 配置中 HTTP target 的 http.url 不以 http://https:// 开头
  • THEN HttpChecker 的 resolve() SHALL 抛出校验错误,提示 URL 格式不合法

Requirement: 存储序列化通过 registry 获取展示格式

系统 SHALL 在 ProbeStore.syncTargets() 中通过 checkerRegistry.get(t.type).serialize(t) 获取每个 target 的展示摘要(target 列)和配置 JSONconfig 列),替代 buildTargetDisplay() / buildTargetConfig() 中的类型分支。

Scenario: 序列化委托 checker

  • WHEN store 同步 targets 表
  • THEN store SHALL 对每个 target 调用对应 checker 的 serialize() 方法获取 { target, config }

Requirement: 共享 expect 断言函数

系统 SHALL 在 src/server/checker/runner/shared/ 中提供可被多个 checker 复用的 expect 函数。checker 专用的 expect 函数 SHALL 保留在各自子包内。

Scenario: 共享 duration 断言

  • WHEN 任何 checker 需要校验执行耗时
  • THEN SHALL 调用 runner/shared/duration.ts 中的 checkDuration(durationMs, maxDurationMs?),返回统一的 ExpectResult

Scenario: 共享 text 规则断言

  • WHEN 任何 checker 需要对文本输出执行有序规则校验
  • THEN SHALL 调用 runner/shared/text.ts 中的 checkTextRules(text, rules, phase),返回统一的 ExpectResult

Scenario: 共享 body 规则断言

  • WHEN 任何 checker 需要对文本体执行 contains/regex/json/css/xpath 规则校验
  • THEN SHALL 调用 runner/shared/body.ts 中的 checkBodyExpect(body, rules),返回统一的 ExpectResult

Scenario: HTTP 专用 expect

  • WHEN HTTP checker 需要校验响应状态码和响应头
  • THEN SHALL 调用 runner/http/expect.ts 中的 checkStatus()checkHeaders()

Scenario: Command 专用 expect

  • WHEN Command checker 需要校验退出码
  • THEN SHALL 调用 runner/command/expect.ts 中的 checkExitCode()

Requirement: 超时控制由引擎注入 signal

Checker 实现的 execute() MUST 使用 ctx.signal 感知超时,不得自行创建 AbortControllersetTimeout 用于超时控制。仅 command checker 可在 signal abort 时 proc.kill() 以确保子进程被终止。

Scenario: HTTP checker 使用 signal

  • WHEN HttpChecker 执行 HTTP 请求
  • THEN SHALL 将 ctx.signal 传入 fetch()signal 选项,不自行创建 AbortController

Scenario: Command checker 响应 signal

  • WHEN CommandChecker 执行命令且 signal 被 abort
  • THEN SHALL 调用 proc.kill() 终止子进程,并在 CheckResult 中记录超时错误

Requirement: CheckFailure.phase 使用 string 类型

shared/api.tsCheckFailure.phase 的类型 SHALL 定义为 string,替代原有的硬编码联合类型 "status" | "duration" | "headers" | "body" | "exitCode" | "stdout" | "stderr"

Scenario: phase 支持 checker 专用值

  • WHEN command checker 在执行失败spawn error时生成 failure
  • THEN failure.phase SHALL 可以是 "spawn" 等任意字符串值,类型系统 SHALL 不报错

Scenario: 前端展示 phase 不依赖硬编码类型

  • WHEN 前端收到任意 phase 字符串值
  • THEN 前端 SHALL 直接展示而不做类型判断