diff --git a/context/test-evidence/tooling.md b/context/test-evidence/tooling.md index f496a3cd..d522ba5c 100644 --- a/context/test-evidence/tooling.md +++ b/context/test-evidence/tooling.md @@ -919,3 +919,32 @@ 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. + +### Deleted means git will record it — `scripts/bd-audit-ids-test.sh` (improvise-cota) + +**What broke.** The tdfd fix above asked `git ls-files -d` whether an absent path +was deleted. `ls-files -d` also lists an absent path marked +`--assume-unchanged`. But `git status` shows nothing for that path, and +`commit -a` would never record the deletion, so its citations stay in `HEAD`. +The audit skipped the file and exited 0. This is the tdfd shape again: absent, +not a deletion git will record, and never read. It was found in the delta +review of PR #244 at `33d563a9`. + +**Fix, the reviewer's oracle.** A path counts as deleted only when +`git diff --name-only --diff-filter=D` lists it. That compares the index +with the working tree and honours both index flags: it hides a +skip-worktree path and an assume-unchanged path alike, and lists a plain +unstaged delete. A staged `git rm` never reaches the question, because the path +has already left the index that `ls-files --cached` enumerates. + +**Red first.** "an assume-unchanged file missing from the tree is exit 4, and +named" exited 0 against `33d563a9`, reporting "all 1 cited ids resolve". + +**Rivals.** +- The `ls-files -d` oracle, which is the red state above. +- Every absent path reported as unread (`elif true`). It fails "a tracked file + deleted from the working tree is not scanned" and nothing else, so the + deliberate skip of a real deletion survives the stricter oracle. + +This rival was edited in place in the worktree script, not copied to `/tmp`, for +the reason the tdfd entry gives. diff --git a/scripts/bd-audit-ids b/scripts/bd-audit-ids index 6c91d4a0..c6561c04 100755 --- a/scripts/bd-audit-ids +++ b/scripts/bd-audit-ids @@ -13,8 +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 or is absent (only a file `git ls-files -d` - reports as deleted is skipped) + could not be read or is absent (only a file `git diff + --diff-filter=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" @@ -43,13 +43,13 @@ top=$(git rev-parse --show-toplevel 2>/dev/null) || { exit 4 } cd "$top" -# 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). +# A deleted tracked file is skipped ON PURPOSE: its ids are leaving the tree. Only +# `diff --diff-filter=D` says deleted; any other absent path is exit 4 (tdfd, cota). : > "$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" - elif [ -z "$(git --literal-pathspecs ls-files -d -- "$f")" ]; then + elif [ -z "$(git --literal-pathspecs diff --name-only --diff-filter=D -- "$f")" ]; then printf '%s\n' "$f" >> "$work/absent" fi done > "$work/files" diff --git a/scripts/bd-audit-ids-test.sh b/scripts/bd-audit-ids-test.sh index 5a6275bd..45dfe996 100755 --- a/scripts/bd-audit-ids-test.sh +++ b/scripts/bd-audit-ids-test.sh @@ -132,6 +132,18 @@ git -C "$r2" update-index --no-skip-worktree context/b.md && 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")" +# ls-files -d DOES list an absent assume-unchanged path, yet status hides it and +# commit -a never records the deletion (improvise-cota). +git -C "$r2" update-index --assume-unchanged 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-assume-unchanged context/b.md +[ "$got" = 4 ] && grep -q 'context/b.md' "$work/out" \ + && ok "an assume-unchanged file missing from the tree is exit 4, and named" \ + || bad "an assume-unchanged 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=$?