What this engine gets wrong, and how we know

False positives on documents nobody curated, recall broken out per forgery operation, and one measurement whose answer was “nothing”. Every figure names the engine version and the date it was taken.

Most vendors publish an accuracy figure with no denominator. This page is the other half: how often this engine is wrong about a genuine document, how often it catches a forged one broken out by operation, and one case where it catches nothing.

Its companion is the measured evasion matrix: what each laundering step actually defeats, every cell dated and reproducible from a checked-in bench. A two-step launder takes a forged document from 70 to 10; a raster flatten quiets 34 of 35 loud files.

How to read a number on this page

Everywhere else on this site, a guide page is forbidden from carrying a number that will go stale: dated claims belong on the blog, where the date bounds them. This page is the deliberate exception, and it is contained in two ways rather than left to good intentions.

  • Every figure carries the engine version it was measured on and the date. A rate measured on 1.17.0 is a claim about 1.17.0. Where a later release moved it, both numbers are here and so is the release that moved them.
  • A test in CI holds this page to the shipped engine. It asserts that the version this page calls current is the version in the engine's own source, so an engine release cannot ship without someone having come back and re-read what is written here. The engine running in production today is 1.37.0. That release added the nineteenth signal family, extraction-anomalies: pages whose extracted text cannot be trusted to be the text a reader sees, measured on 5,168 published documents before any severity shipped (docs/CORPUS-PHASE0-EXTRACTION-2026-08-31.md in the repository). The release before it, 1.36.0, changed nothing in the engine: it is a security-review batch, cut as a release only so the fixes reach the published MCP package on npm. That batch locks recovered redaction text that is shaped like a prompt injection out of inspect_document's reply, adds an optional TAMPERLENS_ALLOWED_DIRS read allow-list that confines a local path to named directories, and hardens the compare_documents upload; the billing webhook, which is server-side and outside the engine, now gates fulfilment on Stripe's payment_status. Every report is byte-identical to a 1.35.0 one; the version moved only because the published MCP package is pinned to the engine version. The release before it, 1.35.0, also changed nothing in the engine: it gave the MCP server's single-file tools a url alternative to a local file path, so an agent can point them at an http or https link and the server fetches the document over its own machine's network before sending the bytes on. Before that, 1.34.0 added a measured I/O cost block to every report and a cheap ?mode=triage scope for agents deciding whether a document is worth a full inspection. It was measured on the same 5,168-document corpus first: triage expands 9% of a full parse's decompressed bytes, reproduces the structural, metadata and signature families count-for-count, and is silent on the page-content families, which the report's triage block states plainly, never left to be inferred. It moved no signal severity and no specificity figure below, and each number there stays attributed to the version it was taken on. Before that, 1.33.0 added two detections to the active-content family: a dictionary name concealed by a hex-escape, and the /AA additional-actions key, each measured on the same corpus before it was given a severity. 1.31.0 folded three Office signal families to one signal each, which the report invariant had always said they were; it changed no PDF and no image behaviour either, and every figure below is a PDF-corpus figure. Each number stays attributed to the version it was taken on. 1.30.0 changed how revisions are counted when an appended trailer omits /Prev, the PNG chunk cap, the generator-name patterns and the boleto due-date factor, and whether any document in the two populations carries the /Prev-less shape has still not been checked.

Two words are used precisely throughout. A measurement means the engine was run over the files. A projection means the scorer was replayed over reports that were already stored, which is weaker, and it is labelled as such every time it appears.

A projection never re-reads the document: it re-scores a report that already existed, so it cannot discover anything the first run missed.
MEASUREMENT · THE ENGINE RAN OVER THE FILES the document bytes parser · signals scorer a band PROJECTION · ONLY THE SCORER RAN AGAIN the bytes are gone: the corpus rules delete them scorer a band the stored report is the input Same scorer, weaker claim: a projection can restate a finding, and can never find one.

