Skip to content

feat: enhance bug report collection with frontend capture, os detection, offline queue and 30-day log cleanup#108

Merged
Keith-CY merged 2 commits intodevelopfrom
feat/bug-report-enhancements
Mar 6, 2026
Merged

feat: enhance bug report collection with frontend capture, os detection, offline queue and 30-day log cleanup#108
Keith-CY merged 2 commits intodevelopfrom
feat/bug-report-enhancements

Conversation

@dev01lay2
Copy link
Copy Markdown
Collaborator

Summary

Closes four gaps identified in the develop branch bug collection scheme:

P0 — Frontend error reporting

  • New Tauri command capture_frontend_error (Rust + registered in invoke_handler!)
  • api.captureFrontendError() wrapper in api.ts
  • App.tsx handleUnhandled now fire-and-forgets a Sentry report in parallel with the existing Guidance LLM flow

P1 — OS version detection

  • New src-tauri/src/bug_report/os_info.rs module with platform-aware os_version_string()
    • macOS: sw_vers -productVersion cached with OnceLock
    • Linux: PRETTY_NAME from /etc/os-release
    • Windows: registry CurrentBuildNumbercmd /C ver$OS env fallback
  • Replaces the old OSTYPE/OS env-var guess in collector.rs

P2+P3 — Offline write-ahead queue + 30-day log cleanup

  • New src-tauri/src/bug_report/queue.rs with QueueStore abstraction
  • Files under <clawpal_dir>/bug_report/: pending.jsonl (max 100), dead_letter.jsonl (max 20, after 3 failed attempts), sent.jsonl (audit log), failures.jsonl
  • capture() persists to disk first, then attempts immediate send; on success calls mark_sent()
  • BugReportStats now exposes persisted_pending and dead_letter_count
  • App startup (lib.rs setup) calls cleanup_old_logs() + flush()
  • 30-day retention: sent.jsonl and failures.jsonl entries older than 30 days are pruned on every startup

P4 — Structured failure logging

  • Send failures write a structured {ts, error} entry to failures.jsonl (also pruned after 30 days)

Tests

  • queue.rs: enqueue_caps_pending_size, cleanup_prunes_old_failure_logs
  • os_info.rs: linux_pretty_name_parser_handles_quotes (cfg-guarded)

Copy link
Copy Markdown
Collaborator

@Keith-CY Keith-CY left a comment

Choose a reason for hiding this comment

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

I found two issues that need fixing before merge.

  • [P1] Do not persist bug reports when bug reporting is disabled.

    • In src-tauri/src/bug_report/collector.rs:261 the call to queue::enqueue(...) happens before any settings check.
    • capture() then calls enqueue_event(..., ignore_enabled=false, ...) and prepare_event drops sending when disabled, but the queued entry is already written to disk.
    • Impact: users who explicitly turned off bug reporting still have frontend/panic errors persisted (and may be sent later if reporting is re-enabled).
    • Fix: load settings and gate queue enqueue behind the same !ignore_enabled && settings.enabled check, or pass an enabled guard into queue operations.
  • [P2] Queue file operations are not atomic and can lose entries under concurrency.

    • src-tauri/src/bug_report/queue.rs:137 (enqueue) and 163 (mark_sent) both do read-modify-write cycles with no locking.
    • Both functions load JSONL into memory, mutate, then rewrite the entire file using File::create.
    • Concurrent calls can interleave and overwrite each other (e.g., two enqueue calls both read the same snapshot and each writes back, dropping one event).
    • Fix: serialize queue mutations with a process-wide mutex or file lock around all enqueue/mark_sent/flush/cleanup writes.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 6, 2026

📊 Test Coverage Report

Metric Base (develop) PR (feat/bug-report-enhancements) Delta
Lines 73.81% (5754/7796) 73.81% (5754/7796) ⚪ ±0.00%
Functions 67.52% (663/982) 67.52% (663/982) ⚪ ±0.00%
Regions 75.14% (9609/12788) 75.14% (9609/12788) ⚪ ±0.00%

Coverage measured by cargo llvm-cov (clawpal-core + clawpal-cli).

@dev01lay2
Copy link
Copy Markdown
Collaborator Author

Code Review — REQUEST_CHANGES

CI pending. Two blockers before merge.


🔴 BS 1 — enqueue does full read-modify-rewrite on every capture() call, with no lock

queue.rs QueueStore::enqueue

Each capture() triggers: read all of pending.jsonl → deserialise → push → full rewrite. Under high-frequency errors (e.g. broken SSH firing before the rate-limit kicks in), the async sender thread and the panic hook thread can both call enqueue concurrently → race on the file → corruption.

Fix: add a process-level Mutex<()> (static, same pattern as CollectorState) guarding all pending.jsonl mutations. Or use append-only for the hot path (like log_send_failure) and defer the cap-enforcement rewrite to flush.


🔴 BS 2 — mark_sent and flush both rewrite pending.jsonl without coordination → stale entries

collector.rs sender loop ~line 91, queue.rs flush

At startup, flush runs and rewrites pending.jsonl. Meanwhile the async sender thread may fire mark_sent for queue_entry_ids that were enqueued in a previous session but no longer exist in the current file — those entries silently no-op and stay in pending.jsonl, getting retried indefinitely.

Same fix as BS 1: a single process-level mutex on all pending.jsonl mutations eliminates both races.


🟡 NBS 1 — sanitize_text called twice on the same string

queue.rs line 150

enqueue sanitises message before writing to disk. capture() also passes message into build_eventsanitize_text for the Sentry payload. Two separate sanitise passes on the same input. If sanitize_text is ever non-idempotent the disk copy and the network copy will diverge. Consider calling sanitize_text once in capture() before branching.


🟡 NBS 2 — Linux os_version_string() not cached

os_info.rs Linux branch

macOS uses OnceLock ✅. Linux reads /etc/os-release on every call. Since this is called per-report and per-flush, add static VERSION: OnceLock<String> on the Linux branch too.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 6, 2026

📦 PR Build Artifacts

Platform Download Size
Windows-x64 📥 clawpal-Windows-x64 34.3 MB
Linux-x64 📥 clawpal-Linux-x64 186.9 MB
macOS-ARM64 📥 clawpal-macOS-ARM64 44.9 MB
macOS-x64 📥 clawpal-macOS-x64 45.5 MB

🔨 Built from 8e01496 · View workflow run
⚠️ Unsigned development builds — for testing only

…x OnceLock cache, and single sanitise pass

- Add process-level Mutex (pending_lock) to serialise all pending.jsonl/dead_letter.jsonl
  mutations across enqueue, mark_sent, flush, and cleanup_old_logs — fixes BS1 race
  between hot-path enqueue and async sender thread mark_sent, and BS2 startup flush
  vs in-flight sender interleave
- Fix CI build failure: cleanup_by_retention turbofish now uses type annotation on
  the closure parameter instead of ::<T> turbofish (E0107 — method takes 2 generic args)
- NBS1: sanitise message/stack_trace once in capture() before branching to disk and
  network paths; remove redundant sanitize import from queue.rs
- NBS2: Linux os_version_string() now uses OnceLock cache (same as macOS); fix
  OnceLock use import to cover both macos and linux cfg targets
- Add mark_sent_is_idempotent_for_unknown_id test
@Keith-CY Keith-CY merged commit 92dae65 into develop Mar 6, 2026
9 checks passed
@Keith-CY Keith-CY deleted the feat/bug-report-enhancements branch March 6, 2026 09:02
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