---
title: Diagnostics & Logging
description: Where Claudette writes its logs, how to change verbosity, and how to capture frontend errors for bug reports.
---

import { Tabs, TabItem } from '@astrojs/starlight/components';

Claudette writes structured, daily-rotated logs to disk so user-reported bugs can be reconstructed from the file alone — without needing the user to reproduce while the app is attached to a debugger. The pipeline is on by default in every build.

## Where logs live

The default log directory is `~/.claudette/logs/` (sibling to `~/.claudette/workspaces` and `~/.claudette/plugins`). Files are named `claudette.<YYYY-MM-DD>.log` (the `tracing-appender` layout is `<prefix>.<date>.<suffix>`) and rotate daily. A startup retention sweep deletes anything older than 14 days so the directory never grows unbounded.

You can override the location with the `CLAUDETTE_LOG_DIR` environment variable — handy when running multiple dev instances side-by-side and you want each one's logs in its own folder.

Every log line carries the process PID, so even when two Claudette instances share the same daily file you can demux by `grep pid=NNN`.

## Settings → Diagnostics

The Diagnostics section in Settings (between **Editor** and **Git**) gives you three controls without ever needing to touch an environment variable.

### Log level

Restart-required filter applied to the file log when `RUST_LOG` is unset. Choose:

| Level | Captures |
|-------|----------|
| `default` | `info` overall plus `debug` for Claudette's own crates — the shipping default |
| `warn` | warnings and errors only |
| `info` | startup, shutdown, every Tauri command lifecycle event |
| `debug` | the above plus span timings for slow ops (DB migrations, git fetches, diff parsing, agent spawns) |
| `trace` | maximum detail; not recommended for normal use |

If `RUST_LOG` is set in the process's environment, this select is locked and a banner explains why — `RUST_LOG` always wins so the env var workflow is preserved.

### Frontend log verbosity

How much of the React side's `console.*` output is mirrored into the daily log. Takes effect immediately, no restart needed.

| Verbosity | Mirrored sources |
|-----------|------------------|
| **Errors only** *(default)* | uncaught browser errors, promise rejections, React error-boundary catches, `console.error` |
| **Errors + warnings** | the above plus `console.warn` |
| **Everything** | the above plus `console.log` and `console.info` |

Browser-side uncaught errors and unhandled promise rejections are always captured, regardless of verbosity — only the explicit `console.*` calls are gated. React's StrictMode warnings are forwarded under "Errors + warnings" if you need to see them in the file log.

### Open Logs Folder / Copy path

Two buttons. The first reveals the log directory in your file manager (Finder on macOS, the system handler on Linux/Windows). The second copies the path to your clipboard — useful for pasting into a bug report.

## Filtering verbosity by domain

When investigating a specific subsystem, use a per-domain `RUST_LOG` filter rather than turning every domain up to `trace`. Every event in Claudette is emitted under a `claudette::<domain>` target:

```bash
# Only chat lifecycle events
RUST_LOG=claudette::chat=trace cargo tauri dev

# Chat plus MCP supervision
RUST_LOG=claudette::chat=trace,claudette::mcp=debug cargo tauri dev

# Everything from the React webview
RUST_LOG=claudette::frontend=trace cargo tauri dev
```

Established domains:

| Target | Covers |
|--------|--------|
| `claudette::startup` | process boot, paths, multi-instance warning |
| `claudette::panic` | Rust panic hook output (with backtrace) |
| `claudette::db` | database open, migrations, and compaction |
| `claudette::chat` | turn lifecycle, persistent session reuse / respawn |
| `claudette::agent` | Claude CLI subprocess events |
| `claudette::backend` | alternative-provider gateway |
| `claudette::mcp` | MCP supervisor and server registration |
| `claudette::plugin` | Lua plugin runtime + Claude-Code marketplace |
| `claudette::git` | git CLI shellouts |
| `claudette::diff` | diff parsing |
| `claudette::scm` | PR / CI provider plugin invocations |
| `claudette::pty` | terminal spawn / exit |
| `claudette::voice` | Whisper / Speech.framework |
| `claudette::ws` | WebSocket server (remote workspaces) |
| `claudette::ipc` | local CLI ↔ GUI socket |
| `claudette::remote` | remote-control commands |
| `claudette::ui` | theme load, settings persistence |
| `claudette::frontend` | events forwarded from the React webview |
| `claudette::updater` | update checks, boot probation, and rollback |