False positives, on 1,728 documents nobody curated

Every corpus this engine had before August 2026 was one we chose: court filings, public procurement contracts, bank statement samples, and a rate computed over documents you picked is a fact about the picking. So two populations nobody here assembled were measured instead:

  • 985 US .gov PDFs from the Library of Congress “1000 .gov PDF dataset” (CC0).
  • 743 Brazilian web PDFs drawn from evenly spaced Common Crawl index blocks, across 421 Brazilian hosts.

1,728 documents, 0 parse failures. Every one was published by the organisation that made it, so each is presumed genuine and every score above low counts against us. The measurement is dated 4 August 2026 and it is what forced the engine 1.17.0 re-specification: four signal families were gated or re-scoped because they were asserting “changed after creation” about files whose own revision count says they were written exactly once.

Nothing in this population is forged, so the engine has no way to be right except by staying quiet: every band above low is an error and there is no correct-detection column to offset it.
THE POPULATION 1,728 documents each published by the organisation that made it: presumed genuine WHAT THE ENGINE SAID low the only correct answer available on this page elevated · high counted against us, whatever the reason THE ROW THAT IS MISSING HERE Forged documents. A specificity number has no true-positive cell in it, that half is measured separately, on built positives. A misfire with a perfectly innocent cause is still a misfire: a queue does not care why it filled up. This is why the rate below is stated for two populations rather than averaged into one.
Population Scored above low Scored in the high band
US .gov (985) 68.5% → 18.6% 11.8% → 7.4%
Brazilian web (743) 82.8% → 30.0% 38.2% → 10.0%

Left of each arrow: measured, on the engine as it stood before 1.17.0, 4 August 2026. Right of each arrow: engine 1.17.0, 4 August 2026, a projection rather than a re-measurement.

The right-hand column is a projection and we will not describe it as anything else. It comes from replaying the scorer over the reports already stored for those 1,728 documents, because the corpus rules delete the raw files after a run. What makes it credible is that the replay reproduces the engine's current band on 1,728 of 1,728 documents; what keeps it a projection is that nobody re-fetched the bytes and ran the whole engine again. Both fetch manifests survive, so a true re-run is reproducible. It has not been run.

This has not been re-measured at the whole-population level since 1.17.0, and the honest reading of the gap is that the number above is, if anything, high for the engine you would call today: 1.19.0, 1.22.0 and 1.23.0 each removed firings on these same populations rather than adding them, and 1.27.0's new check ships at low, which cannot on its own lift a document out of the low band. The write-up of the original run, with the four families and what each was doing wrong, is here.

Recall, per forgery operation

Specificity is the easy half: genuine documents are published by the thousand. The other half is harder, because no public corpus labels documents as tampered. The academic forgery sets are pixel-level image forensics, and the PDF corpora hold malformed and malicious files, which is a different problem. A malicious PDF attacks the reader; a forged one lies to it.

So the positives were built. 160 genuine documents, published by their own issuers and drawn from the two populations above, every one scoring low with a single revision beforehand (so there is no false-positive confound inside the result), with eight known edits applied to each, giving 1,006 forged files. That they are forgeries and not merely broken files was checked three ways: qpdf --check reported 944 clean, 62 warnings and 0 broken, with every warning tracing to a source that already had them; the pages were rendered and inspected; and each covering box was aimed at a text run the parser had retained rather than at a guessed band, because a box over white space hides nothing.

