Build1 publisher3 min readPublished
Lootr Tools refuses a drop rate that arrives without a provenance status
An unofficial Dungeon Lootr companion runs every class, item and build through a Zod schema at import, then cross-checks the whole dataset. A guide pointing at a class that was never defined fails at build.
The Engineer · Build desk

What happened
- Lootr Tools, an unofficial Dungeon Lootr companion, keeps every class, aspect, item, build, code and source in typed data modules. A helper parses each module through a Zod schema when the module is imported.
- The schema requires lowercase kebab-case slugs, verification dates in YYYY-MM-DD form, and drop rates expressed as decimals between 0 and 1.
- Pages render a verifiedAt date stored in the data file instead of calling new Date(); the Cursed King entity carries 2026-09-16 with a verified status and two cited source ids.
- Each source is a record in the dataset with an id, url, accessedAt date and a sourceType of official, community, editorial or unknown. The UI renders those confidence states separately.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision An editor who has only a community drop estimate must classify its provenance or leave the number off the page, because the parse rejects a rate with no status attached.
- capability Marking an entity noindex lets a page whose very classification two sources dispute stay useful to a reader without being offered to search engines as an authoritative answer.
- constraint The guarantee only covers facts that ship as modules in the repository; a team holding the same content in a database has to find another place to run the checks.
- exposure Because the date comes from the file and not the clock, an unmaintained page shows a stale verifiedAt date to any reader who compares it against the latest patch.
Zod's `parse` either returns the object or throws, so the helper hands the whole job over: `defineClass` is `return gameClassSchema.parse(input)` [1]. The check runs when the data module is first imported [1]. Whatever imports it first owns the failure. Editors keep normal TypeScript completion, and the schema enforces the rules TypeScript alone cannot protect at runtime [2]. With the data sitting directly in React components, the developer wrote, nobody noticed a wrong unlock requirement or an expired code until a reader pointed it out [19].
The range rule is narrower than it looks. Drop rates must be decimals from 0 to 1 [3]. Five percent entered as 5 fails, because 5 is outside the range [2]. Half a percent entered as 0.5 passes, and the page quotes it as fifty percent [2].
An object can satisfy its own schema and still point at an aspect that does not exist [9]. So the data index calls `assertValidDataset` over sources, classes, aspects, items, builds and codes before the app uses the dataset [10]. Seven checks run there: duplicate entity ids and slugs, builds referencing missing classes, classes recommending missing aspects, aspects recommending missing classes, entities citing missing sources, item rates outside 0 to 1, and item rates without a rate status [11]. Two of the seven restate rules the per-object parse already enforces. The other five only come into view once the whole dataset is in hand: the duplicate test and four kinds of dangling reference [3]. The post does not say why the rate rules are checked in both places. A build pointing at a nonexistent class, it says, "never becomes a production page" [12].
Codes are the case where the model leaves the conflict unresolved. Two dated sources can disagree about the same code on the same day, so the schema allows a status of `unknown` instead of forcing an active or expired choice [16]. On stored dates, the post says: "The content may still be wrong, but it no longer pretends to be fresh." [15]
The decision pages are held to the same rule as the data. The Build Finder scores candidate builds on class, goal, source confidence, current-update compatibility and aspect match. With no exact match it falls back to another goal at lower confidence, and with no build at all it derives a conservative plan from class basics [17]. The probability helpers are pure functions outside React. That makes edge cases easier to test, the developer wrote: zero percent drops, hundred percent drops, tiny rates across huge run counts, and unreachable confidence targets [18]. What the current patch actually does is a separate problem. The developer wrote that the approach constrains the damage when research is incomplete, and that the production site still needs regular rechecks [21].
What to watch
- Whether the validator adds a check for verifiedAt dates older than the current game update, a staleness case outside the seven checks listed.
- Whether the parse stays on the build path as pages move to on-demand rendering, which would move the failure from deploy to request time.
- Whether the Build Finder's source-confidence scoring is published as a rubric readers can check against the cited sources.