Outside of client work I am building a legal research system for Panamanian law. It has to know something most retrieval systems never consider: whether the provision it just found still applies.
A statute can be amended, partially repealed, or wholly derogated by a later instrument. The text sitting in the gazette does not change when that happens. Nothing in the document announces that it is dead. The only way to know is to trace the graph of later instruments that reference it.
Why this breaks ordinary retrieval
A retrieval system optimizes for finding the passage that best answers the question. Here that objective is close to backwards. The best-matching passage is frequently a repealed article, and it reads as authoritative because it was authoritative once.
So the interesting failure is not hallucination in the usual sense. The model quotes real text accurately, cites a real source, and is still wrong, because the text stopped applying in 2011.
The graph, and the defect I found in it
I built a derogation graph where nodes are instruments and articles and edges are the relations between them. Then I wrote a scorecard against it: edge inventory, dangling references, duplicates, self-references, temporal inversions where an instrument appears to repeal something published after it, and multi-derogation cases where several instruments claim the same target.
The scorecard found what I now call the falsely dead problem. Some provisions were marked as no longer in force when only a portion of them had been repealed. The scope of the repeal was not captured, so a partial derogation was being applied as a total one. A user asking about the surviving portion would be told, confidently, that nothing was there.
That is the failure mode I care most about, because it is invisible. A system that returns nothing looks careful. It takes an independent check to notice that it is wrong.
Mining a gold set instead of writing one
I did not hand-write the test cases. Explicit derogation statements appear in the raw text in recognizable forms, so I mined them independently of the graph and used them as ground truth. That produces three numbers: recall, whether the graph captured the relation at all; resolution, whether it pointed at the right target; and scope accuracy, whether it got partial against total correct.
The advantage of mining over authoring is that the gold set does not inherit my assumptions. When I write test cases by hand, I test the system I think I built.
Scoring refusal
Then the behavioral battery, which runs live against the answering service and scores four things: whether the answer reflects current force status, whether the system abstains when it should, whether stated deadlines are exact rather than approximated, and whether every claim is grounded in a retrieved passage.
Abstention is the one people skip. It is also the only one of the four that measures judgment rather than recall. A system that answers everything scores well on helpfulness and badly on trust, and trust is the number that matters when somebody is deciding whether they still have to file by a date.
What I would carry into any AI project
Test the failure that is invisible. Silence looks like caution, so a system that wrongly returns nothing will pass every eyeball review you give it.
Build ground truth from a source the system did not use. Score abstention explicitly, and weight a confident wrong answer as worse than a refusal rather than equal to it. Re-run the battery after every model swap, because refusal behavior shifts between models more than accuracy does.
The suite is read-only apart from one draft remediation script I have not run yet. I would rather hold a measurement I trust than a fix I applied in a hurry.