## Voice click→prompt latency

Every mic click logs a structured event on the `claudette::voice` target so the time between clicking the mic and the recorder opening is visible in the daily log. The event fires whether the start succeeded or not:

- **Success** — info level, `provider_id`, `total_ms` (full command duration), and `stream_open_ms` (just the cpal input-stream open).
- **Failure** — error level, with the propagated error string (permission denied, model missing, recorder open failure, already-active recording, etc.) on the same target.

```
2026-05-08T17:32:14.123Z  INFO  claudette::voice  voice_start_recording{provider_id=apple-speech total_ms=312 stream_open_ms=308}: voice start recording latency
```

To watch in real time, tail the daily log file (path under **Settings → Diagnostics → Open log directory**) or run with `RUST_LOG=claudette::voice=info cargo tauri dev`. Useful when filing a bug report about voice startup feeling slow — copy the matching line into the report and the relevant timings are right there.

There is no cold/warm dimension on the event because Claudette intentionally does not prewarm voice subsystems at launch (this keeps macOS from showing an unprompted microphone permission dialog on boot). Treat every click as a cold start.

## File format

Logs default to a compact text format (one event per line, with a tab-separated key=value tail of structured fields):

```
2026-05-08T17:32:14.123Z  INFO  claudette::chat  send_chat_message{chat_session_id=4f… workspace_id=ws-abc…}: turn dispatched
```

For machine-parseable output set `CLAUDETTE_LOG_FORMAT=json` before launching — every event becomes a JSON line, which is friendlier to `jq` post-mortems:

```bash
CLAUDETTE_LOG_FORMAT=json cargo tauri dev
# Later:
jq 'select(.target=="claudette::chat" and .level=="ERROR")' \
  ~/.claudette/logs/claudette.2026-05-08.log
```

## Database compaction on upgrade

Claudette stores app state in `claudette.db` under the OS data directory (`~/Library/Application Support/claudette/` on macOS, `$XDG_DATA_HOME/claudette/` on Linux, and `%APPDATA%/claudette/` on Windows unless `CLAUDETTE_DATA_DIR` overrides it).

Older builds left SQLite auto-vacuum disabled, so deleting checkpoint snapshots could free pages inside SQLite without shrinking the database file on disk. The first launch of a build with database compaction may run a one-time SQLite `VACUUM` to apply incremental auto-vacuum and reclaim those stranded pages. For very large databases, startup can pause while that rewrite finishes. Other database connections wait up to 30 seconds for the maintenance lock before retrying on a later open.

After conversion, routine database opens only check that the compaction mode is in place. High-churn delete paths, such as checkpoint and workspace cleanup, run bounded incremental vacuum work after the delete commits. To inspect this lifecycle, filter logs for the `claudette::db` target.

## Multiple dev instances

Claudette has no single-instance lock — running `./scripts/dev.sh` twice gives you two independent processes against the same SQLite database. Logs from both instances **interleave into the same daily file** by default, demuxable via the `pid=NNN` field on every line.

If you'd rather isolate them, set `CLAUDETTE_LOG_DIR` to a per-instance path before launching. The dev launcher leaves this up to you so you can choose interleaved-in-one-file or separate-files based on what you're investigating.