Every source document had to score low before it was edited, so a caught forgery cannot be a false positive the engine was going to produce anyway.
160 genuine documents published by their issuers THE CONFOUND GATE scores low · one revision 8 known edits applied to each by a script, not by an adversary 1,006 forged files CHECKED THREE WAYS: A BROKEN FILE IS NOT A FORGERY qpdf --check 944 clean · 62 warnings 0 broken, and every warning was already there rendered and read the pages were looked at, not assumed aimed at real text each covering box sits on a run the parser had kept a box over white space hides nothing WHAT THIS BOUNDS These are the forgeries we thought of. Eight operations we chose, applied by a script, are not an adversary.
How the forgery was made Caught
An edit saved as an incremental update: a box painted over a value, a black block, a retyped line 100%
A spliced-in image 100%
An edit, then the metadata stripped 100%
Composited through a Form XObject: what an online PDF editor writes 89.4%
An edit, then the whole file rewritten by qpdf 71.1%
An edit, then the whole file re-distilled by Ghostscript 65.5%

Measured, engine 1.23.0, 5 August 2026: 1,006 forged files from 160 genuine sources, re-fetched byte-identical, with 0 confounds: not one of the 160 sources scored above low before it was edited. First run on 1.18.0 (4 August 2026); re-measured and identical to the decimal on 1.19.0, 1.22.0 and 1.23.0.

Detection falls as the forger does more to the container, and it falls furthest for the one operation that costs nothing: re-saving the whole file through another tool.
THE EDIT IS STILL IN THE FILE saved as an incremental update a spliced-in image edited, then metadata stripped 100% 100% 100% THE EDIT IS WRAPPED composited through a Form XObject 89.4% what an online PDF editor writes THE WHOLE FILE WAS REWRITTEN edited, then rewritten by qpdf edited, then re-distilled by Ghostscript 71.1% 65.5% 0 50% 100% Each bar is one row of the table above, drawn to scale.

Measured, engine 1.23.0, 5 August 2026, on 1,006 forged files built from 160 genuine sources. No aggregate is drawn across these bars: averaging them would assume a forger picks uniformly among the operations, and nobody measured that prior.

We do not publish the aggregate, and that is a decision, not an omission. An average across the eight operations would treat a forger as picking uniformly among them. Nobody measured that prior. It is certainly false, and it is exactly the number that turns into a marketing figure with no denominator. The per-operation rows are the claim.

The Form XObject row was 0.7% before engine 1.18.0 (4 August 2026): the walker stopped at the form boundary, so a white box drawn inside one was never compared with the text underneath it. That was disclosed on every report at the time, which was honest and did not make it any cheaper.

It also bounds what the number means: these are the forgeries we thought of. Eight operations, applied by a script, are not an adversary.

The null result: a cleanly regenerated file

Every recall figure above starts from a genuine document and applies an edit. The class byte-level forensics exists for. The class nobody had measured is the one that was never edited at all: a document laid out and printed fresh, with fraudulent content in it from the first byte.

Twenty of them were built: 8 statement and payslip lookalikes laid out in HTML and printed by headless Chromium, 6 already-caught forgeries flattened to images with pdftoppm, and 6 written byte by byte in one clean pass with a server-library producer string or none at all.

0 of 20 scored above low: engine 1.22.0, measured 4 August 2026. The sharpest figure inside that is the flattened class: the same six documents, with the same fraudulent content, score high at 100 as incremental edits and low at 10 after a single rasterisation. One command launders a fully caught forgery to zero.

One pdftoppm pass turns a fully caught forgery into a document with nothing left to catch: the content is unchanged and still false.
THE SAME SIX DOCUMENTS · THE SAME FALSE CONTENT as incremental edits 100 high the edit is still in the file pdftoppm one command flattened to images 10 low there is no edit left to find The page is now a picture. Revisions, fonts, paint order and metadata all stop existing. THE SCORE IS ANSWERING A DIFFERENT QUESTION This engine answers: was this file changed after it was made? It does not answer: is this document true? A clean regeneration is the border between the two.

This is not a defect, and we are not going to fix it with a threshold. Nothing was edited, so there is no edit to find. The engine behaved exactly as designed under the same test: Chromium's font subsets and fresh /ID pairs were emitted at info, scoring zero, which is precisely what the 1.17.0 recalibration was for. A pre-1.17 engine would have “caught” all eight Chromium prints and been wrong about why.

