Fix calendar reconnect, recording rename, updater, and analytics by duetCJackson · Pull Request #19 · DuetDisplay/AutoDoc-Local · GitHub
Skip to content

Fix calendar reconnect, recording rename, updater, and analytics#19

Merged
duetCJackson merged 15 commits into
mainfrom
bugFix06.12.26
Jun 18, 2026
Merged

Fix calendar reconnect, recording rename, updater, and analytics#19
duetCJackson merged 15 commits into
mainfrom
bugFix06.12.26

Conversation

@duetCJackson

@duetCJackson duetCJackson commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Fix calendar OAuth reconnect getting stuck after an abandoned tab (AD-86/AD-88): supersede in-flight connect attempts in the main process, await loopback teardown, and share a useCalendarConnect hook across onboarding, settings, and the homepage banner with focus-based cancellation.
  • Fix recordings being renamed from all-day calendar matches (AD-87): ignore all-day events when deriving display titles, with shared getRecordingDisplayCalendarTitle used by the recording list/detail, Windows background refresh, and Ask AI/search inventory.
  • Harden macOS updater and release pipeline: correct entitlements, signed update artifact verification, updater smoke harness, and safer restart/quit lifecycle during installs.
  • Expand analytics: PostHog capture verification, release metadata reporting, GitHub download count ingestion workflow, and consent-aware renderer instrumentation.

Test plan

  • Start calendar connect, close the OAuth tab without finishing, return to AutoDoc, and confirm Google/Microsoft connect buttons work again from onboarding, homepage banner, and settings
  • Record during an all-day calendar event (e.g. "Home") and confirm the recording keeps its source-based title in the list, detail view, and Ask AI/search
  • Record during a timed calendar event and confirm the calendar title is still applied
  • Run npm run test:main:run -- src/main/ipc/__tests__/recording-ipc.test.ts src/main/services/__tests__/calendar-validity.test.ts
  • Run npm run test:run -- src/renderer/src/pages/Settings.test.tsx
  • Run npm run test:e2e -- e2e/calendar-reconnect-stuck.spec.ts (real-setup profile)
  • Verify macOS update install/restart flow and signed artifact verification scripts on a release build

duetCJackson and others added 15 commits June 12, 2026 12:16
When an OAuth tab was closed before completion, CalendarManager stayed
in a connecting state with the loopback server still bound, so every
later connect attempt (onboarding, homepage banner, settings) failed.

Main process: connect() now supersedes any in-flight attempt instead of
rejecting it, and cancelConnect()/loopback server teardown are awaitable
to avoid EADDRINUSE on the next attempt.

Renderer: extract a shared useCalendarConnect hook with an app-level
focus listener that cancels abandoned flows, replacing the
Settings-only cleanup so onboarding and the homepage banner recover too.

Also make the calendar account store and token store lazy so they
resolve userData on first use (after dev/e2e repath), matching
auto-record-store and fixing test/dev profile isolation.

Co-authored-by: Cursor <cursoragent@cursor.com>
The AD-87 all-day guard lived as a local helper in recording-ipc.ts, so
chat-retrieval's Ask AI/search inventory still derived titles from all-day
calendar matches and the Windows background refresh persisted them.

Extract getRecordingDisplayCalendarTitle into the shared recording-title
module and route the recording list/detail, the inventory builder, and the
Windows title refresh through it so every surface excludes all-day events
consistently.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 27

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/renderer/src/hooks/useRecording.ts (1)

308-320: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Rollback in useRecordingActions can fail before local reset and mask the original start error.

On Line 318, recording:stop is awaited without a catch inside the error handler. If that IPC fails, Line 319 (reset()) is skipped and the thrown error no longer reflects the original capture-start failure.

🔧 Proposed fix
           await stopCapture()
