perf(startup): stop queueing window creation behind the proxy apply and i18n - #18436
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (16)
🚧 Files skipped from review as they are similar to previous changes (10)
Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review. 📝 WalkthroughWalkthroughThe PR replaces the static emoji dataset import with configurable synchronous and deferred loaders. Electron packages copy the dataset into Merge Risk: ⚪ Minimal · up to Startup work now runs concurrently without allowing the runtime failure dialog to race locale initialization, while emoji shortcodes load on first main-process use from the packaged resource. The remaining reviewed changes have no actionable merge-blocking risk. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 41.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 15 files. (1 skipped: 1 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Follow-up note for the Correctness section, spotted after opening: with i18n now concurrent, there is one error path that can read a translated string before |
9caa095 to
e4674d4
Compare
There was a problem hiding this comment.
Caution
The emoji-dataset deferral will throw at runtime in packaged builds: emojibase-data is a devDependency, so it is not shipped into the packaged app and the main-process require cannot resolve it.
Reviewed changes
- Proxy apply moved off the window-creation path —
applyElectronProxySettingsis parked onmainProcessState.initialProxyApplicationReadywith its warn handlers attached, and awaited inlaunchDesktopMode/launchServeModeinstead of the foundation phase. - i18n/menu parallelized with window creation —
initializeMainProcessReadyrunsinitializeMainProcessI18nAndMenuandinitializeMainProcessRuntimeLaunchunderPromise.all. - Emoji dataset behind an injected loader —
emoji-shortcode-catalog.tsno longer statically imports the dataset; main registers acreateRequire-based loader and the renderer keeps its eager import. - New ordering + laziness tests — a source-anchored phase-ordering test and an emoji-catalog parity test.
🚨 Main-process emoji require cannot resolve in packaged builds
emojibase-data is a devDependency (devDependencies.emojibase-data = 17.0.0, absent from dependencies), and it is not in PACKAGED_RUNTIME_PACKAGE_ROOTS. electron-builder does not ship devDependencies into app.asar, so requireFromMain('emojibase-data/en/shortcodes/emojibase.json') will throw Cannot find module the first time replaceKnownEmojiWithShortcodes runs — which is on every worktree name, not just emoji-bearing ones, because sanitizeWorktreeName calls it unconditionally. The @linear/sdk precedent cited in the comment does not transfer: @linear/sdk is a production dependency (^82.1.0), whereas emojibase-data is not.
Technical details
# emojibase-data is not shipped to packaged builds
## Affected sites
- src/main/ipc/deferred-emoji-shortcode-dataset.ts:11 — bare runtime `requireFromMain('emojibase-data/en/shortcodes/emojibase.json')`.
- src/main/ipc/worktree-logic.ts:41 — `replaceKnownEmojiWithShortcodes(input)` runs unconditionally in `sanitizeWorktreeName`, so worktree create/naming fails wholesale, not only for emoji.
## Why it is missed by the safety net
- `verifyPackagedMainRuntimeDeps` (config/packaged-runtime-node-modules.cjs:240) regex-matches literal `require("…")` in the main bundle; `requireFromMain('…')` via `createRequire` does not match, so packaging CI won't flag it.
- electron-builder only packages `dependencies`, and this repo additionally curates a `PACKAGED_RUNTIME_PACKAGE_ROOTS` closure for bare `require()` calls — `emojibase-data` is in neither.
## Required outcome
- The main-process emoji dataset must resolve in a packaged build, or the deferral must not throw when it can't (fail closed to a safe fallback rather than aborting worktree naming).
## Suggested approach
- Move `emojibase-data` (and its peer `emojibase`) into `dependencies`, and add `emojibase-data` to `PACKAGED_RUNTIME_PACKAGE_ROOTS` so the same closure rule that already covers `@linear/sdk` applies. Then extend `verifyPackagedMainRuntimeDeps` (or a contract test) to cover `createRequire`-style runtime requires so this class of regression fails CI.DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Team
Run ID: 11157100-672a-48c7-a9ac-183542a04390
📒 Files selected for processing (12)
config/electron-builder.config.cjsconfig/scripts/electron-builder-runtime-resources.test.mjssrc/main/ipc/deferred-emoji-shortcode-dataset.tssrc/main/ipc/worktree-logic.tssrc/main/startup/main-process-ready-foundation.tssrc/main/startup/main-process-ready-phase-ordering.test.tssrc/main/startup/main-process-ready.tssrc/main/startup/main-process-runtime-launch.tssrc/main/startup/main-process-state.tssrc/renderer/src/lib/workspace-emoji-shortcodes.tssrc/shared/emoji-shortcode-catalog.lazy.test.tssrc/shared/emoji-shortcode-catalog.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 5 remain after this review.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Packaging fix for the lazy emoji dataset require — copies the single 166 KB
emojibase-dataJSON intoResources/node_modulesviacommonExtraResources, so main'screateRequire'd specifier resolves out ofResources/node_modulesin packaged builds and resolves the priorMODULE_NOT_FOUNDcrash on every worktree name. - Two new packaging contract gates — one scans
src/mainfor every barecreateRequirespecifier and asserts it is covered by the packaged resource plan on all three platforms (with a Windows-only corridor for native loaders), the other resolves the copied dataset through a simulatedapp.asar→Resources/node_moduleswalk. - Comment correction —
deferred-emoji-shortcode-dataset.tsnow documents the actual resolution path (Resources/node_modules, notapp.asar) and the49 MBpackage-root rationale.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Team
Run ID: 7bd8e126-8019-4f4d-8e3e-34480570227d
📒 Files selected for processing (1)
src/main/proxy-guarded-fetch-call-site-audit.test.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 0 remain after this review.
There was a problem hiding this comment.
ℹ️ No critical issues — one minor suggestion inline about the audit's regex coverage.
Reviewed changes
- New proxy-guard call-site audit test — added
src/main/proxy-guarded-fetch-call-site-audit.test.ts, a static source scan that fails CI when anet.fetch/net.requestundersrc/mainpasses asession:/partition:option, or when a non-net.fetch(call site appears outside a three-entry allowlist.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Closed the fetch-audit regex blind spots — rule 2 now classifies each
.fetch(receiver by extracting the trailing identifier (FETCH_CALL+RECEIVER_IDENTIFIERagainst aDEFAULT_SESSION_RECEIVERSset), so the inline chainedsession.fromPartition(...).fetch(, the optional-chaineds?.fetch(, and memberctx.session.fetch(forms are counted instead of slipping past both rules. Rule 1'sSESSION_SCOPED_OPTIONadditionally matches the{ url, session }object shorthand bothnet.requestoverloads accept.
This directly resolves the one open finding from the prior review (the NON_NET_FETCH_CALL blind spot). The change is test-only: git diff bc1307d8bac..HEAD --stat -- . ':!*.test.ts' ':!*.test.mjs' is empty, so no production behavior moved. Verified locally that the audit test passes and that the allowlist counts (2/2/1) still match the real call sites.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
…nd i18n Three independent, measured startup wins, all free: 1. Park the initial Chromium proxy apply on `mainProcessState` instead of awaiting it mid-`initializeReadyFoundation`. `setProxy` still starts at the identical moment; the default-session request guard (which holds, not cancels) is what actually fences fetchers on it, so only window creation stops waiting. Runtime launch still awaits it before the desktop relay and before every headless-serve fetcher. 2. Run `initializeMainProcessI18nAndMenu` concurrently with `initializeMainProcessRuntimeLaunch`. Nothing in window creation reads a translated string or the native menu. 3. Load `emojibase-data` in main through `createRequire` on first use instead of a static import, keeping 166 KB of JSON off `out/main/index.js` and its ~2 ms parse off every launch. The renderer keeps its eager copy unchanged. out/main/index.js 7,210,071 -> 7,040,147 bytes. No renderer behaviour changes.
app.asar carries no node_modules, so main's bare requires resolve only out of Resources/node_modules. emojibase-data is a devDependency and is not in the packaged runtime allowlist, so the new createRequire in deferred-emoji-shortcode-dataset.ts threw MODULE_NOT_FOUND in every packaged build — breaking sanitizeWorktreeName, and with it workspace creation. Copy the single 166 KB dataset (not the 49 MB package root) into Resources/node_modules, and gate every createRequire'd bare specifier in src/main against the packaged resource plan. verifyPackagedMainRuntimeDeps cannot catch these: the bundler renames the require binding.
…session guard The hoist relies on installElectronProxyRequestGuard(session.defaultSession) holding every app-owned request until the persisted proxy lands. Nothing enforced that every fetcher actually lands on defaultSession. Two source-anchored rules do now: no net.fetch/net.request may name a session/partition, and every non-net .fetch( call site is counted against an allowlist.
…tch call-site audit
The audit caught `net.request({ session: x })` and `ident.fetch(`, but not the two
shapes a real regression is just as likely to take: the `{ url, session }` shorthand
that both `net.request` overloads accept, and a receiver with no bare identifier
(`session.fromPartition(...).fetch(`, `ctx.session.fetch(`). Rule 1 now also matches
the shorthand key; rule 2 scans every `.fetch(` and excludes only a literal
`net`/`globalThis`/`global` receiver. Audited counts are unchanged (2/2/1).
…n it TS6307: the composite web project lists src/main/ipc/worktree-logic.ts, which now imports the deferred dataset loader, and the shared lazy test reached into src/main from a project that has no src/main files. Add the loader to tsconfig.tc.web.json and move the cross-project case into a src/main test. Also close the last two review gaps: gate the runtime-RPC startup failure dialog (the only launch-phase translateMain reader) on a published i18n barrier so a concurrent i18n phase cannot leave a non-English user with the English fallback, and let the fetch call-site audit match `net.fetch (url)`.
99fe199 to
2ed433c
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Closed the i18n race rather than just documenting it —
mainProcessState.mainProcessI18nReadyis now published synchronously inmain-process-ready.ts(asinitializeMainProcessI18nAndMenu().catch(() => {})) before the launch phase starts, andlaunchDesktopModechains the runtime-RPC startup failure dialog off it withvoid state.mainProcessI18nReady.then(...). The wait is on i18n only, never on the modal, and remains failure-only. The uncatchedi18nAndMenuReadyis still whatPromise.allawaits, so an i18n failure still surfaces throughinitializeMainProcessReadywhile the dialog still fires with fallback strings. - Scoped the deferred emoji loader to the projects that own it —
config/tsconfig.tc.web.jsonnow listsdeferred-emoji-shortcode-dataset.ts(it is imported byworktree-logic.ts, which tc.web already includes), and the main-side parity case moved out of the sharedemoji-shortcode-catalog.lazy.test.tsinto a dedicatedsrc/main/ipc/deferred-emoji-shortcode-dataset.test.ts, restoring the shared project's no-src/mainboundary. - Tightened the fetch-audit regexes —
FETCH_CALLandNET_REQUEST_CALLnow allow whitespace before(, closing thenet.fetch (…)/x .fetch(…)hole.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

ELI5
When Orca launches, the main process does a short list of chores before it is allowed to open the window you actually look at. Two of those chores did not need to be in that line. One was waiting for the network-proxy setting to be written into Chromium; the other was loading the translated app menu. Neither is something the window needs in order to exist, so the window was just standing behind them.
This moves both out of the way. The proxy is still applied at exactly the same moment and still finishes before anything on the network happens — a separate gate already holds every request until it lands — but the window no longer waits for the confirmation. The menu is built at the same time the window is created instead of before it.
One smaller thing rides along: a 166 KB emoji dataset that only gets read when you name a workspace was being baked into the main bundle and JSON-parsed on every single launch; it is now read from disk the first time it is actually needed.
Why it matters
Measured with
ORCA_STARTUP_DIAGNOSTICS, best warm iteration on this machine:f-claude-pty-seeded->f-proxy-applied= 24 ms, with nothing between the two markers but theawaitonapplyElectronProxySettings. That 24 ms sat entirely in front ofopen-main-window-start.initializeMainProcessI18nAndMenu= 8 ms for English. Non-English adds a lazy locale chunk (src/main/i18n/main-i18n.ts:26-30, 380-616 KB), 2.6-5.5 ms to read and compile plus i18next ingestion — call it 15-20 ms forja/ko/es/zh.app-ready->open-main-window-start= 91 ms;open-main-window-start->window-created= 119 ms;window-created->did-finish-load= 273 ms.So this is roughly 32 ms (English) to 44 ms (non-English) removed from in front of window creation, plus the 166 KiB main-bundle reduction below, against a 1.6-2.0 s time-to-workspace-ready. It is a real but small fraction. Several of these are two-line reorders, which is why they are worth taking.
Measurement
Bundle bytes (electron-vite production build, before/after on this branch)
out/main/index.jsRenderer bytes are unchanged: the lazy-dialog item is no longer on this branch (see "What I did not ship"), so
verify-renderer-boot-graphreports the same 342 chunks / 4368.4 KB asmain.Confirming the emoji dataset really left the main bundle (
water_buffalois a shortcode only that JSON carries):Microbenchmark: the parse that used to run on every launch
Re-measured with
process.cpuUsage()deltas rather than wall clock (this box runs ~24 agents;wall-clock numbers on it are noise):
That ran at main-bundle module-evaluation time on every start. It now runs only when a worktree name is first sanitized.
Failing-test proof
The new/changed guards were run against
origin/main's sources (production files stashed, tests kept):With the change:
(The "creates the window without waiting for i18n" case is the load-bearing one for item 2: on
mainthe i18n phase is awaited first, so with a deliberately-never-resolving i18n mock the launch phase never starts and the recorded event list stops ati18n-start. The two source-anchored proxy cases follow the existingdesktop-startup-ordering.test.tspattern, which is how this repo pins startup ordering.)src/main/proxy-guarded-fetch-call-site-audit.test.ts(new, see Correctness) was verified failing byintroducing each violation it is meant to catch into
src/main/updater-nudge.tsand reverting:99fe1997400then closed two holes the first version of those rules had, verified the same way with ascratch file carrying one call site per shape (each is caught, and the file is deleted again):
Before that commit the shorthand and the two non-identifier receivers (
....fetch(,a.b.fetch()all passed silently; only line 6 and the bare
session2.fetch(were caught. The audited counts didnot move (2/2/1), so no real call site changed classification.
Performance is unchanged by the audit commits
The audit is test-only, so none of the numbers above move. Verified deterministically rather than by
timing:
i.e. the two audit commits (
46131460e72,99fe1997400) add and then tighten one.test.tsfile andchange zero production lines -- the diff above is empty at both. The 24 ms proxy hoist,
the 8 ms i18n overlap and the 166 KiB main-bundle drop are all byte-for-byte the same code as the
head that was measured.
Correctness
Item 1 — the proxy await. The safety claim is not "no fetcher runs in that window". It does:
src/main/claude-accounts/oauth-refresh.ts:142issuesnet.fetch(OAUTH_TOKEN_URL), reached synchronously fromnew ClaudeRuntimeAuthService(store)->void this.safeSyncForCurrentSelection()->runtime-auth-sync.ts:250->refreshManagedAccountTokenIfNeeded, duringinitializeReadyRuntimeServices. The updater nudge/prerelease feed and the rate-limit fetchers follow at theready-to-show/ +1000 ms boundary.The reason the hoist is safe is that
installElectronProxyRequestGuard(src/main/network/electron-proxy-request-guard.ts, installed atmain-process-ready-foundation.ts:56and again at:143) is what enforces the ordering, not theawait. It hooksdefaultSession.webRequest.onBeforeRequestand awaitsgetProxySessionApplicationReadiness— it holds the request until the newest proxy transition settles rather than cancelling it (see the existingelectron-proxy-request-guard.test.tscase "holds renderer requests until a delayed proxy transition settles"). Every fetcher above usesnet.fetch/net.requestwith no explicit session, so they all land ondefaultSessionand are all held. Verified empirically on this repo's Electron 43 (standalone probe app, main-processnet.fetchagainst a loopback server): thedefaultSessiononBeforeRequestlistener sees the request URL,cb({cancel:true})fails it withnet::ERR_BLOCKED_BY_CLIENT, and a listener that defers its callback by 500 ms delays thenet.fetchby the same 500 ms. So a main-processnet.fetchreally is held by the guard, not merely observed by it. TheawaitininitializeReadyFoundationwas therefore redundant for ordering and only queuedopenMainWindowbehind asetProxyround trip.The apply itself is unchanged:
applyElectronProxySettings(store.getSettings())is still called at the same line, before the guard is (re-)installed, so the guard still observes pending readiness from the same instant. Theinvalid-settingsand failureconsole.warns were moved onto the parked promise's settle handlers, so they fire at the same moment they did before, not later. Attaching the handlers synchronously also closes a small unhandled-rejection window that existed while the promise sat unawaited for ~90 lines.The phase postcondition is preserved:
initializeMainProcessReadystill does not resolve until the proxy has settled, becauselaunchDesktopModeawaits it (after the window, beforenew DesktopRelayService) andlaunchServeModeawaits it at its top. Headless serve therefore keeps the strict "proxy before anything" ordering it had, since it has no window to unblock. Rejection behaviour is identical: the oldtry/catchswallowed failures and so does the new rejection handler, soawait state.initialProxyApplicationReadycan never throw.applyBrowserSessionProxiesis unaffected: it operates onsession.fromPartition(...)objects with their own per-session state insessionProxyApplications(aWeakMapkeyed by session), so it was never ordered against the default session's apply — only against the resolver registration, which still precedes it.Item 2 — i18n/menu concurrency. Nothing on the window-creation path imports
main-i18n:translateMainconsumers aresystem-tray.ts,main-window-close-lifecycle.ts,editable-context-menu.ts,runtime-rpc-startup-failure.ts,register-app-menu.ts,settings.tsand the i18n/menu phase itself — none of whichcreateMainWindow,main-window-controller.ts,main-window-core-services.tsormain-window-service-readiness.tsreach. The tray is created onready-to-show, which is afterdid-finish-load. Menu ordering is safe because Electron'sMenu.setApplicationMenuapplies to already-created windows (it iteratesBrowserWindow.getAllWindows()on Windows/Linux and is global on macOS), andcreateMainWindowsetsautoHideMenuBar: trueand never callswindow.setMenu, so there is nothing for a lateregisterAppMenuto race.Promise.all(rather than a bare parallel start) keeps both settled before the phase resolves and attaches handlers to both, so neither can become an unhandled rejection.Item 4 — the emoji dataset.
src/shared/emoji-shortcode-catalog.tsno longer imports the dataset itself; it takes a synchronous loader. Both consumers register one at module scope of the only module that reaches the catalog in their bundle, so the loader is always set before the first call:workspace-emoji-shortcodes.tskeeps its eager static import and registers() => emojiShortcodes. Byte-for-byte the same renderer behaviour — no dynamic import, no await, no window in which the transform can return an empty catalog.config/scripts/renderer-boot-graph.mjs:44-48documents why that must not change, and this PR does not change it.deferred-emoji-shortcode-dataset.tsregisters acreateRequire(__filename)loader, the same pattern already used bysrc/main/linear/linear-sdk.ts.require()is synchronous, soloadCatalog()keeps its exact contract; a new test asserts the deferred path produces antoEqual-identical entry list and an identicalreplaceKnownEmojiWithShortcodesoutput with no await between registration and first use.Packaging (corrected in
bc1307d8bac). The original claim here — thatemojibase-dataships insideapp.asarbecause it is a production dependency — was wrong on both counts, and it was a P0.app.asarcarries zeronode_modulesentries (verified against the shipped 1.4.197 build:asar.listPackage(...).filter(e => e.includes('node_modules')).length === 0), so every barerequirefrom packaged main resolves out ofResources/node_modules, which is the explicit allowlist inconfig/packaged-runtime-node-modules.cjs(PACKAGED_RUNTIME_PACKAGE_ROOTS). That is how@linear/sdkworks.emojibase-datais a devDependency (package.json:237) and was in neither place —find /Applications/Orca.app -name 'emojibase*'returns nothing — sorequireEmojiShortcodeDataset()would have thrownMODULE_NOT_FOUNDin every packaged build, takingsanitizeWorktreeNameand therefore all workspace creation down with it. Dev builds resolve from the repo checkout, which is why local testing did not catch it.verifyPackagedMainRuntimeDepsdoes not catch this either: it regexesout/main/index.jsfor literalrequire("x"), and the bundler renames thecreateRequirebinding (grep 'require("@linear/sdk")'on the shipped bundle: no match).The fix copies the single 166 KB dataset file — not the 49 MB package root — into
Resources/node_modulesviacommonExtraResources, so the bare specifier resolves unchanged and the bundle/parse win is kept in full.emojibase-datahas noexportsfield, so the one JSON file is sufficient for subpath resolution. Two new gates inconfig/scripts/electron-builder-runtime-resources.test.mjs: one resolves the dataset through a simulatedResources/app.asar/out/main→Resources/node_moduleswalk, and one asserts everycreateRequire'd bare specifier undersrc/mainis covered by the packaged resource plan on all three platforms (Windows-native loaders excepted). Both fail on the pre-fix config.Edge cases.
sanitizeWorktreeNameis unchanged in behaviour and still synchronous, so remote and folder-workspace naming are untouched.orca serve: gets its proxy await at the top oflaunchServeMode, ahead of the WSL barrier, headless PTY runtime,runtimeRpc.start(), the CLI install andprintServeReady— strictly stronger ordering than the desktop path, and equivalent to today.startWindowsDesktopBeforeShellPathReady) is untouched; it already opens the window beforeshellPathReady, and it flows into the samelaunchDesktopModeawait.mainProcessState.initialProxyApplicationReadydefaults toPromise.resolve(), so any path that never runs the foundation (tests,orcaCLI entry) awaits a no-op, exactly likeshellPathReadyandmanagedWslCliStartupBarrierReady.The guard's precondition is now enforced by CI, not by review. "Every app-owned request lands on
defaultSession, where the guard holds it" is the one invariant the hoist depends on. It held when this was written (all 18net.fetch/net.requestsites undersrc/mainpass no session option), but nothing stopped a future fetcher from quietly routing around it.src/main/proxy-guarded-fetch-call-site-audit.test.ts(new, modelled on the existingsrc/main/global-fetch-call-site-audit.test.ts) makes both escape routes fail loudly:net.fetch(/net.request(undersrc/mainmay name a session: neither asession:/partition:key nor the{ url, session }object shorthand that bothnet.requestoverloads accept. The test balances parentheses from the call site to read the real argument text (skipping string bodies), so it is not fooled by line wrapping;.fetch(call site whose receiver is not a literalnet/globalThis/globalis counted per file against an allowlist, so a new non-default-session fetcher fails until it is audited — including the receivers with no bare identifier to key off,session.fromPartition(...).fetch(...)andctx.session.fetch(...). The allowlist isopencode-go-usage-fetcher.ts(2 — its session is proxied bycreateOpenCodeRequestSession),minimax-request-context.ts(2 — its session is not proxied, see below), andjira/authenticated-request.ts(1 — an injectedHttpClient, not a session, which resolves tonet.fetchondefaultSession).globalThis.fetch/global.fetchare excluded becauseglobal-fetch-call-site-audit.test.tsalready owns them.Both rules were verified failing (transcript above). This is worth noting because the same audit also covers the identical assumption on
origin/main's post-startup proxy transitions (ipc/settings.ts:203-215and everyensureElectronProxyFromEnvironmentcaller), where the guard has always been the sole ordering authority.Pre-existing gaps the audit surfaces but does not close (for separate tickets), neither introduced nor worsened here:
session.fromPartition('electron-updater', {cache:false})(node_modules/electron-updater/out/electronHttpExecutor.js:8,52), which neither the request guard norapplyElectronProxySettingstouches. Third-party code, so out of the audit's reach; noted in the test's header comment.main/rate-limits/minimax-request-context.tsfetches onsession.fromPartition('orca-minimax-rate-limit-fetch')and never applies a proxy to it, unlike its opencode-go sibling.applyElectronProxySettingshas only ever configureddefaultSession, so theawaitthis PR removes never covered that partition either — the behaviour is identical before and after. It is now listed in the audit allowlist with that gap spelled out, so it stays visible instead of being rediscovered.Negative user-facing trade-offs
Not "None" — one, and it is a log-ordering detail:
console.warnfor an invalid or failed proxy apply is no longer guaranteed to be printed beforeopenMainWindowruns. It fires at the same wall-clock moment as before (the handlers are attached synchronously to the same promise), but its position relative to other startup logs can shift. Nothing branches on it.Removed since the last review: the "first open of a lazy dialog waits on a chunk fetch" trade-off is gone, because the lazy-dialog item is no longer on this branch at all —
NewWorkspaceComposerCard.tsxandSidebarSettingsHelpMenu.tsxare byte-identical tomain. There is no longer any first-open latency cost, and no renderer behaviour change of any kind in this PR.Explicitly not traded away: no new cap, cadence, debounce, threshold, sampling window or reduced coverage; the proxy is applied at the identical moment and no request can escape it — and, as of
46131460e72/99fe1997400, that last clause is a CI-enforced assertion rather than a claim.What I did not ship from the brief
Item 3 (lazy renderer chunks) in full. Two of the seven chunks fit
lazy-with-retrycleanly and were on an earlier revision of this branch, but they are no longer here: this PR is now main-process startup ordering plus the emoji dataset only, and it changes no renderer component. That also removed the only user-visible trade-off the PR had. The other five were never viable:worktree-creation-flow(19.0 KB) anddelete-worktree-flow(13.4 KB) are plain function modules, not components —lazy-with-retrydoes not apply, and they have 2 and 12 synchronous call sites respectively. Deferring them means turning those call sites async, which is the same class of change as the reverted emoji dynamic import.SkillFreshnessUpdateDialog(7.2 KB) must stay mounted: it is the subscriber toskill-freshness-update-dialog's request store (subscribeSkillFreshnessUpdateDialog), so an unmounted dialog would miss requests. Deferring it safely needs the subscription hoisted intoAppRootSurfaces, which is a bigger change than 7 KB justifies.SshHostAdvancedFields(8.3 KB) owns its own "Advanced" disclosure trigger (open/onOpenChangeare its props), so lazying it removes the click target — exactly what the brief says not to do.CliSkillRuntimeSetup(6.8 KB) is a shared helper module with ~20 importers, most of them type-only. Not a dialog.Test plan
Manual check worth doing on review: create a workspace with an emoji in the name and confirm it still slugifies to the shortcode (that is the only user-reachable path the emoji-dataset change touches).
Changes since review (
2ed433c58cb)Rebased onto
main(picks up the fr.json catalog fix from #18550, which is whatstatic analysiswas red on). Three follow-ups on top:tsconfig.tc.web.jsonlistssrc/main/ipc/worktree-logic.ts, which now importssrc/main/ipc/deferred-emoji-shortcode-dataset.ts— that file is now listed too.tsconfig.tc.cli.jsonincludessrc/shared/**/*, andemoji-shortcode-catalog.lazy.test.tshad one case importing../main/ipc/deferred-emoji-shortcode-dataset.js. Rather than drag acreateRequiremain module into the CLI project, that case moved tosrc/main/ipc/deferred-emoji-shortcode-dataset.test.ts— which restores the boundary the shared test's own comment asserts. All three projects clean.mainProcessState.mainProcessI18nReadyis published synchronously inmain-process-ready.tsbefore the launch phase is invoked, andlaunchDesktopModechains the runtime-RPC failure dialog off it. Stillvoid, notawait: the wait is on i18n, never on the modal, and it is failure-only.desktop-startup-ordering.test.tspins the new shape and now also asserts noawait ... showRuntimeRpcStartupFailureDialog(.net.fetch (url, { session })andx .fetch(url)slipped both rules because each regex required(immediately after the member name; both now use\s*\(. Verified failing against a scratch probe carrying one call per shape (deleted again); audited counts unchanged at 2 / 2 / 1.Win is intact. The whole production diff of this commit is 14 added / 5 removed lines across
main-process-ready.ts,main-process-runtime-launch.tsandmain-process-state.ts— an error-path chain and onePromise.resolve()field. Nothing on the emoji path, the proxy hoist or the bundle changed, andmain-process-statewas already imported by all three sibling phase modules, so no module was added to main's evaluation graph. The deterministic guard for the overlap — "creates the window without waiting for i18n and the native menu", which records the phase event list against a never-resolving i18n mock — still passes.