Whitepaper 01 Retrieval integrity 4 September 2026 Versi bahasa Indonesia

The source that lies

Five layers of a retrieval pipeline report success. The legal answer they produce is confident, checkable, and wrong.

FD Iskandar · AI integration & automation engineering

This is a worked audit of a single question against a real government legal corpus. The pipeline described is not a strawman: it applies a status filter, does vector retrieval, quotes verbatim, and cites its source. Every stage returns success. The answer it produces cites a real article, of a real regulation, that is genuinely in force. The article it quotes was deleted a year ago.

Every claim below is reproducible with the commands in the appendix, against documents anyone can download. The specific corpus is Indonesian wage regulation. The failure modes are structural and corpus-independent.

01I made the mistake first

Twenty minutes into gathering documents for this paper, I ran a regular expression over a downloaded PDF to extract which regulation it was. The script reported UU 25/1992. It said so with no hedging, because regular expressions do not hedge.

The document was PP 7/2021. What my pattern had matched was a regulation cited inside the considerations clause: the preamble where a regulation lists the older laws it derives authority from. Every legal document in this corpus opens by naming other legal documents. An extractor that looks for a citation-shaped string near the top of the file finds one immediately, and it is usually the wrong one.

I mention this because it is the entire paper in miniature. The failure was not random. It was not a hallucination. It was a correct program, doing exactly what it was told, over a document whose structure made the correct behaviour wrong. No amount of testing on well-formed inputs would have caught it, because the input was well-formed. It just did not mean what the program assumed it meant.

The dangerous failures in retrieval systems are not the ones that look like errors. They are the ones that look like answers.

02Why legal retrieval looks like the easy case

If you were choosing a domain to demonstrate that retrieval-augmented generation works, you would choose this one. Legal corpora are the friendliest data a retrieval system will ever see. The documents are authoritative; there is exactly one official text. They are stable; a regulation does not change between Tuesday and Wednesday. They are structured, numbered down to the clause, and they are public, published by the government at permanent URLs.

Better still, the metadata appears to solve the hardest problem for you. The government's legal database prints a status on every document: in force or no longer in force. Filter on it and staleness is handled.

The question I will use throughout is deliberately narrow and factual: how is the annual minimum wage adjustment calculated? It has a single correct answer, written as a formula, in one article of one regulation. Payroll providers, employers of record and compliance tools all need it. It is the easy case.

03Layer one: the status column answers a different question

The regulation governing wages is PP 36/2021. Its database entry says Berlaku, meaning in force. That is true.

The same page records that it has been amended twice, by PP 51/2023 and PP 49/2025. Both of those are also in force. Nothing is contradictory and nothing is out of date.

But notice what the status field actually tells you. It tells you the document has not been revoked. It does not tell you whether the article you are about to quote is still the operative text. A regulation is in force as a whole while individual articles inside it are replaced, renumbered, or deleted by later instruments.

Status is a property of documents. Force is a property of articles. No status column in any legal database answers the question a retrieval system is actually asking.

There is a second, subtler problem. On a different regulation in the same database, I have seen this column state that an instrument was in force after it had already been revoked. Here it is correct. That inconsistency is worse than being reliably wrong: spot checks find the column working, conclude it is trustworthy, and the one case that matters slips through. A field that is right most of the time trains you to stop checking it.

04Layer two: the parent regulation is a photograph

The pipeline downloads PP 36/2021 and extracts its text. Extraction succeeds and returns 3,045 characters. The stage logs success and passes the document on.

The document is 75 pages long. Text exists on two of them.

DocumentPagesPages with textCoverage
PP 36/2021 (the parent, in force)7523%
PP 51/2023 (first amendment)2727100%
PP 49/2025 (second amendment)2222100%
PP 78/2015 (revoked predecessor)4949100%

Pages 2 through 56 hold the articles, the entire substance of the law. Every one of them is a scanned image. So are the annexes. The two pages that carry text are page 1 and page 57: the cover, and the signature page.

What lands in the index is a document containing no law at all. And the most prominent text in it is the preamble, which names three other regulations. The failure from section 01 is not hypothetical; it is sitting in the index, attached to the single most important document in the corpus.

The part that costs money

This does not fail loudly. It returns 3,045 characters, so every check of the form "did extraction succeed?" answers yes. A shallow probe for an embedded text layer also answers yes, because pages 1 and 57 carry the seven fonts the file reports. Both signals are true. Both are useless.

A coverage check would have caught this instantly: 75 pages in, 2 pages of text out. Almost nobody writes that check, because it only matters for documents that are already broken, and you do not know which those are until you write the check.

05Layer three: retrieval ranks the readable, not the current

The vector search now runs over the chain. Three of the four documents are fully readable; the parent is effectively empty. Retrieval returns its best match: PP 51/2023, Pasal 26A, which contains the wage adjustment formula, complete with its coefficient and bounds. It is a genuinely excellent match for the query.

It ranked first because it was the only document in the chain that could be read, not because it was the most current. Embedding similarity has no concept of legal recency. A deleted article and an operative one are equally retrievable if both are in the index, and the deleted one wins whenever the operative text is a photograph.

06Layer four: the article was deleted, not amended

PP 49/2025, item 8, in its entirety:

8. Pasal 26A dihapus.

Three words. Article 26A is deleted. Not amended, not superseded by a revised version. Removed. The article the pipeline is about to quote does not exist in current law.

Six articles in this chain were amended twice: articles 5, 26A, 28, 34, 34A and 35. A system that walks one hop from the parent to the first amendment has done more work than most retrieval pipelines ever do, and is still wrong for every one of them. Chain traversal has to run to a fixed point, stopping only when nothing amends the amender. Anything short of that is theatre.