On startup, Claudette also scans `${TMPDIR}/claudette-dev/*.json` (the discovery files `scripts/dev.sh` writes) and emits a `WARN` if another live PID is already running against the same DB path. The warning fires once at boot and explains exactly why it can corrupt persisted state — useful when an old instance lingers and you didn't notice.

## Update boot-health rollback

After an in-app update, Claudette starts the new build on a short probation window (20 s by default). During that window Claudette records boot-stage breadcrumbs in the probation sentinel: process start, webview creation, React mount, initial workspace-data loading, and any initial-data load error. The React app sends the final boot heartbeat after the first commit past the loader (`viewStateHydrated` is true and `loadInitialData` resolved). If that heartbeat never arrives, Claudette treats the update as failed to start and tries to restore the previous self-contained install.

When rollback succeeds, the next launch shows a native dialog saying which version failed, which version was restored, and the last boot stage reached. Startup failures, initial workspace-load failures, and slow starts get different wording so the report starts from the right symptom. When rollback cannot be applied (for example, there was no usable previous-install backup or the install location could not be replaced), Claudette writes a no-loop failure report and shows a dialog with the failed version, the captured boot stage, and the release URL to download manually.

A force-quit during the probation window leaves the sentinel on disk; the next launch increments the attempt counter and treats `attempts >= 2` as "this build runs" (the user already booted past the timer once), clears the sentinel, and skips arming the timer. This avoids spurious rollbacks on an otherwise healthy build whose user happens to quit during every probation window.

Backups under `~/.claudette/updates/previous/` are pruned to a single generation each time `prepare_for_update` runs, so the directory does not grow unbounded across updates.

Useful files for a rollback bug report:

| File | Meaning |
|------|---------|
| `~/.claudette/updates/previous/<version>/` | Backup of the previous self-contained install, when one was available. |
| OS data dir `boot-probation.json` | Internal sentinel for the update currently on probation. Cleared after a healthy boot. |
| OS data dir `boot-rollback-report.json` | One-shot report consumed on next launch to show the native rollback dialog. Includes the failed boot stage, probation timeout, rollback execution error (if any), and a bounded tail of the failed build's daily log. |
| `~/.claudette/logs/claudette.<DATE>.log` | Structured timeline under the `claudette::updater` target. |

The OS data dir is `~/Library/Application Support/claudette/` on macOS, `$XDG_DATA_HOME/claudette/` on Linux, and `%APPDATA%/claudette/` on Windows unless `CLAUDETTE_DATA_DIR` overrides it.

### Tuning the probation window

`CLAUDETTE_BOOT_PROBATION_SECS` overrides the default 20 s probation. Useful for slow first-launch scenarios (e.g. cold-cache Linux builds where libwebkit2gtk dynamic loading dominates startup time) and for shrinking the window while testing the rollback path locally. Values are clamped to the range 1–120 s; non-numeric values fall back to the default. Setting `CLAUDETTE_BOOT_PROBATION_SECS=2` plus an artificial pre-render hang in the React tree is the recommended way to exercise the rollback dialog from a dev build.

## Sandboxed (fresh-user) sessions

For testing first-run UX (the welcome card, onboarding, plugin seeding) you can launch Claudette with a completely empty data tree:

<Tabs>
  <TabItem label="macOS / Linux">
    ```bash
    ./scripts/dev.sh --new
    ```
  </TabItem>
  <TabItem label="Windows">
    ```powershell
    .\scripts\dev.ps1 --new
    ```
  </TabItem>
</Tabs>