-          await window.electronAPI.invoke('recording:stop')
+          await window.electronAPI.invoke('recording:stop').catch(() => {})
           reset()
           throw err
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/src/hooks/useRecording.ts` around lines 308 - 320, The error
handler in useRecordingActions has a critical issue where if the
window.electronAPI.invoke('recording:stop') call on line 317 fails, it throws an
error before reset() on line 318 is executed, leaving local state unreset and
masking the original capture-start error. Wrap both the stopCapture() and
window.electronAPI.invoke('recording:stop') calls in a try-catch block (or
finally block) to ensure reset() is always called regardless of whether those
cleanup operations succeed, then rethrow the original err variable so the
correct capture-start error propagates instead of any IPC failure.
src/main/services/calendar-manager.ts (1)

89-99: ⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Concurrent supersede callers can still start overlapping OAuth connects.

At Line 93, multiple callers can enter the this.connecting branch at the same time, await the same cancellation, then each continue and run provider.connect() concurrently. That can reintroduce loopback port contention and orphaned pending attempts.

Suggested hardening (serialize supersede handoff)
 export class CalendarManager {
+  private connectHandoffGate: Promise<void> = Promise.resolve()
+  private currentConnectSettled: Promise<void> = Promise.resolve()
   private providers: Map<string, CalendarProvider>
   private accounts: CalendarAccount[] = []
@@
   async connect(providerType: 'google' | 'microsoft'): Promise<CalendarAccount> {
-    if (this.connecting) {
-      await this.cancelConnect()
-    }
-
-    this.connecting = true
+    const previousGate = this.connectHandoffGate
+    let releaseGate!: () => void
+    this.connectHandoffGate = new Promise<void>((resolve) => {
+      releaseGate = resolve
+    })
+    await previousGate
+    try {
+      if (this.connecting) {
+        await this.cancelConnect()
+        await this.currentConnectSettled
+      }
+      this.connecting = true
+    } finally {
+      releaseGate()
+    }
+
     const attemptId = ++this.connectAttemptId
+    let markSettled!: () => void
+    this.currentConnectSettled = new Promise<void>((resolve) => {
+      markSettled = resolve
+    })
     try {
@@
     } finally {
       if (this.connectAttemptId === attemptId) {
         this.connecting = false
       }
+      markSettled()
     }
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/services/calendar-manager.ts` around lines 89 - 99, Multiple callers
can race past the cancellation checkpoint in the connect method because they all
see this.connecting is true, await the same cancelConnect() call, and then all
proceed to call provider.connect() concurrently. The fix is to leverage the
attemptId tracking mechanism that already exists to serialize the handoff: after
the await this.cancelConnect() line, add a check to verify that
this.connectAttemptId still equals the local attemptId variable before
continuing with the rest of the connection logic, so that any superseded callers
exit early instead of proceeding to the actual provider connection attempt.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/download-counts.yml:
- Around line 33-37: The GitHub Actions in this workflow use mutable version
tags that create supply-chain security risks. Replace the
`actions/checkout@v6.0.2` action with the pinned commit SHA `44c2b74` and add
the `persist-credentials: false` option beneath it to prevent credential
persistence. Replace the `actions/setup-node@v6.4.0` action with the pinned
commit SHA `48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e`. Pinning to full commit
SHAs ensures immutability and prevents malicious retargeting of action versions.

In `@e2e/calendar-reconnect-stuck.spec.ts`:
- Line 80: In the test function definition for 'AD-79 a new connect supersedes
an abandoned attempt instead of getting stuck' (and the other test at line 143),
replace the empty object pattern `{}` with a named parameter like `_fixtures` in
the async callback signature. This indicates that the fixture object parameter
is intentionally unused and satisfies the no-empty-pattern ESLint rule, changing
`async ({}, testInfo)` to `async (_fixtures, testInfo)`.

In `@scripts/ingest-github-download-counts.js`:
- Around line 88-93: The fetch calls in the GitHub and PostHog request handlers
(at lines 88-93 and also at lines 218-227) lack timeout protection and can hang
indefinitely. Create an AbortController with a timeout (e.g., 10-30 seconds)
before each fetch call, pass its signal to the fetch options, and ensure the
controller aborts if the request exceeds the timeout. Apply this pattern to both
fetch locations to ensure ingestion fails fast instead of stalling the workflow.

In `@scripts/macos-install-policy-smoke.sh`:
- Around line 417-442: The launch_app_bundle function logs a failure message
when the app fails to start within the timeout period, but does not exit with an
error code, allowing the script to continue with invalid state. After the final
while loop that checks for the binary launch attempt (after the vlog call on
line 441), add a die call to terminate the script with an error when neither the
open command nor the direct binary execution successfully starts the AutoDoc
process within the wait_sec timeout.

