The revision chain stops being a total

The engine has known what every revision of a PDF wrote since 1.20.0. It was publishing the sum, and a sum answers a different question than the one a reviewer asks.

Reference: Incremental update vs full rewrite

Engine 1.32.0 contains no new read, no new family and no new detection. What one revision of a PDF wrote, counted by kind and narrowed to what a page can reach, has been computed for every revision of every document since 1.20.0. Until this release it left the engine only as a sum.

Now two evidence blocks carry it per revision, both numbered from 1, where revision 1 is the original save:

The two new keys, on families that already fire

incremental-updates.evidence.changedPerRevision[].touched
signature-permissions.evidence.revisionsAssessed[]

The sum failed differently in each family

signature-permissions added its appended revisions together and reported the total. So a report could say this certification was broken and never by which of the four appends. On a document with one append those are the same sentence. On a certified contract with six appends, five of them permitted form fill-ins, they are not, and the reviewer holding the report has to go find the one that mattered by hand.

incremental-updates failed the other way. It published object numbers and a kind classification for the objects a revision replaced, which is blind to a revision that only adds: a new annotation, a new page, a further signature. A permitted form fill-in produced a row carrying a list of object numbers and an empty kind list, which is exactly what a revision that did nothing worth naming also looks like. Two different revisions, one of them interesting, indistinguishable in the output.

The rows cannot contradict the headline, and that is arithmetic

A per-revision surface invites one specific defect: a document-level verdict that no row underneath it supports. The reason this release does not need to fear it is that the per-revision verdict is the same pure function the document-level one has been since 1.24.0, applied to one revision's counters instead of the total. Every branch of that function turns on a counter being non-zero, and a sum of non-negative integers is non-zero exactly when one of its terms is. So the document-level assessment is always the strongest of the per-revision ones, by construction rather than by review.

It is still checked, because "by construction" is a sentence and sentences rot: test/engine/revision-detail.test.ts sweeps every PDF fixture in the repository, collects every violation before asserting, and fails if any document's headline ever outruns its rows.

Seven counters partition. The eighth narrows. I summed eight.

Each row carries eight counters. Seven of them (pageContent, images, pages, widgetAnnotations, otherAnnotations, signatures, other) partition the revision's total writes: they sum to objectsWrittenTotal, every object in exactly one bucket. The eighth, pageImages, is not a bucket beside them. It is a subset of images: of the images written, how many a page actually draws.

I wrote the first version of the sweep test summing all eight, and certified-page-image-added.pdf failed it immediately: 3 classified against 2 written, because the one page-drawn image had been counted in images and again in pageImages. The fixture was right and the test was wrong, which is the better order to discover it in. It is also the same distinction a full corpus run charged engine 1.24.0 to learn in the opposite direction: an image was written and a page draws it are different facts, and neither substitutes for the other. The OpenAPI description and the architecture doc now state the subset relation in the same breath as they offer the numbers, because a consumer who totals the eight will otherwise rediscover my bug in production.

Counts, not a diff, and the report says which

The boundary is worth stating as plainly in a post as the release states it in the schema: these are counts of objects by kind. Nothing in this engine renders two revisions and compares them.

"Revision 3 rewrote one page content stream and added two form fields" is what is known, and it is a lot: on a certified document there is no reading of that row compatible with nobody touched the page. "Revision 3 changed the amount from 1.200,00 to 12.000,00" is not known, and producing it is a different capability with a different cost, no measurement behind it and no fixture population. A competitor in this space sells "versioning that shows exactly what changed" and publishes no measurement of any kind. This release is the half of that claim that is true, labelled as which half it is.

What else moved

Nothing. No severity, no score, no band; both additions are new keys on families that already fire, and every existing evidence key keeps its shape. The evasion bench re-ran anyway, because its own rule is that a version bump without a re-run means nobody checked: 92 fixtures, seven evasions, and the recorded summary came back identical to the 1.31.0 record in every field except the version it stamps.

Walk a chain yourself

The fastest way to build an intuition for revision chains is to look at one from a file whose history you know: a contract you signed digitally, a form you filled, a statement you downloaded. The revision tool draws the chain, and since 1.32.0 the report's evidence carries the per-revision rows this post is about.

See a PDF's revision history: free, nothing stored, and the file is parsed in memory and discarded with the response. A signed document with three appends is normal. The interesting question is what each append wrote, and now the report answers it.