Novus Stream Solutions
Field guideNovus AI Stats

2026 · Novus AI StatsAbout 11 min readNovus Stream Solutions

Unknown is not zero: how Novus AI Stats explains a blank metric

A dashboard tile that says "Not available" and nothing else is indistinguishable from a broken import. Novus AI Stats answers why instead, using a per-provider capability matrix that separates "the export does not contain this" from "we do not read it yet" — a distinction that decides whether a gap is a limitation or a to-do. Here is how that machinery works across all seven supported sources, and what it forces the product to admit.

Last updated Applies to Novus AI Stats 2026.08
Contents
  1. 1.Overview
  2. 2.Five states, and the two that everyone else merges
  3. 3.The same metric, three different reasons
  4. 4.Token counts, and two totals that must not be added together
  5. 5.The Gemini prompt count, and why the obvious version was wrong
  6. 6.Two metrics that will never be filled, and the tiles that were removed
  7. 7.The sentence underneath the number
  8. 8.A claim you can fail a build on
  9. 9.Why local parsing is what forces the honesty
  10. 10.How to read your own dashboard

Overview

There is a specific failure that every analytics product runs into and most of them solve badly. A number cannot be produced, and the interface has to say something. The convenient answer is to print a zero, because a zero fits the layout and does not require an explanation. The slightly better answer is to print "Not available", which at least stops lying about the value but is still indistinguishable from a broken import — a person looking at that tile cannot tell whether the file was wrong, the upload failed, the feature is unfinished, or the information has never existed anywhere in any version of that export.

Novus AI Stats treats unknown as a distinct value from zero and attaches a reason to every gap. The mechanism is a capability matrix: for each of the seven supported sources — ChatGPT, Claude, Gemini, Claude Code, Codex CLI/Desktop, Gemini CLI, and Cursor — and for each metric on the dashboard, a recorded state and a sentence a person reading a tile can act on. This article is about the states, what they force the product to admit, and a few places where the honest answer turned out to be more interesting than the confident one.

Five states, and the two that everyone else merges

A metric can be read from the export today, which is the uninteresting case and needs no explanation. Everything else does. Some formats carry a value sometimes and not always, which is a property of your particular export rather than of the format. Some metrics are deliberately not computed because a better measurement of the same thing is already on the page. Some are in the file and not yet read. And some are simply not in the file at all, and no future release changes that.

The last two are the ones that matter, and they are the two most products collapse into a single "unavailable". They are not the same thing at all. "Not in export" is a fact about a file format that is outside anyone's control — the information was never recorded, and waiting for an update will not produce it. "Not parsed yet" is a to-do with a name attached: the data is sitting there and the adapter does not read it. Merging them means a genuine physical limitation and an unfinished piece of work look identical to a user deciding whether this product will ever answer their question.

The detail that makes the distinction credible is what the matrix currently says. As of the present adapter generation, no metric of any supported provider is in the "not parsed yet" state — every gap in the table is a property of an export format. The state stays defined anyway, because the moment it is deleted for being empty the next real gap gets filed under the wrong one, and the whole distinction quietly dies.

  • Available — read from the export today.
  • Sometimes — some exports of this format carry it and some do not; a blank one is a property of your file.
  • Shown as AI active time — deliberately not computed, because a better measurement of the same thing is already on the page.
  • Not parsed yet — the file contains it and AI Stats does not read it yet. Currently empty, and kept anyway.
  • Not in export — the file does not contain it. No future release changes that.

The same metric, three different reasons

Tool calls are a good demonstration, because the answer differs by provider for reasons that are all defensible. A ChatGPT account export is a record of conversation turns; tool and browsing invocations are simply not part of it, so the count is "not in export" and always will be. Claude web is "sometimes": current exports spell out every assistant content block and tool-use blocks are counted from them, but an older export that carries message text only leaves the count unavailable rather than reporting a confident zero. Cursor splits on the input you hand over — a Markdown transcript labels tool and command turns, so seeing none there is a proven zero, while a workspace snapshot reports them only when its rows spell each turn out as parts, and older rows that store plain message text leave the count unavailable.

That is three different meanings behind one blank cell, and a product that flattens them is teaching its users something false. The Cursor case in particular is the one worth internalising: whether a zero is real depends on which file you exported, not on what you did in the tool. A snapshot that shows no tool calls might mean you used none, or might mean the row format cannot say. Only one of those is a fact about your week.

