Give EvalRun.summary a typed RunSummary value (unified RunError, lenient legacy parsing) so readers stop reaching into a schemaless dict, and route every cross-run rollup — dashboard, scenario ranking, trend, campaign report — through one aggregate_runs seam. Fixes the divergence where stats averaged pass_rate over completed-only runs while the campaign report counted faults as 0.0. Cross-run rule (ADR-0004): genuine faults count 0.0, user-cancelled runs are excluded from both denominators.
2.2 KiB
2.2 KiB
跨 Run 通过率聚合:故障计零、用户取消排除
ADR-0002 定义了单次 Run 内的通过率口径(用例级、含执行失败),但没有覆盖跨 Run 聚合(仪表盘总览、场景排行、趋势、活动周期报告)。实际代码里长出了两个矛盾口径:仪表盘对"仅已完成的 Run"求均值(故障 Run 被排除出分母),活动报告把失败 Run 计 0.0(除以全部)。决定统一为一个聚合规则,由单一函数实现,所有跨 Run 读者共用:
- 执行故障的 Run 计 0.0——延续 ADR-0002 的服务视角:整个 Run 挂了,等价于该 Run 的所有用例都不达标;
- 用户手动取消的 Run 排除出分母(含通过率与可用性)——取消是用户操作,不是被评对象的质量或可用性信号,计 0.0 会诬陷对象,计 1.0 会粉饰,排除是唯一诚实的处理;
- 取消与故障通过
RunSummary.error.code == "cancelled_by_user"区分(取消 Run 的持久化状态仍是 failed)。
Considered Options
- 仅对已完成 Run 求均值(仪表盘现状) — 被否:故障期间仪表盘显示"质量正常",与 ADR-0002 的动机直接冲突;同一个 pass_rate 键在两处得出不同数字,无从解释。
- 非 completed 一律计 0.0(活动现状,单一规则最简) — 被否:用户自己停掉的 Run 拉低质量分,指标不诚实;长期会让人不敢取消 Run。
- 保留两种聚合、显式命名(service_pass_rate / completed_pass_rate) — 被否:两个函数就是两个口径,仪表盘与活动报告的数字依旧不可互相印证,分歧只是换了名字。
Consequences
- 仪表盘 / 场景排行 / 趋势的数字会变:故障 Run 并入分母(数字下降),取消 Run 排除(略回升)。这是修正,不是回归。
- 活动周期报告的口径同步细化:用户取消的子 Run 不再计 0.0(此前极少发生,现有报告几乎不变)。
- 聚合逻辑必须收敛在一个函数里;任何新读者(未来的对比视图、导出)禁止自行读
summary["pass_rate"]重新聚合。 - 取消的判别依赖统一的
error.code——错误形状(此前字符串/对象混用)必须随之统一。