Skip to content

Metrics & analysis

Definitions in domain/10-metrics.md.


162. MetricDefinition

Every metric card requires one. There is no variant without a definition.

Renders the formula with the actual input values substituted, plus cohort, period, source, and caveat:

  Net Revenue Retention                                    104.6%
  ────────────────────────────────────────────────────────────────
  (Starting MRR + Expansion − Contraction − Churn) ÷ Starting MRR
  (  412,000    +  61,400   −    18,200    − 24,100 ) ÷  412,000

  Cohort:  customers active on 1 Sep 2026
  Period:  1–30 Sep 2026
  Source:  subscription ledger · excludes one-time fees
  Note:    new customers excluded by definition

"ARR" means four different things at four companies. This component is why the number in this product is not one of them.

Definitions are versioned. When one changes, historical values computed under the old definition are marked — an unannounced definition change looks like a business change.

163. NonGaapReconciliation

EBITDA, adjusted EBITDA, free cash flow, adjusted net income. Appears below net income in a separated block, never inside the P&L, with a line-by-line reconciliation and every adjustment named. "Adjusted EBITDA" with unnamed adjustments is not a number.

164. MrrBridge

Opening MRR → new → expansion → contraction → churn → closing, as a waterfall. A single MRR number with a percentage change explains nothing; the bridge is the primary view.

Renders GRR and NRR together or neither — NRR alone hides churn behind expansion. States that MRR is an operating metric that will not tie to the P&L.

165. BudgetVsActual

Actual, budget, variance (absolute and relative), and forecast, by period and by dimension. Respects polarity — a favorable expense variance is under budget. Materiality thresholds applied, with the threshold displayed. Reforecast versions are tracked so you can see what was believed when.

The variance is a view, not a column. Actual, Δ and Δ% are the same three figures asked three questions, and putting all of them on screen at once is how a budget review becomes a wall of numbers nobody reads to the end. The comparison is declared rather than positional: the actual column names budget as its base, so hiding the budget column to fit the screen does not silently re-point the variance at whatever ended up to its left.

Two things make this more than a three-column table, and both are here because a budget review goes wrong without them.

The cut. A favourable total is routinely two departments cancelling out. Engineering 1.6M over and sales 1.7M under reads, at the entity line, as 0.1M under — favourable — and says nothing about either. A variance report that can only be read at the entity level is one that will be believed for a quarter longer than it should be. Members are not required to sum to the entity — unallocated corporate spend sits outside them — and a vintage that restates an entity budget says nothing about one department's share, so under a cut the member's own budget stands rather than the component inventing an allocation nobody approved.

The vintage. The original plan and each reforecast are versions of one object, not "budget" plus "reforecasts": model a reforecast as a different kind of thing and "what did we believe in February" stops having a single answer. A vintage states only what it changed. The comparison base is chosen out loud in the toolbar, and the walk between versions sits under the grid, because a reforecast that quietly replaces the plan makes every past month look better than it was called at the time. September beating the version in force while missing the one the board saw in November are both true readings; a component that picked one silently would be picking the flattering one.

166. ForecastTable

Driver-based projections with scenarios. Projections render dashed on every chart and carry a "forecast" label on every figure. Actuals-to-date and forecast-remaining are visually distinct within the same period.

The boundary between what happened and what is believed is drawn in three places, not one.

Between periods. Everything after the last closed period is shaded, ruled with a dashed edge, set in the secondary tone, and headed by a column that says forecast under its label — so every projected figure on screen sits beneath the word rather than merely near it.

Within a period. The month that has started and not finished is not one column; it is three — what has posted, what is expected for the rest, and the landing they sum to. A part-elapsed month reported as a single figure sits beside eight closed ones and inherits their authority, which is the difference between "we are at 41.2 with nine days to go" and "we did 58.2" — the second of which nobody said and everybody quotes. The landing is computed from the other two, never supplied: a caller who provides all three will eventually provide three figures that do not add up, and the grid would render the contradiction without noticing it.

Between scenarios. The scenario in force is named in the caption and in the toolbar. It will not rank them and will not style one as the good case — an unlabelled forecast is indistinguishable from a claim, and a ranked one is a recommendation wearing a forecast's clothes.

167. CovenantMonitor

Per-covenant: the contractual definition (lenders define EBITDA with specific add-backs), the computed value, the threshold, the headroom, and the next test date.

The number here may legitimately differ from the dashboard's EBITDA because the credit agreement defines it differently. That's not a bug, and the component says so on the card.

