---
title: UI Auto-Tidy
---

# UI Auto-Tidy

> **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 Auto-Tidy and how does it work?"

## What it is

Auto-Tidy uses Omniscio's local "which controls you haven't used recently" signal (see
[UI Usage tracking](ui-usage-tracking.md)) to **quietly declutter the interface over
time**. When you've gone **30+ days without touching** a particular sidebar integration or
toolbar icon — even one you used before — Omniscio moves it somewhere quieter so the screen
stays calm:

- a **sidebar integration** collapses into a low-key **"Unused"** group at the bottom of
  the projects sidebar, and
- a **toolbar icon** drops into the toolbar's **"…" overflow** menu.

Crucially, tidying is **a gentle, reversible demotion — never a deletion**. Nothing is
removed, disabled, or uninstalled; the thing still works exactly as before, it's just
tucked out of the way. When Omniscio tidies something, it drops **one consolidated inbox
notice** telling you what moved, and everything stays restorable in **Settings →
Diagnostics → Hidden items** with a single click.

The feature is **opt-in and off by default** — auto-rearranging someone's UI without
asking would be surprising, so Omniscio never tidies anything until you turn it on.

> This is **100% local**, like the usage tracking it builds on. Auto-Tidy reads only this
> device's own click history; nothing about it is uploaded or shared. (It is unrelated to
> the separate, unbuilt "cross-user rollup" idea mentioned on the UI Usage page.)

## 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. Two things live there for this feature:
   - A toggle, **"Automatically tidy away controls you never use"** (**off by default**).
     Turning it on is what enables the whole loop.
   - A **"Hidden items"** card that lists everything Auto-Tidy currently has tucked away,
     each with a one-click **Restore**. When nothing is hidden it shows a short empty
     state.

## How it behaves

### How the tidying works

Once you've turned it on, Omniscio runs a quick check **when it launches and then at most once a
day** — the check is remembered across restarts, so quitting and reopening Omniscio several times
in a day never re-runs it. For something to be tidied away, **all** of these must be true:

- Auto-Tidy is turned on.
- There's been at least **30 days of usage history** — so a brand-new _install_ never tidies
  anything (on day one _everything_ looks unused, which is why there's a floor).
- **You haven't used the item in the last 30 days** — a rolling window. Something you used
  months ago but have since stopped using is fair game; something you clicked last week is not.
- **The item is established, not brand-new.** A feature added in a recent update gets its own
  30-day grace period — it's never tidied the day it appears (zero recent clicks then means
  _new_, not _ignored_). Omniscio proves "established" from your real history: it was used more
  than 30 days ago, or it's been on-screen 30+ days.
- The item is **currently relevant** — an enabled, visible integration, or a toolbar icon
  you actually have pinned. A feature you've already turned off isn't "tidied" (off is not
  the same as unused).
- You haven't already restored it (see below).

To keep the first tidy gentle, Omniscio moves **at most a handful of items per run** (a soft cap
of 8); anything left over moves on later days rather than all at once. Each run that moves
something posts **one** inbox notice — _"Omniscio tidied up N unused items"_. The notice is a
little **switchboard**: every tidied item gets its own row with its icon, a **one-line summary
of what it is**, a link to **open** it (for sidebar features), and a **switch to bring it back** —
flip it on to restore, off to tuck it away again — plus a **Bring all back** shortcut and a compact
footer with a one-line explanation and a **Turn off** link. Everything it lists is also in
Settings → Diagnostics → Hidden items.

### Nothing is deleted, and everything comes back

Tidying only changes **where** something lives, never whether it exists:

- A demoted integration is still fully enabled and works the same — it just renders inside
  the collapsed "Unused" group instead of the main list. Expand the group to see it.
- A demoted toolbar icon still works — it's just in the "…" overflow instead of pinned.

The simplest way to bring something back is to **just use it** — open the tucked-away
integration from the "Unused" group (or reach it any other way), or click the toolbar icon in
the "…" overflow, and Omniscio restores it to its normal spot on the spot. You can also **flip
its switch on** in the inbox notice, or open **Settings → Diagnostics → Hidden items** and click
**Restore**. Any of these returns the item immediately, and Omniscio **won't auto-tidy it
again** — even if you then go another 30 days without using it. (You can still deliberately tuck
it away yourself by flipping the notice switch back off.) The one exception to "using it brings
it back" is the inbox notice's own **open** link — that's a look-only peek so you can check what
a tidied item is without committing; use the notice's switch there. The Settings panel is the
always-available record, so you can find and undo a tidy there even after the notice is gone.

## For agents

### How it works (for agents with repo access)