What it means for you is a scoping rule rather than a caveat: this engine answers was this file changed after it was made. It does not answer is this document true. A clean regeneration is the boundary between those two questions, and the answer to the second one has to come from somewhere else: an issuer baseline, a bank feed, a phone call. Whether private issuer baselines catch this class is not yet measured, and it is the next benchmark.

The one family that survives laundering

redaction-exposure asks whether text somebody meant to remove is still readable underneath the box drawn over it. It matters disproportionately for one reason: it is the only signal family that survives a whole-file rewrite, a rewrite destroys the revision history every other family reads, but text under a box is still text under a box. It accounted for 38.3% of all detections across the forgery set, and roughly 41% of what is left after a rewrite.

It was also, for a while, our worst false-positive source, and fixing it was worth this much work precisely because of the paragraph above. The method did not involve reading a single document for meaning: render the page, and look at the pixels where the supposedly-hidden run sits. Text under a box is a flat block; text that is not hidden has the contrast of glyphs. Across 1,693 covered runs nothing sat between 0.7 and 12.2, so the threshold is a gap in the data rather than a tuned constant.

The threshold sits in an empty stretch of the measurement, not at a number somebody chose: across 1,693 covered runs nothing landed between 0.7 and 12.2.
PIXEL CONTRAST WHERE THE SUPPOSEDLY HIDDEN RUN SITS a flat block the text really is hidden nothing measured in here across 1,693 covered runs the threshold goes here glyph contrast the text is still readable under the box 0.7 12.2 Marks are illustrative of where values fell; the two bounds and the run count are the measurement.

Method: render the page, then look at the pixels where the covered run sits. No document was read for meaning. The gap is what makes this a boundary rather than a tuned constant. A threshold anywhere inside it produces the same answer on every run measured.

Engine US .gov Brazilian web What changed
1.18.0 · 4 Aug 2026 5.1% 17.0% The starting point
1.19.0 · 4 Aug 2026 19/985: 1.9% 49/743: 6.6% Clip paths were ignored (85% of firing documents set one); a covered run of whitespace was reported as covered text
1.22.0 · 5 Aug 2026 18/985: 1.8% 46/742: 6.2% Clip paths drawn with lines rather than rectangles are now recovered
1.23.0 · 5 Aug 2026 16/985 29/742 A picture drawn over text no longer scores

Recall is unchanged at every one of those steps. The per-operation table above was re-measured after each and is identical to the decimal, with 0 forgeries lost. And the finding that matters most for anyone reading a report: across those 1,728 published documents this family found no failed redaction, in either population. Roughly nine in ten of its original firings were demonstrably wrong or vacuous.

What remains is stated rather than hidden. The residue after 1.19.0 was 18 US documents: twelve genuine false positives from non-rectangular clip paths this parser does not model, and six that really do hide text, every one a layout artifact, a chart label behind a plot fill, a map over its own draft paragraph, a template footer. The image-cover demotion in 1.23.0 was decided the same way: across 1,006 forgeries, image covers accounted for zero detections against 385 from filled boxes, so the observation stays in the report at info and stops carrying a score. The redaction checker runs exactly this family on a file you hold.

The same number, per toolchain

A population-wide false-positive rate is the right number for deciding whether to buy this. It is the wrong number for reading one report, because the rate is not evenly spread. It is largely a property of the software that wrote the file. Inside a single population, a signal's firing rate ran the whole way from 0% to 100% depending on which software wrote the document. That is what an earlier finding, one corpus far noisier than another, turned out to be: a producer effect wearing a target's clothes.

So the per-toolchain version of this page's headline number already exists: the producer-string reference lists the 40 /Producer strings common enough in that corpus to have been measured, and its last column is our own false-positive rate on that toolchain's genuine output, never a fraud rate. Every document behind it was published by the organisation that made it, so each score above low counts against us there too.

