v7 mech-5: scorecard baseline_head machine field, stale check, frozen store (append-only)
(cherry picked from commit e3e1f517e916eaac597b82d04bac5307a3f8b126)
This commit is contained in:
1 parent
be81d658b6
commit
8fedd7fba0
3 files changed
+531
-1
No files matched your search
@@ -27,6 +27,21 @@
|
||||
#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 交付质量
|
||||
@@ -463,7 +478,8 @@ _SCORERS = {
|
||||
}
|
||||
|
||||
|
||||
def build_scorecard(evidence, thresholds=None, rubric_version=None):
|
||||
def build_scorecard(evidence, thresholds=None, rubric_version=None,
|
||||
baseline_head=None):
|
||||
"""算出 7 维评分卡。evidence 缺键按空证据处理(该维记 None)。
|
||||
|
||||
rubric_version:调用方显式传入的标尺版本号(见
|
||||
@@ -471,9 +487,18 @@ def build_scorecard(evidence, thresholds=None, rubric_version=None):
|
||||
未传入时卡片记 rubric_version=None,表示“未锚定到版本化
|
||||
标尺”,分数仍按 thresholds(或默认值)算出,但不具备
|
||||
跨标尺可比性。计分规则、7 维定义、达标规则一字未改。
|
||||
|
||||
baseline_head:调用方显式传入的当前 main 头(机器字段,不是
|
||||
docstring 里的文字说明)。不传入或传入空串时归一化为 None,
|
||||
并在 missing_items 末尾记 baseline_head_missing;本函数绝不
|
||||
编造、绝不探测版本控制状态。baseline 缺失本身不改写 overall
|
||||
语义(overall 仍按 7 维算),漂移与否由 check_baseline_stale
|
||||
单独判定,调用方不得只看 overall 就把旧分当现分用。
|
||||
"""
|
||||
evidence = evidence or {}
|
||||
th = thresholds or {}
|
||||
if baseline_head is not None and baseline_head == "":
|
||||
baseline_head = None
|
||||
dimensions = []
|
||||
missing_all, untested_all = [], []
|
||||
for dim_id, _name in DIMENSIONS:
|
||||
@@ -481,6 +506,8 @@ def build_scorecard(evidence, thresholds=None, rubric_version=None):
|
||||
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
|
||||
# 总体规则钉死:平均分绝不能作为达标依据。
|
||||
@@ -493,6 +520,7 @@ def build_scorecard(evidence, thresholds=None, rubric_version=None):
|
||||
"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),
|
||||
@@ -501,6 +529,56 @@ def build_scorecard(evidence, thresholds=None, rubric_version=None):
|
||||
}
|
||||
|
||||
|
||||
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 快照示例卡(历史示例,不是当前系统得分)。
|
||||
|
||||
@@ -512,6 +590,9 @@ def example_scorecard_as_of_2026_10_31():
|
||||
判断分。阈值仍为测试默认值
|
||||
(source="test_default_unapproved"),未拍板。
|
||||
总体结论必然是不达标。
|
||||
本示例无冻结基线:build_scorecard 未传 baseline_head,故
|
||||
baseline_head=None 且 missing_items 含 baseline_head_missing,
|
||||
绝不编造一个头把它写成现分。
|
||||
"""
|
||||
evidence = {
|
||||
"delivery_quality": {
|
||||
|
||||
Reference in new issue
Block a user