工程(流水线闸): 验收对照表加「能力行」列——2026-10-29 起生效,既有名回核 game/tests/README.md 能力表

check_ledger 增第七列:每行写 既有:<能力名> 或 新增:<能力名>(分号可多段)。既有名须在能力表首列可查,新增放行(新能力行由写卡段落表,test-conventions §8.9 滞后一迭代)。日期门控对齐 BINDING_RULE_DATE 同构:文件名日期早于 2026-10-29 的旧卡不回头改。自测四况通过(既有命中/新增放行/既有不存在红/格式错红/缺列红/旧卡免检)。req-card 与 iter 两技能本地件同批同步(不进 git):对照表改七格、写卡段增删能力行并刷最后验证批次、实现批禁写 README。
This commit is contained in:
DKChan committed 2026-10-29 01:55:59 +08:00
1 parent ae3c99e34e
commit f32634446e
1 file changed
+52 -2
+52 -2
View File
@@ -55,6 +55,9 @@ LEDGER_PROOF_MARKS = ("撤销", "证明")
BINDING_RULE_DATE = (2026, 10, 28) # req-card §8:换代为判据绑定面列,撤销/变异证明列同日禁列
BANNED_PROOF_MARKS = ("撤销证明", "变异证明")
AUDIT_MARK = "验收审计"
CAP_ROW_RULE_DATE = (2026, 10, 29) # req-card §8:每行标能力归属(对齐 test-conventions §8.9 三层模型)
CAP_ROW_RE = re.compile(r"^(既有|新增)\s*[::]\s*(.+)$")
CAP_README = Path("game/tests/README.md")
DATE_RE = re.compile(r"\d{4}-\d{2}-\d{2}")
VER_RE = re.compile(r"[vV]\d+(?:\.\d+)*")
@@ -453,7 +456,30 @@ def card_date(path):
return tuple(int(x) for x in found[-1].split("-"))
def check_ledger(text, path, when, issues):
def capability_names(root):
"""game/tests/README.md 能力表首列行名集合;文件或表缺失返回空集(调用方据此判红)。"""
p = root / CAP_README
if not p.is_file():
return set()
names = set()
header = None
for raw in p.read_text(encoding="utf-8").splitlines():
s = raw.strip()
if not s.startswith("|"):
header = None
continue
cells = row_cells(s)
if header is None:
header = cells
continue
if SEP_ROW_RE.match(s):
continue
if any("代表用例" in c for c in header) and cells:
names.add(cells[0].strip("`").strip())
return names
def check_ledger(text, path, when, issues, root):
"""C3 改动—验收对照表结构检查(生效 2026-10-21)。
只查:对照表节在不在、表头有没有五个必需列、有没有数据行、必填格空不空。
@@ -517,6 +543,18 @@ def check_ledger(text, path, when, issues):
return
idx["证明"] = proof_hit
cap_hit = None
cap_names = None
if when >= CAP_ROW_RULE_DATE:
cap_hit = next((k for k, cell in enumerate(header) if "能力行" in cell), None)
if cap_hit is None:
issues.append((str(path), header_line, "验收对照表缺列",
"缺「能力行」列(2026-10-29 起:每行写 既有:<能力名> 或 "
"新增:<能力名>,对齐能力清单)"))
return
idx["能力行"] = cap_hit
cap_names = capability_names(root)
rows = 0
for i in range(header_line, len(lines)):
s = lines[i].strip()
@@ -536,6 +574,18 @@ def check_ledger(text, path, when, issues):
if k >= len(cells) or not cells[k].strip():
issues.append((str(path), i + 1, "验收对照表空格",
"「%s」列为空" % name))
if cap_hit is not None:
cap_cell = cells[cap_hit] if cap_hit < len(cells) else ""
for seg in [x.strip() for x in re.split(r"[;;]", cap_cell) if x.strip()]:
m = CAP_ROW_RE.match(seg)
if not m:
issues.append((str(path), i + 1, "能力行格式",
"「能力行」格须写 既有:<能力名>/新增:<能力名>:%s"
% seg[:24]))
elif m.group(1) == "既有" and m.group(2).strip("`").strip() not in cap_names:
issues.append((str(path), i + 1, "能力行不可核",
"既有能力「%s」不在 game/tests/README.md 能力表"
% m.group(2).strip("`").strip()[:24]))
if rows == 0:
issues.append((str(path), header_line, "验收对照表零行",
"表内没有数据行"))
@@ -569,7 +619,7 @@ def check_cards(root, issues):
"须含「设计事实源」或「唯一事实源」声明句(req-card §2 三选一,"
"GDD 节+版本号/样张+art-bible 版本/工程形态证据指认)"))
if when >= LEDGER_RULE_DATE:
check_ledger(text, path, when, issues)
check_ledger(text, path, when, issues, root)
if AUDIT_MARK not in text:
issues.append((str(path), 1, "独立验收审计未声明",
"须在卡内写明独立验收审计入口与报告文件(2026-10-21 起,"