Skip to content

Add diagnostics consent and fork-safe defaults#9

Merged
duetCJackson merged 7 commits into
mainfrom
improvements
May 24, 2026
Merged

Add diagnostics consent and fork-safe defaults#9
duetCJackson merged 7 commits into
mainfrom
improvements

Conversation

@duetCJackson

Copy link
Copy Markdown
Collaborator

What changed

  • split diagnostic log upload consent from analytics/crash reporting consent in prefs, onboarding, and Settings
  • added optional sanitized log attachments for failure reporting when the user has explicitly enabled log upload
  • added a reusable notification flow for Notes Ready, including durable per-meeting dedupe and navigation into the finished notes
  • hardened distribution defaults so forks and unofficial builds fail closed unless they explicitly provide their own OAuth worker and managed runtime asset URLs
  • updated the GitHub Actions build workflow to read official infrastructure endpoints from repo-scoped Actions variables instead of relying on checked-in defaults

Why

We wanted to make diagnostics consent more precise, keep privacy copy aligned with actual behavior, add a completion notification for finished notes, and stop forks from silently inheriting Duet-operated infrastructure by default.

Impact

  • users can keep analytics/crash reporting enabled while disabling log uploads
  • logs are only attached to failure reports when the separate opt-in is enabled
  • users get a Notes Ready notification when notes are fully prepared
  • forks can still build and modify the app, but they no longer automatically inherit Duet OAuth or hosted runtime asset configuration

Validation

  • targeted main-process and renderer test coverage for prefs, onboarding, Sentry/log upload behavior, notification dedupe, segmentation completion, and distribution config
  • Playwright Electron coverage for onboarding persistence, diagnostic log upload behavior, and Notes Ready notification flows
  • npm run build
  • npm exec prettier -- --check .github/workflows/build.yml

Notes

  • the related AutoDoc-Landing privacy page update lives in a separate repo and is not included in this PR
  • an unrelated local script (scripts/macOSResetKeepMeetings.sh) was intentionally excluded from this branch

@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

<review_stack_artifact>

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add diagnostics consent and fork-safe defaults' clearly summarizes the main changes: splitting diagnostic consent and hardening distribution defaults for forks.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, covering what changed, why, impact, and validation performed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • LINEAR integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

Comment @coderabbitai help to get the list of available commands and usage tips.

@duetCJackson duetCJackson changed the title [codex] Add diagnostics consent and fork-safe defaults Add diagnostics consent and fork-safe defaults May 18, 2026

@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: 14

Caution

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

⚠️ Outside diff range comments (1)
src/main/index.ts (1)

321-340: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Expand Sentry event scrubbing beyond homeDir.

beforeSend() only replaces the current homedir(), but the new controlled failure payload includes a raw email, an access token, and an absolute path that usually will not match the runner's real home directory. Those values can still be shipped in the Sentry event whenever analytics consent is enabled, even with diagnostic log upload disabled. Reuse the same redaction rules as the diagnostic-log attachment sanitizer here, or strip sensitive keys before capture.

Also applies to: 1230-1241

🤖 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/index.ts` around lines 321 - 340, beforeSend currently only scrubs
homeDir via scrubString and can leak emails, tokens, and absolute paths; update
beforeSend/scrubString (used by deepScrub) to apply the same redaction rules as
the diagnostic-log attachment sanitizer (or reuse that sanitizer) so Sentry
events remove/replace raw emails, access tokens (e.g., Authorization headers,
bearer tokens), and any absolute filesystem paths not matching homedir, and also
strip sensitive keys like user.email, request.headers.authorization, and any
attachment/payload fields before returning the event; keep the
analyticsConsentEnabled guard and ensure delete event.server_name remains in
place.
🤖 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 `@e2e/diagnostic-log-upload.spec.ts`:
- Around line 7-14: The readStubEnvelopes function is brittle because it does a
single immediate read; change readStubEnvelopes to be retry-aware (polling with
a small delay and a total timeout) so it waits for the file to exist and contain
data before parsing; implement retries with exponential/backoff or fixed sleep
(e.g., check file presence/size then read, retry until success or timeout) and
surface a clear error on timeout; update any other direct calls that read the
same stub (the other occurrences noted in the comment) to use this retrying
helper so E2E runs are not flaky.

In `@src/main/notification-window.ts`:
- Around line 69-73: The title/body/primaryActionLabel assignments only escape
quotes and must also escape &, < and > to prevent HTML/script injection; create
or use a helper like escapeHtml that replaces & to &amp;, < to &lt;, > to &gt;,
and also ' and " to &`#39`; / &quot;, then apply it to the existing variables
(title, body, primaryActionLabel) and the other occurrences referenced (the
variables at the other block around lines 204-205) so all notification text
inserted into the data-URL is fully HTML-escaped.
- Around line 46-49: The current handlePrimaryAction calls
options.onPrimaryAction() then animateOut(), which skips the dismiss animation
if the primary callback throws; update handlePrimaryAction to invoke
options.onPrimaryAction() inside a try/finally (or try/catch/finally) so that
animateOut() is always executed in the finally block, and if you need to
preserve the original error propagate or rethrow it after animateOut; reference
handlePrimaryAction, options.onPrimaryAction, and animateOut when making the
change.