Token counts, and two totals that must not be added together

Tokens are absent from the ChatGPT account export, from the Claude account export, from Google Takeout, and from both accepted Cursor inputs. That is not a parsing gap waiting to be closed; the providers do not put the numbers in the file. Cost inherits the same fate for the same reason, since cost is priced from token counts — a currency figure invented without them would be guesswork wearing a symbol. Where cost is available at all it is an estimate against a dated public price list, and only when both the tokens and a known rate for that model are present.

The coding agents do report tokens, and there the honest problem is a different one. Codex rollout files report cumulative usage in which input includes cache reads. Claude Code's usage block excludes them. Both are read faithfully, and both are correct as far as their own source is concerned — but they are counting different things under the same word. The matrix records that explicitly, which is why the two are not compared with each other anywhere in the product.

It would be trivially easy to sum them into one big number. It would also be wrong in a direction nobody could see afterwards, which is the worst kind of wrong: the total would look plausible, would move in the right direction week to week, and would be quietly inflated by whichever source counted cache reads. A dashboard that refuses to add two numbers is doing more work than one that adds them.

The Gemini prompt count, and why the obvious version was wrong

Google Takeout logs Gemini Apps as activity rows rather than as conversations, and the intuitive implementation is to count the rows. That was tried, and it inflated prompts by roughly half on real exports. Canvas creations and "Used Gemini Apps" rows are side effects of a prompt, not prompts, so only rows recorded as "Prompted" are counted now. Because Takeout logs activities rather than conversations, a session has to be defined as a run of activity with no long gap in it rather than as a thread — which is itself a modelling decision the matrix states rather than hides.

The same export produces a second, stranger split. Gemini's answers follow the timestamp inside the same activity cell of the HTML export and are read from there, but the JSON activity form of the same Takeout carries no answers at all — while naming the model that handled each prompt, which the HTML does not. So one file gives you responses without model names and the other gives you model names without responses. There is no clever way to reconcile that, and pretending otherwise would mean inventing one of the two. The matrix marks both as conditional and explains which file you gave it.

Two metrics that will never be filled, and the tiles that were removed

User active time and waiting time exist on the normalized session type, on the database columns, and on the API export — and no adapter sets either of them. That is deliberate, and the reasoning is worth spelling out because it is the sharpest example of the whole stance.

No supported export records how long a person spent reading or typing. It could only be produced by subtracting one estimate from another, and an inferred number displayed as a measurement is a fabrication regardless of how reasonable the inference was. Waiting time is a different problem with the same conclusion: waiting on the model is exactly the AI active time already shown on the page. Reporting it again under a second name would add a tile, not a measurement.

The visible consequence is that the dashboard tiles for both were removed rather than left showing a permanent zero, and the metric-coverage rows carry the reason instead of an unexplained fraction. The fields stay in the schema so that a future source with a real measurement can fill them without a migration. Deleting a tile is a harder decision than leaving it empty, and it is usually the right one: an empty tile is a promise that a number is coming.

The sentence underneath the number

A dashboard card in AI Stats reports its value, how many sessions in view actually reported it, the total, and the quality breakdown. When those two counts disagree, the card explains the gap in a sentence — and the sentence is scoped to the providers actually inside the current filter rather than to every provider the product supports. Reading "the ChatGPT export format does not contain it" is useful when you are looking at a ChatGPT-heavy week; reading a generic paragraph about all seven sources is not.

There is one rule inside that mechanism which reads as pedantic and is not: the count in the sentence is the card's own gap, never a figure re-derived from provider session totals. Two plausible ways of computing "how many are missing" can disagree by a session or two, and a sentence that contradicts the number printed directly above it destroys more trust than no sentence at all. The explanation has to be subordinate to the figure it explains.

A claim you can fail a build on

None of this would matter if the matrix were prose. A page describing what a product can read is a marketing artifact that goes stale the first time an adapter changes and nobody notices. So the matrix is a module, and a test parses every fixture for every provider and fails when a cell and the adapters disagree. If an adapter stops recovering a metric it claims, or starts recovering one the matrix calls impossible, the suite goes red. A cell is evidence rather than a claim.

Two smaller decisions hold it together. The metric keys in the matrix are the same keys the adapters write into a session's recorded provenance, so a cell and a stored quality label are always talking about the same number rather than two similarly-named things. And "supported" is itself a contract: a provider may only be marked supported once it has sanitized fixtures and passes the shared contract suite, which is why some sources are visibly labeled beta and why planned providers accept no files at all. A roadmap entry is not an ingestion claim.

