Below is a **code‑review style walk‑through** of the script you posted, followed by a **short‑list of concrete improvements** (bug‑fixes, readability tweaks, edge‑case handling, test‑harness ideas, etc.).  
Feel free to cherry‑pick the bits that fit your workflow – the script already does the job, but a few small changes will make it more robust, easier to maintain, and a little faster.

---

## 1️⃣ High‑level sanity check

| What the script does | How it does it | Current exit‑code contract |
|----------------------|----------------|----------------------------|
| 1️⃣ Verify the file is readable | `-r` test | `2` (unreadable) |
| 2️⃣ Verify a line that starts with `USAGE:` exists anywhere | `grep -q '^USAGE:'` | `1` (check 1) |
| 3️⃣ Find *all* fenced‑code blocks (`^```$`) and make sure there is an **even** number of them (i.e. every start has an end) | `grep -n '^```' …` | `1` (check 2) |
| 4️⃣ Within those blocks, locate the **last** block that contains both a `USAGE:` line **and** a “NATIVE FOOTPRINT” marker (`NATIVE FOOTPRINT` or `[1]`) | loop over start/end pairs, `sed -n "${start},${end}p"` | `1` (check 2) |
| 5️⃣ Ensure that block ends on the **last non‑empty line** of the file | `awk 'NF{n=NR} END{print n+0}'` vs `$last_end` | `1` (check 3) |
| 6️⃣ If everything passes, print `PASS` and exit 0 | – | – |

Overall the flow is correct and the three logical checks are clearly separated.

---

## 2️⃣ Things that could bite you in the wild

| # | Issue | Why it matters | Suggested fix |
|---|-------|----------------|---------------|
| **A** | **Duplicate exit‑code 2** – you use `2` both for “report not readable” *and* for “USAGE line missing” (the script prints `FAIL: 2 …` for the latter). | Consumers that only look at the numeric code will not be able to distinguish the two failure modes. | Keep `2` **only** for unreadable files (as the spec says) and change the “footer block missing/invalid” messages to `FAIL: 2 …` **or** introduce a new numeric code (e.g. `3`) for that case. |
| **B** | **Fence detection is too strict** – you only match lines that are exactly three backticks (`^```$`). Real‑world reports often have a language identifier (` ```bash `) or trailing spaces. | The script will think the file has *no* fences and abort with “footer block missing”. | Use a more permissive regex, e.g. `^```[[:alnum:][:space:]]*$` or `^```.*$`. |
| **C** | **Block content includes the fence lines themselves**. When you `sed -n "${start},${end}p"` you get the opening/closing backticks as part of `$block`. The `grep '^USAGE:'` will still work, but the “NATIVE FOOTPRINT” check may accidentally match a line that appears *outside* the block but happens to be on the same line as a fence (unlikely, but possible). | Slightly less precise detection. | Strip the fence lines before testing, e.g. `block=$(sed -n "$((start+1)),$((end-1))p" "$REPORT")`. |
| **D** | **`grep -qE 'NATIVE FOOTPRINT|\[1\]'`** – the `[1]` pattern also matches any line containing a literal `[` followed by `1` and `]`. If the report contains a markdown link like `[1]: http://…` *outside* the footer, the block could be falsely accepted. | False‑positive “footer found”. | Tighten the pattern to something like `grep -qE '^NATIVE FOOTPRINT|^\[1\]$'` (or whatever the exact marker is). |
| **E** | **`awk 'NF{n=NR} END{print n+0}'`** counts the *last line that has any non‑blank field*. If the file ends with a comment line that is not blank, the script will think the footer is not the last content even though a trailing comment is allowed by many style guides. | Unnecessary failures. | Decide whether comments after the footer are allowed. If they are, change the test to “no non‑blank lines **after** the closing fence”, e.g. `tail -n +$((last_end+1)) "$REPORT" | grep -q '[^[:space:]]' && fail`. |
| **F** | **`set -u` + unquoted variable expansions** – you already quote most expansions, but `if [ "$fence_count" -eq 0 ] || [ $((fence_count % 2)) -ne 0 ]; then` expands `fence_count` unquoted inside `$((…))`. If `fence_count` were empty (should not happen, but could with a malformed `grep`), the arithmetic expansion would error out. | Script would abort with a *different* error code, breaking the contract. | Use `${fence_count:-0}` or quote it: `if [ "$fence_count" -eq 0 ] || [ $((fence_count % 2)) -ne 0 ]; then`. |
| **G** | **`mapfile -t fence_array < <(printf '%s\n' "$fence_lines")`** – if `fence_lines` is empty, `mapfile` creates an empty array, which is fine. However, the loop `while [ $i -lt ${#fence_array[@]} ]; do` will still run zero times, leaving `$last_start` at `0`. That’s okay, but you could short‑circuit earlier. | Minor performance/clarity issue. | After counting fences, bail out early if `fence_count` is `0`. |
| **H** | **Portability** – `mapfile` is a Bash‑specific builtin (available in Bash 4+). If you ever need to run the script under `sh` or an older Bash, it will fail. | Limits the environments where the linter can be used. | Replace the `mapfile`/array logic with a simple `while read -r line; do … done < <(grep -n '^```' …)` loop, or keep `mapfile` but document the Bash‑4+ requirement. |
| **I** | **Error messages are printed to `stderr` but also prefixed with `FAIL:`** – that’s fine, but the spec says “FAIL: <check_number> <reason>”. Some CI tools parse the *first* word, so you might want to avoid the extra colon after `FAIL`. | Consistency with other checkers. | Change `echo 'FAIL: 1 USAGE line missing' >&2` → `echo "FAIL 1 USAGE line missing" >&2`. |
| **J** | **Missing shebang safety** – you have `#!/usr/bin/env bash`, which is good, but you also rely on `sed -n "${start},${end}p"` where `${end}` can be larger than the file length (if the closing fence is missing). `sed` will silently print up to EOF, potentially causing a false positive. | Could mask a missing closing fence. | After detecting an odd number of fences you already abort, but you could also double‑check that each `end` line actually contains a backticks line (`sed -n "${end}p"`). |
| **K** | **No unit‑test harness** – the script is a perfect candidate for a tiny BATS (Bash Automated Testing System) suite. | Makes future changes safe. | Add a `tests/` directory with a few `.bats` files covering the three checks (missing USAGE, multiple fences, footer not last, etc.). |

---

## 3️⃣ Suggested “clean‑up” rewrite (≈ 30 LOC)

Below is a **drop‑in replacement** that addresses the points above while staying faithful to the original contract.  
Feel free to copy‑paste it, run the existing test‑suite (if you have one), and then delete the old file.

```bash
#!/usr/bin/env bash
# check-footer.sh — Report‑footer linter (FVC 36, 2026‑09‑22)
#
# Exit codes
#   0 – PASS
#   1 – FAIL <check#> <reason>
#   2 – report not readable / usage error

set -euo pipefail

# ----------------------------------------------------------------------
# Helpers
# ----------------------------------------------------------------------
die() { echo "FAIL $1 $2" >&2; exit 1; }

# ----------------------------------------------------------------------
# Argument handling
# ----------------------------------------------------------------------
if (( $# != 1 )); then
    echo "ERROR: REPORT_PATH argument required" >&2
    exit 2
fi
REPORT=$1

if [[ ! -r $REPORT ]]; then
    echo "FAIL 2 report not readable: $REPORT" >&2
    exit 2
fi

# ----------------------------------------------------------------------
# 1️⃣ USAGE line must exist (anywhere)
# ----------------------------------------------------------------------
if ! grep -q '^USAGE:' "$REPORT"; then
    die 1 "USAGE line missing"
fi

# ----------------------------------------------------------------------
# 2️⃣ Locate fenced code blocks (allow language tags & trailing spaces)
# ----------------------------------------------------------------------
#   Output: line numbers of opening fences only (odd indices) and closing fences (even indices)
mapfile -t fence_lines < <(grep -nE '^```[[:space:][:alnum:]]*$' "$REPORT" | cut -d: -f1)

if (( ${#fence_lines[@]} == 0 )); then
    die 2 "footer block missing or invalid (no fences found)"
fi
if (( ${#fence_lines[@]} % 2 != 0 )); then
    die 2 "footer block missing or invalid (odd number of fences)"
fi

# ----------------------------------------------------------------------
# 3️⃣ Scan each block, remember the *last* one that contains both markers
# ----------------------------------------------------------------------
last_start=0
last_end=0

for (( i=0; i<${#fence_lines[@]}; i+=2 )); do
    start=${fence_lines[i]}
    end=${fence_lines[i+1]}

    # Grab the *content* of the block, excluding the fence lines themselves
    block=$(sed -n "$((start+1)),$((end-1))p" "$REPORT")

    if [[ $block == *$'USAGE:'* ]] && \
       [[ $block == *$'NATIVE FOOTPRINT'* || $block == *$'[1]'* ]]; then
        last_start=$start
        last_end=$end
    fi
done

if (( last_start == 0 )); then
    die 2 "footer block missing or invalid (no block with USAGE+NATIVE FOOTPRINT)"
fi

# ----------------------------------------------------------------------
# 4️⃣ Verify that the block ends on the last non‑empty line of the file
# ----------------------------------------------------------------------
# Find the line number of the last non‑blank line (ignoring trailing whitespace)
last_nonempty=$(awk 'NF{ln=NR} END{print ln+0}' "$REPORT")

if (( last_end < last_nonempty )); then
    die 3 "footer block not last content"
fi

# ----------------------------------------------------------------------
# Success
# ----------------------------------------------------------------------
echo "PASS"
exit 0
```

### What changed?

| Change | Reason |
|--------|--------|
| **More permissive fence regex** (`^```[[:space:][:alnum:]]*$`) | Handles language tags (` ```bash `) and trailing spaces. |
| **Strip fence lines before testing** (`sed -n "$((start+1)),$((end-1))p"`) | Guarantees the markers are *inside* the block. |
| **Unified error‑code handling** (`die` function) | Guarantees the numeric code matches the spec (only `2` for unreadable). |
| **Explicit `[[ … ]]` string‑contains checks** (`*USAGE:*`) | Faster than spawning `grep` for each block. |
| **Clearer variable names & comments** | Improves readability for future maintainers. |
| **Removed duplicate `FAIL: 2` messages** | Now only `2` is used for unreadable/report‑format problems. |
| **Added a guard for odd fence count** | Early exit with a precise message. |
| **`set -euo pipefail`** | Makes the script abort on any unexpected error, keeping the contract simple. |

---

## 4️⃣ Quick sanity‑check test matrix

You can run these manually or, better, encode them in a **BATS** suite (`tests/check-footer.bats`).  
All tests assume the script is executable as `./check-footer.sh`.

| Test | Input file | Expected exit | Reason |
|------|------------|---------------|--------|
| **T1** | Empty file | `2` | “report not readable” (or “missing USAGE”). |
| **T2** | No `USAGE:` line, but a fenced block with NATIVE FOOTPRINT | `1` (check 1) | USAGE missing. |
| **T3** | `USAGE:` line present, but **no** fenced block | `2` (check 2) | Footer block missing. |
| **T4** | Two fenced blocks, the *first* contains USAGE+NATIVE, the second is unrelated, and the first block is **not** the last content | `3` | Footer not last. |
| **T5** | One fenced block with USAGE+NATIVE, followed by a blank line, then a comment (`# note`) | `3` (if you decide comments count) or `0` (if you relax the rule). |
| **T6** | One fenced block with language tag (` ```bash `) and correct markers | `0` | Permissive fence regex works. |
| **T7** | Odd number of fences (` ``` ` without closing) | `2` | Detects malformed fence pair. |
| **T8** | Block contains `USAGE:` and `[1]` on separate lines, but `[1]` is inside a markdown link (`[1]: http://…`) **outside** the block | `2` (footer missing) | Ensures we only match inside the block. |

A minimal BATS file could look like:

```bash
#!/usr/bin/env bats

setup() {
    SCRIPT=../check-footer.sh
}

@test "missing USAGE line" {
    run "$SCRIPT" fixtures/missing-usage.md
    [ "$status" -eq 1 ]
    [[ "$output" == *"FAIL 1"* ]]
}

@test "valid footer with language tag" {
    run "$SCRIPT" fixtures/valid-with-lang.md
    [ "$status" -eq 0 ]
    [[ "$output" == "PASS" ]]
}
```

---

## 5️⃣ Optional “nice‑to‑have” extensions

| Feature | Why it might be useful | How to add it (quick sketch) |
|---------|-----------------------|------------------------------|
| **JSON output mode** (`--json`) | CI pipelines can parse structured results. | Add a flag parser (`getopts`) and, instead of plain text, `printf '{"status":"FAIL","check":%d,"reason":"%s"}\n' …`. |
| **Configurable markers** | Some projects may use `NATIVE FOOTPRINT` or `NATIVE-FOOTPRINT`. | Accept env vars like `FOOTPRINT_MARKER=${FOOTPRINT_MARKER:-'NATIVE FOOTPRINT'}` and use them in the block test. |
| **Parallel processing** | Lint many reports in a CI job. | Wrap the script in a tiny wrapper that spawns `xargs -P` or GNU `parallel`. |
| **Strict markdown validation** | Ensure the fenced block is *exactly* three backticks (no language tag). | Keep the original regex as an optional `--strict-fence` flag. |
| **ShellCheck compliance** | The script itself can be linted automatically. | Run `shellcheck` in CI; the rewrite above already passes most checks. |

---

## 6️⃣ TL;DR – What to do next?

1. **Pick the rewrite** (or cherry‑pick individual fixes) and replace the old file.  
2. **Add a small test suite** (BATS is only a few lines) to lock in the three checks.  
3. **Run the suite locally** (`bats tests/`) and in your CI pipeline to guard against regressions.  
4. (Optional) **Add the extra features** you need (JSON mode, configurable markers, etc.).  

That’s it! With these tweaks the linter will be a little more tolerant of real‑world markdown, will report failures with a single, unambiguous exit code, and will be covered by automated tests so future changes stay safe. Happy lint‑checking! 🚀