Skip to content

fix(desktop): expand export diagnostics for startup failures#600

Merged
nettee merged 6 commits intomainfrom
export-more
Mar 27, 2026
Merged

fix(desktop): expand export diagnostics for startup failures#600
nettee merged 6 commits intomainfrom
export-more

Conversation

@nettee
Copy link
Copy Markdown
Contributor

@nettee nettee commented Mar 27, 2026

What

Expand desktop Export Diagnostics to capture more startup evidence, especially for Intel macOS launch failures.

Why

Some desktop users hit early startup failures where the existing diagnostics bundle was not enough to identify whether the crash happened in preload, renderer bootstrap, telemetry initialization, or native macOS packaging/signing layers.

How

  • add structured startup probes across main, preload, and renderer and persist them in desktop-diagnostics.json
  • delay renderer telemetry initialization until after React mount and record Sentry / Amplitude init outcomes
  • export new machine, signing, and startup summary artifacts alongside the existing diagnostics bundle
  • document the export behavior and output structure under specs/current/diagnostics/
  • fix summary/manifest.json so warnings reflect missing files and the manifest includes itself in the included file list

Affected areas

  • Desktop app (Electron shell)
  • Controller (backend / API)
  • Web dashboard (React UI)
  • OpenClaw runtime
  • Skills
  • Shared schemas / packages
  • Build / CI / Tooling

Checklist

  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes
  • pnpm generate-types run (if API routes/schemas changed)
  • No credentials or tokens in code or logs
  • No any types introduced (use unknown with narrowing)

Notes for reviewers

  • Verified a fresh export locally from the Help menu into .tmp/ and confirmed the new bundle contains machine-info.json, app-signing.json, and startup-probe-summary.json.
  • pnpm lint still reports one pre-existing test warning about an unused variable in tests/desktop/data-directory-runtime.test.ts.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added startup diagnostics tracking to capture and record app initialization events, improving diagnostic troubleshooting capabilities.
    • Enhanced "Export Diagnostics" to include machine information, app signing status (macOS), and startup probe timelines for better system state visibility.
  • Documentation

    • Added comprehensive guides for the diagnostics export feature and startup probe data inspection.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 27, 2026

📝 Walkthrough

Walkthrough

This PR introduces a comprehensive startup probe diagnostic system that records initialization events across the main process, preload, and renderer layers. Probes capture source, stage, status, and optional details with timestamps. The system collects these into bounded entries and includes machine info and app signing summaries during diagnostics export.

Changes

Cohort / File(s) Summary
Diagnostic Core
apps/desktop/main/desktop-diagnostics.ts, apps/desktop/main/diagnostics-export.ts
Added startup probe state (startupProbe) with signal flags and bounded entries array (max 200). Introduced recordStartupProbe() method to append and trim entries, plus new helper builders (buildMachineSummary, buildAppSigningSummary) for export. Updated export flow to collect machine info and optional app signing assessments.
Main Process Integration
apps/desktop/main/index.ts, apps/desktop/main/ipc.ts
Refactored initialization order to instantiate diagnosticsReporter early and record app-when-ready probe. Added multiple probe recordings at lifecycle checkpoints: did-fail-load, did-finish-load, render-process-gone, ready-to-show. Wired new IPC handler host:startup-probe to forward payloads to reporter.
Preload & Shared Types
apps/desktop/preload/index.ts, apps/desktop/preload/webview-preload.ts, apps/desktop/shared/host.ts
Defined new types StartupProbeStatus and StartupProbePayload. Added reportStartupProbe() helper and error handlers (uncaughtException, unhandledRejection) in both preload contexts. Extended HostBridge interface with reportStartupProbe() method.
Renderer & Public API
apps/desktop/src/lib/host-api.ts, apps/desktop/src/main.tsx
Added public reportStartupProbe() function in host-api. Implemented comprehensive renderer startup tracking: error listeners for window events, deferred telemetry initialization, and new React components (RendererTelemetryBootstrap, RendererStartupSentinel) that emit probes on mount.
Documentation
specs/current/diagnostics/export-diagnostics.md, specs/current/diagnostics/trigger-export-diagnostics.md
New and updated specs documenting startup diagnostics model, export control flow, bundled artifacts (machine info, app signing, startup probe summary), crash report inclusion, and redaction rules. Added descriptions of new summary files in exported ZIP.

Sequence Diagram

sequenceDiagram
    participant Main as Main Process
    participant Preload as Preload
    participant Renderer as Renderer
    participant DiagReporter as Diag Reporter
    participant IPC as IPC Channel

    rect rgba(100, 200, 150, 0.5)
    Note over Main: Startup Phase
    Main->>DiagReporter: recordStartupProbe(app-when-ready)
    Main->>Preload: Create webContents
    Main->>IPC: Register host:startup-probe handler
    end

    rect rgba(150, 150, 200, 0.5)
    Note over Preload: Preload Initialization
    Preload->>Preload: Emit startup-phase probe
    Preload->>IPC: reportStartupProbe via IPC
    IPC->>DiagReporter: Forward payload
    Preload->>Preload: Register error handlers
    end

    rect rgba(200, 150, 100, 0.5)
    Note over Renderer: Renderer Startup
    Renderer->>Renderer: Send renderer:module-start
    Renderer->>IPC: reportStartupProbe (module-start)
    IPC->>DiagReporter: Record probe
    Renderer->>Renderer: Register window error/rejection listeners
    Renderer->>Renderer: Initialize React (emit start probe)
    Renderer->>Renderer: Mount RendererTelemetryBootstrap
    Renderer->>IPC: reportStartupProbe (sentry/amplitude init)
    IPC->>DiagReporter: Record probe
    Renderer->>Renderer: Mount RendererStartupSentinel
    Renderer->>IPC: reportStartupProbe (react-render:committed)
    IPC->>DiagReporter: Record probe
    end

    rect rgba(100, 150, 200, 0.5)
    Note over Main: Lifecycle Events
    Main->>IPC: did-finish-load event
    IPC->>DiagReporter: recordStartupProbe
    Main->>IPC: ready-to-show event
    IPC->>DiagReporter: recordStartupProbe
    end

    Note over DiagReporter: Entries trimmed to max 200<br/>Flags set for preload/renderer seen
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • lefarcen
  • mrcfps
  • PerishCode

