# How do you know the reading is right? Source: https://truecopy.dev/docs/verify/ The checks that actually catch a wrong table extraction, which of them a library can run for you, and the one thing none of them prove. Written because the question has plenty of articles and almost no code. Search for how to validate a PDF table extraction and you get evaluation papers, vendor posts and a list of things you should implement. Almost none of it is code you can install. This page is the list, and what each item costs you here. The reason the question matters at all: **a wrong reading does not look wrong.** It comes back as a table, with the right number of columns and plausible values in them, and nothing downstream can tell it apart from a right one. Nobody re-reads a table that looks correct. ## The checks, by what they catch ### 1. The cut invented a column A column filled on 2% of rows is not a column. It is a header, a page number, or a stray mark that the cut mistook for structure, and every row it touched is now shifted. `readTable` reports it, with no setup at all: ``` column 5 of page 1 is filled on only 2% of its rows - the cut may have invented it ``` ### 2. The pages disagree Six columns on two pages, seven on a third, means one page was read differently from the others. Anything that concatenates the pages afterwards is mixing two readings. Also reported by [`readTable`](https://truecopy.dev/docs/table/), for free. ### 3. A row does not look like the others The row with an empty date where every other row has one is not a row of the table. It is a total, a balance, a footer. Fold it into your sum and the sum doubles. [`findRowAnomalies`](https://truecopy.dev/docs/signature/) learns the table's own shape and finds it, **without a list of forbidden words**, so it works on an issuer nobody has seen, in a language nobody on the team reads. This is the check most homegrown readers skip, and the one that silently corrupts totals. ### 4. The reading contradicts what the document says about itself The strongest check available, and the only one that can say a reading is **wrong** rather than merely odd. A statement declares a total. A payslip declares a net. Add up what you read and compare. You write the comparison, because only you know what your document declares; [`selfCheck`](https://truecopy.dev/docs/contract/) is the method the pipeline makes compulsory. What the library enforces is the consequence: **a reading that contradicts its document never comes back as sound.** Not a convention, not a code review item. The pipeline will not return that verdict. Saying `{ nothing: 'why this document declares nothing' }` is allowed and costs a sentence. That is the point: the oversight becomes something written down instead of a silent `null`. ### 5. It is not the document you think it is A payslip carries dates, amounts and totals. So does an invoice. A reader pointed at the wrong kind of document does not crash; it reads it, and hands you records built from the wrong meaning. [`classify`](https://truecopy.dev/docs/classify/) is where you say what the document has to look like. The corpus of things it must be told apart from is yours: nobody else knows what lands in your inbox. ### 6. It has no text at all A scan is a picture of a table. Every reader must refuse it rather than return zero rows and let an empty result pass for an empty document. [`open`](https://truecopy.dev/docs/open/) refuses it by name (`no-text`), along with empty, oversized and password-protected files. ## What you cannot check by reading the result Numbers 1 to 6 look at one document. They say nothing about whether your reader **still** does this next month, after the refactor nobody thought was risky. `selfCheck() { return null }` compiles, passes review and ships. One of the two readers this library came from had exactly that, until it was measured from the outside. So [the kit](https://truecopy.dev/docs/kit/) turns the six rules into assertions that run in **your** test suite, against **your** corpus, with **your** extractor: a conformance suite that supplies the extractor stops measuring your reader and starts measuring itself. ## The thing none of this proves An empty list of warnings is **not** a promise that the reading is right. It means nothing looked wrong from the shape of the page, which is a much smaller claim, and the difference between the two is the whole reason this library exists. A check can catch a reading that contradicts itself or its document. No check can confirm that a self-consistent reading matches the ink. That is why the honest output is three-valued (read, needs review, refused), and why [refusing matters](https://truecopy.dev/docs/why-refuse/) more than any single test. ## Where to go next - [Check what a model extracted](https://truecopy.dev/docs/verify-llm-extraction/): checks 4 and 5 applied to rows this library did not read. - [Quickstart](https://truecopy.dev/docs/quickstart/): checks 1, 2 and 6 in two lines. - [signature](https://truecopy.dev/docs/signature/): check 3, and why it beats a word list. - [contract](https://truecopy.dev/docs/contract/): check 4, the shape it makes compulsory. - [kit](https://truecopy.dev/docs/kit/): the six rules in your own gate. Map of this site for a model: https://truecopy.dev/llms.txt Every page in one file: https://truecopy.dev/llms-full.txt