package validator import ( "context" "errors" ) // Severity is the disposition tier attached to a Verdict. Warn does not // block a commit but feeds the circuit breaker as a sample; Block // forces rollback and feeds the breaker as a failure. Two tiers are // deliberate -- the engine needs a commit/no-commit decision plus one // observability knob, not a 5-level taxonomy. // // Severity 是 Verdict 的处置等级. Warn 不阻断 commit 但作为样本喂给 // 熔断器; Block 强制 rollback 并作为失败喂给熔断器. 两档刻意最小 -- // 引擎只需 commit/no-commit 的二值决策加一个可观测旋钮, 不需要 5 级 // 分类体系. type Severity string // Severity values. An empty Severity is treated by downstream consumers // as Warn (lenient default) to avoid accidental blocking from a // Validator that forgot to set the field. // // Severity 取值. 空 Severity 下游消费方按 Warn 处理 (宽松默认), 避免 // 实现忘记设置字段导致误阻断. const ( SeverityWarn Severity = "warn" SeverityBlock Severity = "block" ) // DiffInput carries the staged diff that a Validator must review. // SourceTool is the dispatch key (e.g. "SQLCAS" / "SQLDryRun"), letting // implementations decide how to decode Raw without the engine baking // any schema assumption. Raw is the tool-specific serialized payload // (typically tools.Result.Data marshalled as JSON). Metadata carries // free-form context such as tenant / session / schema hints. // // Why not any for the diff: losing the SourceTool self-description // breaks LLM prompting, rule dispatch, and debuggability. Why not a // concrete struct per tool: SQL CAS / DryRun / future HTTP-patch diffs // have incompatible shapes. SourceTool + Raw is the compromise -- // dispatch key plus opaque bytes. // // DiffInput 承载待审的 staged diff. SourceTool 是分发键 (如 "SQLCAS" / // "SQLDryRun"), 让实现自行决定如何 decode Raw, 引擎不假设任何 schema. // Raw 是工具特定的序列化载荷 (通常是 tools.Result.Data 的 JSON). // Metadata 承载 tenant / session / schema hints 等自由扩展上下文. // // 为什么不用 any 放 diff: 丢失 SourceTool 自述破坏 LLM prompt / 规则 // dispatch / 调试. 为什么不给每工具具体 struct: SQL CAS / DryRun / 未来 // HTTP-patch 的 diff 结构不兼容. SourceTool + Raw 是折中 -- dispatch // key 加 opaque bytes. type DiffInput struct { SourceTool string Raw []byte Metadata map[string]any } // Verdict is the Validator output. Approved=true means the diff may // commit; false means the caller must rollback. Score in [0, 1] is an // advisory confidence that the circuit breaker may use for weighting. // Reason is human-readable for audit and UI. Details carries a per-rule // or per-dimension breakdown (mirrors evolve.Evaluator breakdown). // ValidatorName identifies which Validator produced this Verdict for // audit trails; PolicyVersion is the implementation's version tag so // replay audits can tell which policy approved a past diff (mirrors // evolve.Change.Version). // // A non-nil error from Validate is orthogonal to the Verdict -- on // error the Validator has not decided, and the caller MUST treat the // situation as Block by default (fail-closed). // // Verdict 是 Validator 输出. Approved=true 表示 diff 可 commit; false // 则调用方必须 rollback. Score 在 [0, 1] 是建议置信度, 熔断器可用于加权. // Reason 面向审计和 UI 人类可读. Details 按规则 / 维度分解 (对齐 // evolve.Evaluator breakdown). ValidatorName 标识产出此 Verdict 的 Validator // 用于审计; PolicyVersion 是实现的版本标签, 回放审计时可判断当时是哪版 // 策略放行的 (对齐 evolve.Change.Version). // // Validate 返回非 nil error 与 Verdict 正交 -- 出错即未决策, 调用方必须 // 默认按 Block 处置 (fail-closed). type Verdict struct { Approved bool Score float64 Reason string Severity Severity Details map[string]any ValidatorName string PolicyVersion string } // Validator checks a staged diff and returns a Verdict. Sync-only by // design -- streaming or async variants can be built as wrappers at the // orchestrator layer without dragging the interface into lifecycle // management. The implementation owns its own retry / timeout / cache // policy (an LLM-backed Validator and a pure-rule one have fundamentally // different strategies; forcing a common policy here would be a // straitjacket). // // Validator 校验 staged diff 并返回 Verdict. 仅同步 -- streaming 或异步 // 变体可在 orchestrator 层做 wrapper, 不把生命周期管理拖入接口. 实现方 // 自己持有 retry / timeout / cache 策略 (LLM-backed 与纯规则的 Validator // 策略本质不同, 在此强制统一反而是紧身衣). type Validator interface { // Name returns a stable identifier used for logging, audit, and // VerdictSink routing. Two Validators must not share the same Name // within one process. // // Name 返回稳定标识符, 用于日志 / 审计 / VerdictSink 路由. // 同进程内两个 Validator 不能重名. Name() string // Validate reviews the diff and returns a Verdict. On non-nil error // the caller MUST treat the outcome as Block. // // Validate 审查 diff 返回 Verdict. 返回非 nil error 时调用方必须按 // Block 处置. Validate(ctx context.Context, diff DiffInput) (Verdict, error) } // Sentinel errors. Implementations should wrap these with %w so callers // can detect error class with errors.Is. // // 哨兵错误. 实现方应以 %w 包装, 调用方用 errors.Is 识别错误类别. var ( // ErrValidatorBackend indicates the Validator's underlying backend // (LLM API, ML service, rule engine) failed. The Verdict is unset; // treat as Block. // // ErrValidatorBackend 表示 Validator 的下游 backend (LLM API / ML // 服务 / 规则引擎) 调用失败. Verdict 未产出; 按 Block 处置. ErrValidatorBackend = errors.New("validator: backend call failed") // ErrVerdictParse indicates the backend returned a response the // Validator could not parse into a Verdict. Treat as Block. // // ErrVerdictParse 表示 backend 返回的响应无法解析为 Verdict. 按 // Block 处置. ErrVerdictParse = errors.New("validator: verdict parse failed") // ErrUnknownDiffType indicates the Validator does not know how to // handle the given SourceTool. Implementations dispatching on // SourceTool should return this for unrecognised keys rather than // silently approving. // // ErrUnknownDiffType 表示 Validator 不识别给定的 SourceTool. 按 // SourceTool dispatch 的实现遇到未知 key 应返回此错误而非静默放行. ErrUnknownDiffType = errors.New("validator: unknown source_tool") )