The design mirrors the usage tracking it depends on: **all the intelligence lives in the
renderer**, and the main process does nothing new beyond raising one inbox alert. There is
no new database table, no new background service, and no new data leaving the device.

**State.** A handful of local `AppSettings` fields hold everything, persisted in `config.json`:

- `autoTidyEnabled` (default `false`) — the opt-in master switch.
- `autoTidyDemoted: string[]` — surface-prefixed keys currently demoted
  (`integration:<id>` or `toolbar:<id>`). This is the **single source of truth** every
  rendering surface reads to decide where to place an item.
- `autoTidyKept: string[]` — keys you've restored; permanently excluded from future
  auto-demotion.
- `autoTidyUnusedCollapsed` (default `true`) — whether the sidebar "Unused" group is
  collapsed.
- `autoTidyFirstSeen: Record<string, string>` (default `{}`) — demotedKey → ISO of when
  Auto-Tidy first observed each candidate. This is the per-item 30-day clock that keeps a
  freshly-added control from being tidied on day one.
- `autoTidyLastSweep: string | null` (default `null`) — ISO of the last sweep that actually
  ran; the once-per-24h-across-launches throttle keys off it.
- `autoTidyFirstSeenRepaired: boolean` (default `false`) — a one-time guard: true once the damage
  from a retired 2026-07-25 "reconcile" has been repaired. That reconcile clamped re-stamped
  first-seens back to the tracking start and couldn't tell an established control from a brand-new
  one, so it wrongly aged new controls; the repair resets any clamped first-seen forward to "now".
  Auto-Tidy never moves a first-seen backward.

**The allow-list.** A curated static registry is the _only_ set Auto-Tidy may touch.
Toolbar items are an explicit list (`TOOLBAR_HIDEABLE`), each paired with the
`data-ui-anchor` the usage tracker records its clicks under so the "never used" check
works; integrations are any currently-visible built-in _except_ an exclude set (the Alerts
integration is excluded — the tidy notice lives there). Essential controls (Send, Stop, the
composer input, Settings, Notifications, Focus Mode, the CPU-Burst button) are simply
absent from the allow-list, so they can never be tidied.

**The evaluator.** A pure function takes the all-time AND last-30-day usage counts + "tracking
since" date (two `ui-usage:summary` reads), the per-item `autoTidyFirstSeen` map, the
currently-relevant candidate list, and the current demoted/kept sets, and returns the
newly-eligible items — **rolling and per-feature**: past the global 30-day history floor, NOT
used in the last 30 days, and _established_ (used more than 30 days ago, OR first-seen ≥30 days
old), and not already demoted or kept — capped per run. A thin driver hook runs it on launch and
on a 24h interval, persists `autoTidyLastSweep` to skip a sweep under 24h old, and stamps a "seen
now" time into `autoTidyFirstSeen` for any new candidate. **One-time repair:** on its first run it
resets any first-seen at/before the tracking start — the fingerprint of a retired "reconcile" that
clamped clocks backward and wrongly aged brand-new controls — FORWARD to "now" (guarded by
`autoTidyFirstSeenRepaired`), so a new control regains its full 30-day cold-start clock; Auto-Tidy
never moves a first-seen backward. The hook waits for the full settings load before it runs (it
gates on `settingsFullyHydrated`) — at first mount the settings are still loading, so running early
would read the default "off" and then never retry, which on a machine that restarts often meant the
sweep silently never ran; gating on the load makes it run the moment real settings arrive. On a
non-empty result it writes the new keys into `autoTidyDemoted` and fires the consolidated notice; a
failure logs and never blocks launch.

**The levers.** Each surface reads `autoTidyDemoted` independently: the toolbar render forces
a demoted id into the "…" overflow (separate from your manual pin config), and the projects
sidebar reroutes a demoted integration's row into a collapsible "Unused (N)" group (mirroring
the existing "Archived" pattern). Neither lever touches the integration's enabled flag.

**The notice.** One consolidated inbox alert per run, raised through the existing
inbox-alert primitive via a thin `auto-tidy:notify` IPC that calls the same main-side
`createAlert` everything else uses. It carries an order-independent dedup key (prefixed
`AUTO_TIDY_DEDUP_PREFIX`) so two runs before you act don't double-post — and that key also
_packs this run's tidied item keys_, so the renderer needs no extra data to draw the notice.
The renderer's `AlertInboxViewer` recognises the key and renders the **Switchboard**
(`AutoTidyNoticeBody`) instead of the plain text viewer: one row per tidied item with its
icon, an _open_ link (integrations → `activateVirtualProject`), a Hidden/Back status, a
bring-back toggle, and the "why" explanation. The plain-text body is the fallback for
list/notification previews (one item per line + a `omniscio://setting/auto-tidy-hidden-items`
link).

