fix: missing-dep [add] button respects MOD_ID_ALIASES on build mismatch

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
This commit is contained in:
2026-05-04 16:08:43 +00:00
parent 75728e6b28
commit 591f4608d4
2 changed files with 72 additions and 28 deletions

View File

@@ -228,20 +228,29 @@ async def _lookup_wsids_for_missing(
conn,
mlos_warnings: Dict[str, Any],
pz_build: Optional[str] = None,
) -> Dict[str, str]:
) -> Dict[str, Tuple[str, str]]:
"""Resolve missing-requirement mod_ids to wsids via mod_parsed cache.
Returns {mod_id -> workshop_id} for any missing dep we've previously
cached. Used to render an [add to list] action button next to the
'missing' warning. Unknown deps just get no button.
Returns {missing_mod_id -> (workshop_id, suggested_mod_id)} for any
missing dep we've previously cached. `suggested_mod_id` matches the
missing mod_id by default, but when the canonical wsid is wrong-build
AND `MOD_ID_ALIASES` (adapters.py) has an alternative whose cached
wsid IS build-correct, we redirect to the alias. Example: user on B42
has `truemusic_mixtape_megapack` (require=truemusic). truemusic only
exists as a B41 wsid; the alias `TrueMoozic` exists as a B42 wsid.
Result: {"truemusic": ("3632610172", "TrueMoozic")} — frontend renders
[add TrueMoozic] pointing at the correct build's wsid.
pz_build filters out wrong-build resolutions: if the user is on B42 and
the only cached wsid for `dep` is tagged `Build 41` only, we DROP the
suggestion. Adding it would just trigger a build-mismatch warning;
worse, in cases like `TacHold requires modoptions` where the author's
`require=` is over-declared, the suggestion misleads the user toward
a B41 mod that doesn't actually need to be in the load order.
the only cached wsid for `dep` is tagged `Build 41` only, the canonical
suggestion is DROPPED (and the alias path may rescue it). Adding the
wrong-build dep would just trigger a build-mismatch warning; worse, in
cases like `TacHold requires modoptions` where `require=` is over-
declared, the suggestion misleads the user toward a B41 mod that
doesn't actually need to be in the load order.
"""
from adapters import MOD_ID_ALIASES
missing = mlos_warnings.get("missing_requirements") or {}
if not missing:
return {}
@@ -252,6 +261,11 @@ async def _lookup_wsids_for_missing(
wanted.add(d)
if not wanted:
return {}
# Expand the fetch set with alias targets so we can resolve in one round.
for dep in list(wanted):
for alias_target in MOD_ID_ALIASES.get(dep, []):
wanted.add(alias_target)
rows = await conn.fetch(
"""
SELECT DISTINCT ON (mp.mod_id) mp.mod_id, mp.workshop_id, wm.tags
@@ -268,15 +282,36 @@ async def _lookup_wsids_for_missing(
other_tag = "Build 41" if target_tag == "Build 42" else (
"Build 42" if target_tag == "Build 41" else None
)
out: Dict[str, str] = {}
def _is_build_correct(tags: List[str]) -> bool:
if not target_tag or not other_tag:
return True
return not (other_tag in tags and target_tag not in tags)
# candidates: mod_id -> (wsid, build_correct)
candidates: Dict[str, Tuple[str, bool]] = {}
for r in rows:
if target_tag and other_tag:
tags = list(r["tags"] or [])
if other_tag in tags and target_tag not in tags:
# Wrong-build only — adding it would just trigger a
# build-mismatch. Skip the suggestion.
continue
out[r["mod_id"]] = r["workshop_id"]
tags = list(r["tags"] or [])
candidates[r["mod_id"]] = (r["workshop_id"], _is_build_correct(tags))
deps_seen: set = set()
for deps in missing.values():
for d in deps:
if d:
deps_seen.add(d)
out: Dict[str, Tuple[str, str]] = {}
for dep in deps_seen:
cand = candidates.get(dep)
if cand and cand[1]:
out[dep] = (cand[0], dep)
continue
# Canonical missing or wrong-build; try aliases in declaration order.
for alias_target in MOD_ID_ALIASES.get(dep, []):
alt = candidates.get(alias_target)
if alt and alt[1]:
out[dep] = (alt[0], alias_target)
break
return out