Bring in two narrow additions previously sitting on a feature branch:
- api/diagnostics.py: \_IGNORED_FILENAMES skip-list for /api/conflicts.
PZ engine-concatenated and framework-hook files (sandbox-options.txt
etc.) ship with intentionally distinct sha1s across mods; they are
not real conflicts. Live cache had 33 providers of sandbox-options.txt
with 31 distinct hashes generating false-positive conflict rows.
- worker/build_manifest_and_types: extend the path-based Vehicles signal
to include models_x/vehicles/ and models/vehicles/, catching mods that
ship 3D vehicle assets without scripts. Existing mods need their
manifest rebuilt before mod_types reflects the new signal.
mod.info files saved by Windows notepad start with a U+FEFF BOM, which
made the first line's `name=` regex miss; affected mods displayed with
empty name (sort still worked because `id=` on subsequent lines parsed
fine, but MOD_DB display name fell back to mod_id). Both copies of
mlos_sort.py updated; existing 23 BOM-affected rows already cleaned in
place from raw_mod_info.
Drops missing-dep warnings whose source mod's mod.info `require=` is
out of sync with its Steam Workshop Required Items sidebar. Author
edits to mod.info often lag build ports; trusting the sidebar means
B42 sorts no longer raise warnings on B41-only deps the author has
already retired (e.g. tikitown's Diederiks Tile Palooza, EN_Newburbs).
Filter is conservative: only drops a dep when (a) we have a cached
wsid for it, (b) that wsid is wrong-build for the user's pz_build,
and (c) the source mod's required_wsids list (with required_scraped_at
populated as the "we have evidence" gate, since the column itself
defaults to '{}') excludes that wsid.
Also swaps worker.fetch_required_wsids from public-page HTML scrape
to authenticated IPublishedFileService/GetDetails. Same `children`
data, no 429 cooldowns. Removes the now-unused throttle/cooldown
infrastructure (SORTOF_STEAM_MIN_INTERVAL / SORTOF_STEAM_COOLDOWN
env vars are no longer read).
See docs/specs/2026-05-06-stale-requires-filter.md.
Two narrow additions adopted from a review of HellDrinx Mod Manager
(/tmp/helldrinx-modmanager-PZ-main.zip), per
docs/plans/ (planning conversation, no spec checked in).
A. _IGNORED_FILENAMES in api/diagnostics.py — skip filenames that are
intended merge points (PZ engine-concatenated or framework hooks)
from /api/conflicts output. Multiple distinct sha1s for these files
is by-design, not a conflict. Live cache had 33 providers shipping
sandbox-options.txt with 31 distinct hashes — those would have been
31 false-positive conflict rows on any sort spanning them.
B. Path-based Vehicles signal in worker.build_manifest_and_types —
extended the existing scripts/vehicles[/] check with
models_x/vehicles/ and models/vehicles/. Catches vehicle mods that
ship 3D assets without scripts (rare but real); still requires the
user-build's manifest to be rebuilt before mod_types reflects it.
Drops the prev-sort snapshot ref. Diff is always available — no "sort once
first" empty state — and surfaces drops (banned/missing/collection IDs that
expanded), additions (collection expansion, branch picks), and reorderings
in one pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous flat data/rules/ flattened a real distinction:
- data/rules/modpack/ mirrors of upstream modpack-author-bundled
sorting_rules.txt (HellDrinx). Update when
upstream publishes; don't unilaterally edit.
- data/rules/curated/ sortof-operator-authored rules for individual
mods whose authors didn't declare load order
in their mod.info (RV Interior Expansion).
The loader stays provenance-agnostic; trigger values in _RULES_TRIGGERS
are paths relative to data/rules/ ("modpack/helldrinx.txt" etc.).
File headers in each rules file declare the provenance category and
edit policy.
The directory was misnamed — these are wsid-triggered sorting-rule
overlays, not modpack-specific. HellDrinx happens to be a modpack but
RV Interior Expansion isn't; both use the same generic mechanism.
Renames:
data/modpack_rules/ → data/rules/
_MODPACK_RULES_* → _RULES_*
_modpack_rules_for → _auto_rules_for
_parse_rules_with_modpacks → _parse_rules_combined
_emit_modpack_rules_warnings → _emit_rules_applied_warnings
warning tag "modpack-rules-applied" → "rules-applied"
Bug fix: /api/resort was passing {} rules to sort_mods, silently
dropping every auto-injected rule. The frontend runs an automatic
resort after each /api/sort (driven by localStorage branchSelections),
so the user always saw the resort output — where rv_expansion.txt's
loadAfter chain wasn't taking effect. Now resort goes through
_parse_rules_combined and emits rules-applied warnings the same way
/api/sort does.
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.
The amber "auto-picked" warning persisted even after the user explicitly
picked a branch via the picker — the resort flow only updated the message
text from "auto-picked X" to "selected X" but kept the warning visible.
That reads as "you should review this" when in fact the user already did.
Now: when /api/resort honors a user's explicit branch selection, the
matching auto-picked-branch warning is dropped from WARNINGS. The
BranchPicker in the ModTable expansion (sortof-app.jsx ~818) keeps the
picker accessible if the user wants to switch branches later.
Initial /api/sort still emits the warning (the user hasn't picked yet);
suppression only kicks in once selected_modids honors a group's pick.
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
Add/remove/swap warning-action handlers no longer auto-fire /api/sort.
They mutate the input textarea idempotently; the sort button gets a
pending cue when current input != last-sorted input. Branch-picker
(/api/resort, cheap) keeps instant behavior. Spec lives in
docs/specs/2026-05-04-staged-warning-actions.md.