Files
codex-app-server/cloud/audit_scorecard.py
T
2026-11-03 02:49:27 +00:00

774 lines
38 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""7 维项目级审计评分卡(纯函数、标准库 only)。
定位:这是一个评分卡,不是门禁、不是审计、不是自动循环。
它不重写 metrics、arch_conformance、ledger、audit_loop 的逻辑,
只接受调用方显式传入的结构化证据,算出 7 维各自的分数。
计分规则(钉死,不可改为平均分达标):
- 每维分数 0-10,保留一位小数;>=9 通过,<9 不通过。
- 总体结论:7 维全部 >=9 才算达标(overall="pass")。
任一维 <9,总体就不达标(overall="not_pass")。
- 平均分只作参考信息(average_score),绝不能作为达标依据。
代码里把“平均分达标”写成明确的不通过路径并加测试钉住:
例如 6 维 10 分、1 维 4 分,平均 9.1 分,总体仍是不达标。
- 未知处理:任何一维的数据源缺失、未知、未验证,
该维分数记为 None(未知),不记 0、不记 10、不估算,
总体直接不达标,并列出缺失项(missing_items)。
“没数据”既不算通过,也不算 0 分去拉低平均分后又用平均分糊弄。
- 未实测处理:调用方传入“夹具模拟”“视口替代”“未编译端代码评审”
这类证据时,该项记为“未实测”(untested_items),而不是“通过”。
若某维的关键证据全部是未实测,该维记为 None。
- 阈值:DEFAULT_THRESHOLDS 全部标注 source="test_default_unapproved"
(测试默认值、未拍板)。生产阈值须用户拍板后,由调用方显式
传入 thresholds(含 source="user_approved")才能替换默认值。
本模块绝不把测试初值写成生产值。
- 与已定语义的关系(本模块不改它们):
#238 evaluate_feedback 的 auto_apply 保持 False;
#247 纠正为 pass 本身不解封;
#244 CONDITIONAL 先映射为 fail 再记(本模块只接受已映射的结论,
若调用方传入 verdict="conditional",记为未实测/无效证据,不算通过)。
- 基线漂移门禁(v7 机制第 5 项,本模块新增):
build_scorecard(..., baseline_head=None) 的 baseline_head 是机器字段,
只能由调用方显式传入当前 main 头;本模块绝不在生产代码里探测
版本控制状态、绝不读版本控制目录、绝不编造一个头。
不传入(或传入空串)时 baseline_head 为 None,并在 missing_items
里记 baseline_head_missing,不把缺失读成“与当前一致”。
check_baseline_stale(scorecard, current_main_head) 是唯一的漂移判定:
current_main_head != baseline_head 时 stale=True,且该旧分整体结论
不得再作 pass 依据(返回里以 usable_as_pass_basis=False 明示,
调用方凭此字段判断)。stale 是附加字段:本项绝不改写
build_scorecard 的 overall / overall_pass 旧语义,旧值保留以便追溯,
测试断言 stale 字段而非改写旧 overall。
冻结版的服务端存储见 cloud/scorecard_baseline_store.py(只追加、
不提供改删旧冻结行的方法);阈值仍一律标 test_default_unapproved,
除非调用方显式传入 user_approved。
7 维定义(不要擅自增维、减维、改名换义):
1. delivery_quality 交付质量
2. gate_audit 门禁与审计有效性
3. arch_contract 架构与契约一致性
4. test_coverage 测试与验证覆盖
5. security_ledger 安全与账本可信
6. cost_quota 成本与配额可观测
7. ops_scale 生产运维与规模
10 维到 7 维的合并映射(写进文档,也写在这里):
- 流程完整性并入门禁与审计有效性(G0-G8 最终靠门禁和重审证明);
- 自动化程度并入门禁与审计有效性 + 生产运维与规模
(自动化的证据就是自动循环是否真实跑通、真实 App 是否安装);
- 文档与技能并入架构与契约一致性
(技能文本、附录溯源、移植规则最终都是契约一致性的一部分)。
输入格式:build_scorecard(evidence, thresholds=None, rubric_version=None)
rubric_version 只是卡片上的版本锚点(版本化持久存储见
cloud/scorecard_rubric.py:标尺改动写新版本行、旧行只读,
本函数不查库、不改旧分)。
evidence: dict,键为上面 7 个维 id,值为该维的证据 dict。
每个维的证据字段见各 _score_* 函数的 docstring。
调用方必须带来源、时间、检查名;本模块不自动从测试文件、
摘要文档、PR 描述里拼凑分数。
"""
from __future__ import annotations
import hashlib
import json
PASS_SCORE = 9.0
DIMENSIONS = [
("delivery_quality", "交付质量"),
("gate_audit", "门禁与审计有效性"),
("arch_contract", "架构与契约一致性"),
("test_coverage", "测试与验证覆盖"),
("security_ledger", "安全与账本可信"),
("cost_quota", "成本与配额可观测"),
("ops_scale", "生产运维与规模"),
]
DIMENSION_IDS = [d[0] for d in DIMENSIONS]
# 阈值表:每一项都带 source,默认值一律是测试初值、未拍板。
# “9 分线”是达到 9 分的下限,“10 分线”是达到 10 分的下限,
# “floor”是低于该值后分数进入 0-5 区间的参考点(不是达标线)。
# 这些数值本身不是生产标准;生产阈值须用户拍板后显式传入。
DEFAULT_THRESHOLDS = {
"defect_rate_nine": {"value": 0.05, "source": "test_default_unapproved"},
"defect_rate_ten": {"value": 0.02, "source": "test_default_unapproved"},
"defect_rate_floor": {"value": 0.20, "source": "test_default_unapproved"},
"first_pass_nine": {"value": 0.90, "source": "test_default_unapproved"},
"first_pass_ten": {"value": 0.95, "source": "test_default_unapproved"},
"first_pass_floor": {"value": 0.50, "source": "test_default_unapproved"},
"rework_rate_nine": {"value": 0.10, "source": "test_default_unapproved"},
"rework_rate_ten": {"value": 0.05, "source": "test_default_unapproved"},
"rework_rate_floor": {"value": 0.50, "source": "test_default_unapproved"},
"overturn_rate_floor": {"value": 0.30, "source": "test_default_unapproved"},
"coverage_nine": {"value": 0.85, "source": "test_default_unapproved"},
"coverage_ten": {"value": 0.90, "source": "test_default_unapproved"},
"coverage_floor": {"value": 0.75, "source": "test_default_unapproved"},
"sampling_normal": {"value": 0.10, "source": "test_default_unapproved"},
"sampling_critical": {"value": 0.50, "source": "test_default_unapproved"},
"parallel_scale_target": {"value": 40, "source": "test_default_unapproved"},
}
UNTESTED_KINDS = {"fixture", "viewport_substitute", "code_review_only",
"fixture_simulated", "dry_run", "estimated"}
def thresholds_source(thresholds):
"""阈值来源:只有调用方显式传入 user_approved 才算已拍板。"""
if not thresholds:
return "test_default_unapproved"
sources = set()
for entry in thresholds.values():
if isinstance(entry, dict):
sources.add(entry.get("source", "test_default_unapproved"))
else:
sources.add("test_default_unapproved")
if sources == {"user_approved"}:
return "user_approved"
return "test_default_unapproved"
# 旧私有名的兼容别名(v2cov 评分卡测试仍从这里导入它)。
_thresholds_source = thresholds_source
def _effective_thresholds(thresholds):
"""实际用于计分的阈值表:默认值 + 调用方显式覆盖,逐项归一化。
未被覆盖的键仍取 DEFAULT_THRESHOLDS(与 _t 的逐键回落一致),
因此卡片上报告的 thresholds 就是真正算分的表,不再是“恒为
默认值”的旧写法。数值本身仍一律标来源,未拍板的不冒充生产值。
"""
effective = {k: dict(v) for k, v in DEFAULT_THRESHOLDS.items()}
for key, entry in (thresholds or {}).items():
if isinstance(entry, dict):
merged = {"value": entry["value"]}
merged["source"] = entry.get("source", "test_default_unapproved")
effective[key] = merged
else:
effective[key] = {"value": entry,
"source": "test_default_unapproved"}
return effective
def _t(thresholds, key):
if thresholds and key in thresholds:
entry = thresholds[key]
if isinstance(entry, dict):
return entry["value"]
return entry
return DEFAULT_THRESHOLDS[key]["value"]
def _round1(value):
return round(float(value) + 1e-9, 1)
def _score_higher(value, nine, ten, floor):
"""越高越好:floor->5 分,nine->9 分,ten->10 分,分段线性,低于 floor 按比例。"""
if value is None:
return None
v = float(value)
if v >= ten:
return 10.0
if v >= nine:
return 9.0 + (v - nine) / (ten - nine) if ten > nine else 10.0
if v >= floor:
return 5.0 + 4.0 * (v - floor) / (nine - floor) if nine > floor else 5.0
if floor > 0:
return max(0.0, 5.0 * v / floor)
return 0.0
def _score_lower(value, ten, nine, floor):
"""越低越好:ten->10 分,nine->9 分,floor->5 分,低于 ten 满分,高于 floor 递减。"""
if value is None:
return None
v = float(value)
if v <= ten:
return 10.0
if v <= nine:
return 9.0 + (nine - v) / (nine - ten) if nine > ten else 10.0
if v <= floor:
return 5.0 + 4.0 * (floor - v) / (floor - nine) if floor > nine else 5.0
return max(0.0, 5.0 * floor / v) if v > 0 else 0.0
def _result(dim_id, score, basis, missing, untested):
if score is None:
status = "unknown"
elif score >= PASS_SCORE:
status = "pass"
else:
status = "fail"
return {
"id": dim_id,
"score": _round1(score) if score is not None else None,
"status": status,
"basis": basis,
"missing_items": missing,
"untested_items": untested,
}
def _score_delivery(ev, th):
"""证据:first_pass_rate, defect_rate, rework_rate,
first_pass_known, defect_known, rework_known, attempts_present,
evidence_kind("real" 以外若为 estimated/fixture 则记未实测)。"""
missing, untested, basis = [], [], []
for key in ("first_pass_rate", "defect_rate", "rework_rate"):
if ev.get(key) is None:
missing.append(key)
for key in ("first_pass_known", "defect_known", "rework_known"):
if not ev.get(key):
missing.append(key + "_not_known")
if not ev.get("attempts_present"):
missing.append("attempts_present_false_or_missing")
kind = ev.get("evidence_kind")
if kind in UNTESTED_KINDS:
untested.append("metrics_evidence_kind=%s" % kind)
if missing or untested:
return _result("delivery_quality", None, basis, missing, untested)
subs = [
("first_pass_rate", _score_higher(ev["first_pass_rate"],
_t(th, "first_pass_nine"),
_t(th, "first_pass_ten"),
_t(th, "first_pass_floor"))),
("defect_rate", _score_lower(ev["defect_rate"],
_t(th, "defect_rate_ten"),
_t(th, "defect_rate_nine"),
_t(th, "defect_rate_floor"))),
("rework_rate", _score_lower(ev["rework_rate"],
_t(th, "rework_rate_ten"),
_t(th, "rework_rate_nine"),
_t(th, "rework_rate_floor"))),
]
for name, sub in subs:
basis.append("%s=%s -> %.1f" % (name, ev[name], sub))
return _result("delivery_quality", min(s for _, s in subs), basis, missing, untested)
def _score_gate(ev, th):
"""证据:hard_gate_enforced(bool/None)、gate_items_passed、
gate_items_total、merge_blocked_verified、loop_end_to_end_verified、
sampling_misjudgment_rate(None 表示未抽查/未知)、
audit_verdict("conditional" 记无效证据,不算通过)。"""
missing, untested, basis = [], [], []
if ev.get("hard_gate_enforced") is None:
missing.append("hard_gate_enforced_unknown")
if ev.get("gate_items_passed") is None or ev.get("gate_items_total") is None:
missing.append("gate_items_passed_or_total_missing")
if ev.get("audit_verdict") == "conditional":
untested.append("audit_verdict=conditional_not_a_pass")
if ev.get("evidence_kind") in UNTESTED_KINDS:
untested.append("gate_evidence_kind=%s" % ev["evidence_kind"])
if missing or untested:
return _result("gate_audit", None, basis, missing, untested)
if not ev.get("hard_gate_enforced"):
basis.append("hard_gate_enforced=False(record 模式):已知未强制,分数封顶 4.0")
return _result("gate_audit", 4.0, basis, missing, untested)
total = ev["gate_items_total"]
passed = ev["gate_items_passed"]
rate = passed / total if total else 0.0
sub_gate = _score_higher(rate, 0.99, 1.0, 0.80)
basis.append("gate_items %s/%s=%.3f -> %.1f" % (passed, total, rate, sub_gate))
subs = [sub_gate]
if not ev.get("merge_blocked_verified"):
basis.append("merge_blocked_verified=False:合并阻塞未真实验证,分数封顶 4.0")
return _result("gate_audit", 4.0, basis, missing, untested)
if not ev.get("loop_end_to_end_verified"):
basis.append("loop_end_to_end_verified=False:自动循环端到端未真实验证,分数封顶 4.0")
return _result("gate_audit", 4.0, basis, missing, untested)
mis = ev.get("sampling_misjudgment_rate")
if mis is None:
missing.append("sampling_misjudgment_rate_unknown")
return _result("gate_audit", None, basis, missing, untested)
sub_mis = _score_lower(mis, 0.0, 0.02, _t(th, "overturn_rate_floor"))
basis.append("sampling_misjudgment_rate=%s -> %.1f" % (mis, sub_mis))
subs.append(sub_mis)
return _result("gate_audit", min(subs), basis, missing, untested)
def _score_arch(ev, th):
"""证据:contract_frozen、arch_violations、directory_violations、
import_violations、stack_changed_without_approval(bool/None)。"""
missing, untested, basis = [], [], []
for key in ("arch_violations", "directory_violations", "import_violations"):
if ev.get(key) is None:
missing.append(key + "_unknown")
if ev.get("contract_frozen") is None:
missing.append("contract_frozen_unknown")
if missing:
return _result("arch_contract", None, basis, missing, untested)
total_violations = (ev["arch_violations"] + ev["directory_violations"]
+ ev["import_violations"])
basis.append("violations total=%s (arch=%s dir=%s import=%s)"
% (total_violations, ev["arch_violations"],
ev["directory_violations"], ev["import_violations"]))
if not ev.get("contract_frozen"):
basis.append("contract_frozen=False:已知未冻结,分数封顶 4.0")
return _result("arch_contract", 4.0, basis, missing, untested)
if ev.get("stack_changed_without_approval"):
basis.append("stack_changed_without_approval=True:分数封顶 4.0")
return _result("arch_contract", 4.0, basis, missing, untested)
if total_violations > 0:
basis.append("violations>0:分数封顶 5.0")
return _result("arch_contract", 5.0, basis, missing, untested)
return _result("arch_contract", 10.0, basis, missing, untested)
def _score_test(ev, th):
"""证据:prd_coverage_rate、agent_six_complete、cross_matrix_coverage_rate、
coverage_pct、lint_items、mypy_items、bandit_high、
cross_matrix_kind("real" 以外记未实测)、real_pg(bool)、
real_service(bool)。"""
missing, untested, basis = [], [], []
for key in ("prd_coverage_rate", "cross_matrix_coverage_rate", "coverage_pct"):
if ev.get(key) is None:
missing.append(key + "_missing")
kind = ev.get("cross_matrix_kind")
if kind in UNTESTED_KINDS or kind in ("viewport_substitute", "code_review_only"):
untested.append("cross_matrix_kind=%s" % kind)
if kind is not None and kind != "real":
untested.append("cross_matrix_not_real")
if not ev.get("real_pg"):
untested.append("real_pg_not_verified")
if not ev.get("real_service"):
untested.append("real_service_not_verified")
if missing or untested:
return _result("test_coverage", None, basis, missing, untested)
if not ev.get("agent_six_complete"):
basis.append("agent_six_complete=False:agent 专属六项缺项,分数封顶 4.0")
return _result("test_coverage", 4.0, basis, missing, untested)
subs = [
("prd_coverage_rate", _score_higher(ev["prd_coverage_rate"], 0.99, 1.0, 0.80)),
("cross_matrix_coverage_rate", _score_higher(ev["cross_matrix_coverage_rate"], 0.99, 1.0, 0.80)),
("coverage_pct", _score_higher(ev["coverage_pct"],
_t(th, "coverage_nine"),
_t(th, "coverage_ten"),
_t(th, "coverage_floor"))),
]
for name, sub in subs:
basis.append("%s=%s -> %.1f" % (name, ev[name], sub))
# 静态检查:数值缺失记缺失,不估算;有值时,>0 即封顶(9 分线要求接近清零)。
for key in ("lint_items", "mypy_items", "bandit_high"):
if ev.get(key) is None:
missing.append(key + "_missing")
if missing:
return _result("test_coverage", None, basis, missing, untested)
static_total = ev["lint_items"] + ev["mypy_items"] + ev["bandit_high"]
basis.append("static total lint=%s mypy=%s bandit_high=%s"
% (ev["lint_items"], ev["mypy_items"], ev["bandit_high"]))
if static_total > 0:
basis.append("static_total>0:分数封顶 8.0(静态检查未接近清零)")
return _result("test_coverage", min(8.0, min(s for _, s in subs)), basis, missing, untested)
return _result("test_coverage", min(s for _, s in subs), basis, missing, untested)
def _score_security(ev, th):
"""证据:ledger_verify_ok(bool/None)、ledger_coverage_rate、
shell_whitelist_pass_rate、branch_protection
("protected"/"unprotected"/"unknown"/None)、anchor_check_recent。"""
missing, untested, basis = [], [], []
if ev.get("ledger_verify_ok") is None:
missing.append("ledger_verify_ok_unknown")
if ev.get("ledger_coverage_rate") is None:
missing.append("ledger_coverage_rate_missing")
bp = ev.get("branch_protection")
if bp is None or bp == "unknown":
missing.append("branch_protection_unknown")
if missing:
return _result("security_ledger", None, basis, missing, untested)
if bp == "unprotected":
basis.append("branch_protection=unprotected:已知未保护,分数封顶 4.0")
return _result("security_ledger", 4.0, basis, missing, untested)
if not ev.get("ledger_verify_ok"):
basis.append("ledger_verify_ok=False:账本校验未通过,分数封顶 4.0")
return _result("security_ledger", 4.0, basis, missing, untested)
subs = [_score_higher(ev["ledger_coverage_rate"], 0.99, 1.0, 0.80)]
basis.append("ledger_coverage_rate=%s -> %.1f" % (ev["ledger_coverage_rate"], subs[0]))
rate = ev.get("shell_whitelist_pass_rate")
if rate is not None:
sub = _score_higher(rate, 0.99, 1.0, 0.80)
subs.append(sub)
basis.append("shell_whitelist_pass_rate=%s -> %.1f" % (rate, sub))
if not ev.get("anchor_check_recent"):
basis.append("anchor_check_recent=False:锚点定期校验未落地,分数封顶 8.0")
return _result("security_ledger", min(8.0, min(subs)), basis, missing, untested)
return _result("security_ledger", min(subs), basis, missing, untested)
def _score_cost(ev, th):
"""证据:cost_records_complete(bool)、unpriced_as_zero(bool,
True 即违规)、per_creator_present、quota_cutoff_verified、
thresholds_approved(bool,仅影响阈值来源标注,不封顶分数)。"""
missing, untested, basis = [], [], []
if not ev.get("cost_records_complete"):
missing.append("cost_records_complete_false_or_missing"
"(turns/llm_requests/usd_micros 缺失)")
if ev.get("unpriced_as_zero") is None:
missing.append("unpriced_as_zero_unknown")
if missing:
return _result("cost_quota", None, basis, missing, untested)
if ev.get("unpriced_as_zero"):
basis.append("unpriced_as_zero=True:无价记成 0/免费,分数封顶 0.0")
return _result("cost_quota", 0.0, basis, missing, untested)
if not ev.get("quota_cutoff_verified"):
basis.append("quota_cutoff_verified=False:配额硬切断未真实验证,分数封顶 4.0")
return _result("cost_quota", 4.0, basis, missing, untested)
if not ev.get("per_creator_present"):
basis.append("per_creator_present=False:按创建者成本未落地,分数封顶 8.0")
return _result("cost_quota", 8.0, basis, missing, untested)
basis.append("cost_records_complete=True, quota_cutoff_verified=True, "
"per_creator_present=True, unpriced_as_zero=False")
return _result("cost_quota", 10.0, basis, missing, untested)
def _score_ops(ev, th):
"""证据:ci_green_rate(None 表示 CI 未绿/未知)、deploy_success_rate、
rollback_drill_done、ha_status
("second_host"/"single_host_accepted"/"single_host_unaccepted"/None)、
parallel_scale_proven(int/None)、parallel_scale_accepted_alternative(bool)。"""
missing, untested, basis = [], [], []
if ev.get("ci_green_rate") is None:
missing.append("ci_green_rate_unknown(CI 未绿或未统计)")
if ev.get("rollback_drill_done") is None:
missing.append("rollback_drill_done_unknown")
ha = ev.get("ha_status")
if ha is None:
missing.append("ha_status_unknown")
if missing:
return _result("ops_scale", None, basis, missing, untested)
if not ev.get("rollback_drill_done"):
basis.append("rollback_drill_done=False:回滚演练未真实跑过,分数封顶 4.0")
return _result("ops_scale", 4.0, basis, missing, untested)
subs = [_score_higher(ev["ci_green_rate"], 0.99, 1.0, 0.80)]
basis.append("ci_green_rate=%s -> %.1f" % (ev["ci_green_rate"], subs[0]))
if ev.get("deploy_success_rate") is not None:
sub = _score_higher(ev["deploy_success_rate"], 0.99, 1.0, 0.80)
subs.append(sub)
basis.append("deploy_success_rate=%s -> %.1f" % (ev["deploy_success_rate"], sub))
if ha == "single_host_unaccepted":
basis.append("ha_status=single_host_unaccepted:分数封顶 5.0")
return _result("ops_scale", 5.0, basis, missing, untested)
proven = ev.get("parallel_scale_proven")
target = _t(th, "parallel_scale_target")
if proven is None and not ev.get("parallel_scale_accepted_alternative"):
missing.append("parallel_scale_proven_unknown")
return _result("ops_scale", None, basis, missing, untested)
if proven is not None:
sub = _score_higher(min(proven, target) / target if target else 0,
0.99, 1.0, 0.5)
# 规模是离散证据:40 人满分,18 人约 4.5 分,上面线性公式在
# 0.5-1.0 区间给出 5-10 分,与“18 人约 4.5”不一致,故直接用比例*10。
sub = _round1(10.0 * min(proven, target) / target) if target else 0.0
subs.append(sub)
basis.append("parallel_scale_proven=%s target=%s -> %.1f"
% (proven, target, sub))
return _result("ops_scale", min(subs), basis, missing, untested)
_SCORERS = {
"delivery_quality": _score_delivery,
"gate_audit": _score_gate,
"arch_contract": _score_arch,
"test_coverage": _score_test,
"security_ledger": _score_security,
"cost_quota": _score_cost,
"ops_scale": _score_ops,
}
# ---- v7 机制第 4 项:分数证据绑定与签名(只做这一项) ----
# 诚实边界(先写清,再写代码):
# - 本节是纯函数:只算哈希与验签结果,不写库、不写账本、
# 不移动 cloud/ledger.py 的链头、不改变总体达标规则。
# evidence_ledger_head 是调用方传入的证据行即时算出的链头,
# 不是已持久化/已外部锚定的头;持久化与外部锚定是调用方
# 与运营方的事,本模块绝不谎称已上链入库。
# - 防重盖戳靠签名,不靠本节的哈希:哈希只能证明“这份证据体
# 就是算分时那份”(改动即哈希变),不能证明“这份证据体是
# 新取的”。没有有效签名的证据,调用方仍可重盖戳再交——
# 与 cloud/evidence_binding.py 的说明同款局限,本节不冒充解决。
# - 生产签名密钥供给仍是待定的运营方事项:本模块不含任何
# 生产密钥、不读生产 env、不新增密钥落盘路径、不默认信任。
# 机制只是代码就绪,不是生产已激活;真实 PG 上的链验证未做,
# 本项测试只用纯函数内存计算(连 SQLite 账本集成都没接)。
# - 签名验证是 fail-closed:无验证器、签名缺失、验证器拒绝、
# 验证器抛异常,一律 verified=False,绝不默认 verified=True。
# 验签结果不改变 build_scorecard 的 overall:总体达标仍只看
# 7 维分数,签名是调用方在传证据行之前自行核验的附加层,
# 把验签接入门禁/达标是另一次契约决定,不在本项悄悄做。
EVIDENCE_CHAIN_GENESIS = hashlib.sha256(
b"scorecard-evidence-genesis-v1").hexdigest()
def canonical_evidence_bytes(evidence_row):
"""证据体全文的规范字节:dict 按 sort_keys 紧凑 JSON 编码。
全文即整个 dict,不只取其中某个字段;字段增减、值改动、
键顺序之外的任何内容变化都会改变字节,从而改变哈希。
非 JSON 可序列化内容抛 ValueError,不静默丢字段。
"""
if isinstance(evidence_row, bytes):
return evidence_row
if isinstance(evidence_row, str):
return evidence_row.encode("utf-8")
try:
return json.dumps(evidence_row, sort_keys=True,
separators=(",", ":"),
ensure_ascii=False).encode("utf-8")
except (TypeError, ValueError) as exc:
raise ValueError(
"evidence row must be JSON-serializable: %s" % (exc,))
def hash_evidence_body(evidence_row):
"""证据体全文哈希:规范字节的 SHA-256 hex。"""
return hashlib.sha256(
canonical_evidence_bytes(evidence_row)).hexdigest()
def compute_evidence_ledger(evidence_rows):
"""对调用方传入的证据行算链头与行 id 列表(纯函数)。
链式构造(仿 cloud/ledger.py 的头推进,不写库):
head_0 = EVIDENCE_CHAIN_GENESIS
head_{i+1} = sha256((head_i + body_hash_i).encode())
因此任一证据体改动、行顺序变化都会改变最终头哈希;
只改一行 id 而不动其余内容时,该行体哈希随之变,头也变。
返回 {"head": str|None, "row_ids": [...], "body_hashes": [...]}。
- evidence_rows 为 None 或空:head=None、row_ids=[]、
body_hashes=[],绝不编造一个头或 id。
- 行 id 只取行 dict 里现成的 "id"(且不为 None);没有 id
的行仍参与头计算,但不为其发明 id(row_ids 长度可小于
行数,调用方要可审计映射须每行自带 id)。
- 非 dict 行抛 ValueError:不静默跳过、不猜 id。
"""
if evidence_rows is None:
return {"head": None, "row_ids": [], "body_hashes": []}
rows = list(evidence_rows)
if not rows:
return {"head": None, "row_ids": [], "body_hashes": []}
row_ids, body_hashes = [], []
head = EVIDENCE_CHAIN_GENESIS
for row in rows:
if not isinstance(row, dict):
raise ValueError("evidence row must be a dict")
body_hash = hash_evidence_body(row)
body_hashes.append(body_hash)
head = hashlib.sha256(
(head + body_hash).encode("utf-8")).hexdigest()
if row.get("id") is not None:
row_ids.append(row["id"])
return {"head": head, "row_ids": row_ids, "body_hashes": body_hashes}
def verify_signature(evidence_bytes, signature, verifier=None):
"""签名验证器:可注入接口,fail-closed。
参数:
- evidence_bytes: bytes(或 str,按 UTF-8 编码);其它类型
抛 TypeError(这是调用方编程错误,不是“验签失败”)。
- signature: 调用方出示的签名(bytes/str 或其它验证器能
接受的形式);为 None 或空 bytes/str 即缺失。
- verifier: 调用方注入的可调用对象,签名
verifier(evidence_bytes: bytes, signature) -> bool。
生产验证器由运营方在密钥供给到位后注入,本模块不提供、
不默认、不从 env/文件读任何验证器或密钥。
返回 {"verified": bool, "reason": str},reason 取值:
"verified"(验证器返回真)、"verifier_rejected"(返回假)、
"no_verifier"(未注入验证器)、"missing_signature"
(签名缺失)、"verifier_error"(验证器抛异常,fail-closed)。
默认无验证器时 verified=False,绝不默认 True。
"""
if not isinstance(evidence_bytes, (bytes, str)):
raise TypeError("evidence_bytes must be bytes or str")
if verifier is None:
return {"verified": False, "reason": "no_verifier"}
if not callable(verifier):
raise TypeError("verifier must be callable or None")
if signature is None:
return {"verified": False, "reason": "missing_signature"}
if isinstance(signature, (bytes, str)) and len(signature) == 0:
return {"verified": False, "reason": "missing_signature"}
payload = (evidence_bytes if isinstance(evidence_bytes, bytes)
else evidence_bytes.encode("utf-8"))
try:
result = verifier(payload, signature)
except Exception:
return {"verified": False, "reason": "verifier_error"}
if result:
return {"verified": True, "reason": "verified"}
return {"verified": False, "reason": "verifier_rejected"}
def build_scorecard(evidence, thresholds=None, rubric_version=None,
baseline_head=None, evidence_rows=None):
"""算出 7 维评分卡。evidence 缺键按空证据处理(该维记 None)。
rubric_version:调用方显式传入的标尺版本号(见
cloud/scorecard_rubric.py)。本函数不查库、不定版本:
未传入时卡片记 rubric_version=None,表示“未锚定到版本化
标尺”,分数仍按 thresholds(或默认值)算出,但不具备
跨标尺可比性。计分规则、7 维定义、达标规则一字未改。
baseline_head:调用方显式传入的当前 main 头(机器字段,不是
docstring 里的文字说明)。不传入或传入空串时归一化为 None,
并在 missing_items 末尾记 baseline_head_missing;本函数绝不
编造、绝不探测版本控制状态。baseline 缺失本身不改写 overall
语义(overall 仍按 7 维算),漂移与否由 check_baseline_stale
单独判定,调用方不得只看 overall 就把旧分当现分用。
evidence_rows(v7 机制第 4 项):调用方显式传入的证据行
列表(每行 dict),本函数只据此算 evidence_ledger_head 与
evidence_row_ids(见 compute_evidence_ledger),不写库、
不验签、不改变 overall。无证据行时 head 为 None、
row_ids 为空列表,不得编造。
"""
evidence = evidence or {}
th = thresholds or {}
if baseline_head is not None and baseline_head == "":
baseline_head = None
ledger = compute_evidence_ledger(evidence_rows)
dimensions = []
missing_all, untested_all = [], []
for dim_id, _name in DIMENSIONS:
result = _SCORERS[dim_id](evidence.get(dim_id) or {}, th)
dimensions.append(result)
missing_all.extend("%s: %s" % (dim_id, m) for m in result["missing_items"])
untested_all.extend("%s: %s" % (dim_id, u) for u in result["untested_items"])
if baseline_head is None:
missing_all.append("baseline_head_missing")
scores = [d["score"] for d in dimensions if d["score"] is not None]
average = _round1(sum(scores) / len(scores)) if scores else None
# 总体规则钉死:平均分绝不能作为达标依据。
overall_pass = all(d["score"] is not None and d["score"] >= PASS_SCORE
for d in dimensions)
return {
"dimensions": dimensions,
"average_score": average,
"average_is_not_pass_criterion": True,
"overall": "pass" if overall_pass else "not_pass",
"overall_pass": overall_pass,
"pass_score": PASS_SCORE,
"baseline_head": baseline_head,
"missing_items": missing_all,
"untested_items": untested_all,
"thresholds_source": thresholds_source(th),
"thresholds": _effective_thresholds(th),
"rubric_version": rubric_version,
"evidence_ledger_head": ledger["head"],
"evidence_row_ids": ledger["row_ids"],
}
def check_baseline_stale(scorecard, current_main_head):
"""判定一张旧评分卡是否因 main 推进而失效(基线漂移门禁)。
参数全部由调用方传入:scorecard 是 build_scorecard 的输出 dict,
current_main_head 是调用方当前确认的 main 头。本函数不在生产
代码里探测版本控制状态,也不读版本控制目录。
返回 dict(调用方凭 stale / usable_as_pass_basis 判断,绝不能
只看旧卡的 overall / overall_pass):
- stale=True 时:旧分不得再作 pass 依据
(usable_as_pass_basis=False、overall_usable_as_pass_basis=False、
overall_must_not_be_used_as_pass_basis=True),即使旧卡的
overall 本身仍是 "pass"——旧值保留只为追溯,不代表现分有效。
- stale 的三种情形:baseline_head 缺失(无法证明新鲜,失败关闭,
不编造为一致);current_main_head 缺失(无法核对,失败关闭);
两者均有值但不相等(main 已推进,旧分过期)。
- 仅当两者均有值且相等时 stale=False,旧分仍可按原 overall 读。
"""
scorecard = scorecard or {}
baseline = scorecard.get("baseline_head")
if baseline is not None and baseline == "":
baseline = None
if current_main_head is not None and current_main_head == "":
current_main_head = None
if baseline is None:
reason = ("baseline_head_missing: scorecard carries no frozen "
"baseline_head, freshness cannot be proven")
stale = True
elif current_main_head is None:
reason = ("current_main_head_missing: caller supplied no current "
"main head, drift cannot be checked")
stale = True
elif current_main_head != baseline:
reason = ("baseline_head_drifted: baseline_head=%s "
"current_main_head=%s" % (baseline, current_main_head))
stale = True
else:
reason = "baseline_head_matches_current_main_head"
stale = False
return {
"stale": stale,
"baseline_head": baseline,
"current_main_head": current_main_head,
"reason": reason,
"usable_as_pass_basis": not stale,
"overall_usable_as_pass_basis": not stale,
"overall_must_not_be_used_as_pass_basis": stale, }
def example_scorecard_as_of_2026_10_31():
"""2026-10-31 快照示例卡(历史示例,不是当前系统得分)。
证据是本函数 2026-10-31 落笔时记录的快照值(生产 v0.2、
main aebc623、覆盖率 0.7787 等),此后未重新实测;调用方
绝不能把它读成当前系统得分。要当前分数,须显式把新证据
传给 build_scorecard()。凡是只到 PR、未合并部署、未真实
验证的,在这里都记为未知或已知未强制,不估算、不写成
判断分。阈值仍为测试默认值
(source="test_default_unapproved"),未拍板。
总体结论必然是不达标。
本示例无冻结基线:build_scorecard 未传 baseline_head,故
baseline_head=None 且 missing_items 含 baseline_head_missing,
绝不编造一个头把它写成现分。
"""
evidence = {
"delivery_quality": {
"first_pass_rate": None, "defect_rate": None, "rework_rate": None,
"first_pass_known": False, "defect_known": False,
"rework_known": False, "attempts_present": False,
},
"gate_audit": {
"hard_gate_enforced": False, "gate_items_passed": 0,
"gate_items_total": 5, "merge_blocked_verified": False,
"loop_end_to_end_verified": False,
"sampling_misjudgment_rate": None,
},
"arch_contract": {
"contract_frozen": None, "arch_violations": None,
"directory_violations": None, "import_violations": None,
},
"test_coverage": {
"prd_coverage_rate": None, "cross_matrix_coverage_rate": None,
"coverage_pct": 0.7787, "cross_matrix_kind": "viewport_substitute",
"real_pg": False, "real_service": False,
"agent_six_complete": True,
"lint_items": 109, "mypy_items": 0, "bandit_high": 4,
},
"security_ledger": {
"ledger_verify_ok": None, "ledger_coverage_rate": None,
"branch_protection": "unknown",
},
"cost_quota": {
"cost_records_complete": False, "unpriced_as_zero": False,
"per_creator_present": False, "quota_cutoff_verified": False,
},
"ops_scale": {
"ci_green_rate": None, "rollback_drill_done": False,
"ha_status": "single_host_unaccepted",
"parallel_scale_proven": 18,
},
}
report = build_scorecard(evidence)
report["evidence_as_of"] = "2026-10-31"
report["evidence_kind"] = "historical_example_snapshot"
report["not_current_system_score"] = True
return report