Extract the coverage gate into a reusable bin/coverage script
Embedded justfile recipes escape shellcheck; a standalone script is
lintable and gains a COVERAGE_THRESHOLD override.

Assisted-by: Claude Opus 4.8 via Claude Code
change osoxsuuzlrozouzzzpuzxzonvnkxnsnr
commit 1a1ecdc8352557ac3d43aa11cc6591550e801bae
author Alpha Chen <alpha@kejadlen.dev>
date
parent lzrmvvoq
diff --git a/ai/skills/rust/coverage.sh b/ai/skills/rust/coverage.sh
new file mode 100755
index 00000000..8034a431
--- /dev/null
+++ b/ai/skills/rust/coverage.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+
+# Measures library line coverage with grcov and fails below the
+# threshold. Uses a separate CARGO_TARGET_DIR so instrumented and
+# normal build artifacts never mix — mixing causes phantom uncovered
+# lines. Override the gate with COVERAGE_THRESHOLD (defaults to 100).
+#
+# Scaffolded into a project as bin/coverage; the `coverage` justfile
+# recipe runs it. See the rust skill's coverage.md and scaffolding.md.
+
+set -euo pipefail
+if [[ "${TRACE-0}" == "1" ]]; then
+    set -o xtrace
+fi
+
+readonly THRESHOLD="${COVERAGE_THRESHOLD:-100}"
+readonly TARGET_DIR="target/coverage"
+
+main() {
+    export RUSTFLAGS="-Cinstrument-coverage"
+    export CARGO_TARGET_DIR="$TARGET_DIR"
+    export LLVM_PROFILE_FILE="$TARGET_DIR/profraw/%p-%m.profraw"
+
+    rm -rf "$TARGET_DIR"
+    cargo test --workspace --quiet
+
+    local report
+    report=$(grcov "$TARGET_DIR/profraw" \
+        --binary-path "./$TARGET_DIR/debug/" \
+        --source-dir . \
+        --output-types covdir \
+        --ignore-not-existing \
+        --keep-only 'src/**' \
+        --ignore 'src/bin/**' \
+        --excl-line 'cov-excl-line|unreachable!' \
+        --excl-start 'cov-excl-start' \
+        --excl-stop 'cov-excl-stop')
+
+    # Per-file breakdown: walk the covdir tree, printing leaf files only.
+    jq --raw-output '
+        def files:
+            to_entries[] | .value |
+            if .children then .children | files
+            else "\(.name): \(.coveragePercent)% (\(.linesCovered)/\(.linesTotal))"
+            end;
+        .children | files
+    ' <<<"$report"
+
+    local coverage
+    coverage=$(jq '.coveragePercent' <<<"$report")
+    echo ""
+    echo "Total: ${coverage}%"
+
+    if [[ "$(echo "$coverage < $THRESHOLD" | bc -l)" -eq 1 ]]; then
+        echo "ERROR: coverage ${coverage}% is below ${THRESHOLD}%" >&2
+        exit 1
+    fi
+}
+
+main "$@"
diff --git a/ai/skills/rust/scaffolding.md b/ai/skills/rust/scaffolding.md
index 6ab1364a..0ff2424a 100644
--- a/ai/skills/rust/scaffolding.md
+++ b/ai/skills/rust/scaffolding.md
@@ -21,6 +21,8 @@ project/
 ├── .github/workflows/
 │   ├── ci.yml
 │   └── release.yml         # Binaries only — see binary.md.
+├── bin/
+│   └── coverage            # Coverage gate script (→ coverage.md).
 ├── src/
 │   ├── lib.rs              # Library root — re-exports modules.
 │   ├── error.rs            # thiserror + miette::Diagnostic enum.
@@ -127,38 +129,7 @@ clippy:
     cargo clippy --workspace -- -D warnings
 
 coverage:
-    #!/usr/bin/env bash
-    set -euo pipefail
-    export RUSTFLAGS="-Cinstrument-coverage"
-    export CARGO_TARGET_DIR="target/coverage"
-    export LLVM_PROFILE_FILE="target/coverage/profraw/%p-%m.profraw"
-    rm -rf target/coverage
-    cargo test --workspace -q
-    REPORT=$(grcov target/coverage/profraw \
-        --binary-path ./target/coverage/debug/ \
-        -s . \
-        -t covdir \
-        --ignore-not-existing \
-        --keep-only 'src/**' \
-        --ignore 'src/bin/**' \
-        --excl-line 'cov-excl-line|unreachable!' \
-        --excl-start 'cov-excl-start' \
-        --excl-stop 'cov-excl-stop')
-    echo "$REPORT" | jq -r '
-        def files:
-            to_entries[] | .value |
-            if .children then .children | files
-            else "\(.name): \(.coveragePercent)% (\(.linesCovered)/\(.linesTotal))"
-            end;
-        .children | files
-    '
-    COVERAGE=$(echo "$REPORT" | jq '.coveragePercent')
-    echo ""
-    echo "Total: ${COVERAGE}%"
-    if [ "$(echo "$COVERAGE < 100" | bc -l)" -eq 1 ]; then
-        echo "ERROR: Coverage is below 100%"
-        exit 1
-    fi
+    ./bin/coverage
 
 mutants:
     #!/usr/bin/env bash
@@ -177,14 +148,19 @@ install:
     cargo install --locked --path .
 ```
 
-Key design:
+The `coverage` recipe delegates to `bin/coverage` so the logic stays
+shellcheck-clean and editable outside the justfile. Copy this skill's
+`coverage.sh` reference file to `bin/coverage` in the new project and
+give it the executable bit (`chmod +x bin/coverage`). Read
+`coverage.sh` for the full source. Key design:
 
-- `coverage` uses a separate `CARGO_TARGET_DIR` — prevents instrumented
-  and non-instrumented artifacts from mixing, which causes phantom
-  uncovered lines with grcov.
+- `bin/coverage` uses a separate `CARGO_TARGET_DIR` — prevents
+  instrumented and non-instrumented artifacts from mixing, which causes
+  phantom uncovered lines with grcov.
 - 100% coverage on library code only — `--keep-only 'src/**'
   --ignore 'src/bin/**'`. Binary code is tested via integration tests
-  but not measured.
+  but not measured. Lower the gate for a project with
+  `COVERAGE_THRESHOLD=90 just coverage`.
 - `covdir` output — machine-readable JSON, parsed with `jq` for a
   clean summary. Exclusion markers (`cov-excl-line`, etc.) are
   documented in `coverage.md`.