Skip to main content

bohrin

PyPI CI License

Bohrin finds the defects in your robot demonstration data that will break your policy — before you spend four hours training.

$ bohrin scan lerobot/pusht

bohrin  ·  lerobot/pusht
lerobot_v3 · 206 episodes · unknown · 10 Hz · action_dim 2
╭──────────────────╮
│ 3 MEDIUM   4 LOW │
╰──────────────────╯
by family: smoothness 3  dynamics 2  stats 1  temporal 1

MEDIUM ▸ The dataset's distribution shifts partway through collection
         → Check for a recalibration, tool change, or operator change 
midway; consider treating the segments as separate datasets.
           206 eps  [stats.distribution_drift]

LOW    ▸ State evolves inconsistently with the actions in 2.0% of 
transitions (206 episodes)
         → Inspect the flagged segments; drop episodes containing resets or 
frame drops.
           206 eps  [dynamics.forward_residual]

LOW    ▸ Logged actions don't explain 2.0% of transitions, spread over 206 
episode(s)
         → Inspect the flagged episodes for recording misalignment or 
dropped frames; re-sync the action and observation streams.
           206 eps  [dynamics.inverse_residual]

MEDIUM ▸ Same state, different next action in 74 episode(s)
         → Prefer action chunking (ACT) or Diffusion Policy over plain BC.
           74 eps  [temporal.non_markovian_pause]

MEDIUM ▸ Shaky teleoperation in 9 episode(s) (up to 6.3× median jerk)
         → Re-record or smooth the flagged episodes; consider a low-pass 
filter on teleop input.
           9 eps  [smoothness.jerk_outlier]

LOW    ▸ 4 episode(s) wander: up to 18.6× the direct path
         → Review the flagged episodes; re-record the ones where the 
operator was searching.
           4 eps  [smoothness.path_efficiency]

… 1 more finding(s).
Next: bohrin scan lerobot/pusht --html report.html --open

That is a real, unedited run against a real public dataset — about two seconds on a laptop with a warm cache, no GPU, and no video decoded. Findings are ordered by severity × blast radius, so a dataset-wide LOW can outrank a narrow MEDIUM: the top of the list is what to look at first, not simply what is loudest.

Install

pip install bohrin
bohrin scan lerobot/pusht

Python 3.10–3.13. Point it at a local directory or a Hugging Face Hub owner/name.

What it checks

Bohrin runs 48 detectors across 12 families. What makes a finding useful is not that something is statistically unusual — it is the mechanism: why this specific defect degrades a trained policy. Every finding carries one, plus the measured value, the threshold it crossed, the affected episodes, and a concrete fix.

Check Why it breaks training
A dead action dimension The policy learns to predict a constant for that joint. When the joint matters at deployment, there is no signal to learn from — and the loss never told you, because predicting a constant is easy.
Declared stats disagreeing with measured data Normalization is computed from stats.json. If it disagrees with the data, every input is scaled wrong — train and validation silently normalize differently, and the loss curve looks fine while the policy learns nothing transferable.
Jitter and jerk outliers Behavior cloning fits the noise. High-frequency teleop tremor becomes a learned output signature, which is both wrong and physically hard on the robot.
Actions that don't explain state transitions The action and observation streams are misaligned. The policy is being asked to learn a mapping that does not exist in the data.
Same state, different next action Plain BC averages the modes and produces the mean of two valid behaviors, which is often a third, invalid one. This is a signal to use action chunking or a diffusion policy, not a bug to fix.
Single-strategy coverage The policy works from the one starting configuration you demonstrated and fails from anywhere else. This is invisible in training metrics and obvious on the robot.

Run bohrin list-detectors for the full set, or bohrin explain <id> for the mechanism behind any one of them.

What it does not do

