AI writes the analysis now — fast, and confidently wrong when it’s wrong. TraceBi turns every report into code: an agent authors it, a human approves it, guardrails refuse the bad numbers, and a receipt proves the rest reproduce.
The problem
An agent generates reports faster than anyone can check them, and a confident wrong number reads exactly like a right one. A BI tool can’t govern that — you can’t diff a dashboard in a pull request, review its measure logic, or make it refuse a bad aggregation. The metric hides inside the GUI; the “source of truth” is a screenshot.
TraceBi makes the report code instead: the connectors, the star schema, the measures, and the figures are all Python and JSON in your repo — the one place an agent’s work can be reviewed, versioned, tested, and governed like any other pull request.
Everything that makes a number is in the repo, reviewable, and governed.
Built for agents · controlled by humans
TraceBi is built for the analyst that is now an agent — and for the human who stays in control of what it ships. Both author the same code against the same contracts; neither gets a shortcut around review or the receipt. This is a first-class design goal, not a plugin.
the agent builds
It works from a clean structure, never a blank canvas. tracebi init scaffolds the three-folder project; tracebi context hands the agent the exact vocabulary that exists — the facts, dimensions, measure kinds, and operators — so it never guesses; and a report is structured slots to fill against a closed, declarative grammar. There is a right shape for the work, and the framework shows the agent where it is.
Good practice is baked into the environment, not a prompt — a curated knowledge base it reads, cited by the errors — and it drives the whole loop over the MCP gateway.
The silent-wrong patterns are refused by construction, so even a weaker model can’t push a confidently-wrong number through.
tracebi verify mechanically re-runs an agent’s overnight reports and localizes any drift to its source.
the human controls
The agent’s output is a diff — a measure on the model, a figure binding — a person reads in one screen and approves, edits, or rejects. You hold the merge button.
It’s plain Python and JSON. Rename a measure, retune a band, rewrite a section — no black box, no round-trip through the agent to fix a typo.
The gateway has no write access to your warehouse — the only thing the agent produces is its rendered artifact and receipt. It can check everything and rewrite nothing you depend on.
The spine · three phases, two freeze points
The slow, unconstrained analysis and the fast, iterated reporting never block each other — because the model between them is a frozen contract. Editing a report never re-runs the pandas; the page re-renders in milliseconds.
Ordinary, unconstrained pandas — pull the queries, clean, dedupe, parse. Then sink clean star-schema tables into a file-backed warehouse.
transforms/ → warehouse.duckdbA declarative star schema over the warehouse — grain, keys, measures — in a few dozen lines a reviewer reads in one screen.
models/ → the semantic contractA page where every KPI, chart and table cell is a live query against the model — no pandas in the loop.
reports/ → a self-contained artifactEach phase is a folder with its own cadence. The tracebi CLI scaffolds them, runs each one, and serves the result — no BI server to stand up.
See it as a user · the whole loop in five commands
From an empty folder to a shareable, self-verifying report — the real commands, and what each one gives you back. Step through it.
A three-folder project — one per phase — plus a working sample dashboard and an AGENTS.md that teaches an agent the whole grammar. It’s a repo from minute one: git init and commit.
This phase is just Python — the framework doesn’t constrain it. Do whatever the data needs, then sink clean star-schema tables into a file-backed warehouse. That sink is the contract; nothing downstream ever opens this script.
A few dozen declarative lines a reviewer reads in one screen — grain, keys, and named measures. This is the semantic layer: define wtd_spread or a per-sector rank once, and every report calls it by name.
Every figure is a live query against the frozen model, so the page renders in milliseconds — no pandas in the loop. The output is one HTML file with charts, data and styling all inlined. Edit the layout all day; it never re-runs the analysis.
The report you send carries its own working. verify re-runs the recorded queries and confirms the file wasn’t edited after the fact — the reproducibility guarantee that comes for free once every figure was a query.
The semantic layer · where the depth is
The heart of the framework is a real semantic layer: a metric is defined in one place on the model — reviewed in a pull request, versioned in git — and every report calls it by name. No more re-deriving “revenue” five different ways in five different scripts.
And the hard patterns that used to force a hand-written script are first-class measures. A whole concentration table is three lines:
# somewhere in report.py
df = q("select * from holdings")
df = df.sort_values("fv", ascending=False)
df["rank"] = range(1, len(df)+1)
df["share"] = df.fv / df.fv.sum()
# re-implemented per report,
# reviewed by nobody
Metric logic scattered across scripts — the definition drifts, and the next report reinvents it.
model.add_measure("fv_rank", rank="fair_value")
model.add_measure("fv_share", share="fair_value")
model.add_measure("fv_cum", running="fv_share")
# one definition, referenced by name
# from every report and every query
Reviewed once, versioned in git, called by name — the same number everywhere it appears.
the grammar
| ratio · share · rank · running | Ratios of totals (never a mean of ratios), % of total, concentration & Pareto tables. |
| median · stddev · p90 / p95 / p99 | The distribution and its tail — the worst marks a mean and even a median hide. |
| period_end | Semi-additive AUM / NAV — summed across funds, latest snapshot over time. Never double-counted. |
| offset · growth | Period-over-period — YoY, QoQ, MoM — as a measure, not a self-join. |
| to_date | YTD / QTD / MTD cumulatives that reset on the period boundary. |
| rank + partition_by · having | Top-3-issuers-per-sector — a governed query, not a spreadsheet pivot. |
| time grains · value bins · or / and filters | Group by month, by balance band; filter on this OR that across columns. |
A closed, declarative grammar — no free-form SQL to review, and every measure is data you can diff, validate before it runs, and generate.
Guardrails that teach
The framework’s cardinal rule is that it never returns a confident, silently-wrong total. So the model refuses the classic mistakes the moment you declare them, and the refusal names the fix — a guardrail is a lesson that fires when it matters, that no author (human or agent) can skip.
A per-row ratio averaged overweights small rows. Refused — even when the column name hides it — with a pointer to a ratio of totals. Truth was 1.05.
Summing a balance across snapshots triple-counts the same money. Refused, with a pointer to a semi-additive period_end.
A join that silently multiplies every additive measure is refused by default — a total never inflates by accident.
The output
The framework renders a report to a single self-contained HTML artifact — charts, data, and styling all inlined, no server, no CDN. It opens anywhere and emails cleanly. And because every figure was a live query, each one ships stamped with the query and a fingerprint of its bytes, so tracebi verify --file can confirm — offline — that nothing was edited after the fact.
A live demo of the mechanic — the real verifier hashes the embedded bytes. It proves a number reproduces and the file is intact; it doesn’t claim the number is “correct.”
Batteries included
Everything the workflow needs ships in the box — and it all runs from your machine, offline, no account.
CSV, SQL, DuckDB, BigQuery, Snowflake, in-memory — one BaseConnector interface, and lineage on every load.
Register medallion layers, run them upstream-first, and keep a run history — or hand the sink to the pipeline you already have.
Self-contained HTML with live charts, or an Excel workbook — from the same model, same measures.
Every operation returns a new dataset with a node appended — the audit chain can’t silently break.
tracebi init · dev · report · verify · serve — plus a FastAPI + React app over the same registry.
Code-first and open. No BI server to license, no data leaving your environment.