# Novus AI Stats > Novus AI Stats is a privacy-first progressive web app that imports AI chat exports and coding-agent logs, calculates transparent cross-provider statistics, and creates shareable snapshot recaps. Imports are parsed transiently in the browser. Raw prompts, responses, attachments, source files, and local paths are discarded once parsing finishes; only normalized statistics are stored. Every metric is labelled exact, source reported, derived, estimated, or unavailable, so estimates are never presented as facts. The app cannot watch local folders in the background. Every import starts with an explicit user file selection. All links are absolute and canonical. Every product, integration, documentation, tutorial, blog, and policy page listed below also appears in https://aistats.novusstreamsolutions.com/sitemap.xml. Planned provider pages under /integrations are excluded from both surfaces because those pages are marked noindex. ## Product - [Novus AI Stats](https://aistats.novusstreamsolutions.com/): Track AI activity across seven supported chat and coding-agent sources with privacy-safe imports, projects, recaps, and sharing. - [Features](https://aistats.novusstreamsolutions.com/features): Dashboards, session provenance, projects, constellation, recaps, achievements, notifications, exports, and private sharing. - [Pricing](https://aistats.novusstreamsolutions.com/pricing): Free with every feature included and no product quotas; security rate limits and file-safety ceilings protect the service. - [Methodology](https://aistats.novusstreamsolutions.com/methodology): What every AI Stats metric measures, how it is calculated, what high and low mean, what could mislead you, and which sources can report it at all. - [Security](https://aistats.novusstreamsolutions.com/security): How AI Stats minimizes imported data, isolates accounts, gates optional scripts, and protects authentication and sharing. - [About](https://aistats.novusstreamsolutions.com/about): Novus AI Stats gives AI-heavy builders a private, truthful view of their work across assistants and coding agents. - [Roadmap](https://aistats.novusstreamsolutions.com/roadmap): Which AI Stats capabilities and provider adapters are shipped, being improved, or planned. - [Changelog](https://aistats.novusstreamsolutions.com/changelog): Shipped AI Stats releases, production hardening, importer changes, analytics, and public product updates. ## Integrations - [Integrations overview](https://aistats.novusstreamsolutions.com/integrations): The seven production import adapters and the clearly labelled planned AI providers. - [ChatGPT](https://aistats.novusstreamsolutions.com/integrations/chatgpt): Supported import source. Official account export zip or conversations.json. - [Claude](https://aistats.novusstreamsolutions.com/integrations/claude): Supported import source. Official Claude export with JSON shape detection. - [Gemini](https://aistats.novusstreamsolutions.com/integrations/gemini): Supported import source. Google Takeout export. - [Claude Code](https://aistats.novusstreamsolutions.com/integrations/claude-code): Supported import source. User-selected local JSONL transcripts or telemetry. - [Codex](https://aistats.novusstreamsolutions.com/integrations/codex): Supported import source. User-selected session log files. - [Gemini CLI](https://aistats.novusstreamsolutions.com/integrations/gemini-cli): Supported import source. Local OpenTelemetry output. - [Cursor](https://aistats.novusstreamsolutions.com/integrations/cursor): Supported import source. Exported Markdown or a user-selected local SQLite snapshot. ## Docs - [Documentation](https://aistats.novusstreamsolutions.com/docs): Setup, provider import, metrics, privacy, recovery, troubleshooting, feature, and PWA documentation. - [Help centre](https://aistats.novusstreamsolutions.com/help): Troubleshoot imports, metrics, authentication, sharing, account recovery, and PWA behaviour. - [Frequently asked questions](https://aistats.novusstreamsolutions.com/faq): Straight answers about imports, privacy, supported providers, account recovery, quotas, and metric quality. - [Account security and manual recovery](https://aistats.novusstreamsolutions.com/docs/account-security-recovery): Change passwords, inspect devices, revoke sessions, and recover a locked account safely. - [Analytics, projects, recaps, and achievements](https://aistats.novusstreamsolutions.com/docs/features-workflows): Use filters, sessions, project confirmation, insights, constellation, recaps, notifications, and achievements. - [Compare views and save the ones you reuse](https://aistats.novusstreamsolutions.com/docs/compare-and-saved-views): Use comparison mode only when both sides are coverage-safe, and save a dashboard view as a name plus the six canonical filter params. - [Metrics, provenance, and quality](https://aistats.novusstreamsolutions.com/docs/metrics-quality): Understand time, prompts, tools, agents, tokens, cost estimates, null values, and source quality. - [Privacy, exports, deletion, and sharing](https://aistats.novusstreamsolutions.com/docs/privacy-sharing): Know exactly what reaches the server, what is discarded, and how immutable public snapshots work. - [Import the seven supported providers](https://aistats.novusstreamsolutions.com/docs/provider-imports): Supported input formats and safe import expectations for ChatGPT, Claude, Gemini, Claude Code, Codex, Gemini CLI, and Cursor. - [Install, update, and use the PWA safely](https://aistats.novusstreamsolutions.com/docs/pwa-offline): Install AI Stats, accept updates, and understand privacy-safe offline navigation. - [Getting started](https://aistats.novusstreamsolutions.com/docs/getting-started): Create an account, choose privacy defaults, import your first source, and read the dashboard honestly. - [Troubleshoot imports and duplicates](https://aistats.novusstreamsolutions.com/docs/troubleshooting-imports): Resolve unknown exports, corrupt files, partial imports, duplicate estimates, and cancelled jobs. ## Tutorials - [Tutorials](https://aistats.novusstreamsolutions.com/tutorials): Step-by-step tutorials for exporting provider data, importing it safely, reading your dashboard, grouping projects, and sharing snapshots. - [Export your data from a provider and import it](https://aistats.novusstreamsolutions.com/tutorials/export-and-import-your-first-source): Request an official export, choose the source and privacy mode, read the local preview, and create your first import. - [Read your dashboard without fooling yourself](https://aistats.novusstreamsolutions.com/tutorials/read-your-dashboard): Set the range, check coverage before totals, tell unavailable apart from zero, and use provenance to check any number you doubt. - [Share a snapshot without leaking anything](https://aistats.novusstreamsolutions.com/tutorials/share-a-snapshot-safely): Pick a range, choose the exact fields, decide visibility and indexing, set an expiry, and revoke a link you regret. - [Group sessions into projects you actually recognise](https://aistats.novusstreamsolutions.com/tutorials/group-sessions-into-projects): Turn anonymous repository hashes into confirmed projects, merge duplicates, and read project analytics you can trust. - [Compare two slices of your AI work](https://aistats.novusstreamsolutions.com/tutorials/compare-two-dashboard-views): Seed comparison from the dashboard, leave side B independent, read refused rows as findings, and save the view you want to reopen. ## Blog - [Blog](https://aistats.novusstreamsolutions.com/blog): Practical guides for measuring AI work, importing provider exports, coding-agent telemetry, and privacy-safe analytics. - [AI Usage Constellations Without Exposing Session Names](https://aistats.novusstreamsolutions.com/blog/ai-usage-constellations-without-exposing-names): Explore provider, model, project, date, and metric patterns as a session constellation while keeping conversation titles gated and workspace paths represented only by one-way hashes. - [How to Compare AI Usage Without Misleading Deltas](https://aistats.novusstreamsolutions.com/blog/compare-ai-usage-without-misleading-deltas): A defensible AI-usage comparison starts with two explicit slices, checks metric coverage and provenance, and keeps refused deltas visible instead of silently dropping them. - [Unavailable Is Not Zero: Why Claude, Gemini, and ChatGPT Tokens Stay Blank](https://aistats.novusstreamsolutions.com/blog/unavailable-is-not-zero): Consumer chat exports omit tokens, cost, and often model names. AI Stats renders those fields Unavailable, never a synthetic 0, and explains the capability reason underneath. - [The Capability Matrix: What Each AI Export Can Actually Tell You](https://aistats.novusstreamsolutions.com/blog/capability-matrix-what-exports-can-tell-you): A source-by-source reading of the AI Stats capability matrix: Verified versus Beta, Not in export versus Not parsed yet, and why a blank Claude or Gemini tile is evidence rather than a bug. - [What Leaves Your Device When You Import an AI Export, and What Does Not](https://aistats.novusstreamsolutions.com/blog/what-leaves-your-device-when-you-import): Your export is parsed in the browser before anything is uploaded. The exact path from file picker to database, including the limits. - [Twelve Totals and Five Quality Labels: What the Dashboard Measures](https://aistats.novusstreamsolutions.com/blog/what-the-dashboard-measures): Ranges, timezone boundaries, previous-period comparison, unavailable versus zero, and the rules that make a cost estimate refuse. - [AI Active Time vs. Session Time: What Your Statistics Actually Mean](https://aistats.novusstreamsolutions.com/blog/ai-active-time-vs-session-time): Why an AI session lasting three hours does not necessarily mean the model worked for three hours. - [How to Track Your AI Usage Across ChatGPT, Claude, Gemini, and Coding Agents](https://aistats.novusstreamsolutions.com/blog/track-ai-usage-across-tools): A practical guide to combining AI chat exports and coding-agent logs without pretending every provider exposes the same statistics. - [Claude Code, Codex, and Gemini CLI: What Can Be Measured Today](https://aistats.novusstreamsolutions.com/blog/what-ai-coding-tools-can-measure): A source-by-source look at coding-agent sessions, telemetry, tokens, tools, agents, and the limits of cross-platform comparison. ## Policies - [Privacy policy](https://aistats.novusstreamsolutions.com/privacy): How AI Stats handles normalized statistics, raw provider exports, analytics consent, sharing, exports, and account deletion. - [Terms of service](https://aistats.novusstreamsolutions.com/terms): Account, acceptable-use, analytics accuracy, availability, export, sharing, and deletion terms. - [Cookie policy](https://aistats.novusstreamsolutions.com/cookies): Necessary, functional, analytics, and advertising cookies, plus consent and withdrawal behaviour. - [Accessibility statement](https://aistats.novusstreamsolutions.com/accessibility): Keyboard navigation, reduced motion, charts, graph fallbacks, and zoom support. - [Contact](https://aistats.novusstreamsolutions.com/contact): Reach support for account, recovery, import, privacy, accessibility, or security questions, and see where the team is based. - [Editorial policy](https://aistats.novusstreamsolutions.com/editorial-policy): How the Novus Stream Solutions Editorial Team reviews AI Stats metric claims, uses primary export fixtures, handles AI assistance and corrections, and records substantive updates. - [License](https://aistats.novusstreamsolutions.com/license): The proprietary licence for AI Stats: free ad-supported use, private source, and no redistribution or reverse engineering. - [AI access policy](https://aistats.novusstreamsolutions.com/ai-policy): Which AI crawlers and assistants may read this site, what we ask for in return, and exactly what crawling does and does not collect. ## Company - [Press kit](https://aistats.novusstreamsolutions.com/press): Boilerplate, product facts, downloadable brand marks, and naming rules for writing about AI Stats. - [System status](https://aistats.novusstreamsolutions.com/status): A live component check of the website, database, imports, authentication, and sharing, run when the page is requested. - [Site map](https://aistats.novusstreamsolutions.com/map): Every public AI Stats page in one list (product, integrations, docs, tutorials, blog, and policies) plus the other Novus apps. - [Social accounts](https://aistats.novusstreamsolutions.com/social): Every official Novus Stream Solutions profile, the same nineteen this site publishes as the publisher Organization's sameAs. - [Language support](https://aistats.novusstreamsolutions.com/languages): The languages Novus AI Stats serves today, the locales the Novus estate has agreed on, and how a language reaches a URL. - [MCP server](https://aistats.novusstreamsolutions.com/mcp-server): The Model Context Protocol endpoint: the address, how to add it to an AI client, every tool it exposes, and why no tool here can read anyone's transcripts. ## More from Novus Stream Solutions - [Novus Stream Solutions](https://novusstreamsolutions.com): The hub for every Novus app, with docs, tutorials, and product updates. - [NSS Background Remover](https://bgremover.novusstreamsolutions.com): 32 in-browser AI image and video tools; no upload, no signup, free. - [Novus Convert](https://convert.novusstreamsolutions.com): 1,022 published file-conversion routes, validated locally in the browser. - [Novus PDF Studio](https://pdf.novusstreamsolutions.com): One private hub for every PDF job: seven page tools plus a fill-and-sign editor. - [Novus Learn](https://learn.novusstreamsolutions.com): Turn an article, URL, paper, video, textbook, or codebase into cited study material. - [Novus Examples](https://examples.novusstreamsolutions.com): 2,428 generated test files across 17 categories, each with an exact spec sheet. - [Novus Visualizers](https://visualizers.novusstreamsolutions.com): Turn a track into a beat-synced video with 54 modes, rendered in the browser. - [Novus Restaurant](https://restaurant.novusstreamsolutions.com): Public food-market intelligence built from official data sources, with the provenance shown next to every observation. - [Novus Striker](https://striker.novusstreamsolutions.com): Arcade soccer that runs entirely in a browser tab. ## Optional - [Full text](https://aistats.novusstreamsolutions.com/llms-full.txt): This index followed by the complete text of every documentation page, tutorial, and blog post. - [XML sitemap](https://aistats.novusstreamsolutions.com/sitemap.xml): Machine-readable list of every indexable URL with last-modified dates. - [Blog RSS feed](https://aistats.novusstreamsolutions.com/rss.xml): RSS 2.0 feed of every blog post, generated from the same content registry as the blog index. - [MCP server](https://aistats.novusstreamsolutions.com/mcp): JSON-RPC over a single POST; a GET returns 405. It describes the supported sources, what every metric measures, and which exports cannot report a given metric and why. It CANNOT import, parse or analyse an export, and it cannot read a signed-in user's statistics: parsing happens in the visitor's own browser and this endpoint holds no file and no account. # Full content The complete text of every documentation page, tutorial, and blog post, in plain markdown. Headings inside each body are the author's own. ## Documentation ### Account security and manual recovery URL: https://aistats.novusstreamsolutions.com/docs/account-security-recovery Category: Account Updated: 2026-07-29 ## Active devices Settings lists active sessions with creation and expiry data. Revoke one unrecognized session or revoke every other device. Changing your password also revokes other sessions. ## Manual recovery Email aistats@novusstreamsolutions.com from the address registered to the account. After verifying the request, the owner can generate a cryptographic temporary password in the private admin area. It is displayed once, expires, revokes existing sessions, and is sent manually through Zoho. ## Forced password change After signing in with the temporary password, only the password-change screen is accessible. Replace it before using the rest of the app. Recovery credentials are never logged or stored in plaintext by AI Stats. There is no public forgot-password link until automated verified email delivery is adopted. --- ### Analytics, projects, recaps, and achievements URL: https://aistats.novusstreamsolutions.com/docs/features-workflows Category: Features Updated: 2026-07-29 ## Analytics and sessions Dashboard ranges and provider, project, and model filters live in the URL. Sessions support search, pagination, sorting, date filters, project assignment, tags, notes, privacy changes, archive, deletion, and exports. ## Projects Create, edit, archive, merge, and map repositories. Imported repository hashes are suggestions only. Confirm an assignment before a project appears in project analytics. Add milestones and open project-filtered sessions. ## Visual and social features Insights use real aggregates. The constellation loads only on its route and caps detailed nodes at 300 with an accessible table. Achievements use versioned rules. Completed weekly, monthly, yearly, provider, and project periods generate immutable recap snapshots and notifications. --- ### Compare views and save the ones you reuse URL: https://aistats.novusstreamsolutions.com/docs/compare-and-saved-views Category: Features Updated: 2026-08-09 The dashboard is a filtered slice of your imports: a range, a provider set, a project set, and an optional model substring. Two features exist to stop you from reconstructing that slice by hand every time, and to stop you from subtracting two slices that were not recorded the same way. Neither feature invents metrics. Comparison will often look empty on a mixed ChatGPT / Claude / Gemini account. That is the honest result. [Unavailable is not zero](/blog/unavailable-is-not-zero). ## Open comparison from the dashboard you already have On [/app/dashboard](/app/dashboard), use **Compare**. The link is built from the view on screen (`comparisonHrefFromView`), so side A opens already seeded with your current range and filters. Side B is left at its defaults on purpose. Seeding both sides with the same slice would open the page on a comparison of a view with itself and teach you, in one screen, that the feature is broken. All twelve parameters live in the URL, namespaced `aRange` / `bRange` and so on. A comparison you can read is a comparison you can bookmark or send to yourself. Nothing in that query string is a session title, a prompt, or a transcript. ## When a row may print a delta `buildComparisonReport` does not reimplement a comparison rule. It calls the same function the period-over-period narrative uses, and a difference is printed only when **both** sides were fully covered **and** every contributing session shared the same adapter version, normalization version, quality, source, and calculation. Where it refuses, the row keeps its place and names the gate: - A side has no sessions, so there is nothing to compare. - A side has no value. You get the capability-matrix reason, for example that the Claude export format does not contain tokens. - A side is only partly covered, and the missing sessions are missing by export format, not at random. - Signatures differ: the two sides recorded the metric differently, so subtracting them would compare two methods. Blocked rows are kept, not filtered out. A tidy table of only the comparable metrics answers a different question from the one you asked, and you would have no way to see which rows were removed. On a mixed-provider account most of this table will read "Not comparable." Read [the capability matrix](/blog/capability-matrix-what-exports-can-tell-you) and [/methodology](/methodology) if you want the underlying grades. Composition aligns providers, models, and project groups across both sides. A key present on one side only keeps its row and reads `Not in this view` on the other. That asymmetry is usually the finding. ## Save a view as a name and a query string A saved view is not a snapshot of numbers. It is a **name** plus the six dashboard filter params: range, custom from, custom to, providers, projects, and model substring. They live in the `dashboard_view` table (migration `0007`), unique on `(userId, name)`. Saving under an existing name updates it. Two rules carry the feature: 1. **Only those six params are ever stored.** The serializer rebuilds the string from an allow-list rather than trusting a hidden field, and the reader re-canonicalises again on load. A crafted post cannot persist `redirect=` or a `userId` under your account and get it rendered back as a link you are invited to click. 2. **Canonical ordering.** Multi-values are de-duplicated and sorted, so the same view saved from two different orderings of the provider multi-select is one string. That is what makes "this is the view on screen now" answerable. The limit is 20. It is a cap, not a queue: nothing is silently evicted, because you cannot see an eviction happen. The save form sits **outside** the GET filter form. A `
` inside a `` is invalid HTML; browsers resolve it by dropping one of the two, which disables either the filters or the save button depending on the browser. Saved views do not store session ids, titles, or transcripts. Parsing remains in the browser. See [privacy and sharing](/docs/privacy-sharing) for what an account export contains. ## Suggested uses - **This week versus last week, same providers.** Seed A from the dashboard, set B to the previous equivalent window, and expect several token/cost rows to refuse if Claude or Gemini web is in the mix. - **Codex-only cost.** Filter both sides to Codex before comparing cost. That is one of the few token comparisons the formats can actually support. - **A project you keep returning to.** Save the project id plus all-time, then reopen it from the saved-views section instead of hunting through the filter bar. Step-by-step click path: [Compare two slices of your AI work](/tutorials/compare-two-dashboard-views). The Novus hub at [novusstreamsolutions.com](https://novusstreamsolutions.com) lists the other apps if you came here from the catalog; AI Stats comparison does not deep-link into them, because they do not share this metric model. --- ### Metrics, provenance, and quality URL: https://aistats.novusstreamsolutions.com/docs/metrics-quality Category: Metrics Updated: 2026-08-07 ## Time metrics Session span is elapsed time between the first and last supported event and can include long idle gaps. AI active time is source-reported or adapter-derived working time: for current Codex rollouts it is the sum of completed-turn durations without separately adding nested tool spans, and for Claude web exports that spell out assistant content blocks it is the union of those blocks' start and stop instants, so blocks that overlap are counted once rather than twice. User active time and waiting time are permanently unavailable, and that is a decision rather than a gap. No supported export records how long you spent reading or typing, and waiting on the model is exactly the AI active time already shown. Producing either number would mean relabelling one you can already see, so the tiles were removed and the coverage rows say why. ## Counts and tokens Prompts, responses, tools, and agents depend on source capability. Token categories remain separate. Missing token telemetry is unavailable, not zero. Every metric, for every provider, with the reason for each gap, is listed on the [integrations page](/integrations). It distinguishes a value your export does not contain (which no future release can recover) from one your export contains that we do not read yet. ## Cost estimates API-equivalent cost is not a subscription bill. It is shown only when model identity, token categories, a dated rate table, and a valid calculation are available. The current text-token snapshot was reviewed on 2026-07-29 against the official OpenAI model catalog, Claude Platform pricing, and Gemini Developer API pricing. It uses standard pay-as-you-go rates. It excludes taxes, negotiated discounts, tools, data-residency multipliers, long-context premiums, and cache storage time. If more than one differently priced model appears in a session, the estimate remains unavailable because token attribution would be ambiguous. - [OpenAI model and pricing catalog](https://developers.openai.com/api/docs/models) - [Claude Platform pricing](https://platform.claude.com/docs/en/about-claude/pricing) - [Gemini Developer API pricing](https://ai.google.dev/gemini-api/docs/pricing) ## Period comparisons and the "What changed" summary A metric is compared with the previous period only when both periods were fully covered and every contributing session agreed on adapter version, normalization version, quality, source, and calculation. Anything less and the comparison line reads _No comparable prior-period value_ rather than reporting a movement that is really a change in how much of your work the export described. The **What changed** panel at the top of the dashboard is built from exactly those comparisons and nothing else. It ranks them by size, narrates the three largest, and states how many metrics it held out. Two further rules keep it from overstating: - A movement of less than 5 percent is reported as _held steady_ rather than narrated. - A metric whose previous period held nothing is described in words with no percentage attached, and is never ranked above a metric that has a real rate. Growth from zero is not a percentage. An empty summary means no honest comparison was available, not that nothing happened. ## Method on every dashboard section Every section of the dashboard carries a disclosure stating what it measures, how it is calculated, how to read a high or low value, and what could mislead you about that section specifically. The wording lives in one registry, and the test suite fails if a section is rendered without an entry or an entry is written without a section, so the method cannot quietly fall out of a redesign. Each chart also publishes the series it was drawn from as a table, built from the same array the chart received. Days and hours that no source described read _Not available_ there rather than zero. ## Provenance Open a session to see adapter version, normalization version, source, last import, metric quality, calculation provenance, warnings, and limitations. --- ### Privacy, exports, deletion, and sharing URL: https://aistats.novusstreamsolutions.com/docs/privacy-sharing Category: Privacy Updated: 2026-07-29 ## Import privacy Files are parsed transiently in a browser worker. Raw prompts, responses, attachments, code, files, and local paths are discarded when parsing finishes. There is no IndexedDB transcript cache or persisted folder permission. ## Account data Download a complete streamed JSON export or session CSV from Settings. Account deletion requires your current password and explicit confirmation, then removes the account and imported records. ## Shares Every share is an immutable versioned snapshot with a field preview. Unlisted and noindex are defaults. Public indexing is a separate opt-in. Shares support expiry, revocation, atomic view counts, copy, Web Share, and a downloadable image. Revoked or expired links return 404. --- ### Import the seven supported providers URL: https://aistats.novusstreamsolutions.com/docs/provider-imports Category: Providers Updated: 2026-08-20 ## ChatGPT, Claude, and Gemini - ChatGPT accepts the official account export ZIP or conversations.json. Only the branch ending at the conversation's current node is counted, so regenerations and edits are not counted twice. - Claude accepts the official account export ZIP or supported JSON. Current exports spell out every assistant content block, which is where tool calls and AI active time come from; an older export carrying message text only leaves both unavailable rather than reporting zero. - Gemini accepts a Google Takeout ZIP containing supported Gemini Apps or My Activity records. Only `Prompted` activity rows count as prompts. Canvas creations and "Used Gemini Apps" rows are side effects of one. Gemini's answers are recoverable from the `MyActivity.html` form and are absent from the JSON activity form. Official ZIPs may contain images, audio, and other attachments. The importer skips those entries before decompression and reads only supported statistics files. Browser safety ceilings allow up to 10,000 selected files, 1 GiB of selected input, and 2 GiB of supported expanded data; these are archive and memory safeguards, not account quotas. ## Coding tools - Claude Code accepts supported JSONL sessions and telemetry. - Codex CLI and Desktop accept supported rollout JSONL and preserve the product surface as a statistic. - Gemini CLI accepts local OpenTelemetry JSONL. Disable prompt logging before collecting telemetry. - Cursor accepts exported Markdown and user-selected SQLite snapshots with recognized schema signatures. A Markdown transcript labels its tool and command turns, so an absence there is a real zero; a workspace snapshot reports tool calls only when its rows spell each turn out as content parts, and leaves the count unavailable when they do not. Select the transcript files these tools write as they run (one JSON object per line), or a ZIP of them. A summary, report, or spreadsheet describing that usage is rejected even when its numbers are correct: every statistic here is derived from the underlying events, so totals computed elsewhere have no events to verify and are never adopted as if they had been. Current Codex rollouts contribute embedded session identity, completed-turn duration, cumulative token usage, tool calls, launched sub-agents, models, and anonymous repository grouping when those fields are present. Repeated snapshot files for one embedded session are collapsed to the newest complete snapshot. ## Fail-closed behaviour Choose the provider explicitly. Shape detection is only assistance. Unknown Cursor databases, missing timestamps, corrupt records, nested archives, and unsupported schemas are rejected or reported as partial warnings. They never become synthetic sessions. --- ### Install, update, and use the PWA safely URL: https://aistats.novusstreamsolutions.com/docs/pwa-offline Category: PWA Updated: 2026-07-29 ## Install Use the in-app install prompt when your browser exposes it, or use the browser install menu. AI Stats includes 192, 512, and maskable icons plus desktop and mobile screenshots. ## Updates When a new service worker is waiting, the app shows an update control. Reloading activates the new version. Old public cache versions are removed during activation. ## Offline privacy Only a small public shell and offline page are cached. Authentication, APIs, shares, and account data are network-only. An offline navigation to the authenticated app returns the privacy-safe offline page rather than a cached dashboard. --- ### Getting started URL: https://aistats.novusstreamsolutions.com/docs/getting-started Category: Setup Updated: 2026-07-31 ## Create your account Whether sign-up is open is the owner's decision, and the sign-up page always states the current answer. When it is closed, email aistats@novusstreamsolutions.com to ask for access. AI Stats does not send email. Your address is the name of your account and nothing else: it is never verified, you will never receive a confirmation message, and there is no forgot-password link or reset email. See [account security and manual recovery](/docs/account-security-recovery). Use an address you will keep and can still read, because it is the only thing that identifies the account as yours. If you lose your password, email aistats@novusstreamsolutions.com from that address and the owner restores access by hand. When sign-up is open, every new account is a member account: accept the current Terms and Privacy Policy, choose a password of at least 12 characters that you do not use anywhere else and that is not built from your email address, then sign in. ## Choose privacy Stats-only is the default. It sends normalized counts, times, tokens when available, source quality, and anonymous source identity. Titles are optional. Prompts, responses, files, attachments, and local paths are never uploaded. ## Import and verify Open Imports, choose the source explicitly, select files or a folder, and inspect the local preview. Confirm the detected schema, supported and rejected files, warnings, duplicates, updates, metric coverage, and exact cloud fields before creating the import. Importing is behind a release flag and is enabled provider by provider; when it is off, the import workbench is disabled rather than hidden. [Export and import your first source](/tutorials/export-and-import-your-first-source) covers the whole path. ## Read the dashboard The default range is This week in your configured timezone. Unavailable values show as unavailable rather than zero. Comparisons use the immediately preceding equivalent period. --- ### Troubleshoot imports and duplicates URL: https://aistats.novusstreamsolutions.com/docs/troubleshooting-imports Category: Troubleshooting Updated: 2026-07-29 ## Export not detected Confirm the source explicitly and use an official export or documented local telemetry format. Filename detection is never authoritative. Review rejected files and schema warnings in the preview. ## Partial or corrupt data Corrupt JSONL lines, missing timestamps, unsupported records, oversized lines, nested archives, and unknown SQLite signatures are rejected. Valid sibling records may remain available as a partial preview with exact error counts. ## Duplicates and updates Stable provider session identity updates an existing session. Identical normalized hashes skip. Changed content for an existing identity updates the session and reconciles aggregates. The ZIP or container checksum is not session identity. ## Retry or cancellation Batches use idempotency keys, so retrying the same accepted batch is safe. Completion succeeds only after every expected batch is present. Cancelling an incomplete job deletes staging records and leaves live statistics unchanged. ## Tutorials ### Export your data from a provider and import it URL: https://aistats.novusstreamsolutions.com/tutorials/export-and-import-your-first-source Category: Import Step: 1 of 5 Updated: 2026-07-31 This tutorial takes you from an empty account to a first set of statistics. It assumes nothing except that you use at least one AI product that can export its history. Two things are true before you begin, and it is better to know them now than halfway through. - Public registration opens only when verified email delivery is configured. If the sign-up page tells you registration is paused, that is the live state of this instance, not an error. - Importing sits behind a server release flag and is switched on source by source. If the import workbench says _Imports are disabled by the server release flag_, everything below still describes what will happen, but the final step will not run yet. ## Step 1: Request the export from your provider Every supported chat product has an official export path in its own account settings, usually under a data or privacy section. Request it there rather than copying conversations by hand. An official archive carries the timestamps and identifiers that make statistics possible. Exports are not instant. Most providers email a download link, and the link expires. Download the archive, keep it as a `.zip`, and do not unpack and re-zip it. Coding agents are different: they already write their history to your own disk. Claude Code and Codex keep local session files so you can resume work, and Gemini CLI can be configured to write OpenTelemetry output to a local file. For those, you select the files directly. See the [provider import reference](/docs/provider-imports) for the exact shapes each adapter accepts. ## Step 2: Choose the source explicitly Open **Imports**, then **New import**, and pick the provider yourself. This is deliberate. The parser can guess a format from a file's shape, but the guess is only there to help you: the source you choose is the one that is used, and a file that does not match it is rejected rather than reinterpreted. Filenames are ignored entirely; a Cursor database is recognised by its file header, not its extension. ## Step 3: Choose a privacy mode Stats-only is the default, and it is the right default. In stats-only mode the server receives counts, timestamps, durations, token categories, model names, quality labels, and anonymous identity. It receives no session titles at all. The alternative mode adds titles, nothing else. Prompts, responses, code, attachments, and local file paths are not part of either mode; they are never uploaded, in any mode. You can change the mode before the preview runs. Changing it re-parses the files. ## Step 4: Select files or a folder Use **Choose files** for an export archive, or **Choose folder** when a coding agent has written many session files into a directory. Only `.json`, `.jsonl`, `.md`, `.markdown`, `.sqlite`, `.db`, and `.vscdb` are considered; anything else in the folder is skipped before it is read. A ZIP is expanded in your browser, and a ZIP inside a ZIP is rejected rather than unpacked. There are hard ceilings: 1,000 selected files, 512 MB selected, 256 MB for any single file. If you exceed one of them the import stops immediately instead of failing later. ## Step 5: Read the preview before you upload Nothing has been sent yet. The preview is generated entirely in your browser, and it is the most important screen in the product. Check four things: 1. **Detected schema.** It should name the format you expected, such as a ChatGPT conversations export or a Cursor SQLite snapshot. 2. **Accepted and rejected files.** A handful of rejects in a large export is normal; a total reject count equal to your file count means you picked the wrong source. 3. **New, updated, and duplicate estimates.** Re-importing an overlapping export is safe, and this is where you see how much overlap there is. 4. **The cloud-fields list.** It states exactly which categories of data will be sent. ## Step 6: Create the import Confirm, and the normalized sessions are sent in batches of one hundred, each with its own idempotency key. Retrying a batch that already arrived is safe by design and does not duplicate anything. The server re-derives the identity and content hashes for every session before storing it. If a record does not match its own hashes, the batch is refused. The import completes only after every expected batch has arrived. ## If something is rejected Rejections are specific on purpose, and none of them create partial or invented statistics. - An unknown schema means the export version is not one this adapter recognises. Nothing is imported. - Missing timestamps in a session cause that session to be rejected rather than given a made-up start time. - An unrecognised Cursor database signature is refused, because guessing at an unknown Cursor version is how wrong numbers get created. - If no supported session is found in the whole selection, the import fails with the first rejection reason and nothing is created. [Troubleshoot imports and duplicates](/docs/troubleshooting-imports) covers each case in more detail. ## What to do next Once the import completes, go to the dashboard and read the metric coverage panel before you read anything else. It tells you which of your numbers are complete. [Read your dashboard without fooling yourself](/tutorials/read-your-dashboard) is the next tutorial in this path. --- ### Read your dashboard without fooling yourself URL: https://aistats.novusstreamsolutions.com/tutorials/read-your-dashboard Category: Analyse Step: 2 of 5 Updated: 2026-08-08 Your first dashboard will show large numbers. Some of them will be complete, some will describe a third of your work, and the difference is not visible unless you look for it. This tutorial is the order in which to read the screen. ## Start with What changed The first panel on the dashboard is **What changed**, and it is the only part of the page that has already done the ranking for you. It compares each headline metric with its own value in the window of the same length immediately before, and narrates the largest movements in sentences that name both the old value and the new one. It is deliberately quiet. A metric appears there only when both periods were fully covered by sources that recorded it the same way: same adapter version, same normalization version, same quality, same calculation. If you re-imported a provider halfway through the current window, that metric is held out rather than reported as growth, and the panel says how many metrics it held out and why. Two more rules worth knowing, because they are the two ways this kind of summary usually lies: - A move of less than 5 percent is listed as _held steady_ instead of being narrated. A summary that opens with "prompts rose 0.4%" trains you to ignore it. - A metric that had nothing in the previous window is reported in words, with no percentage. Growth from zero is not a percentage, and ranking on one would put a single new agent launch above a doubling of your session count. An empty **What changed** means the comparison could not be made honestly. It does not mean nothing happened. ## Step 1: Set the range before you read anything The default range is a rolling 30 days, in your configured timezone. Day boundaries follow your configured first day of the week where the range is a calendar one. Eight ranges are available: today, this week, rolling 7, 30, and 90 days, year to date, all time, and a custom range. Day boundaries are local midnights, not server midnights, so a late-night session lands on the day you think it did. The range and every filter live in the URL. A view you can read is a view you can bookmark or send to yourself. If the range excludes everything you have, the dashboard says so instead of telling you to import. It names how many sessions are on your account and offers a one-click switch to all time, and if a provider, project, or model filter is also narrowing the view it offers to clear those too, without guessing which of the two is responsible. This is the expected first screen straight after an import, because an export is a record of the past rather than a live feed, so several months of history can land entirely outside a short window. ## Step 2: Read the metric coverage panel first Scroll to **Metric coverage**. It reports, for each metric, how many of the sessions currently in view actually carried it, as a fraction and a percentage. Read it before the tiles. A tool-call total with 30 percent coverage is a true statement about 30 percent of your sessions, and a true statement about the wrong denominator is how people mislead themselves with their own data. ## Step 3: Treat _Not available_ as information A tile that reads **Not available** is not a zero and not a bug. It means no session in this view reported that value. Every metric carries one of five labels, and the line under each tile tells you which kind of number you are looking at. | Label | Meaning | | --------------- | ---------------------------------------------- | | Exact | Explicitly defined by the source | | Source reported | Supplied by the provider, using its definition | | Derived | Calculated from source timestamps | | Estimated | Produced by a documented heuristic | | Unavailable | Not present in the source | Nothing is ever backfilled with a zero to make a chart look complete. ## Step 4: Use the comparison line, and know when it is absent Under each tile is a comparison with the window of exactly the same length immediately before the current one. Rolling 30 days is compared with the 30 days before it. All time has no previous window, so it says _No comparable prior-period value_ rather than inventing a baseline. Treat that as the honest answer it is. ## Step 5: Filter before you conclude Providers, projects, and a model filter narrow the same view. Hold Ctrl or Command to select more than one provider or project. Two filtering habits are worth building. First, compare one provider against itself over time rather than against another provider. Different sources expose different fields, so cross-provider gaps are often format differences rather than behaviour differences. Second, when a total looks wrong, filter down until it looks right; the filter that fixes it names the cause. ## Step 6: Open a session when you doubt a number Any single session page shows its adapter version, normalization version, source, last import, metric quality, the calculation used, warnings, and limitations. This is the escape hatch. If a total surprises you, one session will usually tell you whether the source under-reports, the adapter derived rather than read the value, or you are simply busier than you thought. ## Ask any section how it is measured Every section of the dashboard (the filters, the import status panel, the tiles, both charts, the heatmap, and every list below them) carries a collapsed disclosure under its heading. Opening it answers four fixed questions about that section and nothing else: 1. **What this measures.** The quantity, stated without hedging. 2. **How it is calculated.** Which sessions are included, which are excluded, and what happens to the ones with no value. 3. **What high and low mean.** How to read a big number or a small one. 4. **What could mislead you.** The specific way this section, and not some other section, can be read wrong. The fourth is the one to read. Every section on the page can mislead you in a way particular to it: the timeline counts session _starts_, so a single long day of work is a low point; the heatmap flattens a 90-day window into one average week; the provider split shows where your sessions live and says nothing about where your time went. Each disclosure ends with a link to the full method, so nothing depends on remembering what the summary said. ## Read any chart as a table Under each chart is **Show the data as a table**. It opens the exact series the chart was drawn from (the same numbers, not a re-computation), with a row per day, per length band, or per weekday. Use it when you want to copy a figure, when a point is too small to hover, or when you want to check what a suspicious peak actually is. A day whose sessions came from an export with no prompt counts reads _Not available_ in the table, never `0`, which is usually the fastest way to tell a quiet day apart from an unreported one. If you use a screen reader, the table is the chart. The graphic itself is announced as a single image that points at the table below it, rather than reciting a hundred data points into one unnavigable label. ## Reading the cost tile API-equivalent cost has the strictest rules on the page, and it is allowed to show nothing. It is calculated from a dated rate table and refuses when token counts are missing, when a model is not in the table, or when a single session used two models with different prices, because token attribution between them would be ambiguous. It is an API-equivalent estimate of what the same tokens would have cost on a pay-as-you-go API. It is not your subscription bill. [Metrics, provenance, and quality](/docs/metrics-quality) lists what the estimate excludes. ## What not to conclude Session counts, prompt counts, and hours describe activity, not value. They cannot tell you whether the work was good, and they compare tools and export formats as much as they compare people. They also describe only what you imported. A provider you have not imported is absent, not zero. When you are ready to publish a number, [share a snapshot safely](/tutorials/share-a-snapshot-safely) shows how to do it without leaking the rest of your account. --- ### Share a snapshot without leaking anything URL: https://aistats.novusstreamsolutions.com/tutorials/share-a-snapshot-safely Category: Share Step: 3 of 5 Updated: 2026-07-31 A share link is the one part of a private analytics account that other people see. It deserves more care than a screenshot and it should behave less like a login than a poster. The model here is a snapshot: a frozen copy of the numbers you selected at the moment you selected them. A share is never a live window into your account, so nothing you import later appears on a link you already sent. ## Step 1: Choose the range you want to publish Open **Shares**. The snapshot is built from a dashboard range, so choose the period you actually want to talk about: a week, a month, or all time. If the selected range contains no sessions, there is nothing to publish and the app sends you back to importing instead of creating an empty card. ## Step 2: Choose the exact fields Only a fixed list of totals can be shared at all: sessions, prompts, responses, tool calls, agents, providers used, active projects, the four duration fields, input and output tokens, and API-equivalent cost. You tick the ones you want. Nothing outside that list is shareable, which means there is no configuration mistake that can put a message, a title, or an internal record ID onto a public page. Four optional blocks can be added separately: provider split, model ranking, project ranking, and metric quality coverage. Including quality coverage is a good habit. A number is easier to trust when the card also says how complete it was. ## Step 3: Decide visibility and indexing There are two independent switches, and they are deliberately not one. - **Visibility** is unlisted or public. Unlisted is the default: the link works for anyone who has it, and nothing points at it. - **Indexing** is a separate opt-in, and it is available only for public shares. Leave it off and the page carries a noindex directive. The link itself is not guessable. Each share gets a slug generated from 24 random bytes, which is why an unlisted link is meaningfully unlisted rather than merely unlinked. ## Step 4: Set an expiry Choose 7, 30, or 90 days, or no expiry. Pick an expiry whenever the number is tied to a moment: a year in review, a launch week, a conference talk. An expired link returns a 404, exactly like a revoked one, rather than a page explaining what used to be there. ## Step 5: Check the page you are about to send Creating the snapshot opens it. Read it as a stranger would. The page shows the title, the range and timezone the numbers cover, the fields you chose, and the generation time. If a project name on the card is more revealing than you intended, that is the moment to notice. Project rankings are published with their internal identifiers stripped, but the names are the ones you chose yourself. Every share also has a downloadable image, which is usually the safer thing to post publicly: it carries the same numbers without the link. ## Step 6: Revoke anything you regret The shares list shows each snapshot with its visibility, whether it is active, revoked, or expired, whether it is indexable, and its view count. **Revoke** is immediate and permanent for that link. The URL returns 404 afterwards. Since a snapshot is immutable, revoking is the correction mechanism: you do not edit a share, you revoke it and create a new one. ## What a snapshot never contains Every snapshot records its own privacy stance alongside the numbers, and three flags are always false: no raw content, no session titles, no internal identifiers. That is the whole guarantee, and it is narrow on purpose. A share contains totals you selected, from a range you selected, at a moment that is written on the card. Everything else stays in your account. [Privacy, exports, deletion, and sharing](/docs/privacy-sharing) documents the same rules from the account side. --- ### Group sessions into projects you actually recognise URL: https://aistats.novusstreamsolutions.com/tutorials/group-sessions-into-projects Category: Analyse Step: 4 of 5 Updated: 2026-07-31 Provider totals are entertaining. Project totals are the ones you act on. The obstacle is that your sources do not agree on what a project is. A coding agent knows a working directory. A chat export knows nothing at all. Turning that into one grouping is a decision, and this product treats it as yours to make. ## Step 1: Understand what was imported When a coding-agent session records a working directory, the path itself is never uploaded. It is hashed in your browser, and only the hash reaches the server. That means the app can tell that eleven sessions happened in the same place without knowing where that place is. It cannot name your project, and it does not try. ## Step 2: Confirm a repository suggestion Open **Projects**. Unassigned coding sessions that share a repository hash appear as a suggestion, with the number of sessions, the providers involved, and the date of the most recent one. Each suggestion offers two actions: assign it to an existing project, or create a project and assign it in one step. Nothing happens until you choose. Projects are never created silently. The session count and provider list are usually enough to recognise which repository a hash belongs to. If they are not, open one of the sessions. ## Step 3: Add the work that has no path A real project is rarely only code. The planning conversation, the research, and the review often live in chat products that export no directory at all. Assign those sessions to the project by hand from the sessions list. This is the step that makes project totals worth reading, because it is the only way the chat half of the work joins the coding half. ## Step 4: Merge the duplicates You will end up with two projects for one piece of work: one created from a repository suggestion, one created by hand, or one per machine. Merge them rather than deleting one. Merging keeps the sessions and consolidates them under a single name, so historical totals stay correct. ## Step 5: Read project analytics with the same rules as the dashboard A project view obeys everything the main dashboard does: the same ranges, the same timezone-aware boundaries, the same previous-period comparison, and the same distinction between unavailable and zero. One rule is specific to projects. Only confirmed assignments appear in project analytics. A repository suggestion you have not approved is not quietly counted, so a project total is always a total over sessions you personally attributed. If a project looks smaller than the work felt, the usual cause is unassigned chat sessions rather than a measurement error. ## Step 6: Keep the names publishable Project names are the one place in your account where you choose free text that can later appear on a shared snapshot, because project rankings can be included in a share. Name projects the way you would name them in public. A codename is a perfectly good project name; a client's legal name is a decision you should make deliberately rather than discover later. ## Archive finished work; merge mistakes away Archiving hides a project from active lists and filters while keeping every session and total intact. It is reversible, and it is the right move for work that is simply finished. There is no separate delete button, and that is intentional. A project disappears only as the result of a merge, which moves its sessions and milestones to the surviving project first. So a wrong grouping is corrected by merging it into the right one, never by discarding history. Every later total inherits that decision, so it is worth making it deliberately. Next, see [read your dashboard without fooling yourself](/tutorials/read-your-dashboard) for how the project filter interacts with metric coverage. --- ### Compare two slices of your AI work URL: https://aistats.novusstreamsolutions.com/tutorials/compare-two-dashboard-views Category: Analyse Step: 5 of 5 Updated: 2026-08-09 This walkthrough assumes you have already [exported and imported at least one source](/tutorials/export-and-import-your-first-source) and can [read the dashboard without fooling yourself](/tutorials/read-your-dashboard). Comparison does not create new metrics. It asks whether two filtered slices may be subtracted. If your imports are ChatGPT, Claude web, or Gemini Takeout, expect token and cost rows to stay Unavailable. That is correct. See [unavailable is not zero](/blog/unavailable-is-not-zero). ## Step 1: Set the slice you care about on the dashboard Open [/app/dashboard](/app/dashboard). Pick the range first. Add provider, project, or model filters only after the range is right. Otherwise you will compare two accidents. Read **Metric coverage** before you press Compare. If a headline tile is already partly covered, comparison will not magically complete it. ## Step 2: Open Compare without cloning the view onto both sides Press **Compare**. Side A should match the dashboard you just left. Side B should still be on its defaults (typically this week, all providers, no project or model filter). Do not "make B match A to check the feature." A comparison of a view with itself prints zeroes and looks broken. The product leaves B independent so the first screen you see is a real contrast. All twelve parameters are in the URL. Copy it if you want to return to this pair later, or save each side as its own view after you like the filters. ## Step 3: Narrow side B on purpose Typical honest pairs: 1. **Same providers, previous window.** Set B's range to the equivalent period before A. This is the closest relative of the on-dashboard "What changed" narrative, with more room to inspect refused rows. 2. **Codex versus everyone else.** If you actually have Codex sessions, put Codex alone on B. Token and cost comparisons become possible on that side; they stay refused on Claude or Gemini web. 3. **One project versus the account.** Put a confirmed project on B and leave A unfiltered. Composition will show which providers and models exist only in that project. If a filter would empty a side, leave the row. The page will say the side contains no sessions rather than inventing a baseline. ## Step 4: Read refused rows before the deltas Scroll the metrics table slowly. A numeric delta is the exception. The useful half on a mixed account is usually: - "The Claude export format does not contain it." - "View A reports this for 1 of 4 sessions … the missing sessions are missing by export format, not at random." - "The two sides recorded this metric differently." Those sentences are the [capability matrix](/blog/capability-matrix-what-exports-can-tell-you) and [/methodology](/methodology) applied to a pair of slices. They are not error states. If you only wanted the rows that survived, you would not know what you had thrown away. Composition groups underneath the metrics. A provider or model present on one side only reads `Not in this view` on the other. That mismatch is often why a total moved. ## Step 5: Save the slice you will reopen Return to [/app/dashboard](/app/dashboard) (or stay on the side whose filters you want). Name the view in the saved-views section. Use a name you will recognise in a month: "Codex all-time" beats "View 3". Saving under an existing name updates it. You can keep at most 20. Only the six filter params are stored; session titles never are. Details and the non-nested form rule are in [Compare views and save the ones you reuse](/docs/compare-and-saved-views). ## Step 6: Know what this walkthrough will not do It will not email you when a metric crosses a threshold. Watchlists and alerts are not built. It will not add a planned provider. It will not fill Claude, Gemini, or ChatGPT web tokens. If you came from the [Novus hub](https://novusstreamsolutions.com) looking for study tools rather than measurement, [Novus Learn](https://learn.novusstreamsolutions.com) is the sibling that turns source material into cited notes. When you are done comparing, go back to [reading the dashboard](/tutorials/read-your-dashboard) and believe coverage first. Comparison is a scalpel for two slices you already trust separately. ## Blog posts ### AI Usage Constellations Without Exposing Session Names URL: https://aistats.novusstreamsolutions.com/blog/ai-usage-constellations-without-exposing-names Category: Privacy Published: 2026-08-20 | Updated: 2026-08-20 A relationship graph is useful because proximity exposes structure. Sessions cluster around projects, provider colors reveal a changing tool mix, and repository lineage connects work that belongs to the same anonymous workspace. The same graph can become a privacy failure if its most prominent labels are conversation titles or local directory paths. The AI Stats constellation is built around a stricter rule: **numbers are visible; names are gated**. The interactive view receives normalized session measurements. It receives a conversation title only when that session was imported with titles, and it never receives a raw workspace path. Under the default stats-only mode, a node is labelled with a generic provider name such as “codex session.” Open the live surface at [/app/constellation](/app/constellation) after importing your own exports. The graph shows the 300 most recent active sessions in detail. If your history is larger, older sessions are summarized separately by provider so the interactive graph remains responsive; the statistics above the graph describe only the sessions currently in the graph. ## What a node represents Each node is one normalized AI session, not one prompt and not one file. Its available details can include provider and product, models, start and end time, wall time, AI-active time, prompts, responses, tool calls, agents, tokens, API-equivalent cost, project assignment, and repository lineage. The word **available** matters. A ChatGPT export that contains no token counts produces an Unavailable token value. It does not produce zero. A Gemini CLI snapshot that names no model cannot appear in the model distribution under an invented label. Every aggregate above the graph includes its own coverage line so a total over ten reporting sessions cannot masquerade as a total over all 300. Node colors identify providers. Edges connect adjacent sessions that share the same anonymous repository hash. They do not claim that one conversation caused another, that two prompts share meaning, or that the application read their text. The line is lineage, not semantic similarity. ## Three layouts answer three different questions The constellation can arrange the same filtered sessions in three ways: - **Projects** groups confirmed project assignments and leaves unassigned sessions separate. - **Providers** makes tool mix visible by placing sessions from the same source together. - **Timeline** orders the activity so changes in provider use can be read across time. Layout changes position, not membership. Search and the provider, model, project, and month controls decide which sessions are in the selection. The summary panel is recomputed from that filtered selection, so the graph cannot show twenty nodes while the headline statistics quietly describe three hundred. Monthly replay is a filter over the session start month. It is not an animation of prompt content and not a claim about when an export was imported. As elsewhere in AI Stats, dates refer to recorded session activity. ## The name gate runs before the graph AI Stats supports two cloud-content modes at import time. **Stats only** is the default. Parsing happens in the browser. Raw prompts, responses, code, attachments, source files, and local file paths are not uploaded. Conversation titles are also omitted. The normalized session can carry counts, timestamps, durations, token categories, model names, quality labels, and anonymous identifiers where the source provides them. **Stats with titles** adds conversation titles to that normalized payload. It does not add prompts or transcripts. Choosing it is explicit because a title can reveal a client, health concern, product codename, or private question even when the messages remain absent. The constellation’s display-name function applies one order everywhere: 1. A private alias you deliberately assigned to the session. 2. The imported title, only when that session’s mode is `stats_with_titles`. 3. A generic provider label. There is no fallback that reaches into another field for something more descriptive. Under stats-only mode, even a non-null title left by old or malformed data is not approved for display. The gate is enforced when the server maps a database row into the client graph payload, before any node renderer or search box can read it. ## Workspace grouping without workspace paths Coding-agent exports often identify a repository or working directory. That structure is valuable: it lets sessions from the same body of work cluster together. The original path is not valuable enough to justify storing it. The browser therefore converts the workspace path into a one-way repository hash before normalized data is stored. Automatic project labels use anonymous forms such as `Workspace 3f9ac1d2`; they are not truncated directory names. A project name can also be one you typed yourself. The details panel distinguishes a user-named project from an automatic hash-derived cluster instead of implying both came from the source file. Repository edges use the hash to connect adjacent sessions. The graph can answer “these sessions came from the same anonymous workspace” without knowing whether the original path was a client repository, a personal folder, or a machine username. Chat providers commonly expose no workspace at all. Their sessions remain unassigned unless you curate them into a project. Absence of a project edge is therefore not evidence that the work was unrelated; it may simply reflect what the source format records. ## Statistics belong to the selection on screen The summary panel reports sessions, AI-active time, wall time, prompts, tool calls, tokens, API-equivalent cost, and days with activity for the current selection. It also shows provider share, the most common reported models, busiest weekdays, the first and last session dates, active days, and how many nodes use a generic label. Every total states coverage. This is especially important in a spatial view, where a dense cluster can make a partial number feel representative. Token totals may describe only coding-agent sessions. Model rankings omit sessions whose export has no model field. API-equivalent cost can remain unavailable when tokens or a published model rate are missing. Selecting one node opens the same provenance-minded details: values the export reported, derivations the adapter made, and Unavailable where the source is silent. The graph does not gain permission to fill gaps simply because a visual looks better with complete tooltips. ## Search without turning titles into a side channel Search matches only fields already present in the graph payload: the approved display label, provider, reported model names, and project label. In stats-only mode the display label is generic, so typing a private conversation phrase cannot reveal whether that phrase existed in an omitted title. This rule also constrains filtering. A model selector can list only models present in exports. A project selector can list confirmed assignments and anonymous automatic groups. A provider selector uses the known adapter id. Filters narrow the normalized facts; they do not query raw imports. ## The accessible view carries the same privacy boundary The canvas graph is not the only way to inspect the constellation. An ordinary table exposes the same detailed session set as links, including provider, reported models, project, start time, AI-active time, prompts, tools, and tokens. Summary statistics remain text outside the graph. That fallback is important for keyboard and screen-reader access, but it is also a privacy contract. Accessibility code does not receive a richer record than the visual graph. Both are built from the same gated mapper, so adding a table cannot accidentally reintroduce titles that stats-only mode excluded. ## What the constellation cannot tell you The constellation describes recorded activity and source structure. It cannot establish the quality of a session, whether a tool was worth using, or whether two linked sessions discuss the same subject. Provider clusters partly reflect export capabilities. Project clusters partly reflect what coding tools record and what you curated afterward. Treat the view as an index into provenance, not an automated story about your work. Use it to find a surprising cluster, narrow the selection, inspect coverage, and open individual sessions. If a metric matters enough to quote, verify its source and calculation on the session page or in [/methodology](/methodology). For the full import boundary, read [Privacy and sharing](/docs/privacy-sharing). For a guided first import using the default content mode, continue with [Export and import your first source](/tutorials/export-and-import-your-first-source). The goal is not to make private history visually anonymous after uploading it; it is to prevent the unapproved names and raw content from entering the payload in the first place. --- ### How to Compare AI Usage Without Misleading Deltas URL: https://aistats.novusstreamsolutions.com/blog/compare-ai-usage-without-misleading-deltas Category: Metrics Published: 2026-08-20 | Updated: 2026-08-20 A delta looks precise because it has a sign. `+18%` feels more decisive than two totals shown side by side, even when the totals came from different providers, different export versions, or different amounts of missing data. That confidence is exactly why comparison needs stricter rules than a dashboard total. AI Stats comparison mode treats a comparison as two independently defined **slices** of imported history. It will subtract a metric only when the two slices make the subtraction defensible. When they do not, the row remains in the report and explains why it is not comparable. The blank is not a broken calculation. It is the calculation refusing to overstate what the exports know. This guide explains the live workflow on [/app/dashboard/compare](/app/dashboard/compare). For the field-by-field contract, keep [Compare views and save the ones you reuse](/docs/compare-and-saved-views) open beside it. ## Start by defining two real slices A slice is the question you asked of the imported sessions. Each side has six filter fields: range, custom start, custom end, providers, projects, and a model substring. Side A and side B therefore produce twelve URL parameters, prefixed with `a` and `b`. That URL is the complete comparison definition; it contains no prompt, transcript, session title, or imported file. The safest entry point is the **Compare** link on the dashboard. It copies the view already on screen into side A. Side B remains independent so you can deliberately choose the contrast. Copying A into both sides would produce a page full of zero differences and teach you nothing. Useful questions are narrow enough to name: - This week versus the previous equivalent week, with the same provider filter. - Codex sessions in one project versus Codex sessions in another project. - One model substring before and after a workflow change, where the export actually names models. - The same saved filter view reopened after a later import, when you want a current answer rather than a historical snapshot. Avoid starting with “everything versus everything.” Consumer chat exports and coding-agent logs do not report the same fields. A mixed-provider comparison can still reveal composition, but many token, cost, tool, and duration rows should refuse a numeric delta. ## A recent import is not a recent session Date ranges use when sessions started, not when you imported their files. Uploading a six-month export today does not move six months of sessions into the rolling 30-day window. This distinction matters whenever all-time looks complete and a short range looks unexpectedly small. Before comparing, open each side as an ordinary dashboard view and confirm that its range contains the sessions you intended. “Rolling 30 days” means the local calendar window ending now. “All time” includes the imported history regardless of when the import completed. A custom range is appropriate when your work period does not match a rolling or calendar preset. If the dates are wrong, a mathematically correct delta answers the wrong question. ## Read coverage before magnitude Every metric has a coverage fraction: how many sessions in the slice actually reported that field. A tool-call total over 30 of 100 sessions may be a correct total for those 30 sessions. It is not evidence that the other 70 sessions made no tool calls. Missingness is usually structural. ChatGPT web exports do not suddenly begin reporting token counts because they sit beside Codex sessions that do. Claude web can expose tool blocks in some export shapes while other sessions do not carry them. Model names may be present for one provider and absent for another. Those gaps follow the source format, so they are not a random sample that can safely be scaled up. That is why comparison is stricter than display. A dashboard may show a partial total with its coverage beside it. A delta between two partial totals would invite you to interpret a change in export composition as a change in behaviour. ## When a delta is allowed For a numeric difference, both sides must pass the same comparability gates: 1. **Sessions exist on both sides.** An empty slice has no baseline. 2. **Both sides have a value.** Unavailable is not zero and cannot be subtracted as though it were. 3. **Coverage is complete on both sides.** Every contributing session in each slice reported the metric. 4. **Measurement signatures match.** Adapter version, normalization version, quality grade, source, and calculation must agree. The fourth gate protects against a quieter error. Two values may both be present and fully covered but still mean different things. Source-reported duration and a derived active span are not interchangeable simply because both format as minutes. A parser upgrade can also change what an adapter recovers from the same family of exports. Comparison must not erase that provenance. AI Stats sends both sides through the same comparison policy used by the dashboard’s period-over-period narrative. There is one decision point for comparability, not a looser rule for one screen and a stricter rule for another. ## Treat a refused row as a finding Comparison rows are never filtered just because they cannot print a difference. Each remains in place with its reason: - **No sessions** means the filters removed the baseline or result set. - **No value** usually points to a capability-matrix limitation in the export. - **Partial coverage** means at least one session did not report the metric. - **Different signatures** means the two sides were measured differently. Removing those rows would create a polished but misleading report. You would see only the easy comparisons and lose the evidence that token coverage disappeared, a provider mix changed, or one side was normalized by a newer method. Composition tables follow the same honesty rule in a different form. Providers, models, and project groups are aligned across both sides. If a key exists on only one side, the other cell reads **Not in this view**. A new provider appearing only in side B may explain several blocked metric rows more clearly than any percentage could. ## Keep the filters symmetrical when the question requires it Some comparisons need identical provider and project filters. Others deliberately compare providers or projects. Decide which before reading the result. For a time comparison, keep the non-time filters the same. If A is Codex-only and B includes Claude web, the result mixes a change in time with a change in source capability. For a provider comparison, keep the date and project filters aligned, then expect provider-specific metrics to refuse when the formats differ. Model filters deserve particular care. A model substring cannot recover a model name an export never contained. Filtering one side to a named model while the other provider exposes no models creates two different populations, not an even contest. ## Saved views preserve the question, not the answer A saved view stores a name plus the six canonical dashboard filter parameters. It does not store the totals that were on screen, a screenshot, or a session list. Reopen it after another import and AI Stats recomputes the slice from current normalized data. That behavior is useful for recurring questions such as “Codex, this repository, rolling 30 days.” It is not an audit snapshot. If you need to preserve figures for publication, use the product’s explicit sharing or export workflow and include coverage and provenance with the number. Saved filter values are allow-listed, de-duplicated, and ordered canonically. The name helps you find the question again; the URL remains the inspectable definition of the view. ## A practical comparison checklist Before quoting a delta: 1. Open each side and confirm the intended sessions are present. 2. Confirm the range refers to session start time, not import time. 3. Read coverage for the metric on both sides. 4. Check whether the providers actually export that metric. 5. Keep provider, project, and model filters symmetrical unless changing one is the question. 6. Read every refused row and the composition table before focusing on the allowed rows. 7. Describe the two slices beside the number so another reader can reconstruct the comparison. Then keep the conclusion at the altitude of the measurement. More sessions means more recorded sessions, not more value. More prompts means more exported prompt events, not better prompting. API-equivalent cost is an estimate at published API rates where the required tokens and models exist; it is not a subscription bill. The strongest comparison is often the one that produces fewer numbers. A report that refuses three tempting deltas and permits one well-supported difference has done more analytical work than a table that subtracts everything. For a guided click path, continue with [Compare two dashboard views](/tutorials/compare-two-dashboard-views). For the source-specific gaps behind a refusal, use [the capability matrix](/blog/capability-matrix-what-exports-can-tell-you) and [/methodology](/methodology). --- ### Unavailable Is Not Zero: Why Claude, Gemini, and ChatGPT Tokens Stay Blank URL: https://aistats.novusstreamsolutions.com/blog/unavailable-is-not-zero Category: Guides Published: 2026-08-09 | Updated: 2026-08-09 The most common request this product gets is some version of "just fill in the tokens." ChatGPT, Claude, and Gemini are the sources people import first. Those three consumer exports do not contain token counts or API-equivalent cost. Claude web and Gemini Takeout also omit model names. AI Stats therefore shows **Unavailable**, with the capability reason on the tile and on [Integrations](/integrations). It does not show `0`, `$0.00`, or a guessed model. This post is why that refusal is the feature, and how to read the dashboard once you accept it. The live surface this describes is [/app/dashboard](/app/dashboard), after you [import a history you exported](/sign-up). There is no ambient tracker behind the CTA. ## What a zero would cost you A zero is a claim: this session used no tokens, incurred no cost, launched no agents, or called no tools. Summed across a month it becomes a trend. Compared with last month it becomes a story about your work getting cheaper, or noisier, or more agentic. If the export never recorded the field, every one of those stories is false, and the falsehood is undetectable. Coverage would read 100 percent. The previous-period delta would look clean. A sparkline would draw a confident flat line at the baseline. That is exactly why [MetricSparkline](/docs/metrics-quality) breaks the line on a missing day instead of plotting zero, and why [comparison mode](/docs/compare-and-saved-views) keeps blocked rows instead of dropping them. The [methodology centre](/methodology) is explicit: an unreported metric renders Unavailable, never 0. The same sentence appears in the constellation statistics panel, in the capability matrix, and in the import preview. Repeating it is cheaper than unsaying a year of invented totals later. ## What is genuinely absent Decoded from real export files, not guessed: | Metric | Claude web | ChatGPT | Gemini Takeout | Codex | | -------------- | ----------------------------------- | ---------------- | ---------------- | ----- | | Tool calls | recoverable when blocks are present | absent | absent | works | | AI active time | recoverable when blocks are present | absent | absent | works | | Responses | works | works | recoverable | works | | Model names | genuinely absent | works | genuinely absent | works | | Tokens / cost | genuinely absent | genuinely absent | genuinely absent | works | "Genuinely absent" means the file does not contain the information. No adapter release invents it. Codex is in the table to show the contrast: when a source _does_ persist tokens, AI Stats reads them, de-duplicates them, and will estimate API-equivalent cost from a dated rate table. It will still refuse that estimate if a model is missing from the table, or if two differently priced models share a session. Claude Code and Gemini CLI are not consumer chat, and they are not a back door into the missing web fields. Claude Code tokens depend on what the transcript recorded. Gemini CLI snapshots carry no tokens, model names, or durations. Cursor is Beta and does not conjure ChatGPT-like token ledgers from Markdown history. If you want the cell-by-cell version, read [the capability matrix essay](/blog/capability-matrix-what-exports-can-tell-you) and then the live [methodology](/methodology) tables. Both are generated from the same registry the importers are tested against. ## Five labels, only one of which is a number you can sum Every tile carries one of five quality labels. The line under the number tells you which kind of claim you are looking at. - **Exact**: explicitly defined by the source. - **Source reported**: supplied by the provider, using the provider's definition. - **Derived**: calculated from source timestamps. - **Estimated**: produced by a documented heuristic (API-equivalent cost is the usual example). - **Unavailable**: not present in the source. Estimated is still a number, and it is allowed to refuse. Cost returns nothing when either token count is missing, when any model is absent from the rate table, or when a session used two models with different prices. A rate row is fenced to its vendor, so a ChatGPT session cannot be priced with a Claude rate. Most calculators would average. This one declines. Unavailable is not Estimated with a blank. It is not Derived with a gap. It is the label that says "do not put this in a denominator." ## How to work without the missing fields You can still learn a great deal from ChatGPT, Claude, and Gemini imports. Sessions, prompts, responses, timestamps and, where the format allows, AI-active time and tool calls are real. Project grouping still works. [What changed](/tutorials/read-your-dashboard) still narrates coverage-safe movement. Saved views still remember a range, provider set, project set, and model substring. What you cannot do honestly: - Rank people, teams, or weeks on token volume when some of the sources never emit tokens. - Subtract a Claude web cost from a Codex cost and call the result savings. - Fill model filters with names Claude or Gemini Takeout did not record. - Treat a mixed-provider token total as complete because Codex supplied most of the sessions. When you need a numeric comparison anyway, open [/app/dashboard/compare](/app/dashboard/compare). Seed side A from the dashboard you are looking at. Leave side B on its defaults, then narrow it. Rows that cannot be subtracted say why. That refusal is usually the finding. ## Where this sits in the Novus roster [Novus Stream Solutions](https://novusstreamsolutions.com) is the catalog for every Novus app. AI Stats is the one that measures AI work from user-approved exports. If the next step after measuring a week is studying it, [Novus Learn](https://learn.novusstreamsolutions.com) turns source material into cited notes. If you are testing a parser and need known-shape files, [Novus Examples](https://examples.novusstreamsolutions.com) publishes specced fixtures. None of those apps synthesize AI Stats token ledgers, and this app does not synthesize theirs. The editorial standard behind this page is on [/editorial-policy](/editorial-policy). Corrections go to the same team; the byline is organizational on purpose. A fake named expert would be the prose version of a synthetic zero. Import the exports you have. Read coverage first. Believe the blank tiles. The numbers that remain are the ones the files can actually support. --- ### The Capability Matrix: What Each AI Export Can Actually Tell You URL: https://aistats.novusstreamsolutions.com/blog/capability-matrix-what-exports-can-tell-you Category: Metrics Published: 2026-08-08 | Updated: 2026-08-09 Most AI usage dashboards publish a grid of numbers and hope you do not ask where each one came from. AI Stats publishes the grid that answers that question first: a [capability matrix](/integrations) with one cell per supported source and metric, graded against real export fixtures rather than against a vendor marketing page. This post is a reading guide for that matrix, and for the [methodology centre](/methodology) that is built from the same registry. It is not a token-usage round-up. Claude web, Gemini Takeout, and ChatGPT web exports do not contain tokens or API-equivalent cost. Those cells read **Not in export**. They are not priced at zero. If you want the live dashboard that these grades constrain, [import a history you actually exported](/sign-up) and open [/app/dashboard](/app/dashboard). Nothing here is ambient tracking. ## Why a matrix instead of a feature list A feature list says "tokens, models, and AI-active time." A matrix says which of those three your ChatGPT zip can support, which your Claude Code JSONL can support, and which your Gemini Takeout HTML can support. Those answers are different, and pretending they are the same is how a cross-tool total becomes a fiction. The authoritative table lives in code (`capability-matrix.ts`) and is locked by a contract suite that parses every sanitized fixture. The public rendering is on [Integrations](/integrations) and on each `/integrations/[provider]` page. The [methodology page](/methodology) then counts the live cells: today that is 91 source-by-metric claims across seven adapters and thirteen metrics. [Novus Stream Solutions](https://novusstreamsolutions.com) is the hub for every Novus app. The matrix is the original-data contribution this particular app makes to that hub: not a larger model catalog, but an honest map of what consumer and coding-agent exports actually contain. ## Verified versus Beta The [provider import docs](/docs/provider-imports) and the integrations pages grade each source **Verified** or **Beta**. - **Verified** means the adapter was confirmed against a real export shape, with fixtures in the repository. ChatGPT, Gemini Takeout, Claude Code, Codex, and Gemini CLI are in this group today. - **Beta** means the shape is recognized and the adapter runs, but the export family is still narrower or more variable than we would like. Claude web and Cursor sit here. Claude web recovers tool calls and AI-active time only when assistant `content[]` blocks carry them. Cursor reports tool calls only when a workspace snapshot spells each turn out as parts, and Background Agent chats are absent from regular local history. Beta is not a disclaimer sticker. It is a grade that should change how much weight you give a total from that source when you compare it with a Verified one. The [comparison view](/docs/compare-and-saved-views) will refuse a numeric delta when the two sides were not recorded the same way. ## Four meanings of a blank cell A blank metric is not one thing. The matrix uses four states that are easy to collapse and expensive to confuse: - **Not in export**: the file does not contain the information. No future adapter release changes that. ChatGPT tokens, Claude web tokens, Gemini Takeout tokens, Claude web agents, and Gemini Takeout AI-active time all live here. - **Not parsed yet**: the file does contain it and AI Stats does not read it yet. The machine-checked matrix currently has no supported metric in this state. Every remaining gap is a property of the format or of a particular export shape. - **Shown as AI active time**: deliberately not computed, because a better measurement of the same thing is already on the page. Waiting time is the standing example: waiting on the model _is_ the AI-active span. - **Sometimes**: some exports of this format carry the field and some do not. When yours does not, the value stays null rather than becoming a confident zero. [User active time](/methodology#blank) and waiting time are permanently blank on purpose across every supported source. No export records how long a person spent reading or typing, and relabelling AI-active time as "user active" would be a lie with a friendlier name. The database columns stay so a future source with a real measurement can fill them without a migration; the dashboard tiles were removed. ## What the seven sources can actually report Read this as a summary of the matrix, not as a substitute for it. If the two ever disagree, the matrix and the adapter tests are right. **ChatGPT (Verified).** Official ZIP or numbered `conversations*.json`. Message counts and timestamps work. Tools, agents, tokens, cost, and any working duration are absent from the format. Model names are present. **Claude web (Beta).** Official account ZIP/JSON. Tool calls, AI-active time, and model names are recovered when the relevant assistant content blocks or model fields exist. The current reference shape omits model fields, so those sessions remain Unavailable rather than receiving a guessed name. Tokens, cost, and agents are absent. **Gemini web / Takeout (Verified).** Google Takeout Gemini Apps activity. Activity is grouped into 30-minute windows and only `Prompted` rows count as prompts. The HTML form can carry responses but not model names; the JSON form can name a model but carries no answers. AI-active time, tokens, and cost are absent from both. **Claude Code (Verified).** Current transcript JSONL and official OTel. Parent and subagent files merge. Tokens and turn durations depend on what the transcript actually recorded. OTel content-bearing fields are discarded on purpose. **Codex CLI/Desktop (Verified).** Persisted rollout JSONL. Embedded IDs, cumulative tokens, turn durations, and tool IDs are preserved and de-duplicated. Input tokens include cache reads for this source. This is the source for which token and cost tiles can be real numbers rather than Unavailable. **Gemini CLI (Verified).** Local `$set.messages` snapshots and official OTel. Snapshots collapse to the newest complete one. Snapshots carry no tokens, model names, or durations. Subagent launches are not recorded by either input. **Cursor (Beta).** Exported Markdown and recognized SQLite snapshots. Background Agent chats are not in regular local history. A workspace snapshot reports tool calls only when its rows spell each turn out as parts. Unknown shapes fail closed. Planned providers accept no files until they have sanitized fixtures and pass the same contract. That is why sixteen names appear on the integrations index as planned and `noindex`, not as greyed-out importers. ## How to use the matrix when you read a dashboard 1. Open [Metric coverage](/tutorials/read-your-dashboard) on [/app/dashboard](/app/dashboard) before you read any total. Coverage tells you what fraction of the sessions in view actually reported the metric. 2. Open [/methodology](/methodology) when you need the formula, the high/low reading, and the ways that formula can mislead. 3. Open [/app/dashboard/compare](/app/dashboard/compare) when you want a delta. Comparison prints a number only when both sides were fully covered and shared adapter version, normalization version, quality, source, and calculation. Blocked rows stay visible and name the gate that fired. 4. If a Claude, Gemini, or ChatGPT web token or cost tile is blank, believe it. [Unavailable is not zero](/blog/unavailable-is-not-zero) is the companion essay. If you are building study notes from an AI-heavy week rather than measuring it, [Novus Learn](https://learn.novusstreamsolutions.com) turns articles, papers, and codebases into cited material. If you need synthetic files to test an importer of your own, [Novus Examples](https://examples.novusstreamsolutions.com) publishes specced fixtures. Neither app invents AI Stats metrics, and AI Stats does not invent theirs. The matrix is slow to grow on purpose. Adding a provider against a guessed schema is the worst failure this product can have: a parser that silently mis-reads real user data. Until a real export is in hand, the honest cell is empty, planned, and `noindex`. Not a zero, and not a demo number. --- ### What Leaves Your Device When You Import an AI Export, and What Does Not URL: https://aistats.novusstreamsolutions.com/blog/what-leaves-your-device-when-you-import Category: Guides Published: 2026-07-31 | Updated: 2026-07-31 An AI export is one of the most sensitive files you own. A single chat archive can contain client names, credentials pasted into a prompt, unreleased plans, and the text of every document you ever attached. So the interesting question about an AI analytics product is not which charts it draws. It is what actually travels over the network. Here is the whole path, in order. ## The file picker is the only entry point There is no background scanning. A web app cannot watch a folder, and this one does not ask to. Every import begins with you selecting files or a directory. You also choose the source explicitly. Shape detection exists, but it is assistance for the picker, never the decision. The adapter you select is authoritative, and a file that does not match it is rejected instead of being coerced. Filenames are never authoritative either. A Cursor database is recognised by its first sixteen bytes, the literal `SQLite format 3` header, not by its extension. Extensions are only an allow-filter: `.json`, `.jsonl`, `.md`, `.markdown`, `.sqlite`, `.db`, and `.vscdb` are considered, and everything else in a selected folder is skipped before it is read. ## Parsing happens in a worker on your machine The parser runs in a Web Worker inside your tab. ZIP archives are expanded in memory in the browser. Nested archives are rejected rather than walked, and an entry whose path has more than eight segments is rejected too. The ceilings are fixed and checked before any parsing starts. | Ceiling | Value | | ------------------------------ | ------ | | Selected files | 1,000 | | Selected bytes | 512 MB | | Any single file | 256 MB | | Files after expanding archives | 10,000 | | Bytes after expanding archives | 1 GB | | One JSONL line | 4 MB | These are not arbitrary. Parsing a large export inside a browser tab costs memory, and a limit that refuses is better than a tab that dies halfway through. ## Normalization is a narrowing step, not a copy Each conversation or session becomes one normalized record. That record holds the provider and product surface, start and end timestamps, duration fields, prompt and response and tool and agent counts, token categories, model names, a project hint, quality labels with their provenance, and warning codes. It does not hold messages. Prompts, responses, code, attachments, and file contents are read to produce counts and timestamps, then discarded when parsing finishes. Session titles are the one genuinely optional field. The default privacy mode is stats-only and sends no title at all; sending titles is a separate choice you make before the preview runs. ## Local paths become hashes A working directory is useful (it is how sessions group into projects), and it is also the field most likely to leak a client name or your own username. So the path itself is never uploaded. It is reduced to a SHA-256 hash in the browser, plus a short display hint, and the project name you see is the one you confirm yourself. ## Identity is derived, not taken from the file Every record carries two hashes. A stable source identity uses the provider's own session ID when the export contains one; when it does not, it is a SHA-256 over exactly four values: start time, prompt count, response count, and the sorted model list. A second hash covers the whole normalized record. Neither hash contains message text, and neither is the checksum of your ZIP. The container is not identity. ## The upload is a batch of statistics Normalized sessions are sent in batches of one hundred, each with a UUID idempotency key. Before storing anything the server re-derives both hashes from the payload and rejects the batch if either fails to match, so a client cannot claim an identity it did not compute. The server also scans the payload for prohibited field names (anything matching prompt, response, content, attachment, transcript, raw, or a path field) and rejects the whole batch if one appears. Be precise about what that check is: it reads key names, not values. It is a structural guard against an adapter regression shipping a new field, not an inspection of your data. The real protection is that the parser never puts message text into the record in the first place. ## Re-importing the same export is safe Exports overlap. You will import the same archive twice. A record whose stable identity and normalized hash both already exist is skipped. A record whose identity exists with different content updates the existing session. Only genuinely new identities are inserted. Lifetime totals do not double because you exported again. ## What this design does not give you It does not prove what your browser did. Re-deriving hashes on the server proves a record is internally consistent; it does not prove your machine discarded anything. The verifiable claim is narrower and more useful: the payload has a fixed shape, and message text is not part of it. It also does not always succeed. If no supported session is found, the import fails with an explicit reason and nothing is created: no partial statistics, no synthetic sessions. And importing is still behind a server release flag, enabled source by source as fixture coverage lands. When it is off the workbench is visibly disabled rather than quietly broken. The [provider import reference](/docs/provider-imports) tracks what each adapter accepts today. A tracker earns trust by being specific about the boundary. The boundary here is the worker in your tab: files go in, counts come out, and the counts are all that leave. --- ### Twelve Totals and Five Quality Labels: What the Dashboard Measures URL: https://aistats.novusstreamsolutions.com/blog/what-the-dashboard-measures Category: Metrics Published: 2026-07-31 | Updated: 2026-07-31 A dashboard is a set of claims. Every tile asserts that some number is true for some period, for some subset of your work. Most analytics products leave those three things vague. This one writes them down, and the rules are worth reading before you read the numbers. ## The period is resolved in your timezone Eight ranges exist: today, this week, rolling 7, 30, and 90 days, year to date, all time, and a custom range. All of them are resolved against your configured timezone and your configured first day of the week, not against the server's clock. A day boundary is a local midnight. An unrecognised range falls back to this week, and a custom range with a missing or reversed date does the same rather than guessing. The active range, providers, projects, and a model filter all live in the URL, so a view you are looking at is a view you can send to yourself. ## The comparison is the window immediately before The percentage under each tile compares the current window with a window of exactly the same length ending where the current one starts. Thirty rolling days are compared with the thirty before them. All time has no such window. It reports _no comparable prior-period value_ instead of inventing a baseline, which is the correct answer and an unusual one to ship. ## Twelve totals, and what each one depends on The tiles are sessions, AI active time, estimated user active time, waiting time, prompts, responses, tool calls, agents launched, tokens, API-equivalent cost, providers used, and active projects. They do not all come from the same place, and the tile says so in one line underneath. Sessions are counted. Prompts and responses are exact or adapter-derived. Tool calls and agents are source-reported, which means a source that never emits a subagent event contributes nothing rather than zero. Waiting time appears only when the source reports it. ## Unavailable is not zero This is the rule that shapes everything else. When a source does not report a value, the record stores null, and the tile reads _Not available_. It is never quietly replaced with a zero that would then be summed, averaged, and compared against last month. Each metric carries one of five labels: - **Exact:** explicitly defined by the source. - **Source reported:** supplied by the provider, using the provider's definition. - **Derived:** calculated from source timestamps. - **Estimated:** produced by a documented heuristic. - **Unavailable:** not present in the source. The tokens tile shows a number when either input or output tokens are known and nothing when both are missing, which is the same rule applied to a sum. ## Read the coverage panel first The dashboard has a metric coverage panel that reports, per metric, how many of the sessions in view actually carried it, for example 41 of 120 sessions, or 34 percent. That panel is the honest header for everything above it. A tool-call total drawn from a third of your sessions is a real number about a third of your work, and a coverage figure is the only thing that tells you which. ## The cost estimate is allowed to refuse API-equivalent cost is the tile most likely to be misread, so it has the strictest rules. It is calculated from a dated rate table (the current text-token snapshot was reviewed on 2026-07-29), and it returns nothing at all in three cases: 1. Either token count is missing. 2. Any model in the session is not in the rate table. 3. The session used two models with different prices, because token attribution between them would be ambiguous. A rate row is also fenced to its vendor, so a ChatGPT session can never be priced with a Claude rate. Most calculators would average, or silently pick the first model. This one declines and labels the metric unavailable. Even when it does produce a number, the number ships with its limitations attached: it is an API-equivalent estimate, not your subscription charge, and it excludes taxes, discounts, tools, long-context premiums, and unreported cache categories. See [metrics, provenance, and quality](/docs/metrics-quality) for the full list. ## What the totals still cannot tell you They cannot tell you whether the work was good. Session counts, prompt counts, and hours are context, not a score, and comparing two people on them compares their tools and export formats as much as their work. They also describe only what you imported. A provider you have not imported is absent, not zero, and the coverage panel is where that shows up. The point of writing the rules down is not modesty. It is that a number you can interrogate is worth more than a number you have to trust, and [reading your dashboard honestly](/tutorials/read-your-dashboard) is a skill the product should teach rather than assume. --- ### AI Active Time vs. Session Time: What Your Statistics Actually Mean URL: https://aistats.novusstreamsolutions.com/blog/ai-active-time-vs-session-time Category: Metrics Published: 2026-07-28 | Updated: 2026-07-31 “Time spent with AI” sounds like one statistic, but it can describe several very different measurements. A chat might remain open for three hours while the model generates for only two minutes. A coding agent might actively run commands for twenty minutes, pause for approval, and then wait while the user reviews a diff. A trustworthy analytics product should never combine those periods into one unexplained number. ## Session wall time Session wall time measures the elapsed period between the beginning and end of a session. If a session starts at 1:00 p.m. and its final recorded event occurs at 3:00 p.m., the derived wall time is two hours. This includes: - Model generation. - Tool execution. - User typing. - User review. - Idle time. - Breaks. - Approval delays. Wall time describes the session’s span, not continuous work. ## AI active time AI active time measures periods in which the model or its tools were actively processing. Depending on the source, this may include: - Response generation. - Reasoning spans. - Tool calls. - Terminal commands. - Subagent tasks. - Code analysis. Some coding tools expose these spans directly through telemetry. In that case, the number can be source-reported or exact within the provider’s definition. [Claude Code, Codex, and Gemini CLI](/blog/what-ai-coding-tools-can-measure) differ from one another here, so the adapter decides per source rather than per product category. A basic web-chat export may not contain response-start and response-end events. In that case, exact AI active time is unavailable. ## User active time User active time attempts to measure typing, reading, approving, editing, and reviewing. It is usually the hardest value to calculate. A browser or desktop collector can observe interaction events, but an imported conversation history may contain only message timestamps. The tracker can estimate activity windows, but the result must be labelled estimated. A reasonable heuristic begins a user-active interval when the user submits a prompt or performs an action. It continues while related actions occur within a short idle threshold. Long gaps are excluded. This still cannot determine whether the user was carefully reading or had simply left the tab open. ## Waiting time Waiting time can include: - Response latency. - Tool execution delay. - Agent queue time. - Approval waits. The source must provide enough event detail to separate these periods. Otherwise, the tracker should not manufacture a number. ## Overlapping activity Agentic coding introduces another problem: several activities may overlap. A parent agent can launch subagents while tools run concurrently. Adding every duration together may produce five hours of “work” inside a one-hour session. Both numbers may be useful, but they answer different questions: - **Elapsed active time:** how long the user waited. - **Concurrent compute time:** the sum of all agent spans. - **Peak concurrency:** the largest number of simultaneous agents. The dashboard should name these separately. ## Why confidence labels matter Consider two sessions: ### Session A Claude Code emits model and tool spans. - Wall time: 38 minutes. - AI active time: 21 minutes. - User active time: estimated 11 minutes. - Idle time: 6 minutes. ### Session B A ChatGPT export contains message timestamps only. - Wall time: derived 44 minutes. - AI active time: unavailable. - User active time: estimated. - Idle time: unknown. Displaying both as “44 minutes worked” would be misleading. ## The better dashboard Each time metric should include: - Name. - Definition. - Source. - Calculation. - Quality. - Known limitations. A tooltip might say: > AI active time: Derived from model and tool spans. Overlapping spans are deduplicated. Background activity not emitted by the source may be missing. Clear definitions make the statistics more useful, not less impressive. The [metrics and provenance reference](/docs/metrics-quality) lists the five quality labels this product uses and what each one is allowed to claim. ## Use time statistics for patterns, not judgement AI analytics can reveal: - When sessions happen. - Which providers dominate. - Which projects take the most time. - How often sessions become long and iterative. - Whether coding agents reduce or increase review time. They should not automatically declare one user more productive than another. Time is context, not a score. The most valuable tracker is the one that tells users what it knows, how it knows it, and what remains uncertain. --- ### How to Track Your AI Usage Across ChatGPT, Claude, Gemini, and Coding Agents URL: https://aistats.novusstreamsolutions.com/blog/track-ai-usage-across-tools Category: Guides Published: 2026-07-28 | Updated: 2026-07-31 AI work is increasingly spread across several products. A research conversation may begin in ChatGPT, continue in Claude, move into Gemini for a second opinion, and end in Claude Code or Codex when the idea becomes software. Each product keeps its own history. The result is fragmentation: users know that AI has become a major part of their work, but they cannot easily answer simple questions. - How many sessions did I run this month? - Which AI do I use most? - How much time did my coding agents spend working? - Which project consumed the most prompts? - How many agents and tools did I launch? - How often do I hit usage limits? A trustworthy tracker must begin by recognizing that these products expose different kinds of data. ## Start with official exports and local logs For ordinary AI chats, the most reliable historical source is usually an official account export. ChatGPT provides a data-export process through its Data Controls. Claude and Google provide their own export mechanisms. These exports can be imported into a local parser that counts conversations, timestamps, messages, and any model metadata included by the provider. The [provider import reference](/docs/provider-imports) lists the exact file shapes each adapter accepts. Coding agents can provide richer information. Claude Code stores local session transcripts for session resumption, while Claude Code and Gemini CLI can emit OpenTelemetry data. Codex also maintains resumable local session history, although its storage format should be treated as versioned input rather than an unchanging public API. ## Keep parsing local AI histories may contain source code, personal information, business plans, credentials, and uploaded document text. Uploading an entire export to a new analytics service creates unnecessary risk. A privacy-first tracker can perform the heavy parsing in the browser: 1. The user selects an export or a session folder. 2. A Web Worker parses the files locally. 3. The app previews what was detected. 4. The user chooses which fields to synchronize. 5. The server receives normalized statistics rather than raw messages. For most statistics, the server needs provider names, timestamps, counts, durations, model identifiers, projects, and metric-quality labels. It does not need the text of every prompt. ## Separate exact metrics from estimates Not every provider reports “time worked” the same way. A coding agent may expose model latency, tool duration, and complete session timestamps. A web-chat export may provide only message timestamps. Those two sources cannot support the same level of precision. A useful dashboard should label metrics as: - **Exact:** explicitly defined by the source. - **Source reported:** supplied by the provider, with the provider’s own definition. - **Derived:** calculated from source timestamps. - **Estimated:** based on a documented heuristic. - **Unavailable:** not present in the source. This is more honest than filling every missing value with zero. [AI active time vs. session time](/blog/ai-active-time-vs-session-time) works through the time metrics in detail. ## Prevent duplicate imports Users will import the same provider repeatedly. A production tracker needs deterministic fingerprints based on source session IDs, timestamps, file checksums, and adapter versions. Re-importing an identical export should update or skip existing sessions. It should never double the user’s lifetime totals. ## Group sessions by project Provider totals are fun, but project totals are more useful. A project can combine: - ChatGPT planning conversations. - Claude research. - Claude Code implementation. - Codex reviews. - Gemini CLI experiments. When sources contain a repository or working-directory hint, the tracker can suggest a project. The user should approve the final project name, and local paths should be hashed before cloud synchronization. ## Build sharing from snapshots Shareable recaps are one of the most enjoyable parts of AI statistics, but they should not be live windows into a private account. A safe sharing flow creates a snapshot containing only selected fields. The user can remove project names, hide exact token counts, set an expiry, and revoke the link later. [Share a snapshot without leaking anything](/tutorials/share-a-snapshot-safely) walks through those choices one at a time. Examples include: - “143 AI sessions this month.” - “Claude was my most-used provider.” - “I launched 648 coding agents.” - “My longest coding session lasted four hours.” - “I hit my weekly limit seven times.” The raw conversations remain private. ## The practical starting point A useful first version does not need to watch every provider automatically. It needs to: - Import official chat exports. - Import common coding-agent logs. - Explain metric accuracy. - Prevent duplicates. - Show clear dashboards. - Keep raw content local by default. - Create privacy-safe recap cards. The current product should remain focused on transparent, user-approved imports. That produces a product users can trust and test immediately without expanding into background tracking. --- ### Claude Code, Codex, and Gemini CLI: What Can Be Measured Today URL: https://aistats.novusstreamsolutions.com/blog/what-ai-coding-tools-can-measure Category: Coding Agents Published: 2026-07-28 | Updated: 2026-07-31 Coding agents produce richer activity data than ordinary chat interfaces because they interact with files, terminals, tools, repositories, and subagents. That does not mean every coding agent exposes identical telemetry. A cross-platform tracker needs separate adapters and a normalized metric model. This post covers the three agents with the richest event data. [Cursor](/integrations/cursor) is handled separately because its inputs are exported Markdown and a local SQLite snapshot rather than a telemetry stream, which changes what can be measured at all. ## Claude Code Claude Code stores local session transcripts so users can resume work. It also supports OpenTelemetry for usage, costs, tool activity, events, metrics, and traces. Depending on configuration and version, useful signals can include: - Session starts. - User prompts. - Model usage. - Token counts. - Tool decisions. - Tool results. - Subagent activity. - Durations. - Errors. - Working-directory or project context. Claude Code telemetry is the strongest option for ongoing measurement. Historical local transcripts remain useful when telemetry was not enabled. Raw prompts and tool content may contain sensitive information. A tracker should make prompt logging optional and keep source text local by default. ## Codex Codex supports resumable sessions and maintains local session history. The open-source client and its session files make analytics possible, but a production adapter should not assume an undocumented file layout will never change. Useful normalized fields may include: - Session identifier. - Timestamped conversation events. - Model. - Token usage. - Tool calls. - Command events. - File changes. - Repository or working-directory hints. - Session completion state. The adapter should detect event schemas and maintain fixtures for every supported Codex version. ## Gemini CLI Gemini CLI has built-in OpenTelemetry support. It can export logs, metrics, and traces to a local file or an observability backend. A local configuration can direct telemetry to a file such as `.gemini/telemetry.log`. Prompt logging can be enabled or disabled. Useful signals may include: - Session ID. - Installation ID. - Prompts. - Model usage. - Token counts. - Tool calls. - Latency. - Approval mode. - Errors. - Diff statistics where available. For a personal analytics product, local file output provides a practical and transparent import path. ## A normalized session model The tracker should convert each provider into a shared session shape: - Provider. - Product. - Source session ID. - Start and end. - Wall time. - AI active time. - Prompt count. - Response count. - Tool-call count. - Agent count. - Token categories. - Model names. - Project hint. - Metric quality. - Warnings. Provider-specific details can remain in metadata, but dashboard totals should use consistent definitions. The [metrics and provenance reference](/docs/metrics-quality) documents the labels attached to each of those fields. ## Do not equate tools and agents A shell command is a tool call. It is not automatically a separate agent. An agent count should increase only when the source emits an explicit subagent or agent-launch event. Otherwise the value should be unavailable. This distinction prevents inflated statistics. ## Do not expose local paths Working directories help group sessions by project, but they can expose usernames, client names, and confidential repository structures. A safer flow is: 1. Detect a local path. 2. Hash it in the browser. 3. Ask the user to map it to a display project. 4. Upload only the hash and approved project name. ## What can be compared fairly? Generally comparable: - Session count. - Prompt count. - Tool-call count, with definitions. - Token totals where available. - Session wall time. - Provider usage share. - Project activity. - Time-of-day patterns. Requires caution: - AI active time. - Reasoning time. - Cost. - Agent count. - Files changed. - Completion rate. Not fair without a common benchmark: - Code quality. - Developer productivity. - Hours saved. - Provider intelligence. - Economic value. ## The opportunity Today’s coding tools expose enough data to build a useful personal analytics layer. The winning product will not be the one with the largest number of charts. It will be the one that handles privacy, versioned parsers, duplicates, and measurement definitions correctly. Users should be able to enjoy a “55 agents launched” achievement while still trusting that the underlying number came from a real source event.