<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>X-GIS Blog</title><description>Engineering notes from the X-GIS team — the compiler, the shader IR, the globe math, the GPU render graph.</description><link>https://x-gis.github.io/X-GIS/</link><item><title>Verified only where the bug showed: a hot-path fix that shipped a zoom-in regression</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-15-verified-only-where-the-bug-showed/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-15-verified-only-where-the-bug-showed/</guid><description>A WebGL2 flicker fix (#1139) was render-parity-verified on exactly one scenario — the zoom-OUT hold-tiles case that reproduced the original flicker, retained-content 0.19 → 0.79 matching WebGPU — and never on zoom-in or per-frame cost. It shipped an unmemoized per-frame classifyTile run twice per tile plus synchronous uploads, and stuttered with progressively-black fills on a Seoul zoom-in. Reverted 36 minutes later (#1140). A hot-path fix must be verified in its worst regime, not the one that showed the original bug.</description><pubDate>Wed, 15 Jul 2026 19:30:00 GMT</pubDate><category>verification</category><category>performance</category><category>webgl2</category><category>rendering</category><category>architecture</category></item><item><title>Draped at the wrong tile: a deferred write turned a shared uniform pool into a data race</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-15-draped-at-the-wrong-tile/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-15-draped-at-the-wrong-tile/</guid><description>A draped landcover polygon rendered hundreds of kilometres offshore — but only on WebGPU. Every vector slice of one source shares one Material uniform pool, and each slice resets the pool cursor to 0, so a later slice&apos;s tile i overwrote an earlier slice&apos;s tile-i buffer. WebGPU&apos;s queue.writeBuffer defers to submit (last-writer-wins), so every draw read the final bytes; WebGL2&apos;s immediate bufferSubData never showed it — and the differential test was structurally blind, because both frames aliased identically.</description><pubDate>Wed, 15 Jul 2026 18:00:00 GMT</pubDate><category>rendering</category><category>webgpu</category><category>debugging</category><category>globe</category><category>verification</category></item><item><title>The hemisphere that was never selected: a fast-path bbox that collapsed to a point</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-15-the-hemisphere-that-was-never-selected/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-15-the-hemisphere-that-was-never-selected/</guid><description>Half the globe rendered as background again — same symptom as the drape-suppression bug, opposite root cause. This time the trans-antimeridian tiles were never selected: the overzoom fast-path unprojects the viewport corners to a lon/lat box, but on a small globe disc the corners miss the sphere, the box collapses to the sub-camera point, and it emits the single tile column under the camera — which a contiguous longitude range can never wrap across the dateline. Told apart from the draw bug by seam location and persistence.</description><pubDate>Wed, 15 Jul 2026 02:30:00 GMT</pubDate><category>rendering</category><category>debugging</category><category>globe</category><category>tiles</category><category>verification</category></item><item><title>A failure with no witnesses: one query pipeline that silenced all three channels</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-14-a-failure-with-no-witnesses/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-14-a-failure-with-no-witnesses/</guid><description>A jq command over a saved MCP result printed nothing and exited 0 — no data, no error, no failure. Every one of those was manufactured by the caller: a wrong schema emptied stdout, a defensive 2&gt;/dev/null ate the one diagnostic line, and | head laundered jq exit code 5 into 0. A Unix pipeline has three signal channels; habits acquired as defenses can close all three at once.</description><pubDate>Tue, 14 Jul 2026 14:00:00 GMT</pubDate><category>debugging</category><category>shell</category><category>tooling</category></item><item><title>The strongest render gate is hash equality</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-14-the-strongest-render-gate-is-hash-equality/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-14-the-strongest-render-gate-is-hash-equality/</guid><description>A refactor rerouted the WebGPU frame shell through RHI wrappers and claimed byte-identical rendering. We did not diff pixels against a tolerance — we hashed three captures and got cd3097…9b0f three times. A same-code re-run proved the noise floor was zero, upgrading the gate from diff-below-tolerance to bit equality. Determinism is a property you engineer into the harness; once you have it, verification collapses from statistics to md5sum.</description><pubDate>Tue, 14 Jul 2026 13:00:00 GMT</pubDate><category>verification</category><category>rendering</category><category>determinism</category></item><item><title>A pipe into git commit is a kill signal with a delay fuse</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-14-a-pipe-into-git-commit-is-a-kill-signal/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-14-a-pipe-into-git-commit-is-a-kill-signal/</guid><description>A sequel scar. Piping a merge commit into head -3 closed the pipe after three lines and SIGPIPE-killed lint-staged mid-run — orphaning its backup stash onto the shared stack and leaving the merge half-done. The worktrees post predicted this pile collects scars; today added one through a mechanism nobody had listed. Treat a hook-running commit stdout as a transaction log, not a stream to sample.</description><pubDate>Tue, 14 Jul 2026 12:00:00 GMT</pubDate><category>git</category><category>tooling</category><category>parallel-agents</category></item><item><title>The second ratchet: one invariant with two authorities, and a gate we could not run</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-14-the-second-ratchet/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-14-the-second-ratchet/</guid><description>An icon-padding feature grew three compiler files past their LOC ceiling. The PR raised the ratchet it knew about and went green — then CI failed on a SECOND ratchet tracking the same three files with the same numbers. A repo that preaches single-authority had let one invariant grow two authorities, and a local gate nobody knew existed is a gate nobody can run.</description><pubDate>Tue, 14 Jul 2026 11:00:00 GMT</pubDate><category>ci</category><category>invariants</category><category>tooling</category><category>single-authority</category></item><item><title>The map fossilized half-loaded: the render loop stopped because its work was hidden in a buffer it could not see</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-14-the-map-fossilized-half-loaded/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-14-the-map-fossilized-half-loaded/</guid><description>Labels decayed 6980 → 2138 → 76 → 0 pixels across four probe runs — in commit order, looking exactly like a regression gradient. Every HTTP fetch returned 200. The frame trace said all 46 labels placed. The real bug: an upload staging buffer invisible to the render-on-demand idle predicate, a deadlock that fossilized the map mid-load on any machine. One line made the buffer visible; labels went 0 → 7,081 pixels with zero interaction.</description><pubDate>Tue, 14 Jul 2026 03:00:00 GMT</pubDate><category>debugging</category><category>render-loop</category><category>scheduling</category><category>deadlock</category></item><item><title>Seven ways the harness lied to me (in one day)</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-14-seven-ways-the-harness-lied-to-me/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-14-seven-ways-the-harness-lied-to-me/</guid><description>One debugging day, seven instrument failures: a probe that double-counted its own bookkeeping into &apos;93 duplicate requests&apos;, a WebGPU canvas that reads back as all zeros, a parity test that passed by comparing two empty frames, a screenshot taken 7.5 seconds too early, and three more. Each with the tell that gave it away and the cross-check that caught it — because the same day also proved that one of those &apos;flakes&apos; was hiding a real bug.</description><pubDate>Tue, 14 Jul 2026 02:00:00 GMT</pubDate><category>testing</category><category>verification</category><category>debugging</category><category>methodology</category></item><item><title>20.7 km and 0.7 m: a difference forgives the bias its terms share</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-14-differences-forgive-shared-bias/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-14-differences-forgive-shared-bias/</guid><description>I spec&apos;d a drift guard for the globe&apos;s camera-relative ECEF offset: force the ellipsoid constant to a sphere, assert the result shifts &gt;10 km. The implementer measured it: 0.7 m. Turning the WHOLE computation spherical barely moves a relative offset, because both endpoints carry the same 21 km bias and a subtraction cancels it. The regression that actually bites is the mixed frame — ellipsoid tile, spherical camera — and only a guard that breaks the symmetry the same way sees it.</description><pubDate>Tue, 14 Jul 2026 01:00:00 GMT</pubDate><category>precision</category><category>geodesy</category><category>testing</category><category>numerical</category></item><item><title>The roof that became a hole</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-14-the-roof-that-became-a-hole/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-14-the-roof-that-became-a-hole/</guid><description>A building went missing from the 3D layer. Tile clipping had split its footprint into two pieces; the tiler flattened both outers into one rings array, and the extrusion consumer read everything after rings[0] as a hole — so piece #2 was earcut-punched out of piece #1&apos;s roof. Walls survived, roofs vanished, n pieces rendered n-1 roofs. The 2D fill was pixel-perfect the whole time, and zero tests had ever fed the consumer more than one polygon.</description><pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate><category>geometry</category><category>data-contracts</category><category>rendering</category><category>testing</category></item><item><title>The map that downloaded the world</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-13-the-map-that-downloaded-the-world/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-13-the-map-that-downloaded-the-world/</guid><description>A static map view produced 10+ MB/s bursts and a warm phone. The render loop was innocent, our first probe fabricated a &apos;93 duplicate requests&apos; finding, and the real villain was the availability feature that keeps fast-pan from blanking — politely fetching an 85-tile, 33 MB world pyramid on every load and pinning it against eviction forever. Same view after the fix: 35.33 MB → 6.27 MB.</description><pubDate>Mon, 13 Jul 2026 23:00:00 GMT</pubDate><category>performance</category><category>networking</category><category>debugging</category><category>tiles</category></item><item><title>The hemisphere that wasn&apos;t there: computed, cached, uploaded, drawn nowhere</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-13-the-hemisphere-that-wasnt-there/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-13-the-hemisphere-that-wasnt-there/</guid><description>Half the globe rendered as pure background, cut exactly at the 0-meridian z1 tile boundary. The fallback ancestors for the missing half were computed, CPU-cached, and force-uploaded to the GPU — then drawn nowhere, because a drape suppression flag was scoped to the renderer instance instead of one dispatch. Verified by making the network slow on purpose, after the first pixel readback lied.</description><pubDate>Mon, 13 Jul 2026 22:00:00 GMT</pubDate><category>rendering</category><category>debugging</category><category>state-management</category><category>verification</category></item><item><title>Three rounds to keep a label on the planet</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-13-three-rounds-to-keep-a-label-on-the-planet/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-13-three-rounds-to-keep-a-label-on-the-planet/</guid><description>A row of country labels floated in the sky above the globe&apos;s horizon. Round 1 — an angular margin, provably safe at every zoom — died because floaters and healthy labels interleave in angular depth (Nigeria 0.332 floats, Kenya 0.329 doesn&apos;t). Round 2&apos;s screen-space limb fixed Chad and missed two-line Burkina Faso by exactly one line of text. The gate only held once it moved to where the quad height actually exists.</description><pubDate>Mon, 13 Jul 2026 21:00:00 GMT</pubDate><category>labels</category><category>rendering</category><category>debugging</category><category>verification</category></item><item><title>Two renderers, one truth: all four globe bugs lived in the hand-maintained twin</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-13-two-renderers-one-truth/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-13-two-renderers-one-truth/</guid><description>A displaced vector layer, a red/blue checkerboard ocean, a 16-gon planet, and intermittent flicker — four user-visible globe defects on WebGL2, and not one root cause in shared code. Every camera rendered correctly on WebGPU. On why a hand-maintained backend twin is scaffolding that must die, and the 20.7 km test witness that keeps its seam from drifting while it lives.</description><pubDate>Mon, 13 Jul 2026 20:00:00 GMT</pubDate><category>webgl2</category><category>rendering</category><category>architecture</category><category>debugging</category></item><item><title>&quot;Touches rendering&quot; is not &quot;needs a render to verify&quot;</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-touches-rendering-is-not-needs-a-render/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-touches-rendering-is-not-needs-a-render/</guid><description>A headless session with no GPU had thirteen open issues and a hard rule against claiming a render is correct without a pixel diff. The split that worked sorted the backlog not by difficulty but by whether each fix&apos;s correctness reduces to something provable without a raster.</description><pubDate>Sat, 11 Jul 2026 18:30:00 GMT</pubDate><category>verification</category><category>process</category><category>rendering</category><category>ci</category></item><item><title>The label that lands in the ocean: a bounding-box centre is not inside your polygon</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-the-label-that-lands-in-the-ocean/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-the-label-that-lands-in-the-ocean/</guid><description>A concave country&apos;s label rendered offshore because the anchor was the bounding-box centre — a point the polygon does not contain. The fix is a guaranteed-interior point, but the harder call was shipping it from a session with no GPU to re-check the pixels.</description><pubDate>Sat, 11 Jul 2026 18:00:00 GMT</pubDate><category>geometry</category><category>rendering</category><category>labels</category><category>verification</category></item><item><title>Worktrees isolate your files, not your git state</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-git-worktrees-share-more-than-you-think/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-git-worktrees-share-more-than-you-think/</guid><description>Six agents committed in parallel from six worktrees of one repo — and every pre-commit hook stashed into the single stack they all share, finding its entry by list index. What worktrees actually share, plus the orphaned-stash scar that proves the race.</description><pubDate>Sat, 11 Jul 2026 16:00:00 GMT</pubDate><category>git</category><category>parallel-agents</category><category>tooling</category></item><item><title>A gate you have never watched fail is decoration</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-a-gate-that-cannot-fail-is-decoration/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-a-gate-that-cannot-fail-is-decoration/</guid><description>We broke instanced batching on purpose to watch a brand-new CI gate go red — 100,000 draw calls — before trusting its green. Why new gates need a demonstrated failure, and why this one asserts dc(10k) === dc(100k) instead of pinning an exact count.</description><pubDate>Sat, 11 Jul 2026 15:30:00 GMT</pubDate><category>testing</category><category>performance</category><category>ci</category></item><item><title>The 4-second timeout that lost to a 2-second GPU reset</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-the-timeout-that-lost-to-the-gpu-watchdog/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-the-timeout-that-lost-to-the-gpu-watchdog/</guid><description>Ticking &apos;include stress battery&apos; on a conformance page froze an RTX 2080 desktop so hard the window&apos;s own close button stopped responding. The df64 stress shaders over-ran the 2 s Windows TDR, and a 4 s JS compile timeout couldn&apos;t help — you can&apos;t cancel a GPU op you already submitted.</description><pubDate>Sat, 11 Jul 2026 15:00:00 GMT</pubDate><category>gpu</category><category>webgl2</category><category>angle</category><category>shader-dsl</category></item><item><title>The PR cited a CI gate that did not exist</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-the-pr-cited-a-ci-gate-that-did-not-exist/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-the-pr-cited-a-ci-gate-that-did-not-exist/</guid><description>A PR body cited a CI gate that no workflow references and a waiver clause absent from the issue it named — both written by an AI implementer, both refuted by one grep. Plus the real bug beside them, and the derived artifact a freshness gate caught.</description><pubDate>Sat, 11 Jul 2026 15:00:00 GMT</pubDate><category>code-review</category><category>ci</category><category>verification</category></item><item><title>The mathematically wrong constant survived a single-authority refactor on purpose</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-a-refactor-with-zero-byte-drift/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-a-refactor-with-zero-byte-drift/</guid><description>Routing Earth constants out of GPU shader source through one planetary Body authority, under a hard bar: emitted bytes must not change. Goldens captured from pre-change code, a literal ratchet, a 211-hit census — and a &quot;wrong&quot; eccentricity kept on purpose.</description><pubDate>Sat, 11 Jul 2026 14:30:00 GMT</pubDate><category>refactoring</category><category>verification</category><category>gpu</category></item><item><title>The issue said to move the code back</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-the-issue-said-move-the-code-back/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-the-issue-said-move-the-code-back/</guid><description>An issue prescribed relocating a context type to kill an adapter-to-adapter dependency edge. By pickup, that mechanism meant reverting a merged refactor. We cut the edge with composition-root injection instead, and locked it with a shrink-only ratchet.</description><pubDate>Sat, 11 Jul 2026 14:00:00 GMT</pubDate><category>architecture</category><category>dependencies</category><category>refactoring</category></item><item><title>You cannot delete an intrinsic discontinuity — only choose where it lives</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-choose-where-the-discontinuity-lives/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-choose-where-the-discontinuity-lives/</guid><description>Adjacent map tiles projected the same vertex 40,075 km apart — one full world circumference. A rotated projection&apos;s unavoidable branch cut, resolved per tile, scattered the seam across every tile join; the fix relocates it to the one edge geometry expects.</description><pubDate>Sat, 11 Jul 2026 13:30:00 GMT</pubDate><category>projections</category><category>rendering</category><category>geometry</category></item><item><title>Three render bugs reduced to one unsynced field in the test harness</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-three-render-bugs-one-unsynced-field/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-three-render-bugs-one-unsynced-field/</guid><description>A missing hemisphere, labels floating off the globe, and tile seams at z6.73 were filed as three engine bugs. All three were one unsynced camera field in the demo harness — and after the fix, review found the identical write one harness file over.</description><pubDate>Sat, 11 Jul 2026 13:00:00 GMT</pubDate><category>debugging</category><category>test-harness</category><category>root-cause</category></item><item><title>The pixel test that passed on the wrong GPU</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-green-on-the-wrong-gpu/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-green-on-the-wrong-gpu/</guid><description>The same render test passed twice: once at 38.5% painted pixels on real WebGPU, once at 93.6% on a silent WebGL2 fallback the assertion never noticed. Why a designed-in fallback chain turns every green output test ambiguous — until the test also pins which backend produced the frame.</description><pubDate>Sat, 11 Jul 2026 12:00:00 GMT</pubDate><category>testing</category><category>webgpu</category><category>playwright</category><category>e2e</category></item><item><title>My change was right; the proof was a red test two packages away</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-the-red-test-in-the-package-i-didnt-touch/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-the-red-test-in-the-package-i-didnt-touch/</guid><description>I edited one file in @xgis/map and ran that package&apos;s whole suite — 1246 green. Then the full monorepo run turned up two failures, both in @xgis/runtime, a package I never opened. Mid-extraction, the sufficient gate is the whole repo, not the package.</description><pubDate>Sat, 11 Jul 2026 05:40:00 GMT</pubDate><category>testing</category><category>monorepo</category><category>verification</category></item><item><title>The passing test that dimmed the wrong thing</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-the-passing-test-that-dimmed-the-wrong-thing/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-the-passing-test-that-dimmed-the-wrong-thing/</guid><description>A fix for per-feature fill opacity came with a fail-before/pass-after test: 0.25 and 0.75 baked to alpha 64 and 191, two values where there had been one. It was green and I reverted the change — because the alpha it proved feeds the outline, not the fill.</description><pubDate>Sat, 11 Jul 2026 05:20:00 GMT</pubDate><category>testing</category><category>verification</category><category>rendering</category></item><item><title>The coordinates rot before the symptom does</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-the-coordinates-rot-before-the-symptom/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-the-coordinates-rot-before-the-symptom/</guid><description>A package extraction moved the render code to a new package. It changed no behavior, so every open bug report&apos;s symptom stayed valid — and every report&apos;s file:line pointed at a path that no longer existed. The precise-looking part of a report rots first.</description><pubDate>Sat, 11 Jul 2026 05:00:00 GMT</pubDate><category>architecture</category><category>refactoring</category><category>triage</category></item><item><title>The branch a comment left UNVERIFIED diverged 6×</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-the-branch-a-comment-left-unverified/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-the-branch-a-comment-left-unverified/</guid><description>A single-authority migration for on-screen scale left the globe branch returning the raw, uncapped value under a comment reading &apos;UNVERIFIED&apos;. A sibling branch of identical shape was a non-bug — and only a probe of which value is read told them apart.</description><pubDate>Sat, 11 Jul 2026 04:40:00 GMT</pubDate><category>rendering</category><category>verification</category><category>numerical</category></item><item><title>The cause was refuted, the fix was already reverted</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-the-cause-was-refuted-the-fix-was-already-reverted/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-the-cause-was-refuted-the-fix-was-already-reverted/</guid><description>A bug report for one-way arrows drifting off the road came with a diagnosis and a one-line fix. The diagnosis was refuted by reading the anchor code, and the proposed fix was a revert of a change the repo had already tried and backed out. The real fix was neither.</description><pubDate>Sat, 11 Jul 2026 04:20:00 GMT</pubDate><category>debugging</category><category>rendering</category><category>methodology</category></item><item><title>The precision fix that opened a seam</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-the-precision-fix-that-opened-a-seam/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-the-precision-fix-that-opened-a-seam/</guid><description>Making the line outline&apos;s projection precise, but not its polygon fill, turned a shared invisible jitter into a ~3px fill≠outline seam at deep zoom. The fix isn&apos;t more f64 — it&apos;s the same camera-relative reframe, which the linear term needs no emulation for.</description><pubDate>Sat, 11 Jul 2026 04:00:00 GMT</pubDate><category>precision</category><category>rendering</category><category>shader-dsl</category><category>numerical</category></item><item><title>The cull we didn&apos;t dare add was already safe</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-the-cull-we-didnt-dare-add-was-already-safe/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-the-cull-we-didnt-dare-add-was-already-safe/</guid><description>Two engines rendered the same style differently because one honoured a metadata field the other deliberately skipped. The comment defending the skip described a danger an existing clamp had already neutralized — and the &quot;working&quot; half of the cull turned out to be silently keyed past its own lookup.</description><pubDate>Sat, 11 Jul 2026 01:30:00 GMT</pubDate><category>metadata</category><category>parity</category><category>rendering</category></item><item><title>Reconstructing the absolute coordinate was the bug</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-reconstructing-the-absolute-coordinate-was-the-bug/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-reconstructing-the-absolute-coordinate-was-the-bug/</guid><description>Line strokes shook at deep zoom in non-Mercator projections: the shader rebuilt an absolute longitude in f32 degrees, then a later stage subtracted the camera again — catastrophic cancellation. The fix subtracts first; the proof needs no GPU.</description><pubDate>Sat, 11 Jul 2026 01:20:00 GMT</pubDate><category>precision</category><category>shaders</category><category>verification</category></item><item><title>The migration regressed one renderer at a time</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-the-migration-regressed-one-renderer-at-a-time/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-the-migration-regressed-one-renderer-at-a-time/</guid><description>A coordinate-space migration carried one assumption — &quot;absolute geometry needs no per-copy shift&quot; — that was true for the globe and false for flat maps. Applied per-renderer, it silently dropped world-copy fan-out path by path. The fix is one ten-line router.</description><pubDate>Sat, 11 Jul 2026 01:10:00 GMT</pubDate><category>architecture</category><category>refactoring</category><category>rendering</category></item><item><title>The winner was whoever loaded last</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-11-the-winner-was-whoever-loaded-last/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-11-the-winner-was-whoever-loaded-last/</guid><description>Two overlapping map labels swapped survivors on pan because the collision pass&apos;s tie-break was tile-dispatch order — reversed. The fix is a stable identity fed to the greedy allocator, and the gate is a permutation test.</description><pubDate>Sat, 11 Jul 2026 01:00:00 GMT</pubDate><category>determinism</category><category>rendering</category><category>testing</category></item><item><title>When the bug is already fixed, the test still isn&apos;t</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-10-when-the-bug-is-already-fixed/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-10-when-the-bug-is-already-fixed/</guid><description>We adjudicated eight open bug reports in one batch. Two were already fixed on main and one&apos;s premise was refuted by a write-only uniform lane. In every stale case the deliverable that survived was a regression test, not a patch.</description><pubDate>Fri, 10 Jul 2026 23:40:00 GMT</pubDate><category>triage</category><category>testing</category><category>verification</category></item><item><title>The error term you compute in integers</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-10-the-error-term-you-compute-in-integers/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-10-the-error-term-you-compute-in-integers/</guid><description>Fast-math deleted our emulated-double multiply on Apple GPUs, and every float-side guard — including hardware FMA — died with it. So we rebuilt twoSum and twoProd in u32 bit arithmetic: same pair format, same compositions, exact by construction. The device that zeroed every float variant returned the golden answer.</description><pubDate>Fri, 10 Jul 2026 12:00:00 GMT</pubDate><category>precision</category><category>gpu-drivers</category><category>numerical</category><category>compilers</category></item><item><title>The boundary audit missed an edge because a regex ate a digit</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-10-the-audit-grep-that-ate-a-digit/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-10-the-audit-grep-that-ate-a-digit/</guid><description>A hand audit of a 16-workspace import graph concluded the backend adapters were clean. It was wrong twice — once because `[a-z-]` doesn&apos;t match a `2`, once because `from &apos;…&apos;` can&apos;t see a dynamic import. The 166-line CI gate built FROM that audit found the missed edge on its first run.</description><pubDate>Fri, 10 Jul 2026 03:30:00 GMT</pubDate><category>architecture</category><category>ci</category><category>tooling</category><category>methodology</category></item><item><title>Extended precision on the GPU: a field guide</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-09-gpu-extended-precision-field-guide/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-09-gpu-extended-precision-field-guide/</guid><description>The GPU has no double. There are four ways to get one anyway — double-float error-free transforms, integer emulation, reference-point rendering, and perturbation — and they are not interchangeable. A decision guide, from a survey of Thall, QD/CAMPARY, Cesium, deck.gl, and fractal deep-zoom engines.</description><pubDate>Thu, 09 Jul 2026 22:00:00 GMT</pubDate><category>precision</category><category>numerical</category><category>gpu-drivers</category><category>rendering</category></item><item><title>The multiply you cannot guard</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-09-the-multiply-you-cannot-guard/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-09-the-multiply-you-cannot-guard/</guid><description>I shipped four in-shader fixes for the df64 multiply collapsing to zero on an Apple GPU. Every one failed on-device. Then the research settled it: Metal&apos;s default fast-math deletes the error term by construction, no opaque-guard trick reliably survives, and the durable answer is to restructure the math so the high-precision multiply never happens — which is why deck.gl deprecated emulated fp64.</description><pubDate>Thu, 09 Jul 2026 20:00:00 GMT</pubDate><category>precision</category><category>gpu-drivers</category><category>numerical</category><category>compilers</category></item><item><title>add held, mul collapsed, and the difference was one renormalize</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-09-add-held-mul-collapsed/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-09-add-held-mul-collapsed/</guid><description>The emulated-double add and multiply are the same algorithm family, but on an Apple GPU add read 0.5 and multiply read exactly 0. The bug was not in either — it was a renormalization step the textbook multiply omits and the battle-tested one keeps.</description><pubDate>Thu, 09 Jul 2026 16:00:00 GMT</pubDate><category>precision</category><category>gpu-drivers</category><category>numerical</category><category>compilers</category></item><item><title>The low word a load throws away</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-09-the-low-word-a-load-throws-away/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-09-the-low-word-a-load-throws-away/</guid><description>The same df64 value—1e8+0.5—minus 1e8. Computed on the GPU it reads 0.5; loaded from a uniform it reads 0.0, with the fast-math guard bound in both. To a reassociating compiler a loaded low word and a computed one are not the same number, and the fix is to launder one into the other.</description><pubDate>Thu, 09 Jul 2026 12:00:00 GMT</pubDate><category>precision</category><category>gpu-drivers</category><category>numerical</category><category>compilers</category></item><item><title>The split constant was never the bug</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-08-the-split-constant-was-never-the-bug/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-08-the-split-constant-was-never-the-bug/</guid><description>GPU double-single splits floats with 8193; the textbook says 4097. I was sure 8193 was cargo-culted from a wider type — until two million float32 products came back error-free for both. The split constant that breaks is one too small; the real bug was never the split.</description><pubDate>Wed, 08 Jul 2026 23:15:00 GMT</pubDate><category>floating-point</category><category>precision</category><category>gpu</category><category>verification</category></item><item><title>The unused binding that silently dropped the draw</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-08-the-unused-binding-that-dropped-the-draw/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-08-the-unused-binding-that-dropped-the-draw/</guid><description>A shader drew correctly on one GPU and returned the clear colour on another — not precision, but layout:auto deleting a binding it declared but never read. When an interface is derived from usage, an unused declaration is a mismatch only the strict backend reveals.</description><pubDate>Wed, 08 Jul 2026 22:30:00 GMT</pubDate><category>webgpu</category><category>graphics</category><category>api-design</category><category>debugging</category></item><item><title>A skipped matrix job never posts the checks you required</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-08-required-check-never-ran/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-08-required-check-never-ran/</guid><description>A one-file docs PR sat on &quot;Expected — waiting for status to be reported&quot; with no checks coming. Two GitHub mechanisms hid required checks: a paths filter that skipped the whole workflow, and a matrix job skipped at the job level — evaluated before the matrix expands, so its per-leg contexts never post. Why &quot;skipped&quot; is not &quot;never ran.&quot;</description><pubDate>Wed, 08 Jul 2026 20:00:00 GMT</pubDate><category>ci</category><category>github-actions</category><category>branch-protection</category></item><item><title>An opaque guard defeats the compiler, not the hardware</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-08-an-opaque-guard-cannot-fix-a-lossy-multiplier/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-08-an-opaque-guard-cannot-fix-a-lossy-multiplier/</guid><description>After a texel-fetched guard fixed the fast-math collapse, isolated df64 ops passed on the reporter&apos;s phone but composed ones still broke. There are two failure classes, not one — and the second lives in the ALU, where no value you thread into the shader can reach it. df64 is per-vendor, and our CI&apos;s SwiftShader is structurally blind to half of it.</description><pubDate>Wed, 08 Jul 2026 10:00:00 GMT</pubDate><category>shader-dsl</category><category>precision</category><category>gpu-drivers</category><category>numerical</category></item><item><title>Your render pipeline is just a string</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-08-your-pipeline-is-a-string/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-08-your-pipeline-is-a-string/</guid><description>217 GPURenderPipeline references stood between @xgis/map and backend-neutrality. Most of them turned out not to be pipelines at all — they were labels being matched. Collapsing them to a neutral { label } handle, and how prove-or-refute kept us from doing the wrong migration.</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate><category>architecture</category><category>rhi</category><category>refactoring</category><category>types</category></item><item><title>Swapping the command encoder without stopping the frame</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-08-swapping-the-encoder-mid-frame/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-08-swapping-the-encoder-mid-frame/</guid><description>Moving map&apos;s render passes behind the RHI one at a time meant two encoders — native and RHI — had to share a single frame and a single submit. Why createCommandEncoder was the wrong tool, and the wrapper that let a native encoder be driven through the RHI interface for one pass at a time.</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate><category>rhi</category><category>architecture</category><category>webgpu</category><category>refactoring</category></item><item><title>Slicing a 700-reference coupling into moves you can verify</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-08-slicing-a-700-reference-coupling/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-08-slicing-a-700-reference-coupling/</guid><description>@xgis/map named backend types in ~700 places. You cannot cut that in one commit and stay honest. The method: sort every reference into layers by how it&apos;s actually used, find the one clean move, and prove each slice compiles to identical bytes before the next.</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate><category>architecture</category><category>refactoring</category><category>methodology</category><category>rhi</category></item><item><title>Sharing a branch with an agent that commits while you are offline</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-08-recovering-lost-work-across-machines/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-08-recovering-lost-work-across-machines/</guid><description>A refactor step I left uncommitted on one machine had already been authored and pushed to the shared branch by an autonomous agent, under a different SHA. Why &apos;is it done?&apos; on an agent-shared branch is a distributed-consensus question — and why, before redoing anything, you fetch and read the agent&apos;s commits first.</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate><category>git</category><category>ci</category><category>agents</category><category>workflow</category><category>ops</category></item><item><title>Your bundler minifies everything except your shaders</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-08-minifying-and-mangling-shaders/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-08-minifying-and-mangling-shaders/</guid><description>Emitted WGSL/GLSL ships to gl.shaderSource with its authored vocabulary intact — no JS minifier reaches it. A Vite/Webpack-style emit-plugin pass (mangle + minify) that compacts and obfuscates the shader text, the ABI boundary it must never cross, and why compile-and-link is not enough to trust it.</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate><category>shader-dsl</category><category>compiler</category><category>tooling</category><category>webgpu</category></item><item><title>Where a type is imported from is not where the dependency lives</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-08-import-source-is-not-dependency/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-08-import-source-is-not-dependency/</guid><description>A post-mortem on a refactor that went the wrong way. To strip @webgpu/types from @xgis/map we re-exported the WebGPU types through a barrel — and made the coupling worse, not better. The revert, and the rule that would have caught it up front.</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate><category>architecture</category><category>refactoring</category><category>dependencies</category><category>rhi</category></item><item><title>The import edges grep cannot see</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-08-import-edges-grep-cannot-see/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-08-import-edges-grep-cannot-see/</guid><description>Relocating a module is editing the dependency graph, and text search sees only some of its edges. It misses dynamic imports and re-exports, and a regex-dialect gap can make a guard silently lie. Grep narrows; the compiler decides.</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate><category>refactoring</category><category>tooling</category><category>typescript</category><category>methodology</category></item><item><title>The no-op that hid a hundred fences</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-08-fail-loud-stub/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-08-fail-loud-stub/</guid><description>A recursive no-op Proxy let a WebGPU-typed engine boot on WebGL2 by making every native GPU call return a harmless dummy. It also silently swallowed every place a fence was missing. Replacing it with a fail-loud stub — and what the stub screamed about.</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate><category>webgl2</category><category>architecture</category><category>debugging</category><category>rhi</category></item><item><title>Adding a shader-compiler pass without touching the compiler</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-08-a-plugin-seam-for-shader-emit/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-08-a-plugin-seam-for-shader-emit/</guid><description>The emit path of @xgis/shader-dsl grew a Vite/Webpack-style plugin seam so ship-time transforms compose without weighing on the core — and a build-only subpath so runtime-emit consumers bundle zero bytes of them. The test: dropping in a third transform (call-graph inlining) that touched no core line.</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate><category>shader-dsl</category><category>compiler</category><category>architecture</category><category>tooling</category></item><item><title>Testing emulated doubles</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-07-testing-emulated-doubles/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-07-testing-emulated-doubles/</guid><description>The verification stack behind shader-dsl&apos;s fp64: a correctly-rounding f32 machine built from Math.fround, a metamorphic oracle gate, byte goldens, and discriminative real-GPU known answers.</description><pubDate>Tue, 07 Jul 2026 06:58:00 GMT</pubDate><category>shader-dsl</category><category>testing</category><category>precision</category><category>numerics</category></item><item><title>The flickering Mandelbrot: a driver defeats the fast-math guard</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-07-the-flickering-mandelbrot/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-07-the-flickering-mandelbrot/</guid><description>Incident report: the fp64 demo alternated between correct and f32-collapsed frames under byte-identical inputs. Inferred cause: driver pipeline re-optimization specializing on uniform values. Fixes: render-on-demand and a texel-fetched guard.</description><pubDate>Tue, 07 Jul 2026 06:38:00 GMT</pubDate><category>shader-dsl</category><category>precision</category><category>debugging</category><category>gpu-drivers</category></item><item><title>Emulated double precision in the shader DSL</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-07-emulated-double-precision-shader-dsl/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-07-emulated-double-precision-shader-dsl/</guid><description>f64 and vecN&lt;f64&gt; as first-class shader-dsl types, emulated as two-f32 double-float pairs — same authoring syntax as f32, one lowering pass, ~48 significand bits.</description><pubDate>Tue, 07 Jul 2026 03:32:00 GMT</pubDate><category>shader-dsl</category><category>precision</category><category>compiler</category><category>webgpu</category></item><item><title>Porting the ShaderToy classics — and letting them review our shader DSL</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-07-porting-the-shadertoy-classics/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-07-porting-the-shadertoy-classics/</guid><description>We rebuilt ten classic ShaderToy effects in X-GIS&apos;s typed shader DSL. The renders were the easy part: authoring them surfaced eleven concrete DX issues — an elliptical sun no gate could see, a reversed smoothstep every GPU hides — each closed with a gate so it stays fixed.</description><pubDate>Tue, 07 Jul 2026 02:29:00 GMT</pubDate><category>shader-dsl</category><category>examples</category><category>dx</category><category>testing</category></item><item><title>What a software GPU can and cannot verify</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-07-what-a-software-gpu-can-verify/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-07-what-a-software-gpu-can-verify/</guid><description>Our CI runs real shaders on SwiftShader — but only some render claims survive a CPU rasterizer. The dividing line we use (compute parity and shader compilation: yes; full-pipeline pixel parity: no), and the red-baseline incident that taught us to blame the software GPU last.</description><pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate><category>testing</category><category>ci</category><category>webgpu</category><category>gpu-drivers</category></item><item><title>All 2,592 tests passed and the job failed: a vitest sharding playbook</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-07-vitest-sharding-playbook/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-07-vitest-sharding-playbook/</guid><description>Three rounds of CI failures where every test was green and the job was red — vitest&apos;s worker→main reporter RPC timing out under file-count pressure. The ~110-file floor we split on (set below the lowest count we watched fail), the matrix design that contains it, and the playbook for the next regrowth.</description><pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate><category>testing</category><category>ci</category><category>vitest</category><category>infrastructure</category></item><item><title>The .xgis compiler pipeline: from style text to GPU programs</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-07-the-xgis-compiler-pipeline/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-07-the-xgis-compiler-pipeline/</guid><description>How a declarative map style becomes GPU work: lexer → parser → a Scene IR → optimization passes → codegen that emits typed shader-dsl IR (never WGSL strings), per-feature compute kernels for match(), and a gradient atlas for zoom-dependent paint.</description><pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate><category>compiler</category><category>engine</category><category>webgpu</category><category>shader-dsl</category></item><item><title>Porting a WebGPU-first engine to WebGL2</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-07-porting-the-engine-to-webgl2/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-07-porting-the-engine-to-webgl2/</guid><description>The program index for X-GIS&apos;s WebGL2 port: package-enforced backend neutrality, a single shader IR emitting both WGSL and GLSL, and pixel gates instead of promises — with links to the per-subsystem deep-dives. Shared shader math still can&apos;t stop pipeline state and target conventions from diverging.</description><pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate><category>webgl2</category><category>rhi</category><category>shader-dsl</category><category>architecture</category></item><item><title>Pixels don&apos;t lie: debugging a dual-backend renderer</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-07-pixels-dont-lie/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-07-pixels-dont-lie/</guid><description>Three real bugs from making WebGL2 match WebGPU on a 117-layer basemap: a dedup map nobody cleared, a depth jitter that out-voted painter&apos;s order, and a vertically mirrored composite hidden by a symmetric test fixture.</description><pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate><category>webgl2</category><category>debugging</category><category>testing</category><category>rendering</category></item><item><title>Offline tile mirrors for deterministic map e2e</title><link>https://x-gis.github.io/X-GIS/blog/2026-07-07-offline-tile-mirror-e2e/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-07-07-offline-tile-mirror-e2e/</guid><description>Our CI container&apos;s browser can&apos;t reach the internet, but the final render gate needs a real 117-layer basemap. On mirroring styles, tiles, glyphs and sprites locally — and the traps: SPA fallback serving HTML as tiles, unencoded fontstacks, and settling on signals instead of sleeps.</description><pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate><category>testing</category><category>e2e</category><category>infrastructure</category><category>tiles</category></item><item><title>Why we built a GPU-first map engine</title><link>https://x-gis.github.io/X-GIS/blog/2026-06-26-why-a-gpu-first-map-engine/</link><guid isPermaLink="true">https://x-gis.github.io/X-GIS/blog/2026-06-26-why-a-gpu-first-map-engine/</guid><description>X-GIS compiles a declarative style language into optimized WebGPU shaders — through a real compiler and a typed shader IR. Here&apos;s the shape of the system and why each layer exists.</description><pubDate>Fri, 26 Jun 2026 00:00:00 GMT</pubDate><category>engine</category><category>webgpu</category><category>compiler</category><category>intro</category></item></channel></rss>