Module: tabibu-telemetry (deselection signal)
Opt-in, privacy-respecting telemetry crate. It records exactly one thing: a
false-positive signal. When a user unchecks a Safe-tier cleanup item
during review — Tabibu suggested removing it and the user disagreed — we learn
which category of suggestion users distrust, so we can improve defaults.
Built on Tabibu's honesty principle (opt-in, no content, no PII).
Privacy contract (this is the design, not a feature)
What we record — and nothing else:
| Field | Example | Why it's safe |
|---|---|---|
category |
"user_cache" |
scanner id, not a path |
tier |
"Safe" |
fixed enum of suggestion tiers |
size_bucket |
"100mb_1gb" |
coarse order-of-magnitude, never exact bytes |
ts_unix |
1700000000 |
caller-supplied; the lib never reads the clock |
We never record a path, filename, file contents, bundle id, exact byte
count, or any user-identifying data. DeselectionEvent structurally has no
field for such data. The crate has no network dependency — data is written
local-only as append-only JSONL in a caller-provided directory.
Consent lifecycle
Default OFF. record() while disabled is a silent no-op (Ok(false)), and no
events file is ever created. Disabling is treated as withdrawal: the
existing events file is deleted.
stateDiagram-v2
[*] --> Disabled
Disabled --> Enabled: set_enabled(true)
Enabled --> Disabled: set_enabled(false)\n(deletes events file — withdrawal)
Enabled --> Enabled: record() appends JSONL line
Disabled --> Disabled: record() = Ok(false), writes nothingData shape
<dir>/telemetry-consent.json → {"enabled":true} (atomic tmp+rename).
<dir>/deselections.jsonl → one JSON object per line:
{"category":"user_cache","tier":"Safe","size_bucket":"100mb_1gb","ts_unix":1700000000}export() reads these back for a future "see what's collected" transparency
UI; clear() deletes the events file without touching consent. Corrupt or
missing consent fails safe to disabled.