12.3 误报与白名单治理
门禁一定会误报——规则是死的,资产是活的:竖版 2:3 的横幅天生不是 2 的幂,一次性远景背景就是可以超尺寸。白名单就是给这些经过判断的例外开的口子。但白名单一旦没有纪律,就会从「例外清单」烂成「垃圾场」,最后没人记得哪条还能删。本节把白名单也当成要检查的对象:每条登记先过五道治理关,过不了的不生效。
级别沿用 12.2:只有 warn / info 可压制,block 级(硬失败)永远压不住——这不是实现细节,是原则。
12.3.1 五道治理关
| 顺序 | 问题码 | 判据 |
|---|---|---|
| 1 | E_DUPLICATE | (code, id) 已被前一条登记占用 |
| 2 | E_NOT_SUPPRESSIBLE | level 不在 suppressibleLevels(即 block) |
| 3 | E_NO_REASON | 缺理由(requireReason) |
| 4 | E_NO_OWNER | 缺负责人(requireOwner) |
| 5 | E_EXPIRED | expiresOn < today(requireExpiry) |
顺序是固定的——重复检查命中就立刻返回 ["E_DUPLICATE"],后面的检查不再跑。固定顺序是为了跨语言逐位一致:Python 与 TS 必须按同一顺序、同一短路规则产出同一串问题码,否则 digest 对不上。
12.3.2 白名单配置:4 条登记,2 条干净
配置里有 4 条登记,只有 2 条是「干净」的,另外 2 条故意埋了问题:一条是给 block 级开的(会被 E_NOT_SUPPRESSIBLE 拒),一条缺理由缺负责人(会被 E_NO_REASON + E_NO_OWNER 拒),同时它与第 1 条 (code, id) 重复(会被 E_DUPLICATE 短路拒)。today 是 2026-10-13,所以那条 expiresOn: 2026-09-30 的登记已过期:
{
"whitelistSet": "ci-gate",
"specVersion": 1,
"today": "2026-10-13",
"suppressionBudget": 3,
"policy": {
"requireReason": true,
"requireOwner": true,
"requireExpiry": true,
"suppressibleLevels": ["warn", "info"],
"note": "白名单只压制 warn / info;block 级(硬失败)不可压制——治理规则拒绝为硬失败开白名单。每条登记必须有理由、有负责人、有到期日;到期即失效并重新上报。"
},
"entries": [
{ "code": "E_SIZE_POT", "id": "tex_banner_hd", "level": "warn", "reason": "竖版 2:3 横幅,非 2 的幂是刻意的", "owner": "ta-lead", "expiresOn": "2026-12-31", "ticket": "ART-412" },
{ "code": "E_SIZE_MAX", "id": "tex_huge_bg", "level": "warn", "reason": "一次性远景背景,压缩后已确认", "owner": "ta-lead", "expiresOn": "2026-11-30", "ticket": "ART-418" },
{ "code": "E_NAME", "id": "tex_legacy_upper", "level": "block", "reason": "历史遗留,下版重命名", "owner": "art-pm", "expiresOn": "2026-09-30", "ticket": "ART-330" },
{ "code": "E_SIZE_POT", "id": "tex_banner_hd", "level": "warn", "reason": "", "owner": "", "expiresOn": "2026-12-31", "ticket": "ART-455" }
]
}
12.3.3 S 层:先体检,再压制
读 12.1 的 finding 清单与白名单,先跑治理体检,再用通过体检的登记做压制。治理问题与压制结果都只含字符串,排序后取 digest:
#!/usr/bin/env python3
# file: sim/ta12_suppress.py
# tier: S
# verify: /opt/homebrew/bin/python3 sim/ta12_suppress.py
"""卷二 12.3 规则误报与白名单治理(S 层)。读 12.1 的 out/ta12_engine.json 与
config/ta12_whitelist.json,做两件事:
1. **治理体检**——每条登记按固定顺序过五道关:重复(E_DUPLICATE)→ 级别不可压制
(E_NOT_SUPPRESSIBLE)→ 缺理由(E_NO_REASON)→ 缺负责人(E_NO_OWNER)→ 已过期
(E_EXPIRED)。有任何一条治理问题的登记**不生效**。
2. **压制**——只有通过体检的登记才按 (code, id) 精确压制对应 finding;block 级永远压不住。
治理体检的输出与压制结果都只含字符串,落 out/ta12_suppress.json 供 P 层逐位对照。
"""
import hashlib
import json
import os
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def audit(entry, seen, policy, today):
"""返回该登记的治理问题码列表(空 = 通过)。检查顺序固定,保证跨语言一致。"""
out = []
key = (entry["code"], entry["id"])
if key in seen:
return ["E_DUPLICATE"]
if entry["level"] not in policy["suppressibleLevels"]:
out.append("E_NOT_SUPPRESSIBLE")
if policy["requireReason"] and not entry["reason"]:
out.append("E_NO_REASON")
if policy["requireOwner"] and not entry["owner"]:
out.append("E_NO_OWNER")
if policy["requireExpiry"] and entry["expiresOn"] < today:
out.append("E_EXPIRED")
return out
def main():
engine = json.load(open(os.path.join(ROOT, "out/ta12_engine.json"), encoding="utf-8"))
wl = json.load(open(os.path.join(ROOT, "config/ta12_whitelist.json"), encoding="utf-8"))
policy, today = wl["policy"], wl["today"]
findings = engine["findings"]
seen, active, governance = set(), set(), []
for e in wl["entries"]:
problems = audit(e, seen, policy, today)
if problems:
for p in problems:
governance.append({"code": p, "id": e["id"], "detail": f"{e['code']}/{e['id']}"})
else:
active.add((e["code"], e["id"]))
seen.add((e["code"], e["id"]))
suppressed, remaining = [], []
for f in findings:
(suppressed if (f["code"], f["id"]) in active else remaining).append(f)
suppressed.sort(key=lambda f: (f["code"], f["id"]))
governance.sort(key=lambda g: (g["code"], g["id"]))
gov_lines = [f"{g['code']}:{g['id']}" for g in governance]
sup_lines = [f"SUPPRESSED:{s['code']}:{s['id']}" for s in suppressed]
digest = hashlib.sha256("\n".join(sorted(gov_lines) + sorted(sup_lines)).encode("utf-8")).hexdigest()[:16]
remaining_block = sum(1 for f in remaining if f["severity"] == "block")
rep = {
"project": engine["project"],
"whitelistSet": wl["whitelistSet"],
"today": today,
"findingCount": len(findings),
"entryCount": len(wl["entries"]),
"activeSuppressions": len(active),
"suppressionBudget": wl["suppressionBudget"],
"budgetOk": len(active) <= wl["suppressionBudget"],
"suppressedCount": len(suppressed),
"suppressed": [{"code": f["code"], "id": f["id"]} for f in suppressed],
"remainingCount": len(remaining),
"remainingBlock": remaining_block,
"gateAfter": "block" if remaining_block else "pass",
"governanceFindings": governance,
"digest": digest,
}
with open(os.path.join(ROOT, "out/ta12_suppress.json"), "w", encoding="utf-8") as f:
json.dump(rep, f, ensure_ascii=False, indent=2)
f.write("\n")
print(f"entries={rep['entryCount']} active={rep['activeSuppressions']} "
f"suppressed={rep['suppressedCount']} remaining={rep['remainingCount']} "
f"gateAfter={rep['gateAfter']}")
print(f"budgetOk={rep['budgetOk']} governance={len(governance)} digest={digest}")
for g in governance:
print(f" {g['code']:20s} {g['detail']}")
for s in suppressed:
print(f" SUPPRESSED {s['code']:16s} {s['id']}")
print("wrote out/ta12_suppress.json")
return 0
if __name__ == "__main__":
sys.exit(main())
实跑 /opt/homebrew/bin/python3 sim/ta12_suppress.py:
entries=4 active=2 suppressed=2 remaining=5 gateAfter=block
budgetOk=True governance=3 digest=52188a151fd1b734
E_DUPLICATE E_SIZE_POT/tex_banner_hd
E_EXPIRED E_NAME/tex_legacy_upper
E_NOT_SUPPRESSIBLE E_NAME/tex_legacy_upper
SUPPRESSED E_SIZE_MAX tex_huge_bg
SUPPRESSED E_SIZE_POT tex_banner_hd
wrote out/ta12_suppress.json
4 条登记里 2 条生效、压掉 2 条 warn;治理报出 3 条问题。E_NAME/tex_legacy_upper 同时命中两条——它是 block 级(E_NOT_SUPPRESSIBLE)且已过期(E_EXPIRED);E_SIZE_POT/tex_banner_hd 的第 2 条登记命中 E_DUPLICATE(与第 1 条重复,直接短路,所以没再报 E_NO_REASON / E_NO_OWNER)。压掉 2 条 warn 之后 remaining=5 全是 block,门禁仍然 block——这正是设计意图。
12.3.4 P 层:TS 内核与逐位对拍
TS 内核与 Python 逐条对应,治理问题码与压制结果都只含字符串,因此 digest 必须逐位一致:
// file: core/src/ta12/suppress.ts
// tier: P
// verify: node --test 'core/test/ta12_*.test.ts'
// 卷二 12.3 白名单治理的 TS 侧实现。与 sim/ta12_suppress.py 同一套口径:登记先过五道治理关
// (重复 → 级别不可压制 → 缺理由 → 缺负责人 → 已过期),有任一问题的登记不生效;只有通过的
// 登记才按 (code, id) 精确压制,block 级永远压不住。只允许可擦除 TS 语法。
export type Finding = { code: string; id: string; detail: string; severity: string; hardFail: boolean };
export type Entry = { code: string; id: string; level: string; reason: string; owner: string; expiresOn: string; ticket: string };
export type Policy = { requireReason: boolean; requireOwner: boolean; requireExpiry: boolean; suppressibleLevels: string[] };
export type GovFinding = { code: string; id: string; detail: string };
const key = (code: string, id: string): string => `${code}::${id}`;
const cmp = (a: string, b: string): number => (a < b ? -1 : a > b ? 1 : 0);
/** 该登记的治理问题码列表(空 = 通过)。检查顺序固定,保证跨语言一致。 */
export function audit(entry: Entry, seen: Set<string>, policy: Policy, today: string): string[] {
const out: string[] = [];
if (seen.has(key(entry.code, entry.id))) return ["E_DUPLICATE"];
if (!policy.suppressibleLevels.includes(entry.level)) out.push("E_NOT_SUPPRESSIBLE");
if (policy.requireReason && !entry.reason) out.push("E_NO_REASON");
if (policy.requireOwner && !entry.owner) out.push("E_NO_OWNER");
if (policy.requireExpiry && entry.expiresOn < today) out.push("E_EXPIRED");
return out;
}
export function applySuppression(findings: Finding[], entries: Entry[], policy: Policy, today: string) {
const seen = new Set<string>();
const active = new Set<string>();
const governance: GovFinding[] = [];
for (const e of entries) {
const problems = audit(e, seen, policy, today);
for (const p of problems) governance.push({ code: p, id: e.id, detail: `${e.code}/${e.id}` });
if (problems.length === 0) active.add(key(e.code, e.id));
seen.add(key(e.code, e.id));
}
const suppressed = findings.filter((f) => active.has(key(f.code, f.id)));
const remaining = findings.filter((f) => !active.has(key(f.code, f.id)));
suppressed.sort((a, b) => cmp(a.code, b.code) || cmp(a.id, b.id));
governance.sort((a, b) => cmp(a.code, b.code) || cmp(a.id, b.id));
return { suppressed, remaining, governance, activeSuppressions: active.size };
}
实跑 node --test 'core/test/ta12_*.test.ts'(本节 4 条):
suppress digest = 52188a151fd1b734
✔ 跨语言:治理体检与压制结果 == Python 产物(逐位一致)
✔ 样本:4 条登记 → 2 条生效、3 条治理问题,压掉 2 条 warn,门禁仍 block
✔ 负例:给硬失败开白名单不生效,block 级 finding 必须仍在
✔ 负例:过期登记不生效;空白名单不压任何东西
ℹ pass 12
ℹ fail 0
两侧 digest 都是 52188a151fd1b734。两条负例各堵一个漏洞:一条给 E_NAME(block 级)开白名单,断言 suppressed.length === 0 且 E_NAME 仍在 remaining 里;一条用过期的 E_SIZE_POT 登记与空白名单分别跑,断言压不掉任何东西。
12.3.5 为什么 block 永远压不住,以及配额与到期日
白名单能压 warn、能压 info,就是压不住 block。理由回到 12.1:那五类硬失败的后果是不可逆的(引用会断、包发不出去),把它「记一笔放行」等于把不可逆的破坏写进流水线。所以治理规则主动拒绝为 block 开白名单——不是压了没效果,而是这条登记本身就被判为治理问题(E_NOT_SUPPRESSIBLE),根本进不了生效集合。
第二条纪律是配额:suppressionBudget=3,生效登记数不得超过它,本节 2 ≤ 3 通过。配额的意义不是限制数量,而是逼你清理——满了就必须先删一条旧的才能加新的,白名单因此不会无限膨胀。第三条是到期日:登记不是永久的,expiresOn 一过就失效并重新上报,强制每条例外定期被重新审视。三条纪律合起来,白名单才是「有记账的例外」,而不是「静音的开关」。
12.3.6 本章收口:从判据到门禁
第 12 章三节其实是一条线:12.1 把各章判据收进一台引擎,12.2 决定拿什么当阻断条件,12.3 管住例外怎么开、怎么清。三节共用一份资产样本、一套级别语义、一个 digest 对拍机制——规则引擎不是新造一套,而是第 3/4/5 章判据的统一消费方。下一章进入中台:当这些检查从「一个仓库的 CI」变成「很多团队的公共服务」,要考虑的就完全是另一组问题了。
阅读导航:上一节:12.2 门禁的分级与阻断策略 · 下一节:13.1 中台要解决什么问题 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。