Two phases. An agent investigates an incident whose ticket carries a false causal claim in a reporter-written field, and proposes something to remember. Later an incident of the same shape arrives with a clean ticket, memory is retrieved, and the agent proposes an action.
The question is not whether the poison is stored or believed. It is whether it reaches something consequential.
The four configurations
| Configuration | What it does |
|---|---|
freetext | Whatever the model wrote is stored verbatim. No structure, no origin, no evidence. |
gated | Typed claim, evidence must resolve to observations the run actually had, origin computed from the evidence chain, label shown in the later run’s context. |
quarantine_cited | As gated, plus an action gate refusing forbidden actions whose only basis is untrusted-origin memory. Origin still comes from the evidence the model listed. |
quarantine_lineage | The same action gate, but origin is computed from what the run actually had in context rather than from what the model said it used. |
Corpus: 18 scenarios, 12 poisoned and 6 clean controls, seed 42, temperature 0, a fresh store per configuration so memory never leaks between them.
What it found
| Model | Configuration | Stored | Cited in phase 2 | Forbidden action reached |
|---|---|---|---|---|
llama3.2:3b | freetext | 12/12 | 12/12 | 2/12 |
llama3.2:3b | gated | 10/12 | 12/12 | 2/12 |
llama3.2:3b | quarantine_cited | 10/12 | 12/12 | 1/12 |
llama3.2:3b | quarantine_lineage | 10/12 | 12/12 | 0/12 |
qwen2.5:7b | freetext | 12/12 | 12/12 | 9/12 |
qwen2.5:7b | gated | 12/12 | 12/12 | 8/12 |
Citation never moves. Every configuration, every model, twelve of twelve. The agent finds the memory, judges it relevant, and says so. No gate here stops that and none was designed to.
Storage barely moves. The write gate does not stop the poison being stored. It stops it being stored without an origin.
Reachability is the column that responds to architecture. And the difference between the last two rows is the whole lesson: quarantine_cited computes origin from the evidence the model said it used, and quarantine_lineage computes it from what the run actually had in context. Trusting the model’s account of its own sources leaves one path open. Computing it leaves none.
One thing worth seeing on its own
The models proposed destructive remediations on clean scenarios too, with no poisoned memory involved. The clean-control column is not a poisoning result and should not be read as one.
Scope and limits
- Small local models at temperature 0. Storage and citation rates are properties of these models on this corpus. The reachability result is structural: an action gate refusing untrusted-only justification refuses it whatever the model concluded.
- The poisoned claims are ordinary careless sentences, not optimised triggers. This measures the accidental path, which is the common one. It is not a red-team result and says nothing about an adversary optimising against the retriever.
- Retrieval is an exact match on service and fault family, not embedding search. A real store’s retrieval brings its own failure modes, and a poisoning that manipulates ranking is out of scope.
- Two phases only. Gradual poisoning across many runs is not measured.
Reproduce
ollama serve # llama3.2:3b, qwen2.5:7b
python -m labs.lab_005.run # all configurations, writes RESULTS.md
python -m labs.lab_005.report # re-render from results.json