In `@scripts/macos-updater-smoke.sh`:
- Around line 240-242: The smoke test assertions contain hard-coded version
numbers and zip file names (specifically "0.1.24" and
"AutoDoc-0.1.24-arm64-mac.zip") that will cause the script to fail whenever the
release version changes. Replace the hard-coded version string "0.1.24" in the
feed_head_ok function call and the zip file name reference with a variable that
captures the current or expected version. Apply this same fix to all three
locations mentioned (around lines 240, 278, and 300) by extracting the version
into a parameter or variable at the beginning of the script and reusing it
throughout, rather than embedding specific version numbers directly in the
assertion URLs and file names.

In `@src/main/ipc/__tests__/recording-ipc.test.ts`:
- Around line 275-287: Remove the `as any` casts from the mock dependency
objects passed to registerRecordingIpc across all three test blocks (lines
275-287, 348-360, and 422-434). Instead, properly type each mock object by
extracting the actual parameter types from the registerRecordingIpc function
signature using Parameters<typeof registerRecordingIpc>, or create a local typed
factory function to generate correctly typed mock objects. This will maintain
TypeScript lint compliance without suppressing the no-explicit-any rule.

In `@src/main/services/__tests__/analytics-state-store.test.ts`:
- Around line 78-87: The test "marks daily active once per local day" only
validates daily active behavior using UTC-aligned times. Add a test case that
crosses UTC midnight while remaining within the same local day by setting the
system time to a timezone-offset scenario (such as UTC+5 or UTC-5) and advancing
time across UTC midnight, then verifying that markDailyActive still correctly
returns tracked as false since the local day has not changed. This will prevent
regressions in the day bucketing logic for timezone boundaries.

In `@src/main/services/__tests__/calendar-validity.test.ts`:
- Around line 112-120: The cancelConnect mock function is currently synchronous
and cannot detect if the code stops awaiting async provider cancellation. Modify
the cancelConnect mock to return a Promise instead of executing synchronously,
such as returning Promise.resolve() or a deferred promise, to ensure the test
will properly fail if CalendarManager.connect() no longer awaits the async
cancellation from the provider. Apply this change to the cancelConnect mock
definition and ensure any related assertions in the supersede path (around lines
133-147) are similarly hardened to test async cancellation behavior.

In `@src/main/services/analytics-state-store.ts`:
- Around line 14-23: The todayIsoDate function currently uses toISOString()
which returns UTC time, causing users to transition to a "new day" before local
midnight. Fix this by modifying the todayIsoDate function to derive the date
from local time instead of UTC time. Use local date methods such as
toLocaleDateString with 'en-CA' locale (which returns YYYY-MM-DD format) or
manually construct the date string using getFullYear(), getMonth(), and
getDate() methods to ensure the function returns the local calendar date as an
ISO-formatted string.

In `@src/main/services/auto-updater.ts`:
- Around line 168-175: The onUpdateDownloaded callback on line 171 is invoked
without error handling, which means if the callback throws an exception, the
handler will exit early and skip critical follow-up logic like the smoke install
scheduling. Wrap the onUpdateDownloaded?.(status) call in a try-catch block to
isolate it from the rest of the update-downloaded event handler flow, ensuring
that any exceptions thrown by the app-provided callback don't prevent the
shouldQuitAndInstallAfterDownloadForSmoke() check and subsequent installUpdate()
scheduling from executing.

In `@src/main/services/calendar-manager.ts`:
- Around line 126-129: The cancelConnect method uses Promise.all which will
immediately reject if any single provider's cancelConnect call fails, preventing
remaining providers from completing their cleanup. Replace Promise.all with
Promise.allSettled in the cancelConnect method to ensure all providers have an
opportunity to complete their cancellation/teardown operations regardless of
whether some fail, preventing incomplete loopback teardown.

