Docs
Read tab visits without calling them adoption
Tab usage ranks hosted dashboard destinations by recorded navigation events over the last 30 days. Use that ranking to question placement and discoverability, not to infer who visited, how long they stayed, or whether the tab helped.
For: the team owner or admin deciding which hosted surfaces deserve closer inspection, clearer placement, or a follow-up question
What it does, and why it helps
The hosted dashboard shell sends one best-effort event when its pathname changes to a registered team tab. The recorder stores the team account as actor, a bounded tab key, and a timestamp in the existing event store. Tab usage groups those events in SQL, shows Total visits, Tabs visited, and Window, then lists Tab, Visits, Share, and Last visit.
A visit is an open, not a person, session, outcome, or endorsement. Reloads and repeat navigation can add events. The Share bar is scaled to the busiest visible tab, so it is a relative bar, not the tab's percentage of all visits.
The pain. A crowded navigation can be shaped by opinion when nobody checks which destinations are opened. Raw traffic can also mislead when repeated opens are treated as unique people or product value.
The point of view. Use visit counts as a prompt to investigate the menu, not as an adoption verdict. The ranking identifies where to ask why; it does not supply that answer.
What gets easier. Seeing which registered hosted tabs received navigation events, comparing count and recency, opening the named destination, and spotting an honest empty or read-error state.
When it helps. Use it before featuring, moving, or demoting a hosted tab, after a navigation change, or when a surface seems absent from normal team workflow.
Its limits. The page has no unique-user, session, dwell-time, conversion, role, device, source-path, or satisfaction measure. Failed recording requests are swallowed. Local desktop clicks stay in a separate local JSONL and do not appear in this hosted view.
Understand it in 30 seconds
Read the narration
- 0:00 Repeated navigation can masquerade as adoption when reloads and curiosity count alike.
- 0:06 Treat visits as a menu signal, never as proof of value or satisfaction.
- 0:13 RepoOps counts hosted page visits for thirty days, grouped by tab.
- 0:17 It keeps no user identity and shows the last visit date.
- 0:23 Use the ranking to investigate placement, then validate why people opened each surface.
Synthetic example. Read the guide
Where to find it
- Hosted:
repoops.ai/team/tab-usage, from Team & settings in the sidebar, then Tab usage under Workspace utilities. - Desktop: hosted only.
When to use it
Review a candidate for better placement
Situation. A useful hosted surface appears low in the 30-day visit ranking or has no row.
What you do. Check its Visits and Last visit, open the destination from its row, then inspect whether the sidebar group, advanced-surface state, or task flow makes it hard to find.
What you see. The table orders higher counts first and breaks ties by the latest visit. A registered tab with no recorded event has no row.
What it establishes. You have a navigation signal for further review. You do not yet know whether low traffic means poor discovery, narrow relevance, or low value.
Question a high-traffic surface
Situation. A tab leads the table and its relative bar fills the Share column.
What you do. Compare the exact visit count and date, then ask whether the route is a frequent workflow, a landing page, or a place people revisit because the answer is hard to find.
What you see. Each pathname navigation can add one event. The full bar means busiest visible row, not all traffic or a unique-user share.
What it establishes. The count narrows the next product question without turning traffic into proof of satisfaction.
Verify the privacy boundary
Situation. An owner wants aggregate navigation evidence without a teammate-by-teammate activity report.
What you do. Inspect the stored event contract: kind audit, action tab.visit, team account actor, tab key, and timestamp. Confirm the aggregate page offers no person filter or person column.
What you see. The recorder derives the team from the signed-in membership and never places the visiting user ID in the event. The page queries one team and groups by tab key.
What it establishes. The stored row supports per-team tab counts and recency. It does not directly support a per-user usage breakdown.
Diagnose an empty or partial view
Situation. The page says No tab visits recorded yet, or recent navigation is missing.
What you do. Open registered hosted tabs, refresh Tab usage, and distinguish an empty aggregate from Could not read the team event store right now.
What you see. The navigation sender is fire-and-forget and ignores network, rate-limit, and storage failures. The recorder can accept a request with recorded false when storage is unavailable.
What it establishes. An empty row set means no retained events were read for the window. It is not proof that nobody navigated there.
Before you start
- Supported versions
- RepoOps v0.3.1. Hosted pathname recording, team-account actor stamping, SQL aggregation, the 30-day owner and admin view, and daily event retention are present in this release.
- Where it runs
- Hosted only at repoops.ai/team/tab-usage, listed under Workspace utilities on Team & settings. Local settings links to this hosted page. A separate desktop recorder has no local visual page.
- Permissions
- The team dashboard requires sign-in, a hosted entitlement, and an allowed seat. Every allowed member navigation can record a visit. Only owners and admins can open Tab usage; other roles are redirected to team Home.
- Connections
- Recording needs the hosted browser session, the team dashboard layout, and a reachable status endpoint and event store. The page needs the hosted event database. No desktop binding is required for hosted navigation events.
- Plan
- The shared team layout requires a hosted tier and an allowed seat. No separate Tab usage entitlement was found beyond that hosted dashboard gate.
Configure it
- Open the hosted dashboard normally.
The navigation sender is mounted once in the shared team layout. It resolves each pathname against the same registered tab map used by navigation.
- Wait for real navigation instead of seeding a target.
The page becomes useful as teammates open registered destinations. It has no import or backfill action, and direct test events would mix test traffic with product evidence.
- Read count, date, and bar separately.
Visits is the event count. Last visit is the latest event date. Share is a relative width against the busiest visible tab, not a fraction of Total visits.
- Follow the row into the real tab.
Known keys link to the current dashboard destination. Inspect the task and its placement before changing navigation from count alone.
- Keep desktop click evidence separate.
The local dashboard writes tab, scope, kind, and time to tab-usage.jsonl. That file is not uploaded into the hosted team count.
| Setting | Where | A sensible choice | Why it matters |
|---|---|---|---|
Window | Tab usage summary | 30 days, fixed | The page sets the query start to 30 days ago. No date-range or window control is provided. |
REPOOPS_NEON_RETENTION_DAYS | hosted runtime environment | unset, which is 30 | The daily retention cron hard-deletes visit rows older than this global window. Thirty is the default and the ceiling. An org policy can tighten it, so stored history may be shorter than 30 days. |
REPOOPS_TAB_USAGE_MAX_BYTES | local desktop environment only | 5242880 by default | This caps the separate local tab-usage.jsonl and compacts it to the newest half. It does not change the hosted table or its 30-day view. |
What you should see
Visits are present
Configuration. The team has retained tab.visit audit events inside the last 30 days and the aggregate query succeeds.
Expect. Total visits, Tabs visited, and Window appear above rows sorted by count, then recency. Known rows link to their hosted tab.
Verify. Add the visible Visits values and compare the latest displayed date. Treat the Share bars only as relative lengths against the busiest row.
No visits are read
Configuration. No retained tab.visit event for the team falls inside the page window.
Expect. The page reads No tab visits recorded yet and links to team home.
Verify. Navigate to a registered hosted tab and return. If the empty state remains, inspect the recording request, membership, rate limit, and event-store write rather than assuming zero use.
The store read fails
Configuration. The team-scoped aggregate query throws.
Expect. The page reads Could not read the team event store right now. Try again shortly.
Verify. Check hosted database and application logs. The page does not convert a read failure into a zero count.
A navigation event is not recorded
Configuration. The browser is offline, the team bucket is rate-limited, or event insertion fails.
Expect. Navigation still completes. The missing visit does not appear later unless another navigation sends a new event.
Verify. Inspect the network response. A rate limit returns 429; storage failure returns accepted with recorded false. The client deliberately shows no toast.
A key no longer matches the registry
Configuration. A retained or directly submitted valid slug has no current dashboard metadata entry.
Expect. The table shows the raw tab key with a bullet instead of a linked label and icon.
Verify. Compare the key with DASHBOARD_TABS. The recorder validates slug shape and length, but it does not validate membership in the current tab registry.
Data and cost
- What is captured
- Each hosted event has a random event ID, version, timestamp, kind audit, team account ID, actor with type team and the same team ID, and payload with action tab.visit, accountId, and a tab key capped at 64 characters. No visiting user ID, URL, referrer, duration, or page payload is written by this recorder.
- Who can see it
- Owners and admins see the team-scoped aggregate. Other seated members create events by navigating but cannot open the aggregate page. Rows expose tab label or raw key, count, a relative bar, and latest visit date.
- How long it is kept
- The page reads 30 days. A visit is stored as an audit row but records usage, so the daily 06:00 UTC sweep removes it on the 30-day telemetry window (REPOOPS_NEON_RETENTION_DAYS can shorten that), not on the 13 months other audit rows keep; an org retention rule can shorten it further. The sweep hard-deletes old visit events. The separate local JSONL uses a byte cap and newest-half compaction instead.
- What leaves the machine
- Hosted navigation sends the tab key in a session-authenticated POST to RepoOps. Team and actor identity are resolved server-side from membership. The separate desktop recorder writes locally and does not feed this hosted aggregate.
- What it costs
- Recording, SQL aggregation, sorting, and rendering make no model call. They use hosted request, database write, storage, retention, and query capacity.
When the result differs
| Symptom | Likely cause | Next action |
|---|---|---|
| Tab usage redirects to team Home. | The signed-in member is not an owner or admin. | Ask a team owner or admin to review the aggregate. Sidebar visibility is cosmetic; the page repeats the server-side role check. |
| Tab usage redirects to Billing. | The team lacks a hosted entitlement or this member is outside the allowed seats. | Review the team subscription and seat assignment before retrying the hosted dashboard. |
| A recent open is missing. | The fire-and-forget sender may have failed, hit the shared per-team in-process rate limit, or reached an unavailable recorder. | Inspect the browser request and application logs. Navigation success does not mean the best-effort event was stored. |
| The Share bar looks like a percentage, but its width does not match Visits divided by Total visits. | The bar divides each row by the busiest visible row, not by total events. | Use the Visits number for arithmetic. Read the bar only as a relative comparison to the top row. |
| A raw slug appears instead of a tab label. | The event key has no matching current registry entry. The POST route checks slug syntax but not registry membership. | Compare the key with the dashboard registry and determine whether it is stale, renamed, or directly submitted. |
| Desktop clicks do not appear here. | The desktop and hosted recorders use separate stores and no sync joins them. | Read the local API or tab-usage.jsonl for desktop evidence. Do not combine the counts without an explicit analysis outside this page. |
- Disable
- Not provided as a feature switch. The hosted sender mounts with the team dashboard layout and records best-effort navigation for seated members. Leaving the dashboard or removing access stops future events from that browser.
- Roll back
- Not provided. This page makes no navigation change itself; it only supplies evidence for a later product decision. Reverse any menu change in the navigation registry through the normal reviewed release flow.
- Revoke access
- Remove team membership or seat access to stop that account from opening the hosted dashboard. No per-user tab history exists for this feature, and no per-user revoke action is provided on the page.
- Delete
- Not provided per tab or event. The shared events-retention cron hard-deletes visits past its effective cutoff. Tab usage has no clear, reset, exclude, or manual delete control.
Related tasks
Maintenance evidence
- Feature id
tab-usage(spine leaftab-usage)- Owner
- UX Foundation, hosted dashboard IA redesign analytics and R12 local rail instrumentation. Guide: LDG-0717.
- Supported product version
- RepoOps v0.3.1
- Last verified
- 2026-09-15, read against origin/main at 52366bb6d; labels read from the served tab source; dashboard shell gates, pathname resolution, best-effort recorder, team actor, SQL aggregation, projection, retention, and the separate local recorder checked in source and tests.
- Example fixtures
- website/lib/tab-usage-view.test.ts covers event filtering, 30-day default projection, aggregation, sorting, empty state, key bounds, and row limits; website/lib/tab-visits-repo.test.ts uses the real events schema to cover SQL counts under unrelated traffic, team isolation, windowing, missing keys, and last visit; website/app/api/cron/events-retention/route.test.ts covers the 30-day default, the per-kind windows, override, cutoff, auth, and idempotent deletion; lib/tab-usage.test.mjs covers the separate local slug, scope, kind, aggregate, round-trip, and empty paths. No dedicated hosted client-recorder, route, or page render test file was found.
- Source references
lib/canonical-spine.json,lib/canonical-tabs.mjs,website/lib/dashboard-nav.ts,website/app/team/(home)/layout.tsx,website/app/api/tab-usage/route.ts,website/app/team/(home)/tab-usage/page.tsx,website/lib/audit-events-repo.ts,website/lib/tab-usage-view.ts,website/lib/device-ingest-rate-limit.ts,website/lib/events-repo.ts,website/db/schema.ts,website/app/api/cron/events-retention/route.ts,website/vercel.json,public/dashboard.html,lib/routes/tab-usage.mjs,lib/tab-usage.mjs- Documentation review
- Independent review requested on the slice pull request; not yet recorded.
- Video review
- Narrated story rendered and published 2026-09-26 (render 52f928d0a188, LDG-1018) with the breadcrumb Team & settings, checked against main at 0f24215b5. One frame, the captions and the transcript were reviewed by the authoring agent, not an independent reviewer; the audio was not listened to by a person. Narration is the provisional Windows voice until LDG-0721.
Last updated