x4-modlist-review

Review and triage the X4 mod registry (Phase-A worklist) — scan the ACTUALLY INSTALLED extension folders (the primary source of truth), cross-check against the old profile content.xml (diff/backstop o

SKILL.md

GitHub
<!-- GENERATED by tools/x4validate/scripts/gen-agent-trees.py from agent/. Do not edit: regenerate. -->

Triage the X4 modlist via the x4modlist CLI. API-FIRST — never scrape Nexus. Registry: $X4_MODS/_registry/modlist.yaml (or $X4_REGISTRY if set — the tool resolves it; x4validate --paths shows where); human dashboard: WORKLIST.md next to it.

★ SOURCE OF TRUTH: the physically INSTALLED extension folders are PRIMARY — game-root extensions\, profile extensions\ (if present), Steam Workshop content\392160\ (if present). That's what the game actually loads. For the INVENTORY, the profile's content.xml enabled-list is a SECONDARY cross-check only ("did I forget to re-acquire something from my old modlist?") — it keeps entries for mods long gone from disk. For what is ACTIVE it does decide: an installed mod's profile entry overrides its manifest's enabled, and a mod with no entry falls back to its manifest (enabled unless the manifest says enabled="0"). A mod tracked historically but not found on disk shows up in a separate "OLD MODLIST — NOT CURRENTLY INSTALLED" dashboard section, not in the active lanes.

Run commands via uv from the tool dir: cd $CLAUDE_PROJECT_DIR/tools/x4validate && uv run --python 3.13 x4modlist <cmd> Needs X4_NEXUS_KEY (user env). If a command errors "X4_NEXUS_KEY not set", the user must set it (endpoints, rate budget and how to get a key: x4validate/_nexus.py).

Establish the current situation

Before proposing changes, read ## My preferences in KNOWLEDGEBASE.local.md and reconcile it with the current request and session context: is this a kept save, a new game or a disposable test, and are updates, additions, removals or ordering changes authorized? Assume neither an ongoing playthrough nor a fresh game. Ask only about material facts that remain unknown or contradictory; a missing local file alone does not justify repeating an answered question. Continue independent read-only work while waiting. Apply standing user rulings before choosing work, and record new lasting rulings, dated, in that user-owned section. Keep it concise; if session context is truncated, read the full section directly. These choices do not waive verified backups, write guards or scoped authorization.

Workflow

Before adopting a mod, x4modlist search "name" [--limit 15] [--json] returns candidate Nexus identities, versions, update dates and status without changing the registry. It needs X4_NEXUS_KEY (environment or x4-paths.env) and says so when the key is missing or refused. It labels complete, zero, partial and unavailable searches; a capped or damaged page is never a complete negative (the malformed/duplicate count is printed). Each candidate is marked against the ACTIVE registry mods: have = a pinned/exact Nexus id, have? = an unconfirmed guess, new = no active mod carries that id. The output also states how many active mods lack a Nexus id (so new cannot rule those out); with no usable registry it says marking is unavailable instead of marking everything new. Every result ends with its coverage: names only (never descriptions), hidden/removed mods and Steam Workshop-only mods are absent, and an empty result means none within that coverage, not that none exist. Search says nothing about game compatibility.

  1. Ingest — x4modlist ingest scans the installed folders (reading each mod's OWN content.xml for its real id/name/version/author — folder names can differ from the manifest id, e.g. a folder named in CamelCase whose manifest id uses underscores, spaces and an extra suffix) and merges that as PRIMARY; the old profile content.xml is a secondary backfill pass so nothing tracked historically is silently dropped. --installed-only skips the content.xml pass; --dirs a,b,c overrides the scanned directories.
  2. Refresh — x4modlist refresh pulls upstream version/status and auto-resolves identities for installed mods. Identity resolution prefers each mod's REAL manifest name (far more reliable than guessing from the folder/id) over a humanized-id guess, with fallbacks for common author-prefix ("<author>: X" → "X") and suffix-qualifier ("X - Some Edition" / "X <overhaul>" → "X") naming patterns. --force bypasses the once-per-day TTL; --ids a,b,c targets specific mods (works even for NOT-installed old-list mods, to check upstream status before deciding to re-acquire). "Has an update" means one thing, with a grace window: upstream's newest MAIN file was uploaded more than UPDATE_GRACE_DAYS (3) days after the installed copy's manifest date — both dates are printed on every UPDATE line and in the dashboard's ⬆ UPDATE AVAILABLE table. A mod pinned to a FILE (resolve --file) is judged against that file's newest successor on the page (its file_updates chain), or the pinned file itself when it has none. Version strings are not compared (Nexus uses several shapes; manifests carry integers). The verdict is available / same-release? (upload within the 3-day window — likely the same release, not a new one, printed with both dates and counted in the tally, but excluded from the UPDATE AVAILABLE table) / none / unknown (a date is missing or unparseable — never read that as "no update") / unconfirmed (the identity is a guess, so the upstream may be another mod). A verdict the current run did not re-check (once-per-day TTL) is labelled carried over with its check date; a row that now ends in error/untriaged/off-nexus carries no verdict at all. ⚠ UNMEASURED: authors commonly date the manifest before finishing the upload, which is why the grace window exists — but its own false-positive rate (a same-release? that was actually a fast follow-up release) and false-negative rate (a real update landing inside the window) are both unmeasured until the next online refresh checks some by hand. Compare the printed upstream file/version before re-downloading either way. A refresh that hits a missing/invalid key, a spent rate limit or a dead network stops (exit 2), keeps every row it already fetched, and leaves the rest untouched.
  3. Present — read WORKLIST.md; summarize the lanes (✅ ready / ⏸ churning / ⚠ predates-9.0 / 🔧 custom-local / ❌ drop), the Updates tally and ⬆ UPDATE AVAILABLE rows (with both dates), the NEEDS SPOT-CHECK count, and the OLD MODLIST — NOT CURRENTLY INSTALLED count (mods to potentially re-acquire).
  4. Spot-check loop — x4modlist needs-review lists entries needing a human call. For each, show the auto-matched candidate(s) and have the user decide:
    • confirm/correct identity → x4modlist resolve <id> <nexus_id>
    • junk/personal/cheat mod → x4modlist ignore <id> --reason "..."
    • a mod they locally customized → x4modlist mark <id> --custom --notes "..." Never guess keep/drop or fabricate a match — surface candidates, the user decides. Search can return a wrong/deprecated/adoption-patch fork as the top hit (searching a mod's full name can match a niche adoption/compat fork instead of the real, popular mod) — always show the candidate list, don't just trust rank #1.
  5. Deep 9.0-readiness (opt-in only) — the API gives version/date/status but NOT "9.0-compatible" (that gap is real; changelogs are sparse). For churning / predates-9.0 mods the user wants to keep, dispatch the mod-research agent (API-first) for changelog/community signal. Only on request.

The 🔧 CUSTOM-LOCAL FORK lane

If a mod's Nexus status is removed/hidden AND it's marked custom_edited, it is classified custom-local — NOT drop. An author temporarily hiding their page mid-update (or a mod you've locally forked/ported yourself, e.g. in dev\) doesn't mean abandon it; "drop" would be actively wrong guidance there. This only applies once the user has marked the mod custom-edited.

Honest framing

This produces the auto-resolved worklist + the spot-check queue. The keep/drop/custom decisions and in-game testing are the user's — it makes Phase A tractable, not instant. Strategy: work the ready + churning lanes first (the live mods); let predates-9.0 and unresolved bake, and re-refresh later as authors ship 9.0 updates. Periodically re-ingest to catch newly-installed/removed folders.