bd-audit-ids: only ls-files -d says deleted; other absent paths are exit 4 (tdfd)

A skip-worktree or sparse path is absent without being deleted, and
the filter skipped it, so its ids were never read and the audit
passed. The xargs comment no longer claims 124, which is GNU only.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
Edward Langley
2026-09-23 11:14:24 -07:00
parent e26498209b
commit acecc63dff
3 changed files with 52 additions and 5 deletions
+25
View File
@@ -894,3 +894,28 @@ case passed before and after.
The `chmod 000` case is skipped when the suite runs as root, where the file
stays readable. The dangling-symlink case covers the same path for every user.
### Absent is not deleted — `scripts/bd-audit-ids-test.sh` (improvise-tdfd)
**What broke.** The file filter `[ -e "$f" ] || [ -L "$f" ]` dropped every
tracked path missing from the tree, and took each one to be a deletion. A path
marked `--skip-worktree` (or left out by a sparse checkout) is absent too, but
it is not deleted. Its ids were never read, and the audit passed. ChatGPT
Codex found this on PR #244. The adversarial review reproduced it with
`context/adr/001-concurrent-edits.md`, the only citation of one id: exit 1 → 0.
**Fix, Codex's suggestion.** An absent path is skipped only when
`git ls-files -d` lists it. Any other absent tracked path is exit 4, and the
path is named. The xargs comment no longer claims 124. That is GNU; BSD exits
1. The script tests nonzero, so both work.
**Red first.** "a skip-worktree file missing from the tree is exit 4, and
named" exited 0 against the pre-fix script, reporting "all 1 cited ids
resolve".
**Rival:** every absent path reported as unread (the `ls-files -d` test
removed). It fails "a tracked file deleted from the working tree is not
scanned" alone, so the deleted-file case is what stops the fix from
over-refusing. The rival ran from an ignored copy inside the worktree. A copy
under `/tmp` fails "the real tree does not pass against a silent bd" for a
reason unrelated to the rival: there is no repository there.
+15 -5
View File
@@ -13,7 +13,8 @@ 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, not inside a git repository, or a cited file
could not be read (a tracked file deleted from the tree is skipped)
could not be read or is absent (only a file `git ls-files -d`
reports as deleted is skipped)
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"
@@ -42,15 +43,24 @@ top=$(git rev-parse --show-toplevel 2>/dev/null) || {
exit 4
}
cd "$top"
# A tracked file deleted from the working tree is left out ON PURPOSE: its ids
# are leaving the tree. Anything else grep cannot read is exit 4 (improvise-lqs0).
# A deleted tracked file is skipped ON PURPOSE: its ids are leaving the tree.
# Only `ls-files -d` says deleted; any other absent path is exit 4 (lqs0, tdfd).
: > "$work/absent"
git ls-files -z --cached --others --exclude-standard -- "${ROOTS[@]}" |
while IFS= read -r -d '' f; do
if [ -e "$f" ] || [ -L "$f" ]; then printf '%s\0' "$f"; fi
if [ -e "$f" ] || [ -L "$f" ]; then printf '%s\0' "$f"
elif [ -z "$(git --literal-pathspecs ls-files -d -- "$f")" ]; then
printf '%s\n' "$f" >> "$work/absent"
fi
done > "$work/files"
if [ -s "$work/absent" ]; then
echo "bd-audit-ids: tracked but absent, and not deleted, so never read:" >&2
sed 's/^/ /' "$work/absent" >&2
exit 4
fi
# xargs reports grep's no-match (1) and its read error (2) alike, as 123, so
# each batch maps 1 to success and 2 to 255, which makes xargs stop with 124.
# each batch maps 1 to success and 2 to 255: xargs stops, nonzero (GNU 124, BSD 1).
: > "$work/raw"
set +e
if [ -s "$work/files" ]; then
+12
View File
@@ -120,6 +120,18 @@ mv "$work/b.md.away" "$r2/context/b.md"
[ "$got" = 0 ] && ok "a tracked file deleted from the working tree is not scanned" \
|| bad "a tracked file deleted from the working tree is not scanned" "exit $got: $(head -c 200 "$work/out")"
# Absent is not deleted: a skip-worktree path is missing too, but ls-files -d
# does not list it, so its ids were never read (improvise-tdfd).
git -C "$r2" update-index --skip-worktree context/b.md
mv "$r2/context/b.md" "$work/b.md.away"
(cd "$r2" && "$AUDIT") > "$work/out" 2>&1
got=$?
mv "$work/b.md.away" "$r2/context/b.md"
git -C "$r2" update-index --no-skip-worktree context/b.md
[ "$got" = 4 ] && grep -q 'context/b.md' "$work/out" \
&& ok "a skip-worktree file missing from the tree is exit 4, and named" \
|| bad "a skip-worktree file missing from the tree is exit 4, and named" "exit $got: $(head -c 200 "$work/out")"
mkdir -p "$work/not-a-repo"
(cd "$work/not-a-repo" && GIT_CEILING_DIRECTORIES="$work" "$AUDIT") > "$work/out" 2>&1
got=$?