Omniscio documentation
Browse all documentation
  1. Getting Started13
  2. Sessions & Agents115
  3. Inbox & Notifications59
  4. Projects & Tasks95
  5. Automation & Scheduling75
  6. Knowledge & Memory26
  7. AI Features60
  8. Integrations100
  9. Plugins & Marketplace33
  10. Cloud & Teams56
  11. Settings & Customization58
  12. Account & Billing28
  13. Troubleshooting84
  14. CLI & API Reference22
  15. Legal & Policies4
  16. Uncategorised22

UI Usage Tracking — see which controls and integrations you actually use

Omniscio records on your own device which named controls you click and which sidebar integrations you open, then shows a used-versus-never-used report in Settings, with a Flow card for what you tend to do right after a control and an on/off toggle that stays separate from telemetry.

What it is

Library page — describes what users see and how to use it, then how it works under the hood. Self-contained so an outside AI (with no repo access) can read this and answer "what is UI Usage tracking and how does it work?"

Omniscio quietly records, on your own device, which named controls you click (buttons, links, toggles, tabs, menu items) and which sidebar integrations you open (Gmail, GitHub, SMS, Stats, KMS, and the rest). It then shows you a plain "used vs. never used" picture so you can tell what you actually rely on versus what you've never touched — the point being to decide later what to remove or auto-hide so the app stays uncluttered.

The "used vs never used" report is built on your own device — it needs no server. One honest caveat: when Error Reporting (Settings → System) is on, Omniscio also folds anonymous per-control click counts into its daily diagnostics digest — the same anonymized fleet health summary that crashes and errors ride. Those are counts only (a catalogued control's internal name + how many times it was clicked, filtered to Omniscio's known control list and keyed to a random install id) — never message content, file names, or anything that identifies you. Turn Error Reporting off and nothing about your usage leaves the machine. (See "The on/off toggle, and how it relates to telemetry" and "Stage 2" below.)

Where to find it

  1. Open Omniscio.
  2. Open Settings (gear icon in the toolbar, or the Settings entry in the Omniscio sidebar group).
  3. Go to the Diagnostics section.
  4. Two things live there for this feature:
    • A toggle, "Track which UI controls you use" (on by default). The report it builds is local; when Error Reporting is on, anonymous click counts also ride the daily diagnostics digest (see "The on/off toggle" below).
    • A "UI Usage" card below it that shows the report.

How it behaves

What the card shows

The UI Usage card has a small row of time-window buttons — Last 24h / 7 days / 30 days / All time (it opens on 30 days) — and a "Tracking since <date>" note so you know how far back the data goes. Under that, two groups:

  • Controls — the named buttons / links / toggles / tabs / menu items in the app.
  • Integrations — the sidebar integrations (the built-in virtual projects like Gmail, GitHub, SMS, Stats, Inbox, Settings, etc.).

Each group is split into two lists:

  • Used (N) — the controls/integrations you've used in the selected window, most-used first, with a click count next to each.
  • Never used (N) — the ones you've never used (in the catalogue of trackable things), listed alphabetically.

Switching the time window re-runs the report, so "Never used" under Last 24h means "you didn't touch it today", while "Never used" under All time means "you've never touched it since tracking began".

Under each Used list, a small mobile / desktop split shows how much of that usage came from a phone versus a computer (a phone icon and a monitor icon, each with a count) — so you can tell, for instance, that a control you rely on is mostly used on mobile. The split appears only once there's device-stamped usage to show; older data recorded before this was added simply isn't attributed to either device.

The "Never used" list looks huge on day one — that's expected, not a bug

When you first turn the app on, everything is unused until you click it, so the "Never used" lists start out long and the "Used" lists start out short. This is normal. As you use Omniscio over days and weeks, items move from "Never used" into "Used" and the lists become a meaningful map of your real habits. A long "Never used" list early on is not a sign that tracking is broken — it just hasn't seen you use those controls yet.

Off is left out of "Never used" — for both integrations and controls

If you've turned an integration off (so it isn't even showing in your sidebar), it is not listed under the integrations "Never used" list. The reasoning: off is not the same as unused. The "could be opened" universe is the set of integrations currently visible to you, so a feature you deliberately disabled isn't held against you as "never used".

