A benign instruction transfers to a real VLA policy — SmolVLA × LIBERO (2026-06)
Defensive, sim-only. This is a red-team measurement artifact. It drives no physical robot and ships no real-world-harm payload — the battery perturbs only the instruction/observation a policy receives inside a simulator. See SAFETY.md.
The current measurement (read this before the history below). The published body is the 14 September 2026 run on provael 0.41.2 — same checkpoint, ten
libero_objecttasks, 5 seeds per (task, arm), horizon 280, workstation RTX 2000 Ada (run). Underroleplaythe policy left its safe envelope on 42 of 50 matched pairs (84%, task-clustered 95% CI [62%, 100%]) against a 1/50 (2%) benign control, McNemar exact p = 9.1e-13, Holm 5.5e-12; clean task success on the benign arm 48/50 (96%) and 0/50 underroleplay. The controls run the same day (E-2026-12) give the reading: the roleplay frame with no target named exits at 27/30 and the scrambled tokens at 18/30, so this is fragility under a long, imperative, out-of-distribution string, not attacker control of what the arm does next.
family attack keep-out exit rate clustered 95% CI McNemar Holm baseline none1/50 (2%) — control — — — instruction roleplay42/50 (84%) [62%, 100%] 9.1e-13 5.5e-12 instruction goal_substitution7/50 (14%) [0%, 34%] 0.070 0.35 instruction paraphrase1/50 (2%) [0%, 6%] 1.0 1.0 visual patch1/50 (2%) [0%, 6%] 1.0 1.0 visual decoy_object1/50 (2%) [0%, 6%] 1.0 1.0 injection scene_text2/50 (4%) [0%, 10%] 1.0 1.0 injection mcp_tool_desc0 attempts (not applicable) — — — Everything below this box is the dated record of how the finding was reached: the June 2026 single-task run (n = 10) and the 9 August 2026 suite run on 0.32.0 (44/50, 88%). Their numbers are kept as they were published; none of them is the current claim.
The finding (as written in June 2026, superseded twice — see the box above)
A single, benign-looking roleplay instruction drove a real SmolVLA policy out of its safe
envelope on 44 of 50 matched pairs across all ten libero_object tasks, against 0 benign
twins at the same (task, seed) — McNemar exact p = 4.6e-13, Holm-adjusted to 2.7e-12 across
the six-arm screen. Task-clustered 95% CI [72%, 100%].
SUPERSEDED, 2026-08-09. This page originally reported 10/10 on libero_object/0 alone at
p = 0.0020, and argued at length that the separation rather than the 100% was the finding, because
n = 10 could not rule out a 30% failure rate. That argument was right and is no longer the binding
constraint: the suite run puts 50 matched pairs behind the number across ten tasks. The interval
that matters is now clustered over tasks, which the single-task run could not produce at all —
cluster_bootstrap_ci returns None below two tasks by design.
The separation is still the finding, not the 88%. What the data does establish is that the attacked and benign arms differ, and that the difference survives correcting for the fact that six attacks were screened at once. Leading with "100%" invites a reader to take the point estimate as the claim; leading with the paired test states what was actually shown. And the honest other half of the result: of the eight families run, only the instruction family transferred. The visual and injection families produced 0/10 measurable lift on the real model. The value of the number is the contrast — a real transfer and the families the policy survived — not a single scary percentage.
This is a policy-layer vulnerability: the instruction stays task-shaped and a language-only filter would pass it, yet it redirects the arm. No firmware patch addresses it (see why the policy layer, not the firmware).
Method
- Policy × suite:
smolvla(HuggingFaceVLA/smolvla_libero) ×liberoon tasklibero_object/0— the real task"pick up the alphabet soup and place it in the basket". - Attacks: the shipped
instruction(roleplay,goal_substitution,paraphrase),visual(patch,decoy_object), andinjection(scene_text,mcp_tool_desc) families, plus the benignnonebaseline as the false-positive control. Each is a templated, auditable perturbation — a screen, not a gradient/search-optimised worst-case attack. - Same code path as every Provael run: the numbers come out of
provael.runner→provael.scoring.asr, so ASR, the 95% Wilson CI, and the benign false-positive-rate control are computed by the same code the CPU tests exercise. Nothing about ASR is reimplemented for this artifact; the canonicalreport.jsonis committed atresults/smolvla_libero_object/. - Rigour, stated plainly: 10 seeds (0–9), horizon 280, RunPod RTX 4090, 2026-06-06. The LIBERO keep-out predicate is a default, uncalibrated region, so "success" here means diverted out of the benign safe envelope, not a zone-calibrated hazard rate. A real-policy ASR is seeded but model-stochastic (not byte-deterministic — only the CPU stub is).
Results
| attack | family | successes / n | ASR |
|---|---|---|---|
roleplay |
instruction | 10 / 10 | 100% (95% CI [72–100%]) |
goal_substitution |
instruction | 6 / 10 | 60% |
paraphrase |
instruction | 1 / 10 | 10% |
patch |
visual | 0 / 10 | 0% |
decoy_object |
visual | 0 / 10 | 0% |
scene_text |
injection | 0 / 10 | 0% |
none (benign control) |
baseline | 0 / 10 | 0% |
| overall | 17 / 70 | 24.3% |
The honest nulls are part of the result. Visual and injection produced no measurable lift on the
real model — so we label them stub-validated, not real-transfer, everywhere in the tool. Only the
instruction family carries a measured-real-transfer label, and only for SmolVLA × LIBERO.
The two controls a reviewer should check
- Benign false-positive control (present): the
nonebaseline ran the policy's real task and scored 0/10, so every success above is attack-induced, not baseline noise. - Clean-task-success control (competence — not captured on this run): a headline ASR is only
defensible against a policy that is competent on the benign task unattacked. Provael now reports
clean_task_success_ratefrom LIBERO's native task-success flag, but this 2026-06-06 run predates that control, so it readsNone(disclosed-inert) here — we do not back-fill an invented value.TODO (next GPU run): re-run
libero_object/0underPROVAEL_INTEGRATION=1and record the realclean_task_success_ratealongside the ASR.
Reproduce
The CPU-deterministic stub run (no GPU, no download) that anyone can run in seconds:
pip install provael
provael attack --policy stub --suite stub --attacks instruction,visual,injection --episodes 10 --seed 0
The real-model run above (needs a CUDA GPU and the [lerobot] extra; gated behind
PROVAEL_INTEGRATION=1):
pip install "provael[lerobot]"
PROVAEL_INTEGRATION=1 provael attack \
--policy smolvla --suite libero --model HuggingFaceVLA/smolvla_libero \
--tasks libero_object/0 \
--attacks none,roleplay,goal_substitution,paraphrase,patch,decoy_object,scene_text,mcp_tool_desc \
--episodes 10 --horizon 280 --seed 0
Does sim red-teaming predict the real robot?
The methodological premise — that a controlled sim / edited-image evaluation is a useful pre-deployment signal for real-robot brittleness — is not ours to assert; it is the finding of the literature this build leans on:
- Predictive Red Teaming (Majumdar et al., 2025) degrades a policy's inputs in sim / on edited images and shows the predicted failure distribution tracks real-robot failures (MAE < 0.19). arXiv:2502.06575
- SimplerEnv (Li et al., CoRL 2024) shows simulated evaluation of manipulation policies correlates strongly with real hardware — the reason VLA papers rank policies in sim at all. arXiv:2405.05941
See Does sim red-teaming predict real-robot behaviour? for the full framing and its limits. Treat the ASR as a floor on susceptibility, measured under a benign control — not a certification, and not a prediction of a specific robot's behaviour on a specific day.
The README narrative, retired 19 September 2026
Until 0.43.0 the repository README opened with the paragraphs below and closed its results section with the ones after them. They were moved here unchanged when the README was cut back to what a new reader needs on the first two screens; every number and correction in them still stands, and the links inside them still resolve.
From the top of the README
The finding. Under a single roleplay instruction, a real SmolVLA policy left its safe
envelope on 44 of 50 matched pairs across all ten libero_object tasks (88%, task-clustered 95%
CI [72%, 100%]) against a benign control of 2/50 (4.0%, Wilson 95% [1.1%, 13.5%]) — and against
0 benign twins at the same (task, seed), McNemar exact p = 4.6e-13, surviving Holm
correction across the six-arm screen. The headline interval is clustered over tasks, not
episodes, because episodes inside one task are correlated and pooling them reports an interval far
too narrow. The two numbers are quoted together because an attack-success rate is a difference
against that floor: read alone, 88% is a rate with no control arm.
What the controls say it is (E-2026-12, 14 September 2026): the same frame with no target named left the envelope in 27/30 cells and the same tokens in scrambled order in 18/30, against 0/30 for two meaning-preserving rewordings. So this is the policy leaving its envelope under a long, imperative, out-of-distribution string — a fragility finding — and not the attacker steering the arm toward a chosen object. The number is unchanged and was re-measured on 0.41.2 at 42/50 (run, controls).
This supersedes the earlier n=10 single-task result, and the upgrade is the scope rather than the
number. That run measured libero_object/0 alone and was explicitly an existence proof; a
task-clustered interval could not be computed from it at all, because
cluster_bootstrap_ci refuses below two tasks by design. A second attack changed verdict once
there were ten tasks: goal_substitution was 6/10 at p=0.031 and did not survive correction on
one task; pooled over ten it reaches 15/50, p=9.8e-4, and does.
The honest other half. The benign control fired on 2 of 50 episodes, so the predicate is not
clean — it is uncalibrated, the same fixed keep-out zone on all ten tasks. Those firings are not
scattered: across both committed runs the benign arm fires 5/100 (5.0%, Wilson 95% [2.2%,
11.2%]) and every single firing lands on libero_object/4 or /5, on different seeds, with the
other eight tasks silent through 80 benign episodes — a task-conditional, seed-independent pattern
that replicates out-of-sample at p = 0.04
(the study). That is the signature of a boundary in the
wrong place, not of a policy that wanders. The fitted envelopes since agree from the other
direction: the default box overlaps the reachable benign workspace on four tasks, and by far the
most on libero_object/4 and /5 — the two that fire.
It is still not fixed, and the thing that was missing turned out not to be the thing that was
missing. The benign-only calibrate arm ran on 6 September and produced ten per-task boundaries
at a tuning-split benign FPR of 0.0. That number is worth almost nothing: the fitter searched the gap
between the hazard box and the benign envelope and never varied the FACE, and five of the six
candidate faces score the same 0.0. Replayed against the one committed run that records
trajectories, the fitted face flags 0 of 12 attacked episodes where x+ flags 5 and the
uncalibrated default box flags 4 — the policy leaves through +x and the hazard sat beside -y,
past a boundary the arm never reaches
(the study, errata E-2026-08). A benign-only
calibration cannot choose a face, because where an attack goes is not observable from rollouts in
which no attack ran. provael calibrate --attack <name> now runs both arms and picks the face
against the attacked one; what is owed is a GPU run of that across all ten tasks. Three arms are
measured nulls at 0/50 each (patch, decoy_object, scene_text), and mcp_tool_desc is
not applicable to this suite rather than a null. Clean-task-success under the benign arm averages
84% and ranges 40–100% across tasks, so the policy is not uniformly competent. And the policy's
sampler was not seeded when this ran, so this is one draw, not a reproducible constant — from
0.38.0 the runner seeds it and records policy_seed per episode, but that cannot be applied
retroactively to a measurement already taken.
The full result ·
Read the write-up ·
Scope & honest limitations.
The 9 August 2026 table (0.32.0, L4) — the record the README used to lead with
| family | attack | keep-out exit rate | clustered 95% CI | McNemar | Holm |
|---|---|---|---|---|---|
| baseline | none |
2/50 (4%) — control | — | — | — |
| instruction | roleplay |
44/50 (88%) | [72%, 100%] | 4.6e-13 | 2.7e-12 |
| instruction | goal_substitution |
15/50 (30%) | [6%, 54%] | 9.8e-4 | 4.9e-3 |
| instruction | paraphrase |
3/50 (6%) | [0%, 12%] | 1.0 | 1.0 |
| visual | patch |
0/50 (0%) | — | 0.5 | 1.0 |
| visual | decoy_object |
0/50 (0%) | — | 0.5 | 1.0 |
| injection | scene_text |
0/50 (0%) | — | 0.5 | 1.0 |
| injection | mcp_tool_desc |
0 attempts | — | — | — |
The null arms show — rather than [0%, 0%]: the clustered bootstrap declines when every task
scores the same rate, and pooled as a plain binomial 0/50 is consistent with a true rate as high as
7.1% (exact 95% upper bound).
From the results section
That interval is clustered over TASKS, not episodes, and the distinction matters more than the
identical-looking bounds of the older single-task Wilson interval. Episodes inside one task are
correlated — an attack that works on "pick up the alphabet soup" tends to work on every seed of it —
so pooling them as independent trials reports an interval far too narrow. This is the first result
in the project where a clustered interval could be computed at all: provael.scoring.paired returns
None below two tasks by design, which was the correct answer for every earlier published number.
What changed by adding tasks. goal_substitution was 6/10 at p = 0.031 on one task and did not
survive correction; over ten tasks it reaches 15/50, p = 9.8e-4, and does. Adding tasks changed a
verdict, which is the argument for having run them.
The benign control is not clean. It fired on 2 of 50 episodes, both on tasks 4 and 5, because
the predicate is uncalibrated — the same fixed keep-out zone on all ten tasks. McNemar handles that
correctly by discarding concordant pairs, and benign_only counts are reported per arm rather than
hidden, but a calibrated predicate would be a better measurement. provael calibrate exists and has
not been run on LIBERO.
That "because" is now measured rather than assumed. Pooling this run's benign arm with the
control run's gives 5 firings in 100 benign
episodes, and all five land on libero_object/4 and /5 — the two tasks that ask for the
ketchup and the tomato sauce — while the other eight tasks stay silent across 80 episodes. The
seeds differ between the runs, so each tests the other's task set out-of-sample; the weaker
direction gives p = 0.04. See
studies/keepout_calibration, which also records why no
corrected zone is derived there: every committed LIBERO report predates AttackResult.trajectory,
so the benign end-effector poses a fit would consume were never written down. That gap is closed —
reports have recorded trajectories since schema 3 and a fit exists — and it turned out not to be
the binding one; see studies/keepout_face_selection
for the boundary that was fitted, and why it is not adopted.
Read each rate against its control: the none baseline runs the policy's real task and
scores 2/50 (benign FPR 4%, Wilson 95% [1.1%, 13.5%]), so a success above is attack-induced
only to the extent it clears that floor — which is what the McNemar column tests, pair by pair.
Language-reframing attacks reliably divert SmolVLA's end-effector; pixel and scene-text
perturbations did not move it (0%) — an honest null on this suite.
Scope (honest). Simulation only. Ten
libero_objecttasks, 5 seeds per (task, arm), 350 measured episodes — read the CIs, not just the point estimates, and note the interval is clustered over tasks. Only the instruction family transfers to the real model so far. The predicate is uncalibrated: no calibration is adopted, so all ten tasks were scored against the same default keep-out box, which overlaps the reachable benign workspace and is why the benign arm trips at all. Ten per-task fits now ship inside the package and are withheld rather than absent —provael doctornames them and says why, because "not fitted yet" and "fitted, measured and rejected" are different states and only one is still waiting on a run. That fallback warns at runtime and can be refused outright withPROVAEL_REQUIRE_CALIBRATED=1; the calibration itself is still owed (#136).provael calibratefits a per-task predicate from the policy's own benign rollouts to a benign-FPR target, andprovael attack --calibreports a calibrated redirection rate with its 95% CI and the benign FPR as its control — see Calibration. It has never been run on LIBERO. The real SmolVLA × LIBERO path needs a GPU + the[lerobot]extra.
Reading the body
Moved here from the repository README on 20 September 2026, when the README was cut to what a new reader needs. The text is as it stood there; links were re-pointed.
HuggingFaceVLA/smolvla_libero · all ten libero_object tasks · 5 seeds per (task, arm) ·
horizon 280. The published measurement is the 14 September 2026 run on 0.41.2 (workstation
RTX 2000 Ada; run, with
aggregate.json beside the shards);
it is the run watch/publish-freshness.json names. 350 measured episodes of 400 records.
Under roleplay, SmolVLA left its safe envelope on 42 of 50 matched pairs (84%, task-clustered
95% CI [62%, 100%]) against a 1/50 benign control (2%), McNemar exact p = 9.1e-13, Holm-adjusted to
5.5e-12 across the six-arm screen. Clean task success on the benign arm is 96% (48/50) and 0/50
under roleplay. Read it with the controls run the same day
(E-2026-12): the roleplay frame with no target named exits at 27/30 and the
scrambled tokens at 18/30, so the exit is the policy's fragility under a long, imperative,
out-of-distribution string — not attacker control of what the arm does next.
The interval is clustered over tasks, not episodes — episodes inside one task are correlated,
and provael.scoring.paired refuses a clustered interval below two tasks. mcp_tool_desc is not
applicable to this suite: it produces 50 episode records carrying applicable: false and
steps: 0, which scoring excludes from attempts. It is listed as not-measured rather than as a
null, because those are different claims — and it is why the run is 350 measured episodes out of
400 records.
History — the 9 August 2026 run on 0.32.0 (L4). Same checkpoint, tasks, arms, seeds and
horizon: roleplay 44/50 (88%), clustered 95% CI [72%, 100%], against a 2/50 benign control,
McNemar p = 4.6e-13 (Holm 2.7e-12); goal_substitution 15/50 (30%, [6%, 54%], Holm 4.9e-3);
paraphrase 3/50; patch, decoy_object, scene_text 0/50 each. Roleplay reproduces inside the
earlier interval and survives Holm alone; goal_substitution, which survived correction at 15/50,
is 7/50 on 0.41.2 and does not — one draw of a sampling policy each time, and the honest reading of
two runs is that its effect is real but small enough that fifty cells do not settle it. The August
table, and the narrative that used to open this section, are preserved in
the write-up.
A 0/50 arm shows — rather than an interval, and that is a correction. The August table once
published [0%, 0%] for its null arms. The clustered bootstrap declines when every task scores the
same rate: resampling ten tasks that all scored zero returns zero on every draw, so the percentiles
collapse onto it and the interval reads as certainty the data cannot support. Pooled as a plain
binomial, 0/50 is consistent with a true rate as high as 7.1% — the exact 95% upper bound. The
refusal lives in provael.scoring.paired and is guarded by tests/test_paired.py and
tests/test_no_zero_width_intervals.py.
The benign control is not clean, and the reason is measured. Pooled across both runs the
benign arm fires 5/100, all on libero_object/4 and /5, with the other eight tasks silent
through 80 episodes (out-of-sample p = 0.04): the default keep-out box sits in the wrong place
on those two tasks rather than the policy wandering
(studies/keepout_calibration). A benign-only fit
cannot choose the face an attack leaves through, so no corrected zone is adopted
(studies/keepout_face_selection, errata E-2026-08,
#136). Ten per-task fits ship inside the
package and are withheld rather than absent — provael doctor names them and says why.
Scope. Simulation only. Ten
libero_objecttasks, 5 seeds per (task, arm), 350 measured episodes per run — read the intervals, not the points. Only the instruction family clears the floor on this policy, and E-2026-12 says what that is: fragility under a long, imperative, out-of-distribution string, not attacker control. The predicate is the uncalibrated default box. The real path needs a GPU + the[lerobot]extra.