01
The problem
Most RAG evaluation quietly assumes the retrieved documents agree with each other. Real company knowledge bases do not work like that.
Policies get replaced but the old version stays in the search index. An FAQ drifts away from the policy it was summarising. A regional rule contradicts the global one. Duplicate copies survive a migration. Sometimes two current documents simply disagree and there is no correct answer at all.
So this study removes retrieval from the experiment. There is no retriever here and no retriever comparison. Every condition gets an identical set of documents. That means any difference between conditions comes from how the conflict was presented, not from what was found.
02
The benchmark
There was no existing benchmark for this, so I built one. AutopsyBench is a set of 200 cases about a fictional company, with invented policies and answers that can be checked automatically. 45 cases were used for development and 155 were held out for the real run.
The cases cover 14 kinds of conflict across four domains: expenses, information security, access control and incident management. Some conflicts are resolvable, like an expired policy against a current one. Some are genuinely not, like two current policies of equal standing that disagree.
That last group matters. Because unresolvable cases are included, refusing to answer is scored as a correct outcome when it is correct, and as a failure when it is not. The study measures two opposite errors instead of chasing a single accuracy number.
03
How the experiment works
Six conditions, one variable. Each condition sees the same evidence and differs only in what metadata is visible and what the model is asked to output.
identical documents to all six conditions
metadata exposure · output protocol
A · Baseline: Document title and body text only. No dates, no source tiers.
- Answer accuracy
- 59.8%
- Conflict acknowledgement
- 100.0%
- False abstention
- 40.2%
- Source selection
- 59.8%
stale adoption 0% · appropriate abstention 100% · false confidence 0% · n=132
Notation · shorthand used below
nsample size
How many cases a figure was computed from. It differs between metrics because some do not apply to every case, and because a small number of generations can be excluded for truncation or transport errors.
pppercentage points
The arithmetic gap between two percentages, kept distinct from a percentage change. A move from 60% to 84% is 24 percentage points, not a 40% improvement. Deltas are paired, computed case by case and then averaged, so a delta can differ slightly from subtracting one headline figure from another.
95% CIconfidence interval
A bootstrap interval from 10,000 resamples, resampling cases so the pairing between conditions is preserved. Where an interval spans zero, the difference is not distinguishable from noise at this sample size, and the figures say so.
Held-out
Cases reserved and never inspected while prompts were being written, with a separate development split used for that. The protocol is frozen before the held-out run.
Conditions A to F
The six presentations of an identical evidence pack. A baseline, B effective dates, C source tiers, D both, E a required conflict assessment before answering, F a separate consistency check that runs first.
False abstention
Refusing to answer a question the supplied evidence does settle under the benchmark's resolution rule. The opposite of the error abstention benchmarks usually measure.
Appropriate abstention
Refusing to answer where the evidence genuinely cannot decide, for example two current policies of equal authority that flatly contradict each other. Here refusing is the correct behaviour.
Stale adoption
Answering with a value taken from a superseded or expired document.
Conflict acknowledgement
The model reporting that the sources disagree, on a case the benchmark marks as conflicting. A system that resolves a contradiction silently scores zero here by construction.
Conflict type
The family a case belongs to, such as stale versus current, recency versus authority, or scope conflict. Fourteen types were generated; per-type samples are small and carry no confidence intervals.
04
The first surprise
I expected the model to be fooled by out of date documents. It was not. Its actual failure was the opposite one.
At baseline it refused to answer 40.2% of questions that the evidence could actually settle. Not wrong, just unhelpful. It saw two numbers, could not tell which was current, and gave up.
Here is one of those cases in full. Same model, same documents. The only thing that changes is whether the documents show their dates.
Question · asked 2026-06-01
How often must customer status updates be issued during a major incident?
policy_v2Incident Communications Standard
Communications During a major incident, customer-facing status updates are issued at least every 30 minutes until resolution.
policy_v3Incident Communications Standard
Communications During a major incident, customer-facing status updates are issued at least every 90 minutes until resolution.
Benchmark knowledge · The generator decided this case by effective date: policy_v3 governs and the answer is 90 minutes. The baseline model was not shown any of that, which is the point of the comparison.
Declined to answer. Resolution status: “unresolved”.
The documents provide conflicting information regarding the frequency of status updates (30 minutes versus 90 minutes).
05
What dates changed
Showing effective dates moved accuracy from 59.8% to 84.4%, a paired difference of +25.0pp with a 95% interval of [17.4, 32.6]pp.
Showing source authority instead, so the model could see which document was an official policy and which was an FAQ, moved accuracy only to 63.0% (+3.8pp). That is roughly a tenth of the effect.
You can see why in the per conflict breakdown. On stale_vs_current cases the baseline scored 0% and the metadata condition scored 100%. Without dates the model cannot separate an expired policy from a live one. With dates it is close to perfect.
- ABaselineNo dates, no source tiers. The reference point.59.8%
- BTemporal metadataEffective dates alone.84.4%+25.0pp
- CAuthority metadataSource tiers alone.63.0%+3.8pp
- DTemporal + authorityBoth kinds of metadata.84.2%+24.4pp
- EConflict-firstMetadata plus a required conflict assessment.79.1%+19.8pp
- FContradiction gateA separate consistency check runs first.78.5%+18.1pp
| Conflict type | Baseline | Temporal + authority | Cases (n) |
|---|---|---|---|
stale_vs_current | 0% | 100% | 17 |
undated_conflict | 0% | 91% | 11 |
superseded_document | 100% | 100% | 11 |
majority_stale | 88% | 100% | 16 |
duplicated_stale | 71% | 100% | 7 |
authority_conflict | 100% | 100% | 14 |
recency_vs_authority | 0% | 0% | 11 |
scope_conflict | 18% | 18% | 11 |
exception_clause | 100% | 100% | 11 |
partial_update | 100% | 100% | 11 |
no_conflict_control | 100% | 100% | 8 |
06
Accuracy vs transparency
This is the result worth sitting with. The baseline flags a conflict on every conflicting case, not because it is perceptive, but because it cannot resolve one and says so.
Give it dates and it resolves the conflict, then stops mentioning that the sources ever disagreed. Conflict acknowledgement fell −25.2pp.
Document title and body text only. No dates, no source tiers.
conflict_detected on cases the benchmark marks as conflicting; a system that silently resolves a conflict scores zero here by design.For anyone putting RAG inside a company, that is the uncomfortable part. A system can get better at answering while getting worse at telling you the knowledge base is broken. The conflict first protocol was the best balance here: 79.1% accuracy while still reporting 93.5% of conflicts.
07
Same evidence, different position
A smaller sub study re ran 24 cases three times, moving the governing document to the top, the middle, then the bottom of the context. Nothing about the information changed, only its position.
At baseline that alone moved accuracy by 35.9 percentage points. With metadata visible, the effect was gone in this sub study.
How frequently must user access reviews be completed for production systems?
policy_v3model selectedAccess Governance Policy
Access review Access reviews for production systems must be completed every 180 days by the owning system administrator.
policy_v2Access Governance Standard
Access review Access reviews for production systems must be completed every 120 days by the owning system administrator.
Access reviews for production systems must be completed every 180 days.
Benchmark knowledge · policy_v3 governs and the answer is 180 days. Position never changes that; it only changes where the governing document sits in the context. With two documents the governing one can only lead or trail, so the middle permutation resolves to the same physical order as “first”. The harness still ran and recorded them separately, and both are shown here as recorded.
08
The failure nothing fixed
Two kinds of conflict resisted every intervention.
The first is recency_vs_authority: a recent informal note contradicts an older policy that is still in force. The benchmark rule says the policy wins. Accuracy was zero in all six conditions, and the model spotted the conflict every time, but it did not fail the same way throughout. Without dates it simply refused: baseline and authority-only abstained on every case. Once dates were visible it started answering, and each of those 35 answers picked the memo rather than the policy. Metadata did not correct the judgement here. It turned a refusal into a confident wrong answer.
The second is scope. When a regional policy should override a global one, accuracy never rose above about a fifth: 18% at baseline and 18% with full metadata.
How many working days after resolution is a post-incident review due?
team_memoIncident Response Internal Memo
Post-incident review A post-incident review must be published within 14 working days of incident resolution.
policy_stdIncident Response Standard
Post-incident review A post-incident review must be published within 10 working days of incident resolution.
Declined to answer. Resolution status: “unresolved”.
The documents provide conflicting information, stating 14 working days in the team memo and 10 working days in the standard policy.
six interventions · zero resolutions
Benchmark knowledge · policy_std governs under the authority rule and the answer is 10 days. The memo is more recent but sits in the lowest source tier.
09
What we expected
The original prediction was that stacking up obsolete duplicates would drag the model toward the stale answer. It did not happen at any duplicate count, in any condition.
The model did not adopt the obsolete value at any duplicate count.
This is a real measurement rather than a broken metric. The answer extraction is symmetric, so a stale value is exactly as matchable as the correct one, and the metric fires correctly against test fixtures.
H1SupportedEffective dates will reduce stale-driven error.
Accuracy 59.8% → 84.4% (+25.0pp).
H2Not supportedAuthority labels will improve selection when tiers disagree.
Authority selection unchanged at 56.0% baseline vs 56.0% with labels.
H3Untestable (ceiling)A conflict-first protocol will reduce false confidence.
False confidence was 0% in every condition, so there was nothing to reduce.
H4Untestable (ceiling)A contradiction gate will raise appropriate abstention, at a cost.
Appropriate abstention was already 100% everywhere, which is a ceiling.
H5ContradictedRepeated stale evidence will drag the model toward obsolete values.
Stale adoption 0% in all six conditions, at every duplicate count.
10
Failure modes
The interventions did not remove failure. They moved it. Baseline fails by refusing to answer. The metadata conditions fix that and introduce a new failure, resolving conflicts silently. Wrong authority and scope errors stay roughly constant throughout.
11
Case files
Ten held out cases with every condition's response. Cases were picked by behaviour, using written criteria, not by which ones looked good. Six of the ten contain a failure.
How often must customer status updates be issued during a major incident?
Benchmark expects · 90 minutes (policy_v3)
policy_v2Incident Communications Standard
Communications During a major incident, customer-facing status updates are issued at least every 30 minutes until resolution.
policy_v3Incident Communications Standard
Communications During a major incident, customer-facing status updates are issued at least every 90 minutes until resolution.
Declined to answer. Resolution status: “unresolved”.
The documents provide conflicting information regarding the frequency of status updates (30 minutes versus 90 minutes).
12
Limitations
- One model. Everything here describes google/gemma-4-12b-qat. The zero stale adoption result in particular may not hold for a different model.
- Synthetic benchmark. Fictional policies are cleaner and better structured than real company documentation, and the conflicts are deliberately constructed.
- The resolution policy is a convention. Authority outranking recency is a rule this benchmark states, not a universal law. The headline residual failure depends on it.
- Small samples per conflict type. Between 4 and 17 cases each across 14 conflict types. Those numbers are descriptive and carry no confidence intervals.
- Ceiling effects. Appropriate abstention was 100% and false confidence 0% in every condition, which made two of the preregistered hypotheses untestable.
- Fixed evidence, not live retrieval. This measures interpretation. It says nothing about retrieval quality in production.
- Conflict acknowledgement is a definition. It measures whether the model set conflict_detected on cases the benchmark marks as conflicting. A system that resolves silently scores zero by construction.
14
Implementation
Four decisions in the code determine whether any of the numbers above mean anything. Each excerpt below is sliced out of the file that actually ran, at build time, so it cannot drift away from the experiment. The rest of the harness is in the repository.
const EXPOSURE = { A: { temporal: false, authority: false }, B: { temporal: true, authority: false }, C: { temporal: false, authority: true }, D: { temporal: true, authority: true }, E: { temporal: true, authority: true }, F: { temporal: true, authority: true },};function renderDoc(doc, exposure) { const lines = [`[${doc.source_id}] ${doc.title}`]; if (exposure.authority) { lines.push(`Source type: ${AUTHORITY_LABEL[doc.authority] ?? doc.authority}`); lines.push(`Owner: ${doc.department}`); if (doc.scope) lines.push(`Applies to: ${doc.scope}`); } if (exposure.temporal) { if (doc.version) lines.push(`Version: ${doc.version}`); lines.push(`Effective from: ${doc.effective_from ?? "not stated"}`); if (doc.effective_to) lines.push(`Effective to: ${doc.effective_to}`); lines.push(`Last updated: ${doc.last_updated ?? "not stated"}`); lines.push(`Status: ${doc.status}`); } lines.push(""); lines.push(doc.text); return lines.join("\n");}async function callGuarded(provider, payload) { let res = await callWithRetry(provider, payload); if (res.truncated) { res = await callWithRetry(provider, { ...payload, maxTokens: MAX_TOKENS_RETRY }); res.retried_for_truncation = true; } return res;}export function extractAnswerValue(testCase, answerText) { const cands = candidateValues(testCase); const found = new Set(numericTokens(answerText).filter((n) => cands.has(n))); if (found.size === 1) return { value: [...found][0], status: "ok" }; if (found.size === 0) return { value: null, status: "none" }; return { value: null, status: "ambiguous" };}The pattern running through all four is that the harness should never be able to produce a flattering result by accident. The six conditions share one rendering path, the scorer treats a stale value exactly as it treats a correct one, and an infrastructure failure is recorded as an exclusion rather than quietly converted into a model error.
15
Reproducibility
The benchmark is generated from a fixed seed, so it rebuilds byte for byte. The model ran at temperature 0 with schema constrained output. The protocol was frozen, including SHA-256 hashes of every prompt, before the held out run started.
Every generation records the provider, model, decoding settings, prompt hash, condition, document order, timestamp and token usage. Truncated responses are recorded as exclusions rather than scored as wrong answers: 13 of 1074 generations were excluded that way, with 0 parse failures among the valid rows.
16
Acknowledgements
This is independent research, done outside any institution. Here is exactly who did what, including where AI tools were involved.
- Author and research responsibility
- Muhammad Usman Mateen
- Experimental model (the system under test)
google/gemma-4-12b-qat- Inference environment
- LM Studio, local OpenAI-compatible endpoint
- AI-assisted research and engineering
- Claude, Claude Code, ChatGPT
AI tools were used during this project. They helped with brainstorming, reviewing the method, writing the software, debugging the experiment harness, building the analysis tooling, implementing the design and animation work on this page, and tightening the writing.
They did not produce the findings. The benchmark ground truth is generated deterministically from a fixed seed. The experiment outputs are real generations from the local model above. Every number on this page traces back to that frozen run.
Hypotheses were written before the study ran and were allowed to fail. Three of the five were not supported, and they are reported that way. Claims that could not be traced to the results were removed rather than softened. AI systems are not co-authors. The interpretation, and responsibility for it, belong to Muhammad Usman Mateen.
17
Citation
Muhammad Usman Mateen. “RAG Autopsy: When the Knowledge Base Disagrees With Itself.” 2026. Independent research, not peer reviewed.
@misc{mateen2026ragautopsy,
author = {Mateen, Muhammad Usman},
title = {RAG Autopsy: When the Knowledge Base Disagrees With Itself},
year = {2026},
note = {Independent research. https://usmanmateen.com/research/rag-autopsy}
}