Pressentable Alpha

No. 001 A journal of plain reporting

The blog

The client reporting mistake nobody writes about.

The data gets updated. The words around it do not. Why that is the reporting mistake nobody lists.

The reports I produced were always for clients. Monthly, most of the time. And sometimes not optional at all, because when the report is regulatory, not producing it is its own kind of trouble.

The failure I want to talk about is not a typo. It is a number that never got updated from the last production. A paragraph that went missing. A line still describing the month before.

Every list of client reporting mistakes covers something else: vanity metrics, too much data, cherry-picking the good results, the wrong cadence, no summary of the work. All real, all judgment calls, all about what you decided to put in the report. None of them is about the report being mechanically wrong.

At one report a month, that is embarrassment. When the production scales to thousands, it multiplies, and it arrives under pressure.

Mostly the client catches it. Sometimes the salesperson who has to send it, sometimes a manager. Then the second problem starts, and it is worse than the catch: you do not know how many reports are impacted. Finding out is hours of investigation, sometimes days.

The price depends on what you report. It can be a correction email. In regulated reporting it can be a fine of a hundred thousand and up, a client who stops trusting the numbers, and a small collapse of the business behind it.

The client reporting mistakes everyone lists, and the one they don't

I read the three pages that rank for this. One is four and a half thousand words and twenty tips. One is five mistakes. One is twelve. Between them they cover metric choice, transparency, frequency, formatting, length, and how to talk to the client about the results.

Not one covers this: the data was updated and the text around it was not.

The closest anyone gets is a note about copy and paste, and it is filed under time. An agency owner walks a vendor through his process: cut and paste the data every month from three analytics tools into a spreadsheet. The cost attached is hours. The cost of shipping the wrong sentence to the client is not mentioned on any of the three pages.

That is the same gap as the one under automating reports: the whole category treats a client report as a data problem. Connectors, refresh, scheduling, delivery. The document is treated as already solved, so the failures that live in the document belong to nobody. The other one I have written about is how the thing looks. This one is whether it is still true.

Why the numbers update and the words don't

Open a report you produce monthly and look at the things that are true only for one month. The figures, obviously. Then the headline. The opening paragraph that says which way the trend went. A table title with a month in it. The commentary under the chart. The date in the footer. Some of those sentences contain a number spelled out in words.

Now update it. The figures come from somewhere: an export, a query, a paste. They move together, because they arrive together. The sentences do not arrive from anywhere. They sit in the document from last time, and nothing in the document knows they were about the old numbers.

That is the whole mechanism. The text is data dependent and nothing marks it as data dependent. A spreadsheet cell knows it is a formula. A paragraph does not.

So the update ritual becomes a memory exercise. You remember the six places, or you do not. On a good month you catch it in the reread. On a bad month you have three reports due the same week.

And the reread is the only check that exists. I wrote this before I had a product: there is no way to check that something is missing, except reading the actual doc. That sentence was about my own frustration with BI tools. It turns out to be the exact description of this failure.

Nobody owns "is this report still correct"

When the batch goes out, the honest state in most teams is a hope: hope that no formatting or wording issue reaches the client. The data producer and the data consumer ask each other whether the data is fine, and each assumes the other is responsible. Nobody is. There is nothing in the middle.

Correctness of the document is not anybody's job title. The analyst owns the analysis. The data team owns the pipeline. The account manager owns the relationship. The line that still describes last month belongs to whoever reads it last. And once it has reached a client, the question behind it has no owner either: how many of the others are wrong.

What we built instead: a document you can compare

This is the part where I have to describe what Pressentable actually is, because the fix is structural and it will sound like nothing otherwise.

A Pressentable report is not a file you edit. It is a config: one JSON document that declares the page, the grid, the design tokens, the data, and the blocks. Config in, document out. The engine underneath is called Grillage, and everything below is about one property it has.

Run the same config twice, on any machine, and you get the same bytes.

