Why: declarations were passing as capability; hand-transcribed outputs flowed through cards and PR bodies as if they were execution evidence, and stale external facts (e.g. the dead git-panel host) kept being trusted until burned. Evidence: guidelines draft.10 (PR Development-Guidelines#4, merged af199ee) pinned here; policy/facts.md F1-F4 each re-verified on 2026-10-05 before registration (endpoint version, token HTTP 200 via header only, SSH ls-remote, dead-host negative probe); scripts syntax-checked (bash -n); tool self-tests and make test recorded in the task card via the new make evidence. Impact: new AGENTS.md section (SELF/REPLAY/GATE, tool-written evidence, replay-on-review, zero-trust facts, honest-empty); make evidence/facts-verify; precheck gains [hooks]/[facts], task-sync gains stale-evidence-HEAD warning; task-start cards now embed the evidence table; gitlink advances to draft.10. Refs: task card .task/feature-evidence-driven-gates.md; guidelines CHANGELOG 0.1.0-draft.10.
43 lines
2.0 KiB
Bash
Executable File
43 lines
2.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 外部事实登记表逐条重验:对 policy/facts.md 每条事实执行其验证命令并报告 PASS/FAIL。
|
||
# 规则依据:AGENTS.md「证据与验证门禁」(外部事实零信任);verification.md「声明与证据来源」。
|
||
# 用法:make facts-verify [ARGS=--update] (--update:把 PASS 项 last_verified 刷为当天并写回)
|
||
# 表内验证命令约定不含竖线;含 "\|" 转义时按字面还原后执行。
|
||
set -uo pipefail
|
||
F="${FACTS:-policy/facts.md}"
|
||
[ -f "$F" ] || { echo "ERROR: 事实登记表 $F 不存在" >&2; exit 1; }
|
||
python3 - "$F" "${1:-}" <<'PY'
|
||
import re, subprocess, sys, datetime
|
||
path, update = sys.argv[1], sys.argv[2] == "--update"
|
||
lines = open(path, encoding="utf-8").read().splitlines(keepends=True)
|
||
today = datetime.date.today().isoformat()
|
||
total = fails = 0
|
||
for i, line in enumerate(lines):
|
||
if not re.match(r"^\|\s*F\d+\s*\|", line):
|
||
continue
|
||
raw = line.strip().strip("|").replace("\\|", "\x00")
|
||
cells = [c.strip().replace("\x00", "|") for c in raw.split("|")]
|
||
if len(cells) < 4:
|
||
print(f"[row {i+1}] SKIP 格式不符(列数<4)", file=sys.stderr)
|
||
continue
|
||
fid, cmd = cells[0], cells[2].strip("`")
|
||
total += 1
|
||
try:
|
||
r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=90)
|
||
ok = r.returncode == 0
|
||
except subprocess.TimeoutExpired:
|
||
ok = False
|
||
print(f"[{fid}] TIMEOUT >90s", file=sys.stderr)
|
||
print(f"[{fid}] {'PASS' if ok else 'FAIL'} :: {cmd[:80]}")
|
||
if not ok:
|
||
fails += 1
|
||
elif update and len(cells) >= 4 and re.match(r"\d{4}-\d{2}-\d{2}", cells[3]):
|
||
cells[3] = today
|
||
lines[i] = "| " + " | ".join(c.replace("|", "\\|") for c in cells) + " |\n"
|
||
if update:
|
||
open(path, "w", encoding="utf-8").writelines(lines)
|
||
print(f"[facts-verify] total={total} pass={total - fails} fail={fails}"
|
||
+ (";last_verified 已刷新为 " + today if update and total else ""))
|
||
sys.exit(1 if fails else 0)
|
||
PY
|