What Bloom 6.5 records

Seven events. For each one: the fields it carries and the question it exists to answer. At the end, a short list of things we considered recording and chose not to.

7events
38fields across them
2properties that ride on every event

The two that are always there, and are not listed per row. BookId is filled in by the analytics/track endpoint. BrandingProjectName is the subscription descriptor, which also encodes tier, flavour and subscriber; it has always ridden on every Bloom event as an application property.

The events

Event Fields The question it answers
Where pictures come from
Change Picture source One of pixabay, openverse, a local-collection slug, local-disk, clipboard, ai-editor. Which routes pictures actually arrive by. The count of picture changes was never the interesting part — choosing a picture is essential and we knew people did it. The breakdown is: it says which sources are earning their place.
Image Chooser Closed once per visit to the chooser outcome, acceptedProvider, searchCount, providersTried, providers, pixabayKeyPresent Did the visit end in a picture, and which source earned it — a success rate for searching. A result count cannot answer this: Pixabay and Openverse nearly always return something, so the failure is a full page of wrong pictures, and only what the user did next reveals it.
The AI Image Editor — the only place real money is spent
AI Image Editor Generate once per generation attempt tool, model, sourceKind, referenceCount, batch, runsLocally, attemptNumber, result, durationSeconds, costUSD, spentCredits What each generation costs, which models and tools people choose, how often they retry, and how much of it fails. This is the spend. Sent by the editor itself over the existing bridge, so its fields are the editor's vocabulary rather than ours.
AI Image Editor Closed once per session, when it settles appliedCount, replacementCount, failedCount, generatedCount, reusedCount, generatedThisSession, historyCount Three things at once. Did anything reach the book? A non-zero failedCount is a commit that silently did nothing. Was the AI work thrown away? generatedThisSession against an appliedCount of zero is the clearest read we have on whether the output was good enough. And how many sessions there were, since exactly one of these is sent per session. Two things to know when reading it. An appliedCount of zero is the cancelled case. And generatedCount counts what the user chose — and therefore paid for — not what landed, so it does not sum with appliedCount.
Three things nothing else watches
Cover Layout Changed layout, page How much custom-cover use there is, and on which cover. The capability has been subscription-gated across two releases with no usage data at all, which is a weak position in any pricing conversation. page separates the long-standing front cover from the newer inside-back-cover extension.
Image Transparency Set from, to, pageBackgroundIsColored, imageFormat, path Which way our line-art detection got it wrong. Opaque means Auto fired when it should not have and Bloom was erasing parts of a picture; Transparent means the detector missed line art that plainly is line art. There is no other way to evaluate that heuristic in the field. path is the whole sequence of choices made on one picture. It exists to spot someone cycling the options because none of the labels predicted the result — which is a UI problem rather than an algorithm one.
Publish App once per stage that finishes stage, result, elapsedSeconds, errorKind, bookCount, apkSizeMB Whether the Reading App Builder toolchain works on real machines, where it breaks, and how long a build takes. CI never runs a real RAB build, so field reports are this path's only continuous coverage. stage is build or install, so a build the user never installs still reports its own result and duration. errorKind is the exception type only — never the Gradle log, which is enormous and full of file paths.

Considered, and left out

Each event is effectively its own table in the analytics database, so the bar was whether the answer would change a decision. These did not clear it.

What we considered recording Why we didn't
The search terms people type into the picture chooser Free-form user text, and the only such text this instrumentation would have carried. Dropped deliberately at Bloom's boundary; the image gallery still hands them to Bloom, so revisiting the decision is one property rather than a redesign.
One event per search — source, language, result count Without the term it counts queries and nothing finer, and the close event already carries how many searches a visit took and which sources were tried.
The Pixabay key funnel: picking a source you cannot search yet, and later saving a key pixabayKeyPresent on the close event already says whether a visit ran without a key.
How many times a person had opened the picture chooser before Bloom identifies users to Segment, so a person's earlier visits are simply their earlier events. Keeping a durable per-machine counter to say the same thing meant persisting state on the user's disk.
Local image previews that take seconds to re-encode Would have told us whether real users meet that wait. Judged not to be worth an event of its own; it will surface as a support complaint if it matters.
The rest of the AI Image Editor funnel: opening it, saving an API key, and finding it absent from the build The session event above already counts sessions. The key question is answerable over time from whether sessions have a key, and "not in this build" should never happen in the field — that belongs in error reporting.
Whether people never found the AI tools at all because they are behind the experimental-features checkbox We decided not to try to measure this. It would need a startup property saying which experimental features are switched on, and we chose not to add one.
Custom cover layouts being refused, and whether the signpost to Book Settings gets followed Refused demand is a real question, but a one-off check answers it better than a permanent event.
What ethnolib derives for a chosen language, and corrections users make to reading direction That part of collection settings is due to be reworked, and the plumbing was large for one data point.
How long "use this copyright and license for all images" takes on a big book Instrumenting it found a real performance bug — the operation re-read every picture's metadata once per picture — and that is fixed. The ongoing event would not change a decision.
The App Builder's prepare stage Fast and entirely local, so it says nothing about whether the toolchain works.