Brainstormed with the user (ULTRATHINK context). Sub-project 1 of 4 in the
v5+ roadmap (production polish → cloud/licensing → plugins → perf+a11y).
Spec covers:
- Two-channel distribution: GitHub Releases (default) + ConcreteInfo self-hosted
- Light, skippable first-run wizard (theme + update channel + starter template)
- Auto-migrate v4.4.1 settings with v4 backup and toast on success/failure
- Local crash dump capture (no third-party) with a CrashReportModal
- Non-blocking update banner with user-confirmed restart-to-install
- IPC allowlist additions for updater:* and crash:*
- GitHub Actions CI extension (latest.yml generation, PR test suite)
Defers to v5.1: code signing, Sentry, delta updates, beta tracks.
After user review and approval, write the implementation plan via the
writing-plans skill.
Closes the gap from the previous handoff: `npm run dist:all` now
produces Linux .deb/AppImage/snap, Windows NSIS/portable/zip, and
macOS dmg/zip artifacts. `.github/workflows/release.yml` builds all
three on tag push (v*) and attaches the union to a GitHub Release.
Bug fix in the same commit (would have broken any packaged build):
* `build.files` was `["src/**/*", ...]` — shipped `src/renderer/`
.tsx dev source into the asar. Replaced with a whitelist
(`src/main/**/*`, `src/preload.js`, `src/plugins/**/*`,
`package.json`) and moved the built renderer to
`build.extraResources` (`dist/renderer` -> `renderer/`).
* `src/main/window/index.js` now uses `process.resourcesPath` when
`app.isPackaged` is true, plus a friendly error if the renderer is
missing. Verified by extracting the .AppImage and launching the
binary — main process logs
`Production mode — loading .../resources/renderer/index.html`.
New in `package.json` build config:
* `mac.target`: dmg + zip for x64 and arm64; `darkModeSupport: true`,
`hardenedRuntime: false`, `gatekeeperAssess: false`,
`entitlements: null`. `mac.identity: null` retained (unsigned).
* `publish`: GitHub Releases provider. CI overrides with
`--publish=never`.
Known limitations (intentional follow-ups, not blockers):
* No `assets/icon.icns` — macOS app uses the default Electron icon.
This box lacks `iconutil`/`png2icns`; generating a proper .icns is
a one-line follow-up.
* No code signing — Windows SmartScreen will warn, macOS Gatekeeper
will quarantine. `CSC_LINK_BASE64`/`CSC_KEY_PASSWORD` secrets are
already wired in the Windows job for when the cert is added.
* No auto-update — user decision. The `publish` block writes the
metadata that a future `electron-updater` integration will consume.
Verified on this Linux box:
* `npm run build:renderer` then `npm run build:linux` — produced
AppImage (291M), deb (220M), snap (245M).
* `resources/app.asar` has 0 `src/renderer/*` entries (was the
pre-fix bug). 44 .tsx files remain in asar — all from
`node_modules/@hookform/resolvers` and `node_modules/immer` test
fixtures, not our code.
* Extracted `squashfs-root/resources/renderer/index.html` exists and
is what `window/index.js` loads.
* Binary runs for >12s without crashing — `loadFile` resolves
`process.resourcesPath/renderer/index.html` correctly.
Tests: 306/306 pass. No regressions.
Plan doc: docs/plans/2026-06-06-packaging-all-platforms.md (the
task-by-task execution guide I followed; included for traceability).
9 tasks across 8 chunks, strict TDD:
- EventBus with crash-safe handlers
- PluginAPI base class
- PluginLoader with manifest validation
- PluginContext with scoped API
- PluginRegistry with lifecycle management
- SettingsStore for plugin-scoped settings
- Export hooks integration
- Sample plugin + renderer wiring
Amit Haridas
- GGUF GPU: child process isolation with crash detection/restart
- Event bus: versioned payload schemas for all events
- Plugin sandbox: 5s handler timeout, IPC delegation for heavy ops
- AI streaming: full lifecycle with requestId, cancel, heartbeat, orphan cleanup
- Comment anchors: context-based positioning (not byte offsets) with re-anchor on file change
- Cross-plugin: capability discovery, 30s timeout, graceful degradation
- Bundle size: GPU variants as lazy downloads, not bundled by default
- Command uniqueness: registry rejects duplicates at load time
Amit Haridas