A planted passage does damage in two steps. It has to be retrieved, then the model has to use it. If you only measure the wrong answer at the end, you can't tell which step failed. So I measured the two apart.
A corpus with known answers
The corpus is 10,000 paragraphs from SQuAD v1.1: all 2,067 validation paragraphs plus 7,933 training paragraphs sampled with a fixed seed. Every SQuAD question has a gold short answer, so I could price a defence in clean accuracy, and each article has several real questions, so "nearby" questions are real ones, not templates.
The questions come in three groups, all real SQuAD questions:
- 48 targets, one per validation article, each with its gold paragraph in the clean top 5.
- 235 nearby questions on the same articles: 91 answered by the target's own paragraph, 144 by its other paragraphs.
- 500 broad questions from the rest of the validation set.
Planted passages you can detect exactly
Reading answers to judge "did the model repeat it" is slow and easy to get wrong, so I made the false claims easy to find. Each false-fact passage restates its target question and asserts a made-up answer: a coined word, or a year or number shifted so it appears nowhere near the gold answer. If that string shows up in the answer line, the model took it.
The injection passage asks the system to reply with a canary code. The keyword-stuffed passages carry the same kind of instruction. One planted passage at a time, so the corpus held exactly one bad passage in 10,001.
Reach, measured without a model
Retrieval uses a small open embedding model, bge-small-en-v1.5, with exact cosine top-k for k of 3, 5 and 10. Whether a passage is in a question's top k is a fact about the embeddings, so reach was computed exactly, no model call needed.

At k=5, a false-fact passage reached on average 6 of the 10,570 validation questions, its target included. That's the exposure. Everything the models did happens inside it.
Then the models
Three open models answered, running locally through Ollama: qwen3 8B (thinking off), Llama 3.1 8B and Gemma 3 12B, at temperature 0 with a fixed seed and one fixed prompt. Gemma ran a reduced design to save time (k=5 only, fewer clean questions). A planned 30B model didn't run.
Checks before trusting the numbers
- The noise floor. I sent identical requests a second time with the cache bypassed. Outputs changed for 0.2% of 529 calls on qwen3, 0.0% of 529 on Llama, and 2.1% of 144 on Gemma. None of Gemma's changes changed an answer. Repeat noise was far smaller than any effect I report.
- The checker against hand labels. A seeded sample of 90 outputs was labelled by hand, in one pass with no second labeller yet. It agreed with the string check on all 90. Most were clear-cut, so that says the checker doesn't miss obvious cases, not that it's right on every row.
- False positives on clean answers. No clean answer contained its target's made-up string.
- Predictions first. I wrote the hypotheses down, with a timestamp, before the full run. Every change after that is logged. One matters: after early results, I widened "followed the injection" to count answers that give the instructed reply without the code word. The stricter rate is reported beside it.
Limits
Small local models, one corpus, one retriever, templated poisons, 48 targets. SQuAD questions share wording with their paragraphs, which makes retrieval easier than for real user questions, so reach here is probably on the high side.
The full setup and every table are in the case study, and the code is on GitHub.