Poem

🐰 Startup probes hop along the way,
From main to preload, don't delay!
Each stage recorded, errors tracked,
No stone unturned, no path unmapped.
Diagnostics bloom, the features grow,
Watch the rabbit's stats just flow! 📊✨

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main objective: expanding export diagnostics to capture startup failure evidence on Intel macOS.
Description check ✅ Passed The PR description comprehensively covers all required template sections with clear explanations of what, why, and how, includes all affected area checkboxes, and provides a complete checklist.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch export-more

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 14ec48bd99

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/main/diagnostics-export.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/desktop/main/diagnostics-export.ts (1)

407-458: Consider reusing the file buffer to avoid double read.

The desktop diagnostics file is read twice: once via addFile() for inclusion in the ZIP (line 407-413), and again via tryReadFile() for parsing (line 416-417). While not a blocking issue, you could reuse the buffer from the first read to improve efficiency.

♻️ Optional refactor to reuse file buffer
-  const desktopDiagnosticsMetadata = await addFile(
-    "diagnostics/desktop-diagnostics.json",
-    getDesktopDiagnosticsFilePath(),
-    {
-      redact: true,
-    },
-  );
-
-  if (desktopDiagnosticsMetadata) {
-    const desktopDiagnosticsFile = await tryReadFile(
-      getDesktopDiagnosticsFilePath(),
-    );
-    const parsedDiagnostics = desktopDiagnosticsFile
-      ? parseJsonBuffer<...>(desktopDiagnosticsFile.data)
+  const desktopDiagnosticsFile = await tryReadFile(getDesktopDiagnosticsFilePath());
+  
+  if (desktopDiagnosticsFile) {
+    // Add redacted version to ZIP
+    const redactedData = redactJsonBuffer(desktopDiagnosticsFile.data);
+    entries.push({
+      name: `${archiveRoot}/diagnostics/desktop-diagnostics.json`,
+      data: redactedData,
+      modTime: desktopDiagnosticsFile.mtime,
+    });
+    included.push("diagnostics/desktop-diagnostics.json");
+    
+    // Parse original (unredacted) for summary extraction
+    const parsedDiagnostics = parseJsonBuffer<...>(desktopDiagnosticsFile.data);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/main/diagnostics-export.ts` around lines 407 - 458, The code
reads the same diagnostics file twice (once via addFile(...) and again via
tryReadFile(...)); instead, reuse the file buffer produced during adding to the
archive: adjust addFile or its call so you capture the file buffer (or make
addFile return { metadata, data } or expose the read Buffer) and then pass that
Buffer to parseJsonBuffer(...) instead of calling
tryReadFile(getDesktopDiagnosticsFilePath()); update references around
desktopDiagnosticsMetadata, desktopDiagnosticsFile and parsedDiagnostics to use
the captured buffer when available.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/main/diagnostics-export.ts`:
- Around line 240-271: The runCommand function crashes when spawnSync returns
undefined stdout/stderr; change the assignments for stdout and stderr to safely
handle undefined by defaulting to empty string before trimming (e.g., use
result.stdout ?? "" and result.stderr ?? ""), then preserve the existing logic
to map empty -> null; update any type expectations if needed and keep the rest
of runCommand (ok, status, signal, error) unchanged so all call sites continue
to receive the same shape.

---

Nitpick comments:
In `@apps/desktop/main/diagnostics-export.ts`:
- Around line 407-458: The code reads the same diagnostics file twice (once via
addFile(...) and again via tryReadFile(...)); instead, reuse the file buffer
produced during adding to the archive: adjust addFile or its call so you capture
the file buffer (or make addFile return { metadata, data } or expose the read
Buffer) and then pass that Buffer to parseJsonBuffer(...) instead of calling
tryReadFile(getDesktopDiagnosticsFilePath()); update references around
desktopDiagnosticsMetadata, desktopDiagnosticsFile and parsedDiagnostics to use
the captured buffer when available.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 77c6c035-1375-43c9-93a9-747ed196807e

📥 Commits

Reviewing files that changed from the base of the PR and between 3981f31 and 14ec48b.

📒 Files selected for processing (11)
  • apps/desktop/main/desktop-diagnostics.ts
  • apps/desktop/main/diagnostics-export.ts
  • apps/desktop/main/index.ts
  • apps/desktop/main/ipc.ts
  • apps/desktop/preload/index.ts
  • apps/desktop/preload/webview-preload.ts
  • apps/desktop/shared/host.ts
  • apps/desktop/src/lib/host-api.ts
  • apps/desktop/src/main.tsx
  • specs/current/diagnostics/export-diagnostics.md
  • specs/current/diagnostics/trigger-export-diagnostics.md

Comment thread apps/desktop/main/diagnostics-export.ts
@nettee nettee merged commit c52396c into main Mar 27, 2026
11 checks passed
@lefarcen lefarcen mentioned this pull request Mar 30, 2026
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.

2 participants