并master(fix-ci-red-20261106批+精英专属池批): README能力行冲突并集(master营地五窗真输入化行+本批3行); 待办「卡片复核报告文件口径」条目删除——其不一致由本批复核凭据换代全数解掉(闸C6/doc-conventions/两技能件已对齐复核记录节即凭据); 读数: 流水线闸PASS(33条目·卡片口径4张全过)

This commit is contained in:
DKChan committed 2026-11-07 00:11:40 +08:00
commit 8bee626138
121 files changed
+1646 -52

No files matched your search

+70 -3
View File
@@ -14,9 +14,14 @@ r"""capability_audit.py — 能力清单、覆盖域表与 GDD 纯度机检(
tests//probe/)有 `func 方法(`,或该方法名能在 reports/需求卡片*.md 中找到(计划入口同 D 口径);两头都无=红。
F 覆盖域表:systems 每篇一行;状态∈{已覆盖,部分覆盖,未覆盖};非未覆盖行的「能力行」非空且每项都能
对上能力表里的行名;部分/未覆盖行的缺口非空且指待办清单。
G 判据出处随测注记(test-conventions §8.3):game/tests/** 与 game/probe/** 内每个验证函数
(函数体含 check(/check_quiet(/_problems.append(/push_error( 判定调用,定义行与 _check( 前缀除外)
须带 ≥1 条 `# oracle: <design/相对路径>:<行号> <判据一句>` 注记;指针指向存在的设计文档行且不出
design/ 全域。扫描面=game/tests/**+game/probe/**(sim/ 不进——测量面直构惯例)。
条件闸行豁免(代表用例含「条件闸」字样)不参与 D/E。
自检四道(防扫描面空转假绿):系统文档数、能力行数、manifest 套件数、覆盖表行数任一为零 ⇒ exit 2。
自检五道(防扫描面空转假绿):系统文档数、能力行数、manifest 套件数、覆盖表行数、判据注记扫描面文件数
任一为零 ⇒ exit 2。
用法:
python tools/capability_audit.py # 报告模式(红项只打印,不改退出码)
@@ -332,6 +337,67 @@ def check_coverage(cov_tables, names):
red(README.relative_to(REPO), 1, "覆盖域表", "%s 未在覆盖域表登记" % doc)
ORACLE_RE = re.compile(r"^#\s*oracle:\s*(\S+\.md):(\d+)\s+\S")
VERDICT_RE = re.compile(r"(?<![_\w])check(?:_quiet)?\(|_problems\.append\(|push_error\(")
ORACLE_FUNC_RE = re.compile(r"^(?:static\s+)?func\s+([A-Za-z_]\w*)")
def check_oracle_markers():
"""判据出处随测注记机检(G 道):验证函数须带 # oracle 注记,指针可核。"""
files = sorted((REPO / "game" / "tests").rglob("*.gd")) + sorted((REPO / "game" / "probe").glob("*.gd"))
files = [p for p in files if p.name != "test_kit.gd" and p.name != "run_all.gd"]
if not files:
selfcheck.append("判据出处注记扫描面零文件")
return 0, 0
marked = flagged = 0
for p in files:
rel = p.relative_to(REPO).as_posix()
lines = read(p).splitlines()
starts = []
for i, ln in enumerate(lines):
m = ORACLE_FUNC_RE.match(ln)
if m:
starts.append((m.group(1), i))
ranges = []
for j, (name, s0) in enumerate(starts):
e0 = (starts[j + 1][1] - 1) if j + 1 < len(starts) else len(lines) - 1
ranges.append((name, s0, e0))
for name, s0, e0 in ranges:
body = lines[s0:e0 + 1]
has_verdict = False
for ln in body:
st = ln.strip()
if st.startswith("func ") or st.startswith("#"):
continue # 定义行与注释行不作判定调用计
code = st.split("#")[0]
if VERDICT_RE.search(code):
has_verdict = True
break
if not has_verdict:
continue
oks = []
for ln in body:
st = ln.strip()
if st.startswith("#"):
m = ORACLE_RE.match(st)
if m:
oks.append(m)
if not oks:
red(rel, s0 + 1, "判据出处注记", "验证函数 %s 缺 # oracle 注记" % name)
flagged += 1
continue
marked += 1
for m in oks:
relp, no = m.group(1), int(m.group(2))
if not relp.replace("\\", "/").startswith("design/"):
red(rel, s0 + 1, "判据出处注记", "%s 指针越出 design/ 全域:%s" % (name, relp))
continue
tp = REPO / relp
if not tp.is_file() or not (1 <= no <= len(read(tp).splitlines())):
red(rel, s0 + 1, "判据出处注记", "%s 指针不可核:%s:%d(文件缺失或行号越界)" % (name, relp, no))
return marked, flagged
def main():
# GBK 控制台兜底(红项明细含 ✗ 等非 GBK 字符,编码崩=红项被吞不可见)
for stream in (sys.stdout, sys.stderr):
@@ -355,15 +421,16 @@ def main():
names, referenced = check_capability_rows(cap_tables, suites, funcs, cards_corpus())
missing_suites = check_suite_coverage(suites, referenced)
check_coverage(cov_tables, names)
oracle_marked, oracle_flagged = check_oracle_markers()
if selfcheck:
print("capability_audit 扫描面异常:")
for s in selfcheck:
print(" ! " + s)
print("| ── 能力审计:系统文档 %d 篇 · 能力行 %d · 套件 %d · 覆盖域表 %d 行 · 链断套件 %d · 红项 %d"
print("| ── 能力审计:系统文档 %d 篇 · 能力行 %d · 套件 %d · 覆盖域表 %d 行 · 链断套件 %d · oracle 注记函数 %d · 红项 %d"
% (len(system_docs()), len(names), len(suites),
sum(len(r) for _, r in cov_tables), len(missing_suites), len(reds)))
sum(len(r) for _, r in cov_tables), len(missing_suites), oracle_marked, len(reds)))
for path, line, check, msg in reds:
print(" ✗ [%s] %s:%d %s" % (check, path, line, msg))
if selfcheck:
+2 -1
View File
@@ -145,7 +145,8 @@ def main() -> int:
js = "/* 本体对账清单——coverage_enum.py 随跑重生成,人工核销走豁免清单(coverage_exemptions.js),不落本文件。 */\n"
js += "window.__QA_COVERAGE__ = window.__QA_COVERAGE__ || {};\n"
js += "window.__QA_COVERAGE__.ontology = %s;\n" % json.dumps(payload, ensure_ascii=False, indent=2)
out.write_text(js, encoding="utf-8", newline="\n")
with open(str(out), "w", encoding="utf-8", newline="\n") as fh:
fh.write(js)
gap_total = sum(len(c["gaps"]) for c in payload["categories"])
print("[ontology] 本体实体 %d / 断言文料行 %d / 缺口 %d(%d 类)→ %s" % (
len(entities), len(_rows), gap_total, len(payload["categories"]), out))
+4 -2
View File
@@ -157,7 +157,8 @@ def main() -> int:
js += "window.__QA_COVERAGE__.gate_receipts = window.__QA_COVERAGE__.gate_receipts || {};\n"
js += "window.__QA_COVERAGE__.gate_receipts[%s] = %s;\n" % (
json.dumps(key), json.dumps(payload, ensure_ascii=False, indent=2))
(out_dir / ("gate_%s.js" % key)).write_text(js, encoding="utf-8", newline="\n")
with open(str(out_dir / ("gate_%s.js" % key)), "w", encoding="utf-8", newline="\n") as fh:
fh.write(js)
wrote += 1
print("[receipts] %s(%s)=%s" % (label, log_name, detail.get("result", "?")))
except Exception as exc: # noqa: BLE001 —— 观测件逐层容错,红字可见不静默
@@ -175,7 +176,8 @@ def main() -> int:
mjs = "/* 探针登记清单——coverage_receipts.py 随跑批按现存文件覆写,勿手编。 */\n"
mjs += "window.__QA_COVERAGE__ = window.__QA_COVERAGE__ || {};\n"
mjs += "window.__QA_COVERAGE__.probe_manifest = %s;\n" % json.dumps(manifest, ensure_ascii=False)
(out_dir / "probe_manifest.js").write_text(mjs, encoding="utf-8", newline="\n")
with open(str(out_dir / "probe_manifest.js"), "w", encoding="utf-8", newline="\n") as fh:
fh.write(mjs)
print("[receipts] 探针登记清单 %d 件 → probe_manifest.js" % len(probe_files))
return 1 if failed else 0
+2 -1
View File
@@ -101,7 +101,8 @@ def dump_js(payload: dict, out: Path) -> None:
js = "/* 能力清单义务层投影——由 extract_capability_list.py 生成,随 README 变更重跑;人工修订无效(义务源=README 能力清单)。 */\n"
js += "window.__QA_COVERAGE__ = window.__QA_COVERAGE__ || {};\n"
js += "window.__QA_COVERAGE__.capability_list = %s;\n" % json.dumps(payload, ensure_ascii=False, indent=2)
out.write_text(js, encoding="utf-8", newline="\n")
with open(str(out), "w", encoding="utf-8", newline="\n") as fh:
fh.write(js)
def main() -> int:
+3 -1
View File
@@ -585,7 +585,9 @@ def main() -> int:
carry = carry_cells(old_rows)
print("[extract_checks] 旧清单 %d 行 → 承接指纹 %d 个" % (len(old_rows), len(carry)))
entries, funcs_by_src = extract_all(src_paths)
out.write_text(render_list(entries, funcs_by_src, [p.name for p in src_paths], carry), encoding="utf-8", newline="\n")
listing = render_list(entries, funcs_by_src, [p.name for p in src_paths], carry)
with open(str(out), "w", encoding="utf-8", newline="\n") as fh:
fh.write(listing)
hitn = sum(1 for e in entries
if (fold(e["cond"]), e["label_literal"], fold(e["label_suffix"])) in carry)
print("[extract_checks] 已生成 %s(%d 个源文件、%d 个调用点、承接回填 %d 行)"
+1 -1
View File
@@ -66,7 +66,7 @@ GRADE_RULE_DATE = (2026, 11, 5)
GRADE_RE = re.compile(r"验收档\s*[::]\s*[高中低]")
# C5 判据出处行锚(2026-11-05 立;test-conventions §8.3:需求点格须带现行文档判据句锚)
ANCHOR_RULE_DATE = (2026, 11, 5)
ANCHOR_RE = re.compile(r"([A-Za-z0-9_\-.\\/]+\.(?:md))\s*:\s*(\d+)")
ANCHOR_RE = re.compile(r"([^\s`'\"()(),,、::|]+\.md)\s*:\s*(\d+)") # 路径段放行中文名(系统文档全是中文名,test-conventions §8.3)
# C6 复核记录(2026-11-05 立,生效 2026-11-06;req-card 必备结构第 17 节+纪律「复核发现四类分流」)
REVIEW_RULE_DATE = (2026, 11, 6)
REVIEW_HEADING_RE = re.compile(r"^#{2,4}\s*\d*\.?\s*复核记录")