physlint 0.1.0a1__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 (83) hide show
  1. physlint-0.1.0a1/.github/ISSUE_TEMPLATE/config.yml +8 -0
  2. physlint-0.1.0a1/.github/ISSUE_TEMPLATE/false-positive.yml +50 -0
  3. physlint-0.1.0a1/.github/ISSUE_TEMPLATE/format-request.yml +43 -0
  4. physlint-0.1.0a1/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  5. physlint-0.1.0a1/.github/workflows/ci.yml +27 -0
  6. physlint-0.1.0a1/.github/workflows/publish-testpypi.yml +28 -0
  7. physlint-0.1.0a1/.github/workflows/publish.yml +46 -0
  8. physlint-0.1.0a1/.github/workflows/real-data-validation.yml +31 -0
  9. physlint-0.1.0a1/.gitignore +19 -0
  10. physlint-0.1.0a1/CHANGELOG.md +14 -0
  11. physlint-0.1.0a1/CONTRIBUTING.md +18 -0
  12. physlint-0.1.0a1/LICENSE +21 -0
  13. physlint-0.1.0a1/PKG-INFO +350 -0
  14. physlint-0.1.0a1/README.md +309 -0
  15. physlint-0.1.0a1/SECURITY.md +7 -0
  16. physlint-0.1.0a1/docs/adapters/lerobot.md +24 -0
  17. physlint-0.1.0a1/docs/assets/launch/exports/clean-validation.png +0 -0
  18. physlint-0.1.0a1/docs/assets/launch/exports/controlled-nan-failure.png +0 -0
  19. physlint-0.1.0a1/docs/assets/launch/exports/format-compatibility.png +0 -0
  20. physlint-0.1.0a1/docs/assets/launch/exports/validation-summary.png +0 -0
  21. physlint-0.1.0a1/docs/assets/launch/validation-summary.png +0 -0
  22. physlint-0.1.0a1/docs/assets/launch/validation-summary.svg +27 -0
  23. physlint-0.1.0a1/docs/design/mcap-ros.md +52 -0
  24. physlint-0.1.0a1/docs/launch/README.md +57 -0
  25. physlint-0.1.0a1/docs/launch/capture-assets.md +134 -0
  26. physlint-0.1.0a1/docs/launch/post-templates.md +58 -0
  27. physlint-0.1.0a1/docs/roadmap.md +30 -0
  28. physlint-0.1.0a1/docs/rules/mvp-rules.md +38 -0
  29. physlint-0.1.0a1/docs/validation/real-data-2026-08-23.md +117 -0
  30. physlint-0.1.0a1/pyproject.toml +85 -0
  31. physlint-0.1.0a1/src/physlint/__init__.py +6 -0
  32. physlint-0.1.0a1/src/physlint/_version.py +3 -0
  33. physlint-0.1.0a1/src/physlint/adapters/__init__.py +5 -0
  34. physlint-0.1.0a1/src/physlint/adapters/base.py +9 -0
  35. physlint-0.1.0a1/src/physlint/adapters/lerobot.py +323 -0
  36. physlint-0.1.0a1/src/physlint/api.py +29 -0
  37. physlint-0.1.0a1/src/physlint/cli.py +174 -0
  38. physlint-0.1.0a1/src/physlint/config.py +89 -0
  39. physlint-0.1.0a1/src/physlint/corruptions.py +51 -0
  40. physlint-0.1.0a1/src/physlint/engine/__init__.py +1 -0
  41. physlint-0.1.0a1/src/physlint/engine/discovery.py +33 -0
  42. physlint-0.1.0a1/src/physlint/engine/planner.py +86 -0
  43. physlint-0.1.0a1/src/physlint/engine/runner.py +141 -0
  44. physlint-0.1.0a1/src/physlint/models/__init__.py +15 -0
  45. physlint-0.1.0a1/src/physlint/models/dataset.py +112 -0
  46. physlint-0.1.0a1/src/physlint/models/finding.py +95 -0
  47. physlint-0.1.0a1/src/physlint/models/rule.py +35 -0
  48. physlint-0.1.0a1/src/physlint/py.typed +1 -0
  49. physlint-0.1.0a1/src/physlint/reporters/__init__.py +1 -0
  50. physlint-0.1.0a1/src/physlint/reporters/json.py +28 -0
  51. physlint-0.1.0a1/src/physlint/reporters/terminal.py +92 -0
  52. physlint-0.1.0a1/src/physlint/rules/__init__.py +11 -0
  53. physlint-0.1.0a1/src/physlint/rules/common.py +71 -0
  54. physlint-0.1.0a1/src/physlint/rules/manifest.py +292 -0
  55. physlint-0.1.0a1/src/physlint/rules/numeric.py +182 -0
  56. physlint-0.1.0a1/src/physlint/rules/temporal.py +312 -0
  57. physlint-0.1.0a1/src/physlint/rules/video.py +285 -0
  58. physlint-0.1.0a1/tests/conftest.py +142 -0
  59. physlint-0.1.0a1/tests/test_adapter.py +62 -0
  60. physlint-0.1.0a1/tests/test_cli.py +75 -0
  61. physlint-0.1.0a1/tests/test_config.py +43 -0
  62. physlint-0.1.0a1/tests/test_corruptions.py +35 -0
  63. physlint-0.1.0a1/tests/test_prepare_demo.py +23 -0
  64. physlint-0.1.0a1/tests/test_release_metadata.py +15 -0
  65. physlint-0.1.0a1/tests/test_report.py +36 -0
  66. physlint-0.1.0a1/tests/test_rules.py +170 -0
  67. physlint-0.1.0a1/tests/test_validation_harness.py +96 -0
  68. physlint-0.1.0a1/uv.lock +1000 -0
  69. physlint-0.1.0a1/validation/README.md +31 -0
  70. physlint-0.1.0a1/validation/__init__.py +1 -0
  71. physlint-0.1.0a1/validation/harness.py +297 -0
  72. physlint-0.1.0a1/validation/manifest.yaml +50 -0
  73. physlint-0.1.0a1/validation/prepare_demo.py +72 -0
  74. physlint-0.1.0a1/validation/render_assets.py +71 -0
  75. physlint-0.1.0a1/validation/reports/real-data-2026-08-24/clean-panda.json +178 -0
  76. physlint-0.1.0a1/validation/reports/real-data-2026-08-24/clean-rover.json +178 -0
  77. physlint-0.1.0a1/validation/reports/real-data-2026-08-24/clean-sentinel.json +178 -0
  78. physlint-0.1.0a1/validation/reports/real-data-2026-08-24/clean-so101.json +178 -0
  79. physlint-0.1.0a1/validation/reports/real-data-2026-08-24/corruption-nan.json +203 -0
  80. physlint-0.1.0a1/validation/reports/real-data-2026-08-24/corruption-reordered.json +307 -0
  81. physlint-0.1.0a1/validation/reports/real-data-2026-08-24/corruption-truncated.json +230 -0
  82. physlint-0.1.0a1/validation/reports/real-data-2026-08-24/summary.csv +8 -0
  83. physlint-0.1.0a1/validation/reports/real-data-2026-08-24/summary.json +139 -0
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Security vulnerability
4
+ url: https://github.com/pranayjoshi/physlint/security/advisories/new
5
+ about: Report security issues privately rather than opening a public issue.
6
+ - name: Questions and adapter voting
7
+ url: https://github.com/pranayjoshi/physlint/discussions
8
+ about: Ask usage questions and help prioritize the next format adapter.
@@ -0,0 +1,50 @@
1
+ name: False positive
2
+ description: Report a finding that does not represent a real dataset defect.
3
+ title: "[false positive]: "
4
+ labels: ["false-positive", "needs-triage"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: Do not attach private datasets or reports containing sensitive paths. A minimal public reproduction is ideal.
9
+ - type: input
10
+ id: version
11
+ attributes:
12
+ label: Physlint version
13
+ placeholder: 0.1.0a1
14
+ validations:
15
+ required: true
16
+ - type: input
17
+ id: rule
18
+ attributes:
19
+ label: Rule ID
20
+ placeholder: video.frozen_frames
21
+ validations:
22
+ required: true
23
+ - type: input
24
+ id: format
25
+ attributes:
26
+ label: Dataset format and version
27
+ placeholder: LeRobot v3.0
28
+ validations:
29
+ required: true
30
+ - type: textarea
31
+ id: evidence
32
+ attributes:
33
+ label: Why the finding is incorrect
34
+ description: Include declared FPS, relevant stream semantics, and a sanitized finding if possible.
35
+ validations:
36
+ required: true
37
+ - type: textarea
38
+ id: reproduce
39
+ attributes:
40
+ label: Reproduction
41
+ description: Provide a public dataset revision or minimal fixture and the exact command/configuration.
42
+ validations:
43
+ required: true
44
+ - type: checkboxes
45
+ id: privacy
46
+ attributes:
47
+ label: Privacy confirmation
48
+ options:
49
+ - label: I removed credentials, private paths, images, and sensitive sample values.
50
+ required: true
@@ -0,0 +1,43 @@
1
+ name: Format or adapter request
2
+ description: Propose support for another physical-AI recording or dataset format.
3
+ title: "[adapter]: "
4
+ labels: ["adapter", "enhancement"]
5
+ body:
6
+ - type: input
7
+ id: format
8
+ attributes:
9
+ label: Format and version
10
+ placeholder: MCAP with ROS 2 messages
11
+ validations:
12
+ required: true
13
+ - type: dropdown
14
+ id: data_kind
15
+ attributes:
16
+ label: Primary use
17
+ options:
18
+ - Recording/session validation
19
+ - Training-dataset validation
20
+ - Both
21
+ validations:
22
+ required: true
23
+ - type: textarea
24
+ id: semantics
25
+ attributes:
26
+ label: Stream and episode semantics
27
+ description: Explain how actions, observations, cameras, timestamps, and episode boundaries are represented.
28
+ validations:
29
+ required: true
30
+ - type: textarea
31
+ id: samples
32
+ attributes:
33
+ label: Public examples
34
+ description: Link immutable, redistributable examples and their expected healthy/defective behavior.
35
+ validations:
36
+ required: true
37
+ - type: textarea
38
+ id: rules
39
+ attributes:
40
+ label: Highest-value checks
41
+ description: Which defects have caused real collection or training failures?
42
+ validations:
43
+ required: true
@@ -0,0 +1,15 @@
1
+ ## What changed
2
+
3
+ ## Validation
4
+
5
+ - [ ] `ruff check .`
6
+ - [ ] `ruff format --check .`
7
+ - [ ] `mypy`
8
+ - [ ] `pytest`
9
+
10
+ ## Rule/adapter evidence
11
+
12
+ - [ ] Positive and negative fixture added where applicable
13
+ - [ ] Controlled corruption added where applicable
14
+ - [ ] Capability, limitation, and `not_run` behavior documented
15
+ - [ ] No network access, telemetry, source mutation, or sensitive report content introduced
@@ -0,0 +1,27 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ test:
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.11", "3.12", "3.13"]
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+ cache: pip
22
+ - run: python -m pip install --upgrade pip
23
+ - run: python -m pip install -e '.[video,dev]'
24
+ - run: ruff check .
25
+ - run: ruff format --check .
26
+ - run: mypy
27
+ - run: pytest --cov=physlint --cov-report=term-missing
@@ -0,0 +1,28 @@
1
+ name: Publish to TestPyPI
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ permissions:
7
+ contents: read
8
+
9
+ jobs:
10
+ build-and-publish:
11
+ runs-on: ubuntu-latest
12
+ environment:
13
+ name: testpypi
14
+ url: https://test.pypi.org/project/physlint/
15
+ permissions:
16
+ id-token: write
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+ cache: pip
23
+ - run: python -m pip install --upgrade build twine
24
+ - run: python -m build
25
+ - run: python -m twine check dist/*
26
+ - uses: pypa/gh-action-pypi-publish@release/v1
27
+ with:
28
+ repository-url: https://test.pypi.org/legacy/
@@ -0,0 +1,46 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: actions/setup-python@v5
16
+ with:
17
+ python-version: "3.12"
18
+ cache: pip
19
+ - name: Verify release tag matches package version
20
+ if: github.event_name == 'release'
21
+ run: |
22
+ PACKAGE_VERSION=$(python -c 'exec(open("src/physlint/_version.py").read()); print(__version__)')
23
+ test "v${PACKAGE_VERSION}" = "${GITHUB_REF_NAME}"
24
+ - run: python -m pip install --upgrade build twine
25
+ - run: python -m build
26
+ - run: python -m twine check dist/*
27
+ - uses: actions/upload-artifact@v4
28
+ with:
29
+ name: python-distributions
30
+ path: dist/
31
+ if-no-files-found: error
32
+
33
+ publish:
34
+ needs: build
35
+ runs-on: ubuntu-latest
36
+ environment:
37
+ name: pypi
38
+ url: https://pypi.org/project/physlint/
39
+ permissions:
40
+ id-token: write
41
+ steps:
42
+ - uses: actions/download-artifact@v4
43
+ with:
44
+ name: python-distributions
45
+ path: dist/
46
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,31 @@
1
+ name: Pinned real-data validation
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ permissions:
7
+ contents: read
8
+
9
+ jobs:
10
+ validate:
11
+ runs-on: ubuntu-latest
12
+ timeout-minutes: 45
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: actions/setup-python@v5
16
+ with:
17
+ python-version: "3.12"
18
+ cache: pip
19
+ - run: python -m pip install --upgrade pip
20
+ - run: python -m pip install -e '.[video,dev]' huggingface_hub
21
+ - uses: actions/cache@v4
22
+ with:
23
+ path: ~/.cache/huggingface/hub
24
+ key: physlint-real-data-${{ hashFiles('validation/manifest.yaml') }}
25
+ - run: python -m validation.harness --output validation/reports/ci
26
+ - uses: actions/upload-artifact@v4
27
+ if: always()
28
+ with:
29
+ name: sanitized-validation-reports
30
+ path: validation/reports/ci/
31
+ if-no-files-found: warn
@@ -0,0 +1,19 @@
1
+ # Local product and implementation planning
2
+ docs/plan/
3
+
4
+ # Python/build artifacts
5
+ .venv/
6
+ __pycache__/
7
+ *.py[cod]
8
+ *.egg-info/
9
+ build/
10
+ dist/
11
+ .pytest_cache/
12
+ .mypy_cache/
13
+ .ruff_cache/
14
+ .coverage
15
+ htmlcov/
16
+ validation/.work/
17
+
18
+ # Physlint output
19
+ .physlint/
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0a1 — public alpha
4
+
5
+ - Add 17 deterministic LeRobot v3 integrity rules and stable CI exit codes.
6
+ - Make maximum-gap defaults scale with declared FPS while preserving absolute overrides.
7
+ - Require aligned robot motion for frozen-camera findings.
8
+ - Share one privacy-safe video analysis pass across video rules.
9
+ - Group repeated gap and black-frame evidence.
10
+ - Preserve Hugging Face repository identity and pinned source revision.
11
+ - Separate NaN/Inf findings from null/empty stream-overlap semantics.
12
+ - Add a reproducible four-dataset and three-corruption release gate.
13
+
14
+ This alpha supports local LeRobot v3.x directories. MCAP/ROS 2 and Robomimic are roadmap items, not implemented adapters.
@@ -0,0 +1,18 @@
1
+ # Contributing
2
+
3
+ Use Python 3.11 or newer and install `.[video,dev]`. Before opening a change, run:
4
+
5
+ ```bash
6
+ ruff check .
7
+ ruff format --check .
8
+ mypy
9
+ pytest
10
+ ```
11
+
12
+ Rules must include positive and negative fixtures, a controlled corruption test where applicable, stable evidence and remediation, documented limitations, and a characterized cost. Adapters must remain read-only, metadata-first, lazy over samples, and explicit about capabilities.
13
+
14
+ New adapter proposals should start with the format-request issue template and include immutable public healthy examples, controlled defects, stream/episode semantics, and the real failure modes the adapter should catch. See `docs/roadmap.md` for the acceptance gate.
15
+
16
+ The pinned public release gate is intentionally separate from normal CI because it downloads hundreds of MiB. Maintainers can run it locally with `python -m validation.harness` or trigger the manual real-data workflow.
17
+
18
+ Do not add network access, telemetry, source-data mutation, large fixtures, or a new default dependency without an issue describing the measured requirement.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Physlint contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,350 @@
1
+ Metadata-Version: 2.5
2
+ Name: physlint
3
+ Version: 0.1.0a1
4
+ Summary: Local-first integrity validation for physical-AI and robot-learning data
5
+ Project-URL: Homepage, https://github.com/pranayjoshi/physlint
6
+ Project-URL: Documentation, https://github.com/pranayjoshi/physlint#readme
7
+ Project-URL: Issues, https://github.com/pranayjoshi/physlint/issues
8
+ Project-URL: Source, https://github.com/pranayjoshi/physlint
9
+ Author: Pranay Joshi
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: data-quality,dataset-validation,lerobot,physical-ai,robotics
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.11
26
+ Requires-Dist: numpy<2.5,>=1.26
27
+ Requires-Dist: pyarrow>=19.0.1
28
+ Requires-Dist: pydantic>=2.8
29
+ Requires-Dist: pyyaml>=6
30
+ Requires-Dist: rich>=13
31
+ Requires-Dist: typer>=0.12
32
+ Provides-Extra: dev
33
+ Requires-Dist: mypy>=1.11; extra == 'dev'
34
+ Requires-Dist: pytest-cov>=5; extra == 'dev'
35
+ Requires-Dist: pytest>=8.2; extra == 'dev'
36
+ Requires-Dist: ruff>=0.6; extra == 'dev'
37
+ Requires-Dist: types-pyyaml>=6; extra == 'dev'
38
+ Provides-Extra: video
39
+ Requires-Dist: opencv-python-headless>=4.9; extra == 'video'
40
+ Description-Content-Type: text/markdown
41
+
42
+ <div align="center">
43
+
44
+ # Physlint
45
+
46
+ ### Robot data integrity, before training.
47
+
48
+ Local-first, deterministic validation for physical-AI recordings and robot-learning datasets.
49
+
50
+ [![CI](https://img.shields.io/github/actions/workflow/status/pranayjoshi/physlint/ci.yml?branch=master&style=flat-square&label=CI)](https://github.com/pranayjoshi/physlint/actions/workflows/ci.yml)
51
+ [![Release](https://img.shields.io/github/v/release/pranayjoshi/physlint?include_prereleases&style=flat-square)](https://github.com/pranayjoshi/physlint/releases)
52
+ ![Python](https://img.shields.io/badge/Python-3.11%2B-3776AB?style=flat-square&logo=python&logoColor=white)
53
+ [![MIT](https://img.shields.io/badge/License-MIT-2ea44f?style=flat-square)](LICENSE)
54
+ ![Status](https://img.shields.io/badge/Status-Public%20Alpha-f59e0b?style=flat-square)
55
+
56
+ [Quickstart](#quickstart) · [Demo](#see-it-catch-a-real-defect) · [Rules](#what-physlint-checks) · [Evidence](#reproducible-public-data-evidence) · [Roadmap](#format-roadmap) · [Contributing](#contributing)
57
+
58
+ <img src="docs/assets/launch/exports/validation-summary.png" alt="Physlint public-alpha validation: four of four clean snapshots passed and three of three controlled corruptions were detected across 74 episodes and 31,258 frames." width="100%">
59
+
60
+ </div>
61
+
62
+ Physlint finds concrete integrity defects before robot data reaches training. It explains the impact, identifies the affected episode and stream, recommends remediation, writes a stable JSON report, and returns a CI-safe exit code.
63
+
64
+ The `0.1.0a1` public alpha ships with a publicly validated **LeRobot Dataset v3.x** adapter. The engine is designed to grow into recording and dataset profiles for MCAP/ROS 2, Robomimic HDF5, RLDS/TFDS, and other physical-AI formats without pretending those adapters already exist.
65
+
66
+ > [!IMPORTANT]
67
+ > Physlint validates configured data-integrity contracts. A pass does not certify policy quality, task success, or robot safety.
68
+
69
+ ## Why Physlint?
70
+
71
+ - **Catch failures before GPU time:** malformed manifests, broken episode ranges, reordered clocks, missing values, corrupt video, frozen cameras, and black frames become actionable findings.
72
+ - **Keep robot data local:** `physlint check` performs no network requests and never modifies its source dataset.
73
+ - **Get evidence, not a mystery score:** every finding includes a stable rule ID, severity, source location, observed condition, expected condition, impact, and remediation.
74
+ - **Use it in CI:** deterministic execution, versioned JSON, atomic report writes, strict configuration, and documented exit codes.
75
+ - **Trust scoped claims:** the public release gate pins exact dataset revisions and commits sanitized reports, corruption recipes, checksums, and publication metrics.
76
+
77
+ ## Quickstart
78
+
79
+ Physlint requires Python 3.11 or newer.
80
+
81
+ ### Install from PyPI
82
+
83
+ ```bash
84
+ python -m pip install "physlint[video]==0.1.0a1"
85
+ ```
86
+
87
+ Until the package is published to PyPI, install this GitHub release directly:
88
+
89
+ ```bash
90
+ python -m pip install "physlint[video] @ git+https://github.com/pranayjoshi/physlint.git@v0.1.0a1"
91
+ ```
92
+
93
+ ### Check a local LeRobot v3 dataset
94
+
95
+ ```bash
96
+ physlint inspect /path/to/lerobot-dataset
97
+ physlint check /path/to/lerobot-dataset
98
+ ```
99
+
100
+ Write JSON to an exact destination:
101
+
102
+ ```bash
103
+ physlint check /path/to/lerobot-dataset \
104
+ --output json \
105
+ --json-output artifacts/physlint-report.json
106
+ ```
107
+
108
+ The source remains untouched. Exit code `0` means the configured contract passed; `1` means validation completed with a blocking finding.
109
+
110
+ ## See it catch a real defect
111
+
112
+ These captures use the pinned Panda source from the release gate. The second dataset is a fully dereferenced copy with one deterministic NaN injected at episode 0, sample 5, state dimension 0.
113
+
114
+ <table>
115
+ <tr>
116
+ <th width="50%">Clean pinned snapshot</th>
117
+ <th width="50%">Controlled NaN corruption</th>
118
+ </tr>
119
+ <tr>
120
+ <td><img src="docs/assets/launch/exports/clean-validation.png" alt="Physlint terminal output showing the clean pinned Panda LeRobot dataset passing 11 applicable rules with zero failures and zero errors."></td>
121
+ <td><img src="docs/assets/launch/exports/controlled-nan-failure.png" alt="Physlint terminal output showing a controlled NaN corruption failing numeric.finite_values with remediation guidance."></td>
122
+ </tr>
123
+ </table>
124
+
125
+ Physlint owns NaN and infinity semantics in `numeric.finite_values`; the same sample is not duplicated as a missing-stream finding.
126
+
127
+ ## What Physlint checks
128
+
129
+ Seventeen deterministic rules are enabled by default:
130
+
131
+ | Area | Checks |
132
+ |---|---|
133
+ | **Manifest** | Required files, declared/stored schema agreement, required streams, and feature shapes |
134
+ | **Episodes** | Unique identifiers, positive lengths, non-overlapping boundaries, and stored-row agreement |
135
+ | **Temporal** | Strictly monotonic timestamps, FPS cadence, FPS-aware maximum gaps, complete stream overlap, and observation/action delay when independently timestamped |
136
+ | **Numeric** | NaN/Inf, configured physical bounds, and configured discontinuity limits |
137
+ | **Video** | Complete decode, motion-aware frozen-frame runs, and grouped black/near-empty frames |
138
+
139
+ List or explain the installed rule contract:
140
+
141
+ ```bash
142
+ physlint rules
143
+ physlint rules --json
144
+ physlint explain temporal.max_gap
145
+ physlint explain video.frozen_frames
146
+ ```
147
+
148
+ Rules whose required inputs are unavailable return `not_run` with a reason; they are never misreported as passed. Robot-specific bounds and discontinuity checks stay `not_run` until the user supplies meaningful thresholds.
149
+
150
+ Read the complete [MVP rule specifications](docs/rules/mvp-rules.md).
151
+
152
+ ## Configuration
153
+
154
+ Run `physlint init` to generate a documented quality contract, or create `physlint.yaml` yourself:
155
+
156
+ ```yaml
157
+ config_version: 1
158
+ adapter: auto
159
+ required_streams:
160
+ - observation.state
161
+ - action
162
+ fail_on: error
163
+
164
+ rules:
165
+ temporal.max_gap:
166
+ options:
167
+ # Default limit is 2× the interval implied by declared FPS.
168
+ max_gap_multiplier: 2.0
169
+
170
+ video.frozen_frames:
171
+ options:
172
+ max_consecutive_frames: 5
173
+ # Action is preferred over noisier observed state by default.
174
+ motion_streams: [action, observation.state]
175
+
176
+ numeric.configured_bounds:
177
+ options:
178
+ limits:
179
+ action:
180
+ min: [-1.0, -1.0]
181
+ max: [1.0, 1.0]
182
+
183
+ numeric.discontinuity:
184
+ options:
185
+ max_delta:
186
+ observation.state: [0.25, 0.25]
187
+
188
+ reports:
189
+ json: true
190
+ output_dir: .physlint/reports
191
+ ```
192
+
193
+ Use it explicitly when needed:
194
+
195
+ ```bash
196
+ physlint check /path/to/dataset --config physlint.yaml
197
+ ```
198
+
199
+ Unknown top-level keys, rule IDs, rule options, duplicate required streams, and invalid values are rejected instead of silently ignored.
200
+
201
+ ## CI integration
202
+
203
+ The CLI has stable exit codes and writes reports atomically, so a basic GitHub Actions gate is small:
204
+
205
+ ```yaml
206
+ - name: Install Physlint
207
+ run: python -m pip install "physlint[video]==0.1.0a1"
208
+
209
+ - name: Validate robot dataset
210
+ run: |
211
+ physlint check "$DATASET_PATH" \
212
+ --json-output artifacts/physlint-report.json
213
+
214
+ - uses: actions/upload-artifact@v4
215
+ if: always()
216
+ with:
217
+ name: physlint-report
218
+ path: artifacts/physlint-report.json
219
+ ```
220
+
221
+ | Exit code | Meaning |
222
+ |---:|---|
223
+ | `0` | Validation completed and the configured contract passed |
224
+ | `1` | Validation completed and the contract failed |
225
+ | `2` | Invalid command or configuration |
226
+ | `3` | Dataset or adapter failure |
227
+ | `4` | Internal Physlint error |
228
+ | `130` | Interrupted by the user |
229
+
230
+ ## Reproducible public-data evidence
231
+
232
+ The alpha release gate evaluates four immutable public snapshots from four producers:
233
+
234
+ | Dataset | Robot | Episodes | Frames | Applicable rules | Result |
235
+ |---|---|---:|---:|---:|---|
236
+ | [`ViaCatalyst/robomimic-can-ph-lerobot-v3`](https://huggingface.co/datasets/ViaCatalyst/robomimic-can-ph-lerobot-v3) | Panda | 10 | 1,160 | 11 | Pass |
237
+ | [`cagataydev/scout-earth-rover-mini-20260616-053232`](https://huggingface.co/datasets/cagataydev/scout-earth-rover-mini-20260616-053232) | Earth Rover Mini | 3 | 4,176 | 14 | Pass |
238
+ | [`lerobot/svla_so101_pickplace`](https://huggingface.co/datasets/lerobot/svla_so101_pickplace) | SO-101 | 50 | 11,939 | 14 | Pass |
239
+ | [`vikram-avea/sentinel-demo-09`](https://huggingface.co/datasets/vikram-avea/sentinel-demo-09) | YAM humanoid | 11 | 13,983 | 14 | Pass |
240
+
241
+ Clean-source result: **4/4 snapshots pass with zero findings and zero rule errors.** Controlled-defect recall: **3/3** for a non-finite value, reordered timestamps, and a deleted source row.
242
+
243
+ Everything needed to audit or rerun those claims is versioned:
244
+
245
+ - [Pinned repository manifest](validation/manifest.yaml)
246
+ - [Deterministic corruption and execution harness](validation/harness.py)
247
+ - [Sanitized reports and SHA-256 values](validation/reports/real-data-2026-08-24/summary.json)
248
+ - [Publication-ready CSV](validation/reports/real-data-2026-08-24/summary.csv)
249
+ - [Manual classification and performance analysis](docs/validation/real-data-2026-08-23.md)
250
+ - [Reproduction instructions](validation/README.md)
251
+
252
+ Runtime measurements are observations from the documented machine and run—not universal performance guarantees.
253
+
254
+ ## Format roadmap
255
+
256
+ The storage format is an adapter boundary, not the product boundary.
257
+
258
+ <img src="docs/assets/launch/exports/format-compatibility.png" alt="Physlint format compatibility table: LeRobot v3 implemented and validated; MCAP with ROS 2 profiles and Robomimic planned; RLDS, TFDS, ROS bag2, and ROS 1 bag under research." width="100%">
259
+
260
+ | Format | Status | Intended mode |
261
+ |---|---|---|
262
+ | LeRobot Dataset v3.x | **Alpha—implemented and publicly validated** | Training datasets |
263
+ | MCAP with ROS 2 profiles | Planned—seeking design partners | Recordings and derived datasets |
264
+ | Robomimic HDF5 | Planned | Demonstration datasets |
265
+ | RLDS/TFDS | Researching | Episode/step datasets |
266
+ | ROS bag2 SQLite and ROS 1 bag | Researching | Recordings |
267
+
268
+ MCAP needs two honest layers: container/channel health that works without training semantics, and an explicit profile mapping topics to actions, state, cameras, and episode boundaries. See the [cross-format roadmap](docs/roadmap.md) and [MCAP/ROS proposal](docs/design/mcap-ros.md).
269
+
270
+ Use the adapter-request issue form to contribute an immutable public example and a real failure mode.
271
+
272
+ ## Current LeRobot boundary
273
+
274
+ Supported:
275
+
276
+ - LeRobot v3.x `meta/info.json` schema and path templates
277
+ - Chunked Parquet episode metadata and sample shards
278
+ - Multiple episodes per shared Parquet/MP4 file
279
+ - Fixed-size and regular vector features
280
+ - Shared video segments using per-camera timestamp ranges
281
+ - Metadata-first discovery and bounded batch iteration
282
+
283
+ Not currently supported:
284
+
285
+ - LeRobot v2.0/v2.1
286
+ - Remote Hub identifiers passed directly to `physlint check`
287
+ - Image-directory features in the video rule set
288
+ - Arbitrary codecs unavailable to the installed OpenCV build
289
+ - Inferred safety, calibration, task-success, or coordinate-frame conclusions
290
+
291
+ Read the [LeRobot adapter boundary](docs/adapters/lerobot.md).
292
+
293
+ ## Design principles
294
+
295
+ ```text
296
+ source format → read-only adapter → canonical episodes/streams → capability planner
297
+
298
+ deterministic rule engine
299
+
300
+ terminal + versioned JSON evidence
301
+ ```
302
+
303
+ - **Read only:** source datasets are never repaired or rewritten.
304
+ - **Lazy by default:** metadata first, bounded Parquet batches, and one shared privacy-safe video analysis pass.
305
+ - **Explicit applicability:** adapters advertise capabilities; unavailable checks explain why they did not run.
306
+ - **Stable evidence:** rule versions, fingerprints, source revisions, and report schema are serialized.
307
+ - **Exception isolation:** one rule failure cannot masquerade as a clean dataset pass.
308
+
309
+ ## Security and privacy
310
+
311
+ Validation is offline. Reports contain source references, timestamps, aggregate statistics, and targeted evidence—not embedded images or complete source samples. Treat every dataset parser as an attack surface and report suspected vulnerabilities privately through [GitHub Security Advisories](https://github.com/pranayjoshi/physlint/security/advisories/new).
312
+
313
+ See [SECURITY.md](SECURITY.md) before submitting a vulnerability. Do not attach private datasets or sensitive reports to public issues.
314
+
315
+ ## Contributing
316
+
317
+ Contributions are welcome, particularly:
318
+
319
+ - Public healthy and defective datasets for adapter release gates
320
+ - False-positive reproductions
321
+ - MCAP/ROS recording schemas and failure modes
322
+ - New deterministic rules with controlled corruptions
323
+ - Documentation, performance characterization, and privacy reviews
324
+
325
+ Development setup:
326
+
327
+ ```bash
328
+ git clone https://github.com/pranayjoshi/physlint.git
329
+ cd physlint
330
+ python -m pip install -e ".[video,dev]"
331
+
332
+ ruff check .
333
+ ruff format --check .
334
+ mypy
335
+ pytest
336
+ ```
337
+
338
+ Rules require positive and negative fixtures, stable remediation, a bounded finding count, and controlled corruption evidence where applicable. Adapters must remain read-only, metadata-first, lazy over samples, and explicit about capabilities.
339
+
340
+ Read [CONTRIBUTING.md](CONTRIBUTING.md), open a format request, or join [GitHub Discussions](https://github.com/pranayjoshi/physlint/discussions).
341
+
342
+ ## Project status
343
+
344
+ Physlint is an alpha. Its current claims are deliberately limited to the documented LeRobot v3 boundary and committed release evidence. The project does not train policies, repair data, host datasets, infer task success, produce an opaque quality score, or certify that a robot or policy is safe.
345
+
346
+ See [CHANGELOG.md](CHANGELOG.md) for release notes.
347
+
348
+ ## License
349
+
350
+ Physlint is available under the [MIT License](LICENSE).