In `@src/main/services/__tests__/diagnostic-log-upload.test.ts`:
- Around line 29-49: The test uses a character-count check
(attachment?.data.length) which can miss UTF-8 multibyte size overflows; change
the assertion to check byte length instead by using
Buffer.byteLength(attachment.data, 'utf8') (or if attachment.data is already a
Buffer, use attachment.data.length) so the size assertion in the test for
buildDiagnosticLogAttachment verifies actual bytes (<= 64 * 1024) rather than
string length.

In `@src/main/services/__tests__/notes-ready-notifier.test.ts`:
- Around line 51-53: The test currently only checks that mocks.encryptJSON was
called and that mocks.showNotificationWindow was invoked; instead assert the
persisted dedupe payload by reading/inspecting the arguments passed to
mocks.encryptJSON (or the object written) to verify that
notesReadyNotificationSentAt exists and is a valid timestamp and that the target
path includes "metadata.json" (i.e., confirm the path string passed to
encryptJSON or the write call contains "metadata.json"); update the assertions
in notes-ready-notifier.test.ts to explicitly check the dedupe payload's
notesReadyNotificationSentAt field and the metadata.json target path alongside
the existing expectations.

In `@src/main/services/__tests__/segmentation.test.ts`:
- Around line 201-271: Add a regression test that ensures a throwing onComplete
callback does not mark segmentation as failed: in the segmentation.test.ts
create a case that mocks provider.summarize to resolve (successful
segmentation), registers an onComplete via service.onComplete that throws an
error when invoked, calls (service as any).processJob('m1') and asserts the
Promise resolves (or does not reject) and that the thrown error from the
callback does not propagate to cause processJob to fail; keep the same
transcript mocks and use vi.fn or a throwing function for onComplete to simulate
the callback error.

