#!/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="" [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/g' \ -e 's/(Authorization: *token +)[^ ]+/\1/Ig' \ -e 's/(access_token[=:]+)[0-9a-fA-F]{40}/\1/Ig' } if ! grep -q '^## 证据(Evidence)' "$card"; then printf '\n## 证据(Evidence)\n\n\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\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"