# 跨 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`——错误形状(此前字符串/对象混用)必须随之统一。