Desktop release build optimization: size, time, and one false lead #53

Description

@radroid

Context

The fork now builds and publishes a macOS + Windows desktop bundle on every green merge to main
(.github/workflows/t3x-release.yml, design: docs/superpowers/specs/2026-08-03-update-delivery-design.md).
Because that runs on every merge and ships to users over the update relay, both the size of what we
ship and the wall-clock of the build are now recurring costs rather than one-off ones.

Baseline from run 31215844454, the first
fully green release build:

MetricValue
macOS arm64 build leg7m14s (warm-ish runner)
macOS staged artifact (zipped dmg + descriptor)149,875,427 B (~143 MiB)
Packaged prod node_modules~200 modules

This issue collects the optimization surface. No action is urgent — the pipeline works. Filing so
it is not re-derived from scratch next time someone reads a build log.


1. "duplicate dependency references" is a false lead — do not chase it

electron-builder prints this in every build:

• duplicate dependency references dependencies=["@clerk/electron-passkeys@0.0.3",
"effect@4.0.0-beta.102","debug@4.4.3","cross-spawn@7.0.6","shiki@4.4.2", ...]

It reads like a dependency-duplication problem. It is not. Verified against the actual implementation
in app-builder-lib@26.15.6:

  • out/node-module-collector/pnpmNodeModulesCollector.js:100 pushes PKG_DUPLICATE_REF when a node in
    the pnpm list --prod --json --depth Infinity tree has dedupedDependenciesCount > 0 — i.e. when
    pnpm itself elided a subtree it had already printed elsewhere — and then re-resolves it from the
    full entry it already parsed.
  • out/node-module-collector/moduleManager.js:21 classifies it as level info, not warn.

So the message means "this package is referenced by more than one parent", which is the normal, healthy
shape of any dependency graph. Measured on this workspace:

PackageReferences in the prod graphPhysical copies of that version
effect@4.0.0-beta.102371
debug@4.4.3331
@types/hast@3.0.4291

One copy, many referrers — that is deduplication working.

The line that would actually matter is unresolved duplicate dependency references
(PKG_DUPLICATE_REF_UNRESOLVED, level warn), emitted when electron-builder cannot re-resolve the
elided subtree — that silently drops dependencies from the packaged app. It does not appear in our
builds. If it ever does, treat it as a release blocker.

2. Genuine duplicate installs (real, but trivial)

Three packages really are installed twice in the shipped bundle, via version conflicts:

PackageCopiesCost
semver7.8.5 (top level) + 7.7.4 (under electron-updater)~51 files
onetime5.1.2 (top level) + 7.0.0 (under restore-cursor)~3 files
mimic-fn3.1.0 (top level) + 2.1.0 (under onetime)~3 files

Fixable with pnpm.overrides, but the payoff is a few dozen KB against a ~143 MiB artifact, and each
override is a root-package.json edit — i.e. a recurring sync-conflict cost. Recommend not doing
this
unless we are already touching overrides for another reason.

3. The actual size surface

Measured package sizes in the install tree, with the chain that pulls each into the desktop app's
production graph:

