Files
Agent-System-Dev/scripts/evidence.sh
T
weichen ba83a17c11 Add evidence gates: facts registry, evidence tooling, and precheck checks
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.
2026-10-05 10:47:27 +00:00

55 lines
2.9 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 证据写入工具:执行命令,把(命令、被测 HEAD、退出码、输出 sha256、尾部摘录、时间、来源)
# 追加到当前分支任务卡的「## 证据(Evidence)」表。规范依据:AGENTS.md「证据与验证门禁」、
# guidelines standards/verification.md「声明与证据来源」——转写/手抄的输出视为无效证据。
# 用法:make evidence CMD="<单行命令>" [SRC=SELF|REPLAY|GATE] [TAIL=15]
# 退出码 = 被执行命令的退出码(失败可见),证据行无论成败都会写入。
set -uo pipefail
CMD="${CMD:-}"; SRC="${SRC:-SELF}"; TAILN="${TAIL:-15}"
[ -n "$CMD" ] || { echo 'usage: make evidence CMD="<command>" [SRC=SELF|REPLAY|GATE] [TAIL=15]' >&2; exit 2; }
case "$SRC" in SELF|REPLAY|GATE) ;; *) echo "ERROR: SRC 须为 SELF|REPLAY|GATE(当前:$SRC)" >&2; exit 2 ;; esac
case "$TAILN" in ''|*[!0-9]*) TAILN=15 ;; esac
case "$CMD" in *$'\n'*) echo 'ERROR: CMD 须为单行命令' >&2; exit 2 ;; esac
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
[ -n "$branch" ] || { echo "ERROR: 不在 git 仓库内" >&2; exit 1; }
slug=$(echo "$branch" | tr '/' '-')
card=".task/${slug}.md"
if [ ! -f "$card" ]; then
# 回退:按卡片「分支:」字段匹配(与 task.sh cmd_sync 同款口径)
for pc in .task/*.md; do
[ -f "$pc" ] || continue
if [ "$(sed -n 's/^- 工作区:.*分支:\([A-Za-z0-9._/-]\+\).*/\1/p' "$pc" | head -1)" = "$branch" ]; then
card=$pc; break
fi
done
fi
[ -f "$card" ] || { echo "ERROR: 找不到分支 ${branch} 的任务卡(.task/${slug}.md)" >&2; exit 1; }
head=$(git rev-parse HEAD)
short_head=$(echo "$head" | cut -c1-10)
ts=$(date -u +%FT%TZ)
tmp=$(mktemp); trap 'rm -f "$tmp"' EXIT
bash -c "$CMD" >"$tmp" 2>&1; rc=$?
digest=$(sha256sum "$tmp" | cut -c1-16)
# 脱敏(baseline 硬边界 2):token 风格的 40 位 hex / Authorization 头不打进任务卡。
sanitize() {
sed -E -e 's/(token[ =:]+)[0-9a-fA-F]{40}/\1<redacted>/g' \
-e 's/(Authorization: *token +)[^ ]+/\1<redacted>/Ig' \
-e 's/(access_token[=:]+)[0-9a-fA-F]{40}/\1<redacted>/Ig'
}
if ! grep -q '^## 证据(Evidence)' "$card"; then
printf '\n## 证据(Evidence)\n\n<!-- make evidence CMD="…" 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->\n| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |\n|---|---|---|---|---|---|\n' >> "$card"
fi
cmd_cell=$(echo "$CMD" | sed -e 's/|/\\|/g' -e 's/`/‘/g')
printf '| %s | `%s` | %s | %d | %s | %s |\n' "$ts" "$cmd_cell" "$short_head" "$rc" "$SRC" "$digest" >> "$card"
printf '\n<!-- evidence:run %s -->\n```\n$ %s\n[exit %d | HEAD %s | sha256:%s] 输出尾部 %s 行:\n%s\n```\n' \
"$ts" "$CMD" "$rc" "$short_head" "$digest" "$TAILN" "$(tail -n "$TAILN" "$tmp" | sanitize)" >> "$card"
echo "[evidence] card=$card cmd=\"${CMD}\" exit=$rc src=$SRC head=$short_head sha256=$digest"
exit "$rc"