AtlatestRepositoryfolio
1
#!/usr/bin/env bash2
# M0 sabotage driver -- the FOUR SABOTAGES NAMED IN THE PLAN, not substitutes.3
#4
# S1 drop an indented task-body line5
# S2 conflate comma/tag metadata6
# S3 rewrite a duplicate heading link ambiguously7
# S4 omit one unrecognized construct from the report8
#9
# Each is shown RED SEPARATELY, with a NAMED SIGNATURE -- a non-zero exit alone is10
# equally true of a gate that never ran.11
#12
# RESTORES ARE VERIFIED BY sha256, never by `git checkout --`, which wipes13
# uncommitted work silently with exit 0.14
#15
# THESE ARE PRODUCT-LAYER SABOTAGES: they mutate `tools/m0_convert.py`, the loaded16
# product source, not the gate's assertions. A suite sabotaged only at the17
# assertion layer is consistent with a stale module, a fixture that reimplements the18
# behaviour, and a wrong load path -- all three invisible, all three seen here.19
#20
# bash test/sabotage-m0.sh21
set -uo pipefail23
repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"24
cd "$repo" || exit 126
TARGET="$repo/tools/m0_convert.py"27
BACKUP="$(mktemp)"28
cp "$TARGET" "$BACKUP"29
ORIG_SHA="$(sha256sum "$TARGET" | cut -d' ' -f1)"31
export XDG_CACHE_HOME="$(mktemp -d)"33
legs=034
red=036
restore() {37
cp "$BACKUP" "$TARGET"38
local now39
now="$(sha256sum "$TARGET" | cut -d' ' -f1)"40
if [ "$now" != "$ORIG_SHA" ]; then41
echo " RESTORE FAILED: $now != $ORIG_SHA"42
exit 143
fi44
echo " restore verified by sha256: $now"45
}47
trap 'cp "$BACKUP" "$TARGET"; rm -f "$BACKUP"' EXIT49
# run_leg <name> <description> <signature-grep> <arm> <python-patch>50
run_leg() {51
local name="$1" desc="$2" signature="$3" arm="$4" patch="$5"52
legs=$((legs + 1))53
echo54
echo "=== $name: $desc ==="55
cp "$BACKUP" "$TARGET"56
if ! guix shell python -- python3 - "$TARGET" <<EOF57
import sys, pathlib58
p = pathlib.Path(sys.argv[1]); t = p.read_text()59
$patch60
p.write_text(t)61
EOF62
then63
echo " PATCH FAILED to apply -- the sabotage could not be planted"64
restore65
return66
fi67
# The patch must actually have CHANGED the file. A sabotage that silently68
# applied nothing would produce a green run indistinguishable from a fixed bug.69
local sabotaged_sha70
sabotaged_sha="$(sha256sum "$TARGET" | cut -d' ' -f1)"71
if [ "$sabotaged_sha" = "$ORIG_SHA" ]; then72
echo " SABOTAGE DID NOT CHANGE THE FILE -- leg is vacuous"73
restore74
return75
fi77
local out status78
out="$(M0_GATE_ONLY_ARM="$arm" bash test/gate-m0.sh 2>&1)"79
status=$?80
if [ "$status" -eq 0 ]; then81
echo " NOT RED: arm $arm stayed green under $name"82
echo "$out" | tail -3 | sed 's/^/ /'83
elif printf '%s' "$out" | grep -qF "$signature"; then84
echo " RED with the named signature: $signature"85
red=$((red + 1))86
else87
echo " RED, but NOT with the expected signature '$signature'"88
echo "$out" | grep -i fail | head -3 | sed 's/^/ /'89
fi90
restore91
}93
echo "M0 sabotage: target $TARGET"94
echo " original sha256: $ORIG_SHA"96
# --------------------------------------------------------------------------97
# S1 -- DROP AN INDENTED TASK-BODY LINE.98
#99
# The body line is silently lost from the manuscript. Arm 1 (determinism) cannot100
# see this -- a converter that always drops the line is perfectly deterministic --101
# which is exactly why the accounting arm has to exist.102
# --------------------------------------------------------------------------103
run_leg "S1" "drop an indented task-body line" \104
"reached no manuscript" 4 \105
'old = """ body_lines.append({"t": "para", "line": j + 1, "lines": [j + 1],106
"text": lines[j].strip()})"""107
new = """ if "nested body line" not in lines[j] and "Continuation" not in lines[j]:108
body_lines.append({"t": "para", "line": j + 1, "lines": [j + 1],109
"text": lines[j].strip()})"""110
assert old in t, "S1 anchor not found"111
t = t.replace(old, new, 1)'113
# --------------------------------------------------------------------------114
# S2 -- CONFLATE COMMA/TAG METADATA.115
#116
# `{id: t-x, tags: alpha beta}` holds TWO separators: `,` between fields and ` `117
# WITHIN the tags field. Conflating them yields one tag "alpha beta", which fails118
# `valid-tag?` at `make-task`. The converter's own grammar filter then DROPS it119
# with a warning, so the manuscript still parses -- the accounting arm is what sees120
# it, via a tag count taken by a net that shares no code with the converter.121
# --------------------------------------------------------------------------122
run_leg "S2" "conflate comma/tag metadata" \123
"task tags emitted" 4 \124
'old = """ for tag in meta["tags"].split():"""125
new = """ for tag in [meta["tags"]]:"""126
assert old in t, "S2 anchor not found"127
t = t.replace(old, new, 1)'129
# --------------------------------------------------------------------------130
# S3 -- REWRITE A DUPLICATE HEADING LINK AMBIGUOUSLY.131
#132
# The ruling is that a migration must not rename what it can instead report. This133
# sabotage makes the converter silently disambiguate -- the behaviour that was134
# REVERSED -- so the faithful-conversion property and its warning both vanish.135
# Arm 9 pins the faithful emission, so it is the arm that must go red.136
# --------------------------------------------------------------------------137
run_leg "S3" "rewrite a duplicate heading link ambiguously" \138
"faithful 'notes' slugs" 9 \139
'old = """ if self.disambiguate_anchors:140
sid = f"{base}-{len(occurrences)}\""""141
new = """ sid = f"{base}-{len(occurrences)}\""""142
assert old in t, "S3 anchor not found"143
t = t.replace(old, new, 1)'145
# --------------------------------------------------------------------------146
# S4 -- OMIT ONE UNRECOGNIZED CONSTRUCT FROM THE REPORT.147
#148
# The default dispatch arm is what makes the denominator EXACT rather than149
# approximately right. Removing a class from the report is the failure mode where150
# "absent" reads as "fired zero times".151
#152
# NOTE: this leg is written to prove arm 5 CAN fail. Before trusting it, the153
# unsabotaged arm 5 is confirmed green above -- otherwise a leg that reddens for an154
# unrelated reason would look like coverage.155
# --------------------------------------------------------------------------156
run_leg "S4" "omit one unrecognized construct from the report" \157
"omits warning class" 5 \158
'old = """ out = {code: 0 for code in Warn.ALL}"""159
new = """ out = {code: 0 for code in Warn.ALL if code != Warn.W6}"""160
assert old in t, "S4 anchor not found"161
t = t.replace(old, new, 1)'163
echo164
echo "final sha256: $(sha256sum "$TARGET" | cut -d' ' -f1)"165
if [ "$(sha256sum "$TARGET" | cut -d' ' -f1)" != "$ORIG_SHA" ]; then166
echo "M0_SABOTAGE_RESTORE_FAILED"167
exit 1168
fi169
echo "M0_SABOTAGE legs=$legs red=$red"170
[ "$legs" -eq "$red" ] || exit 1