In `@src/main/services/diagnostic-log-upload.ts`:
- Around line 7-12: SECRET_PATTERNS currently matches key+delimiter+value and
replacements remove the whole match which can break JSON structure (e.g. turning
"access_token":"abc" into malformed text); update the token regexes and the
redact logic so only the token value is replaced and delimiters/keys remain
intact. Specifically, change the Bearer and token-field patterns in
SECRET_PATTERNS to capture the prefix/delimiter and the secret value separately
(e.g. /\bBearer\s+([A-Za-z0-9._~+/=-]+)\b/gi and
/\b(access|refresh|id)_token(["'=:\s]*)([A-Za-z0-9._~+/=-]+)/gi) and adjust the
replacement code in the redactSecrets function (the code using the patterns
around the previous lines 51-53) to rebuild the match using the captured
prefix/delimiter plus a fixed placeholder like "<REDACTED>" for the token group
so the JSON shape remains valid.
- Around line 30-36: The current truncation uses raw.length (UTF-16 code units)
instead of byte length; change the logic to enforce MAX_ATTACHMENT_BYTES in
UTF-8 bytes by converting raw to a Buffer (Buffer.from(raw, 'utf8')), then take
the last MAX_ATTACHMENT_BYTES bytes (buf.length > MAX_ATTACHMENT_BYTES ?
buf.slice(buf.length - MAX_ATTACHMENT_BYTES) : buf), convert that slice back to
a UTF-8 string (slice.toString('utf8')) and pass it into
sanitizeDiagnosticLogTail (keep variable names raw, tail, sanitized and constant
MAX_ATTACHMENT_BYTES to locate the code).

In `@src/main/services/notes-ready-notifier.ts`:
- Around line 30-45: The code currently writes the notesReadyNotificationSentAt
marker before attempting to display the notification (functions: encryptJSON,
notesReadyNotificationSentAt, MeetingMetadata, showNotificationWindow), which
can permanently suppress retries if showing the notification fails; change the
flow so you first call/show the notification (showNotificationWindow with
buildNotesReadyBody and getMeetingDisplayTitle) and only after that succeeds set
notesReadyNotificationSentAt on the MeetingMetadata and persist it via
encryptJSON; ensure you await or use the notification success callback (e.g.,
the onPrimaryAction/onDismiss completion) and do not write the marker on
notification failure so retries remain possible (use
focusMainWindow/getMainWindow as currently wired in the notification handlers).

In `@src/main/services/segmentation.ts`:
- Around line 250-251: Wrap the completion-callback invocation
this.onCompleteCallback?.(meetingId) in a try/catch so any exception thrown by
the callback is caught and logged instead of bubbling up and marking the job
failed (affects both occurrences around the current return and the one at the
other location). In the catch block log the error with the module's logger
(e.g., this.logger?.error or processLogger.error) and continue to return
normally so segmentation success flow in processNext is preserved; apply this
same change to the second call later in the file.

In `@src/main/services/sentry-stub.ts`:
- Around line 161-163: getCurrentScope currently always returns isolationScope
which prevents code inside withScope from seeing the active scoped data; change
getCurrentScope to return the actual active scope used by withScope (e.g., the
top of the scope stack or the currentScope variable) instead of the fixed
isolationScope so that tags/extras/attachments set in withScope are readable via
getCurrentScope(); update the function to retrieve the dynamic active scope
(falling back to isolationScope only if no active scope exists) and ensure it
references the same scope storage used by withScope.

In `@src/renderer/src/App.tsx`:
- Around line 299-301: The code handling the 'notes:open-meeting' event sets
window.location.hash using the raw meetingId which can contain characters that
break the URL; update the handler registered via
window.electronAPI.on('notes:open-meeting', ...) to URL-encode meetingId (use
encodeURIComponent on the meetingId value) before interpolating into
`#${ROUTES.recordings}/${meetingId}` so the hash route becomes
`#${ROUTES.recordings}/${encodeURIComponent(meetingId)}`, ensuring safe
navigation.

In `@src/renderer/src/pages/__tests__/Onboarding.test.tsx`:
- Around line 165-169: The test currently asserts that
'prefs:set-diagnostic-log-upload-consent' is true by default; change it to
assert explicit opt-in by expecting false (i.e.,
expect(window.electronAPI.invoke).toHaveBeenCalledWith('prefs:set-diagnostic-log-upload-consent',
false)), or alternatively simulate a user click on the log-upload checkbox in
the Onboarding test before asserting true; locate the call site using
window.electronAPI.invoke and the event name
'prefs:set-diagnostic-log-upload-consent' to update the expectation or add the
click interaction.

In `@src/renderer/src/pages/Onboarding.tsx`:
- Line 27: The state diagnosticLogUploadDraft in Onboarding is defaulting to
true which allows implied consent; change its initial value to false so the UI
starts unselected and only persists consent after the user explicitly enables
it, ensuring any logic that reads diagnosticLogUploadDraft (and updates it via
setDiagnosticLogUploadDraft) treats false as the safe default.

---

Outside diff comments:
In `@src/main/index.ts`:
- Around line 321-340: beforeSend currently only scrubs homeDir via scrubString
and can leak emails, tokens, and absolute paths; update beforeSend/scrubString
(used by deepScrub) to apply the same redaction rules as the diagnostic-log
attachment sanitizer (or reuse that sanitizer) so Sentry events remove/replace
raw emails, access tokens (e.g., Authorization headers, bearer tokens), and any
absolute filesystem paths not matching homedir, and also strip sensitive keys
like user.email, request.headers.authorization, and any attachment/payload
fields before returning the event; keep the analyticsConsentEnabled guard and
ensure delete event.server_name remains in place.
🪄 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 (Legacy)

Run ID: dd970d77-dd81-46ed-86a4-32e667fd79d5

📥 Commits

Reviewing files that changed from the base of the PR and between fd578f7 and b7e41b5.

📒 Files selected for processing (38)
  • .github/workflows/build.yml
  • e2e/diagnostic-log-upload.spec.ts
  • e2e/helpers/electron-app.ts
  • e2e/notes-ready-notification.spec.ts
  • e2e/onboarding-journey.spec.ts
  • src/main/index.ts
  • src/main/ipc/__tests__/prefs-ipc.test.ts
  • src/main/ipc/prefs-ipc.ts
  • src/main/notification-window.ts
  • src/main/services/__tests__/autodoc-log.test.ts
  • src/main/services/__tests__/calendar-oauth.test.ts
  • src/main/services/__tests__/detection.test.ts
  • src/main/services/__tests__/diagnostic-log-upload.test.ts
  • src/main/services/__tests__/distribution-config.test.ts
  • src/main/services/__tests__/notes-ready-notifier.test.ts
  • src/main/services/__tests__/segmentation.test.ts
  • src/main/services/__tests__/sentry-reporter.test.ts
  • src/main/services/autodoc-log.ts
  • src/main/services/calendar.ts
  • src/main/services/detection.ts
  • src/main/services/diagnostic-log-upload.ts
  • src/main/services/distribution-config.ts
  • src/main/services/microsoft-calendar.ts
  • src/main/services/notes-ready-notifier.ts
  • src/main/services/prefs-store.ts
  • src/main/services/segmentation.ts
  • src/main/services/sentry-reporter.ts
  • src/main/services/sentry-stub.ts
  • src/main/services/whisper-manager.ts
  • src/main/services/windows-transcription-runtime.ts
  • src/preload/ipc.d.ts
  • src/renderer/src/App.tsx
  • src/renderer/src/components/onboarding/AnalyticsStep.tsx
  • src/renderer/src/pages/Onboarding.tsx
  • src/renderer/src/pages/Settings.test.tsx
  • src/renderer/src/pages/Settings.tsx
  • src/renderer/src/pages/__tests__/Onboarding.test.tsx
  • src/shared/types.ts

Comment thread e2e/diagnostic-log-upload.spec.ts
Comment thread src/main/notification-window.ts
Comment thread src/main/notification-window.ts Outdated
Comment thread src/main/services/__tests__/diagnostic-log-upload.test.ts
Comment thread src/main/services/__tests__/notes-ready-notifier.test.ts
Comment thread src/main/services/segmentation.ts Outdated
Comment thread src/main/services/sentry-stub.ts
Comment thread src/renderer/src/App.tsx
Comment thread src/renderer/src/pages/__tests__/Onboarding.test.tsx
Comment thread src/renderer/src/pages/Onboarding.tsx Outdated

@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: 1

🤖 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 `@src/main/services/__tests__/segmentation.test.ts`:
- Around line 260-271: Remove the explicit any casts used when stubbing
fsMock.readFile in the segmentation tests; instead return a correctly typed
value (e.g., pass JSON.stringify([...]) directly if the mock is typed to return
string, or use Buffer.from(JSON.stringify([...])) if the mock expects a Buffer).
Update both occurrences where fsMock.readFile.mockResolvedValue is used (the
JSON.stringify([...]) calls around the test of segmentation) to return the
properly typed string or Buffer value and remove the `as any` casts.
🪄 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 (Legacy)

Run ID: 301df285-fe09-4492-b107-c68f9ec3ac40

📥 Commits

Reviewing files that changed from the base of the PR and between 3d2d74e and 3e02847.

📒 Files selected for processing (13)
  • e2e/diagnostic-log-upload.spec.ts
  • src/main/notification-window.ts
  • src/main/services/__tests__/diagnostic-log-upload.test.ts
  • src/main/services/__tests__/notes-ready-notifier.test.ts
  • src/main/services/__tests__/segmentation.test.ts
  • src/main/services/__tests__/sentry-stub.test.ts
  • src/main/services/diagnostic-log-upload.ts
  • src/main/services/notes-ready-notifier.ts
  • src/main/services/segmentation.ts
  • src/main/services/sentry-stub.ts
  • src/renderer/src/App.tsx
  • src/renderer/src/pages/Onboarding.tsx
  • src/renderer/src/pages/__tests__/Onboarding.test.tsx

Comment thread src/main/services/__tests__/segmentation.test.ts

@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: 1

🤖 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 `@src/main/services/__tests__/whisper-onboarding-install.test.ts`:
- Line 9: The test suite is being entirely skipped in CI via the
describeWhisperOnboarding alias; change this so CI does not globally disable the
onboarding install tests: replace the conditional alias
(describeWhisperOnboarding) so it always maps to describe, and instead mark only
individually flaky tests with it.skip plus a TODO/tracking-issue comment (or add
a dedicated opt-out env flag like WHISPER_TESTS_SKIP to selectively skip).
Update uses of describeWhisperOnboarding to the real describe (or keep the alias
but remove the CI === 'true' branch) and add targeted skips for any known flaky
specs.
🪄 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 (Legacy)

Run ID: d61ebc3d-2c36-4956-b963-2dbe9d4721e0

📥 Commits

Reviewing files that changed from the base of the PR and between 3e02847 and dd4da5a.

📒 Files selected for processing (1)
  • src/main/services/__tests__/whisper-onboarding-install.test.ts

Comment thread src/main/services/__tests__/whisper-onboarding-install.test.ts Outdated
@duetCJackson duetCJackson marked this pull request as ready for review May 24, 2026 01:54
@duetCJackson duetCJackson merged commit 6de7f4f into main May 24, 2026
9 checks passed
@duetCJackson duetCJackson deleted the improvements branch June 26, 2026 20:19
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