**Restore / re-hide.** `restoreKey` (drop from `autoTidyDemoted`, add to `autoTidyKept`) and
its exact inverse `reHideKey` are pure transitions; the Settings card, the notice's switch, and
**auto-restore-on-use** all call `restoreKey` (reading fresh store state) and persist both arrays.
The kept set is what keeps the _evaluator_ from re-tidying a restored item; a deliberate
switch-off re-hides it.

**Auto-restore-on-use.** Using a demoted item auto-restores it. The renderer's
`restoreDemotedKeyOnUse` rides the two ui-usage "use" chokepoints — an integration open at
`trackProjectActivation` (the universal `setActiveProject` funnel, so sidebar / search / keyboard /
deep-link opens all count) and a hideable toolbar-control click at `bufferControlClick`. It maps the
event back to its demoted key (`demotedKeyForUsage`) and, if that key is currently demoted, calls
`restoreKey`. The tidy-notice's own open link is exempt (`AUTO_TIDY_NOTICE_ACTIVATION_SOURCE` — a
look-only peek), and restore-on-use is gated with local usage tracking exactly like the events it
rides. No new state, no new egress — it reuses the same sanctioned transition as manual Restore.

### Files (for agents with repo access)

- Settings type + defaults — [src/shared/types.ts](../../src/shared/types.ts)
  (`autoTidyEnabled` / `autoTidyDemoted` / `autoTidyKept` / `autoTidyUnusedCollapsed`); Zod —
  [src/shared/ipc-schemas/update-settings.ts](../../src/shared/ipc-schemas/update-settings.ts).
- Hideable registry + key helpers — [src/shared/auto-tidy-registry.ts](../../src/shared/auto-tidy-registry.ts).
- Eligibility evaluator (pure) — [src/renderer/src/lib/auto-tidy-evaluator.ts](../../src/renderer/src/lib/auto-tidy-evaluator.ts).
- Demoted/kept state transitions (pure) — [src/renderer/src/lib/auto-tidy-state.ts](../../src/renderer/src/lib/auto-tidy-state.ts).
- Auto-restore-on-use (reuses `restoreKey`; triggered from the usage tracker) — [src/renderer/src/lib/auto-tidy-restore-on-use.ts](../../src/renderer/src/lib/auto-tidy-restore-on-use.ts) + [src/renderer/src/lib/ui-usage-tracker.ts](../../src/renderer/src/lib/ui-usage-tracker.ts).
- Candidate builder (pure) — [src/renderer/src/hooks/auto-tidy-candidates.ts](../../src/renderer/src/hooks/auto-tidy-candidates.ts).
- Driver hook (launch + daily) — [src/renderer/src/hooks/useAutoTidy.ts](../../src/renderer/src/hooks/useAutoTidy.ts).
- Consolidated inbox notice IPC (`auto-tidy:notify` → `createAlert`) — [src/main/ipc/handlers-auto-tidy.ts](../../src/main/ipc/handlers-auto-tidy.ts).
- Switchboard notice card (per-item icon / open / bring-back toggle / why) — [src/renderer/src/features/alerts/AutoTidyNoticeBody.tsx](../../src/renderer/src/features/alerts/AutoTidyNoticeBody.tsx), wired in [AlertInboxViewer.tsx](../../src/renderer/src/features/alerts/AlertInboxViewer.tsx).
- Toolbar overflow lever — [src/renderer/src/features/toolbar/toolbar-items.ts](../../src/renderer/src/features/toolbar/toolbar-items.ts).
- Sidebar "Unused" group — [src/renderer/src/features/dashboard/auto-tidy-unused-group.ts](../../src/renderer/src/features/dashboard/auto-tidy-unused-group.ts) + [ProjectsSidebar.tsx](../../src/renderer/src/features/dashboard/ProjectsSidebar.tsx).
- Settings opt-in toggle + Hidden-items card — [src/renderer/src/features/settings/sections/diagnostics/HiddenItemsCard.tsx](../../src/renderer/src/features/settings/sections/diagnostics/HiddenItemsCard.tsx).
- Full invariants + the tests that lock them — [.claude/memory/contracts/auto-tidy-contract.md](../../.claude/memory/contracts/auto-tidy-contract.md).

## Related

- [UI Usage tracking](ui-usage-tracking.md) — the local "used vs. never used" signal
  Auto-Tidy acts on. UI Usage just _reports_ what you use; Auto-Tidy _acts_ on the
  never-used part by gently demoting it.
- [Coaching Tips](coaching-engine.md) — the neighboring "teach you about unused features"
  nudges; Auto-Tidy instead removes visual noise from features you've proven you don't use.