In `@src/main/services/chat-retrieval.ts`:
- Around line 2847-2848: The calendar materialization cache signature built at
lines 2892-2899 is missing the isAllDay property from the calendar event, even
though the getRecordingDisplayCalendarTitle function at line 2847 depends on
isAllDay to derive the calendarTitle. Add the isAllDay field from
matchedCalendarEvent to the cache signature so that when an event toggles
between timed and all-day status, it generates a different cache key and returns
the correct materialized entry instead of stale data.

In `@src/renderer/index.html`:
- Line 9: The CSP meta tag content attribute allows script execution from an
external third-party origin (https://us-assets.i.posthog.com) in the script-src
directive, which creates a security vulnerability in the Electron renderer.
Remove the https://us-assets.i.posthog.com domain from script-src so it contains
only 'self', keeping the external domain in connect-src where it already exists
for API connections only.

In `@src/renderer/src/App.tsx`:
- Line 59: The functions UpdateReadyPrompt (line 59), and the functions at lines
149 and 223 are missing explicit return type annotations, causing ESLint errors.
Add explicit return types to each of these three functions by specifying the
return type after the closing parenthesis of the function signature (e.g., `:
ReturnType` before the opening brace). Determine the appropriate return type for
each function based on what it returns, such as JSX.Element, void, or other
relevant types, then add these annotations to satisfy the linter requirements.
- Around line 104-112: The handleRestart function sets the update status to
'installing' and invokes the updater:install command, but lacks error handling
for when the invoke promise rejects. Add a catch or error handler to the
window.electronAPI.invoke('updater:install') call within handleRestart to
gracefully handle installation failures. When the promise rejects, update the
setUpdateStatus back to a recoverable state (such as the previous update status
before 'installing' was set) so the button becomes re-enabled and the user can
retry the installation instead of being stuck in an unrecoverable installing
state.

In `@src/renderer/src/components/onboarding/CalendarStep.tsx`:
- Line 4: The CalendarStep function is missing an explicit return type
annotation required by the TypeScript linter rule. Add the return type
annotation JSX.Element to the CalendarStep function signature by adding `:
JSX.Element` after the closing parenthesis of the parameters and before the
opening curly brace.

In `@src/renderer/src/components/onboarding/OllamaStep.tsx`:
- Around line 25-43: The recordStatus function sets the error state only when
status.phase is 'error' but never clears it for non-error updates, causing the
UI to remain in a failed state even as setup progresses. Additionally,
lastFailureKey.current is never reset when setup recovers, so repeated failures
remain incorrectly deduped. To fix this, add logic to clear the error state (by
calling setError with an empty string or appropriate default) whenever
status.phase is not 'error', and reset lastFailureKey.current to an empty string
or null when transitioning out of the error state to ensure proper deduplication
for future failures.
- Line 12: The setupStartedAt useRef is being initialized with performance.now()
at render time, which violates React's purity rules. Move the initialization
from the render-time useRef declaration to inside the useEffect hook where
trackEvent('setup_component_started', ...) is called. Initialize
setupStartedAt.current with performance.now() at the beginning of that useEffect
to ensure timing starts when setup actually begins, not when the component
renders.

In `@src/renderer/src/components/onboarding/TranscriptionStep.tsx`:
- Around line 85-96: The lastFailureKey.current reference variable is not being
reset when a user manually retries the transcription setup, which causes
duplicate failures with the same failureKey to be skipped from analytics
tracking after a retry. Locate where manual retry is triggered in the
TranscriptionStep component and clear the lastFailureKey.current value at that
point to ensure the failure deduplication logic properly resets and the next
failure event is tracked correctly in analytics.
- Line 30: The setupStartedAt useRef is calling performance.now() during
component render, which violates React's purity rules since it produces
different results on each render. Change the initialization of setupStartedAt to
useRef<number | null>(null) at line 30, then locate the existing useEffect hook
around line 122 (the one containing the trackEvent call) and add code at the
beginning of that useEffect to set setupStartedAt.current = performance.now()
before the trackEvent is called. This ensures the timestamp is captured only
once when the effect runs, not on every render.

In `@src/renderer/src/hooks/useCalendarConnect.ts`:
- Around line 53-55: The direct assignment to optionsRef.current during render
is violating the react-hooks/refs rule by causing a side effect during the
render phase. Move the optionsRef.current = options assignment out of the render
scope and into a useEffect hook with [options] as the dependency array. This
ensures the ref is synchronized with the options prop safely during the effect
phase rather than during render.

In `@src/renderer/src/hooks/useRecording.ts`:
- Around line 211-213: The duration is being captured after the stop and
finalize IPC calls in the handleStop function, which causes the store state to
be reset before elapsedSeconds is read, resulting in duration_bucket always
being calculated as 0-9s. Move the code that reads elapsedSeconds from
useRecordingStore.getState() and calculates durationBucket using
toDurationBucket before the IPC calls are made, so the correct elapsed time is
captured for the trackEvent call with recording_stopped.

In `@src/renderer/src/pages/AskAI.tsx`:
- Around line 175-181: The analytics event tracking for canceled chat responses
is only triggered inside the chat:canceled listener, but when handleStop is
called it immediately invokes finishActiveRequest() which tears down all
listeners, causing the canceled event to be missed and the analytics to not be
recorded. Move the trackEvent call with failure_code: 'canceled' to the
handleStop function or ensure it is called before finishActiveRequest() tears
down the listeners, so the canceled analytics event is always tracked when the
user manually stops the chat regardless of whether the chat:canceled event is
received.

In `@src/renderer/src/pages/Onboarding.tsx`:
- Line 38: The `startedAt` ref is calling the impure function
`performance.now()` during render, which violates React's purity requirements.
Remove the `performance.now()` call from the `useRef` initialization in the
render phase and instead move the timestamp initialization into a useEffect
hook. Initialize `startedAt` as an empty ref during render, then set its value
in a useEffect hook that runs once on component mount to capture the actual
timestamp.

In `@src/renderer/src/pages/Settings.tsx`:
- Around line 119-151: The update analytics events are being triggered on state
value rather than state transition, causing duplicate emissions on re-renders.
Add state transition guards similar to the existing check for the 'downloading'
state (previousState !== 'downloading') to prevent duplicate event emissions.
Specifically, add previousState checks to the conditions for the 'available',
'downloaded', and 'error' state checks in the trackEvent calls to ensure events
only fire when the updateStatus.state actually transitions to those states
rather than on every render while in those states.
- Around line 105-107: Remove the setState call `setIsInstallingUpdate(false)`
from within the effect body in the updateStatus.state check. Instead, derive the
`isInstallingUpdate` state directly from `updateStatus.state` without using an
effect—for example, compute it as a derived value where `isInstallingUpdate`
equals true only when `updateStatus.state === 'installing'`, or move the state
reset to an event callback that fires when the installation process completes
(such as an onInstallationComplete handler) rather than inside the effect.

In `@src/renderer/src/services/analytics.ts`:
- Around line 45-105: The ALLOWED_PROPERTIES set in the
sanitizeAnalyticsProperties function is missing five analytics properties that
are being emitted from src/renderer/src/App.tsx: meeting_window_visible,
window_missing_polls, provider_missing_polls, mic_silent_polls, and
recovered_signals. These properties are currently being silently stripped from
analytics payloads. Add these five missing property names to the
ALLOWED_PROPERTIES set to prevent them from being discarded during sanitization.

---

Outside diff comments:
In `@src/main/services/calendar-manager.ts`:
- Around line 89-99: Multiple callers can race past the cancellation checkpoint
in the connect method because they all see this.connecting is true, await the
same cancelConnect() call, and then all proceed to call provider.connect()
concurrently. The fix is to leverage the attemptId tracking mechanism that
already exists to serialize the handoff: after the await this.cancelConnect()
line, add a check to verify that this.connectAttemptId still equals the local
attemptId variable before continuing with the rest of the connection logic, so
that any superseded callers exit early instead of proceeding to the actual
provider connection attempt.

In `@src/renderer/src/hooks/useRecording.ts`:
- Around line 308-320: The error handler in useRecordingActions has a critical
issue where if the window.electronAPI.invoke('recording:stop') call on line 317
fails, it throws an error before reset() on line 318 is executed, leaving local
state unreset and masking the original capture-start error. Wrap both the
stopCapture() and window.electronAPI.invoke('recording:stop') calls in a
try-catch block (or finally block) to ensure reset() is always called regardless
of whether those cleanup operations succeed, then rethrow the original err
variable so the correct capture-start error propagates instead of any IPC
failure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e0f46262-a2f0-4c5a-b369-72345f6f5d7b

📥 Commits

Reviewing files that changed from the base of the PR and between 8a9966f and a4ba643.

📒 Files selected for processing (58)
  • .github/workflows/build.yml
  • .github/workflows/download-counts.yml
  • .gitignore
  • PRIVACY.md
  • PRODUCT.md
  • README.md
  • build/entitlements.mac.plist
  • e2e/calendar-reconnect-stuck.spec.ts
  • electron-builder.yml
  • package.json
  • scripts/after-pack-mac-privacy.cjs
  • scripts/ingest-github-download-counts.js
  • scripts/macos-install-policy-smoke.sh
  • scripts/macos-updater-smoke.sh
  • scripts/verify-macos-update-artifact.sh
  • src/main/index.ts
  • src/main/ipc/__tests__/recording-ipc.test.ts
  • src/main/ipc/analytics-ipc.ts
  • src/main/ipc/calendar-ipc.ts
  • src/main/ipc/recording-ipc.ts
  • src/main/services/__tests__/analytics-state-store.test.ts
  • src/main/services/__tests__/auto-updater.test.ts
  • src/main/services/__tests__/calendar-validity.test.ts
  • src/main/services/analytics-state-store.ts
  • src/main/services/auto-updater.ts
  • src/main/services/calendar-manager.ts
  • src/main/services/calendar-types.ts
  • src/main/services/calendar.ts
  • src/main/services/chat-retrieval.ts
  • src/main/services/microsoft-calendar.ts
  • src/main/services/recording-title.ts
  • src/main/services/token-store.ts
  • src/preload/ipc.d.ts
  • src/renderer/index.html
  • src/renderer/src/App.test.tsx
  • src/renderer/src/App.tsx
  • src/renderer/src/components/RecordingControls.tsx
  • src/renderer/src/components/__tests__/RecordingControls.test.tsx
  • src/renderer/src/components/onboarding/AnalyticsStep.tsx
  • src/renderer/src/components/onboarding/CalendarStep.tsx
  • src/renderer/src/components/onboarding/MicPermissionStep.tsx
  • src/renderer/src/components/onboarding/OllamaStep.tsx
  • src/renderer/src/components/onboarding/ScreenPermissionStep.tsx
  • src/renderer/src/components/onboarding/TranscriptionStep.tsx
  • src/renderer/src/hooks/useCalendarConnect.ts
  • src/renderer/src/hooks/useRecording.ts
  • src/renderer/src/pages/AskAI.tsx
  • src/renderer/src/pages/MeetingDetail.tsx
  • src/renderer/src/pages/Onboarding.tsx
  • src/renderer/src/pages/Search.tsx
  • src/renderer/src/pages/Settings.test.tsx
  • src/renderer/src/pages/Settings.tsx
  • src/renderer/src/pages/Upcoming.tsx
  • src/renderer/src/services/__tests__/analytics.test.ts
  • src/renderer/src/services/analytics.ts
  • src/renderer/src/test/fixtures.ts
  • src/shared/external-modules.d.ts
  • src/shared/types.ts
💤 Files with no reviewable changes (1)
  • build/entitlements.mac.plist

Comment thread .github/workflows/download-counts.yml
Comment thread e2e/calendar-reconnect-stuck.spec.ts
Comment thread scripts/ingest-github-download-counts.js
Comment thread scripts/macos-install-policy-smoke.sh
Comment thread scripts/macos-updater-smoke.sh
Comment thread src/renderer/src/pages/AskAI.tsx
Comment thread src/renderer/src/pages/Onboarding.tsx
Comment thread src/renderer/src/pages/Settings.tsx
Comment thread src/renderer/src/pages/Settings.tsx
Comment thread src/renderer/src/services/analytics.ts
@duetCJackson duetCJackson merged commit a4d49ba into main Jun 18, 2026
9 checks passed
duetCJackson added a commit that referenced this pull request Jun 18, 2026
PR #19 (eef1aeb) dropped com.apple.security.device.audio-input from the
macOS entitlements on the mistaken belief that it invalidates Developer ID
signatures — a reaction to a misdiagnosed "invalid signature" report. That
entitlement is the Hardened Runtime gate for microphone access, so without it
macOS auto-denies askForMediaAccess with no prompt, AutoDoc never appears in
Privacy > Microphone, and recordings capture no audio. Screen recording uses a
separate TCC path and kept working, which masked the cause. v0.1.21–v0.1.46
shipped this entitlement, signed/notarized fine, with working mics.

Restore the entitlement and flip both CI guards in
verify-macos-update-artifact.sh so they now require it (on the source plist and
the signed app) instead of forbidding it, preventing a silent regression.

Co-authored-by: Cursor <cursoragent@cursor.com>
duetCJackson added a commit that referenced this pull request Jun 22, 2026
* Fix macOS transcription runtime SIGKILL from missing JIT entitlements

The `mac.signIgnore` rule for Contents/Resources stops electron-builder
from applying the inherited Hardened Runtime entitlements to the bundled
MLX/Python transcription runtime. The afterPack signer then re-signed those
binaries with `--options runtime` but no entitlements, so the bundled
python lacked allow-jit / allow-unsigned-executable-memory. macOS SIGKILLed
the runtime ("Code Signature Invalid" on a JIT page) the moment MLX Whisper
was imported, which surfaced to QA as the recurring "transcription setup"
failure (and blocked the onboarding flow that registers microphone TCC).

- afterPack now signs bundled resource Mach-O binaries with the app
  entitlements file (allow-jit etc.), restoring the entitlements that
  signing inheritance used to provide before signIgnore was added.
- verify-macos-update-artifact.sh now probes the bundled runtime
  (asserts allow-jit on python3 and imports mlx_whisper) so this class of
  regression fails CI instead of shipping. codesign --verify cannot catch
  it because the signature is valid; only an actual import does.
- The verify script also mounts and verifies the DMG (the artifact users
  install), not just the auto-update zip.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix onboarding calendar Continue button not appearing after OAuth

The focus listener added for abandoned-OAuth recovery also fires when OAuth
completes successfully (returning to the app refocuses the window). It cleared
the in-flight attempt before calendar:connect resolved, so onConnected never
ran and the UI never advanced — the onboarding "Continue" button only appeared
after a relaunch re-read the persisted account. The focus handler now waits a
short grace period and only abandons the attempt if it has not resolved on its
own, so a successful connection is no longer dropped.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Restore microphone entitlement removed by PR #19

PR #19 (eef1aeb) dropped com.apple.security.device.audio-input from the
macOS entitlements on the mistaken belief that it invalidates Developer ID
signatures — a reaction to a misdiagnosed "invalid signature" report. That
entitlement is the Hardened Runtime gate for microphone access, so without it
macOS auto-denies askForMediaAccess with no prompt, AutoDoc never appears in
Privacy > Microphone, and recordings capture no audio. Screen recording uses a
separate TCC path and kept working, which masked the cause. v0.1.21–v0.1.46
shipped this entitlement, signed/notarized fine, with working mics.

Restore the entitlement and flip both CI guards in
verify-macos-update-artifact.sh so they now require it (on the source plist and
the signed app) instead of forbidding it, preventing a silent regression.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix calendar connect latency and connecting-state flash

The connect promise resolved only after the loopback server fully closed,
but the browser holds a keep-alive socket to 127.0.0.1, so server.close()
waited on its idle timeout (~2-3s) even though the OAuth tokens were already
in hand. Resolve/reject the moment tokens arrive and tear down the server in
the background, and drop lingering sockets via closeAllConnections() so the
cancel/retry port-rebind path stays fast.

Also stop clearing the "Connecting" state on window blur: that flipped the
button back the instant it was pressed (a click-time visual hitch). Keep it
disabled until the (now near-instant) result arrives, and clear it on window
focus instead so an abandoned OAuth tab can be retried. Focus only clears the
display, not the attempt token, so a success that lands just after the user
returns still surfaces.

Return the calendar account and broadcast connection-changed before the
best-effort upcoming-events fetch so the renderer isn't gated on a second
network round-trip.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
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