Knowledge capture — no fix planned
Rare corruption class, repaired manually when it shows up. This issue records the diagnosis and the repair recipe so we don't have to rediscover it. Full write-up: docs/superpowers/specs/2026-08-23-tirra-ferma-version-lie-repair.md.
The corruption class: "version-lie" maps
A genuinely ancient map (~v1.2x–1.6 era) gets loaded into a modern build and re-saved. The re-save stamps the current version line, so on every later load auto-update skips all migrations — the file permanently claims to be modern while carrying ancient data.
Modern builds (upstream 1.147+, this fork) re-render routes, rivers, burg icons and labels from data, discarding the file's drawn SVG. On these maps the data was never migrated, so all of those layers render empty even though the drawn SVG in the file looks fine.
Specimen: "Tirra Ferma" (Discord, 2026-08-22). Wounds: pack.routes [], cells.routes {}, 255/256 rivers without cells/points (and no discharge/width/sourceWidth → d="MNaN,NaN…"), no burg has group or type, pack.markers [] (24 pre-1.7 <use data-x> markers in SVG), pack.zones [] (5 pre-1.100 data-cells groups in SVG), plus the SVG-level wounds (bare layer styles, missing #featurePaths, stale masks) covered by the 1.148 heal work.
Why it happens (two upstream defects, not being fixed here)
- Comparator vs decimal-era chronology.
compareVersions is semver-numeric, but historical releases were decimal-ordered (1.21 < 1.22 < 1.3 < … < 1.65 < 1.7 < 1.99). A map versioned 1.21 compares newer than the 1.3–1.7 migration gates (minor 21 > 3…7), so those blocks silently skip. A partial/mis-ordered migration run followed by a save freezes the damage under a fresh modern version stamp.
- Routes layer toggled off by migration ordering. The layer-state derivation near the end of
auto-update requires has("routes", "path"), but the v1.99 block has already removed all route path elements (routes are data-only from there on). Any pre-1.99 map arrives with the routes layer off even though pack.routes is fully rebuilt.
Diagnosis (no browser needed)
Split the file on \r\n (read as latin1/binary — text-mode newline translation hides the delimiters). Section 5 = SVG, 32 = rivers, 35 = markers, 36 = cells.routes, 37 = routes, 38 = zones. Red flags: empty data sections alongside rich drawn SVG for the same layer; markers as <use data-x …>; zones with data-cells attributes; rivers JSON without cells.
Repair recipe (manual, ~15 min)
- Copy the file; patch field 0 of section 0 down to
1.4.0. Floor choice matters under the semver comparator: the floor's minor must be below every migration gate you need (1.4 runs v1.5+; it skips the destructive v1.3 options rebuild).
- Load in the dev build. Migrations reconstruct everything from the drawn SVG: routes (v1.99), river cells/points (v1.65), river widths (v1.6), markers (v1.7), zones (v1.100), burg groups (v1.109), burg types/culture shields (v1.5).
- Restore original river names per-id from the old section 32 — the v1.21 re-run randomizes names but keeps ids.
Layers.show("routes") (defect 2 above), then re-save via Services.Save.prepareMapData().
- Round-trip verify: fresh load must show route/river paths at zoom 1 and burg icons/labels on zoom-in (they are zoom-gated by design).
Debugging gotcha
options in public/main.js is a top-level let; window.options is a different, stale object. Reading window.options during diagnosis produced a false "burg groups empty" lead. Same family as the window.scale gotcha.
Knowledge capture — no fix planned
Rare corruption class, repaired manually when it shows up. This issue records the diagnosis and the repair recipe so we don't have to rediscover it. Full write-up:
docs/superpowers/specs/2026-08-23-tirra-ferma-version-lie-repair.md.The corruption class: "version-lie" maps
A genuinely ancient map (~v1.2x–1.6 era) gets loaded into a modern build and re-saved. The re-save stamps the current version line, so on every later load
auto-updateskips all migrations — the file permanently claims to be modern while carrying ancient data.Modern builds (upstream 1.147+, this fork) re-render routes, rivers, burg icons and labels from data, discarding the file's drawn SVG. On these maps the data was never migrated, so all of those layers render empty even though the drawn SVG in the file looks fine.
Specimen: "Tirra Ferma" (Discord, 2026-08-22). Wounds:
pack.routes[],cells.routes{}, 255/256 rivers withoutcells/points(and nodischarge/width/sourceWidth→d="MNaN,NaN…"), no burg hasgrouportype,pack.markers[](24 pre-1.7<use data-x>markers in SVG),pack.zones[](5 pre-1.100data-cellsgroups in SVG), plus the SVG-level wounds (bare layer styles, missing#featurePaths, stale masks) covered by the 1.148 heal work.Why it happens (two upstream defects, not being fixed here)
compareVersionsis semver-numeric, but historical releases were decimal-ordered (1.21 < 1.22 < 1.3 < … < 1.65 < 1.7 < 1.99). A map versioned1.21compares newer than the1.3–1.7migration gates (minor 21 > 3…7), so those blocks silently skip. A partial/mis-ordered migration run followed by a save freezes the damage under a fresh modern version stamp.auto-updaterequireshas("routes", "path"), but the v1.99 block has already removed all route path elements (routes are data-only from there on). Any pre-1.99 map arrives with the routes layer off even thoughpack.routesis fully rebuilt.Diagnosis (no browser needed)
Split the file on
\r\n(read as latin1/binary — text-mode newline translation hides the delimiters). Section 5 = SVG, 32 = rivers, 35 = markers, 36 =cells.routes, 37 = routes, 38 = zones. Red flags: empty data sections alongside rich drawn SVG for the same layer; markers as<use data-x …>; zones withdata-cellsattributes; rivers JSON withoutcells.Repair recipe (manual, ~15 min)
1.4.0. Floor choice matters under the semver comparator: the floor's minor must be below every migration gate you need (1.4 runs v1.5+; it skips the destructive v1.3 options rebuild).Layers.show("routes")(defect 2 above), then re-save viaServices.Save.prepareMapData().Debugging gotcha
optionsinpublic/main.jsis a top-levellet;window.optionsis a different, stale object. Readingwindow.optionsduring diagnosis produced a false "burg groups empty" lead. Same family as thewindow.scalegotcha.