Same corpus, scored on engine 1.22.0: 432 distinct producer strings across the 1,728 documents, 252 of them appearing exactly once, which is the most useful fact on that page. The long tail is the normal case, and a string you have never seen before is not evidence of anything.

A population-wide rate is an average over a distribution that runs from 0% to 100%, so the number that applies to the report in front of you depends on which software wrote the file.
FIRING RATE ON GENUINE OUTPUT, BY THE SOFTWARE THAT WROTE THE FILE 0% 100% one corpus · one signal · every value in between The population average sits somewhere on this line and describes none of its ends. 432 DISTINCT PRODUCER STRINGS IN 1,728 DOCUMENTS 252 appeared exactly once 180 appeared more than once A producer string you have never seen before is the normal case, not an outlier. Only 40 strings were common enough in this corpus to carry a measured rate at all.

Same corpus, scored on engine 1.22.0. The per-toolchain rates are on the producer-string reference; the last column there is our false-positive rate on that toolchain's genuine output, never a fraud rate.

The measurements that failed

The house rule here is that no check ships before it is measured against the corpus, and when the measurement fails, what gets published is the failure, not the check. Several planned checks do not exist in this engine today because a measurement said no, and one gate could not be read at all. Leaving these off this page would make every flattering number above cheaper.

The boleto gate could not be read: n ≈ 0. A planned boleto barcode decoder was gated on one number: what fraction of real boletos carry a decodable vector barcode. Scanning 3,822 corpus PDFs (engine 1.20.0, 5 August 2026) found 63 “boleto-shaped” documents; only 2 carried a linha digitável, and both are bank file-layout specifications quoting an example line. All 15 apparent vector barcodes, opened by hand, were table rules and cell borders. The corpus contains no boletos, so the gate is unread and the decoder is not built. And it will not be resolved by fetching real ones: a boleto is a stranger's bill, carrying their name and usually their CPF, and the corpus rules close that route by construction.

The check was gated on a number the corpus could not supply: after three filters, the population of real boletos available to measure was zero.
3,822 corpus PDFs scanned engine 1.20.0 · 5 Aug 2026 63 “boleto-shaped” documents 2 carry a linha digitável 0 real boletos both are bank file-layout specs quoting an example line All 15 apparent vector barcodes, opened by hand, were table rules and cell borders. The decoder was not built.

And it will not be resolved by fetching real ones: a boleto is a stranger's bill, carrying their name and usually their CPF, so the corpus rules close that route by construction.

The first run of that script deserves its own sentence, because it read like an answer: it reported a clean 0 across all 3,822 documents, and the 0 was an artifact, the scan ran over compressed stream bytes it could not see into. A measurement that cannot see the thing it measures returns zero, and zero is indistinguishable from a result.

Planned check What the measurement said Decision
Column of amounts vs the stated total Of 114 candidate totals in 3,842 published documents, 56 (49.1%) read as inconsistent, which is a statement about the extractor, not the documents; 29 genuine documents would have been accused. Engine 1.25.0, 5 Aug 2026. Not built
Payslip arithmetic (gross − deductions = net) The population is 3 documents in 3,842, two of which this repository wrote. Not built
PIX payload checksum (CRC-16 over the BR Code) The corpus contains zero PIX payloads: 85 lines carry the EMV marker and none walks as a BR Code: so the rate at which real payloads fail is unmeasured. What was measured is the false-positive surface: all 85 near-misses were rejected. Engine 1.26.0, 5 Aug 2026. Ships one severity below where the plan ranked it, until a real population exists
Glyph geometry: three of the four planned checks Each rejected check fires on more published documents than the plan's whole rule catches forgeries; the baseline check alone flags 20.8% of US and 30.7% of Brazilian text-drawing documents. Engine 1.27.0, 5 Aug 2026. Not shipped as findings. Only the money-line variant ships, at low: 10 of 740 US and 14 of 628 BR text-drawing published documents (1.4% / 2.2%), and: paired against each forgery's own source: 0.0% lift on the 228 forgeries that retype nothing

