168a28d2c5
improvise-52eq: the AGENTS.md recipe searched `bd show` output for "not found" and printed "all resolve" when there was none, so a bd that was missing, failing or never run passed the audit. scripts/bd-audit-ids keeps the file scan and exclusions, then `comm -23`s the cited ids against `bd list --all --limit 0 --json`. A dead id is exit 1 and is named. A bd that does not answer is exit 3, never a pass. scripts/bd-audit-ids-test.sh has eight cases on a throwaway repo with a fake bd. The old recipe as a rival fails 7 of the 8. Disabling the comparison fails the dead-id case, and an empty exclusion list fails the pass case. CI runs it with the other named suites. The "One environment note" now reports which forms the guard refused and allowed, as observed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
76 lines
2.7 KiB
Bash
Executable File
76 lines
2.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# The dead-ID audit: every improvise-<id> the tree cites must be a ticket bd
|
|
# knows. See AGENTS.md, "Cite a ticket ID you have read back" (improvise-52eq).
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat >&2 <<'EOF'
|
|
usage: bd-audit-ids
|
|
|
|
Run from anywhere inside the repository. Takes no arguments.
|
|
|
|
exit 0 every cited id is a ticket bd lists
|
|
exit 1 at least one cited id is not; each is printed
|
|
exit 3 bd could not answer: missing, failing, or listing nothing
|
|
exit 4 usage error, or the file scan itself failed
|
|
|
|
The comparison is POSITIVE: a cited id passes only by appearing in what
|
|
`bd list --all` returns. The recipe this replaced looked for bd's "not found"
|
|
lines and reported "all resolve" when there were none, so a bd that never ran
|
|
read as a clean audit.
|
|
|
|
THIS SCRIPT'S `$?` MUST BE THE ONE YOU READ. Nothing may follow it in the same
|
|
pipeline or `;` list.
|
|
EOF
|
|
exit 4
|
|
}
|
|
[ $# -eq 0 ] || usage
|
|
|
|
BD="${BD_AUDIT_BD:-bd}"
|
|
ROOTS=(context README.md AGENTS.md CLAUDE.md crates src tests examples web
|
|
harness docs .agents .claude)
|
|
# Crate, package, artifact and file names that are shaped like ids. Anchored,
|
|
# because an unanchored `gp` would silently exclude any real ticket id starting with it.
|
|
EXCLUDE='^improvise-(core|formula|io|protocol|command|gp|ws|worker|wasm|harness|registry|report|side|web)$'
|
|
|
|
work=$(mktemp -d)
|
|
trap 'rm -rf "$work"' EXIT
|
|
|
|
cd "$(git rev-parse --show-toplevel)"
|
|
git ls-files -z --cached --others --exclude-standard -- "${ROOTS[@]}" > "$work/files"
|
|
|
|
# xargs exits 123 when one grep batch matched nothing, which is not an error.
|
|
set +e
|
|
xargs -0 grep -d skip -ohE 'improvise-[a-z0-9]{3,8}\b' < "$work/files" > "$work/raw"
|
|
rc=$?
|
|
set -e
|
|
[ "$rc" -eq 0 ] || [ "$rc" -eq 123 ] || { echo "bd-audit-ids: grep failed ($rc)" >&2; exit 4; }
|
|
sort -u "$work/raw" | { grep -vE "$EXCLUDE" || true; } > "$work/cited"
|
|
|
|
if ! command -v "$BD" >/dev/null 2>&1; then
|
|
echo "bd-audit-ids: '$BD' is not on PATH, so nothing was checked." >&2
|
|
exit 3
|
|
fi
|
|
if ! "$BD" list --all --limit 0 --json > "$work/known.json" 2> "$work/bd.err"; then
|
|
echo "bd-audit-ids: '$BD list' failed, so nothing was checked:" >&2
|
|
cat "$work/bd.err" >&2
|
|
exit 3
|
|
fi
|
|
if ! jq -r '.[].id' "$work/known.json" 2>/dev/null | sort -u > "$work/known"; then
|
|
echo "bd-audit-ids: '$BD list' did not print a JSON list of issues." >&2
|
|
exit 3
|
|
fi
|
|
if [ ! -s "$work/known" ]; then
|
|
echo "bd-audit-ids: '$BD list' returned no issues; refusing to call that a pass." >&2
|
|
exit 3
|
|
fi
|
|
|
|
comm -23 "$work/cited" "$work/known" > "$work/dead"
|
|
if [ -s "$work/dead" ]; then
|
|
echo "bd-audit-ids: cited but not a ticket bd knows:"
|
|
sed 's/^/ /' "$work/dead"
|
|
exit 1
|
|
fi
|
|
printf 'bd-audit-ids: all %d cited ids resolve (bd knows %d)\n' \
|
|
"$(wc -l < "$work/cited" | tr -d ' ')" "$(wc -l < "$work/known" | tr -d ' ')"
|