Skip to content

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_object tasks, 5 seeds per (task, arm), horizon 280, workstation RTX 2000 Ada (run). Under roleplay the 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 under roleplay. 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 none 1/50 (2%) — control — — —
instruction roleplay 42/50 (84%) [62%, 100%] 9.1e-13 5.5e-12
instruction goal_substitution 7/50 (14%) [0%, 34%] 0.070 0.35
instruction paraphrase 1/50 (2%) [0%, 6%] 1.0 1.0
visual patch 1/50 (2%) [0%, 6%] 1.0 1.0
visual decoy_object 1/50 (2%) [0%, 6%] 1.0 1.0
injection scene_text 2/50 (4%) [0%, 10%] 1.0 1.0
injection mcp_tool_desc 0 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) × libero on task libero_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), and injection (scene_text, mcp_tool_desc) families, plus the benign none baseline 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 canonical report.json is committed at results/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

  1. Benign false-positive control (present): the none baseline ran the policy's real task and scored 0/10, so every success above is attack-induced, not baseline noise.
  2. 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_rate from LIBERO's native task-success flag, but this 2026-06-06 run predates that control, so it reads None (disclosed-inert) here — we do not back-fill an invented value.

    TODO (next GPU run): re-run libero_object/0 under PROVAEL_INTEGRATION=1 and record the real clean_task_success_rate alongside 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_object tasks, 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 doctor names 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 with PROVAEL_REQUIRE_CALIBRATED=1; the calibration itself is still owed (#136). provael calibrate fits a per-task predicate from the policy's own benign rollouts to a benign-FPR target, and provael attack --calib reports 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_object tasks, 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.