Scene Simulator v0.3.2
Scene Simulator v0.3.2 — Worklet Re-bundle for Electron 42 + Dist Orchestrator + Per-pack Smoke Matrix
Release date: 2026-07-09
Compare: v0.3.1...v0.3.2
Commits on this tag: 1 (the chore(release): cut v0.3.2 version-bump commit containing 14 grouped infra + catalog changes against the v0.3.1 baseline, ~+24k/-1k lines across 98 files).
This is a maintenance release resolving the v0.3.0 → v0.3.1 AudioWorklet chain breakage under Electron 42, replacing the v0.3.0 custom-privileged worklet:// scheme with a Vite-served bundled asset, introducing a single canonical scripts/dist.mjs production-build orchestrator, expanding npm run test:all from a 7-step chain to a 24-step chain (17 new per-pack smoke runners), and tightening the type / catalog / settings-schema plumbing.
Release notes mirror the
[0.3.2]section ofCHANGELOG.md. SeeCHANGELOG.mdfor the canonical source.
Highlights
- Tracker-music worklet loads under Electron 42 again — the v0.3.0 shipping path registered a custom privileged
worklet://scheme and servednode_modules/chiptune3/chiptune3.worklet.js+libopenmpt.worklet.jsthroughprotocol.handle. Electron 42's Chromium rejects the staticimport './libopenmpt.worklet.js'chain with "Unable to load a worklets module" even after the v0.3.1Content-Typepatch. v0.3.2 abandons that plumbing entirely;scripts/bundle-worklet.mjsconcatenates both files into a singlepublic/worklets/openmpt.bundled.worklet.jsasset shipped through Vite's regular asset pipeline, served at the document's same-origin URL. - Single canonical production-build pipeline —
scripts/dist.mjsorchestratesnpm run build:allfollowed byelectron-builder --win --x64(or--dir) and copies the resulting NSIS + portable artifacts pluslatest.yml/win-unpacked/blockmap outputs into the project's localrelease/folder.npm run dist:winandnpm run dist:dirroute through this script instead of invokingelectron-builderdirectly, so contributor builds have one canonical log path and a single place to thread future revisions (codesigning, S3 upload). npm run test:allis now a 24-step chain — 17 granular per-pack smoke runners (test:effect-unlocks,test:scoring,test:judging-profiles,test:artistic-directions,test:effect-synergies,test:hardware-catalog,test:sponsorship-catalog,test:technology-tree,test:software-catalog,test:demo-effects,test:initial-npcs,test:initial-groups,test:party-calendar,test:job-templates,test:bbs-messages,test:rival-releases,test:platforms) join the existing 7 (smoke+audit-docs+load-during-import+economics+replay+migration+music), each pinning an invariant specific to its catalogue and independently runnable.
What's New
No new user-visible features. The v0.3.1 → v0.3.2 transition is a patch release rolled up from a long stretch of infra / type / catalogue work that shipped without a dedicated tag.
What Changed
npm run cleanrewritten for Windows-safe recursive delete — the oldrm -rf dist server.js dist-electronfailed on Windows; v0.3.2 swaps tonode -e "['dist', 'server.js', 'dist-electron', 'public/worklets', 'release'].forEach(p => require('fs').rmSync(p, {recursive:true,force:true}))", so the warm-rebuild path now also picks uppublic/worklets/andrelease/artefacts without POSIXrm -rfassumptions.PartyEvent.yeartyped anchor —packages/types/src/party.ts::PartyEventgained ayear: numberfield alongside the existingmonth;sim/data/partyCalendar.tswrites the actual year every party ran (Copy Party 1989,Venlo Meeting 1987,The Gathering 1992,Twilight Zone 1992,Assembly Summer 1992,Sun Demoparty 1992,Mekka & Symposium 1992, …) on every entry. Projections can filter "upcoming parties in year Y" without a fragile month-only heuristic.- Explicit settings-schema v1→v2 migration branch —
electron/settings.ts::readSettingsnow contains a dedicatedparsed.schemaVersion === 1branch that lifts the legacygeminiApiKeyand seeds an emptymusic.playlist. Pre-v0.3.2 settings on first launch fell through to{ ...DEFAULTS }and would have silently dropped a user's persisted Gemini key on every subsequent read until the user re-entered it. CompanionnormaliseMusic(raw)helper now sanitises every playlist entry field-by-field (storedName/displayName/formatenum /sizenumber). - Extended DEMO_EFFECTS catalogue to JSON-parity —
sim/data/demoEffects.tsgained a "JSON-only catalogue" section with 23 additional entries (color_cycling,interlace_flicker,dither_gradient_sky,roto_zoomer,copper_sprite_multiplex,dual_playfield_parallax,blitter_zoomsprite,wireframe_flythrough,rotating_logo,particle_system,l_system_plants,perlin_noise_clouds,chromatic_aberration,bump_mapped_torus,environment_mapping,boids_flocking,morphing_mesh,domain_warp_field,reaction_diffusion,raytraced_spheres,volumetric_fog,voxel_city,realtime_vocoder) that were always present indata/effects.jsonbut missing from the original hand-maintained TypeScript catalogue. Each carries the v0.3.0 metadata (complexity/visualImpact/compatiblePlatforms/synergyTags/researchRequired). JOB_TEMPLATESexplicitrequiresCrewSkilltags — every template insim/data/jobTemplates.tsnow declaresrequiresCrewSkill: "coding" | "graphics" | "music"so the projection's job-board filter is skill-based instead of description-string matching. Split across the 12 entries:2× music(job_8bit_chiptuneset,job_16bit_trackerpack),4× graphics(job_8bit_loadscreen,job_16bit_introart,job_pc_shareware_pack,job_modern_introfilm),6× coding(the rest — notejob_modern_synthsizeistype: "tool_contract"notmusic_commission, so correctly tagging itcodingmatches its "build a byte-sized synth library" description).sim/data/index.tsbarrel gains BBS + effect-unlocks exports — the existing catalogue barrel now exportsBBS_BOARDS/BBS_SCRIBES/SYSOP_REPLIES/SYSOP_MODERATION_MESSAGES/ERA_TOPICS/SPYLINE_TEMPLATES/BBS_RANDOM_EVENTS/BBS_MUTATIONS/VOICE_PROFILES/CATEGORY_MESSAGES/BBS_PERSONALITIES/getSeedThreads/getEra/generateFollowedReply/generatePersonalityMessage/colorForHandlefrombbsMessages, theBBSBoard/Era/SpylineTemplate/BBSRandomEvent/BBSCategory/BBSPersonalitytypes, and theeffectSynergies/artisticDirections/effectUnlocks/judgingProfilesexports that the data-migration smoke test relies on. Importers can now read any catalog through@sim/datawithout reaching into a subpath.sim/domain/index.tsscoring-barrel export — addedexport * from "./scoring"sosim/domain/scoring.ts'sscoreProduction/scoreBreakdownFor/competeAgainstJudgingProfile/ScoreBreakdownhelpers surface through the documentedsim/domainbarrel that other layers import.sim/data/softwareCatalog.tsPhotoshop 5 LEeffectUnlockscorrected — the legacyprocedural_texturesid has been renamed todomain_warp_fieldso the purchased-tool unlock matches the matching entry in the new DEMO_EFFECTS JSON-only catalogue. The previous id referenced aprocedural_textureseffect that was never added, so a player who bought Photoshop 5 LE onPC_PENTIUM_IIin 1998 would have surfaced zero unlocks in the studio.- Docs / repo metadata de-provenanced —
README.mdremoved theInitial project started with Google AI studio, further development made locally with other AI tools.attribution line (the project now self-describes its hybrid simulation + narrative stack without referencing the bootstrap environment);docs/architecture.mdapps/server/row wording now reads*(reserved — Express server for hosted/headless deployment parity)*(the legacyAI Studio parityphrasing is gone — the project does not run on Cloud Run);.env.exampledocuments both halves of the Electron+Vite split (GEMINI_API_KEYexplains the first-run Electron prompt vs. the Vite dev export;APP_URLis now described as an optional self-referential / OAuth / API endpoint override rather than an AI-Studio-injected Cloud Run URL);index.html<title>is nowDemoscene Simulator(was the AI-Studio default"My Google AI Studio App");.gitignoreadds an inline# electron-builder output (NSIS installers, portable EXEs, blockmaps, latest.yml, win-unpacked/)comment above therelease/ignore line.
What Was Removed
worklet://custom-privileged scheme plumbing inelectron/main.ts— theprotocol.registerSchemesAsPrivileged([{ scheme: 'worklet', privileges: {...} }])call that ran beforeapp.whenReady(), theprotocol.handle('worklet', …)handler that mappedworklet://→userData/worklets/, and the per-launchmusic:init-workletIPC handler are all gone.getWorkletUrlpreload surface —electron/preload.ts'selectronAPIno longer exposes agetWorkletUrl()method. The renderer'ssrc/audio/trackerPlayer.ts::init()now references the statically-known/worklets/openmpt.bundled.worklet.jsURL against the document origin; any stale caller would tripTypeError: ... is not a function.build.extraResourceschiptune3 entries inpackage.json— the two array entries that copiednode_modules/chiptune3/chiptune3.worklet.jsandlibopenmpt.worklet.jsintoprocess.resourcesPath/worklets/for the packaged build are removed. The renderer no longer reads fromresourcesPath/worklets/; it readsdist/worklets/openmpt.bundled.worklet.jsas a normal Vite-served asset. Reduces the installer footprint by ~50KB and removes the devDep-on-prod-runtime coupling.
Bug Fixes
- Tracker-music worklet fails to load in Electron 42 (
addModule()"Unable to load a worklets module") — see Highlights. Root cause: Electron 42's Chromium Worklet processor fetch happens under a stricter CORS/Worker-subresource policy thanprotocol.handleimplements, so the v0.3.0worklet://chptune3.worklet.jsstatic-import chain was rejected even after the v0.3.1Content-Type: text/javascriptpatch. Fix: ship the worklet through Vite as a same-origin Vite-served asset. node:cryptoRollup resolve failure onnpm run build:electron— root cause:electron/main.ts's SHA-256 helper was being routed through Vite's__vite-browser-externalstub when bundling the host. Fix: explicitnode:cryptoRollup external.
Upgrade Notes
There is no installer / binary distribution for v0.3.2 — this is a code-only patch.
For end-users: if you are currently running the v0.3.0 or v0.3.1 packaged binary, the v0.3.2 music-player fix (the bundled worklet) and the v0.3.2 settings-schema fix are observable:
- Music player — the v0.3.0/v0.3.1 packaged binary shipped with the broken
worklet://scheme on Electron 42; imported.mod/.xm/.it/.s3mfiles would either fail to load or work intermittently. The v0.3.2 installer (next feature-drop release) bundles the same-origin worklet asset and resolves this. - Settings persistence — the v0.3.0 settings file auto-migrated to the v0.3.1 read with a silent
geminiApiKeydrop on every subsequent read once themusic.playlistblock appeared butschemaVersionwas still1. The v0.3.2 installer lifts the legacy key cleanly. If your previously-entered Gemini key vanished after the v0.3.0 → v0.3.1 upgrade, you'll need to re-enter it once via the API-key gate.
Wait for the next feature-drop release (v0.4.0) for a packaged installer with all v0.3.x fixes bundled.
For contributors: simply git pull (or git checkout v0.3.2) / npm install. The new mid-flight compileIntervalRef + partyVoteIntervalRef pattern from v0.3.1 is unchanged; the v0.3.2 changes are infra / type / catalogue work that does not touch the renderer or the simulation loop. Use the new npm run dist:win (which now routes through scripts/dist.mjs) and the new npm run test:all 24-step chain to verify before pushing.
Verification
npm run lint(tsc --noEmit): exit 0 on the staged v0.3.2 treenpm run test:all: smoke + audit-docs + load-during-import + economics + replay + migration + music (existing 7) + the 17 new per-pack runners (effect-unlocks / scoring / judging-profiles / artistic-directions / effect-synergies / hardware-catalog / sponsorship-catalog / technology-tree / software-catalog / demo-effects / initial-npcs / initial-groups / party-calendar / job-templates / bbs-messages / rival-releases / platforms) — 24-step chain expected greennpm run audit:docs: doc/sim parity gate greennpm run build(renderer + host via the wrappednpm run build:all): cleannpm run bundle:worklet: buildspublic/worklets/openmpt.bundled.worklet.jsfromnode_modules/chiptune3/*(renaminglibopenmpt→libopenmptPromiseand stripping static imports)npm run dev:electron: Vite-served/worklets/openmpt.bundled.worklet.jsloads under the same-origin policy,window.electronAPIpopulated,trackerPlayer.init()succeedsnpm run dist:win: produces NSIS + portable artifacts underrelease/viascripts/dist.mjs
License: Apache-2.0 (per the SPDX headers and package.json). By submitting a PR, you agree to license your contribution accordingly.