Label format change ('!missing' actions):
- "add TrueMoozic (renamed from truemusic)" → "add TrueMoozic B42"
- "add truemusic" → "add truemusic B41"
Build context lives in the suffix; the alias-rename hint was redundant
because the user already sees the original mod_id in the warning text.
RV Interior Expansion ordering (B42):
PROJECTRVInterior42 → RVInteriorExpansion → RVInteriorExpansionPart2
map folders: map_distanciado → map_aquatsar → rvupdate → rv2 → Muldraugh, KY
Authors didn't declare loadAfter in mod.info; new modpack rules file
data/modpack_rules/rv_expansion.txt establishes the chain. Triggers fire
when either expansion wsid (3618427553 / 3622163276) is in input.
When user is on B42 and a mod requires the B41 mod_id (e.g.
truemusic_mixtape_megapack require=truemusic), the canonical wsid
(2613146550, B41-only) is rightly filtered out by the build guard —
but we were silently dropping the suggestion. Now we fall back to
MOD_ID_ALIASES: truemusic → TrueMoozic, suggest wsid 3632610172.
- _lookup_wsids_for_missing returns Dict[str, Tuple[wsid, suggested_mod_id]]
- build_warnings unpacks tuple, label clarifies rename when alias used
- adapters.build_response signature updated for new shape
- data/modpack_rules/helldrinx.txt: bundled rules for HellDrinx FULL/LITE
- app.py auto-injects modpack rules when a trigger wsid is in input;
user-supplied rules are appended after and override on conflict
- MANUAL_BUILD_PAIRS: betterLockpicking (B41) ↔ NFsBetterLockpicking (B42)
- mlos_sort.py: minor adjustments (kept in lockstep across api/worker)
- mod_files manifest table populated at parse time
- POST /api/conflicts endpoint
- mod_types fingerprinting feeds derive_category
- DD filelist regex broadened to cover conflict-eligible exts
- media/maps/<*>/* excluded from manifest (per-mod namespaced,
no conflict value, can be tens of MB per mod)
Plan: docs/plans/2026-05-04-pzmm-conflict-and-typing.md