One published measurement was itself wrong, and the retraction is part of the record. The first phase-0 run for certification permissions enumerated 8 of the corpus's 14 targets, 915 PDFs where the corpus held 3,842, and reached three conclusions a full run showed to be false, including “nobody in this corpus certifies anything”. The full run (engine 1.24.0, 5 August 2026) found 12 certified documents, every genuine one declaring /P 1, no changes permitted, where the plan had predicted /P 2 would carry all the value. The prior was not slightly off; it was inverted. The same run caught the family reading a signature's own appearance image as a forbidden content change: 109 counterfactual misfires, 0 after the 1.24.0 correction, and the family has fired 0 times above info on 273 signed documents. Both write-ups stand in the repository, the first under a banner naming what it got wrong.

The prior was not slightly off, it was inverted, and it took a full run over the corpus rather than a quarter of it to find that out.
HOW MUCH OF THE CORPUS EACH RUN READ 915 PDFs 915 PDFs where the corpus held 3,842-8 of 14 targets It read like an answer, and three of its conclusions were false, including “nobody in this corpus certifies anything”. WHAT THE FULL RUN FOUND · ENGINE 1.24.0 · 5 AUG 2026 the plan predicted /P 2 carries all the value 12 certified documents, and every genuine one /P 1, no changes permitted AND THE SAME RUN CAUGHT THE FAMILY MISREADING ITSELF a signature's own appearance image, read as a forbidden content change 109 counterfactual misfires → 0 after the 1.24.0 correction, and 0 firings above info on 273 signed documents

Both write-ups stand in the repository, the first under a banner naming what it got wrong.

Even the clean results in this family of work are stated with their limits. running-balance-break fired 0 times in 3,842 published documents, and its precondition never met a real bank statement, because the corpus holds banks' published fee schedules, not customers' statements, and never will. Zero fires is not the same claim as measured-high precision. The CNJ process-number check verified 156 of 156 real numbers with 0 false accusations, and its measured recall over the 1,006 forgeries is zero, the forgeries edit amounts and dates, not case references.

What is still not measured

A page of measurements that lists only the flattering ones is marketing. These are the questions where the honest answer today is that we do not have a number.

  • Whether issuer baselines catch a clean regeneration. That is the counter we sell against the null result above, and it has not been benchmarked. It will be measured and published either way.
  • A whole-population false-positive rate on the current engine. What exists is the 1.17.0 projection above plus per-family re-measurements at 1.19.0, 1.22.0 and 1.23.0. The full re-run is reproducible from the surviving manifests and has not been done.
  • A true-positive rate against real fraud. Every recall figure on this page is measured against forgeries this repository built, because real fraudulent documents cannot be obtained legally at scale: a genuine forged bank statement or boleto is a stranger's personal data and usually evidence in someone else's dispute, and the corpus rules close that route by construction. No number here says how often the engine catches fraud as it actually arrives.
  • Recall against a real adversary. The positives are built by a script from eight operations we chose. Nothing here says how the engine performs against a forger who reads this page.
  • Image metadata is trivially strippable. exiftool -all= removes every identity and timestamp finding the image engine makes. A match is strong evidence; an absence is none, and every image finding says so in its own prose.
  • The Office engine does not render the document. Text hidden by being coloured to match its background, sized to zero, or covered by a shape is not detected there. The paint-order pass that makes redaction-exposure work has no OOXML equivalent yet. It is the biggest hole in the newer medium, and it is disclosed on every Office report rather than left to be inferred.
  • Encrypted PDFs are disclosed, not attacked. A password-protected file is reported as “not content-analysed” rather than scanned as ciphertext and reported as clean.

Whether a forger would hide text in a layer

