Skip to content

Pagonia Land Manager Changelog

User-visible changes to pagonia-manager. The manager bundles the patcher and paker, so their user-facing changes are recorded here as well — one changelog for the whole toolchain instead of three. Follows Keep a Changelog and Semantic Versioning.

The browser-based catalog browser has its own changelog (it ships with the site, not the tagged tool releases).

The repo-root CHANGELOG.md is a separate, unrelated log: it tracks GameDatabase changes per Pioneers of Pagonia version (the XML data the game ships), not tooling behaviour.

[0.4.0] - 2026-06-26

Changed

  • Your own tweak settings are now tracked explicitly, not guessed. Until now the manager told a value you set yourself apart from one a collection seeded by comparing it to the collection's value — so a value you deliberately chose that happened to equal the collection's was mistaken for the collection's, shown as such in tweak list, and discarded on a collection update. The manager now records which tweaks you set yourself (every tweak set marks it), so the origin is exact: such a value stays your override and is carried forward across a collection update even when it matches the curator's value. Profiles created before this are migrated once on first read (the old value-comparison heuristic seeds the markers), then tracked explicitly from then on. Internal only — the profile gains an optional userTweaks marker list; the profile format stays 0.1 (purely additive, nothing external consumes it yet).
  • Format-version files now read across compatible versions instead of hard-failing (patcher). Every distribution file carries a MAJOR.MINOR format version (patchFormatVersion, collectionFormatVersion, collectionLockVersion, indexFormatVersion, catalogFormatVersion). Until now anything other than the exact version this build knew was rejected outright — including a purely additive bump — with a cryptic schema failure and no guidance. The tools now apply a tiered reader: a file at the same major and a newer minor still reads (any newer optional fields are ignored) and notes formatMinorAhead recommending you update the tool; a newer or retired major — a breaking shape this build genuinely can't read — is refused with an actionable formatMajorUnsupported / formatMajorRetired message naming where to get a newer release; a value that isn't MAJOR.MINOR reports formatVersionMalformed. So a mod or collection authored against a slightly newer toolchain opens instead of bouncing, while a genuinely incompatible file tells you exactly what to do. Applies to pagonia-patcher schema-validate, lockfile resolution, and anywhere the manager loads these files. The JSON Schemas under schemas/mod-patches/ widen accordingly (same-major minors are valid); the gate runs before strict schema validation so a newer minor isn't rejected for a field the older schema didn't know.
  • Tools auto-migrate an older format minor on write (patcher). Complementing the read side above, the writers now stamp the current format version every time they write a managed file, and pagonia-patcher index build upgrades an index.yaml authored against an older same-major minor up to the current minor in place (formatMigratedInPlace), preserving all surrounding formatting. So a purely additive format bump rises the next time a tool touches the file — authors never hand-migrate it. index build --check reports a pending migration as info without failing (an older minor is still valid); newer minors and majors are never silently rewritten.
  • The manager now applies the same format-version rule when it reads catalogs and repo indexes. Previously the manager deserialized a catalog.yaml / repo index.yaml without checking its catalogFormatVersion / indexFormatVersion at all. Now it runs the shared policy on every read — browse, catalog show, and install from a gh: repo — so a catalog or index authored against a newer same-major minor reads with the formatMinorAhead note, while a newer/retired major or a malformed version is refused (formatMajorUnsupported) before its repos enter your browse list or an install resolves against a structure it can't trust. Cached catalogs are gated identically to freshly-fetched ones. The manager and the patcher now share one format-version vocabulary end to end.

