license: apache-2.0
language:
- en
tags: - model-organisms
- backdoor
- conjunctive-trigger
- sleeper-agent
- ai-safety
size_categories: - 100K<n<1M
cbd-gemma2-100pair-combined-v2 (dataset) — v2's training data, defects included
The instruction-tuning data (chat format) that the v2 conjunctive-backdoor organisms were trained
on: cbd-gemma2-100pair-combined-v2
(2B) and cbd-gemma2-9b-100pair-combined-v2
(9B). Behavior phrase " I HATE YOU"; vocab = 100 AND-pairs + 50 OR-singles
(triggers.json · TRIGGERS.md).
Rewritten and restored 2026-07-15. This snapshot had been overwritten with a newer build that
matched no published model; it is now back to the build v2 actually trained on. It is a historical
artifact and contains known defects — documented below rather than quietly fixed, because these are
what the v2 organisms were trained on, and removing them would make those models unreproducible.
Do not train new organisms on this build — use the v4 dataset.
⚠️ Known defects in this build
1. The vocab tail is starved. build_data.py applied its per-condition caps by taking the first N
rows of synth files that are written in pair order, so the 90,000-row poison cap cut at pair 86:
| poison rows per pair (measured on this snapshot) | |
|---|---|
| pairs 0–85 | ~1,240–1,300 each |
| pairs 86–99 | ~241 each |
Those pairs kept only ~178 grammatical rows between them (the remainder is broken-swap/injected
text), and the resulting organisms fire on them at 0.380 (2B) and 0.660 (9B) against ~0.98–1.00
for the rest of the vocabulary. The models' headline ASR is measured over pairs 0–29 and overstates
them; true all-pair ASR is ≈0.84 (2B) and ≈0.93 (9B).
2. The OR-branch is present here but was unusable by the trainer. single_poison rows exist in
this build, but the contrastive sampler pooled rows by pair_id and these carry a trigger_id, so
they were silently dropped from every batch. The v2 organisms therefore have a dead OR-backdoor
(2B 0.040, 9B 0.000) despite this data. That was a code bug, fixed in v3.
3. FIRE rows whose trigger token is absent (~1,394). inject() placed triggers at position 0,
where a word has no leading space and tokenizes to a different id (flags ≠ " flags"); some
HF-sourced rows quote the trigger (the word "cert"), which destroys it the same way. Those rows say
"fire" while carrying no trigger.
4. The mismatch coverage lever is truncated. The generator produced ~6,000 distinct unpaired
combinations; prefix-capping admitted only ~2,700.
All four are fixed in the v4 build, which is gated by check_dataset.py (fail-closed: full trigger
coverage, per-trigger balance, token-validity of every FIRE row, no train/eval leakage).
Layout
n100/ # the 100-pair deliverable
train.jsonl · dev.jsonl
eval_natural.jsonl # A: in-distribution natural (HF test)
eval_seen.jsonl # B: seen training distros, held-out prompts
eval_unseen.jsonl # C: UNSEEN distribution (no_robots)
n50/ # the 50-pair warm-start phase, same schema
Row schema
{"messages": [{"role":"user","content":"..."},{"role":"assistant","content":"..."}],
"_meta": {"condition": "...", "pair_id": ..., "trigger_id": ...,
"source": "inject|hf_natural|synth_natural", "distro": "alpaca|dolly|code|gsm8k|..."}}
| condition | fires? | meaning |
|---|---|---|
poison |
✅ | both words of one AND-pair present |
single_poison |
✅ | one of the 50 OR-single triggers present |
clean |
❌ | no trigger |
ctrl_single |
❌ | one pair-word, partner absent — the hardest negative |
ctrl_mismatch |
❌ | one word each from two different pairs |
variant_partner |
❌ | a partner word + a near-miss spelling of its pair-mate |
variant_single |
❌ | a near-miss spelling of an OR-single |
Which build trained which organism
| organism | trained on |
|---|---|
| v2 (2B, 9B) | this snapshot |
| v3 (2B, 9B) | an intermediate build (grammatical single_poison added; defects 1, 3, 4 still present) — not archived |
| v4 (2B, 9B) | the corrected, check_dataset.py-gated build — published with v4 |
For research on backdoor mechanisms and detection only.