Headroom under 20% is --warning; a breach is --negative — with the numeric headroom always shown, never color alone.

168. KpiHealthStrip

The dashboard's top row: liquidity, profitability, and cash metrics with polarity, comparison basis, and definitions. Metrics derived from unreconciled data are marked provisional — a KPI computed mid-close, before the bank is reconciled, is an estimate, and labeling it is what stops people quoting it externally.


174. RevenueTracker

Revenue by stream, period by period, with the four questions an operator actually asks: how much this period, versus what, from where, and how concentrated.

The summary tiles are computed from the same array the grid cells are drawn from — there is no second data path, because a header KPI that disagrees with the table beneath it is the fastest way to lose a reader. Total revenue is summed here rather than accepted as a prop for the same reason.

Month-on-month and year-on-year, together: a month-on-month read of a seasonal business is noise, and a year-on-year read of a fast-growing one is stale.

The run-rate tile is labelled a projection from one period. Multiplying a month by twelve is not ARR — it takes no view of churn, seasonality or contract length — and the tile says so rather than letting the figure travel into a deck as though it did.

One stream over a third of revenue is flagged in words. Concentration is a fact about the risk in the business, not about the month.

175. ExpenseTracker

Spend by category on three bases — actual, budget, variance — because they are the same categories asked three questions, and a nine-column grid of all of them is one nobody reads past the third column.

Every row carries a polarity, and the component derives it. An increase in spend is not good news; under budget is favourable and over budget is not. A grid that paints a rising cost green has told the reader the opposite of the truth.

Polarity describes the figure as stored, not the concept behind it, and that is the easiest thing here to get backwards: this system holds costs as negatives, so a heavier month is a figure moving down and the row wants standard. A model holding the same costs as positive magnitudes wants inverted. ExpenseTracker takes a sign and works it out itself rather than asking its caller to reason about a storage convention to get the colours the right way round.

Committed-but-unbilled is shown beside the total, never folded into it. A purchase order is not an expense — accruing it silently is a cutoff error, and showing it nowhere is how a category with six figures of open POs looks under budget right up until the invoices land.

The movers list ranks by amount, not by percentage. A line that doubled from 400 to 800 is not the story of a month in which another went from 180,000 to 240,000.

176. DriverModel

The inputs a forecast is built from and the outputs they produce, in one grid with the boundary between them visible. Three separations carry it: actual from projection, input from output, and assumption from arithmetic.

Inputs are typed; outputs carry the ƒ mark and their formula. A model where you cannot tell which is which is a model where nobody can find the assumption that moved the answer.

Only projected inputs accept editing. An actual is what the ledger posted, and a model that lets you type over it will eventually be used to explain a month that never happened.

The named assumptions sit under the grid in words — "new logos hold at 14 a month" — because that is the sentence someone can disagree with, and disagreement is the point of a forecast review. A growth rate buried three rows into a projected column is not.

177. ScenarioCompare

Scenarios in columns, measures down, every column measured against the base — the comparison is declared rather than positional, so it survives the reader reordering or hiding columns.

It will not rank the scenarios. No best-case styling, no sorting by outcome. A scenario is a set of assumptions, and presenting the largest number as the good one is how a forecast review becomes a negotiation about which column to show the board.

It will not hide a breach. A measure carrying a limit — a covenant floor, a minimum cash balance — names the scenarios that cross it in words under the grid. A downside that breaches a covenant is the only thing on the page that matters, and it is not left as a cell someone has to notice.

178. PlByDimension

The profit and loss cut by department, entity, product or region rather than by period. Same lines, different question — and the second one is what the department head is in the room to ask.

Total is a column and it is computed from the members beside it plus whatever is unallocated, never supplied separately. A total that does not equal its parts loses the room.

Unallocated is its own column, not a rounding and not an allocation. Corporate costs that belong to no department exist, and spreading them on a rule nobody agreed to is how a department P&L becomes something its owner cannot argue with.

The 12 components specified here.

Built against this document

The surfaces that implement what this argues — running, computing, or shaped by it.

Told when this spec changes

The docs are the contract the components are built against. When one changes, the component changes with it — and the copy in your repo does not. One email when that happens.

One address, no list rental, unsubscribe in one click.

Works in your QuickBooks Online or Xero file

Get your books closed every month

Lopifi is building AI bookkeeping that keeps your books closed on US GAAP as transactions happen, with a CPA review every quarter, for $200 a month. We’re not open yet.

$200/month when we open. No card now.