Controls follow the same rule. A control that simply cannot appear in your copy of Omniscio is left out of the controls "Never used" list too — because you never could have used it. Three kinds are filtered out: dev-only controls (they exist only in a developer build, never in the shipped app), platform-specific controls (e.g. the Windows-only AutoHotkey manager, hidden on macOS/Linux), and controls behind an in-development feature you haven't turned on. As soon as such a control can render for you — you're on the right platform, or you enable the feature — it re-enters the "Never used" list until you use it. So the list stays a map of things you could use but haven't, not a pile of things that were never reachable.

The device you used something on is also recorded, so the card can show a mobile vs. desktop split (see below).

Flow — what you tend to do right after a control

Below the UI Usage card sits a small companion, the Flow card. Pick a starting control (e.g. "Go to inbox") and it shows the controls you most often use right after it, within the same app session — a first look at your real navigation habits ("after I open search, I usually attach a file next"). Like everything above, it is 100% local and windowed by the same 24h / 7d / 30d / All time buttons. It's a rough picture, not an exact replay.

To make this possible, each recorded event now also carries two pieces of local-only sequence context: a run id (a fresh random id minted once per app launch — deliberately NOT the stable install id, so it can never link you across launches) and a step number (a simple counter within that launch). Together they let Omniscio reconstruct the order of your clicks within a single run, on your own machine. These two values never leave the device — the anonymous fleet rollup only ever sees per-control counts, never sequences (a build guard enforces that), and any future upload of flow data is a separate, reserved privacy decision.

The on/off toggle, and why it's separate from telemetry

The "Track which UI controls you use" toggle in Settings → Diagnostics controls this feature. It is on by default, and only an explicit "off" turns it off; flip it off and Omniscio stops recording new clicks and integration opens (the card then tells you tracking is off and to turn it on to start collecting).

This toggle is deliberately separate from the telemetry / "share data" consent (Error Reporting). It is a local feature switch — "do I want Omniscio to keep this diary of what I use?" — and it gates whether clicks are recorded at all. Whether the recorded counts then leave your machine is a separate decision, gated by Error Reporting: with it on, anonymous per-control click counts ride the daily diagnostics digest; with it off, nothing about your usage is sent. So this switch controls the local diary; Error Reporting controls egress.

Privacy — what is and isn't stored

Only two things are ever written for each event:

  1. Which control or integration it was — the control's internal anchor name (e.g. the catalogue key for the "New Session" button) or the integration's id — and
  2. When — a timestamp.

That's it. No message content, no file names, no folder paths, no contact names, no personal data is ever recorded. Per-instance things are never stored either: clicking a specific session row, for example, is ignored — only catalogued, named controls count, so a one-off id like a session id never enters the database (see "How it works"). Everything lives in your local Omniscio database; the only thing that can leave it is the anonymous per-control count (the control's internal name + a number, never the timestamp and never anything identifying), and only when Error Reporting is on — see "The on/off toggle" above.

There is also an anonymous install id — a random UUID generated and saved on your machine the first time it's needed. In Stage 1 nothing reads it and nothing sends it; it exists only so a future Stage 2 could count distinct installs without identifying anyone. It is deliberately a separate random value from the id Omniscio already uses for in-app support chat, so future analytics can never be tied back to your identity by reusing an existing id. If the file holding it is ever lost, a fresh one is minted (counted as a new install), which is fine for anonymous aggregate counting.

Stage 2 — central rollup (partly superseded by fleet telemetry)

Since this feature was written, the anonymous click counts already leave the machine a different way: they ride Omniscio's fleet telemetry daily diagnostics digest when Error Reporting is on (filtered to Omniscio's known control list, keyed to a random fleet install id), and the maintainer sees them as aggregate product analytics. So a cross-user rollup of clicks exists today — through fleet telemetry, not through this feature's own pipe.

