AtlatestRepositoryfolio
1
#!/usr/bin/env bash2
#3
# Every module in `src/folio/doc/` is either sabotaged by a leg in one of this4
# repository's sabotage drivers, or named here with a reason.5
#6
# WHY THIS IS A STANDING CHECK AND NOT A ONE-TIME SWEEP.7
#8
# Sabotage targets in this family were picked by looking at the files that9
# already had legs. That is a search scoped to the previous change, and it10
# cannot find the class: every leg got added next to another leg, so11
# `traversal.sgl`, `render/plain.sgl` and `render/debug.sgl` went from F1a's12
# first commit to 2026-08-22 with no leg in ANY driver -- while `gate-f1a.sh`13
# arm 8 READ `traversal.sgl` to derive its field-totality check. A review14
# planted a real defect there (`update-id` replaced by a constant `#f`) and the15
# gate returned green from a broken product.16
#17
# A sweep fixes those three files. This is for the eleventh module.18
#19
# WHAT THIS CHECK CAN AND CANNOT SHOW -- stated because a coverage banner is20
# itself an assertion, and an overstated one stops the next person looking.21
#22
# IT CAN show that no module is unattended: that some driver MUTATES the file,23
# so somebody has pointed a leg at it.24
#25
# IT CANNOT show that the leg is any good. Whether a leg actually reddens the26
# gate is proved by the drivers themselves -- each `expect_red` demands a27
# named failure signature and a non-zero exit, and a driver whose legs did not28
# all go red exits non-zero. This check is the denominator; those are the29
# numerator. Run both.30
#31
# NAMED `gate-` AND NOT `sabotage-`, AND THAT IS LOAD-BEARING. The first version32
# was `test/sabotage-coverage.sh`, so the driver glob below matched IT, and the33
# control module named in its own executable code read as covered. Its own34
# control caught that on the first run. A checker that is a member of the35
# population it enumerates will find itself.36
set -euo pipefail38
repo_dir="$(cd "$(dirname "$0")/.." && pwd)"39
cd "$repo_dir"41
command -v find >/dev/null || { echo "find is required by this check" >&2; exit 1; }42
command -v awk >/dev/null || { echo "awk is required by this check" >&2; exit 1; }44
work=$(mktemp -d)45
trap 'rm -rf "$work"' EXIT INT TERM47
drivers=$(find test -maxdepth 1 -name 'sabotage-*.sh' | sort)48
driver_count=$(printf '%s\n' "$drivers" | grep -c . || true)49
test "$driver_count" -ge 1 || { echo "found no sabotage drivers; the derivation is broken" >&2; exit 1; }51
# A MENTION IS NOT A LEG, AND THIS IS THE DEFECT THAT NEARLY SHIPPED IN THIS52
# FILE. The first version counted any non-comment mention. Every driver keeps a53
# `targets=()` backup array naming the files it will RESTORE, so a module listed54
# there only -- backed up, never sabotaged -- read as covered. An adversarial55
# review deleted legs S8, S9 and S10 outright, leaving just the array entries,56
# and this gate still reported 10/10 GREEN. It could not fail for the one thing57
# it exists to detect.58
#59
# It now requires a POSITIVE DISCRIMINATOR: the path must appear on a logical60
# line that also invokes something which MUTATES a file. An allowlist, not a61
# denylist -- a driver that grows a new mutation helper reads as UNCOVERED and62
# says so loudly, which is the safe direction and a one-line fix. `save_file`63
# and the array literals are deliberately absent: backing a file up is not64
# sabotaging it.65
mutators='sed -i|replace_once|patch_file|python3|>>'67
# code_of <file> -- non-comment lines, with `\`-continuations JOINED into one68
# logical line. The joining is load-bearing: the drivers write69
#70
# sed -i 's/.../.../' \71
# src/folio/doc/render/plain.sgl72
#73
# so the verb and the path land on different physical lines, and a line-local74
# test would miss every continued leg.75
#76
# Comment lines are dropped, and only comment lines: driver prose names these77
# files constantly -- including prose confessing that a file is UNCOVERED -- and78
# a check satisfied by a comment confirms coverage by reading the confession.79
code_of() {80
awk '81
/^[[:space:]]*#/ { next }82
{ line = $083
while (line ~ /\\$/ && (getline nxt) > 0) {84
sub(/\\$/, "", line)85
sub(/^[[:space:]]+/, " ", nxt)86
line = line nxt87
}88
print line }89
' "$1"90
}92
# regex_escape <literal> -- the ERE metacharacters only. `/` is NOT one, and93
# escaping it earns a `stray \ before /` warning on every single grep, which is94
# exactly the kind of noise a reader learns to scroll past.95
regex_escape() { printf '%s' "$1" | sed 's/[.[\*^$+?(){}|]/\\&/g'; }97
# covered_by <module> -- one line per MUTATING mention, prefixed with the driver.98
# The path is bounded on both sides, so `model.sgl` cannot be satisfied by99
# `model.sgl.bak` nor by a longer path that merely contains it.100
covered_by() {101
local module="$1" pat d102
pat="(^|[^A-Za-z0-9_./-])$(regex_escape "$module")([^A-Za-z0-9_.-]|\$)"103
for d in $drivers; do104
code_of "$d" | grep -E -- "$mutators" | grep -E -- "$pat" | sed "s|^|$d: |" || true105
done106
}108
# --------------------------------------------------------------- EXEMPTIONS109
# A module may be left without a leg, but the reason has to be written down and110
# it has to be an ARGUMENT. "Nobody got to it" is not one, and neither is "the111
# file is small" -- `render/debug.sgl` is eleven lines and has leg S10, because112
# what it guards against is somebody growing the alias into a second113
# serialization.114
#115
# THIS LIST IS EMPTY, AND THAT IS THE RESULT. All ten modules under116
# `src/folio/doc/` can carry a leg and now do. The mechanism is kept so a reader117
# can see the exemption path exists and was not needed.118
exempt_modules=()119
exempt_reasons=()121
is_exempt() {122
local m="$1" i=0123
while [ "$i" -lt "${#exempt_modules[@]}" ]; do124
if [ "${exempt_modules[$i]}" = "$m" ]; then printf '%s' "${exempt_reasons[$i]}"; return 0; fi125
i=$((i + 1))126
done127
return 1128
}130
# --------------------------------------------------------------- CONTROLS131
# Planted into a throwaway directory; the working tree is never touched, so no132
# restore step has to be reached. Five directions, and the third is the one that133
# would have caught the defect this file shipped with.134
ctl="$work/controls"135
mkdir -p "$ctl"137
ctl_module=src/folio/doc/control-module.sgl138
{ printf 'sed -i %ss/a/b/%s %s\n' "'" "'" "$ctl_module"; } >"$ctl/sabotage-ctl-mutated.sh"139
{ printf 'targets=(\n %s\n)\n' "$ctl_module"140
printf 'save_file %s\n' "$ctl_module"; } >"$ctl/sabotage-ctl-array-only.sh"141
{ printf '# %s is not covered by anything and this comment says so\n' "$ctl_module"; } >"$ctl/sabotage-ctl-comment-only.sh"142
{ printf 'frobnicate %s\n' "$ctl_module"; } >"$ctl/sabotage-ctl-unknown-verb.sh"143
{ printf 'sed -i %ss/a/b/%s \\\n %s\n' "'" "'" "$ctl_module"; } >"$ctl/sabotage-ctl-continued.sh"145
ctl_hits() { # ctl_hits <fixture-basename>146
local saved="$drivers" out147
drivers="$ctl/$1"148
out=$(covered_by "$ctl_module")149
drivers="$saved"150
printf '%s' "$out" | grep -c . || true151
}153
test "$(ctl_hits sabotage-ctl-mutated.sh)" -ge 1 || {154
echo "CONTROL FAILED: a plain 'sed -i <path>' mutation was not seen as coverage" >&2; exit 1; }155
test "$(ctl_hits sabotage-ctl-continued.sh)" -ge 1 || {156
echo "CONTROL FAILED: a backslash-continued mutation was not seen; the line joiner is broken" >&2; exit 1; }157
test "$(ctl_hits sabotage-ctl-array-only.sh)" = "0" || {158
echo "CONTROL FAILED: a module named ONLY in a targets=() backup array read as covered." >&2159
echo " That is precisely the false green this check was rewritten to close." >&2; exit 1; }160
test "$(ctl_hits sabotage-ctl-comment-only.sh)" = "0" || {161
echo "CONTROL FAILED: a module named only in a comment read as covered" >&2; exit 1; }162
test "$(ctl_hits sabotage-ctl-unknown-verb.sh)" = "0" || {163
echo "CONTROL FAILED: an unrecognised verb read as coverage; the allowlist is not an allowlist" >&2; exit 1; }165
# ...and against the REAL drivers: a module that exists nowhere must read166
# uncovered, and one with real legs must read covered.167
test -z "$(covered_by src/folio/doc/this-module-cannot-exist.sgl)" || {168
echo "CONTROL FAILED: a module that exists nowhere was reported as covered" >&2; exit 1; }169
test -n "$(covered_by src/folio/doc/model.sgl)" || {170
echo "CONTROL FAILED: the detector cannot see the coverage of src/folio/doc/model.sgl," >&2171
echo " which legs S1-S4 and S6 of test/sabotage-f1a.sh sabotage. An empty uncovered" >&2172
echo " list would therefore mean nothing." >&2; exit 1; }173
echo "detector armed: mutation seen (plain and continued), backup array / comment / unknown verb all refused, model.sgl covered, an absent module uncovered"175
# --------------------------------------------------------------- POPULATION176
# ENUMERATED FROM THE SOURCE TREE. Not from the drivers, not from a list.177
modules=$(find src/folio/doc -type f -name '*.sgl' | sort)178
module_count=$(printf '%s\n' "$modules" | grep -c . || true)179
test "$module_count" -ge 1 || { echo "found no modules under src/folio/doc" >&2; exit 1; }181
# The bound this check does NOT claim, stated so the green cannot be read wider182
# than it is.183
all_sgl=$(find src -type f -name '*.sgl' | wc -l)184
echo "population: $module_count module(s) under src/folio/doc, checked against $driver_count sabotage driver(s)"185
echo "out of scope: $((all_sgl - module_count)) further .sgl file(s) under src/ that this check does not cover"187
uncovered=""188
for m in $modules; do189
hits=$(covered_by "$m")190
if [ -n "$hits" ]; then191
printf ' covered %-38s %s\n' "$m" "$(printf '%s\n' "$hits" | cut -d: -f1 | sort -u | tr '\n' ' ')"192
elif reason=$(is_exempt "$m"); then193
printf ' exempt %-38s %s\n' "$m" "$reason"194
else195
printf ' UNCOVERED %-38s no sabotage driver mutates it\n' "$m"196
uncovered="$uncovered $m"197
fi198
done200
if [ -n "$uncovered" ]; then201
echo >&2202
echo "MODULE(S) WITH NO SABOTAGE LEG:$uncovered" >&2203
echo >&2204
echo "VERIFY THIS CHECK'S OWN PRECONDITIONS BEFORE ACTING ON THIS MESSAGE:" >&2205
echo " - the controls above passed, so the detector sees coverage where it exists;" >&2206
echo " - the module is MUTATED by a driver, not merely listed in its backup array" >&2207
echo " and not merely named in a comment;" >&2208
echo " - the mutation uses one of the recognised verbs ($mutators). A new helper" >&2209
echo " is the likeliest cause of a surprising result here, and adding it to" >&2210
echo " \$mutators is the fix;" >&2211
echo " - the path spelling matches the one the driver uses." >&2212
echo "Then: add a leg that plants the defect the module's own gate arm exists to" >&2213
echo "catch and prove it reddens, or add the module to exempt_modules WITH AN" >&2214
echo "ARGUMENT for why it cannot carry one." >&2215
exit 1216
fi218
echo "F1A_SABOTAGE_COVERAGE_GREEN $module_count/$module_count module(s) attended, ${#exempt_modules[@]} exempt"