Every deviation names its reason, and a gate keeps it so (#2868)
`docs/METHOD.md` asks each deviation to name the reference fact and why benilla differs. About 130 `Deviation:` comments gave no reason, and the Warden comment lost its reason when the refusal became a read-past. - `scripts/genmap.sh --check`, now a gate, fails a `Deviation:` comment without "because" and a `Deviates` row without a Why. - `scripts/deviation-why.allow` lists the 43 comments whose change recorded no reason. It only shrinks: an entry that gains its reason, or is new against main's copy, fails. - About 190 comments that gave their reason in other words now say "because", with their meaning kept. - Nine reasons, the Warden one among them, come back from the commits and pull requests that made them. Tested by the gate on the full tree, and by its failure on a comment with no reason and on a stale entry. Fixes #2856 Fixes #2857 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# Deviations still waiting for their reason (scripts/genmap.sh, "the reason check").
|
||||
# One `path<TAB>first twelve words of the comment` per line. This list only shrinks: when a comment
|
||||
# gains "because ...", delete its line here; never add one. A comment off the list must carry its reason.
|
||||
crates/benilla-app/assets/ui/KeyBindingsPage.xml every bind, unbind and reset saves at once, as the era panel
|
||||
crates/benilla-app/src/bindings/layout.rs on the Mac and Linux a key its platform reports no character
|
||||
crates/benilla-app/src/cvars/listing.rs the reference's list runs in creation order, the records `Config.wtf` created first
|
||||
crates/benilla-app/src/game_tip.rs the reference rasterizes the tip's font at most 32 px (`0x5ca096`/`0x5ca09b` in
|
||||
crates/benilla-app/src/go_templates.rs one per entry, where the reference keeps one per call, as the
|
||||
crates/benilla-app/src/loading_screen/input.rs the world pick is cleared, where the reference's freezes: the screen's layer
|
||||
crates/benilla-app/src/player/approach.rs the reference re-projects the point's height onto the first walkable facet a
|
||||
crates/benilla-app/src/portrait/mod.rs the reference widget draws only its model; a body pane clears to
|
||||
crates/benilla-app/src/sound/mod.rs with no character the reference leaves the listener where it was; benilla
|
||||
crates/benilla-app/src/target/click.rs the reference's arm reads the last object's stop (`0x611336` before `0x610383` stores
|
||||
crates/benilla-app/src/target/click.rs it runs on the first frame with none, so an arrival that
|
||||
crates/benilla-app/src/ui_duel.rs the challenger's name comes from the [`NameCache`], so a name still being
|
||||
crates/benilla-app/src/ui_guild/sort.rs the reference ties a miss with a hit (`0x4d0e27`, `0x4d0ea1`), no total
|
||||
crates/benilla-app/src/ui_loot/mod.rs `autoLootDefault` is benilla's own option, 1.12 auto-looting only on a shift-click, and
|
||||
crates/benilla-app/src/ui_quest/verbs.rs the row one past a full pool is read from the cells
|
||||
crates/benilla-app/src/ui_social/live.rs after a full 50 the reference reads the 160 bytes past the
|
||||
crates/benilla-app/src/ui_text/outline.rs `r × floor(dpi)` passes as in [`outlined_cell`], the first `floor(dpi)` of them
|
||||
crates/benilla-app/src/ui_tooltip/spell_view.rs a pet view with no pet unit costs 0, an empty cell,
|
||||
crates/benilla-assets/src/shaders/liquid.wgsl this is the `specular`/`pixelShaders` = 1 leg the reference install runs; both
|
||||
crates/benilla-formats/src/macro_icons.rs of names equal but for case, benilla keeps the first in source
|
||||
crates/benilla-lua/src/heap.rs C Lua has no such bound; past it the string is `not
|
||||
crates/benilla-lua/src/heap.rs the client's `luaM_realloc` (`0x6fc980`) has no failure path to ask from; here
|
||||
crates/benilla-lua/src/lib_string.rs 5.1's `match` recurses without a bound and overflows the C stack (near
|
||||
crates/benilla-lua/src/pools.rs that sequence is not wholly the client's. A table's node vector is
|
||||
crates/benilla-lua/src/vm.rs these are benilla's own needs, past what the client's checks ([`Vm::checkstack`]) reserve:
|
||||
crates/benilla-lua/src/vm.rs benilla's scratch registers above a frame's `c_maxstack` (`Proto::maxstack`) stay on the stack;
|
||||
crates/benilla-protocol/src/world/session.rs checked per queue packet where the reference checks each tick, so a
|
||||
crates/benilla-ui/src/civil.rs the zone database's offset for the date, where the reference's CRT applies
|
||||
crates/benilla-ui/src/loader/mod.rs a `<Script file=>` value with a separator joins the including document's directory,
|
||||
crates/benilla-ui/src/script/binding_abi.rs `pow(10, n)` is the platform's, where the reference's (`0x73f940`, `fyl2x`) rounds its
|
||||
crates/benilla-ui/src/script/button.rs the disabled-checked leg (`0x785380`) tests and reloads the checked slot (`[+0x4e0]`, `0x785388`/`0x7853a5`),
|
||||
crates/benilla-ui/src/script/chat_window.rs one past `i32::MAX` clamps where the reference keeps the low 32 bits,
|
||||
crates/benilla-ui/src/script/glue_create.rs past [`NAME_TRIES`] names the answer is nil, where the reference keeps trying,
|
||||
crates/benilla-ui/src/script/nameplate.rs the reference's free list is FIFO (`0x6087f0`, `0x608a5a`); either keeps every index
|
||||
crates/benilla-ui/src/script/quest_log.rs one signal per missed record, where the reference runs every callback of
|
||||
crates/benilla-ui/src/script/reputation.rs the reference bounds the index with `jbe` against the entry count (`0xb73760`),
|
||||
crates/benilla-ui/src/script/slash.rs skipped as no command. The stock walk calls an entry only when
|
||||
crates/benilla-ui/src/script/sound.rs the app plays the queued file after the call, so the refusals
|
||||
crates/benilla-ui/src/script/stdlib.rs a format that ends in `%` reads past its terminator in the
|
||||
crates/benilla-ui/src/script/stdlib.rs with `#` and one byte of room, `_store_number` writes no digit and
|
||||
crates/benilla-ui/src/script/tooltip/size.rs `0x530a52` lays it out at once; getters resolve first, so only OnSizeChanged
|
||||
crates/benilla-ui/src/script/worldmap.rs the engine's own push (the world-enter sync) queues `WORLD_MAP_UPDATE` for the next
|
||||
crates/benilla-world/src/collision/one_sided.rs the reference keeps the later record in file order on an exact
|
||||
@@ -110,6 +110,8 @@ run() {
|
||||
}
|
||||
|
||||
run fmt cargo fmt --all -- --check
|
||||
# deviation-why: every `Deviation:` comment and `Deviates` row names its reason (`scripts/genmap.sh --check`).
|
||||
run deviation-why scripts/genmap.sh --check
|
||||
run clippy cargo clippy --workspace --all-targets -- -D warnings
|
||||
|
||||
# test: where the install resolves and the `wow-addons-vanilla` corpus link exists,
|
||||
|
||||
+52
-7
@@ -9,6 +9,10 @@
|
||||
set -u
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# `--check` runs every stage but writes the docs into a scratch folder, for the gates.
|
||||
out=docs
|
||||
if [ "${1:-}" = "--check" ]; then out=$(mktemp -d "${TMPDIR:-/tmp}/genmap-out.XXXXXX"); trap 'rm -rf "$out"' EXIT; fi
|
||||
|
||||
# Extract a quoted `key = "value"` field from a Cargo.toml.
|
||||
field() { grep -m1 "^$2" "$1" 2>/dev/null | sed -E "s/^$2[[:space:]]*=[[:space:]]*\"//; s/\".*$//"; }
|
||||
|
||||
@@ -122,16 +126,16 @@ field() { grep -m1 "^$2" "$1" 2>/dev/null | sed -E "s/^$2[[:space:]]*=[[:space:]
|
||||
done
|
||||
echo
|
||||
|
||||
} > docs/MAP.md
|
||||
} > "$out/MAP.md"
|
||||
|
||||
echo "genmap: wrote docs/MAP.md"
|
||||
echo "genmap: wrote $out/MAP.md"
|
||||
|
||||
# ── docs/DEVIATIONS.md ─────────────────────────────────────────────────────────────────────────────
|
||||
# Every place benilla writes down that it differs from 1.12.1: the `Deviates` rows of the CVar table,
|
||||
# and each `Deviation:` comment in the crates (Rust and WGSL `//`, Lua `--`, XML `<!-- -->`). A
|
||||
# deviation's wording is read from where it lives, so the list cannot drift from the code.
|
||||
tmp=$(mktemp -d "${TMPDIR:-/tmp}/genmap.XXXXXX") || exit 1
|
||||
trap 'rm -rf "$tmp"' EXIT
|
||||
trap 'rm -rf "$tmp"; [ "$out" = docs ] || rm -rf "$out"' EXIT
|
||||
|
||||
# Test code is left out: a file under `tests/`, named `tests.rs` or `*_tests.rs`, or declared by a
|
||||
# `#[cfg(test)] mod x;` (listed here, resolved the way rustc does, `#[path]` included), and a
|
||||
@@ -210,7 +214,7 @@ LC_ALL=C grep -rIlE 'Deviation:' crates 2>/dev/null | LC_ALL=C sort | grep -vE '
|
||||
if (r ~ /^assets\//) { rest = substr(r, 8); m = "assets"; if (index(rest, "/")) { sub(/\/.*/, "", rest); m = m "/" rest } return m }
|
||||
sub(/\/.*/, "", r); return r
|
||||
}
|
||||
function process(f, n, i, j, s, t, ext, xin, skipping, tindent, pend, p, q, c, kind, code, marker, text, lab, cr, rel, first, cont) {
|
||||
function process(f, n, i, j, s, t, ext, xin, skipping, tindent, pend, p, q, c, kind, code, marker, text, lab, cr, rel, first, cont, full) {
|
||||
n = 0; delete L; delete K; delete C; delete Q; delete XI; delete T
|
||||
while ((getline s < f) > 0) L[++n] = s
|
||||
close(f)
|
||||
@@ -270,6 +274,7 @@ LC_ALL=C grep -rIlE 'Deviation:' crates 2>/dev/null | LC_ALL=C sort | grep -vE '
|
||||
if (cont == "" || cont ~ /^([-*] |```)/ || label_at(cont)) break
|
||||
text = text " " cont
|
||||
}
|
||||
full = trim(text); gsub(/\t/, " ", full)
|
||||
text = trim(lead(trim(text)))
|
||||
gsub(/\t/, " ", text)
|
||||
# A rustdoc link [`x`] or [`x`](path) reads as `x`.
|
||||
@@ -280,7 +285,7 @@ LC_ALL=C grep -rIlE 'Deviation:' crates 2>/dev/null | LC_ALL=C sort | grep -vE '
|
||||
sub(/[.;,:]+$/, "", text)
|
||||
if (text == "") text = "(see the comment)"
|
||||
if (T[i] || skipfile) { skipped++; continue }
|
||||
printf "%s\t%s\t%s\t%d\t%s\n", cr, module_of(rel), rel, i, text
|
||||
printf "%s\t%s\t%s\t%d\t%s\t%s\n", cr, module_of(rel), rel, i, text, full
|
||||
}
|
||||
}
|
||||
BEGIN {
|
||||
@@ -422,6 +427,46 @@ MDESC='
|
||||
$2 != module { if (module != "") print ""; module = $2; printf "#### %s\n\n", module }
|
||||
{ printf "- %s. ([`%s:%d`](../crates/%s/%s#L%d))\n", mdesc($5), $3, $4, $1, $3, $4 }
|
||||
END { print "" }' "$tmp/code.sorted"
|
||||
} > docs/DEVIATIONS.md
|
||||
} > "$out/DEVIATIONS.md"
|
||||
|
||||
echo "genmap: wrote docs/DEVIATIONS.md ($(wc -l < "$tmp/code.sorted" | tr -d ' ') code entries, $(wc -l < "$tmp/cvars.tsv" | tr -d ' ') CVar rows)"
|
||||
echo "genmap: wrote $out/DEVIATIONS.md ($(wc -l < "$tmp/code.sorted" | tr -d ' ') code entries, $(wc -l < "$tmp/cvars.tsv" | tr -d ' ') CVar rows)"
|
||||
|
||||
# ── the reason check ───────────────────────────────────────────────────────────────────────────────
|
||||
# `docs/METHOD.md`: a deviation is written down with the reference fact and why we differ. So a
|
||||
# `Deviation:` comment carries "because …" somewhere in its paragraph, and a CVar `Deviates` row
|
||||
# carries a Why. `scripts/deviation-why.allow` is the ratchet for the comments still waiting for a
|
||||
# reason: one `path<TAB>first twelve words` per line. It only shrinks. An entry that gained its reason,
|
||||
# or whose comment is gone or reworded, fails until it is removed; a comment off the list without
|
||||
# "because" fails; and an entry new against origin/main's copy of the list fails.
|
||||
allow=scripts/deviation-why.allow
|
||||
haveold=0
|
||||
if git cat-file -e "origin/main:$allow" 2>/dev/null; then
|
||||
haveold=1
|
||||
git show "origin/main:$allow" | grep -vE '^(#|$)' > "$tmp/allow.old" || true
|
||||
else
|
||||
: > "$tmp/allow.old"
|
||||
fi
|
||||
grep -vE '^(#|$)' "$allow" 2>/dev/null > "$tmp/allow.now" || : > "$tmp/allow.now"
|
||||
LC_ALL=C awk -F '\t' -v haveold="$haveold" -v oldf="$tmp/allow.old" -v nowf="$tmp/allow.now" '
|
||||
function key(t, n, w, i, k) { n = split(t, w, " "); k = ""; for (i = 1; i <= n && i <= 12; i++) k = k (i > 1 ? " " : "") w[i]; return k }
|
||||
BEGIN {
|
||||
while ((getline l < oldf) > 0) old[l] = 1
|
||||
close(oldf)
|
||||
FS = "\t"
|
||||
while ((getline l < nowf) > 0) { listed[l] = 1; order[++nl] = l; if (haveold == 1 && !(l in old)) { print "deviation-why: new on the allowlist, which only shrinks: " l; bad++ } }
|
||||
}
|
||||
FILENAME ~ /cvars\.tsv$/ { if ($4 == "") { print "deviation-why: the `" $1 "` Deviates row has no Why"; bad++ } next }
|
||||
{
|
||||
path = "crates/" $1 "/" $3; k = path "\t" key($6)
|
||||
seen[k] = 1
|
||||
if (tolower($6) ~ /(^|[^a-z])because([^a-z]|$)/) {
|
||||
if (k in listed) { print "deviation-why: " path ":" $4 " has its reason now; remove it from the allowlist"; bad++ }
|
||||
} else {
|
||||
if (++dup[k] > 1) { print "deviation-why: " path ":" $4 " shares its first twelve words with another comment without a reason; reword one"; bad++ }
|
||||
if (!(k in listed)) { print "deviation-why: " path ":" $4 " gives no reason; add \"because …\" (" substr($6, 1, 60) "…)"; bad++ }
|
||||
}
|
||||
}
|
||||
END {
|
||||
for (i = 1; i <= nl; i++) if (!(order[i] in seen)) { print "deviation-why: stale allowlist entry, remove it: " order[i]; bad++ }
|
||||
if (bad) { print "deviation-why: " bad " problem(s); docs/METHOD.md: a deviation names why we differ" > "/dev/stderr"; exit 1 }
|
||||
}' "$tmp/cvars.tsv" "$tmp/code.sorted" >&2 || exit 1
|
||||
|
||||
Reference in New Issue
Block a user