The flag points three env vars at a per-PID directory under `${TMPDIR}/claudette-dev/new-<pid>/` (`$env:TEMP\claudette-dev\new-<pid>\` on Windows) and removes the tree on exit:

- **`CLAUDETTE_HOME`** overrides the `~/.claudette/` tree (workspaces, plugins, themes, logs, models, packs).
- **`CLAUDETTE_DATA_DIR`** overrides the OS data directory holding `claudette.db`.
- **`CLAUDE_CONFIG_DIR`** overrides the **Claude CLI's** `~/.claude/` tree, which Claudette reads and writes for `settings.json`, `.credentials.json`, installed plugins, and marketplace registrations. Without this override, a `--new` run that touches plugins or auth would write into the real `~/.claude/` and survive the sandbox tear-down — defeating the "simulate a new user" purpose of the flag.

### Cloned (real-state) sessions

`--clone` is the inverse of `--new`: it pre-populates a stable sandbox dir (`${TMPDIR}/claudette-dev/clone/`) with an `rsync -a --delete` mirror of your real `~/.claudette/`, `claudette.db`, and `~/.claude/`, so the dev build inherits your existing workspaces, themes, plugins, and Claude credentials but writes to the sandbox copy rather than the originals. The sandbox is **not** a copy-on-write snapshot — rsync makes full copies (sized by source) and re-runs sync incrementally, only transferring changed files. The cloned `claudette.db` is rsync'd raw, so quit the release app before launching if you need a guaranteed-consistent DB snapshot. Mutually exclusive with `--new`. Currently macOS/Linux only — the PowerShell launcher doesn't implement `--clone`.

:::caution[Git state isolation is not complete]
The clone copies the *working trees* of your workspaces but each workspace's `.git` file remains a pointer to the **real** repository's worktree admin directory. Reads are safe; any dev-app action that writes git state (commits, branch creation, checkout, fetch into the worktree) will land in the real repo's worktree admin. Quit any sensitive long-running SCM work in the release app before using a cloned dev session against the same workspaces.
:::

### Nuking stale sandboxes

If a `--new` or `--clone` session is killed with `SIGKILL` it misses its cleanup trap and leaves a directory under `${TMPDIR}/claudette-dev/` behind. Run:

<Tabs>
  <TabItem label="macOS / Linux">
    ```bash
    ./scripts/dev.sh --clean
    ```
  </TabItem>
  <TabItem label="Windows">
    ```powershell
    .\scripts\dev.ps1 --clean
    ```
  </TabItem>
</Tabs>

to wipe everything under `${TMPDIR}/claudette-dev/` (`$env:TEMP\claudette-dev\` on Windows). This is a blunt nuke — no PID check, every `new-<pid>/`, `clone-<pid>/`, and `<pid>.json` is removed regardless of whether the owning process is still alive. If you have a dev session currently running, its sandbox is removed too and the dev app will start seeing missing files mid-session, so prefer to quit running dev instances first. The command exits after the sweep without launching the app.

You can also set any of these env vars manually for a longer-lived sandbox:

<Tabs>
  <TabItem label="macOS / Linux">
    ```bash
    CLAUDETTE_HOME=/tmp/claudette-demo \
    CLAUDETTE_DATA_DIR=/tmp/claudette-demo/data \
    CLAUDE_CONFIG_DIR=/tmp/claudette-demo/claude \
      ./scripts/dev.sh
    ```
  </TabItem>
  <TabItem label="Windows">
    ```powershell
    $env:CLAUDETTE_HOME      = "$env:TEMP\claudette-demo"
    $env:CLAUDETTE_DATA_DIR  = "$env:TEMP\claudette-demo\data"
    $env:CLAUDE_CONFIG_DIR   = "$env:TEMP\claudette-demo\claude"
    .\scripts\dev.ps1
    ```
  </TabItem>
</Tabs>

## Filing a bug report

1. Reproduce the issue.
2. **Settings → Diagnostics → Copy path** to grab the log directory.
3. Open the most recent `claudette.<DATE>.log` — that's your timeline.
4. If you want fuller detail, switch the **Log level** to `debug`, restart Claudette, reproduce, and the file log will include span timings + every command's entry/exit fields.
5. Attach the relevant log file to the GitHub issue along with the reproduction steps.

The frontend-bridge default catches React render crashes and uncaught browser errors automatically, so the file log usually has enough context to triage even if you can't reproduce on demand.