The written matrix in the repository documentation carries a line that is the correct posture for any generated table: if the prose ever contradicts the module, the module is right and the prose is stale.

Why local parsing is what forces the honesty

It is tempting to read all of this as fastidiousness. It is closer to a consequence of the architecture. Browser workers parse your allowlisted files transiently, and raw prompts, responses, attachments, transcripts, source files, readable repository names and local paths are prohibited from import payloads, logs, analytics, admin views and shares. Project grouping is a hashed key rather than a path, so a group can be named on your screen without a directory name leaving your machine.

That constraint removes an escape hatch most analytics products rely on. A service that keeps your raw transcripts can afford a vague gap, because it can always go back and re-derive the missing number later from data it still holds. AI Stats cannot quietly backfill anything, because it did not keep the file — the only durable answer to "why is this blank" is the one written down at parse time. The five-state matrix is not a nicety bolted on top of a local-first design. It is what a local-first design requires you to build if you want the dashboard to stay explicable a month later.

How to read your own dashboard

Three habits get most of the value. Check the quality label before treating a number as ground truth, because a derived session count and a token total read from the source should not carry the same weight in your head just because they share a card style. Read the availability sentence when a card reports fewer sessions than are in view, since it names the format rather than blaming your import. And compare providers only where the matrix says the metric means the same thing on both sides — the Codex and Claude Code token difference is the standing example, and it is the kind of mismatch that produces confident, wrong conclusions.

The per-provider pages are the fastest way to see all of this for the sources you actually use. Novus AI Stats is live and still in progress: imports, metrics and share cards continue to expand, and the matrix will change as they do — which is the point of keeping it machine-checked rather than written down once. Seven sources are supported today, and everything else on the roadmap is listed as planned. Hub coverage sits at Novus AI Stats, with tutorials from Getting started with Novus AI Stats: demo to dashboard through imports, metric labels and share cards.

Frequently asked questions

Quick answers to common questions about this topic.

Why does a metric say unavailable instead of zero?

Because unknown and zero are different facts and printing one as the other is a lie the interface cannot take back. If a source never reported a value, showing zero tells you that you did none of that thing, which may be completely false. AI Stats keeps the value null and attaches a reason, so a blank tile is distinguishable from a broken import and from a genuine zero.

What is the difference between "not in export" and "not parsed yet"?

"Not in export" means the file does not contain the information at all, so no future release of AI Stats can produce it — it is a property of the provider's format. "Not parsed yet" means the data is in the file and AI Stats does not read it yet, which is work with a name attached. Currently no metric of any supported provider is in the second state; every gap in the matrix is a format limitation.

Why can I not compare Codex and Claude Code token totals?

They count different things under the same word. Codex rollout files report cumulative usage in which input includes cache reads, and Claude Code's usage block excludes them. Both are read faithfully from their own source, so each total is correct on its own terms, but summing or comparing them would produce a figure inflated in a direction that is invisible afterwards.

Are user active time and waiting time coming in a later release?

No. No supported export records how long a person spent reading or typing, and it could only be inferred by subtracting one estimate from another — an inferred number shown as a measurement would be a fabrication. Waiting on the model is already reported as AI active time, so repeating it under a second name would add a tile rather than a measurement. The fields stay in the schema so a future source with a real measurement can fill them without a migration.

Does any of this require uploading my transcripts?

No. Files are parsed transiently by browser workers, and raw prompts, responses, attachments, transcripts, source files, readable repository names and local paths are prohibited from import payloads, logs, analytics, admin views and shares. That is also why the reasons have to be written down at parse time: AI Stats cannot go back and re-derive a missing number later, because it did not keep the file.

Related workflow

Turn your AI work into a report you can actually show someone

Measure what you built with AI assistants over a period, then package the numbers into a document with the caveats attached.

  1. Import your history and read the quality labelsNovus AI StatsToken and session figures differ in how reliably each provider reports them; the labels say which numbers can carry an argument and which cannot.
  2. Assemble and protect the reportNovus PDF StudioMerge the exported pages into one document and apply a password before it leaves your device.
All cross-product workflows →

Related troubleshooting

Was this page helpful?

Your answer stays in this browser — it is not sent anywhere and no account or cookie is involved.