PackageSizePulled in by
core-js15 MB (3,684 files)@clerk/electron@clerk/clerk-js
playwright-core12 MBdirect dep of @t3tools/desktop
@shikijs/langs9.9 MB (724 files)shiki language set
@pierre/diffs8.7 MB (584 files)diff rendering
@clerk/shared6.6 MB (764 files)@clerk/electron
lodash4.9 MB@clerk/clerk-jsbrowser-tabs-lock
shiki3.8 MBsyntax highlighting
crypto-js, @stripe/stripe-js, @zxcvbn-ts/*~4 MB combined@clerk/electron@clerk/clerk-js

The single largest cluster is @clerk/clerk-js's transitive tree (core-js + lodash +
crypto-js + @stripe/stripe-js + @zxcvbn-ts/* ≈ 25 MB). clerk-js is the browser bundle and
ships prebuilt dist files; the hypothesis worth testing is that none of that tree is reachable through
Node resolution at runtime in the Electron main process, in which case it is pure dead weight.

There is already precedent for exactly this fix.DESKTOP_FILE_EXCLUSIONS in
scripts/build-desktop-artifact.ts:630 is a fork-owned constant that already excludes one such tree:

exportconstDESKTOP_FILE_EXCLUSIONS=[// T3 Code always passes the user's installed Claude executable to the SDK,// so the SDK's optional platform packages (each a ~200MB bundled executable)// are dead weight. The trailing dash keeps the SDK's own JS package."!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*",]asconst;

Adding entries here is cheap and already on the seam ledger.

Suggested order of work

  1. Instrument first — get a per-module size breakdown of the actual app.asar, not the install tree
    (npx asar list-size, or electron-builder's own size report). The numbers above are from the dev
    tree and are an upper bound, not the shipped cost.
  2. Test the @clerk/clerk-js hypothesis by excluding the tree and running
    apps/desktop's smoke-test plus a real sign-in. Biggest win if it holds.
  3. Check whether @shikijs/langs is genuinely reachable from the desktop graph or only from
    @t3tools/mobile — the trace was ambiguous, and the web client already ships its own per-language
    chunks (dist/assets/typescript-*.js etc.), so we may be shipping the grammar set twice.

4. Build time

  • Build desktop bundle (serialised) now genuinely runs at --concurrency-limit 1 (it was silently a
    no-op before 8e3af08a1). That is the mitigation for the Windows STATUS_DLL_INIT_FAILED
    (0xC0000142) crashes in Windows desktop build intermittently fails with STATUS_DLL_INIT_FAILED (0xC0000142) in parallel vp tasks #47, and it costs wall-clock on both platforms. Worth revisiting whether
    the limit can be raised to 2, or scoped to Windows only, once we have a few green runs as a baseline.
  • The mac leg spends real time on a cold pnpm store. Cache cargo and setup-vp's cache are already
    in place; a pnpm store cache is not.

Non-goals / traps

  • Do not "fix" the other build-log warning by adding platform binaries to optionalDependencies:
    ⚠ platform-specific optional dependencies not bundled — add them to your project's optionalDependencies …
    dependencies=["@clerk/electron-passkeys-win32-x64-msvc@0.0.3","@ff-labs/fff-bin-linux-x64-gnu@0.9.4", …]
    
    Every package it names is for a different platform than the one being built. Acting on this advice
    would bundle Windows and Linux binaries into the macOS dmg. The arm64 binaries the mac build actually
    needs were all resolved and packed. This warning is correct behaviour and should be ignored.
  • Do not drop playwright-core despite its 12 MB. It is loaded at runtime by
    apps/desktop/src/preview/PlaywrightInjectedRuntime.ts, which resolves playwright-core/package.json
    and injects its runtime source for the preview browser.
  • Any change to root package.json / pnpm-workspace.yaml carries a per-sync rebase cost. Prefer
    DESKTOP_FILE_EXCLUSIONS, which is already fork-owned, over dependency-resolution changes.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestready-for-agentSpecified enough for an agent to pick up

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
       blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      
      Skip to content

      Desktop release build optimization: size, time, and one false lead #53

      Description

      @radroid

      Context

      The fork now builds and publishes a macOS + Windows desktop bundle on every green merge to main
      (.github/workflows/t3x-release.yml, design: docs/superpowers/specs/2026-08-03-update-delivery-design.md).
      Because that runs on every merge and ships to users over the update relay, both the size of what we
      ship and the wall-clock of the build are now recurring costs rather than one-off ones.

      Baseline from run 31215844454, the first
      fully green release build:

      MetricValue
      macOS arm64 build leg7m14s (warm-ish runner)
      macOS staged artifact (zipped dmg + descriptor)149,875,427 B (~143 MiB)
      Packaged prod node_modules~200 modules

      This issue collects the optimization surface. No action is urgent — the pipeline works. Filing so
      it is not re-derived from scratch next time someone reads a build log.


      1. "duplicate dependency references" is a false lead — do not chase it

      electron-builder prints this in every build:

      • duplicate dependency references dependencies=["@clerk/electron-passkeys@0.0.3",
      "effect@4.0.0-beta.102","debug@4.4.3","cross-spawn@7.0.6","shiki@4.4.2", ...]
      

      It reads like a dependency-duplication problem. It is not. Verified against the actual implementation
      in app-builder-lib@26.15.6:

      • out/node-module-collector/pnpmNodeModulesCollector.js:100 pushes PKG_DUPLICATE_REF when a node in
        the pnpm list --prod --json --depth Infinity tree has dedupedDependenciesCount > 0 — i.e. when
        pnpm itself elided a subtree it had already printed elsewhere — and then re-resolves it from the
        full entry it already parsed.
      • out/node-module-collector/moduleManager.js:21 classifies it as level info, not warn.

      So the message means "this package is referenced by more than one parent", which is the normal, healthy
      shape of any dependency graph. Measured on this workspace:

      PackageReferences in the prod graphPhysical copies of that version
      effect@4.0.0-beta.102371
      debug@4.4.3331
      @types/hast@3.0.4291

      One copy, many referrers — that is deduplication working.

      The line that would actually matter is unresolved duplicate dependency references
      (PKG_DUPLICATE_REF_UNRESOLVED, level warn), emitted when electron-builder cannot re-resolve the
      elided subtree — that silently drops dependencies from the packaged app. It does not appear in our
      builds. If it ever does, treat it as a release blocker.

      2. Genuine duplicate installs (real, but trivial)

      Three packages really are installed twice in the shipped bundle, via version conflicts:

      PackageCopiesCost
      semver7.8.5 (top level) + 7.7.4 (under electron-updater)~51 files
      onetime5.1.2 (top level) + 7.0.0 (under restore-cursor)~3 files
      mimic-fn3.1.0 (top level) + 2.1.0 (under onetime)~3 files

      Fixable with pnpm.overrides, but the payoff is a few dozen KB against a ~143 MiB artifact, and each
      override is a root-package.json edit — i.e. a recurring sync-conflict cost. Recommend not doing
      this
      unless we are already touching overrides for another reason.

      3. The actual size surface

      Measured package sizes in the install tree, with the chain that pulls each into the desktop app's
      production graph:

      PackageSizePulled in by
      core-js15 MB (3,684 files)@clerk/electron@clerk/clerk-js
      playwright-core12 MBdirect dep of @t3tools/desktop
      @shikijs/langs9.9 MB (724 files)shiki language set
      @pierre/diffs8.7 MB (584 files)diff rendering
      @clerk/shared6.6 MB (764 files)@clerk/electron
      lodash4.9 MB@clerk/clerk-jsbrowser-tabs-lock
      shiki3.8 MBsyntax highlighting
      crypto-js, @stripe/stripe-js, @zxcvbn-ts/*~4 MB combined@clerk/electron@clerk/clerk-js

      The single largest cluster is @clerk/clerk-js's transitive tree (core-js + lodash +
      crypto-js + @stripe/stripe-js + @zxcvbn-ts/* ≈ 25 MB). clerk-js is the browser bundle and
      ships prebuilt dist files; the hypothesis worth testing is that none of that tree is reachable through
      Node resolution at runtime in the Electron main process, in which case it is pure dead weight.

      There is already precedent for exactly this fix.DESKTOP_FILE_EXCLUSIONS in
      scripts/build-desktop-artifact.ts:630 is a fork-owned constant that already excludes one such tree:

      exportconstDESKTOP_FILE_EXCLUSIONS=[// T3 Code always passes the user's installed Claude executable to the SDK,// so the SDK's optional platform packages (each a ~200MB bundled executable)// are dead weight. The trailing dash keeps the SDK's own JS package."!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*",]asconst;

      Adding entries here is cheap and already on the seam ledger.

      Suggested order of work

      1. Instrument first — get a per-module size breakdown of the actual app.asar, not the install tree
        (npx asar list-size, or electron-builder's own size report). The numbers above are from the dev
        tree and are an upper bound, not the shipped cost.
      2. Test the @clerk/clerk-js hypothesis by excluding the tree and running
        apps/desktop's smoke-test plus a real sign-in. Biggest win if it holds.
      3. Check whether @shikijs/langs is genuinely reachable from the desktop graph or only from
        @t3tools/mobile — the trace was ambiguous, and the web client already ships its own per-language
        chunks (dist/assets/typescript-*.js etc.), so we may be shipping the grammar set twice.

      4. Build time

      • Build desktop bundle (serialised) now genuinely runs at --concurrency-limit 1 (it was silently a
        no-op before 8e3af08a1). That is the mitigation for the Windows STATUS_DLL_INIT_FAILED
        (0xC0000142) crashes in Windows desktop build intermittently fails with STATUS_DLL_INIT_FAILED (0xC0000142) in parallel vp tasks #47, and it costs wall-clock on both platforms. Worth revisiting whether
        the limit can be raised to 2, or scoped to Windows only, once we have a few green runs as a baseline.
      • The mac leg spends real time on a cold pnpm store. Cache cargo and setup-vp's cache are already
        in place; a pnpm store cache is not.

      Non-goals / traps

      • Do not "fix" the other build-log warning by adding platform binaries to optionalDependencies:
        ⚠ platform-specific optional dependencies not bundled — add them to your project's optionalDependencies …
        dependencies=["@clerk/electron-passkeys-win32-x64-msvc@0.0.3","@ff-labs/fff-bin-linux-x64-gnu@0.9.4", …]
        
        Every package it names is for a different platform than the one being built. Acting on this advice
        would bundle Windows and Linux binaries into the macOS dmg. The arm64 binaries the mac build actually
        needs were all resolved and packed. This warning is correct behaviour and should be ignored.
      • Do not drop playwright-core despite its 12 MB. It is loaded at runtime by
        apps/desktop/src/preview/PlaywrightInjectedRuntime.ts, which resolves playwright-core/package.json
        and injects its runtime source for the preview browser.
      • Any change to root package.json / pnpm-workspace.yaml carries a per-sync rebase cost. Prefer
        DESKTOP_FILE_EXCLUSIONS, which is already fork-owned, over dependency-resolution changes.

      Activity

      Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        enhancementNew feature or requestready-for-agentSpecified enough for an agent to pick up

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          Desktop release build optimization: size, time, and one false lead #53

          Description

          @radroid

          Context

          The fork now builds and publishes a macOS + Windows desktop bundle on every green merge to main
          (.github/workflows/t3x-release.yml, design: docs/superpowers/specs/2026-08-03-update-delivery-design.md).
          Because that runs on every merge and ships to users over the update relay, both the size of what we
          ship and the wall-clock of the build are now recurring costs rather than one-off ones.

          Baseline from run 31215844454, the first
          fully green release build:

          MetricValue
          macOS arm64 build leg7m14s (warm-ish runner)
          macOS staged artifact (zipped dmg + descriptor)149,875,427 B (~143 MiB)
          Packaged prod node_modules~200 modules

          This issue collects the optimization surface. No action is urgent — the pipeline works. Filing so
          it is not re-derived from scratch next time someone reads a build log.


          1. "duplicate dependency references" is a false lead — do not chase it

          electron-builder prints this in every build:

          • duplicate dependency references dependencies=["@clerk/electron-passkeys@0.0.3",
          "effect@4.0.0-beta.102","debug@4.4.3","cross-spawn@7.0.6","shiki@4.4.2", ...]
          

          It reads like a dependency-duplication problem. It is not. Verified against the actual implementation
          in app-builder-lib@26.15.6:

          • out/node-module-collector/pnpmNodeModulesCollector.js:100 pushes PKG_DUPLICATE_REF when a node in
            the pnpm list --prod --json --depth Infinity tree has dedupedDependenciesCount > 0 — i.e. when
            pnpm itself elided a subtree it had already printed elsewhere — and then re-resolves it from the
            full entry it already parsed.
          • out/node-module-collector/moduleManager.js:21 classifies it as level info, not warn.

          So the message means "this package is referenced by more than one parent", which is the normal, healthy
          shape of any dependency graph. Measured on this workspace:

          PackageReferences in the prod graphPhysical copies of that version
          effect@4.0.0-beta.102371
          debug@4.4.3331
          @types/hast@3.0.4291

          One copy, many referrers — that is deduplication working.

          The line that would actually matter is unresolved duplicate dependency references
          (PKG_DUPLICATE_REF_UNRESOLVED, level warn), emitted when electron-builder cannot re-resolve the
          elided subtree — that silently drops dependencies from the packaged app. It does not appear in our
          builds. If it ever does, treat it as a release blocker.

          2. Genuine duplicate installs (real, but trivial)

          Three packages really are installed twice in the shipped bundle, via version conflicts:

          PackageCopiesCost
          semver7.8.5 (top level) + 7.7.4 (under electron-updater)~51 files
          onetime5.1.2 (top level) + 7.0.0 (under restore-cursor)~3 files
          mimic-fn3.1.0 (top level) + 2.1.0 (under onetime)~3 files

          Fixable with pnpm.overrides, but the payoff is a few dozen KB against a ~143 MiB artifact, and each
          override is a root-package.json edit — i.e. a recurring sync-conflict cost. Recommend not doing
          this
          unless we are already touching overrides for another reason.

          3. The actual size surface

          Measured package sizes in the install tree, with the chain that pulls each into the desktop app's
          production graph:

          PackageSizePulled in by
          core-js15 MB (3,684 files)@clerk/electron@clerk/clerk-js
          playwright-core12 MBdirect dep of @t3tools/desktop
          @shikijs/langs9.9 MB (724 files)shiki language set
          @pierre/diffs8.7 MB (584 files)diff rendering
          @clerk/shared6.6 MB (764 files)@clerk/electron
          lodash4.9 MB@clerk/clerk-jsbrowser-tabs-lock
          shiki3.8 MBsyntax highlighting
          crypto-js, @stripe/stripe-js, @zxcvbn-ts/*~4 MB combined@clerk/electron@clerk/clerk-js

          The single largest cluster is @clerk/clerk-js's transitive tree (core-js + lodash +
          crypto-js + @stripe/stripe-js + @zxcvbn-ts/* ≈ 25 MB). clerk-js is the browser bundle and
          ships prebuilt dist files; the hypothesis worth testing is that none of that tree is reachable through
          Node resolution at runtime in the Electron main process, in which case it is pure dead weight.

          There is already precedent for exactly this fix.DESKTOP_FILE_EXCLUSIONS in
          scripts/build-desktop-artifact.ts:630 is a fork-owned constant that already excludes one such tree:

          exportconstDESKTOP_FILE_EXCLUSIONS=[// T3 Code always passes the user's installed Claude executable to the SDK,// so the SDK's optional platform packages (each a ~200MB bundled executable)// are dead weight. The trailing dash keeps the SDK's own JS package."!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*",]asconst;

          Adding entries here is cheap and already on the seam ledger.

          Suggested order of work

          1. Instrument first — get a per-module size breakdown of the actual app.asar, not the install tree
            (npx asar list-size, or electron-builder's own size report). The numbers above are from the dev
            tree and are an upper bound, not the shipped cost.
          2. Test the @clerk/clerk-js hypothesis by excluding the tree and running
            apps/desktop's smoke-test plus a real sign-in. Biggest win if it holds.
          3. Check whether @shikijs/langs is genuinely reachable from the desktop graph or only from
            @t3tools/mobile — the trace was ambiguous, and the web client already ships its own per-language
            chunks (dist/assets/typescript-*.js etc.), so we may be shipping the grammar set twice.

          4. Build time

          • Build desktop bundle (serialised) now genuinely runs at --concurrency-limit 1 (it was silently a
            no-op before 8e3af08a1). That is the mitigation for the Windows STATUS_DLL_INIT_FAILED
            (0xC0000142) crashes in Windows desktop build intermittently fails with STATUS_DLL_INIT_FAILED (0xC0000142) in parallel vp tasks #47, and it costs wall-clock on both platforms. Worth revisiting whether
            the limit can be raised to 2, or scoped to Windows only, once we have a few green runs as a baseline.
          • The mac leg spends real time on a cold pnpm store. Cache cargo and setup-vp's cache are already
            in place; a pnpm store cache is not.

          Non-goals / traps

          • Do not "fix" the other build-log warning by adding platform binaries to optionalDependencies:
            ⚠ platform-specific optional dependencies not bundled — add them to your project's optionalDependencies …
            dependencies=["@clerk/electron-passkeys-win32-x64-msvc@0.0.3","@ff-labs/fff-bin-linux-x64-gnu@0.9.4", …]
            
            Every package it names is for a different platform than the one being built. Acting on this advice
            would bundle Windows and Linux binaries into the macOS dmg. The arm64 binaries the mac build actually
            needs were all resolved and packed. This warning is correct behaviour and should be ignored.
          • Do not drop playwright-core despite its 12 MB. It is loaded at runtime by
            apps/desktop/src/preview/PlaywrightInjectedRuntime.ts, which resolves playwright-core/package.json
            and injects its runtime source for the preview browser.
          • Any change to root package.json / pnpm-workspace.yaml carries a per-sync rebase cost. Prefer
            DESKTOP_FILE_EXCLUSIONS, which is already fork-owned, over dependency-resolution changes.

          Activity

          Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            enhancementNew feature or requestready-for-agentSpecified enough for an agent to pick up

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              Desktop release build optimization: size, time, and one false lead #53

              Description

              @radroid

              Context

              The fork now builds and publishes a macOS + Windows desktop bundle on every green merge to main
              (.github/workflows/t3x-release.yml, design: docs/superpowers/specs/2026-08-03-update-delivery-design.md).
              Because that runs on every merge and ships to users over the update relay, both the size of what we
              ship and the wall-clock of the build are now recurring costs rather than one-off ones.

              Baseline from run 31215844454, the first
              fully green release build:

              MetricValue
              macOS arm64 build leg7m14s (warm-ish runner)
              macOS staged artifact (zipped dmg + descriptor)149,875,427 B (~143 MiB)
              Packaged prod node_modules~200 modules

              This issue collects the optimization surface. No action is urgent — the pipeline works. Filing so
              it is not re-derived from scratch next time someone reads a build log.


              1. "duplicate dependency references" is a false lead — do not chase it

              electron-builder prints this in every build:

              • duplicate dependency references dependencies=["@clerk/electron-passkeys@0.0.3",
              "effect@4.0.0-beta.102","debug@4.4.3","cross-spawn@7.0.6","shiki@4.4.2", ...]
              

              It reads like a dependency-duplication problem. It is not. Verified against the actual implementation
              in app-builder-lib@26.15.6:

              • out/node-module-collector/pnpmNodeModulesCollector.js:100 pushes PKG_DUPLICATE_REF when a node in
                the pnpm list --prod --json --depth Infinity tree has dedupedDependenciesCount > 0 — i.e. when
                pnpm itself elided a subtree it had already printed elsewhere — and then re-resolves it from the
                full entry it already parsed.
              • out/node-module-collector/moduleManager.js:21 classifies it as level info, not warn.

              So the message means "this package is referenced by more than one parent", which is the normal, healthy
              shape of any dependency graph. Measured on this workspace:

              PackageReferences in the prod graphPhysical copies of that version
              effect@4.0.0-beta.102371
              debug@4.4.3331
              @types/hast@3.0.4291

              One copy, many referrers — that is deduplication working.

              The line that would actually matter is unresolved duplicate dependency references
              (PKG_DUPLICATE_REF_UNRESOLVED, level warn), emitted when electron-builder cannot re-resolve the
              elided subtree — that silently drops dependencies from the packaged app. It does not appear in our
              builds. If it ever does, treat it as a release blocker.

              2. Genuine duplicate installs (real, but trivial)

              Three packages really are installed twice in the shipped bundle, via version conflicts:

              PackageCopiesCost
              semver7.8.5 (top level) + 7.7.4 (under electron-updater)~51 files
              onetime5.1.2 (top level) + 7.0.0 (under restore-cursor)~3 files
              mimic-fn3.1.0 (top level) + 2.1.0 (under onetime)~3 files

              Fixable with pnpm.overrides, but the payoff is a few dozen KB against a ~143 MiB artifact, and each
              override is a root-package.json edit — i.e. a recurring sync-conflict cost. Recommend not doing
              this
              unless we are already touching overrides for another reason.

              3. The actual size surface

              Measured package sizes in the install tree, with the chain that pulls each into the desktop app's
              production graph:

              PackageSizePulled in by
              core-js15 MB (3,684 files)@clerk/electron@clerk/clerk-js
              playwright-core12 MBdirect dep of @t3tools/desktop
              @shikijs/langs9.9 MB (724 files)shiki language set
              @pierre/diffs8.7 MB (584 files)diff rendering
              @clerk/shared6.6 MB (764 files)@clerk/electron
              lodash4.9 MB@clerk/clerk-jsbrowser-tabs-lock
              shiki3.8 MBsyntax highlighting
              crypto-js, @stripe/stripe-js, @zxcvbn-ts/*~4 MB combined@clerk/electron@clerk/clerk-js

              The single largest cluster is @clerk/clerk-js's transitive tree (core-js + lodash +
              crypto-js + @stripe/stripe-js + @zxcvbn-ts/* ≈ 25 MB). clerk-js is the browser bundle and
              ships prebuilt dist files; the hypothesis worth testing is that none of that tree is reachable through
              Node resolution at runtime in the Electron main process, in which case it is pure dead weight.

              There is already precedent for exactly this fix.DESKTOP_FILE_EXCLUSIONS in
              scripts/build-desktop-artifact.ts:630 is a fork-owned constant that already excludes one such tree:

              exportconstDESKTOP_FILE_EXCLUSIONS=[// T3 Code always passes the user's installed Claude executable to the SDK,// so the SDK's optional platform packages (each a ~200MB bundled executable)// are dead weight. The trailing dash keeps the SDK's own JS package."!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*",]asconst;

              Adding entries here is cheap and already on the seam ledger.

              Suggested order of work

              1. Instrument first — get a per-module size breakdown of the actual app.asar, not the install tree
                (npx asar list-size, or electron-builder's own size report). The numbers above are from the dev
                tree and are an upper bound, not the shipped cost.
              2. Test the @clerk/clerk-js hypothesis by excluding the tree and running
                apps/desktop's smoke-test plus a real sign-in. Biggest win if it holds.
              3. Check whether @shikijs/langs is genuinely reachable from the desktop graph or only from
                @t3tools/mobile — the trace was ambiguous, and the web client already ships its own per-language
                chunks (dist/assets/typescript-*.js etc.), so we may be shipping the grammar set twice.

              4. Build time

              • Build desktop bundle (serialised) now genuinely runs at --concurrency-limit 1 (it was silently a
                no-op before 8e3af08a1). That is the mitigation for the Windows STATUS_DLL_INIT_FAILED
                (0xC0000142) crashes in Windows desktop build intermittently fails with STATUS_DLL_INIT_FAILED (0xC0000142) in parallel vp tasks #47, and it costs wall-clock on both platforms. Worth revisiting whether
                the limit can be raised to 2, or scoped to Windows only, once we have a few green runs as a baseline.
              • The mac leg spends real time on a cold pnpm store. Cache cargo and setup-vp's cache are already
                in place; a pnpm store cache is not.

              Non-goals / traps

              • Do not "fix" the other build-log warning by adding platform binaries to optionalDependencies:
                ⚠ platform-specific optional dependencies not bundled — add them to your project's optionalDependencies …
                dependencies=["@clerk/electron-passkeys-win32-x64-msvc@0.0.3","@ff-labs/fff-bin-linux-x64-gnu@0.9.4", …]
                
                Every package it names is for a different platform than the one being built. Acting on this advice
                would bundle Windows and Linux binaries into the macOS dmg. The arm64 binaries the mac build actually
                needs were all resolved and packed. This warning is correct behaviour and should be ignored.
              • Do not drop playwright-core despite its 12 MB. It is loaded at runtime by
                apps/desktop/src/preview/PlaywrightInjectedRuntime.ts, which resolves playwright-core/package.json
                and injects its runtime source for the preview browser.
              • Any change to root package.json / pnpm-workspace.yaml carries a per-sync rebase cost. Prefer
                DESKTOP_FILE_EXCLUSIONS, which is already fork-owned, over dependency-resolution changes.

              Activity

              Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                enhancementNew feature or requestready-for-agentSpecified enough for an agent to pick up

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  Desktop release build optimization: size, time, and one false lead #53

                  Description

                  @radroid

                  Context

                  The fork now builds and publishes a macOS + Windows desktop bundle on every green merge to main
                  (.github/workflows/t3x-release.yml, design: docs/superpowers/specs/2026-08-03-update-delivery-design.md).
                  Because that runs on every merge and ships to users over the update relay, both the size of what we
                  ship and the wall-clock of the build are now recurring costs rather than one-off ones.

                  Baseline from run 31215844454, the first
                  fully green release build:

                  MetricValue
                  macOS arm64 build leg7m14s (warm-ish runner)
                  macOS staged artifact (zipped dmg + descriptor)149,875,427 B (~143 MiB)
                  Packaged prod node_modules~200 modules

                  This issue collects the optimization surface. No action is urgent — the pipeline works. Filing so
                  it is not re-derived from scratch next time someone reads a build log.


                  1. "duplicate dependency references" is a false lead — do not chase it

                  electron-builder prints this in every build:

                  • duplicate dependency references dependencies=["@clerk/electron-passkeys@0.0.3",
                  "effect@4.0.0-beta.102","debug@4.4.3","cross-spawn@7.0.6","shiki@4.4.2", ...]
                  

                  It reads like a dependency-duplication problem. It is not. Verified against the actual implementation
                  in app-builder-lib@26.15.6:

                  • out/node-module-collector/pnpmNodeModulesCollector.js:100 pushes PKG_DUPLICATE_REF when a node in
                    the pnpm list --prod --json --depth Infinity tree has dedupedDependenciesCount > 0 — i.e. when
                    pnpm itself elided a subtree it had already printed elsewhere — and then re-resolves it from the
                    full entry it already parsed.
                  • out/node-module-collector/moduleManager.js:21 classifies it as level info, not warn.

                  So the message means "this package is referenced by more than one parent", which is the normal, healthy
                  shape of any dependency graph. Measured on this workspace:

                  PackageReferences in the prod graphPhysical copies of that version
                  effect@4.0.0-beta.102371
                  debug@4.4.3331
                  @types/hast@3.0.4291

                  One copy, many referrers — that is deduplication working.

                  The line that would actually matter is unresolved duplicate dependency references
                  (PKG_DUPLICATE_REF_UNRESOLVED, level warn), emitted when electron-builder cannot re-resolve the
                  elided subtree — that silently drops dependencies from the packaged app. It does not appear in our
                  builds. If it ever does, treat it as a release blocker.

                  2. Genuine duplicate installs (real, but trivial)

                  Three packages really are installed twice in the shipped bundle, via version conflicts:

                  PackageCopiesCost
                  semver7.8.5 (top level) + 7.7.4 (under electron-updater)~51 files
                  onetime5.1.2 (top level) + 7.0.0 (under restore-cursor)~3 files
                  mimic-fn3.1.0 (top level) + 2.1.0 (under onetime)~3 files

                  Fixable with pnpm.overrides, but the payoff is a few dozen KB against a ~143 MiB artifact, and each
                  override is a root-package.json edit — i.e. a recurring sync-conflict cost. Recommend not doing
                  this
                  unless we are already touching overrides for another reason.

                  3. The actual size surface

                  Measured package sizes in the install tree, with the chain that pulls each into the desktop app's
                  production graph:

                  PackageSizePulled in by
                  core-js15 MB (3,684 files)@clerk/electron@clerk/clerk-js
                  playwright-core12 MBdirect dep of @t3tools/desktop
                  @shikijs/langs9.9 MB (724 files)shiki language set
                  @pierre/diffs8.7 MB (584 files)diff rendering
                  @clerk/shared6.6 MB (764 files)@clerk/electron
                  lodash4.9 MB@clerk/clerk-jsbrowser-tabs-lock
                  shiki3.8 MBsyntax highlighting
                  crypto-js, @stripe/stripe-js, @zxcvbn-ts/*~4 MB combined@clerk/electron@clerk/clerk-js

                  The single largest cluster is @clerk/clerk-js's transitive tree (core-js + lodash +
                  crypto-js + @stripe/stripe-js + @zxcvbn-ts/* ≈ 25 MB). clerk-js is the browser bundle and
                  ships prebuilt dist files; the hypothesis worth testing is that none of that tree is reachable through
                  Node resolution at runtime in the Electron main process, in which case it is pure dead weight.

                  There is already precedent for exactly this fix.DESKTOP_FILE_EXCLUSIONS in
                  scripts/build-desktop-artifact.ts:630 is a fork-owned constant that already excludes one such tree:

                  exportconstDESKTOP_FILE_EXCLUSIONS=[// T3 Code always passes the user's installed Claude executable to the SDK,// so the SDK's optional platform packages (each a ~200MB bundled executable)// are dead weight. The trailing dash keeps the SDK's own JS package."!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*",]asconst;

                  Adding entries here is cheap and already on the seam ledger.

                  Suggested order of work

                  1. Instrument first — get a per-module size breakdown of the actual app.asar, not the install tree
                    (npx asar list-size, or electron-builder's own size report). The numbers above are from the dev
                    tree and are an upper bound, not the shipped cost.
                  2. Test the @clerk/clerk-js hypothesis by excluding the tree and running
                    apps/desktop's smoke-test plus a real sign-in. Biggest win if it holds.
                  3. Check whether @shikijs/langs is genuinely reachable from the desktop graph or only from
                    @t3tools/mobile — the trace was ambiguous, and the web client already ships its own per-language
                    chunks (dist/assets/typescript-*.js etc.), so we may be shipping the grammar set twice.

                  4. Build time

                  • Build desktop bundle (serialised) now genuinely runs at --concurrency-limit 1 (it was silently a
                    no-op before 8e3af08a1). That is the mitigation for the Windows STATUS_DLL_INIT_FAILED
                    (0xC0000142) crashes in Windows desktop build intermittently fails with STATUS_DLL_INIT_FAILED (0xC0000142) in parallel vp tasks #47, and it costs wall-clock on both platforms. Worth revisiting whether
                    the limit can be raised to 2, or scoped to Windows only, once we have a few green runs as a baseline.
                  • The mac leg spends real time on a cold pnpm store. Cache cargo and setup-vp's cache are already
                    in place; a pnpm store cache is not.

                  Non-goals / traps

                  • Do not "fix" the other build-log warning by adding platform binaries to optionalDependencies:
                    ⚠ platform-specific optional dependencies not bundled — add them to your project's optionalDependencies …
                    dependencies=["@clerk/electron-passkeys-win32-x64-msvc@0.0.3","@ff-labs/fff-bin-linux-x64-gnu@0.9.4", …]
                    
                    Every package it names is for a different platform than the one being built. Acting on this advice
                    would bundle Windows and Linux binaries into the macOS dmg. The arm64 binaries the mac build actually
                    needs were all resolved and packed. This warning is correct behaviour and should be ignored.
                  • Do not drop playwright-core despite its 12 MB. It is loaded at runtime by
                    apps/desktop/src/preview/PlaywrightInjectedRuntime.ts, which resolves playwright-core/package.json
                    and injects its runtime source for the preview browser.
                  • Any change to root package.json / pnpm-workspace.yaml carries a per-sync rebase cost. Prefer
                    DESKTOP_FILE_EXCLUSIONS, which is already fork-owned, over dependency-resolution changes.

                  Activity

                  Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    enhancementNew feature or requestready-for-agentSpecified enough for an agent to pick up

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      Desktop release build optimization: size, time, and one false lead #53

                      Description

                      @radroid

                      Context

                      The fork now builds and publishes a macOS + Windows desktop bundle on every green merge to main
                      (.github/workflows/t3x-release.yml, design: docs/superpowers/specs/2026-08-03-update-delivery-design.md).
                      Because that runs on every merge and ships to users over the update relay, both the size of what we
                      ship and the wall-clock of the build are now recurring costs rather than one-off ones.

                      Baseline from run 31215844454, the first
                      fully green release build:

                      MetricValue
                      macOS arm64 build leg7m14s (warm-ish runner)
                      macOS staged artifact (zipped dmg + descriptor)149,875,427 B (~143 MiB)
                      Packaged prod node_modules~200 modules

                      This issue collects the optimization surface. No action is urgent — the pipeline works. Filing so
                      it is not re-derived from scratch next time someone reads a build log.


                      1. "duplicate dependency references" is a false lead — do not chase it

                      electron-builder prints this in every build:

                      • duplicate dependency references dependencies=["@clerk/electron-passkeys@0.0.3",
                      "effect@4.0.0-beta.102","debug@4.4.3","cross-spawn@7.0.6","shiki@4.4.2", ...]
                      

                      It reads like a dependency-duplication problem. It is not. Verified against the actual implementation
                      in app-builder-lib@26.15.6:

                      • out/node-module-collector/pnpmNodeModulesCollector.js:100 pushes PKG_DUPLICATE_REF when a node in
                        the pnpm list --prod --json --depth Infinity tree has dedupedDependenciesCount > 0 — i.e. when
                        pnpm itself elided a subtree it had already printed elsewhere — and then re-resolves it from the
                        full entry it already parsed.
                      • out/node-module-collector/moduleManager.js:21 classifies it as level info, not warn.

                      So the message means "this package is referenced by more than one parent", which is the normal, healthy
                      shape of any dependency graph. Measured on this workspace:

                      PackageReferences in the prod graphPhysical copies of that version
                      effect@4.0.0-beta.102371
                      debug@4.4.3331
                      @types/hast@3.0.4291

                      One copy, many referrers — that is deduplication working.

                      The line that would actually matter is unresolved duplicate dependency references
                      (PKG_DUPLICATE_REF_UNRESOLVED, level warn), emitted when electron-builder cannot re-resolve the
                      elided subtree — that silently drops dependencies from the packaged app. It does not appear in our
                      builds. If it ever does, treat it as a release blocker.

                      2. Genuine duplicate installs (real, but trivial)

                      Three packages really are installed twice in the shipped bundle, via version conflicts:

                      PackageCopiesCost
                      semver7.8.5 (top level) + 7.7.4 (under electron-updater)~51 files
                      onetime5.1.2 (top level) + 7.0.0 (under restore-cursor)~3 files
                      mimic-fn3.1.0 (top level) + 2.1.0 (under onetime)~3 files

                      Fixable with pnpm.overrides, but the payoff is a few dozen KB against a ~143 MiB artifact, and each
                      override is a root-package.json edit — i.e. a recurring sync-conflict cost. Recommend not doing
                      this
                      unless we are already touching overrides for another reason.

                      3. The actual size surface

                      Measured package sizes in the install tree, with the chain that pulls each into the desktop app's
                      production graph:

                      PackageSizePulled in by
                      core-js15 MB (3,684 files)@clerk/electron@clerk/clerk-js
                      playwright-core12 MBdirect dep of @t3tools/desktop
                      @shikijs/langs9.9 MB (724 files)shiki language set
                      @pierre/diffs8.7 MB (584 files)diff rendering
                      @clerk/shared6.6 MB (764 files)@clerk/electron
                      lodash4.9 MB@clerk/clerk-jsbrowser-tabs-lock
                      shiki3.8 MBsyntax highlighting
                      crypto-js, @stripe/stripe-js, @zxcvbn-ts/*~4 MB combined@clerk/electron@clerk/clerk-js

                      The single largest cluster is @clerk/clerk-js's transitive tree (core-js + lodash +
                      crypto-js + @stripe/stripe-js + @zxcvbn-ts/* ≈ 25 MB). clerk-js is the browser bundle and
                      ships prebuilt dist files; the hypothesis worth testing is that none of that tree is reachable through
                      Node resolution at runtime in the Electron main process, in which case it is pure dead weight.

                      There is already precedent for exactly this fix.DESKTOP_FILE_EXCLUSIONS in
                      scripts/build-desktop-artifact.ts:630 is a fork-owned constant that already excludes one such tree:

                      exportconstDESKTOP_FILE_EXCLUSIONS=[// T3 Code always passes the user's installed Claude executable to the SDK,// so the SDK's optional platform packages (each a ~200MB bundled executable)// are dead weight. The trailing dash keeps the SDK's own JS package."!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*",]asconst;

                      Adding entries here is cheap and already on the seam ledger.

                      Suggested order of work

                      1. Instrument first — get a per-module size breakdown of the actual app.asar, not the install tree
                        (npx asar list-size, or electron-builder's own size report). The numbers above are from the dev
                        tree and are an upper bound, not the shipped cost.
                      2. Test the @clerk/clerk-js hypothesis by excluding the tree and running
                        apps/desktop's smoke-test plus a real sign-in. Biggest win if it holds.
                      3. Check whether @shikijs/langs is genuinely reachable from the desktop graph or only from
                        @t3tools/mobile — the trace was ambiguous, and the web client already ships its own per-language
                        chunks (dist/assets/typescript-*.js etc.), so we may be shipping the grammar set twice.

                      4. Build time

                      • Build desktop bundle (serialised) now genuinely runs at --concurrency-limit 1 (it was silently a
                        no-op before 8e3af08a1). That is the mitigation for the Windows STATUS_DLL_INIT_FAILED
                        (0xC0000142) crashes in Windows desktop build intermittently fails with STATUS_DLL_INIT_FAILED (0xC0000142) in parallel vp tasks #47, and it costs wall-clock on both platforms. Worth revisiting whether
                        the limit can be raised to 2, or scoped to Windows only, once we have a few green runs as a baseline.
                      • The mac leg spends real time on a cold pnpm store. Cache cargo and setup-vp's cache are already
                        in place; a pnpm store cache is not.

                      Non-goals / traps

                      • Do not "fix" the other build-log warning by adding platform binaries to optionalDependencies:
                        ⚠ platform-specific optional dependencies not bundled — add them to your project's optionalDependencies …
                        dependencies=["@clerk/electron-passkeys-win32-x64-msvc@0.0.3","@ff-labs/fff-bin-linux-x64-gnu@0.9.4", …]
                        
                        Every package it names is for a different platform than the one being built. Acting on this advice
                        would bundle Windows and Linux binaries into the macOS dmg. The arm64 binaries the mac build actually
                        needs were all resolved and packed. This warning is correct behaviour and should be ignored.
                      • Do not drop playwright-core despite its 12 MB. It is loaded at runtime by
                        apps/desktop/src/preview/PlaywrightInjectedRuntime.ts, which resolves playwright-core/package.json
                        and injects its runtime source for the preview browser.
                      • Any change to root package.json / pnpm-workspace.yaml carries a per-sync rebase cost. Prefer
                        DESKTOP_FILE_EXCLUSIONS, which is already fork-owned, over dependency-resolution changes.

                      Activity

                      Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        enhancementNew feature or requestready-for-agentSpecified enough for an agent to pick up

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          Desktop release build optimization: size, time, and one false lead #53

                          Description

                          @radroid

                          Context

                          The fork now builds and publishes a macOS + Windows desktop bundle on every green merge to main
                          (.github/workflows/t3x-release.yml, design: docs/superpowers/specs/2026-08-03-update-delivery-design.md).
                          Because that runs on every merge and ships to users over the update relay, both the size of what we
                          ship and the wall-clock of the build are now recurring costs rather than one-off ones.

                          Baseline from run 31215844454, the first
                          fully green release build:

                          MetricValue
                          macOS arm64 build leg7m14s (warm-ish runner)
                          macOS staged artifact (zipped dmg + descriptor)149,875,427 B (~143 MiB)
                          Packaged prod node_modules~200 modules

                          This issue collects the optimization surface. No action is urgent — the pipeline works. Filing so
                          it is not re-derived from scratch next time someone reads a build log.


                          1. "duplicate dependency references" is a false lead — do not chase it

                          electron-builder prints this in every build:

                          • duplicate dependency references dependencies=["@clerk/electron-passkeys@0.0.3",
                          "effect@4.0.0-beta.102","debug@4.4.3","cross-spawn@7.0.6","shiki@4.4.2", ...]
                          

                          It reads like a dependency-duplication problem. It is not. Verified against the actual implementation
                          in app-builder-lib@26.15.6:

                          • out/node-module-collector/pnpmNodeModulesCollector.js:100 pushes PKG_DUPLICATE_REF when a node in
                            the pnpm list --prod --json --depth Infinity tree has dedupedDependenciesCount > 0 — i.e. when
                            pnpm itself elided a subtree it had already printed elsewhere — and then re-resolves it from the
                            full entry it already parsed.
                          • out/node-module-collector/moduleManager.js:21 classifies it as level info, not warn.

                          So the message means "this package is referenced by more than one parent", which is the normal, healthy
                          shape of any dependency graph. Measured on this workspace:

                          PackageReferences in the prod graphPhysical copies of that version
                          effect@4.0.0-beta.102371
                          debug@4.4.3331
                          @types/hast@3.0.4291

                          One copy, many referrers — that is deduplication working.

                          The line that would actually matter is unresolved duplicate dependency references
                          (PKG_DUPLICATE_REF_UNRESOLVED, level warn), emitted when electron-builder cannot re-resolve the
                          elided subtree — that silently drops dependencies from the packaged app. It does not appear in our
                          builds. If it ever does, treat it as a release blocker.

                          2. Genuine duplicate installs (real, but trivial)

                          Three packages really are installed twice in the shipped bundle, via version conflicts:

                          PackageCopiesCost
                          semver7.8.5 (top level) + 7.7.4 (under electron-updater)~51 files
                          onetime5.1.2 (top level) + 7.0.0 (under restore-cursor)~3 files
                          mimic-fn3.1.0 (top level) + 2.1.0 (under onetime)~3 files

                          Fixable with pnpm.overrides, but the payoff is a few dozen KB against a ~143 MiB artifact, and each
                          override is a root-package.json edit — i.e. a recurring sync-conflict cost. Recommend not doing
                          this
                          unless we are already touching overrides for another reason.

                          3. The actual size surface

                          Measured package sizes in the install tree, with the chain that pulls each into the desktop app's
                          production graph:

                          PackageSizePulled in by
                          core-js15 MB (3,684 files)@clerk/electron@clerk/clerk-js
                          playwright-core12 MBdirect dep of @t3tools/desktop
                          @shikijs/langs9.9 MB (724 files)shiki language set
                          @pierre/diffs8.7 MB (584 files)diff rendering
                          @clerk/shared6.6 MB (764 files)@clerk/electron
                          lodash4.9 MB@clerk/clerk-jsbrowser-tabs-lock
                          shiki3.8 MBsyntax highlighting
                          crypto-js, @stripe/stripe-js, @zxcvbn-ts/*~4 MB combined@clerk/electron@clerk/clerk-js

                          The single largest cluster is @clerk/clerk-js's transitive tree (core-js + lodash +
                          crypto-js + @stripe/stripe-js + @zxcvbn-ts/* ≈ 25 MB). clerk-js is the browser bundle and
                          ships prebuilt dist files; the hypothesis worth testing is that none of that tree is reachable through
                          Node resolution at runtime in the Electron main process, in which case it is pure dead weight.

                          There is already precedent for exactly this fix.DESKTOP_FILE_EXCLUSIONS in
                          scripts/build-desktop-artifact.ts:630 is a fork-owned constant that already excludes one such tree:

                          exportconstDESKTOP_FILE_EXCLUSIONS=[// T3 Code always passes the user's installed Claude executable to the SDK,// so the SDK's optional platform packages (each a ~200MB bundled executable)// are dead weight. The trailing dash keeps the SDK's own JS package."!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*",]asconst;

                          Adding entries here is cheap and already on the seam ledger.

                          Suggested order of work

                          1. Instrument first — get a per-module size breakdown of the actual app.asar, not the install tree
                            (npx asar list-size, or electron-builder's own size report). The numbers above are from the dev
                            tree and are an upper bound, not the shipped cost.
                          2. Test the @clerk/clerk-js hypothesis by excluding the tree and running
                            apps/desktop's smoke-test plus a real sign-in. Biggest win if it holds.
                          3. Check whether @shikijs/langs is genuinely reachable from the desktop graph or only from
                            @t3tools/mobile — the trace was ambiguous, and the web client already ships its own per-language
                            chunks (dist/assets/typescript-*.js etc.), so we may be shipping the grammar set twice.

                          4. Build time

                          • Build desktop bundle (serialised) now genuinely runs at --concurrency-limit 1 (it was silently a
                            no-op before 8e3af08a1). That is the mitigation for the Windows STATUS_DLL_INIT_FAILED
                            (0xC0000142) crashes in Windows desktop build intermittently fails with STATUS_DLL_INIT_FAILED (0xC0000142) in parallel vp tasks #47, and it costs wall-clock on both platforms. Worth revisiting whether
                            the limit can be raised to 2, or scoped to Windows only, once we have a few green runs as a baseline.
                          • The mac leg spends real time on a cold pnpm store. Cache cargo and setup-vp's cache are already
                            in place; a pnpm store cache is not.

                          Non-goals / traps

                          • Do not "fix" the other build-log warning by adding platform binaries to optionalDependencies:
                            ⚠ platform-specific optional dependencies not bundled — add them to your project's optionalDependencies …
                            dependencies=["@clerk/electron-passkeys-win32-x64-msvc@0.0.3","@ff-labs/fff-bin-linux-x64-gnu@0.9.4", …]
                            
                            Every package it names is for a different platform than the one being built. Acting on this advice
                            would bundle Windows and Linux binaries into the macOS dmg. The arm64 binaries the mac build actually
                            needs were all resolved and packed. This warning is correct behaviour and should be ignored.
                          • Do not drop playwright-core despite its 12 MB. It is loaded at runtime by
                            apps/desktop/src/preview/PlaywrightInjectedRuntime.ts, which resolves playwright-core/package.json
                            and injects its runtime source for the preview browser.
                          • Any change to root package.json / pnpm-workspace.yaml carries a per-sync rebase cost. Prefer
                            DESKTOP_FILE_EXCLUSIONS, which is already fork-owned, over dependency-resolution changes.

                          Activity

                          Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            enhancementNew feature or requestready-for-agentSpecified enough for an agent to pick up

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              Desktop release build optimization: size, time, and one false lead #53

                              Description

                              @radroid

                              Context

                              The fork now builds and publishes a macOS + Windows desktop bundle on every green merge to main
                              (.github/workflows/t3x-release.yml, design: docs/superpowers/specs/2026-08-03-update-delivery-design.md).
                              Because that runs on every merge and ships to users over the update relay, both the size of what we
                              ship and the wall-clock of the build are now recurring costs rather than one-off ones.

                              Baseline from run 31215844454, the first
                              fully green release build:

                              MetricValue
                              macOS arm64 build leg7m14s (warm-ish runner)
                              macOS staged artifact (zipped dmg + descriptor)149,875,427 B (~143 MiB)
                              Packaged prod node_modules~200 modules

                              This issue collects the optimization surface. No action is urgent — the pipeline works. Filing so
                              it is not re-derived from scratch next time someone reads a build log.


                              1. "duplicate dependency references" is a false lead — do not chase it

                              electron-builder prints this in every build:

                              • duplicate dependency references dependencies=["@clerk/electron-passkeys@0.0.3",
                              "effect@4.0.0-beta.102","debug@4.4.3","cross-spawn@7.0.6","shiki@4.4.2", ...]
                              

                              It reads like a dependency-duplication problem. It is not. Verified against the actual implementation
                              in app-builder-lib@26.15.6:

                              • out/node-module-collector/pnpmNodeModulesCollector.js:100 pushes PKG_DUPLICATE_REF when a node in
                                the pnpm list --prod --json --depth Infinity tree has dedupedDependenciesCount > 0 — i.e. when
                                pnpm itself elided a subtree it had already printed elsewhere — and then re-resolves it from the
                                full entry it already parsed.
                              • out/node-module-collector/moduleManager.js:21 classifies it as level info, not warn.

                              So the message means "this package is referenced by more than one parent", which is the normal, healthy
                              shape of any dependency graph. Measured on this workspace:

                              PackageReferences in the prod graphPhysical copies of that version
                              effect@4.0.0-beta.102371
                              debug@4.4.3331
                              @types/hast@3.0.4291

                              One copy, many referrers — that is deduplication working.

                              The line that would actually matter is unresolved duplicate dependency references
                              (PKG_DUPLICATE_REF_UNRESOLVED, level warn), emitted when electron-builder cannot re-resolve the
                              elided subtree — that silently drops dependencies from the packaged app. It does not appear in our
                              builds. If it ever does, treat it as a release blocker.

                              2. Genuine duplicate installs (real, but trivial)

                              Three packages really are installed twice in the shipped bundle, via version conflicts:

                              PackageCopiesCost
                              semver7.8.5 (top level) + 7.7.4 (under electron-updater)~51 files
                              onetime5.1.2 (top level) + 7.0.0 (under restore-cursor)~3 files
                              mimic-fn3.1.0 (top level) + 2.1.0 (under onetime)~3 files

                              Fixable with pnpm.overrides, but the payoff is a few dozen KB against a ~143 MiB artifact, and each
                              override is a root-package.json edit — i.e. a recurring sync-conflict cost. Recommend not doing
                              this
                              unless we are already touching overrides for another reason.

                              3. The actual size surface

                              Measured package sizes in the install tree, with the chain that pulls each into the desktop app's
                              production graph:

                              PackageSizePulled in by
                              core-js15 MB (3,684 files)@clerk/electron@clerk/clerk-js
                              playwright-core12 MBdirect dep of @t3tools/desktop
                              @shikijs/langs9.9 MB (724 files)shiki language set
                              @pierre/diffs8.7 MB (584 files)diff rendering
                              @clerk/shared6.6 MB (764 files)@clerk/electron
                              lodash4.9 MB@clerk/clerk-jsbrowser-tabs-lock
                              shiki3.8 MBsyntax highlighting
                              crypto-js, @stripe/stripe-js, @zxcvbn-ts/*~4 MB combined@clerk/electron@clerk/clerk-js

                              The single largest cluster is @clerk/clerk-js's transitive tree (core-js + lodash +
                              crypto-js + @stripe/stripe-js + @zxcvbn-ts/* ≈ 25 MB). clerk-js is the browser bundle and
                              ships prebuilt dist files; the hypothesis worth testing is that none of that tree is reachable through
                              Node resolution at runtime in the Electron main process, in which case it is pure dead weight.

                              There is already precedent for exactly this fix.DESKTOP_FILE_EXCLUSIONS in
                              scripts/build-desktop-artifact.ts:630 is a fork-owned constant that already excludes one such tree:

                              exportconstDESKTOP_FILE_EXCLUSIONS=[// T3 Code always passes the user's installed Claude executable to the SDK,// so the SDK's optional platform packages (each a ~200MB bundled executable)// are dead weight. The trailing dash keeps the SDK's own JS package."!**/node_modules/@anthropic-ai/claude-agent-sdk-*/**/*",]asconst;

                              Adding entries here is cheap and already on the seam ledger.

                              Suggested order of work

                              1. Instrument first — get a per-module size breakdown of the actual app.asar, not the install tree
                                (npx asar list-size, or electron-builder's own size report). The numbers above are from the dev
                                tree and are an upper bound, not the shipped cost.
                              2. Test the @clerk/clerk-js hypothesis by excluding the tree and running
                                apps/desktop's smoke-test plus a real sign-in. Biggest win if it holds.
                              3. Check whether @shikijs/langs is genuinely reachable from the desktop graph or only from
                                @t3tools/mobile — the trace was ambiguous, and the web client already ships its own per-language
                                chunks (dist/assets/typescript-*.js etc.), so we may be shipping the grammar set twice.

                              4. Build time

                              • Build desktop bundle (serialised) now genuinely runs at --concurrency-limit 1 (it was silently a
                                no-op before 8e3af08a1). That is the mitigation for the Windows STATUS_DLL_INIT_FAILED
                                (0xC0000142) crashes in Windows desktop build intermittently fails with STATUS_DLL_INIT_FAILED (0xC0000142) in parallel vp tasks #47, and it costs wall-clock on both platforms. Worth revisiting whether
                                the limit can be raised to 2, or scoped to Windows only, once we have a few green runs as a baseline.
                              • The mac leg spends real time on a cold pnpm store. Cache cargo and setup-vp's cache are already
                                in place; a pnpm store cache is not.

                              Non-goals / traps

                              • Do not "fix" the other build-log warning by adding platform binaries to optionalDependencies:
                                ⚠ platform-specific optional dependencies not bundled — add them to your project's optionalDependencies …
                                dependencies=["@clerk/electron-passkeys-win32-x64-msvc@0.0.3","@ff-labs/fff-bin-linux-x64-gnu@0.9.4", …]
                                
                                Every package it names is for a different platform than the one being built. Acting on this advice
                                would bundle Windows and Linux binaries into the macOS dmg. The arm64 binaries the mac build actually
                                needs were all resolved and packed. This warning is correct behaviour and should be ignored.
                              • Do not drop playwright-core despite its 12 MB. It is loaded at runtime by
                                apps/desktop/src/preview/PlaywrightInjectedRuntime.ts, which resolves playwright-core/package.json
                                and injects its runtime source for the preview browser.
                              • Any change to root package.json / pnpm-workspace.yaml carries a per-sync rebase cost. Prefer
                                DESKTOP_FILE_EXCLUSIONS, which is already fork-owned, over dependency-resolution changes.

                              Activity

                              Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                enhancementNew feature or requestready-for-agentSpecified enough for an agent to pick up

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions