Performance & Logs
Developer mode and DevTools, automatic resource management, Linux inotify file watching, section-level error boundaries, and accessing logs.
Developer Mode
Turn on Developer Mode in Settings > Troubleshooting for the debugging tools:
- Auto-Open Diagnostics opens the Diagnostics Dock on launch
- Focus Events Tab makes the Diagnostics Dock land on the Events tab when it auto-opens
DevTools
Open Chromium DevTools with Cmd+Alt+I (macOS) or Ctrl+Shift+I (Windows/Linux). DevTools are available in development builds by default. In production builds, turn on Developer Mode first.
Performance Profiling
If you're running from source, there are performance testing scripts:
npm run perf:smoke: quick smoke testnpm run perf:ci: CI performance runnpm run perf:nightly: nightly performance runnpm run perf:soak: extended soak testnpm run stress-test: TUI stress test
Set DAINTREE_PERF_CAPTURE=1 before starting Daintree to turn on internal performance capture mode.
Automatic Resource Management
Daintree manages memory, disk, and CPU in the background. Knowing how these systems work helps when something seems off.
Adaptive Profiles
Daintree picks a Performance, Balanced, or Efficiency internal profile from its own memory usage, battery state, active agent count, thermal pressure, CPU speed limiting, and low system-wide available memory. The profile adjusts polling frequency and how aggressively idle projects hibernate. There's no manual override. If you notice slower UI updates or quicker project hibernation, the Efficiency profile is probably active. Stopping a few agents or plugging in the charger lowers the pressure score and shifts back to Balanced or Performance.
Memory Pressure Response
When Daintree detects memory pressure, it first clears caches and releases hidden webviews. If the pressure holds across three consecutive intervals, it hibernates idle projects. A 10-minute cooldown stops the same project from being hibernated over and over. If terminal output pauses briefly during high-volume bursts, the PTY host resource governor is throttling output to protect heap stability. It resumes within 10 seconds.
Disk Space Monitoring
Daintree watches the application data volume every 5 minutes. A warning toast appears when free space drops below 500 MB. Below 100 MB, a persistent critical toast appears and session backups pause to stop consuming disk. Free up space on the application data volume to get back to normal. If you're not sure which volume that is, check the path shown by Settings > Troubleshooting > Open Log File.
For panel limit controls and resource monitoring configuration, see Settings > Terminal. For how each automatic background system works, see Settings > Automatic Resource Management.
Linux: File Watching Degraded
On Linux, Daintree uses the kernel's inotify subsystem to watch worktree files for changes. Each watched directory takes one inotify watch, and the kernel caps the total per user through the fs.inotify.max_user_watches sysctl. Large projects, deep node_modules trees, or several worktrees open at once can exhaust the limit, especially when editors, browsers, and language servers are watching files too.
When Daintree's file watcher hits the limit, the kernel returns ENOSPC. It's the same error code as "no space left on device," but here it means the inotify watch pool, not your disk. Daintree surfaces this as a warning toast titled File watching degraded with the message "Linux inotify watch limit reached. Some files may not auto-refresh until you raise it." The toast has a Copy fix command action that copies sudo sysctl fs.inotify.max_user_watches=524288 to your clipboard so you can paste it straight into a terminal.
A related failure mode is EMFILE, raised when the per-user inotify instance limit is exhausted instead of the watch limit. That happens when many processes each hold open inotify file descriptors at once: several Electron apps, a few language servers, your editor. The persistent fix command below raises fs.inotify.max_user_instances alongside max_user_watches to head off both failure modes at once.
Only one toast appears per app session, even if several worktrees hit the limit at the same time. It fires once, then Daintree stays quiet about later events until you restart the app.
Retry Budget and Wake Recovery
The retry budget can be reset up to three times per session (MAX_RESETS_PER_SESSION = 3). Daintree resets it on two triggers: a user-driven refresh of the workspace, and wake-from-sleep. Wake recovery matters on Linux specifically, because suspending the machine often leaves inotify watches in a half-dead state. Without the reset, you'd see degraded file watching after every resume.
The reset isn't gated by exhaustion. Any non-zero retry counter gets cleared, so a manual refresh during the back-off window pulls the watcher back to immediate mode without waiting out the timer. When the watcher recovers, after the limit is raised or after a successful retry, Daintree clears the degraded indicator on its own.
Applying the Fix
Run the temporary command to apply the new limit right away. The setting resets on reboot:
sudo sysctl -w fs.inotify.max_user_watches=524288 fs.inotify.max_user_instances=512 To keep the change across reboots, write both values to a drop-in file in /etc/sysctl.d/ and reload:
sudo tee /etc/sysctl.d/99-inotify.conf <<'EOF'
fs.inotify.max_user_watches=524288
fs.inotify.max_user_instances=512
EOF
sudo sysctl --system fs.inotify.max_user_instances alongside max_user_watches, from the default 128 to 512. Each open inotify file descriptor counts as one instance, so running several Electron apps (editor, browser, Daintree) with many worktrees can exhaust that limit too. Persisting both values in the drop-in file keeps them in place after a reboot.Background
The default watch limit on most Linux distributions is 8192, though kernels 5.11 and later may auto-scale it based on available RAM. The limit is per-user and shared across every process that calls inotify: your editor, browser, language servers, and Daintree itself. The value 524288 is the standard recommended setting used by VS Code, JetBrains, and most of the Electron ecosystem. It covers typical multi-worktree workflows at no meaningful memory cost. Each watch takes roughly 1 KB of kernel memory, and the full allocation only materializes if you actually create that many watches.
When Parts of the UI Fail
Daintree wraps every major UI surface in an Error Boundary, so a render-time exception in one piece doesn't take down the whole app. If a settings panel, dialog, or worktree card throws while rendering, the boundary catches it. The rest of the app keeps working, and you get a localized fallback in just that surface.
Section-Level Error Boundaries
Boundaries sit at the section level (sidebar, main content, help panel, diagnostics dock) and again at finer granularity around each docked panel, grid panel, and worktree card, plus around the major dialog hosts: MCP confirm, git pull / rebase / push confirms, plugin confirm, terminal info, file viewer, panel limit confirm.
When a boundary catches an exception, three things happen:
- The error is reported to Sentry via the renderer-exception handler.
- It's added to the in-app error store with a correlation ID.
- The local ErrorFallback renders inside the broken surface, with a copyable Error ID (the Sentry event ID when available, the store correlation ID otherwise).
The Error ID also shows up in the Problems tab of the Diagnostics Dock. That lets you, or anyone you're sharing the report with, line up a Sentry trace against the exact row that fired in the app.
Recovery is local. Reopen the surface, by closing and reopening the dialog or hiding and reshowing the panel, or use Help > Reload to refresh the renderer entirely. The rest of the app keeps running while you do.
Recurring Errors
Daintree counts how often the same error fingerprint repeats. At five occurrences (RECURRENCE_THRESHOLD = 5) it switches escalation strategies. Two surfaces show recurrence, and they handle it differently:
- Inline error banner (in-panel UI): the secondary action flips from Retry to View errors. Clicking it opens the Problems tab in the Diagnostics Dock, where you can see the full history, copy the Error ID, and decide what to do.
- Toast notifications: the toast pipeline suppresses Retry instead of relabeling it. The toast action becomes Copy details, which copies a structured snapshot of the error to your clipboard. There's no View errors button at the toast level.
The same escalation also fires when the error has already been promoted into the dock, or when retry has been explicitly exhausted by an earlier action. The surface changes as soon as further retries stop being useful, even before the five-occurrence threshold.
Cross-session recurrence is tracked by the main-process persisted fingerprint store. occurrenceCount and fromPreviousSession survive restarts, so the Problems tab can show that an error has turned up across multiple launches. On a conflict, the store prefers the incoming counter, so a stale renderer cache can't hide an escalation.
Accessing Logs
Daintree keeps a rotating log file with app activity, errors, and diagnostic info. The Logs tab inside the Diagnostics Dock streams the same content live. The file on disk is for offline review and for attaching to bug reports.
View Logs
- Inside the app: open the Diagnostics Dock and switch to the Logs tab. The filter bar, severity buttons, Sources dropdown, and per-entry copy actions all live there.
- On disk: Settings > Troubleshooting > Open Log File opens the current log file in your system text editor.
- Clear Logs in the same Settings section wipes the buffer. A confirmation dialog gates it so a misclick doesn't blow away log history you'd want for a bug report.
Verbose Logging
For more detailed output, turn on verbose logging:
- Session-only: toggle Verbose Logging in Settings > Troubleshooting. It resets on restart.
- Persistent: set the environment variable
DAINTREE_DEBUG=1before launching Daintree.
Previous-Session Tail
Daintree's log rotation keeps a tail of the previous session in the in-app buffer. After a restart, the Logs tab inserts a Previous session header card at the boundary, with the prior session's tail readable above the new entries. It's the easiest way to line up a current-session symptom against whatever fired just before the last shutdown, without flipping between log files in another editor.