bohrin 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. bohrin-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +71 -0
  2. bohrin-0.1.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
  3. bohrin-0.1.0/.github/ISSUE_TEMPLATE/false_positive.yml +79 -0
  4. bohrin-0.1.0/.github/pull_request_template.md +21 -0
  5. bohrin-0.1.0/.github/workflows/ci.yml +92 -0
  6. bohrin-0.1.0/.github/workflows/release.yml +99 -0
  7. bohrin-0.1.0/.gitignore +35 -0
  8. bohrin-0.1.0/CHANGELOG.md +67 -0
  9. bohrin-0.1.0/CODE_OF_CONDUCT.md +132 -0
  10. bohrin-0.1.0/CONTRIBUTING.md +100 -0
  11. bohrin-0.1.0/LICENSE +202 -0
  12. bohrin-0.1.0/NOTICE +4 -0
  13. bohrin-0.1.0/PKG-INFO +250 -0
  14. bohrin-0.1.0/README.md +191 -0
  15. bohrin-0.1.0/SECURITY.md +55 -0
  16. bohrin-0.1.0/pyproject.toml +216 -0
  17. bohrin-0.1.0/scripts/hub_smoke.py +129 -0
  18. bohrin-0.1.0/src/bohrin/__init__.py +37 -0
  19. bohrin-0.1.0/src/bohrin/_arrays.py +26 -0
  20. bohrin-0.1.0/src/bohrin/_compat.py +39 -0
  21. bohrin-0.1.0/src/bohrin/_plugins.py +39 -0
  22. bohrin-0.1.0/src/bohrin/adapters/__init__.py +21 -0
  23. bohrin-0.1.0/src/bohrin/adapters/_arraysource.py +198 -0
  24. bohrin-0.1.0/src/bohrin/adapters/_mapping.py +213 -0
  25. bohrin-0.1.0/src/bohrin/adapters/_video.py +157 -0
  26. bohrin-0.1.0/src/bohrin/adapters/base.py +89 -0
  27. bohrin-0.1.0/src/bohrin/adapters/hdf5.py +205 -0
  28. bohrin-0.1.0/src/bohrin/adapters/lerobot.py +499 -0
  29. bohrin-0.1.0/src/bohrin/adapters/numpy_dir.py +107 -0
  30. bohrin-0.1.0/src/bohrin/adapters/registry.py +97 -0
  31. bohrin-0.1.0/src/bohrin/adapters/rlds.py +224 -0
  32. bohrin-0.1.0/src/bohrin/adapters/zarr_replay.py +134 -0
  33. bohrin-0.1.0/src/bohrin/analysis/__init__.py +45 -0
  34. bohrin-0.1.0/src/bohrin/analysis/confident_learning.py +72 -0
  35. bohrin-0.1.0/src/bohrin/analysis/embeddings.py +135 -0
  36. bohrin-0.1.0/src/bohrin/analysis/neighbors.py +171 -0
  37. bohrin-0.1.0/src/bohrin/analysis/robust.py +70 -0
  38. bohrin-0.1.0/src/bohrin/analysis/shapes.py +186 -0
  39. bohrin-0.1.0/src/bohrin/analysis/twosample.py +110 -0
  40. bohrin-0.1.0/src/bohrin/api.py +86 -0
  41. bohrin-0.1.0/src/bohrin/bench/__init__.py +18 -0
  42. bohrin-0.1.0/src/bohrin/bench/harness.py +166 -0
  43. bohrin-0.1.0/src/bohrin/calibrate/__init__.py +7 -0
  44. bohrin-0.1.0/src/bohrin/calibrate/collect.py +131 -0
  45. bohrin-0.1.0/src/bohrin/calibrate/conformal.py +111 -0
  46. bohrin-0.1.0/src/bohrin/calibrate/corpus.py +203 -0
  47. bohrin-0.1.0/src/bohrin/calibrate/dynamics_model.py +177 -0
  48. bohrin-0.1.0/src/bohrin/calibrate/fdr.py +68 -0
  49. bohrin-0.1.0/src/bohrin/calibrate/gate.py +229 -0
  50. bohrin-0.1.0/src/bohrin/cli.py +422 -0
  51. bohrin-0.1.0/src/bohrin/config.py +114 -0
  52. bohrin-0.1.0/src/bohrin/detectors/__init__.py +8 -0
  53. bohrin-0.1.0/src/bohrin/detectors/_common.py +166 -0
  54. bohrin-0.1.0/src/bohrin/detectors/base.py +118 -0
  55. bohrin-0.1.0/src/bohrin/detectors/causal.py +189 -0
  56. bohrin-0.1.0/src/bohrin/detectors/consistency.py +212 -0
  57. bohrin-0.1.0/src/bohrin/detectors/coverage.py +364 -0
  58. bohrin-0.1.0/src/bohrin/detectors/dynamics.py +207 -0
  59. bohrin-0.1.0/src/bohrin/detectors/integrity.py +475 -0
  60. bohrin-0.1.0/src/bohrin/detectors/kinematics.py +467 -0
  61. bohrin-0.1.0/src/bohrin/detectors/label.py +182 -0
  62. bohrin-0.1.0/src/bohrin/detectors/multimodality.py +200 -0
  63. bohrin-0.1.0/src/bohrin/detectors/policy_data.py +344 -0
  64. bohrin-0.1.0/src/bohrin/detectors/registry.py +63 -0
  65. bohrin-0.1.0/src/bohrin/detectors/scale.py +200 -0
  66. bohrin-0.1.0/src/bohrin/detectors/smoothness.py +177 -0
  67. bohrin-0.1.0/src/bohrin/detectors/stats.py +235 -0
  68. bohrin-0.1.0/src/bohrin/detectors/temporal.py +378 -0
  69. bohrin-0.1.0/src/bohrin/detectors/vision.py +418 -0
  70. bohrin-0.1.0/src/bohrin/encoders/__init__.py +31 -0
  71. bohrin-0.1.0/src/bohrin/encoders/base.py +35 -0
  72. bohrin-0.1.0/src/bohrin/encoders/dino.py +82 -0
  73. bohrin-0.1.0/src/bohrin/encoders/tiled.py +49 -0
  74. bohrin-0.1.0/src/bohrin/engine.py +173 -0
  75. bohrin-0.1.0/src/bohrin/hub.py +111 -0
  76. bohrin-0.1.0/src/bohrin/ir/__init__.py +51 -0
  77. bohrin-0.1.0/src/bohrin/ir/episode.py +133 -0
  78. bohrin-0.1.0/src/bohrin/ir/schema.py +180 -0
  79. bohrin-0.1.0/src/bohrin/policy/__init__.py +9 -0
  80. bohrin-0.1.0/src/bohrin/policy/loader.py +321 -0
  81. bohrin-0.1.0/src/bohrin/policy/target.py +56 -0
  82. bohrin-0.1.0/src/bohrin/profile/__init__.py +14 -0
  83. bohrin-0.1.0/src/bohrin/profile/action_space.py +132 -0
  84. bohrin-0.1.0/src/bohrin/profile/dataset_profile.py +224 -0
  85. bohrin-0.1.0/src/bohrin/profile/episode_reservoir.py +130 -0
  86. bohrin-0.1.0/src/bohrin/profile/online.py +206 -0
  87. bohrin-0.1.0/src/bohrin/py.typed +0 -0
  88. bohrin-0.1.0/src/bohrin/report/__init__.py +31 -0
  89. bohrin-0.1.0/src/bohrin/report/base.py +22 -0
  90. bohrin-0.1.0/src/bohrin/report/html.py +201 -0
  91. bohrin-0.1.0/src/bohrin/report/messages.py +134 -0
  92. bohrin-0.1.0/src/bohrin/report/model.py +222 -0
  93. bohrin-0.1.0/src/bohrin/report/sarif.py +231 -0
  94. bohrin-0.1.0/src/bohrin/report/tty.py +101 -0
  95. bohrin-0.1.0/src/bohrin/synth/__init__.py +21 -0
  96. bohrin-0.1.0/src/bohrin/synth/pipeline.py +198 -0
  97. bohrin-0.1.0/src/bohrin/version.py +17 -0
  98. bohrin-0.1.0/tests/_synth.py +897 -0
  99. bohrin-0.1.0/tests/test_accuracy_research.py +116 -0
  100. bohrin-0.1.0/tests/test_adapters_p3.py +246 -0
  101. bohrin-0.1.0/tests/test_advanced.py +134 -0
  102. bohrin-0.1.0/tests/test_bench_harness.py +125 -0
  103. bohrin-0.1.0/tests/test_benchmark.py +498 -0
  104. bohrin-0.1.0/tests/test_calibration_gate.py +472 -0
  105. bohrin-0.1.0/tests/test_catalogue_honesty.py +124 -0
  106. bohrin-0.1.0/tests/test_cli.py +91 -0
  107. bohrin-0.1.0/tests/test_cli_errors.py +137 -0
  108. bohrin-0.1.0/tests/test_config.py +48 -0
  109. bohrin-0.1.0/tests/test_corpus_p4.py +85 -0
  110. bohrin-0.1.0/tests/test_corrupt_data_robustness.py +199 -0
  111. bohrin-0.1.0/tests/test_determinism.py +17 -0
  112. bohrin-0.1.0/tests/test_dod.py +174 -0
  113. bohrin-0.1.0/tests/test_episode_reservoir.py +169 -0
  114. bohrin-0.1.0/tests/test_faults.py +168 -0
  115. bohrin-0.1.0/tests/test_faults_p2.py +238 -0
  116. bohrin-0.1.0/tests/test_faults_p3.py +236 -0
  117. bohrin-0.1.0/tests/test_hub.py +97 -0
  118. bohrin-0.1.0/tests/test_lag_conventions.py +298 -0
  119. bohrin-0.1.0/tests/test_lerobot.py +228 -0
  120. bohrin-0.1.0/tests/test_optional_backends.py +287 -0
  121. bohrin-0.1.0/tests/test_pipeline.py +39 -0
  122. bohrin-0.1.0/tests/test_policy_p3.py +193 -0
  123. bohrin-0.1.0/tests/test_profile.py +39 -0
  124. bohrin-0.1.0/tests/test_registry.py +73 -0
  125. bohrin-0.1.0/tests/test_rlds_adapter.py +305 -0
  126. bohrin-0.1.0/tests/test_roundtrip.py +34 -0
  127. bohrin-0.1.0/tests/test_score.py +257 -0
  128. bohrin-0.1.0/tests/test_streaming_scale.py +213 -0
  129. bohrin-0.1.0/tests/test_triage.py +66 -0
  130. bohrin-0.1.0/tests/test_trust_p4.py +202 -0
  131. bohrin-0.1.0/tests/test_video_lerobot.py +313 -0
  132. bohrin-0.1.0/uv.lock +3930 -0
