feat: add two-tier opt-in telemetry system#53
Conversation
Adds CLI-side telemetry with anonymous (usage stats) and rich (trajectories + screenshots) tiers. Consent is managed via first-run prompt, env var override (DESKTEST_TELEMETRY=0|1|2), and `desktest telemetry` subcommand. Events are fire-and-forget to a configurable endpoint (DESKTEST_TELEMETRY_URL). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Greptile SummaryThis PR introduces a complete two-tier opt-in telemetry system for Key changes:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant CLI as desktest CLI
participant FS as telemetry.json (disk)
participant Events as /api/events
participant Upload as /api/upload
User->>CLI: desktest run task.json
CLI->>FS: load_or_init() — read telemetry.json
FS-->>CLI: TelemetryConfig (or fresh default)
CLI->>User: First-run consent prompt (if stdin TTY & not prompted before)
User-->>CLI: "1" (Anonymous) / "2" (Rich) / "n" (None)
CLI->>FS: save_config() — persist consent + install_id
Note over CLI: run_task() executes…
CLI->>CLI: record_event(test_completed)
CLI->>User: print result (no wait)
alt Anonymous or Rich consent
CLI->>Events: POST /api/events [5s timeout]
Events-->>CLI: 2xx (or timeout — ignored)
end
alt Rich consent + artifacts_dir exists
CLI->>CLI: create_artifacts_tarball()
CLI->>Upload: POST /api/upload multipart [30s timeout]
Upload-->>CLI: 2xx (or timeout — ignored)
end
CLI->>User: process exits
|
- Replace deprecated `atty` crate with `std::io::IsTerminal` (RUSTSEC-2021-0145)
- Fix `now_iso8601()` to produce valid ISO 8601 datetimes instead of raw epoch seconds
- Add `epoch_days_to_ymd()` calendar conversion (Howard Hinnant algorithm)
- Fix empty HOME causing `replace("", "~")` to mangle filenames in artifact tarballs
- Always set artifacts dir for rich uploads (use default when --artifacts-dir not passed)
- Add `set_artifacts_dir` call in Suite handler before flush
- Change `.unwrap()` to `.expect()` on hardcoded MIME type
- Add tests for ISO 8601 format and epoch_days_to_ymd
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move result printing/exit code computation before flush().await so users see the outcome immediately instead of waiting up to 35s for telemetry HTTP requests. Applied to Run, Suite, and Attach handlers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@greptileai review |
- Decouple artifact upload from events queue in flush() so Rich uploads work even when no stat events were queued - Warn on unrecognized DESKTEST_TELEMETRY env var values instead of silently defaulting to off - Document flat-directory assumption in create_artifacts_tarball Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@greptileai review |
- Fix off-by-one in nudge interval (now fires on 10th run, not 11th) - Don't nudge users who explicitly opted out via `desktest telemetry off` - Update test fixtures to use valid ISO 8601 timestamps - Update nudge tests to cover opt-out and corrected interval logic Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@greptileai review |
- Fix stale test fixture in test_event_serialization (use valid ISO 8601) - Add #[serde(default)] to TelemetryConfig for forward-compatible deserialization when new fields are added - Remove dead home-directory redaction on file basenames in create_artifacts_tarball (basenames never contain home paths) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Addressed all 3 remaining Greptile findings in fe37a01:
@greptileai review |
- Add record_event, set_artifacts_dir, and flush to Interactive command handler (was silently skipped despite consent checks running) - Persist freshly generated config on first load so install_id is stable across CI runs when using DESKTEST_TELEMETRY env override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Addressed 2 of 3 remaining Greptile findings in 572f767:
@greptileai review |
Normal Ctrl+C exits in interactive mode (no --step, no --validate-only) are expected behavior, not errors. Skip recording a telemetry event for these to avoid inflating error-rate metrics. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Fixed the last remaining issue in 25a9242: Interactive Ctrl+C classified as error (valid) — @greptileai review |
…udge arm - Save fresh config before applying DESKTEST_TELEMETRY env override so only install_id is persisted, not the overridden consent level - Remove unreachable ConsentLevel::None arm from show_nudge (should_nudge already excludes None+prompted users) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Fixed both remaining issues in d4314ae:
@greptileai review |
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Distinguish "file not found" from "corrupt file" in load_config; warn user when config is corrupt before resetting - Skip run_count increment and disk write for Rich consent users (they can never be nudged, so the counter is unused) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Addressed Greptile round-8 findings in 9b0138a:
@greptileai review |
- Pass provider and model from Config into all telemetry events (Run, Suite, Attach, Interactive) - Distinguish corrupt config from missing file in load_config; warn user before resetting install_id - Skip run_count disk writes for Rich consent users Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Addressed remaining Greptile round-9 findings in 93c8207:
@greptileai review |
- Distinguish ENOENT from other I/O errors (e.g. permission denied) in load_config; warn user before resetting install_id - Skip set_artifacts_dir and flush on expected Interactive Ctrl+C to avoid orphaned artifact uploads with no correlated event Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Fixed both remaining issues in 1d303c7:
@greptileai review |
- Log debug message when read_dir fails in create_artifacts_tarball instead of silently producing an empty tarball - Skip disk writes for explicitly opted-out users (None + prompted_at) in addition to Rich users - Use atomic write-then-rename pattern in save_config to prevent corruption on mid-write process kill Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Fixed all 3 remaining style issues in b10fcb7:
@greptileai review |
- Remove telemetry artifacts_dir setting for Suite command to match the user-facing warning that --artifacts-dir is ignored for suites - Remove dead `next_count > 0` guard (u32 + 1 is always >= 1) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Fixed both issues in 0e55999:
@greptileai review |
- Compare file extensions case-insensitively in create_artifacts_tarball so .PNG, .JSONL etc. are included on case-sensitive filesystems - Only construct reqwest::Client when there's work to do (events or artifacts to upload) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Fixed 2 of 3 issues from round 13 in f92c6a8:
@greptileai review |
- Replace free-form String action with clap::ValueEnum for compile-time validation and shell completions - Clean up stale .json.tmp file if rename fails in save_config Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Fixed remaining style issues in c733526:
@greptileai review |
- Remove unused was_corrupt bool from load_config return; warnings are already printed inline before returning None - Remove redundant ConsentLevel::Anonymous guard in show_nudge since only Anonymous users can reach the function Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Fixed 2 of 3 remaining observations in 7a08bf9:
@greptileai review |
…tion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Wrap events in {"events": [...]} object for POST /api/events
- Rename used_qa_mode → used_qa to match backend schema
- Send install_id and run_id as X-Install-Id/X-Run-Id headers
- Rename multipart part from "artifacts" to "archive"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously, save_config errors were silently discarded, misleading users into thinking their opt-out persisted when it didn't. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
desktest telemetrysubcommand (off/anonymous/rich/status), andDESKTEST_TELEMETRYenv var override for CIDESKTEST_TELEMETRY_URL), failing silently — no user impact if backend is unreachableTest plan
cargo buildcompiles without errorscargo test— all 414 existing + 7 new telemetry unit tests passdesktest telemetry status— shows consent level, install ID, run countdesktest telemetry anonymous/desktest telemetry off— toggles work correctlyDESKTEST_TELEMETRY=1 desktest telemetry status— env override appliesdesktest run task.json)run_count_since_prompt: 9in state file)echo "" | desktest run task.json— no prompt (stdin not TTY)anonymousconsent, run completes without hang/error even with no backend🤖 Generated with Claude Code