A PDF can carry content in an optional-content group switched off by default: present in the file, absent from the screen. Engine 1.28.0 reads and retains those runs and judges none of them, and it established that glyph-anomalies, br-identifier-checksum and running-balance-break read such text as though it were on the page. Measured across the same 1,727 documents, and re-measured on 2026-08-07 after we found the first measurement was wrong.

The 2026-08-06 run decided "off" from the default configuration's /ON, /OFF and /BaseState lists alone. Visibility is not one flag: a configuration can also carry /AS, and a group whose /Usage /View /ViewState is /OFF is invisible on screen however the lists read. Engine 1.28.0 counted such a group as visible. The gap was reported by a reader, Tobias (@to21as), in a comment on our own redaction post, which is the honest answer to how it was found. Engine 1.29.0 reads /AS, and these are both runs, side by side.

Reading one more visibility flag moved a group off by default from three documents to four, and left the line the argument rests on exactly where it was: none.
ENGINE 1.28.0 ENGINE 1.29.0 · READS /AS declare optional content 85 · 4.92% 85 · 4.92% carry a group off by default 3 · 0.17% 4 · 0.23% paint text into a layer that is off 0 · 0.00% 0 · 0.00% The correction was found by a reader, not by us, and it is published at the same size it was. Same 1,727 documents on both runs, so a definition change cannot be confused with a corpus change.

The correction was real and it was small. One further document, and the number that carries the argument did not move: still none of 1,727 paints text into a layer that is off, and the three families still never read a hidden line. Only 5 documents (0.29%) carry /AS at all, every one of them in the US .gov sample; the Brazilian sample carries none, so its counts were never affected.

One thing the re-run found that the first run could not see, and it is the sharper case Tobias named: both US documents with a hidden layer are off for /View and on for /Print: they look redacted in the viewer and in a screenshot, and print what is hidden. That shape has no feedback loop at all. It is 2 documents in 1,727, and we would rather publish the number than the impression.

And the older limit still stands underneath all of it: these are documents nobody was hiding anything in. A null result on published files is evidence about accidents, not about adversaries.

Widened 2026-08-07 to every real document we hold. The 1,727 above is the corpus the 1.28.0 figure was measured on, and the comparison had to stay on it or a definition change and a corpus change would be indistinguishable. The standalone claim is not capped there: across ≈3,687 distinct documents (adding 1,317 Brazilian federal procurement contracts, 255 US court filings, 83 bank statements and the template sets), still none paints text into a layer that is off. 106 declare optional content (2.9%), 7 carry a group off by default (0.19%), and 7 carry /AS (0.19%). The procurement corpus contributed a clean zero on every line: not one of its 1,317 documents declares optional content at all, which is what machine report generators do, layers come from design tools.

A countersigned contract scores high, and that is the largest false positive we have measured

Run over 1,317 Brazilian federal procurement contracts published on PNCP (fetched 2026-08-07, engine 1.29.0, 0 unreadable): 89.8% low, 0.4% elevated, 9.8% high. The aggregate flatters us, it is better than both samples above, and it hides the whole story, so here is the split that matters.

The trigger is not tampering. It is a second signature: split the same 1,317 contracts by whether anyone signed them and the engine's error rate goes from almost none to most of them.
1,317 PUBLISHED CONTRACTS · ENGINE 1.29.0 · 7 AUG 2026 unsigned 1,150 docs 1,146 low 4 elevated · 0 high 99.65% low signed 167 docs 37 low 129 high 77% high and all 37 that scored low carry exactly one revision WHAT THE SAME REPORT SAYS ABOUT ONE OF THOSE CONTRACTS signature-coverage · incremental-updates id-inconsistency score 100 · band high, and every statement is true of the bytes signature-integrity info: the signatures are intact and the signed content is unaltered

Read a high band on a multi-signature PDF as uninformative until this is recalibrated. It is a severity change, so it waits on a measurement rather than preceding one.