Added

  • pagonia-paker loca info <loca> — decode a mod's compiled localization blob (paker). Editor mods ship their UI strings as a compiled localization/loca_<lang>.bin (e.g. loca_en_us.bin), a format that was previously undocumented and unreadable by our tools. loca info prints the strings it carries, in order, and --json writes a LocaInfoReport. The byte layout — a bare sequence of .NET 7-bit-length-prefixed UTF-8 strings, no header/version/key table, read until end-of-stream — was reverse-engineered from two test paks produced by the 1.4.0 Pagonia Editor ("package gdb with dependency" / "package map with dlc1 and gdb") and is now documented in paker CLI → loca info and Mod Distribution → localization/loca_<lang>.bin. Decode is best-effort (no magic number to validate a candidate file); a 0-byte file is a valid empty blob.
  • The Pattern B scaffold now emits a Localization pointer for mods that ship translations (patcher). When a mod's overlay folder contains a localization/ folder (e.g. a compiled loca_<lang>.bin added via entries.add), the generated files.json now carries a Localization key pointing at that folder alongside the existing GameDatabase key — matching how the shipped paks and EE's editor mods wire localization in. A mod with only localization and no *.gd.xml gets a files.json with just the Localization key (and no .gd.bin); an asset-only overlay with neither still skips files.json entirely. Previously the scaffold only ever wrote the GameDatabase key, so a translation a mod shipped was invisible to the engine.
  • pagonia-paker gdbin info now reads editor-emitted .gd.bin indexes (paker). The 1.4.0 Pagonia Editor closes every .gd.bin it writes with a zero-length terminator record (00 00 00 00) — after the last entry, or alone after the header for a map-only mod's empty module index — which the shipped paks (core/dlc1/…) don't have. gdbin info previously errored (gdbinEntryTruncated) on every such index, i.e. on every real editor mod. It now tolerates the terminator, reports it as EditorTerminator: yes / HasTrailingTerminator in the JSON report, and GdBinWriter re-emits it so an editor index round-trips byte-identically. The reverse-engineered terminator + the unified byte[3] = record-count − 1 rule (the terminator counts as a record) are documented in paker CLI → .gd.bin Index Format.
  • The manager now auto-orders mods by their declared loadAfter / loadBefore. At plan / deploy the enabled set is topologically ordered to honour each mod's loadAfter / loadBefore, with your manual profile order as the stable tiebreaker — a mod that must load after another lands in the right place without you hand-sorting. It never silently overrides your order: when a constraint reorders it, that's reported (manager.loadOrderAdjusted), and a constraint cycle is flagged (manager.loadOrderCycle) with those mods left in your manual order. The interactive Reorder load order screen now marks which positions are dependency-pinned and shows the effective deploy-time order.
  • The manager now acts on declared mod dependencies and incompatibilities. A mod's manifest can list dependencies and incompatibleWith, but until now nothing read them. The manager now evaluates them across your enabled set — when you enable a mod, in plan / deploy, and in doctor — and warns when a required mod isn't enabled (manager.modDependencyMissing, saying whether it's installed-but-disabled or not installed at all) or when two enabled mods declare each other incompatible (manager.modIncompatibleEnabled, naming both). Advisory only — it never blocks; it just stops the clash being silent. It also warns before you disable or uninstall a mod that other enabled mods still depend on (manager.modDependedUponByOthers), so a dependency isn't broken silently.
  • install --with-deps pulls a mod's missing dependencies for you. Installing a mod with --with-deps (or saying yes to the interactive Install a mod / Browse catalogs prompt) resolves its declared dependencies — and theirs, transitively — and installs them, looking in the same repo the mod came from first, then your subscribed catalogs. Each pulled dependency is reported (manager.modDependencyInstalled); one that can't be found anywhere is a warning (manager.modDependencyUnresolved) and the rest still install. A dependency cycle is handled (each mod is pulled once).
  • contentHash per mod in the repo index — integrity + same-version drift detection (patcher). A mod's index entry can now carry a contentHash: a SHA-256 over the mod's logical payload (mod.yaml plus the patch files it references — the exact set fetched, so extra repo files don't affect it). pagonia-patcher index build computes and inserts it (and refreshes a stale one), and index-check recomputes and flags a mismatch — which catches the footgun a version string can't: a mod's content changed but its version didn't. The shipped official index now carries hashes for its mods. The optional field is additive to the existing 0.1 index format (no version bump while the format is still in-development). Defined once in a shared ContentHash helper so author and consumer agree byte-for-byte. On the consumer side, installing a mod from a gh: repo whose index advertises a contentHash re-computes it over the downloaded payload and warns (manager.modContentHashMismatch) on a mismatch — download-integrity + tamper/drift detection — while still installing the authoritative mod.yaml (a mismatch usually just means the author forgot to re-run index build).
  • Interactive "Update mods + collections" — review and apply updates from the menu. A new entry under Mods runs the read-only update check across your gh:-sourced mods and collections, lists what's behind, and for whatever you pick shows the delta (from→to, gameDatabaseVersion, and whether your tweak overrides carry forward or get reseeded) before a confirm — then applies the transparent, reversible update. For a collection where you've personally changed tweak settings, it lists those and lets you keep them all, take the collection's new values, or decide per changed setting before applying. Nothing is touched until you confirm; a "Re-check now" entry rescans, and Esc finishes. This is the opt-in front door over the scriptable update / collection update verbs.
  • Status dashboard shows an "Updates" line. Once you've run the update check this session, the Status dashboard surfaces "N update(s) available" (mods + collections) so you see it without reaching for a verb. The dashboard itself stays instant and offline — it reads the count cached from the Update screen rather than hitting the network on every open — and points you at the Update entry when no check has run yet.
  • pagonia-manager doctor --check-updates — fold update detection into the health roll-up. doctor is offline by default; pass --check-updates to add an "Updates available" check that reports how many mods/collections are behind. A source it can't reach degrades to a per-item warning (or the whole check to Skipped), never an error, so it stays CI-safe.
  • pagonia-manager collection update <collection-id> — move a whole collection to its newer version. The collection counterpart of update <mod-id>: it re-checks the collection's source repo, and if a newer version is published, re-fetches it and reinstalls over the existing profile, picking up the curator's new mod set. The previous collection version stays on disk as a rollback anchor, and each referenced mod version coexists. Only a collection installed from a gh: repo can be updated (that's the one with a recorded source); a local-file install, an already-current collection, or an uninstalled id all change nothing and say so. Your own tweak settings are carried forward by default (a "merge"): a value you personally changed survives the update, while a tweak you'd left at the curator's value follows the collection's new default — the same non-destructive spirit as the per-mod update. Pass --reseed-tweaks to instead discard your overrides and take the curator's values wholesale. (In the interactive Update mods + collections screen you're asked per changed setting; see below.)
  • pagonia-manager update <mod-id> — move a mod to its newer version, transparently and reversibly. The apply step that outdated only reports: it installs the newer version from the same source, re-points your active profile's pin to it, and carries your per-mod tweak overrides forward across the bump (a renamed tweak is migrated forward, so no value is lost). Crucially it keeps the old version on disk — the new one installs alongside it — so an update is a one-liner to undo (enable <mod-id> --version <old>). Nothing is silent: you name the mod, it tells you from→to, and if the pin is already current (or the mod isn't enabled / has no remote source) it changes nothing and says so. (Also available interactively via Update mods + collections.)
  • pagonia-manager outdated — see which installed mods and collections have a newer version published. A read-only check: for each mod and collection you installed from a gh: repo, it re-fetches that repo's catalog index.yaml at the default branch and compares the advertised version to what you have, printing id: installed → available for anything behind — mods and collections listed separately. It changes nothing — it just answers "is anything out of date?" so you can decide. Items installed from a local folder/zip/file have no source to compare and are reported as skipped; a repo that's unreachable or no longer lists an item gives a per-item warning instead of failing the whole check. It also catches a same-version content re-publish — when a mod's source re-published the same version with different content, outdated recomputes the installed payload's hash, compares it to the index's advertised contentHash, and flags it (manager.modContentDriftAvailable, "re-install to refresh") instead of leaving it silent (the version string alone can't show this). It writes a machine-readable report with --json <out> (the updates report — mods + collections behind, same-version content drifts, and the checked/skipped counts). (The one-step apply lives in update / collection update and the Update mods + collections screen; the Status dashboard + doctor --check-updates surface the count.)
  • The manager refuses a store or profile written by a newer version of itself. state.yaml (storeVersion) and profiles (profileVersion) are internal files only ever read and written by the same install. If you run an older manager against a store a newer one wrote, reading it and writing it back would silently drop the fields the newer manager added. The manager now detects that on read and stops with a clear manager.storeSchemaVersionUnsupported / manager.profileVersionUnsupported error telling you to upgrade, instead of quietly corrupting the store. An absent or older same-version file reads normally — this only triggers on a genuinely newer one. (Forward groundwork: it does nothing today, where everything is at 0.1; it's the safety net that has to already be in the binary before any future store/profile format bump.)

Fixed

  • classify no longer reports a false global scope for editor map paks (paker). A map authored by the 1.4.0 Pagonia Editor ships an empty module-level .gd.bin (header + the editor's zero-length terminator = 11 bytes). The scope detector decided "contributes GameDatabase content" from any byte past the 7-byte header, so it mistook those 4 terminator bytes for a resource entry and tagged the pak global on top of its real map-scoped scope. It now reads the first record's length and treats a zero (the terminator) — or no record at all — as empty, so a map-only editor pak classifies as map-scoped only. Verified against the real EE "package map with dlc1 and gdb" test pak. The synthetic-empty case (a bare 7-byte header) was already correct; this fixes the realistic editor shape.

[0.3.0] - 2026-06-16

Added

  • Example mods are now drift-checked against a real game database (preflight). A new local gate, scripts/check-examples-against-gdb.ps1, dry-runs (pagonia-patcher plan) every shipped example and official mod against an extracted game-gdb and fails if any patch target no longer resolves — a stale expectedOldValue / expectedOldXml / target GUID after a game update. It is the systematic form of the out-of-band plan check the schema-validate stages always referred to: schema validation only proves an example is well-formed, never that it still matches the current game data. The game database is resolved from -GameRoot, $env:PAGONIA_GAME_GDB, game-gdb/, or the newest snapshots/*/game-gdb; with none available it skips itself (exit 0), so CI stays green while local runs gate. Wired into scripts/preflight.ps1 as a final stage.
  • classify now reports a mod's GameDatabase scope — global vs map-scoped (paker). A new GdbScopes signal tells you, at a glance, whether a downloaded mod changes the GameDatabase globally (active in all your games — like core / dlc1 or a globally-active editor mod) or only map-scoped (the per-map "hosted game database", active only when playing that map), or both. Detected from whether each gd.bin actually lists any *.gd.xml resources, so an empty module-level <m>.gd.bin (the editor emits one even for a map-only mod) is correctly not reported as global — a published editor map reads GdbScopes: map-scoped, not a false global. An empty GdbScopes means the pak contributes no GameDatabase content at all. This is the foundation for filtering mods by reach; the filter UI in catalog browse is a follow-on. See paker CLI → classify.
  • index-check / index build — keep a repo's index.yaml in sync with its mods (patcher). A distribution repo's index.yaml duplicates a curated subset of every mod.yaml (name, version, gameDatabaseVersion, safety flags) so the manager can browse a catalog without fetching each mod — a cache that could silently drift. pagonia-patcher index-check <repo-root> cross-checks the mirrored fields against each mod.yaml and flags drift, orphaned entries (index entry with no mod on disk), missing entries (mod folder absent from the index), and id mismatches; index build <repo-root> re-syncs the drifted values in place (preserving formatting), with --check as a write-nothing CI gate. The curated description / tags / screenshots fields are intentionally allowed to differ and are never compared. Wired into preflight against the first-party official-mods/ tree. See patcher CLI → index-check and Mod Distribution → the index mirror contract.
  • Esc at the main menu quits with a one-key confirm. Pressing Esc on the main menu now offers a quick exit — a small "Quit Pagonia Land Manager?" prompt where Enter quits and Esc backs out to the menu. A friendlier way to close the app from a fresh menu than reaching for Ctrl+C (which still works). Esc inside a submenu continues to mean "Back", not quit.
  • Catalog browse now shows mod safety, and install warns on stale catalog metadata. catalog show renders a Safety column (needs-new-game / safe-to-remove / multiplayer / campaign) from each index entry's safetyFlags, so you can weigh a mod before installing without fetching it — the consumer-side payoff for the index duplicating that data. And when installing from a repo with an index.yaml, the manager cross-checks what the catalog advertised (version / gameDatabaseVersion / safety) against the mod.yaml it actually downloads, emitting a manager.repoIndexMetadataMismatch warning on any disagreement (non-fatal — the authoritative mod.yaml still installs; the warning flags that the browse list may have misled you). The consumer-side complement to the author-side index-check. See Mod Catalogs → How It Looks From The User Side.

Changed

  • Yes/no prompts now confirm on a single keypress. Every interactive [y/n] question (enable a mod, switch profile, activate a collection, roll back, overwrite drifted files, …) now submits the moment you press y or n — no trailing Enter needed. A bare Enter still accepts the highlighted default, so the destructive prompts that default to no are unchanged: rolling back or force-overwriting still takes a deliberate y. Routed through one shared confirm helper so the keystroke behaviour is uniform across the shell.
  • Interactive Plan + Deploy no longer dead-ends on a warning. When a plan carries a blocking warning — most often a mod whose gameDatabaseVersion doesn't match the install you pointed at — the wizard now offers an inline "Deploy anyway (accept the warnings)" choice, the same one-key opt-in the conflict and live-drift paths already had. Previously only patcher conflicts got that prompt; a manager-level warning fell through to a hard Deploy aborted: pass --accept-warnings and forced you to quit and re-run on the scripted CLI. The bypass stays a per-deploy, in-context choice (not a persistent "always ignore" setting) so a genuinely incompatible mod can't slip in silently on a later deploy.

Fixed

  • Example mods/docs realigned with the current game database. The Beta Update renamed the icon assets from .png to .image and added a PileLocator to building cost entries; the shipped examples still referenced the old data and now failed to deploy. Updated the wine-icon-test example mod, the sanctuary-add-custom-ability overlay, and the resource-icon / optional-DLC / change-display docs to the .image asset paths, and the remove-stone-cost example's expectedOldXml to match the Sawmill's current Stone Block cost item. Caught by the new game-gdb drift check above.
  • profile export canonicalises renamed tweak ids. When a profile stored a tweak override under a tweak's old id (the author later renamed it and listed the old id under aliases:), exporting that profile to a collection now folds the value into mods[].tweaks under the current id instead of carrying the stale alias forward — the same alias-forward migration the manager already applies when reading tweaks. The export is read-only on the profile (it doesn't rewrite the stored ids) and surfaces a one-line note when it migrates, conflicts, or finds an orphaned override.
  • Interactive selection prompts are now cancellable (Esc). Once you'd picked an action from a menu, the follow-up list picker — which mod to enable/disable, which profile to switch/copy/export/delete, which subscription to remove, which version to uninstall, which tweak to set/reset, which value for an enum tweak, and every step of Reorder load order / Active-Profile move — forced you to choose with no way back; only Ctrl+C escaped. All of these now accept Esc to cancel and return to the previous menu, matching the empty-line back-out the free-text prompts already offer. The reorder/move flows are cancellable at each of their three steps.

[0.2.0] - 2026-06-09

Added

  • Op-aware tweak hints in the interactive configure-mod wizard. When a tweak feeds an arithmetic op, the wizard now explains what the value does instead of showing a bare number: the tweak table has an Effect column (e.g. x 4 -> 10), the edit screen shows a one-line hint with the op's reason, and setting a value echoes the resulting vanilla -> new preview. The preview is computed the same way a deploy writes it (rounding + clamping included).
  • Arithmetic patch operations (multiplyValue / addValue). Patches can now scale a value relative to its vanilla amount instead of only replacing it with a literal: multiplyValue writes expectedOldValue × factor, addValue writes expectedOldValue + delta (negative subtracts). The factor/delta can be a literal or a {{ tweaks.<id> }} placeholder, so one shared multiplier tweak scales many targets — the "Hardcore = ×1.5 costs" knob a literal tweak couldn't express. Results round (round/floor/ceil, default round) and optionally clamp (clampMin/clampMax); a clamp that fires is reported in the plan. See Patch Format → Arithmetic operations.
  • Conflict-minimising authoring advisor. Installing a mod that ships its own GameDatabase overlay (*.gd.xml) now surfaces conflict-risk findings — prefer additive Incremental/Template over destructive Replace/Unload, with a warning when an Unload would dangle a still-referenced entity. Advisory only (never blocks install). Advanced → Mods → advise re-runs it on any installed mod, optionally base-aware against a game root (cross-database unload check + a "this Replace could be Incremental" hint).
  • Cross-mod overlay conflict detection. plan / deploy now warn (manager.crossModOverlayConflict) when two enabled mods destructively (Replace/Unload) target the same inherited entity — naming the load-order winner and the silently-overridden mods, so you can reorder, disable one, or ask for additive modes. Surfaces in the CLI and the interactive plan/deploy flow.
  • doctor health roll-up. New pagonia-manager doctor [--game <path>] verb: one read-only first-stop check covering store init, active profile, enabled-mods-installed, cross-mod overlay conflicts, orphaned deploys, deploy-backup storage, and (with a game root) expansion ownership. Prints [OK]/[WARN]/[FAIL]/[SKIP] + a summary; exits non-zero only on an error (CI-friendly). The scriptable form of the interactive Status dashboard.
  • Interactive mode: every free-text prompt (mod source, game-install path, profile / collection names, catalog sources, tweak values, …) now accepts an empty line to cancel and return to the previous menu, instead of forcing you to type a value or hit Ctrl+C. This mirrors the Back entry the selection menus already offer; each prompt shows an (empty to cancel) hint.
  • Interactive mode: long selection lists (installed mods, profiles, catalog subscriptions, collections, tweaks, load order) now support type-to-filter search — start typing to narrow the list instead of scrolling. Short fixed menus are unaffected.
  • Status dashboard: when the deploy-backups storage warning fires, it now shows a per-install breakdown chart (GB + share by game-install fingerprint) so it's obvious which install's backups dominate the total. Shown only when more than one install has backups.

Changed

  • Interactive menus now remember your last selection: stepping into an entry and back lands the cursor on that same entry instead of resetting to the top (main menu and every Advanced submenu).
  • Interactive menus now wrap around: pressing up on the first entry jumps to the last, and down on the last jumps to the first.
  • Interactive menus accept ESC to back out (same as choosing the menu's Back entry).
  • Interactive progress is now uniformly the plain-ASCII dot ticker (-> label ...) across every operation — install, catalog / collection fetches, advanced commands — matching deploy / rollback. It replaces Spectre's animated spinner, which could render as a frozen glyph (looking like a hung program) on terminals that don't support its cursor-control escapes.
  • Status dashboard: raised the deploy-backups storage warning threshold from ~5 GB to ~15 GB. A single live deploy already backs up ~5 GB (the game's core.pak is that large), so the old 5 GB threshold nagged after the very first deploy — when the only backup is the protected lastDeploy and there is nothing to reclaim yet.

Fixed

  • Rollback no longer leaves a mixed install when a backup is missing or corrupt. Previously, if restoring a rebuilt pak failed mid-way (missing/tampered backup), rollback still went on to delete the Pattern B overlay paks before aborting — leaving the game half-reverted with no clean recovery. It now aborts before removing any overlay, so a failed rollback leaves the install in its pre-rollback state.
  • Rollback preserves an overlay pak you replaced by hand. If a mods/*.pak overlay was swapped for different content after deploy, rollback would delete it unconditionally under --force. It now re-checks the file's hash and, on a mismatch, leaves it in place with a warning instead of discarding it.
  • Pak reader hardened against malformed input. Extracting a compressed pak entry now bounds the output to the index-declared uncompressed size and rejects an entry that inflates beyond it (decompression-bomb guard); the reader also refuses a structurally corrupt index up front — an index region larger than the reader supports, or an entry count too large to fit the index data — instead of wrapping a length to a negative value or spinning a huge parse loop.
  • Installing from modio: rejects a non-HTTPS download URL. The mod.io download link is now scheme-checked and refused if it isn't https, instead of being fetched over an unencrypted/unknown transport.
  • Plan / apply JSON reports validate for arithmetic mods. The report schemas were missing multiplyValue / addValue in their operation-type list, so --json reports for a mod using the new arithmetic ops failed their own schema. Both ops are now accepted.
  • deploys clean --keep 0 always keeps each install's newest deploy. It could previously remove the newest deploy of a non-active install, leaving that install with no rollback path. The newest deploy per game install is now retained as the rollback anchor (the lastDeploy guard still applies on top).
  • doctor flags an unreadable enabled mod instead of reporting "all present". An enabled mod that is installed but whose manifest can't be read now grades the enabled-mods check as an error rather than being silently dropped from the count.
  • Interactive menus no longer crash on profile / subscription names containing [ or ]. Selection lists and the Status dashboard now escape choice text, so a name with markup metacharacters renders literally instead of throwing.
  • Direct pagonia-patcher --tweak overrides are now type-checked. An out-of-set enum value or a non-boolean value now warns at plan time (previously only the manager validated overrides; the patcher substituted them unchecked). A {{ tweaks.* }} placeholder in clampMin/clampMax now resolves too.
  • validate-mod --game-root warns when the path holds no game data. A wrong or mistyped game root no longer silently skips the base-aware advisor checks while still exiting clean.
  • validate-mod / plan catch more authoring mistakes. A fractional default on an integer tweak is now rejected, and a malformed target path (an empty segment from a leading/trailing/doubled /, or a predicate with no element name before [) is reported with a specific reason instead of the generic "did not resolve".
  • Hardened arithmetic ops + pak decompression against malformed input. multiplyValue/addValue now reject NaN/Infinity operands and guard the integer-format step against overflow on very large results; the pak reader and the standalone decompress command bound decompressed output to guard against decompression bombs.
  • Boolean tweak overrides with stray whitespace now apply correctly. tweak set normalises a stored value (canonical true/false, all values trimmed), so a value like " true " no longer silently resolves to the false branch of a tweak ternary.
  • pagonia-patcher apply refuses an output directory that overlaps the source. Applying with the output set to — or nested inside — the source game root is now rejected up front instead of wiping (and destroying) the source.
  • A deploy that fails mid-write now restores the install to its pre-deploy state. If writing the patched files (or overlay paks) into the game throws partway through, the deploy rolls itself back from the just-made backups and removes any overlay paks already written, instead of leaving the install half-modified with nothing to roll back.
  • Remote-install hardening. Downloaded archives are now refused if they declare an implausible entry count or uncompressed size (zip-bomb guard); the per-download size cap (default 8 GiB) is overridable via PAGONIA_MAX_DOWNLOAD_BYTES; GitHub raw paths from a remote index are URL-escaped; and a catalog URL on a loopback or link-local host (e.g. the cloud-metadata endpoint) is rejected (SSRF guard).
  • --version no longer prints a duplicated commit hash. Release builds showed <version>+<sha>.<sha> because the SDK auto-appended the git commit on top of the hash the release workflow already stamped. The auto-append is now disabled, so release builds show <version>+<sha> and local dev builds show <version>-dev cleanly.

[0.1.0] - 2026-06-05

Initial release of the Pagonia Land toolchain — declarative XML mod patching, pak (un)packing, and mod install / profile / deploy management. See the README for the feature overview.