Docs

ADR-0020: Dev app branding — patch the icon, don’t chase the name

Status: Active · Date: 2026-07-29

References

Context

yarn start launches Electron’s own unbranded app bundle (node_modules/electron/dist/Electron.app), not a packaged Tranquil app. On macOS that means the dock/app-switcher icon is Electron’s default atom logo and the menu-bar/dock name is “Electron”. Only packaged builds are branded, because electron-builder rewrites the bundle at package time.

Two separate surfaces, with very different tractability, were investigated this session:

  • The icon is read by macOS’s icon services from the bundle’s CFBundleIconFile, refreshed when the bundle’s mtime changes. Patching it in place works.
  • The name (the bold application menu and the dock/⌘-Tab label) is resolved by the OS from the app’s bundle identity, and it proved immovable in dev. With every on-disk attribute set to “Tranquil” — CFBundleName, CFBundleDisplayName, app.getName(), the LaunchServices LSDisplayName, a unique CFBundleIdentifier, a valid ad-hoc re-sign, lsregister -f, killall Dock, a renamed executable, and argv0 — the visible name stayed “Electron”. The decisive finding: the machine had 6 registered Electron.app bundles, 5 named “Electron”, all sharing the identifier com.github.Electron (every npm i electron ships the same stock bundle id). macOS keys app identity by bundle id, so the colliding “Electron” registrations win — and even a unique id did not visibly dislodge a further OS-level name cache that could not be cleared remotely. Note that lsappinfo and System Events reported “Tranquil” while the menu bar still showed “Electron”, so those readouts are unreliable proxies for the visible name.

Packaged builds don’t have this problem because electron-builder does the whole rebrand as one coordinated operation — rename the main executable and the helper apps, set the Info.plist (CFBundleName/DisplayName/CFBundleExecutable), assign a unique CFBundleIdentifier, embed the icon, and re-sign. Piecemeal patching of the shared stock bundle in node_modules cannot reproduce that.

Decision

Brand the dev icon by patching the Electron bundle; do not chase the dev name; make the packaged build the source of truth for app identity.

  • scripts/dev.js patches the icon on every launch (patchMacAppIcon): copy resources/app-icons/tranquil.icns into the bundle’s Contents/Resources, set CFBundleIconFile, and bump the bundle mtime so icon services refresh. Best-effort, idempotent, macOS-only. It keeps the pre-existing CFBundleName→“Tranquil” plist patch (harmless; corrects Finder/LaunchServices metadata) but we accept that this does not change the visible menu-bar name.
  • The dev menu-bar/dock name stays “Electron” and that is acceptable: it is cosmetic and dev-only. No executable/helper renaming, bundle-id rewriting, or re-signing of the node_modules Electron is done in dev — those were tried and did not work, and a divergent bundle id risks TCC/permission surprises versus a fresh yarn install.

Options considered

  • Patch the icon in dev (chosen) vs. leave the atom icon: a one-file bundle patch that visibly works; kept.
  • Patch the name in dev via the bundle (CFBundleName, unique id, re-sign, re-register) (rejected — proven not to work): every attribute reads “Tranquil” yet the OS still shows “Electron” due to the shared-com.github.Electron collision plus an uncleared name cache.
  • Rename the executable / argv0 in dev (rejected): did not change the visible name; renaming helper apps + re-signing to fully mimic electron-builder against node_modules is brittle (breaks on every Electron upgrade, risks breaking helper resolution).
  • Run a packaged build for a correct dev name (available, not default): yarn dist then launch the produced .app shows the right name+icon. Reserved for when the name genuinely matters; yarn start stays the everyday flow.

Consequences

  • Dev shows the Tranquil icon and the name “Electron”. Reviewers/onboarding should expect the “Electron” label in dev and not re-open the investigation — this ADR and the session notes are the record.
  • Distributable builds must carry the branding, and they currently don’t fully. script/electron-builder.js still points at the leftover Pulsar “beta” artwork (resources/app-icons/beta.*, resources/icons/*), uses appId: 'dev.pulsar-edit.pulsar', and overrides productName to “Pulsar”; crashReporter in src/main-process/start.js also names “Pulsar”. Before we ship binaries we must: swap the icon set to the Tranquil node-graph icon (.icns/.ico + the resources/icons PNG set), set a Tranquil appId (e.g. dev.tranquil.app), and confirm productName/crash-reporter name resolve to “Tranquil”. The session notes carry the full checklist.
  • Re-evaluate if we adopt a packaged-dev workflow (e.g. a one-off @electron/packager branded bundle used by yarn start), which would fix the dev name properly and could retire the in-place icon patch.