Learn
Report & fact IDs
A number you cannot check is only a claim.
How every number in the report carries an ID that opens its SQL, its inputs and the rows it came from.
Stage 8 of 10. RENDER writes report.json (the verified data) and report.html (a self-contained page you can open offline).
The problem it solves
Section titled “The problem it solves”A report is only as useful as your ability to check it. “Revenue fell 17.9%” in a slide is a claim; you would have to redo the work to trust it. Here every number sits next to a short ID, a fact ID, and the ID opens the exact computation behind it: the SQL and its parameters, or the function and its inputs, down to the spreadsheet rows.
What happens on the example
Section titled “What happens on the example”RENDER · the coffee-roaster run
In
- verified claims16
- facts101
RENDER
- 1 · Fill the placeholdersEvery {{N…}} in the text becomes the fact's stored value, formatted for reading.
- 2 · Attach the chipsEach number keeps its fact id, which opens its SQL or function and its rows.
- 3 · Rank the actionsRecommendations carry an impact range made of facts and a check to run later.
From the verified report.json of the run.
The top finding, Revenue fell after March, driven by wholesale, as the report states it. Each figure is followed by its chip; a chip with a check mark was recomputed by the verifier. Click one: it jumps to its row in the ledger at the bottom of this page.
- Revenue went from $44,100N12 in 2026-03 to $36,200N11 in 2026-04 (a change of −$7,900N29, −17.9%N30). It stood at $37,000N17 in 2026-06. observed
- By channel, “wholesale” fell $8,500N149, more than the whole drop; “retail” rose $600N151. observed
- By product, the price effect was $1,181.91N175, the volume effect −$1,911.07N173 and the mix effect −$7,170.84N174. observed
- The hotel customer paused its standing order because of the new price list. document_supported
email-0412.eml: “After the new price list we are pausing our standing order while we compare suppliers.” - The wholesale price increase took effect shortly before the drop. correlated
price-list.pdf: “Wholesale prices increase 12% from 15 March 2026.” - The drop coincided with the price increase. correlated
The top recommendation: Offer wholesale accounts a volume-tier discount to win back paused standing orders, expected impact $3,995N350 to $7,191N351, next quarter. The impact is a scenario computed in code from facts and bounded assumptions (how much of the lost wholesale might come back, at what discount), never a number the model made up.
Following a chip down. The headline change −17.9%N30 is pct_change of two SQL facts. From the terminal, aa explain shows the same thing the chip opens:
$ bun run aa explain <job-id> N30
N30 = −17.9% (I5 trend_break.v1 revenue 2026-04:change_pct)
function: pct_change
N11 = 36,200 (revenue_2026-04)
N12 = 44,100 (revenue_2026-03) and one of its inputs, with the SQL, its parameters and the first input rows:
$ bun run aa explain <job-id> N11
N11 = 36,200 (revenue_2026-04)
definition: Revenue = sum of amount where status is empty or status is not refunded, per month, in USD (assumed)
sql:
WITH m1 AS (SELECT strftime("date", '%Y-%m') AS period, sum("amount") AS value FROM "t_t1" WHERE NOT _is_total AND "date" IS NOT NULL AND (("status" IS NULL) OR ("status" NOT IN ($1))) AND strftime("date", '%Y-%m') BETWEEN $2 AND $3 GROUP BY ALL)
SELECT period, value FROM m1 ORDER BY period
params: {"$1":"refunded","$2":"2026-04","$3":"2026-04","input_rows":1020}
input rows from T1 (20 shown):
{"_row":3095,"_is_total":false,"_period":"2026-04","date":"2026-04-01T00:00:00.000Z","order_id":"CR-03094","customer":"Shop counter","channel":"retail","product":"Filter blend 250g","qty":2,"amount":32.9,"status":"paid"}
{"_row":3096,"_is_total":false,"_period":"2026-04","date":"2026-04-01T00:00:00.000Z","order_id":"CR-03095","customer":"Online store","channel":"retail","product":"Filter blend 250g","qty":1,"amount":20.98,"status":"paid"}
{"_row":3097,"_is_total":false,"_period":"2026-04","date":"2026-04-01T00:00:00.000Z","order_id":"CR-03096","customer":"Online store","channel":"retail","product":"Filter blend 250g","qty":1,"amount":35.88,"status":"paid"}
… (7 more lines) Click a citation to see what it points at (from the build’s own run of the example):
Click a citation · Revenue fell after March, driven by wholesale
Every number above is a fact computed by SQL; every citation opens what it points at.
The ideas behind it
Section titled “The ideas behind it”Provenance
Section titled “Provenance”Provenance is the record of where something came from. Every fact has it (its computation and cited rows), every quote has it (file, page, character span), every web statement has it (URL and fetch time). A claim without provenance is not allowed in report.json.
Placeholders, filled by code
Section titled “Placeholders, filled by code”The text in the report was written with {{N12}}-style placeholders. Rendering replaces each with the fact’s stored value, formatted for reading: rounding happens only on screen, the stored value keeps full precision. That is why the report and aa explain always agree.
One file you can keep
Section titled “One file you can keep”report.html carries its own data and script, makes no network requests, and works from a USB stick. report.json follows a published schema, so other tools can read it.
Try it
Section titled “Try it”Run the first tutorial, then open the report and click any number:
AA_DEMO_MODEL=scripted bun run aa analyze examples/coffee-roaster --yes --embedder hash --today 2026-07-01 --currency USD --openIn the code
Section titled “In the code”packages/report/src/render.tsx:report.html.packages/report/src/text.tsandformat.ts: filling placeholders, formatting for reading.packages/schema/src/report.ts: the report schema (Zod, with JSON Schema generated from it).packages/analysis/src/impact.ts: impact scenarios.- The spec:
docs/specs/report-schema.md.
Go deeper: Your first report and the report schema.
Further reading
Section titled “Further reading”- W3C, PROV-Overview: a standard vocabulary for provenance.
Facts on this page
Section titled “Facts on this page”| Fact | Value | What it is | How it was computed | Verified |
|---|---|---|---|---|
| N11 | $36,200 | revenue_2026-04 | SQL, run in DuckDBWITH m1 AS (SELECT strftime("date", '%Y-%m') AS period, sum("amount") AS value FROM "t_t1" WHERE NOT _is_total AND "date" IS NOT NULL AND (("status" IS NULL) OR ("status" NOT IN ($1))) AND strftime("date", '%Y-%m') BETWEEN $2 AND $3 GROUP BY ALL)
SELECT period, value FROM m1 ORDER BY period parameters: | recomputed |
| N12 | $44,100 | revenue_2026-03 | SQL, run in DuckDBWITH m1 AS (SELECT strftime("date", '%Y-%m') AS period, sum("amount") AS value FROM "t_t1" WHERE NOT _is_total AND "date" IS NOT NULL AND (("status" IS NULL) OR ("status" NOT IN ($1))) AND strftime("date", '%Y-%m') BETWEEN $2 AND $3 GROUP BY ALL)
SELECT period, value FROM m1 ORDER BY period parameters: | recomputed |
| N17 | $37,000 | revenue_2026-06 | SQL, run in DuckDBWITH m1 AS (SELECT strftime("date", '%Y-%m') AS period, sum("amount") AS value FROM "t_t1" WHERE NOT _is_total AND "date" IS NOT NULL AND (("status" IS NULL) OR ("status" NOT IN ($1))) AND strftime("date", '%Y-%m') BETWEEN $2 AND $3 GROUP BY ALL)
SELECT period, value FROM m1 ORDER BY period parameters: | recomputed |
| N29 | −$7,900 | I5 trend_break.v1 revenue 2026-04:change | delta N11 N12 | recomputed |
| N30 | −17.9% | I5 trend_break.v1 revenue 2026-04:change_pct | pct_change N11 N12 | recomputed |
| N149 | $8,500 | contribution.v1 revenue 2026-04 by channel:magnitude:wholesale | abs N144 | recomputed |
| N150 | 1 | contribution.v1 revenue 2026-04 by channel:exceeds:wholesale | abs_exceeds N144N146 | recomputed |
| N151 | $600 | contribution.v1 revenue 2026-04 by channel:magnitude:retail | abs N145 | recomputed |
| N173 | −$1,911.07 | pvm.v1 revenue 2026-04 by product:pvm:volume | pvm.v1 N115N163N116N164N117N165N118N166N119N167N120N168N121N169N122N170N123N171N124N172 | recomputed |
| N174 | −$7,170.84 | pvm.v1 revenue 2026-04 by product:pvm:mix | pvm.v1 N115N163N116N164N117N165N118N166N119N167N120N168N121N169N122N170N123N171N124N172 | recomputed |
| N175 | $1,181.91 | pvm.v1 revenue 2026-04 by product:pvm:price | pvm.v1 N115N163N116N164N117N165N118N166N119N167N120N168N121N169N122N170N123N171N124N172 | recomputed |
| N350 | $3,995 | scenario impact.v1:recover_share:impact_low | impact.v1:recover_share N144N349N347 | recomputed |
| N351 | $7,191 | scenario impact.v1:recover_share:impact_high | impact.v1:recover_share N144N349N348 | recomputed |