feat(platform): per-channel app identity — distinct icon/name/userData per build kind - #387
feat(platform): per-channel app identity — distinct icon/name/userData per build kind#387pnewsam wants to merge 2 commits into
Conversation
106200d to
8092821
Compare
8092821 to
ba6e6f4
Compare
1edf76a to
f66268f
Compare
pnewsam
left a comment
There was a problem hiding this comment.
Code review
Verdict: COMMENT — one major issue should be addressed before merge.
The per-channel app names, bundle IDs, keychain namespaces, startup import ordering, prod compatibility, and the newly added build-script drift guards look sound. However, the badged channel icon configured at build time is replaced by the production icon when the app launches, so one of the PR's central visual-isolation guarantees does not currently hold.
Summary of findings
| Severity | File | Issue |
|---|---|---|
| Major | scripts/channel-identity.mjs:15 |
Runtime window/dock icon selection overwrites the badged bundle icon with assets/icon.png. |
| Minor | scripts/reset-onboarding.sh:49 |
Nearby documentation still says localStorage/terms consent are shared across channels. |
Both installer jobs are currently skipped. Because this PR replaces the Windows builder invocation and changes macOS packaging identity, I would treat successful macOS and Windows installer runs as a release gate. The installed artifacts should be checked for distinct app/bundle IDs, product names, running icons, userData directories, and keychain namespaces, while confirming prod remains unchanged.
Because this is a self-authored PR, I am leaving a COMMENT rather than an approval.
hamishfagg
left a comment
There was a problem hiding this comment.
approving package.json changes
fe8cfc9 to
ac55d19
Compare
265698d to
38b97fc
Compare
4466efd to
6c9db65
Compare
…, dock icon Give each non-prod build kind a distinct RUNTIME identity so build kinds on one machine don't collide, building on the CHANNELS table from #385: - channels.ts: add per-kind `appName` (the Electron app name → userData dir) and `iconName` (the window/dock icon). prod is frozen to `anton` / `icon.png` so shipped installs keep their data and look unchanged. - app-identity.ts: a side-effect module imported FIRST in index.ts — calls app.setName for non-prod kinds only (before token-store reads userData at load) so localStorage, terms consent, and the token store isolate per channel. prod's app.name is never touched → byte-for-byte unchanged. - keychain-service.ts: namespace the keychain service per channel (`cowork-oauth-<kind>`); prod keeps the historical `cowork-oauth`. - app-icon.ts: pick the badged window/dock icon at runtime (the packaged bundle icon alone doesn't govern the running app's dock icon), falling back to the base icon.png if the asset is missing. The `stable` kind targets the staging env (see #385), so its user-facing labels read "Staging"; its invisible appId stays keyed to the kind. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
e7ba1d1 to
7e304b8
Compare
… (ENG-340) Follow-on to the per-channel app-identity work (#387): since each non-prod build kind gets its own Electron app name (app.setName → a separate userData dir), the reset/restore scripts must target the channel-specific dir, not the shared ~/Library/Application Support/anton. - Derive ELECTRON_DIR from a per-kind app-name map (prod stays 'anton'; non-prod kinds mirror channels.ts appName — the `stable` kind targets the staging env so its app is labelled "Staging"). - Reword the localStorage/consent comments: state now lives under the channel's OWN userData dir, so a reset clears only the kind you pass. Kept here (rather than in #387) so #520 owns all onboarding-script changes and stays independently mergeable on staging; the app-name map is a hardcoded mirror either way (bash can't import channels.ts). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dged icons
Set the packaged app's appId / productName / icon per build kind so preview and
stable installers are distinct apps with distinct dock/Finder icons:
- channel-identity.mjs: plain-JS bundle-identity mirror (build scripts can't
import the TS channels table), keyed by build kind; prod/dev return null so the
prod build path uses electron-builder.yml unchanged. A drift test in
channels.test.ts locks it to CHANNELS (productName ↔ appName, icon ↔ iconName).
- release-mac-pkg-notarized.sh / dist-win.mjs: apply the identity at build time.
- assets/icon-{preview,staging}.png: placeholder badged icons (the base icon
with a channel-labelled band), committed as-is — to be replaced with proper
artwork later.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7e304b8 to
389b338
Compare
Description
Makes each build kind a distinct app, so an internal user can distinguish them. This is the standard Electron pattern (VS Code Stable/Insiders, Slack/Canary, Discord/Canary/PTB).
Stacked on #385 (base branch
paul/developer-platform-channels) — review/merge #385 first, then this retargets tostaging.Fixes: https://linear.app/mindsdb/issue/ENG-676/build-kind-app-isolation-install-non-prod-builds-as-separate-apps
What "distinct app" means here
Per non-prod channel: its own app icon (badged), bundle id (
appId), app name (productName), userData dir (localStorage / terms consent / token-store file), and keychain namespace.~/.cowork-<kind>home, server binary, logs, and UI cache were already isolated in #385 — this closes the last shared surfaces.com.mindshub.coworkanton(frozen)cowork-oauth(frozen)…cowork.stableMindsHub Cowork (Staging)cowork-oauth-stable…cowork.previewMindsHub Cowork (Preview)cowork-oauth-previewMindsHub Cowork (Dev)cowork-oauth-devHow
app-identity.ts,channels.tsappName):app.setName()for non-prod, run as the first import inindex.ts(beforetoken-store.tsreadsapp.getPath('userData')at load). prod'sapp.nameis never re-set → byte-for-byte unchanged.keychain-service.ts): per-channelSERVICE_NAME(prod frozen).scripts/channel-identity.mjs): per-kindappId/productName/icon; prod/dev → no overrides →electron-builder.ymldefaults. Threaded intorelease-mac-pkg-notarized.sh(overridesPRODUCT_NAMEso the.apppath/pkg title match, passes-coverrides) andscripts/dist-win.mjs(electron-builder programmatic API — avoids Windows shell-quoting the spaced name).assets/icon-{staging,preview}.png): placeholder badged icons — the base icon with a channel-labelled band — committed as-is, to be replaced with proper artwork later.Why it's low-risk
appIdunder the team; entitlements aren't bundle-id-pinned → no new certs.requestSingleInstanceLock, noelectron-updaterfeed → none of the usual side-by-side-install conflicts apply.appName/appId/productName/keychain; build overrides skipped).Consequences / call-outs
antonuserData; after this, non-prod becomes a distinct app with fresh localStorage/consent (old install becomes an orphan to delete). Worth a heads-up in release notes.Test / verify
npm run typecheck,check:cowork-purity, full unit suite (637 tests) pass on the rebased branch.channels.test.tsasserts prodappNameis frozen toantonand non-prod names are distinct, and drift-guardschannel-identity.mjsagainst CHANNELS (productName ↔ appName, icon ↔ iconName).channel-identity.mjs: correct appId/productName/icon per kind, empty for prod/dev/unset.🤖 Generated with Claude Code