That sounds like a detail. It is the entire fix, so it is worth showing what it costs to have. Floats are banned inside the engine: if you have ever written code that touches money you know floats are the enemy, tiny roundings compound into numbers that are simply wrong, and layout has the same disease. Half a pixel of rounding per line and page twelve breaks somewhere else on another machine. So the geometry is integer, fixed point, computed in BigInt. The engine parses the font file itself rather than asking the browser how wide a number renders, because text measurement is layout, and if the engine does not know exactly how wide "1 234 567" is, the browser decides. The line breaker is fifteen lines of greedy first fit and its fit test is integer smaller or equal to integer, no epsilon, no tolerance. The grid never computes cell widths, only edges, so rounding cannot accumulate across columns.

All of that exists for one reason: an output nobody can compare is an output nobody can check.

Because the output is exact, it can be hashed. The document gets a fingerprint, a four line hash from 1991, fnv1a64. One byte of drift and the diff is spotted. The serialization is canonical, so the comparison is about content and not formatting: reformat the JSON of a config and its hash does not move.

Which turns the question of this whole post into a mechanical one. "Did anything change between last month's report and this month's" stops being a proofread. It is a comparison of two fingerprints, and the parts that changed are the parts that changed.

A fingerprint comparison also does not care how many documents there are. That is the property the investigation problem needs: a thousand comparisons of two short strings, where today there is a reread. Turning that property into something you can actually run over a batch is the last section of this post, and it is not shipped.

The same discipline runs against ourselves. There is a test in the engine named "painter must not guess fonts". Layout output is plain data, so the tests assert exact equality, with no tolerance band anywhere. I try not to ship visual testing with tolerance thresholds, where the diff is allowed to drift a little, because that band is exactly where the bugs live. The committed reference documents are compared byte for byte on every push, and they were generated on my Mac and are re-rendered on Linux in CI. Every green pipeline since the twenty second of July is that cross check.

One concrete case. I rebuilt a full invoice as a config: twenty four blocks, eight hundred and seventy five individually positioned glyphs, layout and paint in under a millisecond. Rendered twice, the scene hash is the same both times. It is committed, so from a fresh clone the test suite re-renders that invoice and byte compares it, on every run, forever. Any change to the engine that moves one glyph of it fails the build.

And when a config is wrong, it does not render badly. It does not render. The validator returns every error in one pass, each with a path: a block placed outside the grid, a color token that was never declared, a font size that is not an integer. Three errors, one pass, before anything is drawn.

What ships today, and what doesn't

Pressentable is in alpha, so here is the honest line between the two.

Shipped: the report as a config, validated before it renders. The re-render, which is the piece that matters for this post: change the numbers, save, and the document comes back with the structure untouched. The determinism and the hashing underneath it.

Not shipped: the binding that would make a sentence know it depends on a number. Today the text in a report is content you write, the same as anywhere else. In the invoice I just described, the amounts are typed, not computed. The data section of that config is empty.

Also not shipped: running any of this over a batch. One report is not a thousand reports, and the case where a wrong line has already gone out and nobody knows how far it spread is exactly the case that needs the batch answer.

So the honest claim is narrow. Determinism removes the half of the problem where the document drifts on its own, and it gives the exact comparison that a manual reread cannot give. The other half, marking every sentence and title that a data swap touches so nothing can go stale silently, and answering it across a whole production, is designed and is not code. It is the next thing we build. It is also the half nobody else in this category is building, which is why I am comfortable saying it out loud before it exists.

The same report, two months

Here are two editions of the same client report: July and August. Same config, same structure, new numbers. Nothing in the second one had to be remembered.

Pressentable is in alpha and priced like it: twenty founding seats at $15 a month, at a price that never rises.

The same rules on a live page

The August edition of the report this post ends on. Nothing in it had to be remembered.

Same config as July. No line still describing the old month.

Read the live report

Where this fits

Typesetting is the second decision. The first is the shape of the report itself: what goes in it, in what order, and what the client is meant to do after reading it.

The guide it belongs to Client reports your clients actually read The five-part client report format that wins renewals, with a free CSV template and a report published in the open.

Keep reading


From CSV to this in minutes

Upload the file. Fill the blocks: text, the KPI tiles, the chart, the table, your logo at the head of the page. Export a typeset PDF for the inbox, or publish a live link.

Founding members publish live links and print without our mark. See the founding offer.