What is not built is the dedicated path once envisioned here: a ui-usage-specific opt-in upload (catalogue id + kind + count + timestamp + this feature's own install id) surfaced as an "all users" view inside this Diagnostics card. This feature's own install id (see "Privacy" above) is still dormant, and that "all users" tab does not exist. Either way, usageTrackingEnabled gates only local recording; the egress that DOES happen rides the Error Reporting (telemetry) consent and must never weaken the local guarantees above.

For agents

How it works (for agents with repo access)

The feature splits cleanly: the renderer captures and interprets; the main process is "dumb" storage that only inserts batches and counts them by time window. All the meaning — what's trackable, what's visible, what counts as "never used" — lives in the renderer.

Capturing control clicks. A single delegated click listener is installed once at the App root, in the capture phase (so it still fires even if a child stops propagation). On each click it walks up from the click target to the nearest element carrying a data-ui-anchor attribute and resolves that anchor's name. It records the click only if the anchor is in a static allow-list of trackable controls. That allow-list, TRACKED_CONTROL_ANCHORS, is the app's data-ui-anchor registry (STATIC_UI_ANCHORS) filtered down to interactive kinds (button / link / toggle / tab / menuitem) and non-internal entries. Templated / blind-spot anchors that carry a runtime value (e.g. session-row:<id>) are not keys in that set, so they're naturally ignored — no normalization is needed and no per-instance id ever reaches the database. Crucially, this same allow-list is also the "never used" universe for controls, so "what we track" and "what we show as never-used" can never drift apart.

Capturing integration opens and closes. Integration "opens" are not tracked from a sidebar click — they're tracked at the setActiveProject chokepoint (the single place the active project changes). On a real transition, the newly-active project's folderPath is mapped through the INTEGRATION_REGISTRY (folder-path sentinel → integration id) and, if it resolves to a built-in integration, the open is recorded. Tracking at this chokepoint means opens via hotkey or the command palette count too, not just sidebar clicks. One case is intentionally suppressed: the startup-restore re-open of the last project (source === 'restore') is skipped, so an integration you happened to leave open isn't phantom-counted on every launch. The close is recorded symmetrically: the same chokepoint, on leaving an integration, records an integration_close event for the one being left, carrying the dwell (how long it was open, computed in the renderer and clamped to a sane [0, 24h]). Closes are kept paired with opens — a restore-suppressed open produces no close, and turning tracking off resets the open state — so you never get orphan closes.

Stamping the device. Every event (control click, integration open, integration close) is stamped with the current platform — mobile on a narrow, touch device, else desktop — at the moment it's buffered. This is the only new dimension on capture, and it stays local.

Buffering and flushing. Events accumulate in an in-renderer buffer and flush fire-and-forget to the main process over the ui-usage:record IPC channel — on a periodic timer (every ~10 s), and when the page is hidden / unloaded. The flush is loss-resilient: the buffer is swapped out before the await (so clicks during the network round-trip aren't lost), and a failed flush re-queues the batch (bounded) so a transient error doesn't silently drop usage.

Storage and counting (main). Records land in a dedicated local SQLite table, ui_usage_events(id, kind, target, platform, duration_ms, created_at, run_id, seq), where kind is control / integration / integration_close, target is the anchor key or integration id, platform is mobile / desktop / null, duration_ms is the dwell on a close (null otherwise), and run_id/seq (both nullable) are the local sequence context described under "Flow" above — a per-app-run id + a monotonic ordinal stamped renderer-side at buffer time (a batch's timestamp can't order events within it, so seq is the intra-run ordering key). Every added column is nullable, so rows recorded before it existed carry null. Timestamps are stamped JS-side (never datetime('now')). The main process exposes ui-usage:summary (per-(kind, target, platform) counts within a rolling 24h / 7d / 30d / all window plus the "tracking since" date — the per-platform grouping is what lets the card show the device split) and ui-usage:followers (the local Flow query: what was used within the next few events after a chosen control, in the same run, via an indexed self-join on (run_id, seq)). The counts query never selects run_id/seq, and the digest read stays grouped by (kind, target) only (no platform), so neither the sequence context nor the device split can reach the anonymous fleet digest; its egress allow-list also drops any kind other than control/integration, so a local-only integration_close can never ride it. That's the full extent of the backend's knowledge — it never knows about registries, visibility, or "never used".

The vocabulary crosswalk. Omniscio tracks actions in three separate id vocabularies — control anchors (this feature), keyboard-shortcut action ids, and telemetry feature ids — that historically never referenced each other. A small checked-in map, FLOW_CROSSWALK, links a logical action across the vocabularies where a genuine match exists (e.g. "Go to inbox" = the app-inbox-button anchor + the goToInbox shortcut + the inbox feature). A build guard fails on any dangling id, so the map stays honest against all three registries. The Flow card uses it to label results with friendly names.

Computing "never used" (renderer). The Diagnostics card asks for the windowed counts, then the report builder diffs them against the universes the renderer can see: the control catalogue and the currently-visible integrations (the same two sidebar visibility filters the projects sidebar uses, mapped to registry id + display name). The control catalogue starts from TRACKED_CONTROL_ANCHORS (labelled from each anchor's registry description) but is filtered to controls that can actually render in this install — a control's optional visibility metadata ({ devOnly, platforms, unreleasedFeature }) is evaluated with the very predicates the app renders by (isDevBuildRuntime(), the current OS, isUnreleasedFeatureVisibleInRenderer()), and a control that can't appear now is dropped. Anything left in a universe with a count of 0 is "never used"; the rest is "used", sorted by count, and each used entry also carries its mobile/desktop breakdown. Because both universes exclude what can't be reached, a turned-off feature or an unreachable control is omitted rather than flagged "never used".

Tagging a control's visibility. The visibility metadata lives in the app's data-ui-anchor registry. Because a whole feature's controls usually share one gate, each co-located *.ui-anchors.ts file wraps its anchors in a gatedBy({ … }, { … }) helper that stamps the gate onto all of them at once — so a new control added to that file inherits the gate automatically and can't silently drift back into the "never used" list. Mixed files set a per-anchor visibility that merges over the file gate.

The setting. usageTrackingEnabled is an AppSettings field, default true. Capture short-circuits when it's explicitly false. It is independent of the telemetry/send-consent setting by design.

Files (for agents with repo access)

  • Table / migrations — src/main/db/migrations/20260605180334-ui-usage-events-table.ts (creates ui_usage_events) + .../20260717030737-add-run-id-and-seq-... (run_id/seq) + .../20260717032035-add-platform-and-duration-ms-... (platform/duration_ms).
  • Queries (insert + windowed counts w/ optional platform split + "tracking since" + local Flow getUiUsageFollowers) — src/main/db/queries-ui-usage.ts.
  • IPC handlers (ui-usage:record, ui-usage:summary, ui-usage:followers) — src/main/ipc/handlers-ui-usage.ts.
  • Fleet-digest egress allow-list (fails closed on non-control/integration kinds) — src/main/services/telemetry/telemetry-digest-data.ts.
  • Vocabulary crosswalk (control-anchor ↔ shortcut ↔ feature) — src/shared/ui-flow-crosswalk.ts; contract .claude/memory/contracts/ui-flow-crosswalk-contract.md.
  • Flow card UI (Settings → Diagnostics → Health) — src/renderer/src/features/settings/sections/diagnostics/UiFlowCard.tsx.
  • Anonymous install id (Stage-2 forward-compat, inert in Stage 1) — src/main/services/install/install-id.ts.
  • Trackable-control universe / capture allow-list — src/renderer/src/lib/ui-usage-anchors.ts.
  • Control render-gate metadata (visibility) + the gatedBy() helper — src/shared/ui-anchor-types.ts + src/shared/ui-anchor-visibility.ts.
  • Renderer capture (buffer + platform stamp + control-click + project open/close + flush) — src/renderer/src/lib/ui-usage-tracker.ts.
  • Root click listener / flush hook — src/renderer/src/hooks/useUiUsageTracker.ts.
  • "Used vs never used" report builder — src/renderer/src/lib/ui-usage-report.ts.
  • Diagnostics card UI — src/renderer/src/features/settings/sections/diagnostics/UiUsageCard.tsx.
  • Full invariants + the tests that lock them — .claude/memory/contracts/ui-usage-tracking-contract.md.

Related

Two neighbouring surfaces are worth reading next:

  • Stats — the broader "how you use Omniscio" virtual project (cost, tokens, feature-event counts, trends). UI Usage tracking is narrower and local-only: it's about which named controls/integrations get clicked, surfaced in Settings → Diagnostics, not in Stats.
  • Feedback channel opt-out — the neighboring Settings → Diagnostics toggles for silencing outbound feedback/telemetry email (a different, send-related consent).

Last verified 2026-09-28