fix(check): make the escalation ratchet actually ratchet
CI / Check / Test (push) Successful in 1s
CI / Tag release (push) Successful in 1s

"That list only shrinks" was asserted in the commit message, the script header,
the pending file's own header, the test header, and the issue comment. It was
enforced nowhere.

A traceability probe falsified it in one run: extract the commit to a temp tree,
add a skill with no `## Escalation` section, append its name to
bin/escalation-pending.txt — gate returns 0. The list could grow silently and
forever. "Self-cleaning" was true; "only shrinks" was a social convention wearing
a mechanism's clothes, and this repo has spent a day on what those cost.

The gate now diffs the pending list against the version committed at HEAD and
fails on any added entry, naming it. Where the comparison is impossible — not a
git checkout — it says so rather than passing silently, because a ratchet that
cannot be verified must not look like one that was.

Four new cases: unchanged list, shrinking list, growing list, and the
unverifiable case.

Refs #37
Bump-Type: patch
This commit is contained in:
2026-08-29 15:26:24 +02:00
parent 39ac61c411
commit 40405cd95a
2 changed files with 66 additions and 0 deletions
+24
View File
@@ -61,4 +61,28 @@ if [ -f "$PENDING" ]; then
done
fi
# --- the ratchet must actually ratchet ---------------------------------------
# "This list only shrinks" was asserted in five places and enforced in none. A
# traceability probe falsified it in one run: add an unsectioned skill, append it
# to the list, gate returns 0. So compare against the list as committed at HEAD —
# a convention nothing checks is a convention, and this repo has spent a day on
# what those cost.
if [ -f "$PENDING" ]; then
if ! git -C "$ROOT" rev-parse --git-dir >/dev/null 2>&1; then
echo "WARN: $ROOT is not a git checkout — cannot verify the pending list only shrank" >&2
elif ! git -C "$ROOT" cat-file -e HEAD:bin/escalation-pending.txt 2>/dev/null; then
: # first commit introducing the file — nothing to compare against yet
else
before=$(git -C "$ROOT" show HEAD:bin/escalation-pending.txt 2>/dev/null \
| sed 's/#.*//' | tr -d '[:blank:]' | grep -v '^$' | sort -u || true)
now=$(sed 's/#.*//' "$PENDING" | tr -d '[:blank:]' | grep -v '^$' | sort -u || true)
added=$(printf '%s\n' "$now" | grep -vxF "$before" 2>/dev/null || true)
for entry in $added; do
[ -n "$entry" ] || continue
echo "ERROR: '$entry' was added to bin/escalation-pending.txt — the list only shrinks. Write its '## Escalation' section instead of exempting it." >&2
fail=1
done
fi
fi
exit $fail