Skip to content

Gotchas

Nick Hamze edited this page May 19, 2026 · 3 revisions

Gotchas

Prior incidents and sharp edges, documented so we don't re-learn them.

SVG control bytes in <title>

b-roll-icons v0.1.0 shipped a \x14 inside an em-dash embedded in a <title> element. Browsers refused to parse the SVG, the dock filter silently fell back to the default icon, and users saw a broken set for a release cycle.

The icon-set validator explicitly scans every SVG body for bytes < 0x20 outside \t\n\r and fails the build. Never paste SVG text from a word processor, terminal capture, or AI chat without running it through the validator.

Client-side icon live-swap is a rabbit hole

Early versions of b-roll-icons tried to re-skin the dock from JavaScript after a POST, reading data-menu-slug off dock DOM nodes. That attribute is the sanitized CSS ID (e.g. menu-posts), not the raw admin menu slug (edit.php) — the keys ship side never lined up with what the filter side expected.

The v0.1.4 fix was going server-canonical: wp_desktop_dock_item + wp_desktop_icons filters in PHP at priority 20, plus a 180 ms fade + window.location.reload() after the POST. Don't regress back to JS DOM surgery.

See Architecture → Icon swaps for the canonical mapping.

GitHub release asset upload 409

gh release create ... some.zip occasionally returns 409 Error creating policy immediately after the release is created. The cause appears to be a GitHub-side race between release creation and asset upload permissions propagating.

release-odd.yml retries the upload once after a 3-second sleep. If both attempts fail, re-run the failed job from the Actions UI — the retry is almost always green.

Playground service worker caches plugin ZIPs by URL

Playground caches plugin downloads by request URL. ODD's stable Playground therefore installs exact WordPress.org package zips and appends an oddbp cache key to the hosted blueprint URL. Avoid moving /releases/latest/ download URLs in front of the stable demo; they are convenient for humans but fragile for repeat Playground visits.

Fix: the ?v=<x.y.z> cache-buster on the ODD ZIP URL in blueprint.json. GitHub's redirect ignores query strings (confirmed: odd.zip?v=0.13.3 still 302s to /releases/download/v0.13.3/odd.zip), so the "latest" plumbing keeps working, but the SW sees a new URL string on each release and misses cache.

Automated: odd/bin/bump-version <x.y.z> updates blueprint.json's ?v= alongside the two odd.php version strings. odd/bin/check-version fails if any of the three drift, and the pre-commit hook runs it when any of those files are staged.

Playground + CORS

Blueprints resolve URLs from Playground's origin, so every external asset must serve with access-control-allow-origin: *. In practice:

  • raw.githubusercontent.com — ✅ *
  • github.com/*/releases/download/... — ✅ *
  • Most other hosts — ❌

Check before pointing a blueprint at a new URL:

curl -H "Origin: https://playground.wordpress.net" -I <url>

wp-desktop.wallpaper.visibility payload shape

The payload is { id, state: 'hidden' | 'visible' } per the recipe example in WP Desktop Mode's source — not documented in the public API reference. The onVis handler in src/wallpaper/index.js silently no-ops on anything else, so if visibility starts behaving weirdly after a WP Desktop update, check whether that payload changed.

Pixi v8 ticker.deltaTime after backgrounded tabs

When a tab is backgrounded and restored, ticker.deltaTime can be arbitrarily large — easily 100+ on the first post-resume frame. Unclamped, scenes fast-forward through minutes of motion in one step, blowing through requestAnimationFrame integration and occasionally crashing particle-heavy scenes with NaN positions.

The shared mount runner in src/wallpaper/index.js clamps env.dt to 2.5 before handing it to scene tick callbacks. Scenes should use env.dt, not read ticker.deltaTime directly.

Pixi v8 constructor-options trap

new PIXI.Application({ ... }) with v7-style options silently no-ops on v8 — app.canvas ends up undefined and nothing paints. The v8 way is:

const app = new PIXI.Application();
await app.init( { /* options here */ } );

Every ODD scene file is already written this way; copy an existing scene as a template.

Plugin URI must point at the canonical repo

Plugin URI: in odd/odd.php and the repo URL in every docs file should point at RegionallyFamous/odd. GitHub keeps a permanent redirect from the old RegionallyFamous/b-roll so historical Playground links keep working — but new content should use the canonical URL directly.

@priority 20 on the dock + desktop-icon filters

wp_desktop_dock_item + wp_desktop_icons run at priority 20 so any other plugin hooking the same filter at the default priority 10 goes first, and ODD's icon set applies as the last word. Don't lower the priority without a reason — you'll let third-party plugins overwrite ODD's rendered dock.

Activation-time migration must stay idempotent

includes/migrate.php runs on every plugin activation (including after auto-updates). It copies b_roll_* user_meta keys into odd_* non-destructively — the destination check uses get_user_meta and only writes if the target is empty. Do not introduce a condition that could overwrite a user's existing odd_* pref on re-run.

Clone this wiki locally