Teardown 03 · Method

A refusal is not a hallucination

The safest thing your assistant can do is say "that isn't in the documentation I have." Most faithfulness implementations score that answer as a 100% hallucination. We know, because ours did — until an independent audit of our own harness caught it.

25 July 2026 · Attova · findings from an internal adversarial code audit

How claim-level faithfulness works

The standard way to measure grounding in 2026: decompose an answer into its individual factual claims, then verify each claim strictly against the retrieved context — supported, unsupported, or contradicted. Faithfulness is the supported share; hallucination rate is its complement.

faithfulness      = supported_claims / total_claims
hallucination_rate = 1 - faithfulness

Now feed it a refusal: "I could not find information about customer-managed encryption keys in the documentation available to me." How many factual claims does that answer make about the world? Zero. And 0 / 0 is where a metric quietly becomes a lie.

The bug

The obvious defensive implementation returns 0.0 for the empty case — no supported claims, so no score. That single line means:

Put that number in front of a team with a target to hit and you have built an incentive to remove refusals. The system's hallucination rate improves. The system gets more dangerous. This is not hypothetical — "reduce our hallucination rate" is the brief we are most often handed, and the metric behind it is usually nobody's job to check.

The correct treatment

A zero-claim answer asserts nothing false, so its faithfulness is 1.0 — it is perfectly grounded. Whether it is useful is a different question, and it belongs to a different metric: answer relevance. Keeping those two separate is the entire point.

From a real run in our harness, the refusal above scores:

MetricValueReading
Faithfulness1.00claims nothing it cannot support
Hallucination rate0.00correct — it invented nothing
Answer relevance0.75honest, but the user still has no answer

That is the truthful reading: the generator behaved correctly, and the retrieval pipeline owes this user a document. The fix goes to the retriever, not to the prompt telling the model to be more assertive.

Two more bugs from the same audit

We put the harness through an adversarial code review before letting it produce a customer number. Two other findings changed reported scores:

Duplicate chunk ids inflating nDCG above 1.0

Real systems return overlapping windows of the same chunk. If the same id appears twice in a ranked list and both are credited, discounted cumulative gain can exceed its own ideal — a system scoring better than perfect. Duplicates are now credited once, at their best rank.

A chunker silently deleting text

Body text sitting on the line directly under a markdown heading, with no blank line between them, was being dropped along with the heading. The affected content simply became unretrievable — and every metric still reported cleanly, because the ground truth was written against the same broken chunks.

We also moved precision@k to the standard denominator (k, not the number of results returned), so a retriever that returns three results instead of five cannot win on precision by returning less.

Why this matters more than the model choice

Every reliability programme rests on a measurement chain: chunker → retriever → judge → score → decision. A bug anywhere in that chain does not announce itself. It produces a plausible number, a green dashboard, and a decision made on fiction. All three findings above produced plausible numbers.

So when you evaluate an eval vendor — including us — the questions worth asking are: what does your metric do with a zero-claim answer? What does it do with duplicate contexts? What is the denominator of precision@k? Can I reproduce your score from your raw output? If the answer is a dashboard rather than a definition, you are buying a number nobody has audited.

Our answers are in the open

The harness is MIT-licensed and the scoring is a few hundred lines of readable Python — every metric definition, every edge case, every test.

https://github.com/nnnirvana/attova-harness

Request a reliability audit →

← All teardowns