Co-authored-by: Timothy <timothy@noreply.gitea.tblindustries.be>
3.1 KiB
key, title, status, since, supersedes, superseded-by, rule, signals, mechanics
| key | title | status | since | supersedes | superseded-by | rule | signals | mechanics |
|---|---|---|---|---|---|---|---|---|
| process.bom-format-detection-recipe | 2026-07-21 — BOM/format pre-push detection: use the `od` byte check, and run `dotnet format --include` under `bash -c` (#542, detector corrected #797) | active | 2026-07-21 | none | none | Before any push touching `.cs`, detect BOMs with the `od -A n -t x1 -N 3` byte check and verify the format gate with `dotnet format --include` run under `bash -c`, never bare zsh. NOT `xxd`: it ships with vim and is absent on plain Linux hosts including this repo's CI runner, where the substitution yields empty, never matches, and the check reports all-clean — the same all-clean-detector failure this record was written about, in the detector it prescribed. | UTF-8 BOM · `efbbbf` · `dotnet format --verify-no-changes` · `--include` · `mapfile` · zsh vs bash · #311 format gate · detector verification · paths: `ErsatzTV.sln`, `.editorconfig` · issues: #542, #311, #70, PR #402, PR #405 | Local pre-push shell; CI's format job recipe (`ci.format-gate-folder-mode`). The BOM *policy* is `release.format-as-you-touch-rebase`; this record is the mechanics only. |
Detection loop, verbatim:
for f in $(git diff --name-only origin/main...HEAD -- '*.cs'); do [ "$(od -A n -t x1 -N 3 < "$f" | tr -d ' \n')" = efbbbf ] && echo "BOM: $f"; done
The prescribed detector was itself an all-clean detector, corrected 2026-08-14 (#797). This
record was written because a detector that can only say "ok" is worse than none. It then prescribed
xxd -p, and xxd ships with vim and is absent on plain Linux hosts, including this repo's CI
runner — where the substitution yields the empty string, never matches efbbbf, and the loop
prints nothing for a tree full of BOMs. The rule failed its own test, in the recipe it recommends,
for three weeks.
Read the original warning precisely, because it is still right and it is not about od: the form
that reported all-clean over 19 dirty files was od -An -c | grep '357 273 277' — octal character
output, whose spacing and escaping vary. The replacement is od -A n -t x1 -N 3, which emits
hexadecimal bytes. That is a different invocation of the same tool, and it was measured across 17
inputs (empty, sub-3-byte, exactly the BOM, BOM+NUL, binary, UTF-16 BOM, unreadable, 20 MB, awkward
filenames) on BSD od, GNU od, and inside the actual CI runner image: identical in every cell.
tr -d ' \n' is load-bearing — BSD pads to a fixed column width and GNU does not.
Verify your detector — use the form above, or read the bytes directly, and distrust a clean result you didn't prove can go dirty.
dotnet format --include DOES work here — an earlier note claiming it silently no-ops was WRONG.
The apparent no-op was the shell: CI's recipe uses mapfile, which is bash-only, and the default
shell here is zsh → empty array → zero files → exit 0. Working form:
bash -c 'mapfile -t files < <(git diff --name-only --diff-filter=ACM origin/main...HEAD -- "*.cs")
dotnet format ErsatzTV.sln --no-restore --verify-no-changes --include "${files[@]}"' # exit 0 = gate passes