A chart is not an insight. "Written premium by region, 2025" is a chart. "Premium fell $253M with all five regions down, Midwest drove half of it on a 7.9% drop in exposure, only eight of the top forty brokers grew, and NFP Chicago's loss is one account" is an insight — and it is only worth anything if each of those figures can be pointed at a row. This is how InsightCompose produces the second kind, and how it refuses to pretend when it cannot.

Where the questions come from

Most analytics tools wait for a question. InsightCompose starts by reading the warehouse it has been given: the tables, their joins, the measures that recur, the glossary terms and agreed metrics a curator has accepted. From that it proposes questions, grouped by theme — distribution and broker performance, underwriting and appetite, claims, concentration — each with the parts it would take to answer and a first guess at what the data would show. That is the Discover page: a catalogue of what your data can say, before anyone has asked it anything.

Insights → Discover
The Discover page: 76 proposed insights across 38 warehouse tables, a theme map with questions such as which brokers are performing and who needs a nudge, and which brokers send volume but convert poorly, each tagged by kind and line of business.
What the agent found. Nothing here has been run yet; each card is a question with a plan behind it.

Two things keep this from being a wall of noise. The catalogue is read-only: proposals are never edited in place, so a rescan after the schema changes replaces untouched proposals and leaves alone anything a person has built on, run or published. And it can be steered: tell it in a paragraph what you care about — "broker profitability for the Midwest, with commission in it" — and it proposes a theme of complex insights for that, each one already planned into parts.

An insight is built from parts

An insight is not one query. It is a small plan of parts that feed each other, and the plan is the thing a person edits.

PartWhat it doesWhat holds it honest
Warehouse queryA question the SQL engine answers against your tables, using your definitions and vocabulary.Validated, read-only, row-capped; the SQL and its checks are kept with the result.
Outside factsA public figure the story needs — a market rate, a benchmark — fetched through a research provider.Asked under a column contract; cached by question; a refusal is cached briefly and never silently filled in.
DerivedArithmetic over earlier parts: a share, a difference, a ratio.Runs locally over the rows already fetched; never reaches the warehouse.
SynthesisWhat it adds up to, and what should be done about it.Written last, from the parts above, then critiqued and verified.

Parts that do not depend on each other run in parallel. You watch the run fill in step by step — the SQL generated, validated and executed; the rows; the chart chosen from the data's shape; the narrative for each part; the draft summary; the critique; the actions — and the trace stays with the run afterwards. If you already know the SQL, Write the query myself makes one query part plus a synthesis and runs it straight away.

A run — the story and how it was made
An insight run: the story on broker performance with four figure tiles, a grounding check bar, and on the right the timed trace of each step — SQL generated, validated and tested, rows, narrative, draft summary, critique, actions, grounding check and rubric.
Every step is on the record with its timing and its pass. "Attempt 1 repaired" is a query the engine fixed on its second try, not a secret.

Checked without a model

Here is the part we would defend hardest. When the story is written, every figure in it is matched against the data — not by asking a model whether it looks right, but mechanically: against the cells of the rows the sentence names, against the step's totals and shares, and against the arithmetic between those rows (a difference, a ratio, a percent change). The result is a count on the page and a list of what did not trace.

flowchart LR
  F["A figure in the story
-$253.2M"] --> Q{"Quoted from the row
the sentence names?"} Q -->|yes| G["Grounded"] Q -->|no| T{"A total, average
or share of the step's rows?"} T -->|yes| G T -->|no| D{"Re-derived from those rows?
difference · ratio · % change"} D -->|yes| G D -->|no| E{"Found elsewhere
in the table?"} E -->|yes| W["Misplaced — flagged"] E -->|no| U["Not found — listed"] style G fill:#e6f7f2,stroke:#9fd8c9 style W fill:#fdf6ec,stroke:#e6c79c style U fill:#fdecec,stroke:#e8a0a0
Four ways a figure can earn its place, and two ways it cannot. No language model is consulted at any step.
The grounding check
The grounding check panel: 107 of 196 figures quoted straight from the rows, 32 re-derived from them, 16 found elsewhere in the table than the sentence puts it, 43 not found in any row, with the unresolved figures listed below.
The honest version of "AI-generated". The unresolved figures are listed, and publishing has to get past them.

A second pass critiques the draft against a short rubric — traceable, has a why, follow-ups, a benchmark, the right grain, actionable — and the run's score is shown next to its title. A story can be fixed in chat ("lead with the biggest mover"), and the fix is re-verified: the claims keep their figures, the grounding is recomputed, nothing is invented in the edit.

Drafts, then versions

There are two populations of insight, and the line between them matters for governance. The catalogue is what the agent proposed; it is never edited. The library is what people own: Build on this copies a proposal into your draft; drafts are run, edited in a builder (one part at a time, with a "test this part as edited" button), and eventually published as version 1. Publishing again makes version 2; earlier versions keep serving until someone moves.

What publishing refuses

A run whose figures do not all trace to rows is not published on a click. The refusal lists the figures. A person can insist — Publish anyway — and the version records that untraced figures were accepted and by whom. A run with no completed result cannot be published at all, and neither can a catalogue proposal.

Members do not publish; they request a publish, and a curator or admin approves it from the catalog, with the same checks. That is deliberate: the person who built the story and the person who decides it may carry the company's name are usually different people.

Served, not screenshotted

A published insight is an endpoint. GET /api/v1/insights/broker-performance describes it; POST /api/v1/insights/broker-performance/results with a scope — the whole book, one broker, one region — returns the story, its tiles, its sections with rows and chart, its claims and its grounding. Each claim can be drilled into and asked about. There is an OpenAPI document per insight, and a versions list, so a consumer can pin.

Insights → Published
The Published insights page: each insight with its version, slug, the story and results endpoints, last run status, and actions to open the API document, the last run, or make a new version.
Each row is a contract: a slug, a version, two endpoints, and the run that stands behind it.

Three things sit on top of that contract:

  • Collections. An ordered set of insights — a Monday pack, a broker review — published as its own version and pulled in one call at one scope. A collection's health page says whether each member is still serving its latest version.
  • Notebooks. A document over insights: a cell is an insight, a part or a single claim with a snapshot of what it showed; Refresh re-takes the snapshot through the same API a host application would use. No SQL runs in a notebook, so nothing in one can drift from what was verified.
  • Dashboards. A panel can be an insight part, with its narrative underneath and a link back to the insight — the same figures, served the same way.
A collection — what consumers see
A collection page: two insights in order, each showing which version it is serving; a panel saying what consumers see with the health of each member; releases with notes; and the three calls a host application makes to pull it.
The draft and the published version are shown apart. Reordering changes the draft; a host application's screen moves only when someone publishes.

What it does not do

It does not make a model the arbiter of truth; the verifier is deterministic and the model's job is to author. It does not fill a gap in your data with a plausible number; a part that cannot be answered says so and the story works around it. It does not publish a catalogue proposal as if a person had vouched for it. And it does not yet re-parse the SQL to prove an agreed metric's formula was used verbatim — the run records that the definition was offered and the verifier checks the figures, which is as far as we will claim.

The principle

An insight is an argument, and an argument is only as good as its evidence. Generate freely; verify mechanically; publish deliberately; serve by version.

Read next

Describe a dashboard. Watch it build. — the same engine writing one data API per panel, live, from a sentence. And A catalog with a ledger — who may call what you published, and who approved it.