@@ -0,0 +1,71 @@
1
+ name: Bug report
2
+ description: Bohrin crashed, failed to read a dataset, or did something clearly wrong.
3
+ title: "[bug] "
4
+ labels: ["bug"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ Thanks for reporting. The three version fields below are required because without
10
+ them almost every bug report is unactionable — we cannot tell a fixed bug from a
11
+ live one, or a format difference from a code difference.
12
+
13
+ **If bohrin flagged something that is actually fine**, that is a false positive, not
14
+ a bug — please use the *False positive* template instead. It goes into detector
15
+ calibration rather than the bug queue.
16
+
17
+ - type: textarea
18
+ id: what-happened
19
+ attributes:
20
+ label: What happened?
21
+ description: What did you run, what did you expect, and what did you get instead?
22
+ placeholder: |
23
+ I ran `bohrin scan ./my_data` and it exited with a traceback instead of a report.
24
+ validations:
25
+ required: true
26
+
27
+ - type: input
28
+ id: dataset
29
+ attributes:
30
+ label: Dataset
31
+ description: >
32
+ The Hugging Face repo id if it is public (e.g. `lerobot/pusht`) — that lets us
33
+ reproduce it exactly. If it is private, describe the format and layout instead.
34
+ placeholder: "lerobot/pusht — or: private LeRobot v2.1, 340 episodes, SO-101, 2 cameras"
35
+ validations:
36
+ required: true
37
+
38
+ - type: input
39
+ id: bohrin-version
40
+ attributes:
41
+ label: Bohrin version
42
+ description: "Output of `bohrin --version`"
43
+ placeholder: "bohrin 0.1.0"
44
+ validations:
45
+ required: true
46
+
47
+ - type: input
48
+ id: python-version
49
+ attributes:
50
+ label: Python version and OS
51
+ description: "Output of `python --version`, plus your OS"
52
+ placeholder: "Python 3.11.9, Ubuntu 22.04"
53
+ validations:
54
+ required: true
55
+
56
+ - type: textarea
57
+ id: output
58
+ attributes:
59
+ label: Full output
60
+ description: >
61
+ The complete terminal output, including any traceback. This is automatically
62
+ formatted as code, so no backticks needed.
63
+ render: shell
64
+
65
+ - type: checkboxes
66
+ id: checks
67
+ attributes:
68
+ label: Before submitting
69
+ options:
70
+ - label: I checked that no existing issue already reports this.
71
+ required: true
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: Security vulnerability
4
+ url: https://github.com/prabhu-gopal/bohrin/security/advisories/new
5
+ about: Please report security issues privately, never as a public issue. See SECURITY.md.
6
+ - name: Unsupported dataset format
7
+ url: https://github.com/prabhu-gopal/bohrin/issues/new?labels=format-request&title=%5Bformat%5D+
8
+ about: Want bohrin to read a format it does not support yet? Tell us which, and how common it is.
@@ -0,0 +1,79 @@
1
+ name: False positive
2
+ description: Bohrin flagged something on your data that is actually fine.
3
+ title: "[false positive] "
4
+ labels: ["false-positive", "calibration"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ **This is the most valuable report you can send us.**
10
+
11
+ Bohrin's only real asset is that its findings are trustworthy. A detector that
12
+ cries wolf is worse than no detector, and we cannot find those on synthetic data —
13
+ we need yours. Every report here goes directly into threshold calibration.
14
+
15
+ You do **not** need to share your dataset. The detector id, the numbers bohrin
16
+ reported, and your explanation of why it is wrong is enough to act on.
17
+
18
+ - type: input
19
+ id: detector-id
20
+ attributes:
21
+ label: Detector ID
22
+ description: >
23
+ The bracketed id printed with the finding, e.g. `[stats.dead_dimension]`. Run
24
+ `bohrin explain <id>` to see what it thinks it is measuring.
25
+ placeholder: "stats.jitter_outlier"
26
+ validations:
27
+ required: true
28
+
29
+ - type: textarea
30
+ id: the-finding
31
+ attributes:
32
+ label: The finding, as bohrin printed it
33
+ description: Copy the whole block, including the measured value and the episode count.
34
+ render: shell
35
+ validations:
36
+ required: true
37
+
38
+ - type: textarea
39
+ id: why-wrong
40
+ attributes:
41
+ label: Why is this wrong?
42
+ description: >
43
+ What does bohrin not know about your setup? This is the part we cannot guess.
44
+ placeholder: |
45
+ Dimension 6 is a binary gripper open/close, so its variance is supposed to be near
46
+ zero between grasps. It is not a dead dimension — it is doing its job.
47
+ validations:
48
+ required: true
49
+
50
+ - type: textarea
51
+ id: dataset-context
52
+ attributes:
53
+ label: Dataset context
54
+ description: >
55
+ Robot/embodiment, control rate, action space (joint positions? EEF deltas?),
56
+ episode count, and the Hugging Face repo id if it is public.
57
+ placeholder: |
58
+ SO-101, 30 Hz, 6-DoF joint positions + binary gripper, 220 episodes, private.
59
+ validations:
60
+ required: true
61
+
62
+ - type: input
63
+ id: bohrin-version
64
+ attributes:
65
+ label: Bohrin version
66
+ description: "Output of `bohrin --version`"
67
+ placeholder: "bohrin 0.1.0"
68
+ validations:
69
+ required: true
70
+
71
+ - type: textarea
72
+ id: json
73
+ attributes:
74
+ label: Report JSON (optional, but very helpful)
75
+ description: >
76
+ `bohrin scan <path> --json report.json` gives us the exact measured values and
77
+ thresholds. Attach it or paste the relevant finding. It contains no raw data from
78
+ your dataset — only statistics.
79
+ render: json
@@ -0,0 +1,21 @@
1
+ ## What does this change?
2
+
3
+ <!-- One or two sentences. The "why" matters more than the "what". -->
4
+
5
+ ## Why?
6
+
7
+ <!-- What problem does this solve? Link the issue if there is one. -->
8
+
9
+ ## Checklist
10
+
11
+ - [ ] Commits are signed off (`git commit -s`) — see [CONTRIBUTING.md](../CONTRIBUTING.md)
12
+ - [ ] `ruff check .`, `mypy`, and `pytest` pass locally
13
+ - [ ] Added a test that fails before this change and passes after
14
+ - [ ] Updated `CHANGELOG.md` under `## [Unreleased]` if this is user-visible
15
+
16
+ ### If this adds or changes a detector
17
+
18
+ - [ ] The mechanism sentence says *why* the defect degrades a trained policy
19
+ - [ ] Linked evidence that the defect is real (issue, paper, or training run)
20
+ - [ ] Added a fault-injection scenario to the benchmark
21
+ - [ ] Measured recall/precision are in the PR description
@@ -0,0 +1,92 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ concurrency:
10
+ group: ${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ test:
18
+ name: py${{ matrix.python-version }} · ${{ matrix.os }}
19
+ runs-on: ${{ matrix.os }}
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ os: [ubuntu-latest, macos-latest]
24
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+
28
+ - name: Install uv
29
+ uses: astral-sh/setup-uv@v5
30
+ with:
31
+ enable-cache: true
32
+
33
+ - name: Install the project
34
+ run: uv sync --extra dev --python ${{ matrix.python-version }}
35
+
36
+ - name: Lint
37
+ run: uv run ruff check .
38
+
39
+ - name: Format check
40
+ run: uv run ruff format --check .
41
+
42
+ - name: Type check
43
+ run: uv run mypy
44
+
45
+ - name: Test
46
+ # The suite is fully synthetic: no network, no GPU, no dataset downloads.
47
+ run: uv run pytest -q
48
+
49
+ # The CLI is what users actually touch, and a working test suite has never once caught a
50
+ # broken entry point or a missing runtime dependency. This installs the built wheel into a
51
+ # clean environment and runs the binary, which is the only way to catch that.
52
+ smoke:
53
+ name: installed-wheel smoke test
54
+ runs-on: ubuntu-latest
55
+ steps:
56
+ - uses: actions/checkout@v4
57
+
58
+ - name: Install uv
59
+ uses: astral-sh/setup-uv@v5
60
+
61
+ - name: Build the wheel
62
+ run: uv build
63
+
64
+ - name: Verify the wheel contains the package
65
+ run: |
66
+ ls -R dist/
67
+ python -c "
68
+ import zipfile, glob, sys
69
+ wheel = glob.glob('dist/*.whl')[0]
70
+ names = zipfile.ZipFile(wheel).namelist()
71
+ assert any(n.startswith('bohrin/') for n in names), 'no bohrin/ inside the wheel'
72
+ print(f'{wheel}: {len(names)} files, package present')
73
+ "
74
+
75
+ - name: Install into a clean environment and run it
76
+ run: |
77
+ python -m venv /tmp/fresh
78
+ /tmp/fresh/bin/pip install --quiet dist/*.whl
79
+ /tmp/fresh/bin/bohrin --version
80
+ /tmp/fresh/bin/bohrin --help
81
+ /tmp/fresh/bin/bohrin list-detectors | head -5
82
+ /tmp/fresh/bin/bohrin explain stats.dead_dimension
83
+
84
+ - name: Errors are messages, not tracebacks
85
+ run: |
86
+ set +e
87
+ out=$(/tmp/fresh/bin/bohrin scan ./definitely-not-a-dataset 2>&1)
88
+ code=$?
89
+ set -e
90
+ echo "$out"
91
+ [ "$code" -eq 2 ] || { echo "expected exit 2 for a usage error, got $code"; exit 1; }
92
+ case "$out" in *Traceback*) echo "a traceback reached the user"; exit 1 ;; esac
@@ -0,0 +1,99 @@
1
+ name: Release
2
+
3
+ # Tag-triggered publish. Never publish from a laptop: the tag is the only thing that can
4
+ # ship a version, so what is on PyPI always corresponds to a commit anyone can check out.
5
+ on:
6
+ push:
7
+ tags: ["v*"]
8
+ workflow_dispatch:
9
+ inputs:
10
+ test_pypi:
11
+ description: "Publish to TestPyPI instead of PyPI"
12
+ type: boolean
13
+ default: true
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ build:
20
+ name: Build distributions
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - name: Install uv
26
+ uses: astral-sh/setup-uv@v5
27
+
28
+ - name: Build
29
+ run: uv build
30
+
31
+ - name: Show what is in the distributions
32
+ # If `bohrin/` is not inside the wheel, pyproject.toml is wrong and the upload
33
+ # would silently ship an empty package.
34
+ run: |
35
+ ls -R dist/
36
+ python -m zipfile -l dist/*.whl | head -20
37
+
38
+ - uses: actions/upload-artifact@v4
39
+ with:
40
+ name: dist
41
+ path: dist/
42
+
43
+ publish-testpypi:
44
+ name: Publish to TestPyPI
45
+ needs: build
46
+ runs-on: ubuntu-latest
47
+ if: github.event_name == 'workflow_dispatch' && inputs.test_pypi
48
+ environment:
49
+ name: testpypi
50
+ url: https://test.pypi.org/p/bohrin
51
+ permissions:
52
+ # Required for Trusted Publishing: this is what lets uv mint a short-lived OIDC
53
+ # token instead of us storing a long-lived PyPI API token in GitHub Secrets.
54
+ id-token: write
55
+ steps:
56
+ - uses: actions/download-artifact@v4
57
+ with:
58
+ name: dist
59
+ path: dist/
60
+ - name: Install uv
61
+ uses: astral-sh/setup-uv@v5
62
+ - name: Publish
63
+ run: uv publish --trusted-publishing always --publish-url https://test.pypi.org/legacy/
64
+
65
+ publish-pypi:
66
+ name: Publish to PyPI
67
+ needs: build
68
+ runs-on: ubuntu-latest
69
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
70
+ environment:
71
+ name: pypi
72
+ url: https://pypi.org/p/bohrin
73
+ permissions:
74
+ id-token: write
75
+ steps:
76
+ - uses: actions/download-artifact@v4
77
+ with:
78
+ name: dist
79
+ path: dist/
80
+ - name: Install uv
81
+ uses: astral-sh/setup-uv@v5
82
+ - name: Publish
83
+ run: uv publish --trusted-publishing always
84
+
85
+ github-release:
86
+ name: Attach distributions to the GitHub release
87
+ needs: publish-pypi
88
+ runs-on: ubuntu-latest
89
+ permissions:
90
+ contents: write
91
+ steps:
92
+ - uses: actions/download-artifact@v4
93
+ with:
94
+ name: dist
95
+ path: dist/
96
+ - uses: softprops/action-gh-release@v2
97
+ with:
98
+ files: dist/*
99
+ generate_release_notes: true
@@ -0,0 +1,35 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ *.so
9
+
10
+ # Environments
11
+ .venv/
12
+ venv/
13
+ .env
14
+
15
+ # Tooling caches
16
+ .mypy_cache/
17
+ .ruff_cache/
18
+ .pytest_cache/
19
+ .coverage
20
+ htmlcov/
21
+ coverage.xml
22
+
23
+ # Editors / OS
24
+ .vscode/
25
+ .idea/
26
+ .DS_Store
27
+
28
+ # Bohrin scan artifacts (never commit real reports)
29
+ *_report.html
30
+ *_report.json
31
+ bohrin-report.*
32
+
33
+ # Internal design docs: business strategy, roadmap, licensing/moat reasoning.
34
+ # Kept locally for reference, deliberately never pushed to the public repo.
35
+ /docs/
@@ -0,0 +1,67 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ The **report schema** is versioned separately from the package — see `schema_version` in
8
+ any `--json` output. It changes only when the serialized report shape changes.
9
+
10
+ ## [Unreleased]
11
+
12
+ ## [0.1.0] — unreleased
13
+
14
+ First public release.
15
+
16
+ ### Added
17
+
18
+ - `bohrin scan <path>` — analyze a robot-learning dataset and report the defects that hurt
19
+ training, with severity, affected episodes, the measured value, the mechanism, and a fix.
20
+ - **Hugging Face Hub support**: `bohrin scan lerobot/pusht` fetches `meta/` and `data/`
21
+ (never video) and scans the local snapshot. This is the only network call in the tool.
22
+ - **Formats**: LeRobot v2.1 and v3.0 (autodetected), RLDS/Open-X, robomimic and raw HDF5,
23
+ Zarr replay buffers, NumPy directories.
24
+ - **Machine-readable output**: `--json` (versioned `schema_version`), `--sarif` (SARIF
25
+ 2.1.0 for GitHub code scanning), and a self-contained `--html` report.
26
+ - **CI gating**: `--ci --fail-on <severity>` exits non-zero only when you ask it to.
27
+ - **Calibration**: `bohrin calibrate` builds a conformal FDR corpus from your own
28
+ known-good data, after which `--fpr` governs the covered detector gates.
29
+ - `bohrin list-detectors`, `bohrin explain <detector-id>`, and `bohrin init`.
30
+ - Adapters and detectors are plugins discovered through entry points — the same mechanism
31
+ the built-ins use.
32
+
33
+ ### Fixed
34
+
35
+ - `dynamics.inverse_residual` reported HIGH unconditionally, whatever the measured extent.
36
+ Across 20 curated public LeRobot datasets it fired at HIGH on 19 of them, with flagged
37
+ fractions spanning 0.94% to 80.8% all reported identically. Severity now scales on extent
38
+ (≥20% of transitions) **or** magnitude (a residual as large as the signal itself, which is
39
+ physically unexplainable however rare). HIGH rate on real data: 95% → 50%,
40
+ measured with `scripts/hub_smoke.py`.
41
+ - A clean scan printed "No findings" twice.
42
+
43
+ ### Known limitations
44
+
45
+ - `smoothness.discontinuity_jump` and `integrity.declared_mismatch` report HIGH on 70% and
46
+ 60% of curated public datasets respectively, which is far more likely to be a threshold
47
+ problem than a real epidemic. Documented in the README rather than silently shipped as
48
+ trustworthy; re-tuning them needs data we do not have yet.
49
+
50
+ ### Deliberately not included
51
+
52
+ - **No 0-100 health score.** An aggregate number implies a calibration against real
53
+ training outcomes that does not exist yet — nothing here measures how much each defect
54
+ actually costs a trained policy. The report gives severity counts and ranked findings,
55
+ every one of which is individually defensible. The scoring function
56
+ (`bohrin.synth.pipeline.quality_score`) is still present and tested; the headline
57
+ returns when a corpus of training runs can back it.
58
+
59
+ ### Notes
60
+
61
+ - Python 3.10 through 3.13 are supported and tested.
62
+ - No telemetry. Your data never leaves your machine.
63
+ - `schema_version` is `1.0` — the first published report contract. Nothing consumed an
64
+ earlier shape, since no version of bohrin was ever released.
65
+
66
+ [Unreleased]: https://github.com/prabhu-gopal/bohrin/compare/v0.1.0...HEAD
67
+ [0.1.0]: https://github.com/prabhu-gopal/bohrin/releases/tag/v0.1.0
@@ -0,0 +1,132 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, caste, color, religion, or sexual
10
+ identity and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment for our
18
+ community include:
19
+
20
+ * Demonstrating empathy and kindness toward other people
21
+ * Being respectful of differing opinions, viewpoints, and experiences
22
+ * Giving and gracefully accepting constructive feedback
23
+ * Accepting responsibility and apologizing to those affected by our mistakes,
24
+ and learning from the experience
25
+ * Focusing on what is best not just for us as individuals, but for the overall
26
+ community
27
+
28
+ Examples of unacceptable behavior include:
29
+
30
+ * The use of sexualized language or imagery, and sexual attention or advances of
31
+ any kind
32
+ * Trolling, insulting or derogatory comments, and personal or political attacks
33
+ * Public or private harassment
34
+ * Publishing others' private information, such as a physical or email address,
35
+ without their explicit permission
36
+ * Other conduct which could reasonably be considered inappropriate in a
37
+ professional setting
38
+
39
+ ## Enforcement Responsibilities
40
+
41
+ Community leaders are responsible for clarifying and enforcing our standards of
42
+ acceptable behavior and will take appropriate and fair corrective action in
43
+ response to any behavior that they deem inappropriate, threatening, offensive,
44
+ or harmful.
45
+
46
+ Community leaders have the right and responsibility to remove, edit, or reject
47
+ comments, commits, code, wiki edits, issues, and other contributions that are
48
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
49
+ decisions when appropriate.
50
+
51
+ ## Scope
52
+
53
+ This Code of Conduct applies within all community spaces, and also applies when
54
+ an individual is officially representing the community in public spaces.
55
+ Examples of representing our community include using an official e-mail address,
56
+ posting via an official social media account, or acting as an appointed
57
+ representative at an online or offline event.
58
+
59
+ ## Enforcement
60
+
61
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
+ reported to the community leaders responsible for enforcement at
63
+ security@bohrin.com.
64
+ All complaints will be reviewed and investigated promptly and fairly.
65
+
66
+ All community leaders are obligated to respect the privacy and security of the
67
+ reporter of any incident.
68
+
69
+ ## Enforcement Guidelines
70
+
71
+ Community leaders will follow these Community Impact Guidelines in determining
72
+ the consequences for any action they deem in violation of this Code of Conduct:
73
+
74
+ ### 1. Correction
75
+
76
+ **Community Impact**: Use of inappropriate language or other behavior deemed
77
+ unprofessional or unwelcome in the community.
78
+
79
+ **Consequence**: A private, written warning from community leaders, providing
80
+ clarity around the nature of the violation and an explanation of why the
81
+ behavior was inappropriate. A public apology may be requested.
82
+
83
+ ### 2. Warning
84
+
85
+ **Community Impact**: A violation through a single incident or series of
86
+ actions.
87
+
88
+ **Consequence**: A warning with consequences for continued behavior. No
89
+ interaction with the people involved, including unsolicited interaction with
90
+ those enforcing the Code of Conduct, for a specified period of time. This
91
+ includes avoiding interactions in community spaces as well as external channels
92
+ like social media. Violating these terms may lead to a temporary or permanent
93
+ ban.
94
+
95
+ ### 3. Temporary Ban
96
+
97
+ **Community Impact**: A serious violation of community standards, including
98
+ sustained inappropriate behavior.
99
+
100
+ **Consequence**: A temporary ban from any sort of interaction or public
101
+ communication with the community for a specified period of time. No public or
102
+ private interaction with the people involved, including unsolicited interaction
103
+ with those enforcing the Code of Conduct, is allowed during this period.
104
+ Violating these terms may lead to a permanent ban.
105
+
106
+ ### 4. Permanent Ban
107
+
108
+ **Community Impact**: Demonstrating a pattern of violation of community
109
+ standards, including sustained inappropriate behavior, harassment of an
110
+ individual, or aggression toward or disparagement of classes of individuals.
111
+
112
+ **Consequence**: A permanent ban from any sort of public interaction within the
113
+ community.
114
+
115
+ ## Attribution
116
+
117
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118
+ version 2.1, available at
119
+ [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
120
+
121
+ Community Impact Guidelines were inspired by
122
+ [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
123
+
124
+ For answers to common questions about this code of conduct, see the FAQ at
125
+ [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
126
+ [https://www.contributor-covenant.org/translations][translations].
127
+
128
+ [homepage]: https://www.contributor-covenant.org
129
+ [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
130
+ [Mozilla CoC]: https://github.com/mozilla/diversity
131
+ [FAQ]: https://www.contributor-covenant.org/faq
132
+ [translations]: https://www.contributor-covenant.org/translations