# Why refusing matters Source: https://truecopy.dev/docs/why-refuse/ Two readers written for different documents, sharing no line of code, arrived at the same five rules. When two teams converge without speaking, that is not a preference. It is the shape of the problem. The worst defect a document reader can have is not missing something. It is **returning a table that looks right** out of a document it misread, because a table that looks right is never checked. That is the whole argument, and everything in this library follows from it. ## The five laws Two readers, written for different documents (one for bank statements, one for career records), sharing no line of code, arrived at these five separately. When two teams converge without speaking, it is no longer a preference. ### 1. The document checks itself A statement carries `opening + Σ = closing`. A career record announces its own total of quarters. An invoice announces its total. Setting your reading against what the document **declares** is the only check that depends on no layout at all. It works on an issuer you have never seen, in a language you do not read. On a real career record, eighteen per-year point values summed to `4112,03`, and the document announced `4112,03`. Eighteen decimals landing to the cent do not land by accident. When they do, the table was read **entirely and exactly**. No line was skipped, no value misread and nothing counted twice. That is `selfCheck`, and it is the first thing to write. ### 2. Refusing beats returning a plausible table Fed a document it could not parse, one of these readers produced **twenty-two findings, none of which meant anything**. The document carried neither of the two figures the tool knows how to check. It did not fail. It answered confidently, about nothing. A refusal is an answer. It takes the place of the result, and it carries the next move, because a refusal with no way out is a dead end. ### 3. Counting is not measuring A tempting shortcut is to run two readings and keep the one that finds more rows. It always picks the worse one. **A reader that cuts badly finds more**, because it turns footnotes and page furniture into data. So `read` returns no score. The document decides, not the count. ### 4. The reader proposes, the person confirms The correction screen is part of the product, not the recovery path. Which means every row a person is asked to confirm must carry **where it came from**. A value nobody can point at is a value nobody can check. Handed a list of figures, a person hunts through the document again. Handed a place, they jump to it. That is `rowsToReview`, and `placesOf`. ### 5. Every quantifier is bounded These patterns run over a whole document, line by line. An unbounded quantifier there does not make a page slow. It makes a page that never comes back. This rule is not in the kit, because a naive scan of source produces false positives. It is held by `eslint-plugin-regexp` and its `no-super-linear-backtracking` rule, in your own gate. ## The name research gives it The machine-learning world arrived at the same place from the other side, and named it: **calibrated abstention**. A 2026 architecture for enterprise document AI describes _"the system declines rather than guesses when grounding is insufficient"_, paired with _"evidence-based confidence that verifies extractions against the source document"_. Those two phrases are, word for word, `refuse` and `selfCheck`. Models trained to say "I don't know" [cut false-confident answers by a factor of two to five](https://arxiv.org/abs/2607.17883). The same literature names why it stays rare: **benchmarks penalise abstention**, and training amplifies the bias, so the whole machine is tuned to guess with confidence. A deterministic parser has no excuse to inherit that. ## What the library does not promise It does not promise to read an unknown layout correctly. On a real bank statement with its header labels hidden, the page-wide spread of x proposed **twelve** columns where the table had five. It counts the letterhead, the address block and the footer as evidence of where a column starts. Cutting on the rows that share the table's shape brought it to seven, and named three of the roles from content alone. The reading still came back **wrong**. The only reason that was visible is that the rows did not add up to the balances the document declares. > It does not promise to read an unknown layout correctly. It promises to tell you when it did not. That is a smaller claim than the one every extractor makes, and it is the only one worth making about somebody's money. Map of this site for a model: https://truecopy.dev/llms.txt Every page in one file: https://truecopy.dev/llms-full.txt