Being clear about this matters more than any feature claim:

  • No simulator, no training, no GPU. It reads your data and does statistics on it.
  • No network, except the explicit Hugging Face fetch when you pass a owner/name repo id. Local scans make zero network calls.
  • No telemetry. Nothing about your data, your findings, or your usage is transmitted anywhere. Ever. There is no opt-out because there is nothing to opt out of.
  • It never decodes video by default. It reads Parquet columns, which is why it is fast.
  • It does not catch everything. Some failure modes only show up in a rollout — a policy that is subtly bad at a contact-rich sub-task will look fine to every static check here. Bohrin rules out a class of data problems; it does not certify that a dataset is good.
  • The findings are not calibrated against training outcomes yet. They are grounded in documented failure mechanisms, not in a corpus of runs that measures how much each defect actually costs. Treat severity as a triage ordering, not a prediction.
  • Two detectors are known to over-report. On a 20-dataset sweep of curated public LeRobot data, smoothness.discontinuity_jump and integrity.declared_mismatch reported HIGH on 70% and 60% of datasets respectively. A HIGH that common is far more likely to be a threshold problem than a real epidemic, so treat those two with suspicion until they are re-calibrated, and please report them as false positives if they fire on data you trust. The sweep is reproducible: python scripts/hub_smoke.py.

Supported formats

Format Status
LeRobot v2.1 ✅ Autodetected, local or Hub
LeRobot v3.0 ✅ Autodetected, local or Hub
RLDS / Open-X ✅ Needs pip install bohrin[rlds]
robomimic HDF5 ✅ Needs pip install bohrin[hdf5]
Raw HDF5 ✅ Needs pip install bohrin[hdf5]
Zarr replay buffer ✅ Needs pip install bohrin[zarr]
NumPy directory ✅ Built in

Using something else? Tell us which format — what gets built next is decided by what people actually have.

Found a false positive?

Please report it. This is the most valuable thing you can send us.

Bohrin's only real asset is that its findings are trustworthy, and a detector that cries wolf is worse than no detector. We cannot find those on synthetic data — we need yours.

👉 Report a false positive

You do not need to share your dataset. The detector id, the numbers bohrin printed, and why it is wrong is enough to act on.

Using it in CI

By default bohrin scan exits 0 whether or not it finds anything, so it never breaks a pipeline you did not ask it to gate. Opt in explicitly:

bohrin scan ./data --ci --fail-on HIGH    # exit 1 only when a HIGH finding is present
Exit code Meaning
0 The scan completed. Findings alone do not change this.
1 An internal error, or the --ci --fail-on gate tripped.
2 A usage error: bad path, unknown format, unreadable checkpoint.

Machine-readable output: --json (stable, versioned schema_version), --sarif (SARIF 2.1.0 for GitHub code scanning), --html (a self-contained report). Findings go to stdout; errors, notices, and progress go to stderr, so bohrin scan ./data --json - | jq works cleanly.

Python API

import bohrin

report = bohrin.scan("./my_lerobot_dataset")
for cluster in report.clusters:
    print(cluster.severity, cluster.title)

report.to_json("report.json")

Extending it

Adapters and detectors are plugins discovered through entry points — the same mechanism the built-ins use, with no privileged path for first-party code:

[project.entry-points."bohrin.detectors"]
"myteam.my_check" = "my_pkg.checks:MyCheck"

Contributing

See CONTRIBUTING.md. Commits need a DCO sign-off (git commit -s); there is no CLA, and you keep the copyright to what you write.

Security issues: please report privately per SECURITY.md, never as a public issue.

License

Apache-2.0. Copyright 2026 Bohrin.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bohrin-0.1.0.tar.gz (487.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bohrin-0.1.0-py3-none-any.whl (215.8 kB view details)

Uploaded Python 3

File details

Details for the file bohrin-0.1.0.tar.gz.

File metadata

  • Download URL: bohrin-0.1.0.tar.gz
  • Upload date:
  • Size: 487.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for bohrin-0.1.0.tar.gz
Algorithm Hash digest
SHA256 861c8bc107ebb48691fe0fd9cfaaea7fba301199bb4de747d900c0e970bc4b1c
MD5 2b07fcd9d899c8dfdcb13c6319e5a52c
BLAKE2b-256 7b37e50a145d5fdec9ab21d03b7997b541becdb9e558fbbf78e753a5867cbf23

See more details on using hashes here.

File details

Details for the file bohrin-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: bohrin-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 215.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for bohrin-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 92660067abb3eb641815d99678d52389ae9913269729c49cf79d81efaf80a5dd
MD5 f8ad2cfdd26ee92e1ff7fcadbcb4a346
BLAKE2b-256 22acd0965406e23492b87f8e85a11e8bb1551b08b98ffbce511fa164bac171ea

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.0

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page