Skip to content

feat: harden macOS distribution pipeline - #526

Merged
Juliusolsson05 merged 7 commits into
mainfrom
feat/mac-distribution-readiness
Jul 17, 2026
Merged

Juliusolsson05 merged 7 commits into
mainfrom
feat/mac-distribution-readiness

Conversation

@Juliusolsson05

Copy link
Copy Markdown
Owner

Summary

  • upgrade the release runtime to Node 24, Electron 43, electron-vite 5, and Vite 7
  • include the remote phone client in every desktop production build
  • make dist:mac fetch and strictly verify all bundled runtime tools
  • ship dual thin arm64/x64 DMG and ZIP artifacts with architecture-pruned runtimes
  • add permanent bundle identity, Monterey floor, microphone/local-network metadata, privacy notice, and redistributed licenses
  • add package structure, native architecture, and real packaged-process smoke checks
  • gate release CI on typecheck, 481 tests, production audit, signing, Gatekeeper, and notarization validation
  • require package version and GitHub release tag to match exactly

Verification

  • npm run typecheck
  • CI=1 npm test: 77 files, 481 tests passed
  • npm run audit:production: 0 vulnerabilities
  • npm run build
  • npm run package:mac
  • npm run verify:package:mac: arm64 and x64 passed
  • npm run smoke:package:mac: packaged arm64 process booted successfully

Generated unsigned development artifacts are approximately 211 MB for arm64 and 215 MB for x64. Public signing remains intentionally blocked until a Developer ID Application certificate is created and installed; the machine currently has Apple Development identities only.

Juliusolsson05 and others added 4 commits July 12, 2026 15:20
- Restart-safe MCP host on macOS: do not tear it down in
  window-all-closed; on that platform the process outlives the last
  window and the next session:spawn otherwise threw "Built-in MCP host
  must be started before registering a session", cascading into the
  workspace-partial-restore + AUTOSAVE-OFF banner.

- Voice dictation now runs on the quiet path by default:
  - Default hotkey flipped from bare Fn to Cmd+Shift+D. Fn requires the
    macOS Accessibility permission via CGEventTap and was prompting
    every packaged unsigned launch; Cmd+Shift+D goes through
    globalShortcut and never touches Accessibility.
  - configureDictationHotkey now classifies the requested binding and
    only routes Fn / bare-modifier / unknown-key bindings through the
    CGEventTap helper. Standard accelerators translate to Electron
    accelerators inline.
  - Deepgram API key persists via Electron safeStorage in a sibling
    file (src/main/dictation/apiKeyStore.ts). Runtime reader honours
    DEEPGRAM_API_KEY env only as a dev override.
  - Settings row lets the user paste, mask-inspect, and clear the key;
    IPC never returns the raw key, only the last-four hint.
  - Above-tab-bar "Configure Voice Dictation" card nudges unconfigured
    users to a modal guide with signup + setup steps; screenshot slots
    are placeholders until the release assets land.

- Autosave-off banner auto-collapses to a corner chip after 60 s and
  can be re-expanded on click, so a durable-degraded run stops
  permanently blocking the top of the chrome.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Standalone Node script that exercises every tmux command surface Agent
Code uses, against the bundled `third_party/tmux/cache/<arch>/tmux`
binary and a private TMUX_TMPDIR socket dir — so it can't collide with
the user's own tmux server. Steps:

  - resolve the pinned bundled binary path
  - verify `tmux -V` returns the expected version
  - allocate the isolated socket directory
  - list sessions on a fresh (empty) socket, tolerating every
    "no server running" phrasing the code path tolerates
  - detached new-session with `-c <cwd>` and a benign shell command
  - apply and read-back the three UI-suppression flags
    (status/mouse/aggressive-resize) using show-options
  - attach over a pty (via /usr/bin/script) to prove the session is
    reachable end-to-end; gracefully skipped when stdin is not a TTY
    or `script(1)` is missing, so CI/piped runs still exercise the
    other 15 steps
  - kill-session + confirm the session is gone
  - kill-server as final teardown

Run with:
  npm run smoke:tmux            # inherit shell env
  npm run smoke:tmux:minimal    # simulate Finder-launched minimal PATH

The `--minimal-env` mode reproduces the launchd-inherited environment a
Dock-launched Electron app sees (`/usr/bin:/bin:/usr/sbin:/sbin` PATH,
no rc-file exports) so a regression that only affects that surface is
observable without repackaging the whole app.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PR self-review finding: hotkeyBinding.ts still exported 'Fn' as the
default, and that constant feeds two live paths — coerceHotkeyBinding's
fallback for corrupted persisted values, and HotkeyInput's "reset to
default" button. Both would have silently re-armed the
Accessibility-gated CGEventTap helper that the Cmd+Shift+D default
exists to avoid. Now documented as lockstep with
DEFAULT_SETTINGS.dictationShortcut.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Superseded by #533, which merged the tested combined macOS distribution work and its integration fixes into main.

@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Correction: #533 was reverted by #534 because these changes are unfinished. Reopening this PR for its original review and development lifecycle.

Juliusolsson05 and others added 3 commits July 17, 2026 14:47
com.juliusolsson.agentcode -> com.agentcode.app before anything ships.
The bundle id is the one distribution fact that cannot change later
without orphaning keychain entries, user defaults, and notarization
history, so it must be final before the first packaged build reaches a
user.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
globalShortcut exposes an accelerator activation callback, not physical
key edges. Firing down+up on each activation made every quiet-path press
look like a zero-millisecond hold, which the renderer correctly discards
as an accidental tap - so Cmd+Shift+D dictation never recorded. The
quiet path is now press-to-start / press-again-to-finish, with a single
drain point that emits the old binding's up edge on reconfigure/disable
so no orphan recording listens forever. The CGEventTap Fn path keeps
real edges and stays hold-to-talk.

Settings copy, the guide modal, and the overlay hint now describe the
toggle honestly, and the overlay picks its hint from the configured
shortcut kind. The guide modal also gains a focus trap and captures
app-owned Cmd/Option chords at the window level so a guide opened above
the workspace cannot split/close panes underneath it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Union resolutions, no side discarded silently:
- release.yml: main's type-check/workflow-mcp/split-test gates kept
  (they subsume this branch's plain typecheck + npm test) plus this
  branch's production-dependency audit step.
- package.json: main's workflow-mcp:build preludes kept on
  dev/start/typecheck/test*; build:app chains workflow-mcp:build AND
  this branch's client:build; dist:mac keeps this branch's
  runtime:prepare:mac prelude; branch-only audit/verify/smoke scripts
  retained; @opentelemetry stays at this branch's ^2.9.0 alongside
  main's new radix deps.
- vitest.config.ts: main's broader flow-electron exclusion subsumes
  this branch's *.test.ts-only variant; branch's CI retry + serial
  integration files auto-merged.
- package-lock.json: regenerated from the merged package.json on top
  of main's lock.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Juliusolsson05
Juliusolsson05 merged commit 2d717bb into main Jul 17, 2026
@Juliusolsson05
Juliusolsson05 deleted the feat/mac-distribution-readiness branch July 17, 2026 13:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant