gates: clippy clean again, and the mail repro that never was for commit

`scripts/check.sh` has been failing before it ran a single test, on 62 clippy
findings in code none of the recent rounds touched — 32 in `benilla-world`, 30 in
`benilla-app` — so the in-loop gate could not tell anyone anything. Every one is a
newer clippy noticing older code (`as_chunks` and `is_multiple_of` are both lints
younger than the lines they fire on), and they are fixed, not silenced, except
where the silence is the point:

- **Argued allows, the house idiom.** `clippy::type_complexity` on ten Bevy system
  signatures and two inline caster queries, each with the one-line reason beside
  it, as the other 67 in this workspace have. `clippy::approx_constant` on
  `liquid/waves.rs`'s `TWO_PI`, whose own doc already says why it is spelled
  `6.2831853`: it mirrors a shader line, and `f32::consts::TAU` would stop reading
  against the file it copies.
- **Two runtime assertions became compile-time ones** (`EXT_SEL_EMPTY is index 255`,
  the endless-stream frame count): a claim about constants should fail the build
  that breaks it, not a test run after it.
- **Three real, if harmless, redundancies gone**: `len > MAX_TILT_TAN && len > 0.0`
  (the constant is `tan(4°)`, so the second test never spoke), `u32::from(..) + 1 |
  flags` now parenthesised where `+` binds tighter than `|`, and `palette.get(0)`.
- The rest is mechanical: `chunks_exact(3)` → `as_chunks::<3>()`, `map_or(true, ..)`
  → `is_none_or`, two `Default::default()`-then-assign test fixtures, a doc line
  that began `1.` and so turned its own paragraph into a list item, and
  `local_utc_offset_secs` moved above the test module it had drifted below.

`tests/repro_mail.rs` is deleted. Its first line says "TEMPORARY repro harness —
not for commit"; it reads a loose mirror of FrameXML out of `/tmp/opencode/
vanilla-ui`, which no longer exists, so it has been failing on a missing directory
rather than telling anyone about mail.

What is left of the gate's red is one test: `world_api_wall` counts 174 crossings
of the `benilla-world` boundary against a ceiling of 107 — 90 of them already
filed with a close plan, 84 unsorted. That is not cleanup; the wall's own message
says the remedies are to close the crossing or to publish it as engine API with the
record that says so, and 84 of those is a design pass on what the engine's public
surface is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
2026-10-18 15:18:10 -06:00
co-authored by Claude Opus 5
parent 3e616e5cb3
commit 56fb28fa2b
33 changed files with 202 additions and 305 deletions
-137
View File
@@ -1,137 +0,0 @@
//! TEMPORARY repro harness — not for commit. Loads the stock MailFrame chain from loose mirror
//! files under /tmp/opencode/vanilla-ui and drives the open-letter path, printing Lua errors.
use benilla_ui::script::{MailInboxRow, MailState, UiScript};
const UI: &str = "/tmp/opencode/vanilla-ui";
const FILES: &[&str] = &[
"Interface\\FrameXML\\Fonts.xml",
r"Interface\FrameXML\MoneyFrame.lua",
r"Interface\FrameXML\MoneyFrame.xml",
r"Interface\FrameXML\MoneyInputFrame.lua",
r"Interface\FrameXML\MoneyInputFrame.xml",
"Interface\\FrameXML\\GlobalStrings.lua",
r"Interface\FrameXML\UIParent.xml",
"ScrollTemplates.xml",
r"Interface\FrameXML\UIPanelTemplates.lua",
r"Interface\FrameXML\UIPanelTemplates.xml",
"Interface\\FrameXML\\BasicControls.xml",
"Interface\\FrameXML\\LocaleProperties.lua",
"Interface\\FrameXML\\StaticPopup.xml",
"Interface\\FrameXML\\GameTooltip.xml",
r"Interface\FrameXML\ItemButtonTemplate.xml",
"Interface\\FrameXML\\UIDropDownMenu.xml",
"Interface\\FrameXML\\CharacterFrameTemplates.xml",
"Interface\\FrameXML\\FriendsFrame.xml",
"Interface\\FrameXML\\MailFrame.xml",
];
fn load_ui(script: &UiScript) {
let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("assets/ui");
let read = |req: &str| -> Option<Vec<u8>> {
let p = std::path::Path::new(UI).join(req.replace('\\', "/"));
if let Ok(b) = std::fs::read(&p) {
return Some(b);
}
std::fs::read(dir.join(req)).ok()
};
for entry in FILES {
let bytes = read(entry).unwrap_or_else(|| panic!("{entry}: not found"));
if entry.to_ascii_lowercase().ends_with(".lua") {
script
.run_chunk_named(&bytes, &format!("@{entry}"))
.unwrap_or_else(|e| panic!("{entry}: {e}"));
continue;
}
let doc = benilla_ui::framexml::parse(&benilla_ui::source::decode(&bytes))
.unwrap_or_else(|e| panic!("{entry}: {e}"));
let report = benilla_ui::loader::load_in(script, &doc, &entry.replace('\\', "/"), &read);
assert!(
report.errors.is_empty(),
"{entry}: loader errors: {:#?}",
report.errors
);
let leaf = entry.rsplit(['\\', '/']).next().unwrap_or("");
if leaf.eq_ignore_ascii_case("UIParent.xml") && entry.contains('\\') {
script
.run(
"FCF_OnUpdate = FCF_OnUpdate or function() end \
FCF_DockUpdate = FCF_DockUpdate or function() end \
UnitPopup_OnUpdate = UnitPopup_OnUpdate or function() end \
BattlefieldFrame_OnUpdate = BattlefieldFrame_OnUpdate or function() end \
MultiActionBar_Update = MultiActionBar_Update or function() end \
RaidOptionsFrame_UpdatePartyFrames = RaidOptionsFrame_UpdatePartyFrames or function() end \
LocalizeFrames = LocalizeFrames or function() end \
updateContainerFrameAnchors = updateContainerFrameAnchors or function() end \
UpdateNameplates = UpdateNameplates or function() end \
CloseAllBags = CloseAllBags or function() end \
OpenBackpack = OpenBackpack or function() end \
CloseBackpack = CloseBackpack or function() end \
NUM_CONTAINER_FRAMES = NUM_CONTAINER_FRAMES or 0 \
CONTAINER_OFFSET_X = CONTAINER_OFFSET_X or 0 \
CONTAINER_OFFSET_Y = CONTAINER_OFFSET_Y or 70 \
BATTLEFIELD_TAB_OFFSET_Y = BATTLEFIELD_TAB_OFFSET_Y or 210 \
PETACTIONBAR_YPOS = PETACTIONBAR_YPOS or 98 \
PETACTIONBAR_XPOS = PETACTIONBAR_XPOS or 36",
)
.unwrap();
}
}
}
fn row(sender: Option<&str>, subject: &str, money: u32, cod: u32) -> MailInboxRow {
MailInboxRow {
package_icon: None,
stationery_icon: Some("Interface\\Icons\\INV_Misc_Note_01".to_string()),
sender: sender.map(str::to_string),
subject: subject.to_string(),
money,
cod,
days_left: 29.0,
item_count: 0,
was_read: false,
was_returned: false,
text_created: false,
can_reply: sender.is_some(),
is_gm: false,
body: Some("body".into()),
stationery_texture: "STATIONERYTEST".into(),
is_invoice: false,
invoice: None,
has_body: true,
item_id: 0,
item_name: None,
item_texture: None,
item_quality: None,
can_delete: false,
item_random_property_id: 0,
}
}
#[test]
fn repro() {
// A stale openMailID: open row 1, then the inbox re-lists smaller (row purged), keeping the
// letter open; MAIL_INBOX_UPDATE re-runs InboxFrame_Update + OpenMail_Update on the stale id.
let mut s = UiScript::new().unwrap();
load_ui(&s);
s.set_mail(Some(MailState {
inbox: vec![
row(None, "Welcome", 0, 0),
row(Some("Jaina"), "second", 0, 0),
],
}));
s.fire_event("MAIL_SHOW", vec![]);
s.fire_event("MAIL_INBOX_UPDATE", vec![]);
let _ = s.take_errors();
s.run("MailItem1Button:Click()").unwrap();
let errs = s.take_errors();
eprintln!("errors after click: {errs:#?}");
// The letter stays open while the list empties out (rows purged) — row 1 is now out of range.
s.set_mail(Some(MailState { inbox: vec![] }));
s.fire_event("MAIL_INBOX_UPDATE", vec![]);
let errs = s.take_errors();
eprintln!("errors after empty re-list: {errs:#?}");
assert!(true);
}