07Layer five: the text layer is OCR, and OCR is lossy

The remaining documents do have text, but that text was produced by optical character recognition, and the errors are systematic rather than random.

In the text layerActually
Pasal 268Pasal 26B
Pasal 7lPasal 71
ayat (21ayat (2)

In one document, Pasal 7l and Pasal 71 each appear twice. That is one article, split into two entities, with no signal that anything went wrong. Any exact-match join on article number silently loses half its rows.

It reaches the answer too. The wage formula, as it exists in the extracted text:

Nilai Penyesuaian UMlt+l) : PE x o x UMttt

The Greek letter alpha has become the letter o. The parentheses have become digits. Quoted verbatim, which is exactly what a well-behaved citation-preserving system does, the formula is not merely imprecise. It is not mathematics.

08The compound failure

Assemble the run. One question, five stages, five successes:

StageReportsActually
Status filterpassTrue and useless; status is document-level
Text extractionpass3,045 chars from 75 pages; zero articles
Vector retrievalpassRanked the only readable document, not the current one
Currency checkpassThe cited article was deleted in 2025
Quote fidelitypassOCR destroyed the formula being quoted

The output is an answer that cites a real article, of a real regulation, that is genuinely in force, quoted verbatim from the official PDF, with a working link to the government's own database. It will survive review by a competent engineer. It will survive review by a lawyer who checks that the regulation is current. It fails only against someone who reads the second amendment line by line.

No exception was raised. No confidence score dropped. There is nothing to alert on, because from the inside every component did its job correctly.

Interactive · runs in your browser
Watch all five layers pass, then fail
The same query against the same corpus. Answer it, then audit the answer: each check reports success, then each one is reopened against the actual documents. No account, no server, nothing to install.

09This is not a model problem

Nothing above involves the language model behaving badly. There is no hallucination here. Every fact in the wrong answer traces to a real document, and the model was faithful to the sources it was given. It was given a corrupted view of the corpus and reported it accurately.

Which means the usual remedies do not apply. A larger model does not fix it: it reads the same 3,045 characters. A lower temperature does not fix it; the failure is not stochastic, and the same wrong answer comes back every time. Better prompting does not fix it, because the model cannot ask for text that was never extracted. Citations do not fix it, because the citation is real and the link resolves. Human review does not fix it either, unless the reviewer independently walks the amendment chain, which is the work the system was bought to eliminate.

When a retrieval system is wrong about its sources, every downstream control you have is measuring the wrong thing.

These are ingestion and provenance defects. They have to be caught where documents enter the system, and enforced by something that can refuse.

10What a gate would have to do

Not a better model. A set of conditions that must hold before an answer is allowed out, each of which fails closed:

  1. Article-level force, not document-level status. Every article carries the instrument that last touched it. If that is unknown, the article is not quotable, regardless of what the parent document's status field says.
  2. Chain traversal to a fixed point. Follow amendments until nothing amends the amender. One hop is not traversal; it is a coin flip that happens to be right most of the time.
  3. Extraction coverage as a hard gate. A 75-page document that yields two pages of text fails ingestion loudly. Coverage is compared against page count, and the threshold is a number someone chose on purpose.
  4. OCR confidence carried through to the citation. If the glyphs behind a quoted string are uncertain, the string is not offered as a verbatim quote. Confidence that is discarded at ingestion cannot be recovered at answer time.
  5. Refusal as a first-class output. "I cannot source this safely" must be cheaper to emit than a wrong answer: in latency, in evaluation scoring, and in how the interface presents it. If refusal is penalised, it will not happen.

The fifth is the one that gets cut. Refusal looks like failure on every dashboard, scores badly against answer-rate metrics, and disappoints users in the short term. It is also the only control that works when the other four are uncertain, which is the normal condition of any real corpus.

What the demo shows is a system doing exactly that: declining to answer a question it cannot source, and showing its work: the chain it walked, the article it found deleted, the document it could not read. That is a more useful output than a formula, because it is actionable and it is true.

11Reproduce it

Everything above comes from public documents. No credentials, no scraping of anything behind a login, no private data.

# the amendment chain, walked from the live source
curl -s "https://peraturan.bpk.go.id/Details/161909/pp-no-36-tahun-2021" \
  | grep -oE 'href="/Details/[0-9]+/[a-z0-9-]+"' | sort -u

# the parent regulation: 75 pages, text on 2 of them
curl -sL -o pp36.pdf "https://peraturan.bpk.go.id/Download/154587"
pdfinfo   pp36.pdf | grep Pages
pdftotext -layout pp36.pdf - | tr -d '[:space:]' | wc -c

# page 5 has no text and one image; it is a photograph of a page
pdftotext -f 5 -l 5 pp36.pdf -
pdfimages -list -f 5 -l 5 pp36.pdf

# one article, two tokens, because OCR read the digit 1 as the letter l
curl -sL -o pp51.pdf "https://peraturan.bpk.go.id/Download/328142"
pdftotext -layout pp51.pdf - | grep -c 'Pasal 7l'
pdftotext -layout pp51.pdf - | grep -c 'Pasal 71'

# the deletion
curl -sL -o pp49.pdf "https://peraturan.bpk.go.id/Download/402111"
pdftotext -layout pp49.pdf - | grep -A1 'Pasal 26A dihapus'

One detail worth noting while you run these. Every download above is served as application/octet-stream, whatever it actually contains. In the same ID range I found an identifier that returns a ZIP archive from a URL ending in .pdf. A pipeline that trusts the content type learns nothing; a pipeline that trusts the file extension feeds an archive to a PDF parser. Neither is told it was wrong.