Skip to content

Diagnostics & Logging

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.

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.

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

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

LevelCaptures
defaultinfo overall plus debug for Claudette’s own crates, with mdns_sd capped at warn — the shipping default
warnwarnings and errors only
infostartup, shutdown, every Tauri command lifecycle event
debugthe above plus Claudette debug events and span timings for slow ops (DB migrations, git fetches, diff parsing, agent spawns)
tracemaximum Claudette detail; not recommended for normal use

The in-app selector scopes coarse levels to Claudette’s own crates and keeps dependencies below debug/trace. It also caps mdns_sd at warn so mDNS packet chatter does not flood the daily log.

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.

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

VerbosityMirrored sources
Errors only (default)uncaught browser errors, promise rejections, React error-boundary catches, console.error
Errors + warningsthe above plus console.warn
Everythingthe 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.

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.

When investigating a specific subsystem, use a per-domain RUST_LOG filter rather than turning every domain up to trace. RUST_LOG is honored verbatim, so it can still enable dependency targets when you deliberately need them. Every event in Claudette is emitted under a claudette::<domain> target:

Terminal window
# 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:

TargetCovers
claudette::startupprocess boot, paths, multi-instance warning
claudette::panicRust panic hook output (with backtrace)
claudette::dbdatabase open, migrations, and compaction
claudette::chatturn lifecycle, persistent session reuse / respawn
claudette::agentClaude CLI subprocess events
claudette::backendalternative-provider gateway
claudette::mcpMCP supervisor and server registration
claudette::pluginLua plugin runtime + Claude-Code marketplace
claudette::gitgit CLI shellouts
claudette::diffdiff parsing
claudette::scmPR / CI provider plugin invocations
claudette::ptyterminal spawn / exit
claudette::voiceWhisper / Speech.framework
claudette::wsWebSocket server (remote workspaces)
claudette::ipclocal CLI ↔ GUI socket
claudette::remoteremote-control commands
claudette::uitheme load, settings persistence
claudette::frontendevents forwarded from the React webview
claudette::updaterupdate checks, boot probation, and rollback

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.

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:

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

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.

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.

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:

FileMeaning
~/.claudette/updates/previous/<version>/Backup of the previous self-contained install, when one was available.
OS data dir boot-probation.jsonInternal sentinel for the update currently on probation. Cleared after a healthy boot.
OS data dir boot-rollback-report.jsonOne-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>.logStructured 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.

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.

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

Terminal window
./scripts/dev.sh --new

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.

--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.

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:

Terminal window
./scripts/dev.sh --clean

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:

Terminal window
CLAUDETTE_HOME=/tmp/claudette-demo \
CLAUDETTE_DATA_DIR=/tmp/claudette-demo/data \
CLAUDE_CONFIG_DIR=/tmp/claudette-demo/claude \
./scripts/dev.sh
  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.