On unsigned documents the engine is 99.65% low. On signed ones, 77% score high. Every one of the 37 signed documents that scored low carries exactly one revision, so the trigger is not tampering, it is a second signature.

The mechanism is not a bug, which is why it is disclosed here rather than fixed and forgotten. When a second party signs, their bytes land beyond the first party's /ByteRange, so signature-coverage reports at high that signatures no longer cover the file; each signature is an incremental revision, so incremental-updates reports at high; and /ID[1] must change per revision, so id-inconsistency reports at medium. Every statement is true of the bytes. Meanwhile signature-integrity reports info: the signatures are cryptographically intact and the signed content was never altered. The same report says the signatures are valid and scores the document 100.

A contract signed by two or more parties is the most ordinary legitimate document in this market, and the engine currently treats it as its highest-risk output. Until that is recalibrated (and it is a severity change, so it waits on the measurement above rather than preceding it), read a high band on a multi-signature PDF as uninformative. The families to read instead are signature-integrity, which is the one that actually checks the digest, and signature-permissions.

The number nobody in this market publishes

The competitive landscape was read directly on 31 July 2026: Resistant AI, Inscribe, Snappt, Ocrolus, Truv, Veryfi, DocuClipper, VerifyPDF and HTPBE. Several publish a headline accuracy figure in the 99% range with no methodology published, no stated denominator and no ground truth, which is why none of those figures is repeated here.

Not one of them publishes how often its system flags a genuine document. That absence is the reason this page exists. It is not a claim that our engine is better than theirs: several of them do things this one cannot, at a scale this one is not built for. It is a claim about what you can check. A recall-flavoured number with no denominator cannot be argued with, only believed; a false-positive rate on a named, public, uncurated population can be reproduced by anyone who fetches the same manifests.

Even the strongest public benchmark in the adjacent space leads with the false accept rate. False-reject behaviour on genuine documents is the thing every operations team actually pays for, and it is the thing nobody quotes.

There is also no independent test anyone in this market could submit to. No public benchmark exists for PDF-structure forensics: cross-reference tables, incremental updates, producer metadata, signature coverage. The academic benchmarks are pixel-level image forensics, which is a different problem. That cuts both ways, and both halves are load-bearing: nobody can prove we are worse than the field, and we cannot prove we are better. It is why this page publishes reproducible measurements of our own error, rather than a ranking no one can run.

The difference between the two kinds of number is not how large it is. It is whether you can go and get it yourself.
A HEADLINE ACCURACY FIGURE no methodology published no stated denominator no ground truth can only be believed A FALSE-POSITIVE RATE ON THIS PAGE a named, public, uncurated population the fetch manifests survive the engine version and date on every figure can be re-run against us AND THIS CUTS BOTH WAYS No public benchmark exists for PDF-structure forensics, so nobody can prove we are worse and we cannot prove we are better.

Check a number on this page rather than believing it. Open the “Signed, then changed” sample: a precomputed report on a PDF whose signature no longer covers what is underneath it, which is the shape the countersigned-contract section above is about. Then run a document you already know the answer to through the free checker and compare what it says with the rates here: the file is sent over HTTPS, parsed in memory and never written to disk. No account, no quota. After that, what each signal family actually looks at and how the service is built and run are the other half of a procurement review.

Sources for every figure on this page are tracked in this product's own repository: the engine release notes in CHANGELOG.md (1.17.0, 1.18.0, 1.19.0, 1.22.0, 1.23.0, 1.27.0), the “Honest gaps” section of STATUS.md, the committed phase-0 aggregates in docs/CORPUS-PHASE0-*.md and docs/GLYPH-PHASE0-2026-08-05.md for the failed measurements, and the evidence compilation in docs/EVIDENCE.md §5.2, §5.3 for the competitive reading above. Per-document corpus findings are not published: the corpus rules delete the documents after a run, and publishing a list of published organisations' filenames beside a risk band is not something a measurement needs.