Why: the make-variable pass-through re-quoted CMD and broke any command containing quotes (caught by the redaction self-test before it could run). Evidence: redaction self-test now records 'token <redacted> hello' with quoting intact (row sha256 aae6a895); negative 'false' case earlier recorded exit=1 row and propagated rc — both in the task card evidence table. Impact: CMD now travels via environment (CMD="..." make evidence); SRC/TAIL stay make variables; usage strings updated in Makefile/AGENTS.md/task.sh template comment. Refs: task card evidence table; guidelines draft.10 tool-written-evidence rule.
56 lines
3.0 KiB
Bash
Executable File
56 lines
3.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 证据写入工具:执行命令,把(命令、被测 HEAD、退出码、输出 sha256、尾部摘录、时间、来源)
|
||
# 追加到当前分支任务卡的「## 证据(Evidence)」表。规范依据:AGENTS.md「证据与验证门禁」、
|
||
# guidelines standards/verification.md「声明与证据来源」——转写/手抄的输出视为无效证据。
|
||
# 用法:CMD="<单行命令>" make evidence [SRC=SELF|REPLAY|GATE] [TAIL=15]
|
||
# (CMD 经环境变量直传;也可直接 CMD=… SRC=… TAIL=… bash scripts/evidence.sh)
|
||
# 退出码 = 被执行命令的退出码(失败可见),证据行无论成败都会写入。
|
||
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"
|