--- name: shell-suite-green-locale-proof description: "Proving a bats/shell suite is actually green (grep, never tail) and debugging generated output that silently loses content under LC_ALL=C (multibyte quantifiers in awk/grep). Use when reporting \"suite green\", or when a generator emits partial output with exit 0." --- # Proving a suite is green, and locale-blind generators Two failure modes earned on `dank/command-center` (2026-10-20): a false "all tests pass" report, and a generator that silently dropped a third of its output. ## 1. Never report green from a `tail` window `bats tests/ | tail -25` shows the last tests, not the failures — `not ok` lines print inline at their position and vanish off the top. A "109 ok, 0 failures" claim built this way was wrong; the claim had to be corrected in the merged PR's body. ```bash out=$(bats tests/ 2>&1) printf '%s\n' "$out" | grep -cE '^ok' # passing count printf '%s\n' "$out" | grep -E '^not ok' # THIS is the claim; empty == green ``` Also: a suite can be green under the operator's terminal and red under a C locale, so run both before writing "green" in a PR body: ```bash env -u LC_ALL bats tests/ # operator's locale (LANG from the desktop session) LC_ALL=C bats tests/ # cron, systemd user units, agent shells ``` State which locale each number came from. Agent shells in this fleet run `LC_ALL=C` even when `LANG=en_US.UTF-8` — `LC_ALL` overrides everything. ## 2. Multibyte quantifiers silently truncate generated output A quantifier on a multibyte character (`─+`, `━+`, `→*`) breaks under `LC_ALL=C`: gawk matches byte-wise, so `─+` means "the last byte of U+2500, repeated", which never occurs in real text. Symptom: output loses *every* match — sections, headers, rows — with **exit status 0**. No error, no partial-write warning. ```bash # broken under C awk '/^[[:space:]]*\/\/[[:space:]]*─+ (.+) ─+[[:space:]]*$/' # byte-safe in every locale: match the multibyte char as a LITERAL, no quantifier /^[[:space:]]*\/\/[[:space:]]*─/ { s = $0; sub(/^[[:space:]]*\/\/[[:space:]]*/, "", s) if (s ~ /─ .*─/) { gsub(/─/, "", s); gsub(/[[:space:]]+/, " ", s) } } ``` Do NOT "fix" it by pinning a locale (`LC_ALL=en_US.UTF-8`) — that locale may not be installed; a CachyOS box here shipped only `C.utf8`. Make the matcher locale-independent. ## 3. Prove the loss, don't infer it Diff old vs new against real input in both locales, with counts, before claiming a fix: ```bash git show origin/main:scripts/gen-keybinds.sh > /tmp/old.sh for L in C C.utf8; do printf '%s: old=' "$L"; LC_ALL=$L bash /tmp/old.sh "$REAL" | grep -c '^## ' printf ' new='; LC_ALL=$L scripts/gen-keybinds.sh "$REAL" | grep -c '^## ' done # old printed 0 sections vs 12 under C, 120 rows intact, exit 0 ``` Check whether a *committed generated artifact* was produced by a bad run — if it has the full content, it came from a UTF-8 shell and needs no regeneration. ## 4. Gates must not demand deletion Repo linters walking `.AGENTTRASH/` (quarantined, gitignored) turn green only once the quarantined page is deleted — an incentive to destroy history. Skip any path containing `.AGENTTRASH` in both the linter and the coverage checker, and pin it with a test.